Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e622b6e
improvement(tables): improve table filtering UX
waleedlatif1 Mar 29, 2026
bcc7974
fix(table-filter): use ref to stabilize handleRemove/handleApply call…
waleedlatif1 Mar 29, 2026
5d037ac
improvement(tables,kb): remove hacky patterns, fix KB filter popover …
waleedlatif1 Mar 29, 2026
2e67864
feat(knowledge): add sort and filter to KB list page
waleedlatif1 Mar 29, 2026
866e91d
feat(files): add sort and filter to files list page
waleedlatif1 Mar 29, 2026
6c18471
feat(scheduled-tasks): add sort and filter to scheduled tasks page
waleedlatif1 Mar 29, 2026
f46f83c
fix(table-filter): use explicit close handler instead of toggle
waleedlatif1 Mar 29, 2026
8a1f3dc
improvement(files,knowledge): replace manual debounce with useDebounc…
waleedlatif1 Mar 29, 2026
4899dc3
fix(resource): prevent popover from inheriting anchor min-width
waleedlatif1 Mar 29, 2026
f6edb88
feat(tables): add sort to tables list page
waleedlatif1 Mar 29, 2026
51c9df9
feat(knowledge): add content and owner filters to KB list
waleedlatif1 Mar 29, 2026
12ea734
feat(scheduled-tasks): add status and health filters
waleedlatif1 Mar 29, 2026
a3ffc2f
feat(files): add size and uploaded-by filters to files list
waleedlatif1 Mar 29, 2026
0142c69
feat(tables): add row count, owner, and column type filters
waleedlatif1 Mar 29, 2026
2553cac
improvement(scheduled-tasks): use combobox filter panel matching logs…
waleedlatif1 Mar 29, 2026
9dd3028
improvement(knowledge): use combobox filter panel matching logs UI style
waleedlatif1 Mar 29, 2026
446a665
improvement(files): use combobox filter panel matching logs UI style
waleedlatif1 Mar 29, 2026
c56e3ac
improvement(tables): use combobox filter panel matching logs UI style
waleedlatif1 Mar 29, 2026
f0e988d
feat(settings): add sort to recently deleted page
waleedlatif1 Mar 29, 2026
bf0bcf3
feat(logs): add sort to logs page
waleedlatif1 Mar 29, 2026
ffe6806
improvement(knowledge): upgrade document list filter to combobox style
waleedlatif1 Mar 29, 2026
483c35c
fix(resources): fix missing imports, memoization, and stale refs acro…
waleedlatif1 Mar 29, 2026
c8672f7
improvement(tables): remove column type filter
waleedlatif1 Mar 29, 2026
71794bb
fix(resources): fix filter/sort correctness issues from audit
waleedlatif1 Mar 29, 2026
aeeec6b
fix(chunks): add server-side sort to document chunks API
waleedlatif1 Mar 29, 2026
7b13a1a
perf(resources): memoize filterContent JSX across all resource pages
waleedlatif1 Mar 29, 2026
90bd958
fix(resources): add missing sort options for all visible columns
waleedlatif1 Mar 29, 2026
0318725
whitelabeling updates, sidebar fixes, files bug
waleedlatif1 Mar 29, 2026
432e4d0
increased type safety
waleedlatif1 Mar 29, 2026
f8a26a3
pr fixes
waleedlatif1 Mar 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(resources): fix missing imports, memoization, and stale refs acro…
…ss resource pages
  • Loading branch information
waleedlatif1 committed Mar 29, 2026
commit 483c35cebffc9c1d5040955a485a0ed68af4406f
3 changes: 2 additions & 1 deletion apps/sim/app/workspace/[workspaceId]/files/files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useParams, useRouter } from 'next/navigation'
import {
Button,
Columns2,
Combobox,
type ComboboxOption,
Download,
DropdownMenu,
Expand Down Expand Up @@ -388,7 +389,7 @@ export function Files() {
}
}
},
[workspaceId]
[workspaceId, uploadFile]
)

const handleDownload = useCallback(async (file: WorkspaceFileRecord) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useParams, useRouter, useSearchParams } from 'next/navigation'
import {
Comment thread
waleedlatif1 marked this conversation as resolved.
Badge,
Button,
Combobox,
Modal,
ModalBody,
ModalContent,
Expand All @@ -26,6 +27,7 @@ import type {
ResourceRow,
SearchConfig,
SelectableConfig,
SortConfig,
} from '@/app/workspace/[workspaceId]/components'
import { Resource, ResourceHeader } from '@/app/workspace/[workspaceId]/components'
import {
Expand Down Expand Up @@ -157,8 +159,10 @@ export function Document({
direction: 'asc' | 'desc'
} | null>(null)

const enabledFilterParam =
enabledFilter.length === 1 ? (enabledFilter[0] as 'enabled' | 'disabled') : 'all'
const enabledFilterParam = useMemo(
() => (enabledFilter.length === 1 ? (enabledFilter[0] as 'enabled' | 'disabled') : 'all'),
[enabledFilter]
)

const {
chunks: initialChunks,
Expand Down Expand Up @@ -636,16 +640,18 @@ export function Document({
</div>
)

const filterTags: FilterTag[] = [
...enabledFilter.map((value) => ({
label: `Status: ${value === 'enabled' ? 'Enabled' : 'Disabled'}`,
onRemove: () => {
setEnabledFilter(enabledFilter.filter((v) => v !== value))
setSelectedChunks(new Set())
void goToPage(1)
},
})),
]
const filterTags: FilterTag[] = useMemo(
() =>
enabledFilter.map((value) => ({
label: `Status: ${value === 'enabled' ? 'Enabled' : 'Disabled'}`,
onRemove: () => {
setEnabledFilter((prev) => prev.filter((v) => v !== value))
setSelectedChunks(new Set())
void goToPage(1)
},
})),
[enabledFilter, goToPage]
)

const handleChunkClick = useCallback((rowId: string) => {
setSelectedChunkId(rowId)
Expand Down
94 changes: 50 additions & 44 deletions apps/sim/app/workspace/[workspaceId]/knowledge/[id]/base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -806,22 +806,25 @@ export function KnowledgeBase({
: []),
]

const sortConfig: SortConfig = {
options: [
{ id: 'filename', label: 'Name' },
{ id: 'fileSize', label: 'Size' },
{ id: 'tokenCount', label: 'Tokens' },
{ id: 'chunkCount', label: 'Chunks' },
{ id: 'uploadedAt', label: 'Uploaded' },
{ id: 'enabled', label: 'Status' },
],
active: { column: sortBy, direction: sortOrder },
onSort: (column, direction) => {
setSortBy(column as DocumentSortField)
setSortOrder(direction)
setCurrentPage(1)
},
}
const sortConfig: SortConfig = useMemo(
() => ({
options: [
{ id: 'filename', label: 'Name' },
{ id: 'fileSize', label: 'Size' },
{ id: 'tokenCount', label: 'Tokens' },
{ id: 'chunkCount', label: 'Chunks' },
{ id: 'uploadedAt', label: 'Uploaded' },
{ id: 'enabled', label: 'Status' },
],
active: { column: sortBy, direction: sortOrder },
onSort: (column, direction) => {
setSortBy(column as DocumentSortField)
setSortOrder(direction)
setCurrentPage(1)
},
}),
[sortBy, sortOrder]
)
Comment thread
waleedlatif1 marked this conversation as resolved.

const filterContent = (
<div className='flex w-[240px] flex-col gap-3 p-3'>
Expand Down Expand Up @@ -897,36 +900,39 @@ export function KnowledgeBase({
</>
) : null

const filterTags: FilterTag[] = [
...(enabledFilter.length > 0
? [
{
label:
enabledFilter.length === 1
? `Status: ${enabledFilter[0] === 'enabled' ? 'Enabled' : 'Disabled'}`
: 'Status: 2 selected',
onRemove: () => {
setEnabledFilter([])
setCurrentPage(1)
setSelectedDocuments(new Set())
setIsSelectAllMode(false)
const filterTags: FilterTag[] = useMemo(
() => [
...(enabledFilter.length > 0
? [
{
label:
enabledFilter.length === 1
? `Status: ${enabledFilter[0] === 'enabled' ? 'Enabled' : 'Disabled'}`
: 'Status: 2 selected',
onRemove: () => {
setEnabledFilter([])
setCurrentPage(1)
setSelectedDocuments(new Set())
setIsSelectAllMode(false)
},
},
]
: []),
...tagFilterEntries
.filter((f) => f.tagSlot && f.value.trim())
.map((f) => ({
label: `${f.tagName}: ${f.value}`,
onRemove: () => {
const updated = tagFilterEntries.filter((_, idx) => idx !== tagFilterEntries.indexOf(f))
setTagFilterEntries(updated)
setCurrentPage(1)
setSelectedDocuments(new Set())
setIsSelectAllMode(false)
},
]
: []),
...tagFilterEntries
.filter((f) => f.tagSlot && f.value.trim())
.map((f) => ({
label: `${f.tagName}: ${f.value}`,
onRemove: () => {
const updated = tagFilterEntries.filter((_, idx) => idx !== tagFilterEntries.indexOf(f))
setTagFilterEntries(updated)
setCurrentPage(1)
setSelectedDocuments(new Set())
setIsSelectAllMode(false)
},
})),
]
})),
],
[enabledFilter, tagFilterEntries]
)

const selectableConfig: SelectableConfig = {
selectedIds: selectedDocuments,
Expand Down
11 changes: 6 additions & 5 deletions apps/sim/app/workspace/[workspaceId]/logs/logs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import type {
ResourceColumn,
ResourceRow,
SearchConfig,
SortConfig,
} from '@/app/workspace/[workspaceId]/components'
import {
ResourceHeader,
Expand Down Expand Up @@ -479,12 +480,12 @@ export default function Logs() {
const handleLogContextMenu = useCallback(
(e: React.MouseEvent, rowId: string) => {
e.preventDefault()
const log = logs.find((l) => l.id === rowId) ?? null
const log = sortedLogs.find((l) => l.id === rowId) ?? null
setContextMenuPosition({ x: e.clientX, y: e.clientY })
setContextMenuLog(log)
setContextMenuOpen(true)
},
[logs]
[sortedLogs]
)

const handleCopyExecutionId = useCallback(() => {
Expand Down Expand Up @@ -1072,7 +1073,7 @@ export default function Logs() {
label: 'Export',
icon: Download,
onClick: handleExport,
disabled: !userPermissions.canEdit || isExporting || logs.length === 0,
disabled: !userPermissions.canEdit || isExporting || sortedLogs.length === 0,
},
{
label: 'Notifications',
Expand Down Expand Up @@ -1105,7 +1106,7 @@ export default function Logs() {
handleExport,
userPermissions.canEdit,
isExporting,
logs.length,
sortedLogs.length,
handleOpenNotificationSettings,
]
)
Expand Down Expand Up @@ -1387,7 +1388,7 @@ function LogsFilterPanel({ searchQuery, onSearchQueryChange }: LogsFilterPanelPr
}, [resetFilters, onSearchQueryChange])

return (
<div className='flex flex-col gap-3 p-3'>
<div className='flex w-[240px] flex-col gap-3 p-3'>
<div className='flex flex-col gap-1.5'>
<span className='font-medium text-[var(--text-secondary)] text-caption'>Status</span>
<Combobox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,7 @@
import { useMemo, useState } from 'react'
import { Search } from 'lucide-react'
import { useParams, useRouter } from 'next/navigation'
import {
ArrowUpDown,
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
SModalTabs,
SModalTabsList,
SModalTabsTrigger,
} from '@/components/emcn'
import { Button, Combobox, SModalTabs, SModalTabsList, SModalTabsTrigger } from '@/components/emcn'
import { Input } from '@/components/ui'
import { formatDate } from '@/lib/core/utils/formatting'
import { RESOURCE_REGISTRY } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
Expand Down Expand Up @@ -51,6 +41,8 @@ interface SortConfig {
direction: 'asc' | 'desc'
}

const DEFAULT_SORT: SortConfig = { column: 'deleted', direction: 'desc' }

const SORT_OPTIONS: { column: SortColumn; direction: 'asc' | 'desc'; label: string }[] = [
{ column: 'deleted', direction: 'desc', label: 'Deleted (newest first)' },
{ column: 'name', direction: 'asc', label: 'Name (A–Z)' },
Expand Down Expand Up @@ -214,8 +206,8 @@ export function RecentlyDeleted() {
const normalized = searchTerm.toLowerCase()
items = items.filter((r) => r.name.toLowerCase().includes(normalized))
}
const col = activeSort?.column ?? 'deleted'
const dir = activeSort?.direction ?? 'desc'
const col = (activeSort ?? DEFAULT_SORT).column
const dir = (activeSort ?? DEFAULT_SORT).direction
return [...items].sort((a, b) => {
let cmp = 0
switch (col) {
Expand All @@ -234,6 +226,7 @@ export function RecentlyDeleted() {
}, [resources, activeTab, searchTerm, activeSort])

const showNoResults = searchTerm.trim() && filtered.length === 0 && resources.length > 0
const selectedSort = activeSort ?? DEFAULT_SORT

function handleRestore(resource: DeletedResource) {
setRestoringIds((prev) => new Set(prev).add(resource.id))
Expand Down Expand Up @@ -285,43 +278,27 @@ export function RecentlyDeleted() {
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'
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant='outline'
size='sm'
className='h-[30px] shrink-0 gap-1.5 px-2.5'
disabled={isLoading}
>
<ArrowUpDown className='h-[12px] w-[12px]' />
<span className='text-xs'>
{SORT_OPTIONS.find(
(o) =>
o.column === (activeSort?.column ?? 'deleted') &&
o.direction === (activeSort?.direction ?? 'desc')
)?.label ?? 'Sort'}
</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align='end' className='w-48'>
{SORT_OPTIONS.map((option) => {
const isActive =
(activeSort?.column ?? 'deleted') === option.column &&
(activeSort?.direction ?? 'desc') === option.direction
return (
<DropdownMenuItem
key={`${option.column}-${option.direction}`}
onClick={() =>
setActiveSort({ column: option.column, direction: option.direction })
}
className={isActive ? 'bg-[var(--bg-selected)]' : ''}
>
{option.label}
</DropdownMenuItem>
<div className='w-[190px] shrink-0'>
<Combobox
size='sm'
align='end'
disabled={isLoading}
value={`${selectedSort.column}:${selectedSort.direction}`}
onChange={(value) => {
const option = SORT_OPTIONS.find(
(sortOption) => `${sortOption.column}:${sortOption.direction}` === value
)
})}
</DropdownMenuContent>
</DropdownMenu>
if (option) {
setActiveSort({ column: option.column, direction: option.direction })
}
}}
options={SORT_OPTIONS.map((option) => ({
label: option.label,
value: `${option.column}:${option.direction}`,
}))}
className='h-[30px] rounded-lg border-[var(--border)] bg-transparent px-2.5 text-small dark:bg-[var(--surface-4)]'
/>
</div>
</div>

<SModalTabs value={activeTab} onValueChange={(v) => setActiveTab(v as ResourceType)}>
Expand Down
19 changes: 17 additions & 2 deletions apps/sim/app/workspace/[workspaceId]/tables/tables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,24 @@ export function Tables() {
case 'updated':
cmp = new Date(a.updatedAt).getTime() - new Date(b.updatedAt).getTime()
break
case 'owner': {
const aName = members?.find((m) => m.userId === a.createdBy)?.name ?? ''
const bName = members?.find((m) => m.userId === b.createdBy)?.name ?? ''
cmp = aName.localeCompare(bName)
break
}
}
return dir === 'asc' ? cmp : -cmp
})
}, [tables, debouncedSearchTerm, rowCountFilter, ownerFilter, columnTypeFilter, activeSort])
}, [
tables,
debouncedSearchTerm,
rowCountFilter,
ownerFilter,
columnTypeFilter,
activeSort,
members,
])

const rows: ResourceRow[] = useMemo(
() =>
Expand Down Expand Up @@ -184,6 +198,7 @@ export function Tables() {
{ id: 'columns', label: 'Columns' },
{ id: 'rows', label: 'Rows' },
{ id: 'created', label: 'Created' },
{ id: 'owner', label: 'Owner' },
{ id: 'updated', label: 'Last Updated' },
],
active: activeSort,
Expand Down Expand Up @@ -466,7 +481,7 @@ export function Tables() {
}
}
},
[workspaceId, router]
[workspaceId, router, uploadCsv]
)

const handleListUploadCsv = useCallback(() => {
Expand Down
Loading