Skip to content

Commit f0e988d

Browse files
committed
feat(settings): add sort to recently deleted page
Add a sort dropdown next to the search bar allowing users to sort by deletion date (default, newest first), name (A–Z), or type (A–Z).
1 parent c56e3ac commit f0e988d

File tree

1 file changed

+94
-16
lines changed

1 file changed

+94
-16
lines changed

apps/sim/app/workspace/[workspaceId]/settings/components/recently-deleted/recently-deleted.tsx

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,17 @@
33
import { useMemo, useState } from 'react'
44
import { Search } from 'lucide-react'
55
import { 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'
717
import { Input } from '@/components/ui'
818
import { formatDate } from '@/lib/core/utils/formatting'
919
import { RESOURCE_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
@@ -34,6 +44,19 @@ function getResourceHref(
3444

3545
type 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+
3760
const ICON_CLASS = 'h-[14px] w-[14px]'
3861

3962
const 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

Comments
 (0)