Skip to content

Commit 4e01d7a

Browse files
committed
feat: add pagination to file priorities in release creation and updation
1 parent a05e406 commit 4e01d7a

2 files changed

Lines changed: 257 additions & 75 deletions

File tree

  • airborne_dashboard/app/dashboard/[orgId]/[appId]/releases

airborne_dashboard/app/dashboard/[orgId]/[appId]/releases/[releaseId]/edit/page.tsx

Lines changed: 129 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
import json from "highlight.js/lib/languages/json";
3838
import hljs from "highlight.js";
3939
import "highlight.js/styles/vs2015.css";
40+
import { useDebouncedValue } from "@/hooks/useDebouncedValue";
4041

4142
hljs.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>

airborne_dashboard/app/dashboard/[orgId]/[appId]/releases/create/page.tsx

Lines changed: 128 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,12 @@ export default function CreateReleasePage() {
124124
const debouncedResourcesSearch = useDebouncedValue(resourceSearch, 500);
125125
const [resourceCurrentPage, setResourceCurrentPage] = useState(1);
126126

127+
// File priorities pagination state
128+
const [filesCurrentPage, setFilesCurrentPage] = useState(1);
129+
const [filesSearch, setFilesSearch] = useState("");
130+
const debouncedFilesSearch = useDebouncedValue(filesSearch, 300);
131+
const filesPerPage = 10;
132+
127133
const { token, org, app, getAppAccess, getOrgAccess, loadingAccess } = useAppContext();
128134

129135
const router = useRouter();
@@ -481,6 +487,18 @@ export default function CreateReleasePage() {
481487
[packages, debouncedPackageSearch]
482488
);
483489

490+
// Calculate filtered and paginated files for step 5
491+
const filteredFiles = useMemo(
492+
() => files.filter((f) => f.file_path.toLowerCase().includes(filesSearch.toLowerCase())),
493+
[files, debouncedFilesSearch]
494+
);
495+
496+
const filesTotalPages = Math.ceil(filteredFiles.length / filesPerPage);
497+
const paginatedFiles = useMemo(
498+
() => filteredFiles.slice((filesCurrentPage - 1) * filesPerPage, filesCurrentPage * filesPerPage),
499+
[filteredFiles, filesCurrentPage, filesPerPage]
500+
);
501+
484502
// Get resource data from API response
485503
const allResources = resourceData?.files || [];
486504
const resourceTotal = resourceData?.total || 0;
@@ -595,6 +613,11 @@ export default function CreateReleasePage() {
595613
setResourceCurrentPage(1); // Reset to first page when searching
596614
};
597615

616+
const handleFilesSearchChange = (value: string) => {
617+
setFilesSearch(value);
618+
setFilesCurrentPage(1); // Reset to first page when searching
619+
};
620+
598621
const renderPaginationItems = (currentPage: number, totalPages: number, onPageChange: (page: number) => void) => {
599622
const items = [];
600623
const maxVisiblePages = 5;
@@ -1323,45 +1346,112 @@ export default function CreateReleasePage() {
13231346
<Info className="h-4 w-4 text-blue-600" />
13241347
<div className="text-sm">All files default to Important. Switch to Lazy to defer loading.</div>
13251348
</div>
1326-
<Table>
1327-
<TableHeader>
1328-
<TableRow>
1329-
<TableHead>File</TableHead>
1330-
<TableHead>Tag</TableHead>
1331-
<TableHead>Version</TableHead>
1332-
<TableHead>Priority</TableHead>
1333-
</TableRow>
1334-
</TableHeader>
1335-
<TableBody>
1336-
{files.map((f) => {
1337-
const id = f.id || `${f.file_path}@version:${f.version}`;
1338-
return (
1339-
<TableRow key={id}>
1340-
<TableCell className="font-mono text-sm">{f.file_path}</TableCell>
1341-
<TableCell className="text-muted-foreground">{f.tag}</TableCell>
1342-
<TableCell className="text-muted-foreground">{f.version}</TableCell>
1343-
<TableCell>
1344-
<Select
1345-
value={filePriority[id] || "important"}
1346-
onValueChange={(val: "important" | "lazy") => {
1347-
setFilePriority((prev) => ({ ...prev, [id]: val }));
1348-
console.log("Updated file priorities", filePriority);
1349-
}}
1350-
>
1351-
<SelectTrigger className="w-36">
1352-
<SelectValue />
1353-
</SelectTrigger>
1354-
<SelectContent>
1355-
<SelectItem value="important">Important</SelectItem>
1356-
<SelectItem value="lazy">Lazy</SelectItem>
1357-
</SelectContent>
1358-
</Select>
1359-
</TableCell>
1349+
1350+
{files.length > 0 && (
1351+
<div className="mb-4">
1352+
<div className="relative">
1353+
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
1354+
<Input
1355+
placeholder="Search files..."
1356+
value={filesSearch}
1357+
onChange={(e) => handleFilesSearchChange(e.target.value)}
1358+
className="pl-10"
1359+
/>
1360+
</div>
1361+
</div>
1362+
)}
1363+
1364+
{files.length === 0 ? (
1365+
<div className="text-center py-8 text-muted-foreground">
1366+
<FileText className="mx-auto h-8 w-8 mb-2" />
1367+
<p className="text-sm">No files available.</p>
1368+
</div>
1369+
) : filteredFiles.length === 0 ? (
1370+
<div className="text-center py-8 text-muted-foreground">
1371+
<FileText className="mx-auto h-8 w-8 mb-2" />
1372+
<p className="text-sm">No files found matching your search.</p>
1373+
</div>
1374+
) : (
1375+
<div className="space-y-4">
1376+
<div className="text-sm text-muted-foreground">
1377+
Showing {Math.min(filesPerPage, filteredFiles.length)} of {filteredFiles.length} files
1378+
{filesCurrentPage > 1 && ` (page ${filesCurrentPage})`}
1379+
{filesSearch && ` matching "${filesSearch}"`}
1380+
</div>
1381+
1382+
<Table>
1383+
<TableHeader>
1384+
<TableRow>
1385+
<TableHead>File</TableHead>
1386+
<TableHead>Tag</TableHead>
1387+
<TableHead>Version</TableHead>
1388+
<TableHead>Priority</TableHead>
13601389
</TableRow>
1361-
);
1362-
})}
1363-
</TableBody>
1364-
</Table>
1390+
</TableHeader>
1391+
<TableBody>
1392+
{paginatedFiles.map((f) => {
1393+
const id = f.id || `${f.file_path}@version:${f.version}`;
1394+
return (
1395+
<TableRow key={id}>
1396+
<TableCell className="font-mono text-sm">{f.file_path}</TableCell>
1397+
<TableCell className="text-muted-foreground">{f.tag}</TableCell>
1398+
<TableCell className="text-muted-foreground">{f.version}</TableCell>
1399+
<TableCell>
1400+
<Select
1401+
value={filePriority[id] || "important"}
1402+
onValueChange={(val: "important" | "lazy") => {
1403+
setFilePriority((prev) => ({ ...prev, [id]: val }));
1404+
}}
1405+
>
1406+
<SelectTrigger className="w-36">
1407+
<SelectValue />
1408+
</SelectTrigger>
1409+
<SelectContent>
1410+
<SelectItem value="important">Important</SelectItem>
1411+
<SelectItem value="lazy">Lazy</SelectItem>
1412+
</SelectContent>
1413+
</Select>
1414+
</TableCell>
1415+
</TableRow>
1416+
);
1417+
})}
1418+
</TableBody>
1419+
</Table>
1420+
1421+
{/* Files Pagination */}
1422+
{filesTotalPages > 1 && (
1423+
<div className="mt-4">
1424+
<Pagination>
1425+
<PaginationContent>
1426+
<PaginationItem>
1427+
<PaginationPrevious
1428+
href="#"
1429+
onClick={(e) => {
1430+
e.preventDefault();
1431+
if (filesCurrentPage > 1) setFilesCurrentPage(filesCurrentPage - 1);
1432+
}}
1433+
className={filesCurrentPage <= 1 ? "pointer-events-none opacity-50" : ""}
1434+
/>
1435+
</PaginationItem>
1436+
1437+
{renderPaginationItems(filesCurrentPage, filesTotalPages, setFilesCurrentPage)}
1438+
1439+
<PaginationItem>
1440+
<PaginationNext
1441+
href="#"
1442+
onClick={(e) => {
1443+
e.preventDefault();
1444+
if (filesCurrentPage < filesTotalPages) setFilesCurrentPage(filesCurrentPage + 1);
1445+
}}
1446+
className={filesCurrentPage >= filesTotalPages ? "pointer-events-none opacity-50" : ""}
1447+
/>
1448+
</PaginationItem>
1449+
</PaginationContent>
1450+
</Pagination>
1451+
</div>
1452+
)}
1453+
</div>
1454+
)}
13651455
</CardContent>
13661456
</Card>
13671457
</div>

0 commit comments

Comments
 (0)