Skip to content

Commit f89a6f5

Browse files
committed
refactor(tables): give the editing shell the host axis and an explicit address
Both surfaces that mount the table keep every capability they had. The mothership panel still mounts the full editing shell — editing a table from chat is a product decision, not something to drop as a side effect of a refactor — it just says `host='panel'` instead of `embedded`. Three prop smells go with it: - `embedded?: boolean` becomes `host: 'page' | 'panel'`, the same vocabulary the canonical views use. `'public'` is deliberately unrepresentable: the shell holds a write path, and an anonymous surface mounts `TableView`. - `workspaceId` / `tableId` become required instead of optional-with-a- `useParams()`-fallback. That fallback was documented as "page mode reads from useParams", which is another way of saying the component could only ever exist once per page. - `page.tsx` now names `tableId` in its params type. It always had it — the type just did not say so. The ids stay plain rather than becoming a `ResourceSource`: `page.tsx` is a Server Component and a source carries functions, so it cannot cross the RSC boundary. That is the same reason the public interface page hands over a plain seed and lets the client mint the source, and the prop doc now says so. Behaviour is identical by construction — `embedded` is simply derived as `host !== 'page'`, and the permission context the panel reads is untouched.
1 parent 35494f3 commit f89a6f5

3 files changed

Lines changed: 43 additions & 18 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,9 @@ export const ResourceContent = memo(function ResourceContent({
264264
return (
265265
<Table
266266
key={resource.id}
267+
host='panel'
267268
workspaceId={workspaceId}
268269
tableId={resource.id}
269-
embedded
270270
viewsEnabled={tableViewsEnabled}
271271
/>
272272
)

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/page.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export const metadata: Metadata = {
1111
}
1212

1313
interface TablePageProps {
14-
params: Promise<{ workspaceId: string }>
14+
params: Promise<{ workspaceId: string; tableId: string }>
1515
}
1616

1717
/**
@@ -28,7 +28,7 @@ interface TablePageProps {
2828
* the two flag lookups share them.
2929
*/
3030
export default async function TablePage({ params }: TablePageProps) {
31-
const [{ workspaceId }, session] = await Promise.all([params, getSession()])
31+
const [{ workspaceId, tableId }, session] = await Promise.all([params, getSession()])
3232
const userId = session?.user?.id
3333
const host = userId ? await getWorkspaceHostContextForViewer(workspaceId, userId) : null
3434
const orgId = host?.hostOrganizationId ?? undefined
@@ -39,7 +39,13 @@ export default async function TablePage({ params }: TablePageProps) {
3939

4040
return (
4141
<Suspense fallback={<TableLoading />}>
42-
<Table tableLocksEnabled={tableLocksEnabled} viewsEnabled={viewsEnabled} />
42+
<Table
43+
host='page'
44+
workspaceId={workspaceId}
45+
tableId={tableId}
46+
tableLocksEnabled={tableLocksEnabled}
47+
viewsEnabled={viewsEnabled}
48+
/>
4349
</Suspense>
4450
)
4551
}

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/table.tsx

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Chip, ChipConfirmModal, toast } from '@sim/emcn'
55
import { Download, Lock, Pencil, Table as TableIcon, Trash, Upload } from '@sim/emcn/icons'
66
import { createLogger } from '@sim/logger'
77
import { getErrorMessage } from '@sim/utils/errors'
8-
import { useParams, useRouter } from 'next/navigation'
8+
import { useRouter } from 'next/navigation'
99
import { useQueryStates } from 'nuqs'
1010
import { usePostHog } from 'posthog-js/react'
1111
import {
@@ -55,6 +55,7 @@ import {
5555
} from '@/hooks/queries/tables'
5656
import { useInlineRename } from '@/hooks/use-inline-rename'
5757
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
58+
import type { ResourceHost } from '@/resources'
5859
import { useLogDetailsUIStore } from '@/stores/logs/store'
5960
import type { DeletedRowSnapshot } from '@/stores/table/types'
6061
import {
@@ -92,12 +93,30 @@ const logger = createLogger('Table')
9293
const BLOCKED_TOAST_MS = 8000
9394

9495
interface TableProps {
95-
/** When set, the table renders without its page header / breadcrumbs / page-level
96-
* options bar. Used by the mothership chat panel to embed a table inline. */
97-
embedded?: boolean
98-
/** Identifiers — only set in embedded mode. Page mode reads from `useParams()`. */
99-
workspaceId?: string
100-
tableId?: string
96+
/**
97+
* Which surface this table is mounted on. `'page'` renders the full route
98+
* chrome — header, breadcrumbs, page-level options bar; every other host
99+
* renders the grid alone, as the mothership chat panel does.
100+
*
101+
* This replaces an `embedded` boolean so the shell speaks the same vocabulary
102+
* as the canonical views it sits beside. `'public'` is not reachable here:
103+
* the editing shell holds a write path, and an anonymous surface mounts
104+
* `TableView` instead.
105+
*/
106+
host: Extract<ResourceHost, 'page' | 'panel'>
107+
/**
108+
* The table's address. Required rather than derived: both mounts know it
109+
* (`page.tsx` from its route params, the panel from the open resource), and a
110+
* `useParams()` fallback meant this component could only ever exist once per
111+
* page.
112+
*
113+
* Plain ids rather than a `ResourceSource` because `page.tsx` is a Server
114+
* Component and a source carries functions, which cannot cross the RSC
115+
* boundary — the same reason the public interface page hands over a plain
116+
* seed and lets the client mint the source.
117+
*/
118+
workspaceId: string
119+
tableId: string
101120
/**
102121
* Whether an admin may CHANGE locks, resolved server-side by the page (the
103122
* flag's gating lives in AppConfig and has no client counterpart). Defaults
@@ -208,16 +227,16 @@ function isSameViewConfig(a: TableViewConfig, b: TableViewConfig): boolean {
208227
* Embedded mode skips the page header but otherwise renders the same surface.
209228
*/
210229
export function Table({
211-
embedded,
212-
workspaceId: propWorkspaceId,
213-
tableId: propTableId,
230+
host,
231+
workspaceId,
232+
tableId,
214233
tableLocksEnabled = false,
215234
viewsEnabled = false,
216-
}: TableProps = {}) {
217-
const params = useParams()
235+
}: TableProps) {
218236
const router = useRouter()
219-
const workspaceId = propWorkspaceId || (params.workspaceId as string)
220-
const tableId = propTableId || (params.tableId as string)
237+
238+
/** `host === 'page'` is the only surface that owns the route chrome. */
239+
const embedded = host !== 'page'
221240

222241
const posthog = usePostHog()
223242
const posthogRef = useRef(posthog)

0 commit comments

Comments
 (0)