@@ -37,6 +37,7 @@ import {
3737import json from "highlight.js/lib/languages/json" ;
3838import hljs from "highlight.js" ;
3939import "highlight.js/styles/vs2015.css" ;
40+ import { useDebouncedValue } from "@/hooks/useDebouncedValue" ;
4041
4142hljs . registerLanguage ( "json" , json ) ;
4243
@@ -140,6 +141,11 @@ export default function EditReleasePage() {
140141 const [ resourceSearch , setResourceSearch ] = useState ( "" ) ;
141142 const [ resourceCurrentPage , setResourceCurrentPage ] = useState ( 1 ) ;
142143
144+ const [ filesCurrentPage , setFilesCurrentPage ] = useState ( 1 ) ;
145+ const [ filesSearch , setFilesSearch ] = useState ( "" ) ;
146+ const debouncedFilesSearch = useDebouncedValue ( filesSearch , 300 ) ;
147+ const filesPerPage = 10 ;
148+
143149 const router = useRouter ( ) ;
144150 const perPage = 50 ;
145151
@@ -294,6 +300,18 @@ export default function EditReleasePage() {
294300 [ packages , pkgSearch ]
295301 ) ;
296302
303+ // Calculate filtered and paginated files for step 5
304+ const filteredFiles = useMemo (
305+ ( ) => files . filter ( ( f ) => f . file_path . toLowerCase ( ) . includes ( filesSearch . toLowerCase ( ) ) ) ,
306+ [ files , debouncedFilesSearch ]
307+ ) ;
308+
309+ const filesTotalPages = Math . ceil ( filteredFiles . length / filesPerPage ) ;
310+ const paginatedFiles = useMemo (
311+ ( ) => filteredFiles . slice ( ( filesCurrentPage - 1 ) * filesPerPage , filesCurrentPage * filesPerPage ) ,
312+ [ filteredFiles , filesCurrentPage , filesPerPage ]
313+ ) ;
314+
297315 // Get resource data from API response
298316 const allResources = resourceData ?. files || [ ] ;
299317 const resourceTotal = resourceData ?. total || 0 ;
@@ -389,6 +407,11 @@ export default function EditReleasePage() {
389407 setResourceCurrentPage ( 1 ) ; // Reset to first page when searching
390408 } ;
391409
410+ const handleFilesSearchChange = ( value : string ) => {
411+ setFilesSearch ( value ) ;
412+ setFilesCurrentPage ( 1 ) ; // Reset to first page when searching
413+ } ;
414+
392415 const renderPaginationItems = ( currentPage : number , totalPages : number , onPageChange : ( page : number ) => void ) => {
393416 const items = [ ] ;
394417 const maxVisiblePages = 5 ;
@@ -935,44 +958,113 @@ export default function EditReleasePage() {
935958 < CardDescription > Choose which files load immediately (important) vs on-demand (lazy)</ CardDescription >
936959 </ CardHeader >
937960 < CardContent >
938- < Table >
939- < TableHeader >
940- < TableRow >
941- < TableHead > File</ TableHead >
942- < TableHead > Tag</ TableHead >
943- < TableHead > Version</ TableHead >
944- < TableHead > Priority</ TableHead >
945- </ TableRow >
946- </ TableHeader >
947- < TableBody >
948- { files . map ( ( f ) => {
949- const id = f . id || `${ f . file_path } @version:${ f . version } ` ;
950- return (
951- < TableRow key = { id } >
952- < TableCell className = "font-mono text-sm" > { f . file_path } </ TableCell >
953- < TableCell className = "text-muted-foreground" > { f . tag } </ TableCell >
954- < TableCell className = "text-muted-foreground" > { f . version } </ TableCell >
955- < TableCell >
956- < Select
957- value = { filePriority [ id ] || "important" }
958- onValueChange = { ( val : "important" | "lazy" ) => {
959- setFilePriority ( ( prev ) => ( { ...prev , [ id ] : val } ) ) ;
960- } }
961- >
962- < SelectTrigger className = "w-36" >
963- < SelectValue />
964- </ SelectTrigger >
965- < SelectContent >
966- < SelectItem value = "important" > Important</ SelectItem >
967- < SelectItem value = "lazy" > Lazy</ SelectItem >
968- </ SelectContent >
969- </ Select >
970- </ TableCell >
961+ < div className = "flex items-center gap-2 p-3 bg-blue-10 border border-blue-200 rounded-lg mb-4" >
962+ < Info className = "h-4 w-4 text-blue-600" />
963+ < div className = "text-sm" > All files default to Important. Switch to Lazy to defer loading.</ div >
964+ </ div >
965+ { files . length > 0 && (
966+ < div className = "mb-4" >
967+ < div className = "relative" >
968+ < Search className = "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
969+ < Input
970+ placeholder = "Search files..."
971+ value = { filesSearch }
972+ onChange = { ( e ) => handleFilesSearchChange ( e . target . value ) }
973+ className = "pl-10"
974+ />
975+ </ div >
976+ </ div >
977+ ) }
978+ { files . length === 0 ? (
979+ < div className = "text-center py-8 text-muted-foreground" >
980+ < FileText className = "mx-auto h-8 w-8 mb-2" />
981+ < p className = "text-sm" > No files available.</ p >
982+ </ div >
983+ ) : filteredFiles . length === 0 ? (
984+ < div className = "text-center py-8 text-muted-foreground" >
985+ < FileText className = "mx-auto h-8 w-8 mb-2" />
986+ < p className = "text-sm" > No files found matching your search.</ p >
987+ </ div >
988+ ) : (
989+ < div className = "space-y-4" >
990+ < div className = "text-sm text-muted-foreground" >
991+ Showing { Math . min ( filesPerPage , filteredFiles . length ) } of { filteredFiles . length } files
992+ { filesCurrentPage > 1 && ` (page ${ filesCurrentPage } )` }
993+ { filesSearch && ` matching "${ filesSearch } "` }
994+ </ div >
995+
996+ < Table >
997+ < TableHeader >
998+ < TableRow >
999+ < TableHead > File</ TableHead >
1000+ < TableHead > Tag</ TableHead >
1001+ < TableHead > Version</ TableHead >
1002+ < TableHead > Priority</ TableHead >
9711003 </ TableRow >
972- ) ;
973- } ) }
974- </ TableBody >
975- </ Table >
1004+ </ TableHeader >
1005+ < TableBody >
1006+ { paginatedFiles . map ( ( f ) => {
1007+ const id = f . id || `${ f . file_path } @version:${ f . version } ` ;
1008+ return (
1009+ < TableRow key = { id } >
1010+ < TableCell className = "font-mono text-sm" > { f . file_path } </ TableCell >
1011+ < TableCell className = "text-muted-foreground" > { f . tag } </ TableCell >
1012+ < TableCell className = "text-muted-foreground" > { f . version } </ TableCell >
1013+ < TableCell >
1014+ < Select
1015+ value = { filePriority [ id ] || "important" }
1016+ onValueChange = { ( val : "important" | "lazy" ) => {
1017+ setFilePriority ( ( prev ) => ( { ...prev , [ id ] : val } ) ) ;
1018+ } }
1019+ >
1020+ < SelectTrigger className = "w-36" >
1021+ < SelectValue />
1022+ </ SelectTrigger >
1023+ < SelectContent >
1024+ < SelectItem value = "important" > Important</ SelectItem >
1025+ < SelectItem value = "lazy" > Lazy</ SelectItem >
1026+ </ SelectContent >
1027+ </ Select >
1028+ </ TableCell >
1029+ </ TableRow >
1030+ ) ;
1031+ } ) }
1032+ </ TableBody >
1033+ </ Table >
1034+ { /* Files Pagination */ }
1035+ { filesTotalPages > 1 && (
1036+ < div className = "mt-4" >
1037+ < Pagination >
1038+ < PaginationContent >
1039+ < PaginationItem >
1040+ < PaginationPrevious
1041+ href = "#"
1042+ onClick = { ( e ) => {
1043+ e . preventDefault ( ) ;
1044+ if ( filesCurrentPage > 1 ) setFilesCurrentPage ( filesCurrentPage - 1 ) ;
1045+ } }
1046+ className = { filesCurrentPage <= 1 ? "pointer-events-none opacity-50" : "" }
1047+ />
1048+ </ PaginationItem >
1049+
1050+ { renderPaginationItems ( filesCurrentPage , filesTotalPages , setFilesCurrentPage ) }
1051+
1052+ < PaginationItem >
1053+ < PaginationNext
1054+ href = "#"
1055+ onClick = { ( e ) => {
1056+ e . preventDefault ( ) ;
1057+ if ( filesCurrentPage < filesTotalPages ) setFilesCurrentPage ( filesCurrentPage + 1 ) ;
1058+ } }
1059+ className = { filesCurrentPage >= filesTotalPages ? "pointer-events-none opacity-50" : "" }
1060+ />
1061+ </ PaginationItem >
1062+ </ PaginationContent >
1063+ </ Pagination >
1064+ </ div >
1065+ ) }
1066+ </ div >
1067+ ) }
9761068 </ CardContent >
9771069 </ Card >
9781070 </ div >
0 commit comments