33import { useMemo , useState } from 'react'
44import { Search } from 'lucide-react'
55import { useParams , useRouter } from 'next/navigation'
6- import { Button , SModalTabs , SModalTabsList , SModalTabsTrigger } from '@/components/emcn'
6+ import {
7+ ArrowUpDown ,
8+ Button ,
9+ DropdownMenu ,
10+ DropdownMenuContent ,
11+ DropdownMenuItem ,
12+ DropdownMenuTrigger ,
13+ SModalTabs ,
14+ SModalTabsList ,
15+ SModalTabsTrigger ,
16+ } from '@/components/emcn'
717import { Input } from '@/components/ui'
818import { formatDate } from '@/lib/core/utils/formatting'
919import { RESOURCE_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
@@ -34,6 +44,19 @@ function getResourceHref(
3444
3545type ResourceType = 'all' | 'workflow' | 'table' | 'knowledge' | 'file'
3646
47+ type SortColumn = 'deleted' | 'name' | 'type'
48+
49+ interface SortConfig {
50+ column : SortColumn
51+ direction : 'asc' | 'desc'
52+ }
53+
54+ const SORT_OPTIONS : { column : SortColumn ; direction : 'asc' | 'desc' ; label : string } [ ] = [
55+ { column : 'deleted' , direction : 'desc' , label : 'Deleted (newest first)' } ,
56+ { column : 'name' , direction : 'asc' , label : 'Name (A–Z)' } ,
57+ { column : 'type' , direction : 'asc' , label : 'Type (A–Z)' } ,
58+ ]
59+
3760const ICON_CLASS = 'h-[14px] w-[14px]'
3861
3962const RESOURCE_TYPE_TO_MOTHERSHIP : Record < Exclude < ResourceType , 'all' > , MothershipResourceType > = {
@@ -100,6 +123,7 @@ export function RecentlyDeleted() {
100123 const workspaceId = params ?. workspaceId as string
101124 const [ activeTab , setActiveTab ] = useState < ResourceType > ( 'all' )
102125 const [ searchTerm , setSearchTerm ] = useState ( '' )
126+ const [ activeSort , setActiveSort ] = useState < SortConfig | null > ( null )
103127 const [ restoringIds , setRestoringIds ] = useState < Set < string > > ( new Set ( ) )
104128 const [ restoredItems , setRestoredItems ] = useState < Map < string , DeletedResource > > ( new Map ( ) )
105129
@@ -174,7 +198,6 @@ export function RecentlyDeleted() {
174198 }
175199 }
176200
177- items . sort ( ( a , b ) => b . deletedAt . getTime ( ) - a . deletedAt . getTime ( ) )
178201 return items
179202 } , [
180203 workflowsQuery . data ,
@@ -191,8 +214,24 @@ export function RecentlyDeleted() {
191214 const normalized = searchTerm . toLowerCase ( )
192215 items = items . filter ( ( r ) => r . name . toLowerCase ( ) . includes ( normalized ) )
193216 }
194- return items
195- } , [ resources , activeTab , searchTerm ] )
217+ const col = activeSort ?. column ?? 'deleted'
218+ const dir = activeSort ?. direction ?? 'desc'
219+ return [ ...items ] . sort ( ( a , b ) => {
220+ let cmp = 0
221+ switch ( col ) {
222+ case 'name' :
223+ cmp = a . name . localeCompare ( b . name )
224+ break
225+ case 'type' :
226+ cmp = a . type . localeCompare ( b . type )
227+ break
228+ case 'deleted' :
229+ cmp = a . deletedAt . getTime ( ) - b . deletedAt . getTime ( )
230+ break
231+ }
232+ return dir === 'asc' ? cmp : - cmp
233+ } )
234+ } , [ resources , activeTab , searchTerm , activeSort ] )
196235
197236 const showNoResults = searchTerm . trim ( ) && filtered . length === 0 && resources . length > 0
198237
@@ -232,18 +271,57 @@ export function RecentlyDeleted() {
232271
233272 return (
234273 < div className = 'flex h-full flex-col gap-4.5' >
235- < div className = 'flex items-center gap-2 rounded-lg border border-[var(--border)] bg-transparent px-2 py-[5px] transition-colors duration-100 dark:bg-[var(--surface-4)] dark:hover-hover:border-[var(--border-1)] dark:hover-hover:bg-[var(--surface-5)]' >
236- < Search
237- className = 'h-[14px] w-[14px] flex-shrink-0 text-[var(--text-tertiary)]'
238- strokeWidth = { 2 }
239- />
240- < Input
241- placeholder = 'Search deleted items...'
242- value = { searchTerm }
243- onChange = { ( e ) => setSearchTerm ( e . target . value ) }
244- disabled = { isLoading }
245- className = 'h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
246- />
274+ < div className = 'flex items-center gap-2' >
275+ < div className = 'flex flex-1 items-center gap-2 rounded-lg border border-[var(--border)] bg-transparent px-2 py-[5px] transition-colors duration-100 dark:bg-[var(--surface-4)] dark:hover-hover:border-[var(--border-1)] dark:hover-hover:bg-[var(--surface-5)]' >
276+ < Search
277+ className = 'h-[14px] w-[14px] flex-shrink-0 text-[var(--text-tertiary)]'
278+ strokeWidth = { 2 }
279+ />
280+ < Input
281+ placeholder = 'Search deleted items...'
282+ value = { searchTerm }
283+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
284+ disabled = { isLoading }
285+ className = 'h-auto flex-1 border-0 bg-transparent p-0 font-base leading-none placeholder:text-[var(--text-tertiary)] focus-visible:ring-0 focus-visible:ring-offset-0'
286+ />
287+ </ div >
288+ < DropdownMenu >
289+ < DropdownMenuTrigger asChild >
290+ < Button
291+ variant = 'outline'
292+ size = 'sm'
293+ className = 'h-[30px] shrink-0 gap-1.5 px-2.5'
294+ disabled = { isLoading }
295+ >
296+ < ArrowUpDown className = 'h-[12px] w-[12px]' />
297+ < span className = 'text-xs' >
298+ { SORT_OPTIONS . find (
299+ ( o ) =>
300+ o . column === ( activeSort ?. column ?? 'deleted' ) &&
301+ o . direction === ( activeSort ?. direction ?? 'desc' )
302+ ) ?. label ?? 'Sort' }
303+ </ span >
304+ </ Button >
305+ </ DropdownMenuTrigger >
306+ < DropdownMenuContent align = 'end' className = 'w-48' >
307+ { SORT_OPTIONS . map ( ( option ) => {
308+ const isActive =
309+ ( activeSort ?. column ?? 'deleted' ) === option . column &&
310+ ( activeSort ?. direction ?? 'desc' ) === option . direction
311+ return (
312+ < DropdownMenuItem
313+ key = { `${ option . column } -${ option . direction } ` }
314+ onClick = { ( ) =>
315+ setActiveSort ( { column : option . column , direction : option . direction } )
316+ }
317+ className = { isActive ? 'bg-[var(--bg-selected)]' : '' }
318+ >
319+ { option . label }
320+ </ DropdownMenuItem >
321+ )
322+ } ) }
323+ </ DropdownMenuContent >
324+ </ DropdownMenu >
247325 </ div >
248326
249327 < SModalTabs value = { activeTab } onValueChange = { ( v ) => setActiveTab ( v as ResourceType ) } >
0 commit comments