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,17 @@ 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
9592
9693 const rows : ResourceRow [ ] = useMemo (
9794 ( ) =>
@@ -125,10 +122,9 @@ export function SimLinks({ organizationId }: SimLinksProps) {
125122 value : search ,
126123 onChange : setSearch ,
127124 onClearAll : ( ) => setSearch ( '' ) ,
128- placeholder : 'Search Sim links...' ,
125+ placeholder : SIM_LINKS_SEARCH_PLACEHOLDER ,
129126 }
130127 const sortConfig : SortConfig = { options : SORT_OPTIONS , active : activeSort , onSort, onClear }
131- const searching = Boolean ( debouncedSearch . trim ( ) )
132128 const startCreate = ( ) => void setView ( { create : '' } )
133129
134130 if ( ! links . data && ! links . isLoadingError ) return < SimLinksLoading />
@@ -138,22 +134,19 @@ export function SimLinks({ organizationId }: SimLinksProps) {
138134 < Resource . Header
139135 icon = { Link }
140136 title = 'Sim Links'
141- actions = { [
142- { id : 'create' , text : 'New link' , icon : Plus , variant : 'primary' , onSelect : startCreate } ,
143- ] }
137+ actions = { [ { ...NEW_SIM_LINK_ACTION , id : 'create' , onSelect : startCreate } ] }
144138 />
145139 < Resource . Options search = { searchConfig } sort = { sortConfig } />
146140 < Resource . Table
147- columns = { COLUMNS }
141+ columns = { SIM_LINK_COLUMNS }
148142 rows = { rows }
149143 onRowClick = { ( id ) => {
150144 const link = linksById . get ( id )
151145 if ( link ) openLink ( link . name )
152146 } }
153147 onRowContextMenu = { ( event , id ) => {
154- const link = linksById . get ( id )
155- if ( ! link ) return
156- setMenuLink ( link )
148+ if ( ! linksById . has ( id ) ) return
149+ setMenuLinkId ( id )
157150 contextMenu . handleContextMenu ( event )
158151 } }
159152 onLoadMore = { ( ) => void links . fetchNextPage ( ) }
@@ -167,9 +160,9 @@ export function SimLinks({ organizationId }: SimLinksProps) {
167160 < p className = 'text-[var(--text-error)] text-small' > { links . error . message } </ p >
168161 < Chip onClick = { ( ) => void links . refetch ( ) } > Retry</ Chip >
169162 </ div >
170- ) : searching ? (
163+ ) : query ? (
171164 < ResourceNoResults
172- search = { debouncedSearch }
165+ search = { query }
173166 filterCount = { 0 }
174167 searchScope = 'Searched every Sim link in your organization.'
175168 onClear = { ( ) => setSearch ( '' ) }
0 commit comments