Skip to content

Commit b371364

Browse files
authored
feat(resources): add sort and filter to all resource list pages (#3834)
* improvement(tables): improve table filtering UX - Replace popover filter with persistent inline panel below toolbar - Add AND/OR toggle between filter rules (shown in Where label slot) - Sync filter panel state from applied filter on open - Show filter button active state when filter is applied or panel is open - Use readable operator labels matching dropdown options - Add Clear filters button (shown only when filter is active) - Close filter panel when last rule is removed via X - Fix empty gap rows appearing in filtered results by skipping position gap rendering when filter is active - Add toggle mode to ResourceOptionsBar for inline panel pattern - Memoize FilterRuleRow for perf, fix filterTags key collision, remove dead filterActiveCount prop * fix(table-filter): use ref to stabilize handleRemove/handleApply callbacks Reading rules via ref instead of closure eliminates rules from useCallback dependency arrays, keeping callbacks stable across rule edits and preserving the memo() benefit on FilterRuleRow. * improvement(tables,kb): remove hacky patterns, fix KB filter popover width - Remove non-TSDoc comment from table-filter (rulesRef pattern is self-evident) - Simplify SearchSection: remove setState-during-render anti-pattern; controlled input binds directly to search.value/onChange (simpler and correct) - Reduce KB filter popover from w-[320px] to w-[200px]; tag filter uses vertical layout so narrow width works; Status-only case is now appropriately compact * feat(knowledge): add sort and filter to KB list page Sort dropdown: name, documents, tokens, created, last updated — pre-sorted externally before passing rows to Resource. Active sort highlights the Sort button; clear resets to default (created desc). Filter popover: filter by connector status (All / With connectors / Without connectors). Active filter shown as a removable tag in the toolbar. * feat(files): add sort and filter to files list page * feat(scheduled-tasks): add sort and filter to scheduled tasks page * fix(table-filter): use explicit close handler instead of toggle * improvement(files,knowledge): replace manual debounce with useDebounce hook and use type guards for file filtering * fix(resource): prevent popover from inheriting anchor min-width * feat(tables): add sort to tables list page * feat(knowledge): add content and owner filters to KB list * feat(scheduled-tasks): add status and health filters * feat(files): add size and uploaded-by filters to files list * feat(tables): add row count, owner, and column type filters * improvement(scheduled-tasks): use combobox filter panel matching logs UI style * improvement(knowledge): use combobox filter panel matching logs UI style * improvement(files): use combobox filter panel matching logs UI style Replaces button-list filters with Combobox-based multi-select sections for file type, size, and uploaded-by filters, aligning the panel with the logs page filter UI. * improvement(tables): use combobox filter panel matching logs UI style * 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). * feat(logs): add sort to logs page * improvement(knowledge): upgrade document list filter to combobox style * fix(resources): fix missing imports, memoization, and stale refs across resource pages * improvement(tables): remove column type filter * fix(resources): fix filter/sort correctness issues from audit * fix(chunks): add server-side sort to document chunks API Chunk sort was previously done client-side on a single page of server-paginated data, which only reordered the current page. Now sort params (sortBy, sortOrder) flow through the full stack: types → service → API route → query hook → useDocumentChunks → document.tsx. * perf(resources): memoize filterContent JSX across all resource pages Resource is wrapped in React.memo, so an unstable filterContent reference on every parent re-render defeats the memo. Wrap filterContent in useMemo with correct deps in all 6 pages (files, tables, scheduled-tasks, knowledge, base, document). * fix(resources): add missing sort options for all visible columns Every column visible in a resource table should be sortable. Three pages had visible columns with no sort support: - files.tsx: add 'owner' sort (member name lookup) - scheduled-tasks.tsx: add 'schedule' sort (localeCompare on description) - knowledge.tsx: add 'connectors' (count) and 'owner' (member name) sorts Also add 'members' to processedKBs deps in knowledge.tsx since owner sort now reads member names inside the memo. * whitelabeling updates, sidebar fixes, files bug * increased type safety * pr fixes
1 parent b9b930b commit b371364

File tree

34 files changed

+2067
-801
lines changed

34 files changed

+2067
-801
lines changed
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
/** Shared className for primary auth form submit buttons across all auth pages. */
2-
export const AUTH_SUBMIT_BTN =
3-
'inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50' as const
1+
/** Shared className for primary auth/status CTA buttons on dark auth surfaces. */
2+
export const AUTH_PRIMARY_CTA_BASE =
3+
'inline-flex h-[32px] items-center justify-center gap-2 rounded-[5px] border border-[var(--auth-primary-btn-border)] bg-[var(--auth-primary-btn-bg)] px-2.5 font-[430] font-season text-[var(--auth-primary-btn-text)] text-sm transition-colors hover:border-[var(--auth-primary-btn-hover-border)] hover:bg-[var(--auth-primary-btn-hover-bg)] hover:text-[var(--auth-primary-btn-hover-text)] disabled:cursor-not-allowed disabled:opacity-50' as const
4+
5+
/** Full-width variant used for primary auth form submit buttons. */
6+
export const AUTH_SUBMIT_BTN = `${AUTH_PRIMARY_CTA_BASE} w-full` as const

apps/sim/app/_styles/globals.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515
--toolbar-triggers-height: 300px; /* TOOLBAR_TRIGGERS_HEIGHT.DEFAULT */
1616
--editor-connections-height: 172px; /* EDITOR_CONNECTIONS_HEIGHT.DEFAULT */
1717
--terminal-height: 206px; /* TERMINAL_HEIGHT.DEFAULT */
18+
--auth-primary-btn-bg: #ffffff;
19+
--auth-primary-btn-border: #ffffff;
20+
--auth-primary-btn-text: #000000;
21+
--auth-primary-btn-hover-bg: #e0e0e0;
22+
--auth-primary-btn-hover-border: #e0e0e0;
23+
--auth-primary-btn-hover-text: #000000;
1824

1925
/* z-index scale for layered UI
2026
Popover must be above modal so dropdowns inside modals render correctly */

apps/sim/app/api/knowledge/[id]/documents/[documentId]/chunks/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const GetChunksQuerySchema = z.object({
1515
enabled: z.enum(['true', 'false', 'all']).optional().default('all'),
1616
limit: z.coerce.number().min(1).max(100).optional().default(50),
1717
offset: z.coerce.number().min(0).optional().default(0),
18+
sortBy: z.enum(['chunkIndex', 'tokenCount', 'enabled']).optional().default('chunkIndex'),
19+
sortOrder: z.enum(['asc', 'desc']).optional().default('asc'),
1820
})
1921

2022
const CreateChunkSchema = z.object({
@@ -88,6 +90,8 @@ export async function GET(
8890
enabled: searchParams.get('enabled') || undefined,
8991
limit: searchParams.get('limit') || undefined,
9092
offset: searchParams.get('offset') || undefined,
93+
sortBy: searchParams.get('sortBy') || undefined,
94+
sortOrder: searchParams.get('sortOrder') || undefined,
9195
})
9296

9397
const result = await queryChunks(documentId, queryParams, requestId)

apps/sim/app/chat/components/auth/email/email-auth.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ export default function EmailAuth({ identifier, onAuthSuccess }: EmailAuthProps)
293293
<button
294294
onClick={() => handleVerifyOtp()}
295295
disabled={otpValue.length !== 6 || isVerifyingOtp}
296-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
296+
className={AUTH_SUBMIT_BTN}
297297
>
298298
{isVerifyingOtp ? (
299299
<span className='flex items-center gap-2'>

apps/sim/app/form/[identifier]/components/error-state.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

33
import { useRouter } from 'next/navigation'
4+
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
45
import { StatusPageLayout } from '@/app/(auth)/components/status-page-layout'
56

67
interface FormErrorStateProps {
@@ -12,10 +13,7 @@ export function FormErrorState({ error }: FormErrorStateProps) {
1213

1314
return (
1415
<StatusPageLayout title='Form Unavailable' description={error}>
15-
<button
16-
onClick={() => router.push('/workspace')}
17-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
18-
>
16+
<button onClick={() => router.push('/workspace')} className={AUTH_SUBMIT_BTN}>
1917
Return to Workspace
2018
</button>
2119
</StatusPageLayout>

apps/sim/app/form/[identifier]/components/password-auth.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Eye, EyeOff, Loader2 } from 'lucide-react'
55
import { Input, Label } from '@/components/emcn'
66
import { cn } from '@/lib/core/utils/cn'
77
import AuthBackground from '@/app/(auth)/components/auth-background'
8+
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
89
import { SupportFooter } from '@/app/(auth)/components/support-footer'
910
import Navbar from '@/app/(home)/components/navbar/navbar'
1011

@@ -75,7 +76,7 @@ export function PasswordAuth({ onSubmit, error }: PasswordAuthProps) {
7576
<button
7677
type='submit'
7778
disabled={!password.trim() || isSubmitting}
78-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
79+
className={AUTH_SUBMIT_BTN}
7980
>
8081
{isSubmitting ? (
8182
<span className='flex items-center gap-2'>

apps/sim/app/form/[identifier]/error.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import { useEffect } from 'react'
44
import { createLogger } from '@sim/logger'
5+
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
56
import { StatusPageLayout } from '@/app/(auth)/components/status-page-layout'
67

78
const logger = createLogger('FormError')
@@ -21,10 +22,7 @@ export default function FormError({ error, reset }: FormErrorProps) {
2122
title='Something went wrong'
2223
description='We encountered an error loading this form. Please try again.'
2324
>
24-
<button
25-
onClick={reset}
26-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
27-
>
25+
<button onClick={reset} className={AUTH_SUBMIT_BTN}>
2826
Try again
2927
</button>
3028
</StatusPageLayout>

apps/sim/app/form/[identifier]/form.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createLogger } from '@sim/logger'
55
import { Loader2 } from 'lucide-react'
66
import { martianMono } from '@/app/_styles/fonts/martian-mono/martian-mono'
77
import AuthBackground from '@/app/(auth)/components/auth-background'
8+
import { AUTH_SUBMIT_BTN } from '@/app/(auth)/components/auth-button-classes'
89
import { SupportFooter } from '@/app/(auth)/components/support-footer'
910
import Navbar from '@/app/(home)/components/navbar/navbar'
1011
import {
@@ -322,11 +323,7 @@ export default function Form({ identifier }: { identifier: string }) {
322323
)}
323324

324325
{fields.length > 0 && (
325-
<button
326-
type='submit'
327-
disabled={isSubmitting}
328-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
329-
>
326+
<button type='submit' disabled={isSubmitting} className={AUTH_SUBMIT_BTN}>
330327
{isSubmitting ? (
331328
<span className='flex items-center gap-2'>
332329
<Loader2 className='h-4 w-4 animate-spin' />

apps/sim/app/invite/components/status-card.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { Loader2 } from 'lucide-react'
44
import { useRouter } from 'next/navigation'
55
import { cn } from '@/lib/core/utils/cn'
6+
import { AUTH_PRIMARY_CTA_BASE } from '@/app/(auth)/components/auth-button-classes'
67

78
interface InviteStatusCardProps {
89
type: 'login' | 'loading' | 'error' | 'success' | 'invitation' | 'warning'
@@ -55,10 +56,7 @@ export function InviteStatusCard({
5556

5657
<div className='mt-8 w-full max-w-[410px] space-y-3'>
5758
{isExpiredError && (
58-
<button
59-
onClick={() => router.push('/')}
60-
className='inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50'
61-
>
59+
<button onClick={() => router.push('/')} className={`${AUTH_PRIMARY_CTA_BASE} w-full`}>
6260
Request New Invitation
6361
</button>
6462
)}
@@ -69,9 +67,9 @@ export function InviteStatusCard({
6967
onClick={action.onClick}
7068
disabled={action.disabled || action.loading}
7169
className={cn(
72-
'inline-flex h-[32px] w-full items-center justify-center gap-2 rounded-[5px] border border-white bg-white px-2.5 font-[430] font-season text-black text-sm transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)] disabled:cursor-not-allowed disabled:opacity-50',
70+
`${AUTH_PRIMARY_CTA_BASE} w-full`,
7371
index !== 0 &&
74-
'border-[var(--landing-border-strong)] bg-transparent text-[var(--landing-text)] hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)]'
72+
'border-[var(--landing-border-strong)] bg-transparent text-[var(--landing-text)] hover:border-[var(--landing-border-strong)] hover:bg-[var(--landing-bg-elevated)] hover:text-[var(--landing-text)]'
7573
)}
7674
>
7775
{action.loading ? (

apps/sim/app/not-found.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,14 @@ import type { Metadata } from 'next'
22
import Link from 'next/link'
33
import { getNavBlogPosts } from '@/lib/blog/registry'
44
import AuthBackground from '@/app/(auth)/components/auth-background'
5+
import { AUTH_PRIMARY_CTA_BASE } from '@/app/(auth)/components/auth-button-classes'
56
import Navbar from '@/app/(home)/components/navbar/navbar'
67

78
export const metadata: Metadata = {
89
title: 'Page Not Found',
910
robots: { index: false, follow: true },
1011
}
1112

12-
const CTA_BASE =
13-
'inline-flex items-center h-[32px] rounded-[5px] border px-2.5 font-[430] font-season text-sm'
14-
1513
export default async function NotFound() {
1614
const blogPosts = await getNavBlogPosts()
1715
return (
@@ -29,10 +27,7 @@ export default async function NotFound() {
2927
The page you&apos;re looking for doesn&apos;t exist or has been moved.
3028
</p>
3129
<div className='mt-3 flex items-center gap-2'>
32-
<Link
33-
href='/'
34-
className={`${CTA_BASE} gap-2 border-white bg-white text-black transition-colors hover:border-[var(--border-1)] hover:bg-[var(--border-1)]`}
35-
>
30+
<Link href='/' className={AUTH_PRIMARY_CTA_BASE}>
3631
Return to Home
3732
</Link>
3833
</div>

0 commit comments

Comments
 (0)