diff --git a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx index ffcef8ce465..3ce3768cd13 100644 --- a/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/tables/[tableId]/components/column-config-sidebar/column-config-sidebar.tsx @@ -1,28 +1,21 @@ 'use client' import { useState } from 'react' -import { Button, ChipCombobox, ChipInput, cn, FieldDivider, Label, Switch, toast } from '@sim/emcn' +import { Button, ChipCombobox, cn, FieldDivider, Label, Switch, toast } from '@sim/emcn' import { X } from '@sim/emcn/icons' import { toError } from '@sim/utils/errors' -import { findValidationIssue, isValidationError } from '@/lib/api/client/errors' +import { useIsMutating } from '@tanstack/react-query' +import { isValidationError } from '@/lib/api/client/errors' import type { ColumnDefinition, SelectOption } from '@/lib/table' -import { - DEFAULT_CURRENCY_CODE, - getCurrencyOptions, - resolveCurrencyCode, -} from '@/lib/table/currency' +import { getColumnId } from '@/lib/table/column-keys' +import { getCurrencyOptions, resolveCurrencyCode } from '@/lib/table/currency' import { FieldError, RequiredLabel, } from '@/app/workspace/[workspaceId]/tables/[tableId]/components/sidebar-fields' -import { useAddTableColumn, useUpdateColumn } from '@/hooks/queries/tables' +import { useUpdateColumn } from '@/hooks/queries/tables' +import { tableKeys } from '@/hooks/queries/utils/table-keys' import { SelectOptionsEditor } from '../select-field' -import { PLAIN_COLUMN_TYPE_OPTIONS } from './column-types' - -/** Whether a column type carries an option set. */ -function isSelectType(type: ColumnDefinition['type']): boolean { - return type === 'select' -} /** * Picker entries, built once at module load: the option list is derived from the @@ -38,41 +31,60 @@ function optionsEqual(a: SelectOption[], b: SelectOption[]): boolean { } /** - * Discriminates the two flows the column-config sidebar handles. Workflow - * configuration is a separate component (``) so this surface - * never has to branch on `isWorkflow`. + * Discriminates the flows the column-config sidebar handles. Name and type + * never appear here — renaming is inline in the header and type changes go + * through the header menu's "Change type" submenu. Workflow configuration is a + * separate component (``). */ export type ColumnConfig = - | { mode: 'create'; proposedName: string; type: ColumnDefinition['type'] } - | { mode: 'edit'; columnName: string } + /** + * A select column being created: its name has been committed in the grid + * header, but it exists only as a draft there until Save hands the option + * set to the creator, which persists name and options in one create. + * Dismissing discards the draft. + */ + | { mode: 'draft-select' } + /** + * Convert an existing column to select: Save applies the type change + * together with the configured options; dismissing leaves the column's + * current type untouched. + */ + | { mode: 'convert-select'; columnName: string } + /** Edit the configuration of an existing select or currency column. */ + | { mode: 'configure'; columnName: string } interface ColumnConfigSidebarProps { /** When non-null the sidebar is open. */ config: ColumnConfig | null onClose: () => void - /** Existing column record for `mode: 'edit'`; ignored otherwise. */ + /** + * `draft-select` Save: hands the validated option set to the draft's owner, + * which persists the column. Resolves `true` once created (the parent then + * closes the sidebar) or `false` to stay open — the draft's name was refused + * or the create failed, and the owner has already surfaced why. + */ + onDraftSave: (options: SelectOption[], multiple: boolean) => Promise + onColumnTypeChange: ( + columnName: string, + previousColumn: ColumnDefinition, + newColumn: ColumnDefinition + ) => void + /** Existing column record for modes that carry a `columnName`; otherwise null. */ existingColumn: ColumnDefinition | null workspaceId: string tableId: string - /** Notify parent of a rename so it can rewrite local `columnOrder` / - * `columnWidths` keys that reference the old name. */ - onColumnRename?: (oldName: string, newName: string) => void } /** - * Right-edge sidebar for plain (non-workflow) column configuration. Handles - * create (with type pre-chosen by the parent's "+ New column" dropdown) and - * edit. No `isWorkflow` branches — workflow-output columns route through - * `` instead. + * Right-edge sidebar for per-type column configuration: a select's option set + * and a currency's display code. Everything else about a column — name, type, + * unique — is edited in the grid header and its menu. * - * Form state seeds from props via lazy `useState` initializers; the parent - * uses `key={config?.columnName ?? 'closed'}` to remount when switching - * columns, eliminating the prop-mirroring `useEffect` the previous combined - * sidebar relied on. + * Form state seeds from props via lazy `useState` initializers; the body is + * keyed on the config identity so opening a different column or mode remounts + * and re-seeds. */ export function ColumnConfigSidebar(props: ColumnConfigSidebarProps) { - // Mount the form body with `key` keyed on the config identity so opening a - // different column / mode remounts and re-seeds state from props. const open = props.config !== null return (