diff --git a/src/components/Common/FilePreviewDialog.tsx b/src/components/Common/FilePreviewDialog.tsx index 4b7a07bd0cd..fa8a333515d 100644 --- a/src/components/Common/FilePreviewDialog.tsx +++ b/src/components/Common/FilePreviewDialog.tsx @@ -4,6 +4,7 @@ import { SetStateAction, Suspense, lazy, + useEffect, useState, } from "react"; import { useTranslation } from "react-i18next"; @@ -15,6 +16,8 @@ import CircularProgress from "@/components/Common/CircularProgress"; import DialogModal from "@/components/Common/Dialog"; import { StateInterface } from "@/components/Files/FileUpload"; +import { FileUploadModel } from "../Patient/models"; + const PDFViewer = lazy(() => import("@/components/Common/PDFViewer")); export const zoom_values = [ @@ -40,6 +43,9 @@ type FilePreviewProps = { className?: string; titleAction?: ReactNode; fixedWidth?: boolean; + uploadedFiles?: FileUploadModel[]; + loadFile?: (file: FileUploadModel, associating_id: string) => void; + currentIndex: number; }; const previewExtensions = [ @@ -56,12 +62,28 @@ const previewExtensions = [ ]; const FilePreviewDialog = (props: FilePreviewProps) => { - const { show, onClose, file_state, setFileState, downloadURL, fileUrl } = - props; + const { + show, + onClose, + file_state, + setFileState, + downloadURL, + fileUrl, + uploadedFiles, + loadFile, + currentIndex, + } = props; const { t } = useTranslation(); const [page, setPage] = useState(1); const [numPages, setNumPages] = useState(1); + const [index, setIndex] = useState(currentIndex); + + useEffect(() => { + if (uploadedFiles && show) { + setIndex(currentIndex); + } + }, [uploadedFiles, show, currentIndex]); const handleZoomIn = () => { const checkFull = file_state.zoom === zoom_values.length; @@ -79,9 +101,27 @@ const FilePreviewDialog = (props: FilePreviewProps) => { }); }; + const handleNext = (newIndex: number) => { + if ( + !uploadedFiles?.length || + !loadFile || + newIndex < 0 || + newIndex >= uploadedFiles.length + ) + return; + + const nextFile = uploadedFiles[newIndex]; + if (!nextFile?.id) return; + + const associating_id = nextFile.associating_id || ""; + loadFile(nextFile, associating_id); + setIndex(newIndex); + }; + const handleClose = () => { setPage(1); setNumPages(1); + setIndex(-1); onClose?.(); }; @@ -102,6 +142,21 @@ const FilePreviewDialog = (props: FilePreviewProps) => { : `rotate-${normalizedRotation}`; } + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (!show) return; + if (e.key === "ArrowLeft" && index > 0) { + handleNext(index - 1); + } + if (e.key === "ArrowRight" && index < (uploadedFiles?.length || 0) - 1) { + handleNext(index + 1); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [show, index, uploadedFiles]); + return ( { onClose={() => { handleClose(); }} - title={t("file_preview")} + title={{t("file_preview")}} show={show} > {fileUrl ? ( <> -
-

- {file_state.name}.{file_state.extension} -

-
+
+
+

+ {file_state.name}.{file_state.extension} +

+ {uploadedFiles && + uploadedFiles[index] && + uploadedFiles[index].created_date && ( +

+ Created on{" "} + {new Date( + uploadedFiles[index].created_date!, + ).toLocaleString("en-US", { + dateStyle: "long", + timeStyle: "short", + })} +

+ )} +
+
{downloadURL && downloadURL.length > 0 && ( {
+ {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index - 1)} + disabled={index <= 0} + aria-label="Previous file" + onKeyDown={(e) => + e.key === "ArrowLeft" && handleNext(index - 1) + } + > + + + )}
{file_state.isImage ? ( {
)}
+ + {uploadedFiles && uploadedFiles.length > 1 && ( + handleNext(index + 1)} + disabled={index >= uploadedFiles.length - 1} + aria-label="Next file" + onKeyDown={(e) => + e.key === "ArrowRight" && handleNext(index + 1) + } + > + + + )}
diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx index d98df131863..50ab3ffdb01 100644 --- a/src/components/Files/FileUpload.tsx +++ b/src/components/Files/FileUpload.tsx @@ -69,6 +69,8 @@ export interface StateInterface { isZoomInDisabled: boolean; isZoomOutDisabled: boolean; rotation: number; + id?: string; + associating_id?: string; } export const FileUpload = (props: FileUploadProps) => { @@ -208,8 +210,15 @@ export const FileUpload = (props: FileUploadProps) => { type, onArchive: refetchAll, onEdit: refetchAll, + uploadedFiles: + fileQuery?.data?.results + .slice() + .reverse() + .map((file) => ({ + ...file, + associating_id: associatedId, + })) || [], }); - const dischargeSummaryFileManager = useFileManager({ type: "DISCHARGE_SUMMARY", onArchive: refetchAll, @@ -244,7 +253,6 @@ export const FileUpload = (props: FileUploadProps) => { id: "record-audio", }, ]; - return (
{fileUpload.Dialogues} diff --git a/src/components/Patient/FileUploadPage.tsx b/src/components/Patient/FileUploadPage.tsx index 518644f54d7..d38b149ebeb 100644 --- a/src/components/Patient/FileUploadPage.tsx +++ b/src/components/Patient/FileUploadPage.tsx @@ -11,12 +11,10 @@ export default function FileUploadPage(props: { type: "CONSULTATION" | "PATIENT"; }) { const { facilityId, patientId, consultationId, type } = props; - const { data: patient } = useQuery(routes.getPatient, { pathParams: { id: patientId }, prefetch: !!patientId, }); - return ( void; onEdit?: () => void; + uploadedFiles?: FileUploadModel[]; } export interface FileManagerResult { viewFile: (file: FileUploadModel, associating_id: string) => void; @@ -48,7 +49,7 @@ export interface FileManagerResult { export default function useFileManager( options: FileManagerOptions, ): FileManagerResult { - const { type: fileType, onArchive, onEdit } = options; + const { type: fileType, onArchive, onEdit, uploadedFiles } = options; const [file_state, setFileState] = useState({ open: false, @@ -72,6 +73,7 @@ export default function useFileManager( const [editDialogueOpen, setEditDialogueOpen] = useState(null); const [editError, setEditError] = useState(""); + const [currentIndex, setCurrentIndex] = useState(-1); const getExtension = (url: string) => { const div1 = url.split("?")[0].split("."); @@ -80,6 +82,8 @@ export default function useFileManager( }; const viewFile = async (file: FileUploadModel, associating_id: string) => { + const index = uploadedFiles?.findIndex((f) => f.id === file.id) ?? -1; + setCurrentIndex(index); setFileUrl(""); setFileState({ ...file_state, open: true }); const { data } = await request(routes.retrieveUpload, { @@ -225,9 +229,12 @@ export default function useFileManager( file_state={file_state} setFileState={setFileState} downloadURL={downloadURL} + uploadedFiles={uploadedFiles} onClose={handleFilePreviewClose} fixedWidth={false} className="h-[80vh] w-full md:h-screen" + loadFile={viewFile} + currentIndex={currentIndex} />