Skip to content

Commit 7e3a19e

Browse files
improvement(tables): announce locks on open instead of a header chip
Drop the lock entry from the table header actions. A locked table now announces itself once on open via an info toast carrying the Lock settings action for admins; the breadcrumb dropdown remains the permanent entry point.
1 parent 49adf91 commit 7e3a19e

2 files changed

Lines changed: 21 additions & 34 deletions

File tree

apps/sim/app/workspace/[workspaceId]/tables/[tableId]/lock-copy.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
2-
* Single source of truth for lock vocabulary shared by the lock settings modal,
3-
* the blocked-action toast, and the table header chip. Kept out of
2+
* Single source of truth for lock vocabulary shared by the lock settings modal
3+
* and the lock toasts (the on-open announcement and blocked actions). Kept out of
44
* `lib/table/mutation-locks.ts` — that module is server-tainted (importing it
55
* from a client component pulls `next/headers` into the browser bundle).
66
*/
@@ -76,8 +76,8 @@ export function describeLocks(locks: TableLocks): { name: string; detail: string
7676

7777
/**
7878
* Why a locked-table notice was raised. `'status'` is the informational case
79-
* (a non-admin clicking the header lock chip); the rest are actions the user
80-
* just tried and couldn't do.
79+
* (the announcement shown once when a locked table is opened); the rest are
80+
* actions the user just tried and couldn't do.
8181
*/
8282
export type BlockedTableAction = 'add-row' | 'add-column' | 'delete-column' | 'edit-cell' | 'status'
8383

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

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useCallback, useMemo, useReducer, useRef, useState } from 'react'
3+
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react'
44
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'
@@ -61,12 +61,7 @@ import {
6161
import { COLUMN_SIDEBAR_WIDTH } from './components/table-grid/constants'
6262
import { COLUMN_TYPE_ICONS } from './components/table-grid/headers'
6363
import { useTable, useTableEventStream } from './hooks'
64-
import {
65-
type BlockedTableAction,
66-
describeBlockedAction,
67-
describeLocks,
68-
lockedNouns,
69-
} from './lock-copy'
64+
import { type BlockedTableAction, describeBlockedAction, lockedNouns } from './lock-copy'
7065
import {
7166
DEFAULT_TABLE_DETAIL_SORT_DIRECTION,
7267
tableDetailParsers,
@@ -627,7 +622,10 @@ export function Table({
627622
if (!tableData) return
628623
if (blockedToastIdRef.current) toast.dismiss(blockedToastIdRef.current)
629624
const { title, text } = describeBlockedAction(action, tableData.locks)
630-
blockedToastIdRef.current = toast.warning(title, {
625+
// 'status' is the on-open announcement — nothing was refused, so it reads
626+
// as information rather than a warning.
627+
const notify = action === 'status' ? toast.info : toast.warning
628+
blockedToastIdRef.current = notify(title, {
631629
description: text,
632630
...(canOpenLockSettings
633631
? {
@@ -641,23 +639,19 @@ export function Table({
641639
[tableData, canOpenLockSettings]
642640
)
643641

642+
// Announce the lock state once per table on open. Marked announced as soon as
643+
// the table resolves, so an admin who just set locks isn't toasted about them.
644+
const announcedLockTableIdRef = useRef<string | null>(null)
645+
useEffect(() => {
646+
if (!tableData || announcedLockTableIdRef.current === tableData.id) return
647+
announcedLockTableIdRef.current = tableData.id
648+
if (lockedNouns(tableData.locks).length === 0) return
649+
showBlockedToast('status')
650+
}, [tableData, showBlockedToast])
651+
644652
const headerActions = useMemo(() => {
645653
if (!tableData) return undefined
646-
// Header space is for state, not for settings: the chip appears only once
647-
// something is actually locked, and names the mode so it reads at a glance.
648-
// Reaching the panel on an unlocked table is the dropdown's job.
649-
const anyLocked = lockedNouns(tableData.locks).length > 0
650654
return [
651-
...(anyLocked
652-
? [
653-
{
654-
label: describeLocks(tableData.locks).name,
655-
icon: Lock,
656-
onClick: () =>
657-
userPermissions.canAdmin ? setShowLockSettings(true) : showBlockedToast('status'),
658-
},
659-
]
660-
: []),
661655
{
662656
label: 'Import CSV',
663657
icon: Upload,
@@ -673,14 +667,7 @@ export function Table({
673667
disabled: tableData.rowCount === 0,
674668
},
675669
]
676-
}, [
677-
tableData,
678-
userPermissions.canEdit,
679-
userPermissions.canAdmin,
680-
handleExportCsv,
681-
onRequestImportCsv,
682-
showBlockedToast,
683-
])
670+
}, [tableData, userPermissions.canEdit, handleExportCsv, onRequestImportCsv])
684671

685672
// Adding a column is a schema change. The trigger stays visible when the
686673
// table is schema-locked and explains itself instead of disappearing.

0 commit comments

Comments
 (0)