@@ -288,6 +288,10 @@ export default function Logs() {
288288 const activeLogRefetchRef = useRef < ( ) => void > ( ( ) => { } )
289289 const logsQueryRef = useRef ( { isFetching : false , hasNextPage : false , fetchNextPage : ( ) => { } } )
290290 const [ isNotificationSettingsOpen , setIsNotificationSettingsOpen ] = useState ( false )
291+ const [ activeSort , setActiveSort ] = useState < {
292+ column : string
293+ direction : 'asc' | 'desc'
294+ } | null > ( null )
291295 const userPermissions = useUserPermissionsContext ( )
292296
293297 const [ contextMenuOpen , setContextMenuOpen ] = useState ( false )
@@ -358,11 +362,43 @@ export default function Logs() {
358362 return logsQuery . data . pages . flatMap ( ( page ) => page . logs )
359363 } , [ logsQuery . data ?. pages ] )
360364
365+ const sortedLogs = useMemo ( ( ) => {
366+ if ( ! activeSort ) return logs
367+
368+ const { column, direction } = activeSort
369+ return [ ...logs ] . sort ( ( a , b ) => {
370+ let cmp = 0
371+ switch ( column ) {
372+ case 'date' :
373+ cmp = new Date ( a . createdAt ) . getTime ( ) - new Date ( b . createdAt ) . getTime ( )
374+ break
375+ case 'duration' : {
376+ const aDuration = parseDuration ( { duration : a . duration ?? undefined } ) ?? - 1
377+ const bDuration = parseDuration ( { duration : b . duration ?? undefined } ) ?? - 1
378+ cmp = aDuration - bDuration
379+ break
380+ }
381+ case 'cost' : {
382+ const aCost = typeof a . cost ?. total === 'number' ? a . cost . total : - 1
383+ const bCost = typeof b . cost ?. total === 'number' ? b . cost . total : - 1
384+ cmp = aCost - bCost
385+ break
386+ }
387+ case 'status' :
388+ cmp = ( a . status ?? '' ) . localeCompare ( b . status ?? '' )
389+ break
390+ default :
391+ break
392+ }
393+ return direction === 'asc' ? cmp : - cmp
394+ } )
395+ } , [ logs , activeSort ] )
396+
361397 const selectedLogIndex = useMemo (
362- ( ) => ( selectedLogId ? logs . findIndex ( ( l ) => l . id === selectedLogId ) : - 1 ) ,
363- [ logs , selectedLogId ]
398+ ( ) => ( selectedLogId ? sortedLogs . findIndex ( ( l ) => l . id === selectedLogId ) : - 1 ) ,
399+ [ sortedLogs , selectedLogId ]
364400 )
365- const selectedLogFromList = selectedLogIndex >= 0 ? logs [ selectedLogIndex ] : null
401+ const selectedLogFromList = selectedLogIndex >= 0 ? sortedLogs [ selectedLogIndex ] : null
366402
367403 const selectedLog = useMemo ( ( ) => {
368404 if ( ! selectedLogFromList ) return null
@@ -381,8 +417,8 @@ export default function Logs() {
381417 useFolders ( workspaceId )
382418
383419 useEffect ( ( ) => {
384- logsRef . current = logs
385- } , [ logs ] )
420+ logsRef . current = sortedLogs
421+ } , [ sortedLogs ] )
386422 useEffect ( ( ) => {
387423 selectedLogIndexRef . current = selectedLogIndex
388424 } , [ selectedLogIndex ] )
@@ -659,7 +695,7 @@ export default function Logs() {
659695
660696 const rows : ResourceRow [ ] = useMemo (
661697 ( ) =>
662- logs . map ( ( log ) => {
698+ sortedLogs . map ( ( log ) => {
663699 const formattedDate = formatDate ( log . createdAt )
664700 const displayStatus = getDisplayStatus ( log . status )
665701 const isMothershipJob = log . trigger === 'mothership'
@@ -710,7 +746,7 @@ export default function Logs() {
710746 } ,
711747 }
712748 } ) ,
713- [ logs ]
749+ [ sortedLogs ]
714750 )
715751
716752 const sidebarOverlay = useMemo (
@@ -721,7 +757,7 @@ export default function Logs() {
721757 onClose = { handleCloseSidebar }
722758 onNavigateNext = { handleNavigateNext }
723759 onNavigatePrev = { handleNavigatePrev }
724- hasNext = { selectedLogIndex < logs . length - 1 }
760+ hasNext = { selectedLogIndex < sortedLogs . length - 1 }
725761 hasPrev = { selectedLogIndex > 0 }
726762 />
727763 ) ,
@@ -732,7 +768,7 @@ export default function Logs() {
732768 handleNavigateNext ,
733769 handleNavigatePrev ,
734770 selectedLogIndex ,
735- logs . length ,
771+ sortedLogs . length ,
736772 ]
737773 )
738774
@@ -978,6 +1014,21 @@ export default function Logs() {
9781014 [ appliedFilters , textSearch , removeBadge , handleFiltersChange ]
9791015 )
9801016
1017+ const sortConfig = useMemo < SortConfig > (
1018+ ( ) => ( {
1019+ options : [
1020+ { id : 'date' , label : 'Date' } ,
1021+ { id : 'duration' , label : 'Duration' } ,
1022+ { id : 'cost' , label : 'Cost' } ,
1023+ { id : 'status' , label : 'Status' } ,
1024+ ] ,
1025+ active : activeSort ,
1026+ onSort : ( column , direction ) => setActiveSort ( { column, direction } ) ,
1027+ onClear : ( ) => setActiveSort ( null ) ,
1028+ } ) ,
1029+ [ activeSort ]
1030+ )
1031+
9811032 const searchConfig = useMemo < SearchConfig > (
9821033 ( ) => ( {
9831034 value : currentInput ,
@@ -1065,6 +1116,7 @@ export default function Logs() {
10651116 < ResourceHeader icon = { Library } title = 'Logs' actions = { headerActions } />
10661117 < ResourceOptionsBar
10671118 search = { searchConfig }
1119+ sort = { sortConfig }
10681120 filter = {
10691121 < LogsFilterPanel searchQuery = { searchQuery } onSearchQueryChange = { setSearchQuery } />
10701122 }
0 commit comments