@@ -26,6 +26,8 @@ export function InstalledScriptsTab() {
2626  const  [ searchTerm ,  setSearchTerm ]  =  useState ( '' ) ; 
2727  const  [ statusFilter ,  setStatusFilter ]  =  useState < 'all'  |  'success'  |  'failed'  |  'in_progress' > ( 'all' ) ; 
2828  const  [ serverFilter ,  setServerFilter ]  =  useState < string > ( 'all' ) ; 
29+   const  [ sortField ,  setSortField ]  =  useState < 'script_name'  |  'container_id'  |  'server_name'  |  'status'  |  'installation_date' > ( 'script_name' ) ; 
30+   const  [ sortDirection ,  setSortDirection ]  =  useState < 'asc'  |  'desc' > ( 'asc' ) ; 
2931  const  [ updatingScript ,  setUpdatingScript ]  =  useState < {  id : number ;  containerId : string ;  server ?: any  }  |  null > ( null ) ; 
3032  const  [ editingScriptId ,  setEditingScriptId ]  =  useState < number  |  null > ( null ) ; 
3133  const  [ editFormData ,  setEditFormData ]  =  useState < {  script_name : string ;  container_id : string  } > ( {  script_name : '' ,  container_id : ''  } ) ; 
@@ -154,20 +156,58 @@ export function InstalledScriptsTab() {
154156    } 
155157  } ,  [ scripts . length ,  serversData ?. servers ,  cleanupMutation ] ) ; 
156158
157-   // Filter scripts based on search and filters 
158-   const  filteredScripts  =  scripts . filter ( ( script : InstalledScript )  =>  { 
159-     const  matchesSearch  =  script . script_name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )  || 
160-                          ( script . container_id ?. includes ( searchTerm )  ??  false )  || 
161-                          ( script . server_name ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )  ??  false ) ; 
162-     
163-     const  matchesStatus  =  statusFilter  ===  'all'  ||  script . status  ===  statusFilter ; 
164-     
165-     const  matchesServer  =  serverFilter  ===  'all'  ||  
166-                          ( serverFilter  ===  'local'  &&  ! script . server_name )  || 
167-                          ( script . server_name  ===  serverFilter ) ; 
168-     
169-     return  matchesSearch  &&  matchesStatus  &&  matchesServer ; 
170-   } ) ; 
159+   // Filter and sort scripts 
160+   const  filteredScripts  =  scripts 
161+     . filter ( ( script : InstalledScript )  =>  { 
162+       const  matchesSearch  =  script . script_name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )  || 
163+                            ( script . container_id ?. includes ( searchTerm )  ??  false )  || 
164+                            ( script . server_name ?. toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )  ??  false ) ; 
165+       
166+       const  matchesStatus  =  statusFilter  ===  'all'  ||  script . status  ===  statusFilter ; 
167+       
168+       const  matchesServer  =  serverFilter  ===  'all'  ||  
169+                            ( serverFilter  ===  'local'  &&  ! script . server_name )  || 
170+                            ( script . server_name  ===  serverFilter ) ; 
171+       
172+       return  matchesSearch  &&  matchesStatus  &&  matchesServer ; 
173+     } ) 
174+     . sort ( ( a : InstalledScript ,  b : InstalledScript )  =>  { 
175+       let  aValue : any ; 
176+       let  bValue : any ; 
177+ 
178+       switch  ( sortField )  { 
179+         case  'script_name' :
180+           aValue  =  a . script_name . toLowerCase ( ) ; 
181+           bValue  =  b . script_name . toLowerCase ( ) ; 
182+           break ; 
183+         case  'container_id' :
184+           aValue  =  a . container_id  ??  '' ; 
185+           bValue  =  b . container_id  ??  '' ; 
186+           break ; 
187+         case  'server_name' :
188+           aValue  =  a . server_name  ??  'Local' ; 
189+           bValue  =  b . server_name  ??  'Local' ; 
190+           break ; 
191+         case  'status' :
192+           aValue  =  a . status ; 
193+           bValue  =  b . status ; 
194+           break ; 
195+         case  'installation_date' :
196+           aValue  =  new  Date ( a . installation_date ) . getTime ( ) ; 
197+           bValue  =  new  Date ( b . installation_date ) . getTime ( ) ; 
198+           break ; 
199+         default :
200+           return  0 ; 
201+       } 
202+ 
203+       if  ( aValue  <  bValue )  { 
204+         return  sortDirection  ===  'asc'  ? - 1  : 1 ; 
205+       } 
206+       if  ( aValue  >  bValue )  { 
207+         return  sortDirection  ===  'asc'  ? 1  : - 1 ; 
208+       } 
209+       return  0 ; 
210+     } ) ; 
171211
172212  // Get unique servers for filter 
173213  const  uniqueServers : string [ ]  =  [ ] ; 
@@ -298,6 +338,15 @@ export function InstalledScriptsTab() {
298338    setAutoDetectServerId ( '' ) ; 
299339  } ; 
300340
341+   const  handleSort  =  ( field : 'script_name'  |  'container_id'  |  'server_name'  |  'status'  |  'installation_date' )  =>  { 
342+     if  ( sortField  ===  field )  { 
343+       setSortDirection ( sortDirection  ===  'asc'  ? 'desc'  : 'asc' ) ; 
344+     }  else  { 
345+       setSortField ( field ) ; 
346+       setSortDirection ( 'asc' ) ; 
347+     } 
348+   } ; 
349+ 
301350
302351  const  formatDate  =  ( dateString : string )  =>  { 
303352    return  new  Date ( dateString ) . toLocaleString ( ) ; 
@@ -652,20 +701,70 @@ export function InstalledScriptsTab() {
652701              < table  className = "min-w-full divide-y divide-gray-200" > 
653702                < thead  className = "bg-muted" > 
654703                  < tr > 
655-                     < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
656-                       Script Name
704+                     < th  
705+                       className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider cursor-pointer hover:bg-muted/80 select-none" 
706+                       onClick = { ( )  =>  handleSort ( 'script_name' ) } 
707+                     > 
708+                       < div  className = "flex items-center space-x-1" > 
709+                         < span > Script Name</ span > 
710+                         { sortField  ===  'script_name'  &&  ( 
711+                           < span  className = "text-primary" > 
712+                             { sortDirection  ===  'asc'  ? '↑'  : '↓' } 
713+                           </ span > 
714+                         ) } 
715+                       </ div > 
657716                    </ th > 
658-                     < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
659-                       Container ID
717+                     < th  
718+                       className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider cursor-pointer hover:bg-muted/80 select-none" 
719+                       onClick = { ( )  =>  handleSort ( 'container_id' ) } 
720+                     > 
721+                       < div  className = "flex items-center space-x-1" > 
722+                         < span > Container ID</ span > 
723+                         { sortField  ===  'container_id'  &&  ( 
724+                           < span  className = "text-primary" > 
725+                             { sortDirection  ===  'asc'  ? '↑'  : '↓' } 
726+                           </ span > 
727+                         ) } 
728+                       </ div > 
660729                    </ th > 
661-                     < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
662-                       Server
730+                     < th  
731+                       className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider cursor-pointer hover:bg-muted/80 select-none" 
732+                       onClick = { ( )  =>  handleSort ( 'server_name' ) } 
733+                     > 
734+                       < div  className = "flex items-center space-x-1" > 
735+                         < span > Server</ span > 
736+                         { sortField  ===  'server_name'  &&  ( 
737+                           < span  className = "text-primary" > 
738+                             { sortDirection  ===  'asc'  ? '↑'  : '↓' } 
739+                           </ span > 
740+                         ) } 
741+                       </ div > 
663742                    </ th > 
664-                     < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
665-                       Status
743+                     < th  
744+                       className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider cursor-pointer hover:bg-muted/80 select-none" 
745+                       onClick = { ( )  =>  handleSort ( 'status' ) } 
746+                     > 
747+                       < div  className = "flex items-center space-x-1" > 
748+                         < span > Status</ span > 
749+                         { sortField  ===  'status'  &&  ( 
750+                           < span  className = "text-primary" > 
751+                             { sortDirection  ===  'asc'  ? '↑'  : '↓' } 
752+                           </ span > 
753+                         ) } 
754+                       </ div > 
666755                    </ th > 
667-                     < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
668-                       Installation Date
756+                     < th  
757+                       className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider cursor-pointer hover:bg-muted/80 select-none" 
758+                       onClick = { ( )  =>  handleSort ( 'installation_date' ) } 
759+                     > 
760+                       < div  className = "flex items-center space-x-1" > 
761+                         < span > Installation Date</ span > 
762+                         { sortField  ===  'installation_date'  &&  ( 
763+                           < span  className = "text-primary" > 
764+                             { sortDirection  ===  'asc'  ? '↑'  : '↓' } 
765+                           </ span > 
766+                         ) } 
767+                       </ div > 
669768                    </ th > 
670769                    < th  className = "px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider" > 
671770                      Actions
0 commit comments