diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx index 77c67abeadd..6f9ca63d51b 100644 --- a/src/Components/Patient/FileUpload.tsx +++ b/src/Components/Patient/FileUpload.tsx @@ -3,21 +3,12 @@ import CircularProgress from "../Common/components/CircularProgress"; import { useCallback, useState, - useEffect, useRef, lazy, ChangeEvent, + useEffect, } from "react"; -import { useDispatch } from "react-redux"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { - viewUpload, - retrieveUpload, - createUpload, - getPatient, - editUpload, -} from "../../Redux/actions"; -import { FileUploadModel } from "./models"; +import { CreateFileResponse, FileUploadModel } from "./models"; import * as Notification from "../../Utils/Notifications.js"; import { VoiceRecorder } from "../../Utils/VoiceRecorder"; import Pagination from "../Common/Pagination"; @@ -39,6 +30,9 @@ import AuthorizedChild from "../../CAREUI/misc/AuthorizedChild"; import Page from "../Common/components/Page"; import FilePreviewDialog from "../Common/FilePreviewDialog"; import useAuthUser from "../../Common/hooks/useAuthUser"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import request from "../../Utils/request/request"; const Loading = lazy(() => import("../Common/Loading")); @@ -145,7 +139,6 @@ export const FileUpload = (props: FileUploadProps) => { claimId, } = props; const id = patientId; - const dispatch: any = useDispatch(); const [isLoading, setIsLoading] = useState(false); const [uploadedArchievedFiles, setuploadedArchievedFiles] = useState< Array @@ -157,7 +150,6 @@ export const FileUpload = (props: FileUploadProps) => { useState>([{}]); const [uploadStarted, setUploadStarted] = useState(false); const [audiouploadStarted, setAudioUploadStarted] = useState(false); - const [reload, setReload] = useState(false); const [uploadPercent, setUploadPercent] = useState(0); const [uploadFileName, setUploadFileName] = useState(""); const [uploadFileError, setUploadFileError] = useState(""); @@ -204,8 +196,6 @@ export const FileUpload = (props: FileUploadProps) => { const [totalDischargeSummaryFilesCount, setTotalDischargeSummaryFilesCount] = useState(0); const [offset, setOffset] = useState(0); - const [facilityName, setFacilityName] = useState(""); - const [patientName, setPatientName] = useState(""); const [modalOpenForEdit, setModalOpenForEdit] = useState(false); const [modalOpenForCamera, setModalOpenForCamera] = useState(false); const [modalOpenForArchive, setModalOpenForArchive] = useState(false); @@ -219,28 +209,15 @@ export const FileUpload = (props: FileUploadProps) => { const [sortFileState, setSortFileState] = useState("UNARCHIVED"); const authUser = useAuthUser(); const limit = RESULTS_PER_PAGE_LIMIT; - const [isActive, setIsActive] = useState(true); const [tabs, setTabs] = useState([ { name: "Unarchived Files", value: "UNARCHIVED" }, { name: "Archived Files", value: "ARCHIVED" }, ]); - useEffect(() => { - async function fetchPatientName() { - if (patientId) { - const res = await dispatch(getPatient({ id: patientId })); - if (res.data) { - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); - setIsActive(res.data.is_active); - } - } else { - setPatientName(""); - setFacilityName(""); - } - } - fetchPatientName(); - }, [dispatch, patientId]); + const { data: patient } = useQuery(routes.getPatient, { + pathParams: { id: patientId }, + prefetch: !!patientId, + }); const captureImage = () => { setPreviewImage(webRef.current.getScreenshot()); @@ -298,104 +275,93 @@ export const FileUpload = (props: FileUploadProps) => { } }; - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const unarchivedFileData = { + const fetchData = useCallback(async () => { + setIsLoading(true); + + const unarchivedQuery = await request(routes.viewUpload, { + query: { file_type: type, associating_id: getAssociatedId(), is_archived: false, limit: limit, offset: offset, - }; - let res = await dispatch(viewUpload(unarchivedFileData)); - if (!status.aborted) { - if (res?.data) { - audio_urls(res.data.results); - setuploadedUnarchievedFiles( - res?.data?.results?.filter( - (file: FileUploadModel) => - file.upload_completed || file.file_category === "AUDIO" - ) - ); - setTotalUnarchievedFilesCount(res.data.count); - } - setIsLoading(false); - } - const archivedFileData = { + }, + }); + + if (unarchivedQuery.data) { + audio_urls(unarchivedQuery.data.results); + setuploadedUnarchievedFiles( + unarchivedQuery.data.results?.filter( + (file) => file.upload_completed || file.file_category === "AUDIO" + ) + ); + setTotalUnarchievedFilesCount(unarchivedQuery.data.count); + } + + const archivedQuery = await request(routes.viewUpload, { + query: { file_type: type, associating_id: getAssociatedId(), is_archived: true, limit: limit, offset: offset, - }; - res = await dispatch(viewUpload(archivedFileData)); - if (!status.aborted) { - if (res?.data) { - setuploadedArchievedFiles(res.data.results); - setTotalArchievedFilesCount(res.data.count); - } - setIsLoading(false); - } - if (type === "CONSULTATION") { - const dischargeSummaryFileData = { + }, + }); + + if (archivedQuery.data) { + setuploadedArchievedFiles(archivedQuery.data.results); + setTotalArchievedFilesCount(archivedQuery.data.count); + } + + if (type === "CONSULTATION") { + const dischargeSummaryQuery = await request(routes.viewUpload, { + query: { file_type: "DISCHARGE_SUMMARY", associating_id: getAssociatedId(), is_archived: false, limit: limit, offset: offset, - }; - res = await dispatch(viewUpload(dischargeSummaryFileData)); - if (!status.aborted) { - if (res?.data) { - setuploadedDischargeSummaryFiles(res.data.results); - setTotalDischargeSummaryFilesCount(res.data.count); - if (res?.data?.results?.length > 0) { - setTabs([ - ...tabs, - { - name: "Discharge Summary", - value: "DISCHARGE_SUMMARY", - }, - ]); - } - } + }, + }); + if (dischargeSummaryQuery.data) { + setuploadedDischargeSummaryFiles(dischargeSummaryQuery.data.results); + setTotalDischargeSummaryFilesCount(dischargeSummaryQuery.data.count); + if (dischargeSummaryQuery.data?.results?.length) { + setTabs([ + ...tabs, + { + name: "Discharge Summary", + value: "DISCHARGE_SUMMARY", + }, + ]); } - setIsLoading(false); } - }, - [dispatch, id, offset] - ); + } - // Store all audio urls for each audio file - const audio_urls = (files: any) => { - let audio_files = files || []; - audio_files = audio_files.filter( - (x: FileUploadModel) => x.file_category === "AUDIO" - ); + setIsLoading(false); + }, [id, offset]); - const getURL = async (audio_files: any) => { - const data = { file_type: type, associating_id: getAssociatedId() }; - const all_urls: any = {}; + useEffect(() => { + fetchData(); + }, [fetchData]); - for (const x of audio_files) { - if (x.id) { - const responseData = await dispatch(retrieveUpload(data, x.id)); - all_urls[`${x.id}`] = responseData.data.read_signed_url; - } - } - seturl(all_urls); - }; - getURL(audio_files); + // Store all audio urls for each audio file + const audio_urls = async (files: FileUploadModel[]) => { + const audioFiles = files.filter((x) => x.file_category === "AUDIO"); + const query = { file_type: type, associating_id: getAssociatedId() }; + const urls = await Promise.all( + audioFiles.map(async (file) => { + const id = file.id as string; + const { data } = await request(routes.retrieveUpload, { + query, + pathParams: { id: id as string }, + }); + return [id, data?.read_signed_url]; + }) + ); + seturl(Object.fromEntries(urls)); }; - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [dispatch, fetchData, id, reload] - ); - // Function to extract the extension of the file const getExtension = (url: string) => { const div1 = url.split("?")[0].split("."); @@ -473,28 +439,35 @@ export const FileUpload = (props: FileUploadProps) => { return "fa-solid fa-file-medical"; }; - const loadFile = async (id: any) => { + const loadFile = async (id: string) => { setFileUrl(""); setFileState({ ...file_state, open: true }); - const data = { - file_type: sortFileState === "DISCHARGE_SUMMARY" ? sortFileState : type, - associating_id: getAssociatedId(), - }; - const responseData = await dispatch(retrieveUpload(data, id)); - const file_extension = getExtension(responseData.data.read_signed_url); - if (file_extension === "pdf") { - window.open(responseData.data.read_signed_url, "_blank"); + const { data } = await request(routes.retrieveUpload, { + query: { + file_type: sortFileState === "DISCHARGE_SUMMARY" ? sortFileState : type, + associating_id: getAssociatedId(), + }, + pathParams: { id }, + }); + + if (!data) return; + + const signedUrl = data.read_signed_url as string; + const extension = getExtension(signedUrl); + + if (extension === "pdf") { + window.open(signedUrl, "_blank"); setFileState({ ...file_state, open: false }); } else { setFileState({ ...file_state, open: true, - name: responseData.data.name, - extension: file_extension, - isImage: ExtImage.includes(file_extension), + name: data.name as string, + extension, + isImage: ExtImage.includes(extension), }); - downloadFileUrl(responseData.data.read_signed_url); - setFileUrl(responseData.data.read_signed_url); + downloadFileUrl(signedUrl); + setFileUrl(signedUrl); } }; @@ -518,63 +491,54 @@ export const FileUpload = (props: FileUploadProps) => { } }; - const partialupdateFileName = async (id: any, name: string) => { - const data = { - file_type: sortFileState === "DISCHARGE_SUMMARY" ? sortFileState : type, - name: name, - associating_id: getAssociatedId(), - }; - if (validateEditFileName(name)) { - const res = await dispatch( - editUpload({ name: data.name }, id, data.file_type, data.associating_id) - ); - if (res && res.status === 200) { - fetchData(res.status); - Notification.Success({ - msg: "File name changed successfully", - }); - setbtnloader(false); - setModalOpenForEdit(false); - } else { - setbtnloader(false); - } - } else { + const partialupdateFileName = async (id: string, name: string) => { + if (!validateEditFileName(name)) { setbtnloader(false); + return; } + + const fileType = + sortFileState === "DISCHARGE_SUMMARY" ? sortFileState : type; + + const { res } = await request(routes.editUpload, { + body: { name }, + pathParams: { + id, + fileType, + associatingId: getAssociatedId(), + }, + }); + + if (res?.ok) { + fetchData(); + Notification.Success({ msg: "File name changed successfully" }); + setModalOpenForEdit(false); + } + setbtnloader(false); }; - const archiveFile = async (id: any, archiveReason: string) => { - const data = { - file_type: type, - is_archived: true, - archive_reason: archiveReason, - associating_id: getAssociatedId(), - }; - if (validateArchiveReason(archiveReason)) { - const res = await dispatch( - editUpload( - { - is_archived: data.is_archived, - archive_reason: data.archive_reason, - }, - id, - data.file_type, - data.associating_id - ) - ); - if (res && res.status === 200) { - fetchData(res.status); - Notification.Success({ - msg: "File archived successfully", - }); - setbtnloader(false); - setModalOpenForArchive(false); - } else { - setbtnloader(false); - } - } else { + const archiveFile = async (id: string, archive_reason: string) => { + if (!validateArchiveReason(archiveReason)) { setbtnloader(false); + return; } + + const { res } = await request(routes.editUpload, { + body: { is_archived: true, archive_reason }, + pathParams: { + id, + fileType: type, + associatingId: getAssociatedId(), + }, + }); + + if (res?.ok) { + fetchData(); + Notification.Success({ msg: "File archived successfully" }); + setModalOpenForArchive(false); + } + + setbtnloader(false); }; const renderFileUpload = (item: FileUploadModel) => { @@ -739,7 +703,7 @@ export const FileUpload = (props: FileUploadProps) => {
{ - loadFile(item.id); + loadFile(item.id!); }} className="m-1 w-full sm:w-auto" > @@ -932,9 +896,9 @@ export const FileUpload = (props: FileUploadProps) => { setFile(f); }; - const uploadfile = (response: any) => { - const url = response.data.signed_url; - const internal_name = response.data.internal_name; + const uploadfile = async (data: CreateFileResponse) => { + const url = data.signed_url; + const internal_name = data.internal_name; const f = file; if (!f) return; const newFile = new File([f], `${internal_name}`); @@ -951,6 +915,7 @@ export const FileUpload = (props: FileUploadProps) => { setUploadPercent(percentCompleted); }, }; + return new Promise((resolve, reject) => { axios .put(url, newFile, config) @@ -959,13 +924,12 @@ export const FileUpload = (props: FileUploadProps) => { // setUploadSuccess(true); setFile(null); setUploadFileName(""); - setReload(!reload); - fetchData({ aborted: false }); + fetchData(); Notification.Success({ msg: "File Uploaded Successfully", }); setUploadFileError(""); - resolve(response); + resolve(); }) .catch((e) => { Notification.Error({ @@ -994,18 +958,18 @@ export const FileUpload = (props: FileUploadProps) => { } return true; }; - const markUploadComplete = async (response: any) => { - return dispatch( - editUpload( - { upload_completed: true }, - response.data.id, - type, - getAssociatedId() - ) - ); + const markUploadComplete = (data: CreateFileResponse) => { + return request(routes.editUpload, { + body: { upload_completed: true }, + pathParams: { + id: data.id, + fileType: type, + associatingId: getAssociatedId(), + }, + }); }; - const handleUpload = async (status: any) => { + const handleUpload = async () => { if (!validateFileUpload()) return; const f = file; @@ -1013,24 +977,23 @@ export const FileUpload = (props: FileUploadProps) => { const filename = uploadFileName === "" && f ? f.name : uploadFileName; const name = f?.name; setUploadStarted(true); - // setUploadSuccess(false); - const requestData = { - original_name: name, - file_type: type, - name: filename, - associating_id: getAssociatedId(), - file_category: category, - mime_type: f?.type, - }; - dispatch(createUpload(requestData)) - .then(uploadfile) - .then(markUploadComplete) - .catch(() => { - setUploadStarted(false); - }) - .then(() => { - fetchData(status); - }); + + const { data } = await request(routes.createUpload, { + body: { + original_name: name, + file_type: type, + name: filename, + associating_id: getAssociatedId(), + file_category: category, + mime_type: f?.type, + }, + }); + + if (data) { + await uploadfile(data); + await markUploadComplete(data); + await fetchData(); + } }; const createAudioBlob = (createdBlob: Blob) => { @@ -1071,7 +1034,7 @@ export const FileUpload = (props: FileUploadProps) => { setAudioUploadStarted(false); // setUploadSuccess(true); setAudioName(""); - setReload(!reload); + fetchData(); Notification.Success({ msg: "File Uploaded Successfully", }); @@ -1102,16 +1065,17 @@ export const FileUpload = (props: FileUploadProps) => { const filename = audioName.trim().length === 0 ? Date.now().toString() : audioName.trim(); setAudioUploadStarted(true); - // setUploadSuccess(false); - const requestData = { - original_name: name, - file_type: type, - name: filename, - associating_id: getAssociatedId(), - file_category: category, - mime_type: audioBlob?.type, - }; - dispatch(createUpload(requestData)) + + request(routes.createUpload, { + body: { + original_name: name, + file_type: type, + name: filename, + associating_id: getAssociatedId(), + file_category: category, + mime_type: audioBlob?.type, + }, + }) .then(uploadAudiofile) .catch(() => { setAudioUploadStarted(false); @@ -1318,7 +1282,7 @@ export const FileUpload = (props: FileUploadProps) => { onSubmit={(event: any) => { event.preventDefault(); setbtnloader(true); - partialupdateFileName(modalDetails?.id, editFileName); + partialupdateFileName(modalDetails!.id!, editFileName); }} className="flex w-full flex-col" > @@ -1364,7 +1328,7 @@ export const FileUpload = (props: FileUploadProps) => { onSubmit={(event: any) => { event.preventDefault(); setbtnloader(true); - archiveFile(modalDetails?.id, archiveReason); + archiveFile(modalDetails!.id!, archiveReason); }} className="mx-2 my-4 flex w-full flex-col" > @@ -1433,8 +1397,8 @@ export const FileUpload = (props: FileUploadProps) => { hideBack={hideBack} breadcrumbs={false} crumbsReplacements={{ - [facilityId]: { name: facilityName }, - [patientId]: { name: patientName }, + [facilityId]: { name: patient?.facility_object?.name }, + [patientId]: { name: patient?.name }, }} backUrl={ type === "CONSULTATION" @@ -1558,8 +1522,12 @@ export const FileUpload = (props: FileUploadProps) => { handleUpload({ status })} + disabled={ + !file || + !uploadFileName || + (patient && !patient.is_active) + } + onClick={handleUpload} className="w-full" > diff --git a/src/Components/Patient/UpdateStatusDialog.tsx b/src/Components/Patient/UpdateStatusDialog.tsx index 11f442b7a65..c302aa1b957 100644 --- a/src/Components/Patient/UpdateStatusDialog.tsx +++ b/src/Components/Patient/UpdateStatusDialog.tsx @@ -5,10 +5,8 @@ import { SAMPLE_TEST_RESULT, SAMPLE_FLOW_RULES, } from "../../Common/constants"; -import { SampleTestModel } from "./models"; +import { CreateFileResponse, SampleTestModel } from "./models"; import * as Notification from "../../Utils/Notifications.js"; -import { createUpload, editUpload } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; import { header_content_type, LinearProgressWithLabel } from "./FileUpload"; import { Submit } from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -18,6 +16,8 @@ import { FieldChangeEvent } from "../Form/FormFields/Utils"; import TextFormField from "../Form/FormFields/TextFormField"; import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; import { useTranslation } from "react-i18next"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; interface Props { sample: SampleTestModel; @@ -62,7 +62,6 @@ const UpdateStatusDialog = (props: Props) => { const [uploadPercent, setUploadPercent] = useState(0); const [uploadStarted, setUploadStarted] = useState(false); const [uploadDone, setUploadDone] = useState(false); - const redux_dispatch: any = useDispatch(); const currentStatus = SAMPLE_TEST_STATUS.find( (i) => i.text === sample.status @@ -97,9 +96,10 @@ const UpdateStatusDialog = (props: Props) => { dispatch({ type: "set_form", form }); }; - const uploadfile = (response: any) => { - const url = response.data.signed_url; - const internal_name = response.data.internal_name; + const uploadfile = (data: CreateFileResponse) => { + const url = data.signed_url; + const internal_name = data.internal_name; + const f = file; if (f === undefined) return; const newFile = new File([f], `${internal_name}`); @@ -116,29 +116,27 @@ const UpdateStatusDialog = (props: Props) => { setUploadPercent(percentCompleted); }, }; + axios .put(url, newFile, config) .then(() => { setUploadStarted(false); setUploadDone(true); - redux_dispatch( - editUpload( - { upload_completed: true }, - response.data.id, - "SAMPLE_MANAGEMENT", - sample.id?.toString() ?? "" - ) - ); - Notification.Success({ - msg: "File Uploaded Successfully", + request(routes.editUpload, { + pathParams: { + id: data.id, + fileType: "SAMPLE_MANAGEMENT", + associatingId: sample.id?.toString() ?? "", + }, + body: { upload_completed: true }, }); + + Notification.Success({ msg: "File Uploaded Successfully" }); }) - .catch(() => { - setUploadStarted(false); - }); + .catch(() => setUploadStarted(false)); }; - const onFileChange = (e: React.ChangeEvent): any => { + const onFileChange = (e: React.ChangeEvent) => { if (e.target.files == null) { throw new Error("Error finding e.target.files"); } @@ -155,19 +153,21 @@ const UpdateStatusDialog = (props: Props) => { const name = f.name; setUploadStarted(true); setUploadDone(false); - const requestData = { - original_name: name, - file_type: "SAMPLE_MANAGEMENT", - name: `${sample.patient_name} Sample Report`, - associating_id: sample.id, - file_category: category, - mime_type: contentType, - }; - redux_dispatch(createUpload(requestData)) - .then(uploadfile) - .catch(() => { - setUploadStarted(false); - }); + + const { data } = await request(routes.createUpload, { + body: { + original_name: name, + file_type: "SAMPLE_MANAGEMENT", + name: `${sample.patient_name} Sample Report`, + associating_id: sample.id, + file_category: category, + mime_type: contentType, + }, + }); + + if (data) { + uploadfile(data); + } }; return ( diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx index fc87bedcb14..05eb780069d 100644 --- a/src/Components/Patient/models.tsx +++ b/src/Components/Patient/models.tsx @@ -147,7 +147,7 @@ export interface SampleTestModel { is_unusual_course?: boolean; sample_type?: string; sample_type_other?: string; - id?: number; + id?: string; status?: string; result?: string; icmr_category?: string; @@ -337,16 +337,36 @@ export interface FacilityNameModel { // File Upload Models +type FileCategory = "UNSPECIFIED" | "XRAY" | "AUDIO" | "IDENTITY_PROOF"; + +export interface CreateFileRequest { + file_type: string; + file_category: FileCategory; + name: string; + associating_id: string; + original_name: string; + mime_type: string; +} + +export interface CreateFileResponse { + id: string; + file_type: string; + file_category: FileCategory; + signed_url: string; + internal_name: string; +} + export interface FileUploadModel { id?: string; name?: string; created_date?: string; upload_completed?: boolean; - uploaded_by?: { username?: string }; - file_category?: string; + uploaded_by?: PerformedByModel; + file_category?: FileCategory; + read_signed_url?: string; is_archived?: boolean; archive_reason?: string; extension?: string; - archived_by?: { username?: string }; + archived_by?: PerformedByModel; archived_datetime?: string; } diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index cfe298bda82..c11a5f9de59 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -175,33 +175,6 @@ export const externalResult = (pathParam: object) => { return fireRequest("externalResult", [], {}, pathParam); }; -// FileUpload - -export const createUpload = (params: object) => { - return fireRequest("createUpload", [], params); -}; - -export const viewUpload = (params: object) => { - return fireRequest("viewUpload", [], params); -}; - -export const retrieveUpload = (params: object, fileId: string) => { - return fireRequest("retrieveUpload", [], params, { fileId: fileId }); -}; - -export const editUpload = ( - params: object, - fileId: string, - fileType: string, - associatingId: string -) => { - return fireRequest("editUpload", [], params, { - fileId, - fileType, - associatingId, - }); -}; - // Investigation export const listInvestigations = ( diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 8b2a3ae08c2..8d2a06c10f8 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -75,7 +75,10 @@ import { } from "../Components/Users/models"; import { Prescription } from "../Components/Medicine/models"; import { + CreateFileRequest, + CreateFileResponse, DailyRoundsModel, + FileUploadModel, PatientModel, SampleReportModel, SampleTestModel, @@ -1008,18 +1011,24 @@ const routes = { createUpload: { path: "/api/v1/files/", method: "POST", + TBody: Type(), + TRes: Type(), }, viewUpload: { path: "/api/v1/files/", method: "GET", + TRes: Type>(), }, retrieveUpload: { - path: "/api/v1/files/{fileId}/", + path: "/api/v1/files/{id}/", method: "GET", + TRes: Type(), }, editUpload: { - path: "/api/v1/files/{fileId}/?file_type={fileType}&associating_id={associatingId}", + path: "/api/v1/files/{id}/?file_type={fileType}&associating_id={associatingId}", method: "PATCH", + TBody: Type>(), + TRes: Type(), }, // Investigation