11'use client'
22
3- import { useCallback , useMemo , useReducer , useRef , useState } from 'react'
3+ import { useCallback , useEffect , useMemo , useReducer , useRef , useState } from 'react'
44import { Chip , ChipConfirmModal , toast } from '@sim/emcn'
55import { Download , Lock , Pencil , Table as TableIcon , Trash , Upload } from '@sim/emcn/icons'
66import { createLogger } from '@sim/logger'
@@ -61,12 +61,7 @@ import {
6161import { COLUMN_SIDEBAR_WIDTH } from './components/table-grid/constants'
6262import { COLUMN_TYPE_ICONS } from './components/table-grid/headers'
6363import { 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'
7065import {
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