feat(pdf pagination)
This commit is contained in:
@@ -14,14 +14,24 @@ import { Eye, Trash, Download, FolderOpen } from "lucide-react";
|
|||||||
import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog";
|
import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog";
|
||||||
import { PatientTable } from "@/components/patients/patient-table";
|
import { PatientTable } from "@/components/patients/patient-table";
|
||||||
import { Patient, PdfFile } from "@repo/db/types";
|
import { Patient, PdfFile } from "@repo/db/types";
|
||||||
|
import {
|
||||||
|
Pagination,
|
||||||
|
PaginationContent,
|
||||||
|
PaginationItem,
|
||||||
|
PaginationLink,
|
||||||
|
PaginationNext,
|
||||||
|
PaginationPrevious,
|
||||||
|
} from "@/components/ui/pagination";
|
||||||
|
|
||||||
export default function DocumentsPage() {
|
export default function DocumentsPage() {
|
||||||
const [selectedPatient, setSelectedPatient] = useState<Patient | null>(null);
|
const [selectedPatient, setSelectedPatient] = useState<Patient | null>(null);
|
||||||
const [expandedGroupId, setExpandedGroupId] = useState<number | null>(null);
|
const [expandedGroupId, setExpandedGroupId] = useState<number | null>(null);
|
||||||
|
|
||||||
// pagination state for the expanded group
|
// pagination state for the expanded group
|
||||||
|
// pagination state
|
||||||
|
const [currentPage, setCurrentPage] = useState<number>(1);
|
||||||
const [limit, setLimit] = useState<number>(5);
|
const [limit, setLimit] = useState<number>(5);
|
||||||
const [offset, setOffset] = useState<number>(0);
|
const offset = (currentPage - 1) * limit;
|
||||||
const [totalForExpandedGroup, setTotalForExpandedGroup] = useState<
|
const [totalForExpandedGroup, setTotalForExpandedGroup] = useState<
|
||||||
number | null
|
number | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -37,7 +47,7 @@ export default function DocumentsPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setExpandedGroupId(null);
|
setExpandedGroupId(null);
|
||||||
setLimit(5);
|
setLimit(5);
|
||||||
setOffset(0);
|
setCurrentPage(1);
|
||||||
setTotalForExpandedGroup(null);
|
setTotalForExpandedGroup(null);
|
||||||
setFileBlobUrl(null);
|
setFileBlobUrl(null);
|
||||||
}, [selectedPatient]);
|
}, [selectedPatient]);
|
||||||
@@ -90,7 +100,7 @@ export default function DocumentsPage() {
|
|||||||
refetch: refetchGroupPdfs,
|
refetch: refetchGroupPdfs,
|
||||||
isFetching: isFetchingPdfs,
|
isFetching: isFetchingPdfs,
|
||||||
} = useQuery({
|
} = useQuery({
|
||||||
queryKey: ["groupPdfs", expandedGroupId, limit, offset],
|
queryKey: ["groupPdfs", expandedGroupId, currentPage, limit],
|
||||||
enabled: !!expandedGroupId,
|
enabled: !!expandedGroupId,
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
// API should accept ?limit & ?offset and also return total count
|
// API should accept ?limit & ?offset and also return total count
|
||||||
@@ -115,11 +125,9 @@ export default function DocumentsPage() {
|
|||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
setIsDeletePdfOpen(false);
|
setIsDeletePdfOpen(false);
|
||||||
setCurrentPdf(null);
|
setCurrentPdf(null);
|
||||||
if (expandedGroupId != null) {
|
queryClient.invalidateQueries({
|
||||||
queryClient.invalidateQueries({
|
queryKey: ["groupPdfs", expandedGroupId],
|
||||||
queryKey: ["groupPdfs", expandedGroupId],
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
toast({ title: "Success", description: "PDF deleted successfully!" });
|
toast({ title: "Success", description: "PDF deleted successfully!" });
|
||||||
},
|
},
|
||||||
onError: (error: any) => {
|
onError: (error: any) => {
|
||||||
@@ -169,20 +177,43 @@ export default function DocumentsPage() {
|
|||||||
const toggleExpandGroup = (groupId: number) => {
|
const toggleExpandGroup = (groupId: number) => {
|
||||||
if (expandedGroupId === groupId) {
|
if (expandedGroupId === groupId) {
|
||||||
setExpandedGroupId(null);
|
setExpandedGroupId(null);
|
||||||
setOffset(0);
|
setCurrentPage(1);
|
||||||
setLimit(5);
|
setLimit(5);
|
||||||
setTotalForExpandedGroup(null);
|
setTotalForExpandedGroup(null);
|
||||||
} else {
|
} else {
|
||||||
setExpandedGroupId(groupId);
|
setExpandedGroupId(groupId);
|
||||||
setOffset(0);
|
setCurrentPage(1);
|
||||||
setLimit(5);
|
setLimit(5);
|
||||||
setTotalForExpandedGroup(null);
|
setTotalForExpandedGroup(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleLoadMore = () => {
|
// pagintaion helper
|
||||||
setOffset((prev) => prev + limit);
|
const totalPages = totalForExpandedGroup
|
||||||
};
|
? Math.ceil(totalForExpandedGroup / limit)
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
const startItem = totalForExpandedGroup ? offset + 1 : 0;
|
||||||
|
const endItem = totalForExpandedGroup
|
||||||
|
? Math.min(offset + limit, totalForExpandedGroup)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
function getPageNumbers(current: number, total: number) {
|
||||||
|
const delta = 2;
|
||||||
|
const range: (number | "...")[] = [];
|
||||||
|
for (
|
||||||
|
let i = Math.max(2, current - delta);
|
||||||
|
i <= Math.min(total - 1, current + delta);
|
||||||
|
i++
|
||||||
|
) {
|
||||||
|
range.push(i);
|
||||||
|
}
|
||||||
|
if (current - delta > 2) range.unshift("...");
|
||||||
|
if (current + delta < total - 1) range.push("...");
|
||||||
|
range.unshift(1);
|
||||||
|
if (total > 1) range.push(total);
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -309,28 +340,87 @@ export default function DocumentsPage() {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* pagination controls */}
|
{/* Pagination */}
|
||||||
<div className="flex items-center gap-2">
|
{totalPages > 1 && (
|
||||||
{totalForExpandedGroup !== null &&
|
<div className="bg-white px-4 py-3 border-t border-gray-200 rounded">
|
||||||
totalForExpandedGroup >
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||||
offset + limit && (
|
<div className="text-sm text-muted-foreground mb-2 sm:mb-0 whitespace-nowrap">
|
||||||
<Button
|
Showing {startItem}–{endItem} of{" "}
|
||||||
size="sm"
|
{totalForExpandedGroup || 0}{" "}
|
||||||
onClick={handleLoadMore}
|
results
|
||||||
>
|
</div>
|
||||||
Load more
|
<Pagination>
|
||||||
</Button>
|
<PaginationContent>
|
||||||
)}
|
<PaginationItem>
|
||||||
|
<PaginationPrevious
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (currentPage > 1)
|
||||||
|
setCurrentPage(
|
||||||
|
currentPage - 1
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
currentPage === 1
|
||||||
|
? "pointer-events-none opacity-50"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
|
||||||
<div className="ml-auto text-sm text-muted-foreground">
|
{getPageNumbers(
|
||||||
Showing{" "}
|
currentPage,
|
||||||
{Math.min(
|
totalPages
|
||||||
offset + limit,
|
).map((page, idx) => (
|
||||||
totalForExpandedGroup ?? 0
|
<PaginationItem key={idx}>
|
||||||
)}{" "}
|
{page === "..." ? (
|
||||||
of {totalForExpandedGroup ?? "?"}
|
<span className="px-2 text-gray-500">
|
||||||
|
...
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<PaginationLink
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setCurrentPage(
|
||||||
|
page as number
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
isActive={
|
||||||
|
currentPage === page
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</PaginationLink>
|
||||||
|
)}
|
||||||
|
</PaginationItem>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<PaginationItem>
|
||||||
|
<PaginationNext
|
||||||
|
href="#"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (
|
||||||
|
currentPage < totalPages
|
||||||
|
)
|
||||||
|
setCurrentPage(
|
||||||
|
currentPage + 1
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
className={
|
||||||
|
currentPage === totalPages
|
||||||
|
? "pointer-events-none opacity-50"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</PaginationItem>
|
||||||
|
</PaginationContent>
|
||||||
|
</Pagination>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user