Skip to content

Commit a3ffc2f

Browse files
committed
feat(files): add size and uploaded-by filters to files list
1 parent 12ea734 commit a3ffc2f

1 file changed

Lines changed: 111 additions & 18 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/files

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

Lines changed: 111 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ export function Files() {
174174
direction: 'asc' | 'desc'
175175
} | null>(null)
176176
const [typeFilter, setTypeFilter] = useState<'all' | 'document' | 'audio' | 'video'>('all')
177+
const [sizeFilter, setSizeFilter] = useState<'all' | 'small' | 'medium' | 'large'>('all')
178+
const [uploadedByFilter, setUploadedByFilter] = useState<string[]>([])
177179

178180
const [creatingFile, setCreatingFile] = useState(false)
179181
const [isDirty, setIsDirty] = useState(false)
@@ -223,6 +225,18 @@ export function Files() {
223225
})
224226
}
225227

228+
if (sizeFilter !== 'all') {
229+
result = result.filter((f) => {
230+
if (sizeFilter === 'small') return f.size < 1_048_576
231+
if (sizeFilter === 'medium') return f.size >= 1_048_576 && f.size <= 10_485_760
232+
return f.size > 10_485_760 // large
233+
})
234+
}
235+
236+
if (uploadedByFilter.length > 0) {
237+
result = result.filter((f) => uploadedByFilter.includes(f.uploadedBy))
238+
}
239+
226240
const col = activeSort?.column ?? 'created'
227241
const dir = activeSort?.direction ?? 'desc'
228242
return [...result].sort((a, b) => {
@@ -244,7 +258,7 @@ export function Files() {
244258
}
245259
return dir === 'asc' ? cmp : -cmp
246260
})
247-
}, [files, debouncedSearchTerm, typeFilter, activeSort])
261+
}, [files, debouncedSearchTerm, typeFilter, sizeFilter, uploadedByFilter, activeSort])
248262

249263
const rowCacheRef = useRef(
250264
new Map<string, { row: ResourceRow; file: WorkspaceFileRecord; members: typeof members }>()
@@ -831,26 +845,105 @@ export function Files() {
831845
</button>
832846
))}
833847
</div>
848+
<div className='border-[var(--border-1)] border-t border-b px-3 py-2'>
849+
<span className='font-medium text-[var(--text-secondary)] text-caption'>Size</span>
850+
</div>
851+
<div className='flex flex-col gap-0.5 px-3 py-2'>
852+
{(
853+
[
854+
{ value: 'all', label: 'All' },
855+
{ value: 'small', label: 'Small (< 1 MB)' },
856+
{ value: 'medium', label: 'Medium (1–10 MB)' },
857+
{ value: 'large', label: 'Large (> 10 MB)' },
858+
] as const
859+
).map(({ value, label }) => (
860+
<button
861+
key={value}
862+
type='button'
863+
className={cn(
864+
'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)]',
865+
sizeFilter === value && 'bg-[var(--surface-active)]'
866+
)}
867+
onClick={() => setSizeFilter(value)}
868+
>
869+
{label}
870+
</button>
871+
))}
872+
</div>
873+
{members && members.length > 0 && (
874+
<>
875+
<div className='border-[var(--border-1)] border-t border-b px-3 py-2'>
876+
<span className='font-medium text-[var(--text-secondary)] text-caption'>
877+
Uploaded By
878+
</span>
879+
</div>
880+
<div className='flex flex-col gap-0.5 px-3 py-2'>
881+
<button
882+
type='button'
883+
className={cn(
884+
'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)]',
885+
uploadedByFilter.length === 0 && 'bg-[var(--surface-active)]'
886+
)}
887+
onClick={() => setUploadedByFilter([])}
888+
>
889+
All
890+
</button>
891+
{members.map((member) => (
892+
<button
893+
key={member.userId}
894+
type='button'
895+
className={cn(
896+
'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)]',
897+
uploadedByFilter.includes(member.userId) && 'bg-[var(--surface-active)]'
898+
)}
899+
onClick={() =>
900+
setUploadedByFilter((prev) =>
901+
prev.includes(member.userId)
902+
? prev.filter((id) => id !== member.userId)
903+
: [...prev, member.userId]
904+
)
905+
}
906+
>
907+
{member.image ? (
908+
<img
909+
src={member.image}
910+
alt={member.name}
911+
referrerPolicy='no-referrer'
912+
className='h-[14px] w-[14px] shrink-0 rounded-full border border-[var(--border)] object-cover'
913+
/>
914+
) : (
915+
<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)]'>
916+
{member.name.charAt(0).toUpperCase()}
917+
</span>
918+
)}
919+
<span className='truncate'>{member.name}</span>
920+
</button>
921+
))}
922+
</div>
923+
</>
924+
)}
834925
</div>
835926
)
836927

837-
const filterTags: FilterTag[] = useMemo(
838-
() =>
839-
typeFilter === 'all'
840-
? []
841-
: [
842-
{
843-
label:
844-
typeFilter === 'document'
845-
? 'Type: Documents'
846-
: typeFilter === 'audio'
847-
? 'Type: Audio'
848-
: 'Type: Video',
849-
onRemove: () => setTypeFilter('all'),
850-
},
851-
],
852-
[typeFilter]
853-
)
928+
const filterTags: FilterTag[] = useMemo(() => {
929+
const tags: FilterTag[] = []
930+
if (typeFilter !== 'all') {
931+
const labels = { document: 'Type: Documents', audio: 'Type: Audio', video: 'Type: Video' }
932+
tags.push({ label: labels[typeFilter], onRemove: () => setTypeFilter('all') })
933+
}
934+
if (sizeFilter !== 'all') {
935+
const labels = { small: 'Size: Small', medium: 'Size: Medium', large: 'Size: Large' }
936+
tags.push({ label: labels[sizeFilter], onRemove: () => setSizeFilter('all') })
937+
}
938+
if (uploadedByFilter.length > 0) {
939+
const label =
940+
uploadedByFilter.length === 1
941+
? `Uploaded by: ${members?.find((m) => m.userId === uploadedByFilter[0])?.name ?? '1 member'}`
942+
: `Uploaded by: ${uploadedByFilter.length} members`
943+
tags.push({ label, onRemove: () => setUploadedByFilter([]) })
944+
}
945+
return tags
946+
}, [typeFilter, sizeFilter, uploadedByFilter, members])
854947

855948
if (fileIdFromRoute && !selectedFile) {
856949
return (

0 commit comments

Comments
 (0)