diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx
index f0d0ab08f04..5c09b0fd201 100644
--- a/src/Common/constants.tsx
+++ b/src/Common/constants.tsx
@@ -1370,3 +1370,18 @@ export const PATIENT_NOTES_THREADS = {
} as const;
export const RATION_CARD_CATEGORY = ["BPL", "APL", "NO_CARD"] as const;
+
+export const DEFAULT_ALLOWED_EXTENSIONS = [
+ "image/*",
+ "video/*",
+ "audio/*",
+ "text/plain",
+ "text/csv",
+ "application/rtf",
+ "application/msword",
+ "application/vnd.oasis.opendocument.text",
+ "application/pdf",
+ "application/vnd.ms-excel",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "application/vnd.oasis.opendocument.spreadsheet,application/pdf",
+];
diff --git a/src/Components/Common/FilePreviewDialog.tsx b/src/Components/Common/FilePreviewDialog.tsx
index 9bf9780c7a3..b4443d330ea 100644
--- a/src/Components/Common/FilePreviewDialog.tsx
+++ b/src/Components/Common/FilePreviewDialog.tsx
@@ -32,6 +32,19 @@ type FilePreviewProps = {
fixedWidth?: boolean;
};
+const previewExtensions = [
+ ".html",
+ ".htm",
+ ".pdf",
+ ".mp4",
+ ".webm",
+ ".jpg",
+ ".jpeg",
+ ".png",
+ ".gif",
+ ".webp",
+];
+
const FilePreviewDialog = (props: FilePreviewProps) => {
const { show, onClose, file_state, setFileState, downloadURL, fileUrl } =
props;
@@ -130,13 +143,21 @@ const FilePreviewDialog = (props: FilePreviewProps) => {
}}
pageNumber={page}
/>
- ) : (
+ ) : previewExtensions.includes(file_state.extension) ? (
+ ) : (
+
+
+ Can't preview this file. Try downloading it.
+
)}
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx
index 6e1a976de35..a728ffc805a 100644
--- a/src/Components/Facility/ConsultationForm.tsx
+++ b/src/Components/Facility/ConsultationForm.tsx
@@ -1,6 +1,6 @@
import * as Notification from "../../Utils/Notifications.js";
-import { BedModel, ConsentRecord, FacilityModel } from "./models";
+import { BedModel, FacilityModel } from "./models";
import {
CONSULTATION_SUGGESTION,
DISCHARGE_REASONS,
@@ -119,7 +119,6 @@ type FormDetails = {
death_confirmed_doctor: string;
InvestigationAdvice: InvestigationType[];
procedures: ProcedureType[];
- consent_records: ConsentRecord[];
};
const initForm: FormDetails = {
@@ -170,7 +169,6 @@ const initForm: FormDetails = {
death_confirmed_doctor: "",
InvestigationAdvice: [],
procedures: [],
- consent_records: [],
};
const initError = Object.assign(
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx
index 8e54f0896bc..74a0d4a0f26 100644
--- a/src/Components/Facility/models.tsx
+++ b/src/Components/Facility/models.tsx
@@ -99,12 +99,18 @@ export interface OptionsType {
export type PatientCategory = "Comfort Care" | "Mild" | "Moderate" | "Critical";
-export type ConsentRecord = {
+export interface PatientConsentModel {
id: string;
type: (typeof CONSENT_TYPE_CHOICES)[number]["id"];
- patient_code_status?: (typeof CONSENT_PATIENT_CODE_STATUS_CHOICES)[number]["id"];
- deleted?: boolean;
-};
+ patient_code_status:
+ | (typeof CONSENT_PATIENT_CODE_STATUS_CHOICES)[number]["id"]
+ | null;
+ archived: boolean;
+ archived_by?: UserBareMinimum;
+ archived_date: string;
+ created_date: string;
+ created_by: UserBareMinimum;
+}
export interface ConsultationModel {
encounter_date: string;
@@ -174,7 +180,6 @@ export interface ConsultationModel {
is_readmission?: boolean;
medico_legal_case?: boolean;
investigation?: InvestigationType[];
- consent_records?: ConsentRecord[];
}
export interface PatientStatsModel {
diff --git a/src/Components/Patient/PatientConsentRecordBlock.tsx b/src/Components/Patient/PatientConsentRecordBlock.tsx
index 9c1969ae5a3..4998ded0acf 100644
--- a/src/Components/Patient/PatientConsentRecordBlock.tsx
+++ b/src/Components/Patient/PatientConsentRecordBlock.tsx
@@ -3,185 +3,174 @@ import {
CONSENT_PATIENT_CODE_STATUS_CHOICES,
CONSENT_TYPE_CHOICES,
} from "../../Common/constants";
-import routes from "../../Redux/api";
-import useQuery from "../../Utils/request/useQuery";
-import { ConsentRecord } from "../Facility/models";
import { FileUploadModel } from "./models";
import CareIcon from "../../CAREUI/icons/CareIcon";
import ButtonV2 from "../Common/components/ButtonV2";
-import { useEffect } from "react";
import useAuthUser from "../../Common/hooks/useAuthUser";
+import { PatientConsentModel } from "../Facility/models";
+import { SelectFormField } from "../Form/FormFields/SelectFormField";
+import { useEffect, useState } from "react";
+import request from "../../Utils/request/request";
+import routes from "../../Redux/api";
export default function PatientConsentRecordBlockGroup(props: {
- consentRecord: ConsentRecord;
+ consentRecord: PatientConsentModel;
+ consultationId: string;
previewFile: (file: FileUploadModel, file_associating_id: string) => void;
archiveFile: (
file: FileUploadModel,
file_associating_id: string,
skipPrompt?: { reason: string },
) => void;
- onDelete: (consentRecord: ConsentRecord) => void;
- refreshTrigger: any;
+ editFile: (file: FileUploadModel) => void;
showArchive: boolean;
- onFilesFound: () => void;
+ files?: FileUploadModel[];
}) {
const {
consentRecord,
previewFile,
archiveFile,
- refreshTrigger,
+ editFile,
+ files,
showArchive,
+ consultationId,
} = props;
const authUser = useAuthUser();
- const filesQuery = useQuery(routes.viewUpload, {
- query: {
- file_type: "CONSENT_RECORD",
- associating_id: consentRecord.id,
- is_archived: false,
- limit: 100,
- offset: 0,
- },
- onResponse: (response) => {
- /*
- if (consentRecord.deleted === true && response.data?.results) {
- const unarchivedFiles = response.data.results;
- console.log("checking for unarchived files on this deleted consent record")
- for (const file of unarchivedFiles) {
- console.log("archiving file", file)
- archiveFile(file, consentRecord.id, {
- reason: "Consent Record Archived",
- });
- }
- }
- */
-
- if ((response.data?.results?.length || 0) > 0) {
- props.onFilesFound();
- }
- },
- });
-
- const archivedFilesQuery = useQuery(routes.viewUpload, {
- query: {
- file_type: "CONSENT_RECORD",
- associating_id: consentRecord.id,
- is_archived: true,
- limit: 100,
- offset: 0,
- },
- prefetch: showArchive,
- onResponse: (response) => {
- if ((response.data?.results?.length || 0) > 0) {
- props.onFilesFound();
- }
- },
- });
-
const consent = CONSENT_TYPE_CHOICES.find((c) => c.id === consentRecord.type);
const consentPCS = CONSENT_PATIENT_CODE_STATUS_CHOICES.find(
(c) => c.id === consentRecord.patient_code_status,
);
+ const [patientCodeStatus, setPatientCodeStatus] = useState(1);
- const data = showArchive
- ? [
- ...(archivedFilesQuery.data?.results || []),
- ...(consentRecord.deleted ? filesQuery.data?.results || [] : []),
- ]
- : filesQuery.data?.results;
-
- const loading = archivedFilesQuery.loading || filesQuery.loading;
+ const handlePCSUpdate = async (status: number) => {
+ const res = await request(routes.partialUpdateConsent, {
+ pathParams: {
+ id: consentRecord.id,
+ consultationId: consultationId,
+ },
+ body: {
+ patient_code_status: status,
+ },
+ });
+ if (res.data) window.location.reload();
+ };
useEffect(() => {
- if (!showArchive) {
- filesQuery.refetch();
- } else {
- archivedFilesQuery.refetch();
- }
- }, [showArchive, refreshTrigger]);
+ if (consentRecord.patient_code_status !== null)
+ setPatientCodeStatus(consentRecord.patient_code_status);
+ }, [consentRecord]);
+
+ // see if the user has permission to edit the file.
+ // only the user who uploaded the file, district admin and state admin can edit the file
+ const hasEditPermission = (file: FileUploadModel) =>
+ file?.uploaded_by?.username === authUser.username ||
+ authUser.user_type === "DistrictAdmin" ||
+ authUser.user_type === "StateAdmin";
return (
{consent?.text} {consentPCS?.text && `(${consentPCS.text})`}
- {consentRecord.deleted && (
-
- )}
- {/*
- {!consentRecord.deleted && !showArchive && (
-
- )}
- */}
- {loading ? (
-
- ) : (
- data?.map((file: FileUploadModel, i: number) => (
-
+
{
+ console.log(e.value);
+ setPatientCodeStatus(e.value);
+ }}
+ value={
+ CONSENT_PATIENT_CODE_STATUS_CHOICES.find(
+ (c) => c.id === patientCodeStatus,
+ )?.id
+ }
+ optionValue={(option: any) => option.id}
+ optionLabel={(option: any) => option.text}
+ options={CONSENT_PATIENT_CODE_STATUS_CHOICES}
+ required
+ error="Please select a patient code status type"
+ />
+ {
+ handlePCSUpdate(patientCodeStatus);
+ }}
+ disabled={patientCodeStatus === consentRecord.patient_code_status}
+ className="h-[46px]"
>
-
-
-
+ Update
+
+
+ )}
+ {files?.map((file: FileUploadModel, i: number) => (
+
+
+
+
+
+
+
+ {file.name}
+ {file.extension} {file.is_archived && "(Archived)"}
-
-
- {file.name}
- {file.extension} {file.is_archived && "(Archived)"}
-
-
- {dayjs(file.created_date).format("DD MMM YYYY, hh:mm A")}
-
+
+ {dayjs(
+ file.is_archived ? file.archived_datetime : file.created_date,
+ ).format("DD MMM YYYY, hh:mm A")}{" "}
+ by{" "}
+ {file.is_archived
+ ? file.archived_by?.username
+ : file.uploaded_by?.username}
-
- {!file.is_archived && (
- previewFile(file, consentRecord.id)}
- className=""
- >
-
- View
-
- )}
- {(file.is_archived ||
- file?.uploaded_by?.username === authUser.username ||
- authUser.user_type === "DistrictAdmin" ||
- authUser.user_type === "StateAdmin") && (
- archiveFile(file, consentRecord.id)}
- className=""
- >
-
- {file.is_archived ? "More Info" : "Archive"}
-
- )}
-
- ))
- )}
+
+ {!file.is_archived && (
+ previewFile(file, consentRecord.id)}
+ className=""
+ >
+
+ View
+
+ )}
+ {!file.is_archived && hasEditPermission(file) && (
+ editFile(file)}
+ className=""
+ >
+
+ Rename
+
+ )}
+ {(file.is_archived || hasEditPermission(file)) && (
+ archiveFile(file, consentRecord.id)}
+ className=""
+ >
+
+ {file.is_archived ? "More Info" : "Archive"}
+
+ )}
+
+
+ ))}
);
}
diff --git a/src/Components/Patient/PatientConsentRecords.tsx b/src/Components/Patient/PatientConsentRecords.tsx
index ca26b270d80..d850179c6d0 100644
--- a/src/Components/Patient/PatientConsentRecords.tsx
+++ b/src/Components/Patient/PatientConsentRecords.tsx
@@ -6,7 +6,6 @@ import {
import routes from "../../Redux/api";
import useQuery from "../../Utils/request/useQuery";
import Page from "../Common/components/Page";
-import { ConsentRecord } from "../Facility/models";
import request from "../../Utils/request/request";
import ConfirmDialog from "../Common/ConfirmDialog";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
@@ -18,6 +17,7 @@ import useFileUpload from "../../Utils/useFileUpload";
import PatientConsentRecordBlockGroup from "./PatientConsentRecordBlock";
import SwitchTabs from "../Common/components/SwitchTabs";
import useFileManager from "../../Utils/useFileManager";
+import { PatientConsentModel } from "../Facility/models";
export default function PatientConsentRecords(props: {
facilityId: string;
@@ -26,7 +26,6 @@ export default function PatientConsentRecords(props: {
}) {
const { facilityId, patientId, consultationId } = props;
const [showArchived, setShowArchived] = useState(false);
- const [filesFound, setFilesFound] = useState(false);
const [showPCSChangeModal, setShowPCSChangeModal] = useState
(
null,
);
@@ -35,14 +34,24 @@ export default function PatientConsentRecords(props: {
patient_code_status: 4,
});
+ const refetchAll = () => {
+ refetch();
+ refetchFiles();
+ refetchArchivedFiles();
+ };
+
const fileUpload = useFileUpload({
type: "CONSENT_RECORD",
+ allowedExtensions: ["pdf", "jpg", "jpeg", "png"],
});
const fileManager = useFileManager({
type: "CONSENT_RECORD",
onArchive: async () => {
- refetch();
+ refetchAll();
+ },
+ onEdit: async () => {
+ refetchAll();
},
});
@@ -51,36 +60,48 @@ export default function PatientConsentRecords(props: {
id: patientId,
},
});
- const {
- data: consultation,
- refetch,
- loading,
- } = useQuery(routes.getConsultation, {
- pathParams: { id: consultationId! },
- onResponse: (data) => {
- if (data.data && data.data.consent_records) {
- setConsentRecords(data.data.consent_records);
- }
+
+ const { data: consentRecordsData, refetch } = useQuery(routes.listConsents, {
+ pathParams: {
+ consultationId,
+ },
+ query: {
+ limit: 1000,
+ offset: 0,
},
});
- const [showDeleteConsent, setShowDeleteConsent] = useState(
- null,
+ const consentRecords = consentRecordsData?.results;
+
+ const { data: unarchivedFiles, refetch: refetchFiles } = useQuery(
+ routes.viewUpload,
+ {
+ query: {
+ file_type: "CONSENT_RECORD",
+ associating_id: consentRecords?.map((cr) => cr.id).join(","),
+ limit: 1000,
+ offset: 0,
+ is_archived: false,
+ },
+ prefetch: (consentRecords?.length || 0) > 0 && showArchived,
+ },
);
- const [consentRecords, setConsentRecords] = useState(
- null,
+ const { data: archivedFiles, refetch: refetchArchivedFiles } = useQuery(
+ routes.viewUpload,
+ {
+ query: {
+ file_type: "CONSENT_RECORD",
+ associating_id: consentRecords?.map((cr) => cr.id).join(","),
+ limit: 1000,
+ offset: 0,
+ is_archived: true,
+ },
+ prefetch: (consentRecords?.length || 0) > 0 && !showArchived,
+ },
);
- const handleDeleteConsent = async () => {
- const consent_id = showDeleteConsent;
- if (!consent_id || !consultationId || !consentRecords) return;
- const newRecords = consentRecords.map((cr) =>
- cr.id === consent_id ? { ...cr, deleted: true } : cr,
- );
- setConsentRecords(newRecords);
- setShowDeleteConsent(null);
- };
+ const files = showArchived ? archivedFiles : unarchivedFiles;
const selectField = (name: string) => {
return {
@@ -91,55 +112,36 @@ export default function PatientConsentRecords(props: {
};
};
- const handleUpload = async (diffPCS?: ConsentRecord) => {
- if (newConsent.type === 0) return;
- const consentTypeExists = consentRecords?.find(
- (record) => record.type === newConsent.type && record.deleted !== true,
+ const handleUpload = async (diffPCS?: PatientConsentModel) => {
+ const consentExists = consentRecords?.find(
+ (record) => record.type === newConsent.type && !record.archived,
);
- if (consentTypeExists && !diffPCS) {
- await fileUpload.handleFileUpload(consentTypeExists.id);
- } else {
- const randomId = "consent-" + new Date().getTime().toString();
- const newRecords = [
- ...(consentRecords?.map((r) =>
- r.id === diffPCS?.id ? { ...r, deleted: true } : r,
- ) || []),
- {
- id: randomId,
- type: newConsent.type,
+ let consentId = consentExists?.id;
+ if (!consentExists || diffPCS) {
+ consentId = undefined;
+ const res = await request(routes.createConsent, {
+ pathParams: { consultationId: consultationId },
+ body: {
+ ...newConsent,
patient_code_status:
newConsent.type === 2 ? newConsent.patient_code_status : undefined,
},
- ];
- await request(routes.partialUpdateConsultation, {
- pathParams: { id: consultationId },
- body: { consent_records: newRecords },
});
- await fileUpload.handleFileUpload(randomId);
- setConsentRecords(newRecords);
+ if (res.data) {
+ consentId = res.data.id;
+ }
}
-
+ consentId && (await fileUpload.handleFileUpload(consentId));
refetch();
};
useEffect(() => {
- const timeout = setTimeout(async () => {
- if (consentRecords) {
- await request(routes.partialUpdateConsultation, {
- pathParams: { id: consultationId },
- body: { consent_records: consentRecords },
- });
- }
- }, 1000);
- return () => clearTimeout(timeout);
+ if (consentRecords && consentRecords.length > 0) {
+ refetchFiles();
+ refetchArchivedFiles();
+ }
}, [consentRecords]);
- const tabConsents = consentRecords?.filter((c) => showArchived || !c.deleted);
-
- useEffect(() => {
- setFilesFound(false);
- }, [showArchived]);
-
return (
{fileUpload.Dialogues}
{fileManager.Dialogues}
- setShowDeleteConsent(null)}
- onConfirm={handleDeleteConsent}
- action="Archive"
- variant="danger"
- description={
- "Are you sure you want to archive this consent record? You can find it in the archive section."
- }
- title="Archive Consent"
- className="w-auto"
- />
setShowPCSChangeModal(null)}
@@ -180,7 +170,7 @@ export default function PatientConsentRecords(props: {
consentRecords?.find(
(record) =>
record.type === 2 &&
- !record.deleted &&
+ !record.archived &&
record.patient_code_status !== showPCSChangeModal,
),
);
@@ -189,7 +179,7 @@ export default function PatientConsentRecords(props: {
}}
action="Change Patient Code Status"
variant="danger"
- description={`Consent records exist with the "${CONSENT_PATIENT_CODE_STATUS_CHOICES.find((c) => consentRecords?.find((c) => c.type === 2 && !c.deleted)?.patient_code_status === c.id)?.text}" patient code status. Adding a new record for a different type will archive the existing records. Are you sure you want to proceed?`}
+ description={`Consent records exist with the "${CONSENT_PATIENT_CODE_STATUS_CHOICES.find((c) => consentRecords?.find((c) => c.type === 2 && !c.archived)?.patient_code_status === c.id)?.text}" patient code status. Adding a new record for a different type will archive the existing records. Are you sure you want to proceed?`}
title="Archive Previous Records"
className="w-auto"
/>
@@ -246,7 +236,7 @@ export default function PatientConsentRecords(props: {
newConsent.type === 2 &&
record.patient_code_status !==
newConsent.patient_code_status &&
- record.deleted !== true,
+ record.archived !== true,
);
if (diffPCS) {
setShowPCSChangeModal(newConsent.patient_code_status);
@@ -255,6 +245,10 @@ export default function PatientConsentRecords(props: {
}
}}
loading={!!fileUpload.progress}
+ disabled={
+ newConsent.type === 2 &&
+ newConsent.patient_code_status === 0
+ }
className="flex-1"
>
@@ -281,29 +275,37 @@ export default function PatientConsentRecords(props: {
>
)}
+ {fileUpload.error}
-
- {loading ? (
+ {consentRecords?.filter(
+ (r) =>
+ files?.results.filter((f) => f.associating_id === r.id).length,
+ ).length === 0 ? (
+
+ No consent records found
+
+ ) : (
+ (!unarchivedFiles || !archivedFiles) &&
+ !consentRecords && (
- ) : tabConsents?.length === 0 || !filesFound ? (
-
- No records found
-
- ) : null}
- {!loading &&
- tabConsents?.map((record, index) => (
-
setShowDeleteConsent(record.id)}
- refreshTrigger={consultation}
- showArchive={showArchived}
- onFilesFound={() => setFilesFound(true)}
- />
- ))}
+ )
+ )}
+
+ {consentRecords?.map((record, index) => (
+
f.associating_id === record.id,
+ )}
+ />
+ ))}
diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx
index 247d9312d6e..0b05009d4a2 100644
--- a/src/Components/Patient/PatientInfoCard.tsx
+++ b/src/Components/Patient/PatientInfoCard.tsx
@@ -140,6 +140,30 @@ export default function PatientInfoCard(props: {
prefetch: !!consultation?.treating_physician_object?.username,
});
+ const { data: consentRecords, loading: consentRecordsLoading } = useQuery(
+ routes.listConsents,
+ {
+ pathParams: {
+ consultationId: consultation?.id ?? "",
+ },
+ prefetch: !!consultation?.id,
+ },
+ );
+
+ const { data: consentFiles, loading: consentFilesLoading } = useQuery(
+ routes.viewUpload,
+ {
+ query: {
+ file_type: "CONSENT_RECORD",
+ associating_id: consentRecords?.results.map((cr) => cr.id).join(","),
+ limit: 1,
+ offset: 0,
+ is_archived: false,
+ },
+ prefetch: (consentRecords?.results.length || 0) > 0,
+ },
+ );
+
return (
<>
)}
- {(
- consultation?.consent_records?.filter((c) => !c.deleted) ||
- []
- ).length < 1 && (
-
-
-
- Consent Records Missing
-
+ {!consentFilesLoading &&
+ !consentRecordsLoading &&
+ !consentFiles?.results.filter((c) => !c.is_archived)
+ .length && (
+
+
+
+ Consent Records Missing
+
+
-
- )}
+ )}
{consultation?.suggestion === "DC" && (
diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx
index f64d71146c4..9664d0f462b 100644
--- a/src/Components/Patient/models.tsx
+++ b/src/Components/Patient/models.tsx
@@ -354,6 +354,7 @@ export interface CreateFileResponse {
export interface FileUploadModel {
id?: string;
name?: string;
+ associating_id?: string;
created_date?: string;
upload_completed?: boolean;
uploaded_by?: PerformedByModel;
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 439b122ca09..8cda2038ecc 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -65,6 +65,7 @@ import {
LocalBodyModel,
LocationModel,
MinimumQuantityItemResponse,
+ PatientConsentModel,
PatientNotesEditModel,
PatientNotesModel,
PatientStatsModel,
@@ -1000,6 +1001,30 @@ const routes = {
TRes: Type
>(),
},
+ // Consents
+ listConsents: {
+ path: "/api/v1/consultation/{consultationId}/consents/",
+ method: "GET",
+ TRes: Type>(),
+ },
+ getConsent: {
+ path: "/api/v1/consultation/{consultationId}/consents/{id}/",
+ method: "GET",
+ TRes: Type(),
+ },
+ createConsent: {
+ path: "/api/v1/consultation/{consultationId}/consents/",
+ method: "POST",
+ TRes: Type(),
+ TBody: Type>(),
+ },
+ partialUpdateConsent: {
+ path: "/api/v1/consultation/{consultationId}/consents/{id}/",
+ method: "PATCH",
+ TRes: Type(),
+ TBody: Type>(),
+ },
+
//Profile
checkUsername: {
path: "/api/v1/users/{username}/check_availability/",
diff --git a/src/Utils/useFileManager.tsx b/src/Utils/useFileManager.tsx
index 133b4d2533d..d45ddc28f06 100644
--- a/src/Utils/useFileManager.tsx
+++ b/src/Utils/useFileManager.tsx
@@ -10,10 +10,12 @@ import TextAreaFormField from "../Components/Form/FormFields/TextAreaFormField";
import { Cancel, Submit } from "../Components/Common/components/ButtonV2";
import { formatDateTime } from "./utils";
import * as Notification from "./Notifications.js";
+import TextFormField from "../Components/Form/FormFields/TextFormField";
export interface FileManagerOptions {
type: string;
onArchive?: () => void;
+ onEdit?: () => void;
}
export interface FileManagerResult {
@@ -23,13 +25,14 @@ export interface FileManagerResult {
associating_id: string,
skipPrompt?: { reason: string },
) => void;
+ editFile: (file: FileUploadModel) => void;
Dialogues: React.ReactNode;
}
export default function useFileManager(
options: FileManagerOptions,
): FileManagerResult {
- const { type: fileType, onArchive } = options;
+ const { type: fileType, onArchive, onEdit } = options;
const [file_state, setFileState] = useState({
open: false,
@@ -49,6 +52,10 @@ export default function useFileManager(
const [archiveReason, setArchiveReason] = useState("");
const [archiveReasonError, setArchiveReasonError] = useState("");
const [archiving, setArchiving] = useState(false);
+ const [editing, setEditing] = useState(false);
+ const [editDialogueOpen, setEditDialogueOpen] =
+ useState(null);
+ const [editError, setEditError] = useState("");
const getExtension = (url: string) => {
const div1 = url.split("?")[0].split(".");
@@ -155,6 +162,43 @@ export default function useFileManager(
});
};
+ const validateEditFileName = (name: string) => {
+ if (name.trim() === "") {
+ setEditError("Please enter a name!");
+ return false;
+ } else {
+ setEditError("");
+ return true;
+ }
+ };
+
+ const partialupdateFileName = async (file: FileUploadModel) => {
+ if (!validateEditFileName(file.name || "")) {
+ setEditing(false);
+ return;
+ }
+
+ const { res } = await request(routes.editUpload, {
+ body: { name: file.name },
+ pathParams: {
+ id: file.id || "",
+ fileType,
+ associatingId: file.associating_id || "",
+ },
+ });
+
+ if (res?.ok) {
+ Notification.Success({ msg: "File name changed successfully" });
+ setEditDialogueOpen(null);
+ onEdit && onEdit();
+ }
+ setEditing(false);
+ };
+
+ const editFile = (file: FileUploadModel) => {
+ setEditDialogueOpen(file);
+ };
+
const Dialogues = (
<>
+ {archiveDialogueOpen?.name} (Archived)
+
+ }
+ fixedWidth={false}
+ className="md:w-[700px]"
+ onClose={() => setArchiveDialogueOpen(null)}
+ >
+
+
+ This file has been archived and cannot be unarchived.
+
+
+ {[
+ {
+ label: "File Name",
+ content: archiveDialogueOpen?.name,
+ icon: "l-file",
+ },
+ {
+ label: "Uploaded By",
+ content: archiveDialogueOpen?.uploaded_by?.username,
+ icon: "l-user",
+ },
+ {
+ label: "Uploaded On",
+ content: formatDateTime(archiveDialogueOpen?.created_date),
+ icon: "l-clock",
+ },
+ {
+ label: "Archive Reason",
+ content: archiveDialogueOpen?.archive_reason,
+ icon: "l-archive",
+ },
+ {
+ label: "Archived By",
+ content: archiveDialogueOpen?.archived_by?.username,
+ icon: "l-user",
+ },
+ {
+ label: "Archived On",
+ content: formatDateTime(archiveDialogueOpen?.archived_datetime),
+ icon: "l-clock",
+ },
+ ].map((item, index) => (
+
+
+
+
+
+
+ {item.label}
+
+
{item.content}
+
+
+ ))}
+
+
+ setArchiveDialogueOpen(null)} />
+
+
+
-
+
-
-
File Details
- This file is archived. Once a file is archived it cannot be
- unarchived.
+
+
Rename File
}
- onClose={() => setArchiveDialogueOpen(null)}
+ onClose={() => setEditDialogueOpen(null)}
>
-
+
>
);
@@ -269,6 +389,7 @@ export default function useFileManager(
return {
viewFile,
archiveFile,
+ editFile,
Dialogues,
};
}
diff --git a/src/Utils/useFileUpload.tsx b/src/Utils/useFileUpload.tsx
index e28e8c9b1b5..f072d66d6bc 100644
--- a/src/Utils/useFileUpload.tsx
+++ b/src/Utils/useFileUpload.tsx
@@ -16,6 +16,7 @@ import routes from "../Redux/api";
import uploadFile from "./request/uploadFile";
import * as Notification from "./Notifications.js";
import imageCompression from "browser-image-compression";
+import { DEFAULT_ALLOWED_EXTENSIONS } from "../Common/constants";
export type FileUploadOptions = {
type: string;
@@ -23,10 +24,10 @@ export type FileUploadOptions = {
onUpload?: (file: FileUploadModel) => void;
} & (
| {
- allowAllExtensions?: boolean;
+ allowedExtensions?: string[];
}
| {
- allowedExtensions?: string[];
+ allowAllExtensions?: boolean;
}
);
@@ -146,6 +147,16 @@ export default function useFileUpload(
setError("Maximum size of files is 100 MB");
return false;
}
+ const extension = f.name.split(".").pop();
+ if (
+ "allowedExtensions" in options &&
+ !options.allowedExtensions?.includes(extension || "")
+ ) {
+ setError(
+ `Invalid file type ".${extension}" Allowed types: ${options.allowedExtensions?.join(", ")}`,
+ );
+ return false;
+ }
return true;
};
const markUploadComplete = (
@@ -414,11 +425,11 @@ export default function useFileUpload(
onChange={onFileChange}
type="file"
accept={
- "allowAllExtensions" in options
- ? "image/*,video/*,audio/*,text/plain,text/csv,application/rtf,application/msword,application/vnd.oasis.opendocument.text,application/pdf,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.oasis.opendocument.spreadsheet,application/pdf"
- : "allowedExtensions" in options
- ? options.allowedExtensions?.join(",")
- : ""
+ "allowedExtensions" in options
+ ? options.allowedExtensions?.join(",")
+ : "allowAllExtensions" in options
+ ? "*"
+ : DEFAULT_ALLOWED_EXTENSIONS.join(",")
}
hidden
/>