document page done

This commit is contained in:
2025-06-20 19:19:37 +05:30
parent bbabb3b035
commit 01806a11cd
5 changed files with 699 additions and 138 deletions

View File

@@ -1,8 +1,6 @@
import { Router } from "express"; import { Router } from "express";
import { Request, Response } from "express"; import { Request, Response } from "express";
import { storage } from "../storage"; import { storage } from "../storage";
import { z } from "zod";
import { ClaimUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
import multer from "multer"; import multer from "multer";
const upload = multer({ storage: multer.memoryStorage() }); const upload = multer({ storage: multer.memoryStorage() });
@@ -57,9 +55,17 @@ router.get("/claim-pdf/:id", async (req: Request, res: Response): Promise<any> =
const id = parseInt(idParam); const id = parseInt(idParam);
const pdf = await storage.getClaimPdfById(id); const pdf = await storage.getClaimPdfById(id);
if (!pdf) return res.status(404).json({ error: "PDF not found" }); if (!pdf || !pdf.pdfData) return res.status(404).json({ error: "PDF not found" });
// Fix bad objectified Buffer
if (!Buffer.isBuffer(pdf.pdfData)) {
pdf.pdfData = Buffer.from(Object.values(pdf.pdfData));
}
res.setHeader("Content-Type", "application/pdf"); res.setHeader("Content-Type", "application/pdf");
res.setHeader(
"Content-Disposition",
`attachment; filename="${pdf.filename}"; filename*=UTF-8''${encodeURIComponent(pdf.filename)}`
);
res.send(pdf.pdfData); res.send(pdf.pdfData);
} catch (err) { } catch (err) {
console.error("Error fetching PDF by ID:", err); console.error("Error fetching PDF by ID:", err);

View File

@@ -6,7 +6,7 @@ import {
StaffUncheckedCreateInputObjectSchema, StaffUncheckedCreateInputObjectSchema,
ClaimUncheckedCreateInputObjectSchema, ClaimUncheckedCreateInputObjectSchema,
InsuranceCredentialUncheckedCreateInputObjectSchema, InsuranceCredentialUncheckedCreateInputObjectSchema,
ClaimPdfUncheckedCreateInputObjectSchema ClaimPdfUncheckedCreateInputObjectSchema,
} from "@repo/db/usedSchemas"; } from "@repo/db/usedSchemas";
import { z } from "zod"; import { z } from "zod";
@@ -166,6 +166,7 @@ export interface IStorage {
// Patient methods // Patient methods
getPatient(id: number): Promise<Patient | undefined>; getPatient(id: number): Promise<Patient | undefined>;
getPatientsByUserId(userId: number): Promise<Patient[]>; getPatientsByUserId(userId: number): Promise<Patient[]>;
getRecentPatients(limit: number, offset: number): Promise<Patient[]>;
createPatient(patient: InsertPatient): Promise<Patient>; createPatient(patient: InsertPatient): Promise<Patient>;
updatePatient(id: number, patient: UpdatePatient): Promise<Patient>; updatePatient(id: number, patient: UpdatePatient): Promise<Patient>;
deletePatient(id: number): Promise<void>; deletePatient(id: number): Promise<void>;
@@ -175,6 +176,8 @@ export interface IStorage {
getAllAppointments(): Promise<Appointment[]>; getAllAppointments(): Promise<Appointment[]>;
getAppointmentsByUserId(userId: number): Promise<Appointment[]>; getAppointmentsByUserId(userId: number): Promise<Appointment[]>;
getAppointmentsByPatientId(patientId: number): Promise<Appointment[]>; getAppointmentsByPatientId(patientId: number): Promise<Appointment[]>;
getRecentAppointments(limit: number, offset: number): Promise<Appointment[]>;
getAppointmentsOn(date: Date): Promise<Appointment[]>;
createAppointment(appointment: InsertAppointment): Promise<Appointment>; createAppointment(appointment: InsertAppointment): Promise<Appointment>;
updateAppointment( updateAppointment(
id: number, id: number,
@@ -234,7 +237,10 @@ export interface IStorage {
getAllClaimPdfs(): Promise<ClaimPdfMetadata[]>; getAllClaimPdfs(): Promise<ClaimPdfMetadata[]>;
getRecentClaimPdfs(limit: number, offset: number): Promise<ClaimPdfMetadata[]>; getRecentClaimPdfs(
limit: number,
offset: number
): Promise<ClaimPdfMetadata[]>;
deleteClaimPdf(id: number): Promise<boolean>; deleteClaimPdf(id: number): Promise<boolean>;
@@ -290,6 +296,14 @@ export const storage: IStorage = {
return await db.patient.findMany({ where: { userId } }); return await db.patient.findMany({ where: { userId } });
}, },
async getRecentPatients(limit: number, offset: number): Promise<Patient[]> {
return db.patient.findMany({
skip: offset,
take: limit,
orderBy: { createdAt: "desc" },
});
},
async createPatient(patient: InsertPatient): Promise<Patient> { async createPatient(patient: InsertPatient): Promise<Patient> {
return await db.patient.create({ data: patient as Patient }); return await db.patient.create({ data: patient as Patient });
}, },
@@ -332,6 +346,35 @@ export const storage: IStorage = {
return await db.appointment.findMany({ where: { patientId } }); return await db.appointment.findMany({ where: { patientId } });
}, },
async getAppointmentsOn(date: Date): Promise<Appointment[]> {
const start = new Date(date);
start.setHours(0, 0, 0, 0);
const end = new Date(date);
end.setHours(23, 59, 59, 999);
return db.appointment.findMany({
where: {
date: {
gte: start,
lte: end,
},
},
orderBy: { date: "asc" },
});
},
async getRecentAppointments(
limit: number,
offset: number
): Promise<Appointment[]> {
return db.appointment.findMany({
skip: offset,
take: limit,
orderBy: { date: "desc" }
});
},
async createAppointment( async createAppointment(
appointment: InsertAppointment appointment: InsertAppointment
): Promise<Appointment> { ): Promise<Appointment> {
@@ -534,7 +577,10 @@ export const storage: IStorage = {
}); });
}, },
async getRecentClaimPdfs(limit: number, offset: number): Promise<ClaimPdfMetadata[]> { async getRecentClaimPdfs(
limit: number,
offset: number
): Promise<ClaimPdfMetadata[]> {
return db.claimPdf.findMany({ return db.claimPdf.findMany({
skip: offset, skip: offset,
take: limit, take: limit,
@@ -543,6 +589,7 @@ export const storage: IStorage = {
id: true, id: true,
filename: true, filename: true,
uploadedAt: true, uploadedAt: true,
patient:true,
}, },
}); });
}, },
@@ -561,7 +608,10 @@ export const storage: IStorage = {
updates: Partial<Pick<ClaimPdf, "filename" | "pdfData">> updates: Partial<Pick<ClaimPdf, "filename" | "pdfData">>
): Promise<ClaimPdf | undefined> { ): Promise<ClaimPdf | undefined> {
try { try {
const updated = await db.claimPdf.update({ where: { id }, data: updates }); const updated = await db.claimPdf.update({
where: { id },
data: updates,
});
return updated; return updated;
} catch { } catch {
return undefined; return undefined;

View File

@@ -1,19 +1,19 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import { useQuery } from "@tanstack/react-query"; import { useMutation, useQuery } from "@tanstack/react-query";
import { TopAppBar } from "@/components/layout/top-app-bar"; import { TopAppBar } from "@/components/layout/top-app-bar";
import { Sidebar } from "@/components/layout/sidebar"; import { Sidebar } from "@/components/layout/sidebar";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { import {
Search, Search,
Edit,
Eye, Eye,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
Settings Settings,
Trash,
Download,
} from "lucide-react"; } from "lucide-react";
import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
import { useAuth } from "@/hooks/use-auth"; import { useAuth } from "@/hooks/use-auth";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { import {
@@ -23,37 +23,19 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import {z} from "zod"; import { ClaimPdfUncheckedCreateInputObjectSchema } from "@repo/db/usedSchemas";
import { z } from "zod";
import "@react-pdf-viewer/core/lib/styles/index.css";
const PatientSchema = ( import "@react-pdf-viewer/default-layout/lib/styles/index.css";
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any> import { Viewer, Worker } from "@react-pdf-viewer/core";
).omit({ import { defaultLayoutPlugin } from "@react-pdf-viewer/default-layout";
appointments: true, import { apiRequest, queryClient } from "@/lib/queryClient";
}); import { toast } from "@/hooks/use-toast";
type Patient = z.infer<typeof PatientSchema>; import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog";
const insertPatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
).omit({
id: true,
createdAt: true,
userId: true,
});
type InsertPatient = z.infer<typeof insertPatientSchema>;
const updatePatientSchema = (
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
)
.omit({
id: true,
createdAt: true,
userId: true,
})
.partial();
type UpdatePatient = z.infer<typeof updatePatientSchema>;
const ClaimPdfSchema =
ClaimPdfUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>;
type ClaimPdf = z.infer<typeof ClaimPdfSchema>;
export default function DocumentsPage() { export default function DocumentsPage() {
const { user } = useAuth(); const { user } = useAuth();
@@ -62,23 +44,138 @@ export default function DocumentsPage() {
const [searchField, setSearchField] = useState("all"); const [searchField, setSearchField] = useState("all");
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 5; const itemsPerPage = 5;
const [selectedPdfId, setSelectedPdfId] = useState<number | null>(null);
const defaultLayoutPluginInstance = defaultLayoutPlugin();
const [isDeletePdfOpen, setIsDeletePdfOpen] = useState(false);
const [currentPdf, setCurrentPdf] = useState<ClaimPdf | null>(null);
// Fetch patients const { data: pdfs = [], isLoading } = useQuery<ClaimPdf[]>({
const { queryKey: ["/api/documents/claim-pdf/recent"],
data: patients = [],
isLoading: isLoadingPatients,
} = useQuery<Patient[]>({
queryKey: ["/api/patients"],
enabled: !!user, enabled: !!user,
queryFn: async () => {
const res = await apiRequest("GET", "/api/documents/claim-pdf/recent");
return res.json();
},
}); });
// Filter patients based on search const deletePdfMutation = useMutation({
const filteredPatients = patients.filter(patient => { mutationFn: async (id: number) => {
if (!searchTerm) return true; await apiRequest("DELETE", `/api/documents/claim-pdf/${id}`);
},
onSuccess: () => {
setIsDeletePdfOpen(false);
setCurrentPdf(null);
queryClient.invalidateQueries({
queryKey: ["/api/documents/claim-pdf/recent"],
});
toast({
title: "Success",
description: "PDF deleted successfully!",
variant: "default",
});
},
onError: (error: any) => {
console.error("Error deleting PDF:", error);
toast({
title: "Error",
description: `Failed to delete PDF: ${error.message || error}`,
variant: "destructive",
});
},
});
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
});
};
const getPatientInitials = (first: string, last: string) =>
`${first[0]}${last[0]}`.toUpperCase();
const [fileBlobUrl, setFileBlobUrl] = useState<string | null>(null);
useEffect(() => {
if (!selectedPdfId) return;
let url: string | null = null;
const fetchPdf = async () => {
try {
const res = await apiRequest(
"GET",
`/api/documents/claim-pdf/${selectedPdfId}`
);
const arrayBuffer = await res.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: "application/pdf" });
const objectUrl = URL.createObjectURL(blob);
setFileBlobUrl(objectUrl);
url = objectUrl;
} catch (err) {
console.error("Failed to load PDF", err);
}
};
fetchPdf();
return () => {
if (url) {
URL.revokeObjectURL(url);
}
};
}, [selectedPdfId]);
const toggleMobileMenu = () => setIsMobileMenuOpen((prev) => !prev);
const viewPdf = (pdfId: number) => {
setSelectedPdfId(pdfId);
};
const downloadPdf = async (pdfId: number, filename: string) => {
try {
const res = await apiRequest("GET", `/api/documents/claim-pdf/${pdfId}`);
const arrayBuffer = await res.arrayBuffer();
const blob = new Blob([arrayBuffer], { type: "application/pdf" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (err) {
console.error("Failed to download PDF:", err);
}
};
const handleDeletePdf = (pdf: ClaimPdf) => {
setCurrentPdf(pdf);
setIsDeletePdfOpen(true);
};
const handleConfirmDeletePdf = () => {
if (currentPdf) {
deletePdfMutation.mutate(currentPdf.id);
} else {
toast({
title: "Error",
description: "No PDF selected for deletion.",
variant: "destructive",
});
}
};
const filteredPdfs = pdfs.filter((pdf) => {
const patient = pdf.patient;
const searchLower = searchTerm.toLowerCase(); const searchLower = searchTerm.toLowerCase();
const fullName = `${patient.firstName} ${patient.lastName}`.toLowerCase(); const fullName = `${patient.firstName} ${patient.lastName}`.toLowerCase();
const patientId = `PID-${patient.id.toString().padStart(4, '0')}`; const patientId = `PID-${patient.id.toString().padStart(4, "0")}`;
switch (searchField) { switch (searchField) {
case "name": case "name":
@@ -91,7 +188,7 @@ export default function DocumentsPage() {
default: default:
return ( return (
fullName.includes(searchLower) || fullName.includes(searchLower) ||
patientId.toLowerCase().includes(searchLower) || patientId.includes(searchLower) ||
patient.phone?.toLowerCase().includes(searchLower) || patient.phone?.toLowerCase().includes(searchLower) ||
patient.email?.toLowerCase().includes(searchLower) || patient.email?.toLowerCase().includes(searchLower) ||
false false
@@ -99,45 +196,31 @@ export default function DocumentsPage() {
} }
}); });
// Pagination const totalPages = Math.ceil(filteredPdfs.length / itemsPerPage);
const totalPages = Math.ceil(filteredPatients.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage; const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage; const currentPdfs = filteredPdfs.slice(startIndex, startIndex + itemsPerPage);
const currentPatients = filteredPatients.slice(startIndex, endIndex);
const toggleMobileMenu = () => {
setIsMobileMenuOpen(!isMobileMenuOpen);
};
const formatDate = (dateString: string) => {
const date = new Date(dateString);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
});
};
const getPatientInitials = (firstName: string, lastName: string) => {
return `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase();
};
return ( return (
<div className="flex h-screen bg-gray-50"> <div className="flex h-screen bg-gray-50">
<Sidebar isMobileOpen={isMobileMenuOpen} setIsMobileOpen={setIsMobileMenuOpen} /> <Sidebar
isMobileOpen={isMobileMenuOpen}
setIsMobileOpen={setIsMobileMenuOpen}
/>
<div className="flex-1 flex flex-col overflow-hidden"> <div className="flex-1 flex flex-col overflow-hidden">
<TopAppBar toggleMobileMenu={toggleMobileMenu} /> <TopAppBar toggleMobileMenu={toggleMobileMenu} />
<main className="flex-1 overflow-auto p-6"> <main className="flex-1 overflow-auto p-6">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
{/* Header */}
<div className="mb-6"> <div className="mb-6">
<h1 className="text-2xl font-semibold text-gray-900 mb-2">Documents</h1> <h1 className="text-2xl font-semibold text-gray-900 mb-2">
<p className="text-gray-600">View and manage all patient information</p> Documents
</h1>
<p className="text-gray-600">
View and manage recent uploaded claim PDFs
</p>
</div> </div>
{/* Search and Filters */}
<Card className="mb-6"> <Card className="mb-6">
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex flex-col md:flex-row gap-4"> <div className="flex flex-col md:flex-row gap-4">
@@ -171,14 +254,18 @@ export default function DocumentsPage() {
</CardContent> </CardContent>
</Card> </Card>
{/* Patient List */}
<Card> <Card>
<CardContent className="p-0"> <CardContent className="p-0">
{isLoadingPatients ? ( {isLoading ? (
<div className="text-center py-8">Loading patients...</div> <div className="text-center py-8">Loading data...</div>
) : currentPdfs.length === 0 ? (
<div className="text-center py-8 text-gray-500">
{searchTerm
? "No results matching your search."
: "No recent claim PDFs available."}
</div>
) : ( ) : (
<> <>
{/* Table Header */}
<div className="grid grid-cols-12 gap-4 p-4 bg-gray-50 border-b text-sm font-medium text-gray-600"> <div className="grid grid-cols-12 gap-4 p-4 bg-gray-50 border-b text-sm font-medium text-gray-600">
<div className="col-span-3">Patient</div> <div className="col-span-3">Patient</div>
<div className="col-span-2">DOB / Gender</div> <div className="col-span-2">DOB / Gender</div>
@@ -188,33 +275,30 @@ export default function DocumentsPage() {
<div className="col-span-1">Actions</div> <div className="col-span-1">Actions</div>
</div> </div>
{/* Table Rows */} {currentPdfs.map((pdf) => {
{currentPatients.length === 0 ? ( const patient = pdf.patient;
<div className="text-center py-8 text-gray-500"> return (
{searchTerm ? "No patients found matching your search." : "No patients available."}
</div>
) : (
currentPatients.map((patient) => (
<div <div
key={patient.id} key={pdf.id}
className="grid grid-cols-12 gap-4 p-4 border-b hover:bg-gray-50 transition-colors" className="grid grid-cols-12 gap-4 p-4 border-b hover:bg-gray-50"
> >
{/* Patient Info */}
<div className="col-span-3 flex items-center space-x-3"> <div className="col-span-3 flex items-center space-x-3">
<div className="w-10 h-10 bg-gray-200 rounded-full flex items-center justify-center text-sm font-medium text-gray-600"> <div className="w-10 h-10 bg-gray-200 rounded-full flex items-center justify-center text-sm font-medium text-gray-600">
{getPatientInitials(patient.firstName, patient.lastName)} {getPatientInitials(
patient.firstName,
patient.lastName
)}
</div> </div>
<div> <div>
<div className="font-medium text-gray-900"> <div className="font-medium text-gray-900">
{patient.firstName} {patient.lastName} {patient.firstName} {patient.lastName}
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
PID-{patient.id.toString().padStart(4, '0')} PID-{patient.id.toString().padStart(4, "0")}
</div> </div>
</div> </div>
</div> </div>
{/* DOB / Gender */}
<div className="col-span-2"> <div className="col-span-2">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">
{formatDate(patient.dateOfBirth)} {formatDate(patient.dateOfBirth)}
@@ -224,78 +308,141 @@ export default function DocumentsPage() {
</div> </div>
</div> </div>
{/* Contact */}
<div className="col-span-2"> <div className="col-span-2">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">
{patient.phone || 'Not provided'} {patient.phone || "Not provided"}
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
{patient.email || 'No email'} {patient.email || "No email"}
</div> </div>
</div> </div>
{/* Insurance */}
<div className="col-span-2"> <div className="col-span-2">
<div className="text-sm text-gray-900"> <div className="text-sm text-gray-900">
{patient.insuranceProvider ? {patient.insuranceProvider
`${patient.insuranceProvider.charAt(0).toUpperCase()}${patient.insuranceProvider.slice(1)}` : ? `${patient.insuranceProvider.charAt(0).toUpperCase()}${patient.insuranceProvider.slice(1)}`
'Not specified' : "Not specified"}
}
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
ID: {patient.insuranceId || 'N/A'} ID: {patient.insuranceId || "N/A"}
</div> </div>
</div> </div>
{/* Status */}
<div className="col-span-2"> <div className="col-span-2">
<span className={cn( <span
"inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium", className={cn(
patient.status === 'active' "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium",
? "bg-green-100 text-green-800" patient.status === "active"
: "bg-gray-100 text-gray-800" ? "bg-green-100 text-green-800"
)}> : "bg-gray-100 text-gray-800"
{patient.status === 'active' ? 'Active' : 'Inactive'} )}
>
{patient.status === "active"
? "Active"
: "Inactive"}
</span> </span>
</div> </div>
{/* Actions */}
<div className="col-span-1"> <div className="col-span-1">
<div className="flex space-x-1"> <div className="flex space-x-1">
<Button variant="ghost" size="sm" className="h-8 w-8 p-0"> <Button
<Edit className="h-4 w-4 text-blue-600" /> variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={() => handleDeletePdf(pdf)}
>
<Trash className="h-4 w-4 text-red-600" />
</Button> </Button>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<Button
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={() =>
downloadPdf(pdf.id, pdf.filename)
}
>
<Download className="h-4 w-4 text-blue-600" />
</Button>
<Button
variant="ghost"
size="sm"
className="h-8 w-8 p-0"
onClick={() => viewPdf(pdf.id)}
>
<Eye className="h-4 w-4 text-gray-600" /> <Eye className="h-4 w-4 text-gray-600" />
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
)) );
})}
<DeleteConfirmationDialog
isOpen={isDeletePdfOpen}
onConfirm={handleConfirmDeletePdf}
onCancel={() => setIsDeletePdfOpen(false)}
entityName={`PDF #${currentPdf?.id}`}
/>
{/* PDF Viewer */}
{selectedPdfId && fileBlobUrl && (
<div className="mt-6 border rounded-lg shadow-sm p-4 bg-white">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold text-gray-700">
Viewing PDF #{selectedPdfId}
</h2>
<Button
variant="ghost"
onClick={() => {
setSelectedPdfId(null);
setFileBlobUrl(null);
}}
>
Close
</Button>
</div>
<div className="h-[80vh] border">
<Worker workerUrl="https://unpkg.com/pdfjs-dist@3.11.174/build/pdf.worker.min.js">
<Viewer
fileUrl={fileBlobUrl}
plugins={[defaultLayoutPluginInstance]}
/>
</Worker>
</div>
</div>
)} )}
{/* Pagination */} {/* Pagination */}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex items-center justify-between p-4 border-t bg-gray-50"> <div className="flex items-center justify-between p-4 border-t bg-gray-50">
<div className="text-sm text-gray-700"> <div className="text-sm text-gray-700">
Showing {startIndex + 1} to {Math.min(endIndex, filteredPatients.length)} of {filteredPatients.length} results Showing {startIndex + 1} to{" "}
{Math.min(
startIndex + itemsPerPage,
filteredPdfs.length
)}{" "}
of {filteredPdfs.length} results
</div> </div>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setCurrentPage(Math.max(1, currentPage - 1))} onClick={() => setCurrentPage(currentPage - 1)}
disabled={currentPage === 1} disabled={currentPage === 1}
> >
<ChevronLeft className="h-4 w-4 mr-1" /> <ChevronLeft className="h-4 w-4 mr-1" />
Previous Previous
</Button> </Button>
{Array.from(
{/* Page Numbers */} { length: totalPages },
{Array.from({ length: totalPages }, (_, i) => i + 1).map((page) => ( (_, i) => i + 1
).map((page) => (
<Button <Button
key={page} key={page}
variant={currentPage === page ? "default" : "outline"} variant={
currentPage === page ? "default" : "outline"
}
size="sm" size="sm"
onClick={() => setCurrentPage(page)} onClick={() => setCurrentPage(page)}
className="w-8 h-8 p-0" className="w-8 h-8 p-0"
@@ -303,11 +450,10 @@ export default function DocumentsPage() {
{page} {page}
</Button> </Button>
))} ))}
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => setCurrentPage(Math.min(totalPages, currentPage + 1))} onClick={() => setCurrentPage(currentPage + 1)}
disabled={currentPage === totalPages} disabled={currentPage === totalPages}
> >
Next Next

356
package-lock.json generated
View File

@@ -13,8 +13,11 @@
"packages/*" "packages/*"
], ],
"dependencies": { "dependencies": {
"@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/default-layout": "^3.12.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"dotenv-cli": "^8.0.0", "dotenv-cli": "^8.0.0",
"pdfjs-dist": "^3.11.174",
"shx": "^0.4.0" "shx": "^0.4.0"
}, },
"devDependencies": { "devDependencies": {
@@ -3293,6 +3296,254 @@
"integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==", "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@react-pdf-viewer/attachment": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/attachment/-/attachment-3.12.0.tgz",
"integrity": "sha512-mhwrYJSIpCvHdERpLUotqhMgSjhtF+BTY1Yb9Fnzpcq3gLZP+Twp5Rynq21tCrVdDizPaVY7SKu400GkgdMfZw==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/bookmark": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/bookmark/-/bookmark-3.12.0.tgz",
"integrity": "sha512-i7nEit8vIFMAES8RFGwprZ9cXOOZb9ZStPW6E6yuObJEXcvBj/ctsbBJGZxqUZOGklM0JoB7sjHyxAriHfe92A==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/core": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/core/-/core-3.12.0.tgz",
"integrity": "sha512-8MsdlQJ4jaw3GT+zpCHS33nwnvzpY0ED6DEahZg9WngG++A5RMhk8LSlxdHelwaFFHFiXBjmOaj2Kpxh50VQRg==",
"license": "https://react-pdf-viewer.dev/license",
"peerDependencies": {
"pdfjs-dist": "^2.16.105 || ^3.0.279",
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/default-layout": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/default-layout/-/default-layout-3.12.0.tgz",
"integrity": "sha512-K2fS4+TJynHxxCBFuIDiFuAw3nqOh4bkBgtVZ/2pGvnFn9lLg46YGLMnTXCQqtyZzzXYh696jmlFViun3is4pA==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/attachment": "3.12.0",
"@react-pdf-viewer/bookmark": "3.12.0",
"@react-pdf-viewer/core": "3.12.0",
"@react-pdf-viewer/thumbnail": "3.12.0",
"@react-pdf-viewer/toolbar": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/full-screen": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/full-screen/-/full-screen-3.12.0.tgz",
"integrity": "sha512-hQouJ26QUaRBCXNMU1aI1zpJn4l4PJRvlHhuE2dZYtLl37ycjl7vBCQYZW1FwnuxMWztZsY47R43DKaZORg0pg==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/get-file": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/get-file/-/get-file-3.12.0.tgz",
"integrity": "sha512-Uhq45n2RWlZ7Ec/BtBJ0WQESRciaYIltveDXHNdWvXgFdOS8XsvB+mnTh/wzm7Cfl9hpPyzfeezifdU9AkQgQg==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/open": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/open/-/open-3.12.0.tgz",
"integrity": "sha512-vhiDEYsiQLxvZkIKT9VPYHZ1BOnv46x9eCEmRWxO1DJ8fa/GRDTA9ivXmq/ap0dGEJs6t+epleCkCEfllLR/Yw==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/page-navigation": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/page-navigation/-/page-navigation-3.12.0.tgz",
"integrity": "sha512-tVEJ48Dd5kajV1nKkrPWijglJRNBiKBTyYDKVexhiRdTHUP1f6QQXiSyDgCUb0IGSZeJzOJb1h7ApKHe8OTtuw==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/print": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/print/-/print-3.12.0.tgz",
"integrity": "sha512-xJn76CgbU/M2iNaN7wLHTg+sdOekkRMfCakFLwPrE+SR7qD6NUF4vQQKJBSVCCK5bUijzb6cWfKGfo8VA72o4Q==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/properties": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/properties/-/properties-3.12.0.tgz",
"integrity": "sha512-dYTCHtVwFNkpDo7QxL2qk/8zAKndLwdD1FFxBftl6jIlQbtvNdxkFfkv1HcQING9Ic+7DBryOiD7W0ze4IERYg==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/rotate": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/rotate/-/rotate-3.12.0.tgz",
"integrity": "sha512-yaxaMYPChvNOjR8+AxRmj0kvojyJKPq4XHEcIB2lJJgBY1Zra3mliDUP3Nlb4yV8BS9+yBqWn9U9mtnopQD+tw==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/scroll-mode": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/scroll-mode/-/scroll-mode-3.12.0.tgz",
"integrity": "sha512-okII7Xqhl6cMvl1izdEvlXNJ+vJVq/qdg53hJIDYVgBCWskLk/cpjUg/ZonBxseG9lIDP3w2VO1McT8Gn11OAg==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/search": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/search/-/search-3.12.0.tgz",
"integrity": "sha512-jAkLpis49fsDDY/HrbUZIOIhzF5vynONQNA4INQKI38r/MjveblrkNv7qbr9j5lQ/WFic5+gD1e+Mtpf1/7DiA==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/selection-mode": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/selection-mode/-/selection-mode-3.12.0.tgz",
"integrity": "sha512-yysWEu2aCtBvzSgbhgI9kT5cq2hf0FU6Z+3B7MMXz14Kxyc3y18wUqxtgbvpFEfWF0bNUUq16JtWRljtxvZ83w==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/theme": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/theme/-/theme-3.12.0.tgz",
"integrity": "sha512-cdBi+wR1VOZ6URCcO9plmAZQu4ZGFcd7HJdBe7VIFiGyrvl9I/Of74ONLycnDImSuONt8D3uNjPBLieeaShVeg==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/thumbnail": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/thumbnail/-/thumbnail-3.12.0.tgz",
"integrity": "sha512-Vc8j3bO6wumWZV4o6pAbktPWKDSC9tQAzOCJ3cof541u4i44C11ccYC4W9aNcsMMUSO3bNwAGWtP8OFthV5akQ==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/toolbar": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/toolbar/-/toolbar-3.12.0.tgz",
"integrity": "sha512-qACTU3qXHgtNK8J+T13EWio+0liilj86SJ87BdapqXynhl720OKPlSKOQqskUGqg3oTUJAhrse9XG6SFdHJx+g==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0",
"@react-pdf-viewer/full-screen": "3.12.0",
"@react-pdf-viewer/get-file": "3.12.0",
"@react-pdf-viewer/open": "3.12.0",
"@react-pdf-viewer/page-navigation": "3.12.0",
"@react-pdf-viewer/print": "3.12.0",
"@react-pdf-viewer/properties": "3.12.0",
"@react-pdf-viewer/rotate": "3.12.0",
"@react-pdf-viewer/scroll-mode": "3.12.0",
"@react-pdf-viewer/search": "3.12.0",
"@react-pdf-viewer/selection-mode": "3.12.0",
"@react-pdf-viewer/theme": "3.12.0",
"@react-pdf-viewer/zoom": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@react-pdf-viewer/zoom": {
"version": "3.12.0",
"resolved": "https://registry.npmjs.org/@react-pdf-viewer/zoom/-/zoom-3.12.0.tgz",
"integrity": "sha512-V0GUTyPM77+LzhoKX+T3XI10/HfGdqRTbgeP7ID60FCzcwu6kXWqJn5tzabjDKLTlFv8mJmn0aa/ppkIU97nfA==",
"license": "https://react-pdf-viewer.dev/license",
"dependencies": {
"@react-pdf-viewer/core": "3.12.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@replit/vite-plugin-shadcn-theme-json": { "node_modules/@replit/vite-plugin-shadcn-theme-json": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/@replit/vite-plugin-shadcn-theme-json/-/vite-plugin-shadcn-theme-json-0.0.4.tgz", "resolved": "https://registry.npmjs.org/@replit/vite-plugin-shadcn-theme-json/-/vite-plugin-shadcn-theme-json-0.0.4.tgz",
@@ -5290,6 +5541,22 @@
], ],
"license": "CC-BY-4.0" "license": "CC-BY-4.0"
}, },
"node_modules/canvas": {
"version": "2.11.2",
"resolved": "https://registry.npmjs.org/canvas/-/canvas-2.11.2.tgz",
"integrity": "sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@mapbox/node-pre-gyp": "^1.0.0",
"nan": "^2.17.0",
"simple-get": "^3.0.3"
},
"engines": {
"node": ">=6"
}
},
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -5879,6 +6146,19 @@
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/decompress-response": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz",
"integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==",
"license": "MIT",
"optional": true,
"dependencies": {
"mimic-response": "^2.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dedent": { "node_modules/dedent": {
"version": "1.6.0", "version": "1.6.0",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz",
@@ -8587,6 +8867,19 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/mimic-response": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz",
"integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/min-indent": { "node_modules/min-indent": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -8706,6 +8999,13 @@
"thenify-all": "^1.0.0" "thenify-all": "^1.0.0"
} }
}, },
"node_modules/nan": {
"version": "2.22.2",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
"integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
"license": "MIT",
"optional": true
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.11", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -9315,11 +9615,34 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/path2d-polyfill": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path2d-polyfill/-/path2d-polyfill-2.0.1.tgz",
"integrity": "sha512-ad/3bsalbbWhmBo0D6FZ4RNMwsLsPpL6gnvhuSaU5Vm7b06Kr5ubSltQQ0T7YKsiJQO+g22zJ4dJKNTXIyOXtA==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/pause": { "node_modules/pause": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
}, },
"node_modules/pdfjs-dist": {
"version": "3.11.174",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.11.174.tgz",
"integrity": "sha512-TdTZPf1trZ8/UFu5Cx/GXB7GZM30LT+wWUNfsi6Bq8ePLnb+woNKtDymI2mxZYBpMbonNFqKmiz684DIfnd8dA==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"canvas": "^2.11.2",
"path2d-polyfill": "^2.0.1"
}
},
"node_modules/pdfservice": { "node_modules/pdfservice": {
"resolved": "apps/PdfService", "resolved": "apps/PdfService",
"link": true "link": true
@@ -10940,6 +11263,39 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/simple-concat": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz",
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT",
"optional": true
},
"node_modules/simple-get": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz",
"integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==",
"license": "MIT",
"optional": true,
"dependencies": {
"decompress-response": "^4.2.0",
"once": "^1.3.1",
"simple-concat": "^1.0.0"
}
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",

View File

@@ -31,8 +31,11 @@
"packages/*" "packages/*"
], ],
"dependencies": { "dependencies": {
"@react-pdf-viewer/core": "^3.12.0",
"@react-pdf-viewer/default-layout": "^3.12.0",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"dotenv-cli": "^8.0.0", "dotenv-cli": "^8.0.0",
"pdfjs-dist": "^3.11.174",
"shx": "^0.4.0" "shx": "^0.4.0"
} }
} }