22
33import { useMemo , useState } from 'react'
44import { Avatar , Chip , ChipConfirmModal , ChipModalError , writeTextToClipboard } from '@sim/emcn'
5- import { Link , Plus } from '@sim/emcn/icons'
5+ import { Link } from '@sim/emcn/icons'
66import { createLogger } from '@sim/logger'
77import { useQueryStates } from 'nuqs'
88import type { SimLink } from '@/lib/api/contracts/sim-links'
99import { simLinkPath } from '@/lib/navigation/paths'
1010import { SEARCH_DEBOUNCE_MS } from '@/lib/url-state'
11+ import {
12+ NEW_SIM_LINK_ACTION ,
13+ SIM_LINK_COLUMNS ,
14+ SIM_LINKS_SEARCH_PLACEHOLDER ,
15+ } from '@/app/o/[organizationId]/links/columns'
1116import SimLinksLoading from '@/app/o/[organizationId]/links/loading'
1217import {
1318 simLinksParsers ,
@@ -28,7 +33,6 @@ import type {
2833import { timeCell } from '@/app/workspace/[workspaceId]/components/resource/components/time-cell'
2934import {
3035 Resource ,
31- type ResourceColumn ,
3236 type ResourceRow ,
3337} from '@/app/workspace/[workspaceId]/components/resource/resource'
3438import { useDeleteSimLink , useSimLinks } from '@/hooks/queries/sim-links'
@@ -39,15 +43,6 @@ import { useUrlSort } from '@/hooks/use-url-sort'
3943
4044const logger = createLogger ( 'SimLinks' )
4145
42- const COLUMNS = [
43- { id : 'name' , header : 'Name' } ,
44- { id : 'destination' , header : 'Destination' , widthMultiplier : 2 } ,
45- { id : 'description' , header : 'Description' , widthMultiplier : 1.4 } ,
46- { id : 'owner' , header : 'Created by' } ,
47- { id : 'access' , header : 'Access' } ,
48- { id : 'updated' , header : 'Updated' } ,
49- ] as const satisfies readonly ResourceColumn [ ]
50-
5146const SORT_OPTIONS = [
5247 { id : 'name' , label : 'Name' } ,
5348 { id : 'popular' , label : 'Opens' } ,
@@ -83,15 +78,18 @@ export function SimLinks({ organizationId }: SimLinksProps) {
8378 )
8479 const debouncedSearch = useSearchFilterValue ( search , SEARCH_DEBOUNCE_MS )
8580 const { sort, dir, activeSort, onSort, onClear } = useUrlSort ( simLinksSortParams , simLinksUrlKeys )
86- const links = useSimLinks ( organizationId , debouncedSearch . trim ( ) , sort , dir )
81+ const query = debouncedSearch . trim ( )
82+ const links = useSimLinks ( organizationId , query , sort , dir )
8783 const remove = useDeleteSimLink ( organizationId )
8884 const contextMenu = useContextMenu ( )
89- const [ menuLink , setMenuLink ] = useState < SimLink | null > ( null )
85+ const [ menuLinkId , setMenuLinkId ] = useState < string | null > ( null )
9086 const [ editing , setEditing ] = useState < SimLink | null > ( null )
9187 const [ deleting , setDeleting ] = useState < SimLink | null > ( null )
9288
9389 const loaded = useMemo ( ( ) => links . data ?. pages . flatMap ( ( page ) => page . links ) ?? [ ] , [ links . data ] )
9490 const linksById = useMemo ( ( ) => new Map ( loaded . map ( ( link ) => [ link . id , link ] ) ) , [ loaded ] )
91+ const menuLink = menuLinkId ? linksById . get ( menuLinkId ) : undefined
92+ if ( menuLinkId && ! menuLink ) setMenuLinkId ( null )
9593
9694 const rows : ResourceRow [ ] = useMemo (
9795 ( ) =>
@@ -125,10 +123,9 @@ export function SimLinks({ organizationId }: SimLinksProps) {
125123 value : search ,
126124 onChange : setSearch ,
127125 onClearAll : ( ) => setSearch ( '' ) ,
128- placeholder : 'Search Sim links...' ,
126+ placeholder : SIM_LINKS_SEARCH_PLACEHOLDER ,
129127 }
130128 const sortConfig : SortConfig = { options : SORT_OPTIONS , active : activeSort , onSort, onClear }
131- const searching = Boolean ( debouncedSearch . trim ( ) )
132129 const startCreate = ( ) => void setView ( { create : '' } )
133130
134131 if ( ! links . data && ! links . isLoadingError ) return < SimLinksLoading />
@@ -138,22 +135,19 @@ export function SimLinks({ organizationId }: SimLinksProps) {
138135 < Resource . Header
139136 icon = { Link }
140137 title = 'Sim Links'
141- actions = { [
142- { id : 'create' , text : 'New link' , icon : Plus , variant : 'primary' , onSelect : startCreate } ,
143- ] }
138+ actions = { [ { ...NEW_SIM_LINK_ACTION , id : 'create' , onSelect : startCreate } ] }
144139 />
145140 < Resource . Options search = { searchConfig } sort = { sortConfig } />
146141 < Resource . Table
147- columns = { COLUMNS }
142+ columns = { SIM_LINK_COLUMNS }
148143 rows = { rows }
149144 onRowClick = { ( id ) => {
150145 const link = linksById . get ( id )
151146 if ( link ) openLink ( link . name )
152147 } }
153148 onRowContextMenu = { ( event , id ) => {
154- const link = linksById . get ( id )
155- if ( ! link ) return
156- setMenuLink ( link )
149+ if ( ! linksById . has ( id ) ) return
150+ setMenuLinkId ( id )
157151 contextMenu . handleContextMenu ( event )
158152 } }
159153 onLoadMore = { ( ) => void links . fetchNextPage ( ) }
@@ -167,9 +161,9 @@ export function SimLinks({ organizationId }: SimLinksProps) {
167161 < p className = 'text-[var(--text-error)] text-small' > { links . error . message } </ p >
168162 < Chip onClick = { ( ) => void links . refetch ( ) } > Retry</ Chip >
169163 </ div >
170- ) : searching ? (
164+ ) : query ? (
171165 < ResourceNoResults
172- search = { debouncedSearch }
166+ search = { query }
173167 filterCount = { 0 }
174168 searchScope = 'Searched every Sim link in your organization.'
175169 onClear = { ( ) => setSearch ( '' ) }
0 commit comments