Skip to content

Commit 51c9df9

Browse files
committed
feat(knowledge): add content and owner filters to KB list
1 parent f6edb88 commit 51c9df9

File tree

1 file changed

+119
-13
lines changed

1 file changed

+119
-13
lines changed

apps/sim/app/workspace/[workspaceId]/knowledge/knowledge.tsx

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ export function Knowledge() {
107107
direction: 'asc' | 'desc'
108108
} | null>(null)
109109
const [connectorFilter, setConnectorFilter] = useState<'all' | 'connected' | 'unconnected'>('all')
110+
const [contentFilter, setContentFilter] = useState<'all' | 'has-docs' | 'empty'>('all')
111+
const [ownerFilter, setOwnerFilter] = useState<string[]>([])
110112

111113
const [searchInputValue, setSearchInputValue] = useState('')
112114
const debouncedSearchQuery = useDebounce(searchInputValue, 300)
@@ -192,6 +194,18 @@ export function Knowledge() {
192194
)
193195
}
194196

197+
if (contentFilter !== 'all') {
198+
result = result.filter((kb) =>
199+
contentFilter === 'has-docs'
200+
? ((kb as KnowledgeBaseWithDocCount).docCount ?? 0) > 0
201+
: ((kb as KnowledgeBaseWithDocCount).docCount ?? 0) === 0
202+
)
203+
}
204+
205+
if (ownerFilter.length > 0) {
206+
result = result.filter((kb) => ownerFilter.includes(kb.userId))
207+
}
208+
195209
const col = activeSort?.column ?? 'created'
196210
const dir = activeSort?.direction ?? 'desc'
197211
return [...result].sort((a, b) => {
@@ -217,7 +231,14 @@ export function Knowledge() {
217231
}
218232
return dir === 'asc' ? cmp : -cmp
219233
})
220-
}, [knowledgeBases, debouncedSearchQuery, connectorFilter, activeSort])
234+
}, [
235+
knowledgeBases,
236+
debouncedSearchQuery,
237+
connectorFilter,
238+
contentFilter,
239+
ownerFilter,
240+
activeSort,
241+
])
221242

222243
const rows: ResourceRow[] = useMemo(
223244
() =>
@@ -375,21 +396,106 @@ export function Knowledge() {
375396
</button>
376397
))}
377398
</div>
399+
<div className='border-[var(--border-1)] border-t border-b px-3 py-2'>
400+
<span className='font-medium text-[var(--text-secondary)] text-caption'>Content</span>
401+
</div>
402+
<div className='flex flex-col gap-0.5 px-3 py-2'>
403+
{(
404+
[
405+
{ value: 'all', label: 'All' },
406+
{ value: 'has-docs', label: 'Has documents' },
407+
{ value: 'empty', label: 'Empty' },
408+
] as const
409+
).map(({ value, label }) => (
410+
<button
411+
key={value}
412+
type='button'
413+
className={cn(
414+
'flex w-full cursor-pointer select-none items-center rounded-[5px] px-2 py-[5px] font-medium text-[var(--text-secondary)] text-caption outline-none transition-colors hover-hover:bg-[var(--surface-active)]',
415+
contentFilter === value && 'bg-[var(--surface-active)]'
416+
)}
417+
onClick={() => setContentFilter(value)}
418+
>
419+
{label}
420+
</button>
421+
))}
422+
</div>
423+
{members && members.length > 0 && (
424+
<>
425+
<div className='border-[var(--border-1)] border-t border-b px-3 py-2'>
426+
<span className='font-medium text-[var(--text-secondary)] text-caption'>Owner</span>
427+
</div>
428+
<div className='flex flex-col gap-0.5 px-3 py-2'>
429+
<button
430+
type='button'
431+
className={cn(
432+
'flex w-full cursor-pointer select-none items-center rounded-[5px] px-2 py-[5px] font-medium text-[var(--text-secondary)] text-caption outline-none transition-colors hover-hover:bg-[var(--surface-active)]',
433+
ownerFilter.length === 0 && 'bg-[var(--surface-active)]'
434+
)}
435+
onClick={() => setOwnerFilter([])}
436+
>
437+
All
438+
</button>
439+
{members.map((member) => (
440+
<button
441+
key={member.userId}
442+
type='button'
443+
className={cn(
444+
'flex w-full cursor-pointer select-none items-center gap-1.5 rounded-[5px] px-2 py-[5px] font-medium text-[var(--text-secondary)] text-caption outline-none transition-colors hover-hover:bg-[var(--surface-active)]',
445+
ownerFilter.includes(member.userId) && 'bg-[var(--surface-active)]'
446+
)}
447+
onClick={() =>
448+
setOwnerFilter((prev) =>
449+
prev.includes(member.userId)
450+
? prev.filter((id) => id !== member.userId)
451+
: [...prev, member.userId]
452+
)
453+
}
454+
>
455+
{member.image ? (
456+
<img
457+
src={member.image}
458+
alt={member.name}
459+
referrerPolicy='no-referrer'
460+
className='h-[14px] w-[14px] shrink-0 rounded-full border border-[var(--border)] object-cover'
461+
/>
462+
) : (
463+
<span className='flex h-[14px] w-[14px] shrink-0 items-center justify-center rounded-full border border-[var(--border)] bg-[var(--surface-3)] font-medium text-[8px] text-[var(--text-secondary)]'>
464+
{member.name.charAt(0).toUpperCase()}
465+
</span>
466+
)}
467+
<span className='truncate'>{member.name}</span>
468+
</button>
469+
))}
470+
</div>
471+
</>
472+
)}
378473
</div>
379474
)
380475

381-
const filterTags: FilterTag[] = useMemo(
382-
() =>
383-
connectorFilter === 'all'
384-
? []
385-
: [
386-
{
387-
label: connectorFilter === 'connected' ? 'Connectors: Active' : 'Connectors: None',
388-
onRemove: () => setConnectorFilter('all'),
389-
},
390-
],
391-
[connectorFilter]
392-
)
476+
const filterTags: FilterTag[] = useMemo(() => {
477+
const tags: FilterTag[] = []
478+
if (connectorFilter !== 'all') {
479+
tags.push({
480+
label: connectorFilter === 'connected' ? 'Connectors: Active' : 'Connectors: None',
481+
onRemove: () => setConnectorFilter('all'),
482+
})
483+
}
484+
if (contentFilter !== 'all') {
485+
tags.push({
486+
label: contentFilter === 'has-docs' ? 'Content: Has documents' : 'Content: Empty',
487+
onRemove: () => setContentFilter('all'),
488+
})
489+
}
490+
if (ownerFilter.length > 0) {
491+
const label =
492+
ownerFilter.length === 1
493+
? `Owner: ${members?.find((m) => m.userId === ownerFilter[0])?.name ?? '1 member'}`
494+
: `Owner: ${ownerFilter.length} members`
495+
tags.push({ label, onRemove: () => setOwnerFilter([]) })
496+
}
497+
return tags
498+
}, [connectorFilter, contentFilter, ownerFilter, members])
393499

394500
return (
395501
<>

0 commit comments

Comments
 (0)