@@ -8,6 +8,8 @@ import Typography from '@mui/joy/Typography';
88import Box from '@mui/joy/Box' ;
99import IconButton from '@mui/joy/IconButton' ;
1010import Button from '@mui/joy/Button' ;
11+ import Select from '@mui/joy/Select' ;
12+ import Option from '@mui/joy/Option' ;
1113import { useMemo , useState } from 'react' ;
1214import {
1315 RectangleVerticalIcon ,
@@ -35,29 +37,49 @@ export default function TaskLibrary() {
3537 // local overlay for create/edit/delete without waiting for refetch
3638 const [ overlayTasks , setOverlayTasks ] = useState < any [ ] > ( [ ] ) ;
3739
40+ // Filtering state
41+ const [ sourceFilter , setSourceFilter ] = useState < 'all' | 'gallery' | 'local' > ( 'all' ) ;
42+ const [ tagFilter , setTagFilter ] = useState < string > ( 'all' ) ;
43+
3844 const tasks = useMemo ( ( ) => {
3945 const localGallery : any [ ] = localGalleryResp ?. data ?? [ ] ;
4046 const remoteGallery : any [ ] = remoteGalleryResp ?. data ?? [ ] ;
4147
42- const localGalleryMapped = localGallery . map ( ( g : any ) => ( {
43- id : `local:${ g . subdir || g . id || g . name } ` ,
44- title : g . name || g . title || g . task_name || 'Task' ,
45- description : g . description || '' ,
46- _isLocal : true ,
47- _subdir : g . subdir ,
48- } ) ) ;
49-
50- const remoteGalleryMapped = remoteGallery . map ( ( g : any ) => ( {
51- id : `gallery:${ g . subdir || g . id || g . name } ` ,
52- title : g . name || g . title || g . task_name || 'Task' ,
53- description : g . description || '' ,
54- _isGallery : true ,
55- _subdir : g . subdir ,
56- } ) ) ;
48+ const localGalleryMapped = localGallery . map ( ( g : any ) => ( {
49+ id : `local:${ g . subdir || g . id || g . name } ` ,
50+ title : g . name || g . title || g . task_name || 'Task' ,
51+ description : g . description || '' ,
52+ _isLocal : true ,
53+ _subdir : g . subdir ,
54+ _tag : g . tag || 'OTHER' ,
55+ } ) ) ;
56+
57+ const remoteGalleryMapped = remoteGallery . map ( ( g : any ) => ( {
58+ id : `gallery:${ g . subdir || g . id || g . name } ` ,
59+ title : g . name || g . title || g . task_name || 'Task' ,
60+ description : g . description || '' ,
61+ _isGallery : true ,
62+ _subdir : g . subdir ,
63+ _tag : g . tag || 'OTHER' ,
64+ } ) ) ;
5765
5866 return [ ...localGalleryMapped , ...remoteGalleryMapped , ...overlayTasks ] ;
5967 } , [ localGalleryResp , remoteGalleryResp , overlayTasks ] ) ;
6068
69+ // Filter tasks based on source and tag filters
70+ const filteredTasks = useMemo ( ( ) => {
71+ return tasks . filter ( ( task ) => {
72+ // Source filter
73+ if ( sourceFilter === 'gallery' && ! task . _isGallery ) return false ;
74+ if ( sourceFilter === 'local' && ! task . _isLocal ) return false ;
75+
76+ // Tag filter
77+ if ( tagFilter !== 'all' && task . _tag !== tagFilter ) return false ;
78+
79+ return true ;
80+ } ) ;
81+ } , [ tasks , sourceFilter , tagFilter ] ) ;
82+
6183 // modal state to show TaskModal when creating/viewing a task
6284 const [ modalOpen , setModalOpen ] = useState ( false ) ;
6385 const [ modalTask , setModalTask ] = useState < any | null > ( null ) ;
@@ -207,14 +229,51 @@ export default function TaskLibrary() {
207229 </ Button >
208230 </ Box >
209231 </ Box >
232+
233+ { /* Filter Controls */ }
234+ < Box sx = { { px : 2 , py : 1 , display : 'flex' , gap : 2 , alignItems : 'center' } } >
235+ < Box sx = { { display : 'flex' , gap : 1 , alignItems : 'center' } } >
236+ < Typography level = "body-sm" sx = { { minWidth : 'fit-content' } } >
237+ Source:
238+ </ Typography >
239+ < Select
240+ value = { sourceFilter }
241+ onChange = { ( _ , value ) => setSourceFilter ( value || 'all' ) }
242+ size = "sm"
243+ sx = { { minWidth : 100 } }
244+ >
245+ < Option value = "all" > All</ Option >
246+ < Option value = "gallery" > Gallery</ Option >
247+ < Option value = "local" > Local</ Option >
248+ </ Select >
249+ </ Box >
250+
251+ < Box sx = { { display : 'flex' , gap : 1 , alignItems : 'center' } } >
252+ < Typography level = "body-sm" sx = { { minWidth : 'fit-content' } } >
253+ Tag:
254+ </ Typography >
255+ < Select
256+ value = { tagFilter }
257+ onChange = { ( _ , value ) => setTagFilter ( value || 'all' ) }
258+ size = "sm"
259+ sx = { { minWidth : 100 } }
260+ >
261+ < Option value = "all" > All</ Option >
262+ < Option value = "TRAIN" > TRAIN</ Option >
263+ < Option value = "EVAL" > EVAL</ Option >
264+ < Option value = "OTHER" > OTHER</ Option >
265+ </ Select >
266+ </ Box >
267+ </ Box >
268+
210269 < List
211270 sx = { {
212271 overflow : 'auto' ,
213272 gap : 1 ,
214273 pt : 2 ,
215274 } }
216275 >
217- { tasks . map ( ( task : any ) => (
276+ { filteredTasks . map ( ( task : any ) => (
218277 < ListItem
219278 key = { task . id }
220279 sx = { {
@@ -234,20 +293,27 @@ export default function TaskLibrary() {
234293 < Typography level = "body-sm" textColor = "text.tertiary" >
235294 { task . description }
236295 </ Typography >
237- { task . _isLocal && (
238- < Box sx = { { mt : 0.5 } } >
296+ < Box sx = { { mt : 0.5 , display : 'flex' , gap : 0.5 , flexWrap : 'wrap' } } >
297+ { task . _isLocal && (
239298 < Chip size = "sm" color = "success" variant = "soft" >
240299 Local
241300 </ Chip >
242- </ Box >
243- ) }
244- { task . _isGallery && (
245- < Box sx = { { mt : 0.5 } } >
301+ ) }
302+ { task . _isGallery && (
246303 < Chip size = "sm" color = "primary" variant = "soft" >
247304 Gallery
248305 </ Chip >
249- </ Box >
250- ) }
306+ ) }
307+ { task . _tag && (
308+ < Chip
309+ size = "sm"
310+ color = { task . _tag === 'TRAIN' ? 'warning' : task . _tag === 'EVAL' ? 'info' : 'neutral' }
311+ variant = "soft"
312+ >
313+ { task . _tag }
314+ </ Chip >
315+ ) }
316+ </ Box >
251317 </ ListItemContent >
252318
253319 < Box
0 commit comments