diff --git a/cypress/e2e/facility_spec/locations.cy.ts b/cypress/e2e/facility_spec/locations.cy.ts
index d5b3cc9990f..cac9adbdde1 100644
--- a/cypress/e2e/facility_spec/locations.cy.ts
+++ b/cypress/e2e/facility_spec/locations.cy.ts
@@ -13,6 +13,7 @@ describe("Location Management Section", () => {
cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities");
cy.get("[id='facility-details']").first().click();
cy.wait("@getFacilities").its("response.statusCode").should("eq", 200);
+ cy.get("h1.text-3xl.font-bold", { timeout: 10000 }).should("be.visible");
cy.get("#manage-facility-dropdown button").should("be.visible");
cy.get("[id='manage-facility-dropdown']").scrollIntoView().click();
cy.get("[id=location-management]").click();
diff --git a/cypress/fixtures/sampleAsset.xlsx b/cypress/fixtures/sampleAsset.xlsx
index f8e234ce447..49421f62bac 100644
Binary files a/cypress/fixtures/sampleAsset.xlsx and b/cypress/fixtures/sampleAsset.xlsx differ
diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts
index 42ec6d8fd7c..e0a572ccaec 100644
--- a/cypress/pageobject/Facility/FacilityCreation.ts
+++ b/cypress/pageobject/Facility/FacilityCreation.ts
@@ -106,6 +106,7 @@ class FacilityPage {
}
clickManageFacilityDropdown() {
+ cy.get("h1.text-3xl.font-bold", { timeout: 20000 }).should("be.visible");
cy.get("#manage-facility-dropdown button").scrollIntoView();
cy.get("#manage-facility-dropdown button")
.contains("Manage Facility")
@@ -198,8 +199,6 @@ class FacilityPage {
cy.intercept("GET", "**/api/v1/facility/**").as("getManagePage");
cy.go("back");
cy.wait("@getManagePage").its("response.statusCode").should("eq", 200);
- cy.get("#manage-facility-dropdown").scrollIntoView();
- cy.get("#manage-facility-dropdown").should("exist");
}
verifyfacilityviewassetredirection() {
diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts
index 7f5484564fc..2dd8c477233 100644
--- a/cypress/support/commands.ts
+++ b/cypress/support/commands.ts
@@ -81,7 +81,7 @@ Cypress.Commands.add(
Cypress.Commands.add("verifyNotification", (text) => {
cy.get(".pnotify-container").should("exist").contains(text);
- return cy.get(".pnotify-container").click({ force: true });
+ return cy.get(".pnotify-container").contains(text).click({ force: true });
});
Cypress.on("uncaught:exception", () => {
diff --git a/package-lock.json b/package-lock.json
index 4871fbe374b..2a9fe3f520f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14496,8 +14496,6 @@
},
"node_modules/npm/node_modules/cross-spawn/node_modules/which": {
"version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -15254,8 +15252,6 @@
},
"node_modules/npm/node_modules/minipass": {
"version": "5.0.0",
- "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
- "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16398,8 +16394,6 @@
},
"node_modules/npm/node_modules/tar": {
"version": "6.1.15",
- "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz",
- "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==",
"dev": true,
"inBundle": true,
"license": "ISC",
@@ -16500,8 +16494,6 @@
},
"node_modules/npm/node_modules/util-deprecate": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"dev": true,
"inBundle": true,
"license": "MIT"
@@ -16569,8 +16561,6 @@
},
"node_modules/npm/node_modules/wrap-ansi": {
"version": "8.1.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
- "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16589,8 +16579,6 @@
"node_modules/npm/node_modules/wrap-ansi-cjs": {
"name": "wrap-ansi",
"version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"inBundle": true,
"license": "MIT",
@@ -16670,8 +16658,6 @@
},
"node_modules/npm/node_modules/wrappy": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"dev": true,
"inBundle": true,
"license": "ISC"
@@ -16691,8 +16677,6 @@
},
"node_modules/npm/node_modules/yallist": {
"version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true,
"inBundle": true,
"license": "ISC"
diff --git a/src/CAREUI/display/RecordMeta.tsx b/src/CAREUI/display/RecordMeta.tsx
index 83928f1f23a..48cc8d370ad 100644
--- a/src/CAREUI/display/RecordMeta.tsx
+++ b/src/CAREUI/display/RecordMeta.tsx
@@ -11,13 +11,14 @@ interface Props {
last_name: string;
last_login: string | undefined;
};
+ inlineUser?: boolean;
}
/**
* A generic component to display relative time along with a tooltip and a user
* if provided.
*/
-const RecordMeta = ({ time, user, prefix, className }: Props) => {
+const RecordMeta = ({ time, user, prefix, className, inlineUser }: Props) => {
const isOnline = user && isUserOnline(user);
let child = (
@@ -25,14 +26,15 @@ const RecordMeta = ({ time, user, prefix, className }: Props) => {
{relativeTime(time)}
{formatDateTime(time)}
- {user && (
- <>
+ {user && !inlineUser && (
+
+ by
{user.first_name} {user.last_name}
{isOnline && (
-
+
)}
- >
+
)}
@@ -43,7 +45,13 @@ const RecordMeta = ({ time, user, prefix, className }: Props) => {
{prefix}
{child}
+ {user && inlineUser && by}
{user && }
+ {user && inlineUser && (
+
+ {user.first_name} {user.last_name}
+
+ )}
);
}
diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx
index f64a9fa27ae..e51494f6bac 100644
--- a/src/Common/constants.tsx
+++ b/src/Common/constants.tsx
@@ -896,7 +896,7 @@ export const XLSXAssetImportSchema = {
Class: {
prop: "asset_class",
type: String,
- oneOf: ["HL7MONITOR", "ONVIF"],
+ oneOf: ["HL7MONITOR", "ONVIF", "VENTILATOR", ""],
},
Description: { prop: "description", type: String },
"Working Status": {
@@ -908,7 +908,7 @@ export const XLSXAssetImportSchema = {
} else if (status === "NOT WORKING") {
return false;
} else {
- throw new Error("Invalid Working Status");
+ throw new Error("Invalid Working Status: " + status);
}
},
required: true,
@@ -917,6 +917,7 @@ export const XLSXAssetImportSchema = {
prop: "not_working_reason",
type: String,
},
+ "Serial Number": { prop: "serial_number", type: String },
"QR Code ID": { prop: "qr_code_id", type: String },
Manufacturer: { prop: "manufacturer", type: String },
"Vendor Name": { prop: "vendor_name", type: String },
@@ -925,10 +926,11 @@ export const XLSXAssetImportSchema = {
prop: "support_email",
type: String,
parse: (email: string) => {
+ if (!email) return null;
const isValid = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/.test(email);
if (!isValid) {
- throw new Error("Invalid Support Email");
+ throw new Error("Invalid Support Email: " + email);
}
return email;
@@ -938,23 +940,38 @@ export const XLSXAssetImportSchema = {
prop: "support_phone",
type: String,
parse: (phone: number | string) => {
- phone = "+91" + String(phone);
- if (!PhoneNumberValidator(["support"])(phone) === undefined) {
- throw new Error("Invalid Support Phone Number");
+ phone = String(phone);
+ if (phone.length === 10 && !phone.startsWith("1800")) {
+ phone = "+91" + phone;
+ }
+ if (phone.startsWith("91") && phone.length === 12) {
+ phone = "+" + phone;
+ }
+ if (phone.startsWith("+911800")) {
+ phone = "1800" + phone.slice(6);
+ }
+ if (
+ PhoneNumberValidator(["mobile", "landline", "support"])(phone) !==
+ undefined
+ ) {
+ throw new Error("Invalid Support Phone Number: " + phone);
}
return phone ? phone : undefined;
},
required: true,
},
- "Warrenty End Date": {
+ "Warranty End Date": {
prop: "warranty_amc_end_of_validity",
type: String,
parse: (date: string) => {
- const parsed = new Date(date);
+ if (!date) return null;
+ const parts = date.split("-");
+ const reformattedDateStr = `${parts[2]}-${parts[1]}-${parts[0]}`;
+ const parsed = new Date(reformattedDateStr);
if (String(parsed) === "Invalid Date") {
- throw new Error("Invalid Warrenty End Date");
+ throw new Error("Invalid Warranty End Date:" + date);
}
return dateQueryString(parsed);
@@ -964,10 +981,13 @@ export const XLSXAssetImportSchema = {
prop: "last_serviced_on",
type: String,
parse: (date: string) => {
- const parsed = new Date(date);
+ if (!date) return null;
+ const parts = date.split("-");
+ const reformattedDateStr = `${parts[2]}-${parts[1]}-${parts[0]}`;
+ const parsed = new Date(reformattedDateStr);
if (String(parsed) === "Invalid Date") {
- throw new Error("Invalid Last Service Date");
+ throw new Error("Invalid Last Service Date:" + date);
}
return dateQueryString(parsed);
@@ -981,13 +1001,14 @@ export const XLSXAssetImportSchema = {
prop: "local_ip_address",
type: String,
parse: (ip: string) => {
+ if (!ip) return null;
const isValid =
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(
ip
);
if (!isValid) {
- throw new Error("Invalid Config IP Address");
+ throw new Error("Invalid Config IP Address: " + ip);
}
return ip;
diff --git a/src/Common/hooks/useRangePagination.ts b/src/Common/hooks/useRangePagination.ts
index e6bbe9f573e..7652ae546c1 100644
--- a/src/Common/hooks/useRangePagination.ts
+++ b/src/Common/hooks/useRangePagination.ts
@@ -9,18 +9,17 @@ interface Props {
bounds: DateRange;
perPage: number;
slots?: number;
- snapToLatest?: boolean;
- reverse?: boolean;
+ defaultEnd?: boolean;
}
const useRangePagination = ({ bounds, perPage, ...props }: Props) => {
const [currentRange, setCurrentRange] = useState(
- getInitialBounds(bounds, perPage, props.snapToLatest)
+ getInitialBounds(bounds, perPage, props.defaultEnd)
);
useEffect(() => {
- setCurrentRange(getInitialBounds(bounds, perPage, props.snapToLatest));
- }, [bounds, perPage, props.snapToLatest]);
+ setCurrentRange(getInitialBounds(bounds, perPage, props.defaultEnd));
+ }, [bounds, perPage, props.defaultEnd]);
const next = () => {
const { end } = currentRange;
@@ -63,24 +62,17 @@ const useRangePagination = ({ bounds, perPage, ...props }: Props) => {
}
const slots: DateRange[] = [];
- const { start, end } = currentRange;
+ const { start } = currentRange;
const delta = perPage / props.slots;
for (let i = 0; i < props.slots; i++) {
- if (props.snapToLatest) {
- slots.push({
- start: new Date(end.valueOf() - delta * (i - 1)),
- end: new Date(end.valueOf() - delta * i),
- });
- } else {
- slots.push({
- start: new Date(start.valueOf() + delta * i),
- end: new Date(start.valueOf() + delta * (i + 1)),
- });
- }
+ slots.push({
+ start: new Date(start.valueOf() + delta * i),
+ end: new Date(start.valueOf() + delta * (i + 1)),
+ });
}
- return props.reverse ? slots.reverse() : slots;
+ return slots;
}, [currentRange, props.slots, perPage]);
return {
@@ -98,7 +90,7 @@ export default useRangePagination;
const getInitialBounds = (
bounds: DateRange,
perPage: number,
- snapToLatest?: boolean
+ defaultEnd?: boolean
) => {
const deltaBounds = bounds.end.valueOf() - bounds.start.valueOf();
@@ -106,7 +98,7 @@ const getInitialBounds = (
return bounds;
}
- if (snapToLatest) {
+ if (defaultEnd) {
return {
start: new Date(bounds.end.valueOf() - perPage),
end: bounds.end,
diff --git a/src/Common/hooks/useSlug.ts b/src/Common/hooks/useSlug.ts
new file mode 100644
index 00000000000..69d8f591c84
--- /dev/null
+++ b/src/Common/hooks/useSlug.ts
@@ -0,0 +1,45 @@
+import { usePath } from "raviger";
+
+/**
+ * Returns the slug from the current path.
+ * @param prefix The prefix of the slug.
+ * @returns The slug.
+ * @example
+ * // Current path: /consultation/94b9a
+ * const consultation = useSlug("consultation"); // consultation = "94b9a"
+ */
+export default function useSlug(prefix: string) {
+ const path = usePath() ?? "";
+ return findSlug(path.split("/"), prefix);
+}
+
+/**
+ * Returns the slugs from the current path.
+ * @param prefix The prefixes of the slug.
+ * @returns The slugs
+ * @example
+ * // Current path: /facility/5b0a/consultation/94b9a
+ * const [facility, consultation] = useSlug("facility", "consultation");
+ * // facility = "5b0a"
+ * // consultation = "94b9a"
+ */
+export const useSlugs = (...prefix: string[]) => {
+ const path = usePath() ?? "";
+ return prefix.map((p) => findSlug(path.split("/"), p));
+};
+
+const findSlug = (segments: string[], prefix: string) => {
+ const index = segments.findIndex((segment) => segment === prefix);
+ if (index === -1) {
+ throw new Error(
+ `Prefix "${prefix}" not found in path "${segments.join("/")}"`
+ );
+ }
+
+ const slug = segments[index + 1];
+ if (!slug) {
+ throw new Error(`Slug not found in path "${segments.join("/")}"`);
+ }
+
+ return slug;
+};
diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx
index 548df2901ec..54ac25e2ac8 100644
--- a/src/Components/Assets/AssetImportModal.tsx
+++ b/src/Components/Assets/AssetImportModal.tsx
@@ -8,7 +8,6 @@ import { Cancel, Submit } from "../Common/components/ButtonV2";
import { listFacilityAssetLocation } from "../../Redux/actions";
import { useDispatch } from "react-redux";
import { Link } from "raviger";
-import SelectMenuV2 from "../Form/SelectMenuV2";
import readXlsxFile from "read-excel-file";
import {
LocalStorageKeys,
@@ -17,6 +16,7 @@ import {
import { parseCsvFile } from "../../Utils/utils";
import useConfig from "../../Common/hooks/useConfig";
import DialogModal from "../Common/Dialog";
+import { SelectFormField } from "../Form/FormFields/SelectFormField";
interface Props {
open: boolean;
@@ -25,14 +25,18 @@ interface Props {
}
const AssetImportModal = ({ open, onClose, facility }: Props) => {
- const [isImporting, setIsUploading] = useState(false);
+ const [isImporting, setIsImporting] = useState(false);
const [selectedFile, setSelectedFile] = useState();
const [preview, setPreview] =
useState<(AssetData & { notes?: string; last_serviced_on?: string })[]>();
const [location, setLocation] = useState("");
+ const [errors, setErrors] = useState({
+ location: "",
+ });
const [locations, setLocations] = useState([]);
const dispatchAction: any = useDispatch();
const { sample_format_asset_import } = useConfig();
+ const [locationsLoading, setLocationsLoading] = useState(false);
const closeModal = () => {
setPreview(undefined);
@@ -41,9 +45,11 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
};
useEffect(() => {
+ setLocationsLoading(true);
dispatchAction(
listFacilityAssetLocation({}, { facility_external_id: facility.id })
).then(({ data }: any) => {
+ setLocationsLoading(false);
if (data.count > 0) {
setLocations(data.results);
}
@@ -85,11 +91,11 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
}
}
}
- } catch (e) {
+ } catch (e: any) {
setPreview(undefined);
console.log(e);
Notification.Error({
- msg: "Invalid file",
+ msg: "Invalid file: " + e.message,
});
setSelectedFile(undefined);
}
@@ -110,10 +116,19 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
closeModal();
return;
}
+ if (!location) {
+ setErrors({
+ ...errors,
+ location: "Please select a location",
+ });
+ return;
+ }
+ setIsImporting(true);
let error = false;
+ Notification.Success({ msg: "Importing assets..." });
for (const asset of preview || []) {
- const asset_data = JSON.stringify({
+ const asset_data: any = {
name: asset.name,
asset_type: asset.asset_type,
asset_class: asset.asset_class,
@@ -129,11 +144,15 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
qr_code_id: asset.qr_code_id,
manufacturer: asset.manufacturer,
meta: { ...asset.meta },
- warranty_amc_end_of_validity: asset.warranty_amc_end_of_validity,
- last_serviced_on: asset.last_serviced_on,
note: asset.notes,
- cancelToken: { promise: {} },
- });
+ };
+
+ if (asset.last_serviced_on)
+ asset_data["last_serviced_on"] = asset.last_serviced_on;
+
+ if (asset.warranty_amc_end_of_validity)
+ asset_data["warranty_amc_end_of_validity"] =
+ asset.warranty_amc_end_of_validity;
const response = await fetch("/api/v1/asset/", {
method: "POST",
@@ -142,7 +161,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
Authorization:
"Bearer " + localStorage.getItem(LocalStorageKeys.accessToken),
},
- body: asset_data,
+ body: JSON.stringify(asset_data),
});
const data = await response.json();
if (response.status !== 201) {
@@ -152,16 +171,22 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
});
error = true;
} else {
- if (preview) setPreview(preview.filter((a) => a.id !== asset.id));
+ setPreview((preview) => {
+ return preview?.slice(1);
+ });
}
}
if (!error) {
Notification.Success({ msg: "Assets imported successfully" });
await sleep(1000);
- setIsUploading(false);
- closeModal();
+ setIsImporting(false);
window.location.reload();
- } else Notification.Error({ msg: "Error importing some assets" });
+ } else {
+ Notification.Error({ msg: "Error importing some assets" });
+ await sleep(1000);
+ setIsImporting(false);
+ closeModal();
+ }
};
const dragProps = useDragAndDrop();
@@ -189,7 +214,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
fixedWidth={false}
>
{facility.name}
- {locations.length === 0 ? (
+ {!locationsLoading && locations.length === 0 ? (
<>
@@ -214,31 +239,28 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => {
{preview && preview?.length > 0 ? (
- {preview.length} assets will be imported
+ {preview.length} assets {isImporting ? "are being" : "will be"}{" "}
+ imported
-
diff --git a/src/Components/Assets/AssetServiceEditModal.tsx b/src/Components/Assets/AssetServiceEditModal.tsx
index 66d44d11907..690524e471b 100644
--- a/src/Components/Assets/AssetServiceEditModal.tsx
+++ b/src/Components/Assets/AssetServiceEditModal.tsx
@@ -7,10 +7,10 @@ import DialogModal from "../Common/Dialog";
import { AssetData, AssetService, AssetServiceEdit } from "./AssetTypes";
import dayjs from "dayjs";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
-import DateInputV2 from "../Common/DateInputV2";
-import { FieldLabel } from "../Form/FormFields/FormField";
import { formatDate, formatDateTime } from "../../Utils/utils";
import CareIcon from "../../CAREUI/icons/CareIcon";
+import DateFormField from "../Form/FormFields/DateFormField";
+import { t } from "i18next";
export const AssetServiceEditModal = (props: {
asset?: AssetData;
@@ -61,12 +61,12 @@ export const AssetServiceEditModal = (props: {
- Update record for asset
+ {t("update_record_for_asset")}
{props.asset?.name}
@@ -110,13 +110,17 @@ export const AssetServiceEditModal = (props: {
-
Edited On
+
+ {t("edited_on")}
+
{formatDateTime(editRecord.edited_on)}
-
Edited By
+
+ {t("edited_by")}
+
{editRecord.edited_by.username}
@@ -125,7 +129,7 @@ export const AssetServiceEditModal = (props: {
- Serviced On
+ {t("serviced_on")}
-
Notes
+
+ {t("notes")}
+
{editRecord.note || "-"}
@@ -151,7 +157,7 @@ export const AssetServiceEditModal = (props: {
editRecord ? setEditRecord(undefined) : props.handleClose();
}}
>
- {editRecord ? "Back" : "Close"}
+ {editRecord ? t("back") : t("close")}
@@ -163,12 +169,12 @@ export const AssetServiceEditModal = (props: {
- Update record for asset
+ {t("update_record_for_asset")}
{props.asset?.name}
@@ -178,19 +184,31 @@ export const AssetServiceEditModal = (props: {
className="col-span-6 sm:col-span-3"
data-testid="asset-last-serviced-on-input"
>
-
Serviced On
-
{
- setForm({
- ...form,
- serviced_on: dayjs(date).format("YYYY-MM-DD"),
- });
+ if (
+ dayjs(date.value).format("YYYY-MM-DD") >
+ new Date(
+ props.service_record.created_date
+ ).toLocaleDateString("en-ca")
+ ) {
+ Notification.Error({
+ msg: `Service date can't be after ${formatDate(
+ props.service_record.created_date
+ )} (Creation date)`,
+ });
+ } else {
+ setForm({
+ ...form,
+ serviced_on: dayjs(date.value).format("YYYY-MM-DD"),
+ });
+ }
}}
- max={new Date(props.service_record.created_date)}
/>
@@ -198,8 +216,8 @@ export const AssetServiceEditModal = (props: {
{
setForm({ ...form, note: e.value });
@@ -210,7 +228,7 @@ export const AssetServiceEditModal = (props: {
diff --git a/src/Components/Auth/Login.tsx b/src/Components/Auth/Login.tsx
index cb7ea7f08f8..140a0013fd9 100644
--- a/src/Components/Auth/Login.tsx
+++ b/src/Components/Auth/Login.tsx
@@ -1,10 +1,9 @@
import { useEffect, useState } from "react";
-import { useDispatch } from "react-redux";
-import { postForgotPassword, postLogin } from "../../Redux/actions";
+import request from "../../Utils/request/request";
+import routes from "../../Redux/api";
import { useTranslation } from "react-i18next";
import ReCaptcha from "react-google-recaptcha";
import * as Notification from "../../Utils/Notifications.js";
-import { get } from "lodash";
import LegendInput from "../../CAREUI/interactive/LegendInput";
import LanguageSelectorLogin from "../Common/LanguageSelectorLogin";
import CareIcon from "../../CAREUI/icons/CareIcon";
@@ -25,7 +24,6 @@ export const Login = (props: { forgot?: boolean }) => {
custom_logo_alt,
custom_description,
} = useConfig();
- const dispatch: any = useDispatch();
const initForm: any = {
username: "",
password: "",
@@ -90,37 +88,35 @@ export const Login = (props: { forgot?: boolean }) => {
};
}, []);
- const handleSubmit = (e: any) => {
+ const handleSubmit = async (e: any) => {
e.preventDefault();
const valid = validateData();
if (valid) {
// replaces button with spinner
setLoading(true);
- dispatch(postLogin(valid)).then((resp: any) => {
- const res = get(resp, "data", null);
- const statusCode = get(resp, "status", "");
- if (res && statusCode === 429) {
- setCaptcha(true);
- // captcha displayed set back to login button
- setLoading(false);
- } else if (res && statusCode === 200) {
- localStorage.setItem(LocalStorageKeys.accessToken, res.access);
- localStorage.setItem(LocalStorageKeys.refreshToken, res.refresh);
-
- if (
- window.location.pathname === "/" ||
- window.location.pathname === "/login"
- ) {
- window.location.href = "/facility";
- } else {
- window.location.href = window.location.pathname.toString();
- }
+ const { res, data } = await request(routes.login, {
+ body: { ...valid },
+ });
+ if (res && res.status === 429) {
+ setCaptcha(true);
+ // captcha displayed set back to login button
+ setLoading(false);
+ } else if (res && res.status === 200 && data) {
+ localStorage.setItem(LocalStorageKeys.accessToken, data.access);
+ localStorage.setItem(LocalStorageKeys.refreshToken, data.refresh);
+ if (
+ window.location.pathname === "/" ||
+ window.location.pathname === "/login"
+ ) {
+ window.location.href = "/facility";
} else {
- // error from server set back to login button
- setLoading(false);
+ window.location.href = window.location.pathname.toString();
}
- });
+ } else {
+ // error from server set back to login button
+ setLoading(false);
+ }
}
};
@@ -146,26 +142,22 @@ export const Login = (props: { forgot?: boolean }) => {
return form;
};
- const handleForgetSubmit = (e: any) => {
+ const handleForgetSubmit = async (e: any) => {
e.preventDefault();
const valid = validateForgetData();
if (valid) {
setLoading(true);
- dispatch(postForgotPassword(valid)).then((resp: any) => {
- setLoading(false);
- const res = resp && resp.data;
- if (res && res.status === "OK") {
- Notification.Success({
- msg: t("password_sent"),
- });
- } else if (res && res.data) {
- setErrors(res.data);
- } else {
- Notification.Error({
- msg: t("something_wrong"),
- });
- }
+ const { res, error } = await request(routes.forgotPassword, {
+ body: { ...valid },
});
+ setLoading(false);
+ if (res?.ok) {
+ Notification.Success({
+ msg: t("password_sent"),
+ });
+ } else if (res && error) {
+ setErrors(error);
+ }
}
};
diff --git a/src/Components/Auth/ResetPassword.tsx b/src/Components/Auth/ResetPassword.tsx
index 08dd1da44a0..47d120e1a97 100644
--- a/src/Components/Auth/ResetPassword.tsx
+++ b/src/Components/Auth/ResetPassword.tsx
@@ -1,7 +1,6 @@
import { useEffect, useState } from "react";
-import { useDispatch } from "react-redux";
+import request from "../../Utils/request/request.js";
import * as Notification from "../../Utils/Notifications.js";
-import { postResetPassword, checkResetToken } from "../../Redux/actions";
import { navigate } from "raviger";
import { useTranslation } from "react-i18next";
import { LocalStorageKeys } from "../../Common/constants";
@@ -9,9 +8,9 @@ import { Cancel, Submit } from "../Common/components/ButtonV2";
import TextFormField from "../Form/FormFields/TextFormField";
import { validateRule } from "../Users/UserAdd";
import { validatePassword } from "../../Common/validation.js";
+import routes from "../../Redux/api.js";
export const ResetPassword = (props: any) => {
- const dispatch: any = useDispatch();
const initForm: any = {
password: "",
confirm: "",
@@ -65,36 +64,37 @@ export const ResetPassword = (props: any) => {
return form;
};
- const handleSubmit = (e: any) => {
+ const handleSubmit = async (e: any) => {
e.preventDefault();
const valid = validateData();
if (valid) {
valid.token = props.token;
- dispatch(postResetPassword(valid)).then((resp: any) => {
- const res = resp && resp.data;
- if (res && res.status === "OK") {
- localStorage.removeItem(LocalStorageKeys.accessToken);
- Notification.Success({
- msg: t("password_reset_success"),
- });
- navigate("/login");
- } else if (res && res.data) {
- setErrors(res.data);
- } else {
- Notification.Error({
- msg: t("password_reset_failure"),
- });
- }
+ const { res, error } = await request(routes.resetPassword, {
+ body: { ...valid },
});
+ if (res?.ok) {
+ localStorage.removeItem(LocalStorageKeys.accessToken);
+ Notification.Success({
+ msg: t("password_reset_success"),
+ });
+ navigate("/login");
+ } else if (res && error) {
+ setErrors(error);
+ }
}
};
useEffect(() => {
- if (props.token) {
- dispatch(checkResetToken({ token: props.token })).then((resp: any) => {
- const res = resp && resp.data;
- if (!res || res.status !== "OK") navigate("/invalid-reset");
+ const checkResetToken = async () => {
+ const { res } = await request(routes.checkResetToken, {
+ body: { token: props.token },
});
+ if (!res || !res.ok) {
+ navigate("/invalid-reset");
+ }
+ };
+ if (props.token) {
+ checkResetToken();
} else {
navigate("/invalid-reset");
}
diff --git a/src/Components/Common/DateInputV2.tsx b/src/Components/Common/DateInputV2.tsx
index bcebd4e0055..7036d5c8bfb 100644
--- a/src/Components/Common/DateInputV2.tsx
+++ b/src/Components/Common/DateInputV2.tsx
@@ -1,4 +1,4 @@
-import { MutableRefObject, useEffect, useRef, useState } from "react";
+import { MutableRefObject, useEffect, useState } from "react";
import {
addMonths,
addYears,
@@ -60,7 +60,6 @@ const DateInputV2: React.FC
= ({
const [displayValue, setDisplayValue] = useState(
value ? dayjs(value).format("DDMMYYYY") : ""
);
- const popover = useRef(null);
const decrement = () => {
switch (type) {
@@ -241,7 +240,6 @@ const DateInputV2: React.FC = ({
onBlur={() => {
setIsOpen?.(false);
}}
- ref={popover}
static
className={classNames(
"cui-dropdown-base absolute mt-0.5 w-72 divide-y-0 p-4",
@@ -252,10 +250,6 @@ const DateInputV2: React.FC = ({
{
- popover.current?.focus();
- e.preventDefault();
- }}
className="cui-input-base bg-gray-50"
value={
displayValue.replace(
diff --git a/src/Components/Common/GLocationPicker.tsx b/src/Components/Common/GLocationPicker.tsx
index 5356d28fa2a..fc121b8519f 100644
--- a/src/Components/Common/GLocationPicker.tsx
+++ b/src/Components/Common/GLocationPicker.tsx
@@ -7,14 +7,6 @@ import CareIcon from "../../CAREUI/icons/CareIcon";
import useConfig from "../../Common/hooks/useConfig";
import { Popover } from "@headlessui/react";
-const render = (status: Status) => {
- if (status === "LOADING") {
- return ;
- }
-
- return {status}
;
-};
-
interface GLocationPickerProps {
lat: number;
lng: number;
@@ -67,22 +59,37 @@ const GLocationPicker = ({
setCenter(m?.getCenter()?.toJSON());
};
+ const render = (status: Status) => {
+ switch (status) {
+ case Status.LOADING:
+ return ;
+ case Status.SUCCESS:
+ return (
+
+ );
+ default:
+ return {status}
;
+ }
+ };
+
return (
-
-
-
+
);
};
@@ -149,7 +156,9 @@ const Map: React.FC = ({
places.length > 0 &&
places[0].geometry?.location
) {
- handleOnChange(places[0].geometry.location);
+ const selectedLocation = places[0].geometry.location;
+ handleOnChange(selectedLocation);
+ map?.setCenter(selectedLocation);
}
});
}
diff --git a/src/Components/Facility/AssetCreate.tsx b/src/Components/Facility/AssetCreate.tsx
index 156d738857a..84fc09188d0 100644
--- a/src/Components/Facility/AssetCreate.tsx
+++ b/src/Components/Facility/AssetCreate.tsx
@@ -37,7 +37,8 @@ import useVisibility from "../../Utils/useVisibility";
import { validateEmailAddress } from "../../Common/validation";
import { dateQueryString, parsePhoneNumber } from "../../Utils/utils.js";
import dayjs from "../../Utils/dayjs";
-import DateInputV2 from "../Common/DateInputV2.js";
+import DateFormField from "../Form/FormFields/DateFormField.js";
+import { t } from "i18next";
const Loading = lazy(() => import("../Common/Loading"));
@@ -305,7 +306,7 @@ const AssetCreate = (props: AssetProps) => {
setLocation("");
setAssetType(assetTypeInitial);
setAssetClass(assetClassInitial);
- setIsWorking("");
+ setIsWorking(undefined);
setNotWorkingReason("");
setSerialNumber("");
setVendorName("");
@@ -404,7 +405,7 @@ const AssetCreate = (props: AssetProps) => {
if (locations.length === 0) {
return (
{
- You need at least a location to create an assest.
+ {t("you_need_at_least_a_location_to_create_an_assest")}
@@ -440,7 +441,8 @@ const AssetCreate = (props: AssetProps) => {
onClick={() => setIsScannerActive(false)}
className="btn btn-default mb-2"
>
-
Close Scanner
+
+ {t("close_scanner")}
{
}
style={{ width: "100%" }}
/>
- Scan Asset QR!
+
+ {t("scan_asset_qr")}
+
);
@@ -479,7 +483,7 @@ const AssetCreate = (props: AssetProps) => {
return (
{
>
setName(value)}
@@ -544,7 +548,7 @@ const AssetCreate = (props: AssetProps) => {
{/* Location */}
- Asset Location
+ {t("asset_location")}
{
data-testid="asset-type-input"
>
{
{
>
setDescription(value)}
error={state.errors.description}
@@ -664,7 +668,7 @@ const AssetCreate = (props: AssetProps) => {
className="col-span-6"
required
name="is_working"
- label="Working Status"
+ label={t("working_status")}
options={["true", "false"]}
optionLabel={(option) => {
return (
@@ -692,8 +696,8 @@ const AssetCreate = (props: AssetProps) => {
>
setNotWorkingReason(e.value)}
error={state.errors.not_working_reason}
@@ -717,7 +721,7 @@ const AssetCreate = (props: AssetProps) => {
id="qr_code_id"
name="qr_code_id"
placeholder=""
- label="Asset QR ID"
+ label={t("asset_qr_id")}
value={qrCodeId}
onChange={(e) => setQrCodeId(e.value)}
error={state.errors.qr_code_id}
@@ -743,9 +747,9 @@ const AssetCreate = (props: AssetProps) => {
setManufacturer(e.value)}
error={state.errors.manufacturer}
/>
@@ -760,7 +764,7 @@ const AssetCreate = (props: AssetProps) => {
{
const value = dayjs(event.value);
@@ -788,8 +792,8 @@ const AssetCreate = (props: AssetProps) => {
setSupportName(e.value)}
error={state.errors.support_name}
@@ -804,7 +808,7 @@ const AssetCreate = (props: AssetProps) => {
>
setSupportPhone(e.value)}
@@ -822,8 +826,8 @@ const AssetCreate = (props: AssetProps) => {
setSupportEmail(e.value)}
error={state.errors.support_email}
@@ -841,9 +845,9 @@ const AssetCreate = (props: AssetProps) => {
setVendorName(e.value)}
error={state.errors.vendor_name}
/>
@@ -858,7 +862,7 @@ const AssetCreate = (props: AssetProps) => {
setSerialNumber(e.value)}
error={state.errors.serial_number}
@@ -874,25 +878,26 @@ const AssetCreate = (props: AssetProps) => {
ref={fieldRef["last_serviced_on"]}
data-testid="asset-last-serviced-on-input"
>
- Last Serviced On
- {
if (
- dayjs(date).format("YYYY-MM-DD") >
+ dayjs(date.value).format("YYYY-MM-DD") >
new Date().toLocaleDateString("en-ca")
) {
Notification.Error({
msg: "Last Serviced date can't be in future",
});
} else {
- setLastServicedOn(dayjs(date).format("YYYY-MM-DD"));
+ setLastServicedOn(
+ dayjs(date.value).format("YYYY-MM-DD")
+ );
}
}}
- max={new Date()}
/>
{
>
setNotes(e.value)}
error={state.errors.notes}
@@ -928,13 +935,13 @@ const AssetCreate = (props: AssetProps) => {
/>
handleSubmit(e, false)}
- label={assetId ? "Update" : "Create Asset"}
+ label={assetId ? t("update") : t("create_asset")}
/>
{!assetId && (
handleSubmit(e, true)}
- label="Create & Add More"
+ label={t("create_add_more")}
/>
)}
diff --git a/src/Components/Facility/Consultations/LiveFeed.tsx b/src/Components/Facility/Consultations/LiveFeed.tsx
index c6ba749b471..aba473e958d 100644
--- a/src/Components/Facility/Consultations/LiveFeed.tsx
+++ b/src/Components/Facility/Consultations/LiveFeed.tsx
@@ -36,6 +36,7 @@ const LiveFeed = (props: any) => {
const [streamStatus, setStreamStatus] = useState(
StreamStatus.Offline
);
+ const [videoStartTime, setVideoStartTime] = useState(null);
const [bed, setBed] = useState({});
const [preset, setNewPreset] = useState("");
const [loading, setLoading] = useState();
@@ -100,6 +101,16 @@ const LiveFeed = (props: any) => {
},
});
+ const calculateVideoLiveDelay = () => {
+ const video = liveFeedPlayerRef.current as HTMLVideoElement;
+ if (!video || !videoStartTime) return 0;
+
+ const timeDifference =
+ (new Date().getTime() - videoStartTime.getTime()) / 1000;
+
+ return timeDifference - video.currentTime;
+ };
+
const getBedPresets = async (id: any) => {
const bedAssets = await dispatch(
listAssetBeds({
@@ -223,6 +234,7 @@ const LiveFeed = (props: any) => {
},
reset: () => {
setStreamStatus(StreamStatus.Loading);
+ setVideoStartTime(null);
startStream({
onSuccess: () => setStreamStatus(StreamStatus.Playing),
onError: () => setStreamStatus(StreamStatus.Offline),
@@ -344,8 +356,25 @@ const LiveFeed = (props: any) => {
playsInline
className="z-10 h-full w-full"
ref={liveFeedPlayerRef}
+ onPlay={() => {
+ setVideoStartTime(() => new Date());
+ }}
+ onWaiting={() => {
+ const delay = calculateVideoLiveDelay();
+ if (delay > 5) {
+ setStreamStatus(StreamStatus.Loading);
+ }
+ }}
>
+ {streamStatus === StreamStatus.Playing &&
+ calculateVideoLiveDelay() > 3 && (
+
+
+ Slow Network Detected
+
+ )}
+
{loading && (
diff --git a/src/Components/Facility/UpdateFacilityMiddleware.tsx b/src/Components/Facility/FacilityConfigure.tsx
similarity index 98%
rename from src/Components/Facility/UpdateFacilityMiddleware.tsx
rename to src/Components/Facility/FacilityConfigure.tsx
index 211d8cf458a..6f0a8a9869c 100644
--- a/src/Components/Facility/UpdateFacilityMiddleware.tsx
+++ b/src/Components/Facility/FacilityConfigure.tsx
@@ -46,7 +46,7 @@ const FormReducer = (state = initialState, action: any) => {
}
};
-export const UpdateFacilityMiddleware = (props: any) => {
+export const FacilityConfigure = (props: any) => {
const [state, dispatch] = useReducer(FormReducer, initialState);
const { facilityId } = props;
const dispatchAction: any = useDispatch();
diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx
index 15317b1b56c..68990e64416 100644
--- a/src/Components/Facility/FacilityHome.tsx
+++ b/src/Components/Facility/FacilityHome.tsx
@@ -323,7 +323,7 @@ export const FacilityHome = (props: any) => {
StaffUserTypeIndex;
const editCoverImageTooltip = hasPermissionToEditCoverImage && (
-
+
{`${hasCoverImage ? "Edit" : "Upload"}`}
@@ -541,9 +541,7 @@ export const FacilityHome = (props: any) => {
- navigate(`/facility/${facilityId}/middleware/update`)
- }
+ onClick={() => navigate(`/facility/${facilityId}/configure`)}
authorizeFor={NonReadOnlyUsers}
icon={}
>
diff --git a/src/Components/Form/Form.tsx b/src/Components/Form/Form.tsx
index 5b1cf018965..e934c4ffe0e 100644
--- a/src/Components/Form/Form.tsx
+++ b/src/Components/Form/Form.tsx
@@ -7,6 +7,7 @@ import { FormContextValue, createFormContext } from "./FormContext";
import { FieldChangeEvent } from "./FormFields/Utils";
import { FormDetails, FormErrors, FormState, formReducer } from "./Utils";
import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave";
+import * as Notification from "../../Utils/Notifications";
type Props = {
className?: string;
@@ -51,6 +52,10 @@ const Form = ({
if (Object.keys(errors).length) {
dispatch({ type: "set_errors", errors });
+
+ if (errors.$all) {
+ Notification.Error({ msg: errors.$all });
+ }
return;
}
}
diff --git a/src/Components/Form/FormFields/NumericWithUnitsFormField.tsx b/src/Components/Form/FormFields/NumericWithUnitsFormField.tsx
index 31ac781e018..02aa03fdf71 100644
--- a/src/Components/Form/FormFields/NumericWithUnitsFormField.tsx
+++ b/src/Components/Form/FormFields/NumericWithUnitsFormField.tsx
@@ -34,6 +34,7 @@ export default function NumericWithUnitsFormField(props: Props) {
max={props.max}
autoComplete={props.autoComplete}
required={field.required}
+ value={numValue}
onChange={(e) => field.handleChange(e.target.value + " " + unitValue)}
/>
diff --git a/src/Components/Form/Utils.ts b/src/Components/Form/Utils.ts
index 2ec5d4b60e5..0592e81a06c 100644
--- a/src/Components/Form/Utils.ts
+++ b/src/Components/Form/Utils.ts
@@ -1,7 +1,9 @@
import { FieldError } from "./FieldValidators";
export type FormDetails = { [key: string]: any };
-export type FormErrors
= Partial>;
+export type FormErrors = Partial<
+ Record
+>;
export type FormState = { form: T; errors: FormErrors };
export type FormAction =
| { type: "set_form"; form: T }
diff --git a/src/Components/Medicine/CreatePrescriptionForm.tsx b/src/Components/Medicine/CreatePrescriptionForm.tsx
index fb7fec5f431..2a58c632d20 100644
--- a/src/Components/Medicine/CreatePrescriptionForm.tsx
+++ b/src/Components/Medicine/CreatePrescriptionForm.tsx
@@ -1,4 +1,4 @@
-import { FieldError, RequiredFieldValidator } from "../Form/FieldValidators";
+import { RequiredFieldValidator } from "../Form/FieldValidators";
import Form from "../Form/Form";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
@@ -11,6 +11,7 @@ import NumericWithUnitsFormField from "../Form/FormFields/NumericWithUnitsFormFi
import { useTranslation } from "react-i18next";
import MedibaseAutocompleteFormField from "./MedibaseAutocompleteFormField";
import dayjs from "../../Utils/dayjs";
+import { PrescriptionFormValidator } from "./validators";
export default function CreatePrescriptionForm(props: {
prescription: Prescription;
@@ -40,16 +41,7 @@ export default function CreatePrescriptionForm(props: {
}
}}
noPadding
- validate={(form) => {
- const errors: Partial> = {};
- errors.medicine_object = RequiredFieldValidator()(form.medicine_object);
- errors.dosage = RequiredFieldValidator()(form.dosage);
- if (form.is_prn)
- errors.indicator = RequiredFieldValidator()(form.indicator);
- if (!form.is_prn)
- errors.frequency = RequiredFieldValidator()(form.frequency);
- return errors;
- }}
+ validate={PrescriptionFormValidator()}
className="max-w-3xl"
>
{(field) => (
diff --git a/src/Components/Medicine/EditPrescriptionForm.tsx b/src/Components/Medicine/EditPrescriptionForm.tsx
new file mode 100644
index 00000000000..d5261f70c7e
--- /dev/null
+++ b/src/Components/Medicine/EditPrescriptionForm.tsx
@@ -0,0 +1,158 @@
+import { useState } from "react";
+import Form from "../Form/Form";
+import { Prescription } from "./models";
+import request from "../../Utils/request/request";
+import routes from "../../Redux/api";
+import * as Notification from "../../Utils/Notifications";
+import useSlug from "../../Common/hooks/useSlug";
+import { RequiredFieldValidator } from "../Form/FieldValidators";
+import { useTranslation } from "react-i18next";
+import { SelectFormField } from "../Form/FormFields/SelectFormField";
+import NumericWithUnitsFormField from "../Form/FormFields/NumericWithUnitsFormField";
+import {
+ PRESCRIPTION_FREQUENCIES,
+ PRESCRIPTION_ROUTES,
+} from "./CreatePrescriptionForm";
+import TextFormField from "../Form/FormFields/TextFormField";
+import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
+import { EditPrescriptionFormValidator } from "./validators";
+
+interface Props {
+ initial: Prescription;
+ onDone: (created: boolean) => void;
+}
+
+const handleSubmit = async (
+ consultation_external_id: string,
+ oldObj: Prescription,
+ { discontinued_reason, ...newObj }: Prescription
+) => {
+ const discontinue = await request(routes.discontinuePrescription, {
+ pathParams: { consultation_external_id, external_id: oldObj.id },
+ body: {
+ discontinued_reason: discontinued_reason
+ ? `Edit: ${discontinued_reason}`
+ : "Edited",
+ },
+ });
+
+ if (discontinue.res?.status !== 200) {
+ Notification.Error({
+ msg: "Failed to discontinue previous prescription",
+ });
+ return;
+ }
+
+ const { res } = await request(routes.createPrescription, {
+ pathParams: { consultation_external_id },
+ body: {
+ ...newObj,
+ // Forcing the medicine to be the same as the old one
+ medicine: oldObj.medicine_object?.id,
+ medicine_old: oldObj.medicine_old,
+ },
+ });
+
+ return res?.status === 201;
+};
+
+export default function EditPrescriptionForm(props: Props) {
+ const consultation = useSlug("consultation");
+ const [isLoading, setIsLoading] = useState(false);
+ const { t } = useTranslation();
+
+ return (
+
+ );
+}
diff --git a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx
index 470caa1042b..c80b66f44c6 100644
--- a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx
+++ b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx
@@ -20,6 +20,7 @@ import {
formatTime,
} from "../../Utils/utils";
import useRangePagination from "../../Common/hooks/useRangePagination";
+import EditPrescriptionForm from "./EditPrescriptionForm";
interface DateRange {
start: Date;
@@ -47,10 +48,8 @@ export default function PrescriptionAdministrationsTable({
const { t } = useTranslation();
const [state, setState] = useState();
-
const [showDiscontinued, setShowDiscontinued] = useState(false);
const [discontinuedCount, setDiscontinuedCount] = useState();
-
const pagination = useRangePagination({
bounds: state?.administrationsTimeBounds ?? {
start: new Date(),
@@ -58,8 +57,7 @@ export default function PrescriptionAdministrationsTable({
},
perPage: 24 * 60 * 60 * 1000,
slots: 24,
- snapToLatest: true,
- reverse: true,
+ defaultEnd: true,
});
const [showBulkAdminister, setShowBulkAdminister] = useState(false);
@@ -159,7 +157,7 @@ export default function PrescriptionAdministrationsTable({
/>
-
+
@@ -184,9 +182,9 @@ export default function PrescriptionAdministrationsTable({
border
className="mx-2 px-1"
variant="secondary"
- disabled={!pagination.hasNext}
- onClick={pagination.next}
- tooltip="Next 24 hours"
+ disabled={!pagination.hasPrevious}
+ onClick={pagination.previous}
+ tooltip="Previous 24 hours"
tooltipClassName="tooltip-bottom -translate-x-1/2 text-xs"
>
@@ -201,26 +199,24 @@ export default function PrescriptionAdministrationsTable({
|
))
- : pagination.slots
- ?.map(({ start, end }, index) => (
-
- {formatDateTime(end, "DD/MM")}
- {formatDateTime(end, "HH:mm")}
-
-
- Administration(s) between
-
- {formatTime(start)} and{" "}
- {formatTime(end)}
-
- on {formatDate(start)}
-
- |
- ))
- .reverse()}
+ : pagination.slots?.map(({ start, end }, index) => (
+
+ {formatDateTime(start, "DD/MM")}
+ {formatDateTime(start, "HH:mm")}
+
+
+ Administration(s) between
+
+ {formatTime(start)} and{" "}
+ {formatTime(end)}
+
+ on {formatDate(start)}
+
+ |
+ ))}
@@ -299,6 +295,7 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => {
const { t } = useTranslation();
// const [showActions, setShowActions] = useState(false);
const [showDetails, setShowDetails] = useState(false);
+ const [showEdit, setShowEdit] = useState(false);
const [showAdminister, setShowAdminister] = useState(false);
const [showDiscontinue, setShowDiscontinue] = useState(false);
const [administrations, setAdministrations] =
@@ -328,7 +325,11 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => {
}, [prescription.id, dispatch, props.intervals]);
return (
- <>
+
{showDiscontinue && (
{
{t("discontinue")}
+ {
+ setShowDetails(false);
+ setShowEdit(true);
+ }}
+ >
+
+ {t("edit")}
+
{
)}
-
- setShowDetails(true)}
+ {showEdit && (
+ setShowEdit(false)}
+ show={showEdit}
+ title={`${t("edit")} ${t(
+ prescription.is_prn ? "prn_prescription" : "prescription_medication"
+ )}: ${
+ prescription.medicine_object?.name ?? prescription.medicine_old
+ }`}
+ description={
+
+
+ {t("edit_caution_note")}
+
+ }
+ className="w-full max-w-3xl lg:min-w-[600px]"
>
-
-
-
- {prescription.medicine_object?.name ??
- prescription.medicine_old}
-
-
- {prescription.discontinued && (
-
- {t("discontinued")}
-
+ {
+ setShowEdit(false);
+ if (success) {
+ props.refetch();
+ }
+ }}
+ />
+
+ )}
+ setShowDetails(true)}
+ >
+
+
+
+ {prescription.medicine_object?.name ?? prescription.medicine_old}
+
- {prescription.route && (
-
- {t(prescription.route)}
-
- )}
-
+ {prescription.discontinued && (
+
+ {t("discontinued")}
+
+ )}
-
- {prescription.dosage}
-
- {!prescription.is_prn
- ? t("PRESCRIPTION_FREQUENCY_" + prescription.frequency)
- : prescription.indicator}
-
-
+ {prescription.route && (
+
+ {t(prescription.route)}
+
+ )}
- |
-
- |
- {/* Administration Cells */}
- {props.intervals
- .map(({ start, end }, index) => (
-
- {administrations === undefined ? (
-
- ) : (
-
- )}
- |
- ))
- .reverse()}
- |
- {/* Action Buttons */}
-
- setShowAdminister(true)}
- >
- {t("administer")}
-
+
+ {prescription.dosage}
+
+ {!prescription.is_prn
+ ? t("PRESCRIPTION_FREQUENCY_" + prescription.frequency)
+ : prescription.indicator}
+
+
+
+ |
+
+ |
+ {/* Administration Cells */}
+ {props.intervals.map(({ start, end }, index) => (
+
+ {administrations === undefined ? (
+
+ ) : (
+
+ )}
|
- |
- >
+ ))}
+ |
+
+ {/* Action Buttons */}
+
+ setShowAdminister(true)}
+ >
+ {t("administer")}
+
+ |
+
);
};
diff --git a/src/Components/Medicine/PrescriptionDetailCard.tsx b/src/Components/Medicine/PrescriptionDetailCard.tsx
index 6da4fa7ae8d..bf27aa34068 100644
--- a/src/Components/Medicine/PrescriptionDetailCard.tsx
+++ b/src/Components/Medicine/PrescriptionDetailCard.tsx
@@ -5,6 +5,7 @@ import ReadMore from "../Common/components/Readmore";
import ButtonV2 from "../Common/components/ButtonV2";
import { PrescriptionActions } from "../../Redux/actions";
import { useTranslation } from "react-i18next";
+import RecordMeta from "../../CAREUI/display/RecordMeta";
export default function PrescriptionDetailCard({
prescription,
@@ -29,7 +30,7 @@ export default function PrescriptionDetailCard({
prescription.discontinued && "bg-gray-200 opacity-80"
)}
>
-
+
@@ -83,7 +84,7 @@ export default function PrescriptionDetailCard({
-
+
{prescription.medicine_object?.name ?? prescription.medicine_old}
@@ -146,6 +147,23 @@ export default function PrescriptionDetailCard({
)}
+
+
+
+ Prescribed
+
+
+ {prescription.discontinued && (
+
+ and was discontinued
+
+
+ )}
+
{props.children}
diff --git a/src/Components/Medicine/models.ts b/src/Components/Medicine/models.ts
index 62aea46b6d2..0c49d199b21 100644
--- a/src/Components/Medicine/models.ts
+++ b/src/Components/Medicine/models.ts
@@ -1,7 +1,7 @@
import { PerformedByModel } from "../HCX/misc";
interface BasePrescription {
- readonly id?: string;
+ readonly id: string;
medicine?: string;
medicine_object?: MedibaseMedicine;
medicine_old?: string;
diff --git a/src/Components/Medicine/validators.ts b/src/Components/Medicine/validators.ts
new file mode 100644
index 00000000000..40261646d05
--- /dev/null
+++ b/src/Components/Medicine/validators.ts
@@ -0,0 +1,49 @@
+import { FieldError, RequiredFieldValidator } from "../Form/FieldValidators";
+import { FormErrors } from "../Form/Utils";
+import { Prescription } from "./models";
+
+export const PrescriptionFormValidator = () => {
+ return (form: Prescription): FormErrors => {
+ const errors: Partial> = {};
+ errors.medicine_object = RequiredFieldValidator()(form.medicine_object);
+ errors.dosage = RequiredFieldValidator()(form.dosage);
+ if (form.is_prn)
+ errors.indicator = RequiredFieldValidator()(form.indicator);
+ if (!form.is_prn)
+ errors.frequency = RequiredFieldValidator()(form.frequency);
+ return errors;
+ };
+};
+
+export const EditPrescriptionFormValidator = (old: Prescription) => {
+ return (form: Prescription): FormErrors => {
+ const errors = PrescriptionFormValidator()(form);
+
+ if (comparePrescriptions(old, form)) {
+ errors.$all = "No changes made";
+ }
+
+ return errors;
+ };
+};
+
+const PRESCRIPTION_COMPARE_FIELDS: (keyof Prescription)[] = [
+ "medicine",
+ "days",
+ "discontinued",
+ "dosage",
+ "frequency",
+ "indicator",
+ "is_prn",
+ "max_dosage",
+ "min_hours_between_doses",
+ "prescription_type",
+ "route",
+];
+
+export const comparePrescriptions = (a: Prescription, b: Prescription) => {
+ return (
+ PRESCRIPTION_COMPARE_FIELDS.every((field) => a[field] === b[field]) &&
+ a.medicine_object?.id === b.medicine_object?.id
+ );
+};
diff --git a/src/Components/Notifications/NotificationsList.tsx b/src/Components/Notifications/NotificationsList.tsx
index f6afa6cccd8..5e3aa65b3ec 100644
--- a/src/Components/Notifications/NotificationsList.tsx
+++ b/src/Components/Notifications/NotificationsList.tsx
@@ -165,6 +165,7 @@ export default function NotificationsList({
const [isMarkingAllAsRead, setIsMarkingAllAsRead] = useState(false);
const [isSubscribed, setIsSubscribed] = useState("");
const [isSubscribing, setIsSubscribing] = useState(false);
+ const [showUnread, setShowUnread] = useState(false);
const { t } = useTranslation();
useEffect(() => {
@@ -351,33 +352,37 @@ export default function NotificationsList({
} else if (data?.length) {
manageResults = (
<>
- {data.map((result: any) => (
-
- ))}
+ {data
+ .filter((notification: any) => showUnread ? notification.read_at === null : true)
+ .map((result: any) => (
+
+ ))}
{isLoading && (
)}
- {totalCount > RESULT_LIMIT && offset < totalCount - RESULT_LIMIT && (
-
- setOffset((prev) => prev + RESULT_LIMIT)}
- >
- {isLoading ? t("loading") : t("load_more")}
-
-
- )}
+ {!showUnread &&
+ totalCount > RESULT_LIMIT &&
+ offset < totalCount - RESULT_LIMIT && (
+
+ setOffset((prev) => prev + RESULT_LIMIT)}
+ >
+ {isLoading ? t("loading") : t("load_more")}
+
+
+ )}
>
);
} else if (data && data.length === 0) {
@@ -448,6 +453,21 @@ export default function NotificationsList({
/>
{t("mark_all_as_read")}
+ setShowUnread(!showUnread)}
+ >
+
+
+
+ {showUnread
+ ? t("show_all_notifications")
+ : t("show_unread_notifications")}
+
+
{
- console.log("ID:", external_id.id);
- const res = await dispatch(getNotificationData({ id: external_id.id }));
+ const res = await dispatch(getNotificationData({ id }));
const data = res.data.caused_objects;
switch (res.data.event) {
case "PATIENT_CREATED":
diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx
index fe0881e6994..2b0b068b0ef 100644
--- a/src/Components/Patient/FileUpload.tsx
+++ b/src/Components/Patient/FileUpload.tsx
@@ -100,7 +100,7 @@ interface FileUploadProps {
hideBack: boolean;
audio?: boolean;
unspecified: boolean;
- sampleId?: number;
+ sampleId?: string;
claimId?: string;
}
diff --git a/src/Components/Patient/SampleDetails.tsx b/src/Components/Patient/SampleDetails.tsx
index 7814109978b..e29df92f47f 100644
--- a/src/Components/Patient/SampleDetails.tsx
+++ b/src/Components/Patient/SampleDetails.tsx
@@ -13,14 +13,11 @@ import { getTestSample } from "../../Redux/actions";
import { navigate } from "raviger";
import { useDispatch } from "react-redux";
+import { DetailRoute } from "../../Routers/types";
const Loading = lazy(() => import("../Common/Loading"));
-interface SampleDetailsProps {
- id: number;
-}
-
-export const SampleDetails = ({ id }: SampleDetailsProps) => {
+export const SampleDetails = ({ id }: DetailRoute) => {
const dispatch: any = useDispatch();
const [isLoading, setIsLoading] = useState(false);
const [sampleDetails, setSampleDetails] = useState({});
diff --git a/src/Components/Resource/ListView.tsx b/src/Components/Resource/ListView.tsx
index b368c9bf3ea..24b4e263039 100644
--- a/src/Components/Resource/ListView.tsx
+++ b/src/Components/Resource/ListView.tsx
@@ -31,7 +31,7 @@ export default function ListView() {
const { t } = useTranslation();
const onBoardViewBtnClick = () =>
- navigate("/resource/board-view", { query: qParams });
+ navigate("/resource/board", { query: qParams });
const appliedFilters = formatFilter(qParams);
const refreshList = () => {
diff --git a/src/Components/Resource/ResourceBoardView.tsx b/src/Components/Resource/ResourceBoardView.tsx
index de3058f9406..d3b2b202649 100644
--- a/src/Components/Resource/ResourceBoardView.tsx
+++ b/src/Components/Resource/ResourceBoardView.tsx
@@ -32,7 +32,7 @@ export default function BoardView() {
const { t } = useTranslation();
const onListViewBtnClick = () => {
- navigate("/resource/list-view", { query: qParams });
+ navigate("/resource/list", { query: qParams });
localStorage.setItem("defaultResourceView", "list");
};
diff --git a/src/Components/Resource/ResourceDetails.tsx b/src/Components/Resource/ResourceDetails.tsx
index 591f6b48bdb..67adc7edd82 100644
--- a/src/Components/Resource/ResourceDetails.tsx
+++ b/src/Components/Resource/ResourceDetails.tsx
@@ -231,7 +231,7 @@ export default function ResourceDetails(props: { id: string }) {
{isPrintMode ? (
diff --git a/src/Components/Shifting/BoardView.tsx b/src/Components/Shifting/BoardView.tsx
index 0eddfddf745..85815770acf 100644
--- a/src/Components/Shifting/BoardView.tsx
+++ b/src/Components/Shifting/BoardView.tsx
@@ -95,9 +95,7 @@ export default function BoardView() {
- navigate("/shifting/list-view", { query: qParams })
- }
+ onClick={() => navigate("/shifting/list", { query: qParams })}
>
{t("list_view")}
diff --git a/src/Components/Shifting/ListView.tsx b/src/Components/Shifting/ListView.tsx
index 6d55122ea11..f3fb14a4c0a 100644
--- a/src/Components/Shifting/ListView.tsx
+++ b/src/Components/Shifting/ListView.tsx
@@ -312,9 +312,7 @@ export default function ListView() {
- navigate("/shifting/board-view", { query: qParams })
- }
+ onClick={() => navigate("/shifting/board", { query: qParams })}
>
{t("board_view")}
diff --git a/src/Components/Shifting/ShiftDetails.tsx b/src/Components/Shifting/ShiftDetails.tsx
index 03e09702aac..7bffe429960 100644
--- a/src/Components/Shifting/ShiftDetails.tsx
+++ b/src/Components/Shifting/ShiftDetails.tsx
@@ -557,7 +557,7 @@ export default function ShiftDetails(props: { id: string }) {
) : (
{
+ return `${dayjs().diff(dayjs(timestamp), "minute")}m ago`;
+};
+
+const isWithinMinutes = (timestamp: string, minutes: number) => {
+ return dayjs().diff(dayjs(timestamp), "minute") < minutes;
+};
export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
const { connect, waveformCanvas, data, isOnline } = useHL7VitalsMonitor(
@@ -30,6 +39,10 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
connect(props.socketUrl);
}, [props.socketUrl]);
+ const bpWithinMaxPersistence = !!(
+ (data.bp?.["date-time"] && isWithinMinutes(data.bp?.["date-time"], 30)) // Max blood pressure persistence is 30 minutes
+ );
+
return (
{props.patientAssetBed && (
@@ -97,24 +110,37 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
{/* Blood Pressure */}
-
+
NIBP
- {data.bp?.systolic.unit ?? "--"}
+
+ {bpWithinMaxPersistence ? data.bp?.systolic.unit ?? "--" : "--"}
+
+
+ {data.bp?.["date-time"] && minutesAgo(data.bp?.["date-time"])}
+
Sys / Dia
- {data.bp?.systolic.value ?? "--"}
+
+ {bpWithinMaxPersistence
+ ? data.bp?.systolic.value ?? "--"
+ : "--"}
+
/
- {data.bp?.diastolic.value ?? "--"}
+
+ {bpWithinMaxPersistence
+ ? data.bp?.diastolic.value ?? "--"
+ : "--"}
+
Mean
- {data.bp?.map.value ?? "--"}
+ {bpWithinMaxPersistence ? data.bp?.map.value ?? "--" : "--"}
diff --git a/src/Components/VitalsMonitor/types.ts b/src/Components/VitalsMonitor/types.ts
index 066b7a7cc78..60979a6f9b0 100644
--- a/src/Components/VitalsMonitor/types.ts
+++ b/src/Components/VitalsMonitor/types.ts
@@ -8,7 +8,7 @@ export interface VitalsDataBase {
"patient-name": string;
}
-export interface VitalsValueBase {
+export interface VitalsValueBase extends VitalsDataBase {
value: number;
unit: string;
interpretation: string;
diff --git a/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts b/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts
index 8b74a2d05d2..ed16cc2edfd 100644
--- a/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts
+++ b/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts
@@ -8,11 +8,12 @@ import useCanvas from "../../Common/hooks/useCanvas";
import {
ChannelOptions,
IVitalsComponentProps,
+ VitalsDataBase,
VitalsValueBase as VitalsValue,
} from "./types";
import { getChannel, getVitalsCanvasSizeAndDuration } from "./utils";
-interface VitalsBPValue {
+interface VitalsBPValue extends VitalsDataBase {
systolic: VitalsValue;
diastolic: VitalsValue;
map: VitalsValue;
diff --git a/src/Locale/en/Asset.json b/src/Locale/en/Asset.json
index cf13de5cd47..f24549ee0b6 100644
--- a/src/Locale/en/Asset.json
+++ b/src/Locale/en/Asset.json
@@ -1,3 +1,15 @@
{
- "create_asset": "Create Asset"
+ "create_asset": "Create Asset",
+ "edit_history": "Edit History",
+ "update_record_for_asset": "Update record for asset",
+ "edited_on": "Edited On",
+ "edited_by": "Edited By",
+ "serviced_on": "Serviced On",
+ "notes": "Notes",
+ "back": "Back",
+ "close": "Close",
+ "update_asset_service_record": "Update Asset Service Record",
+ "eg_details_on_functionality_service_etc": "Eg. Details on functionality, service, etc.",
+ "updating": "Updating",
+ "update": "Update"
}
diff --git a/src/Locale/en/Facility.json b/src/Locale/en/Facility.json
index aa44d86dda5..5e69f8108af 100644
--- a/src/Locale/en/Facility.json
+++ b/src/Locale/en/Facility.json
@@ -20,5 +20,37 @@
"type_b_cylinders": "B Type Cylinders",
"type_c_cylinders": "C Type Cylinders",
"type_d_cylinders": "D Type Cylinders",
- "select_local_body": "Select Local Body"
+ "select_local_body": "Select Local Body",
+ "update_asset": "Update Asset",
+ "create_new_asset": "Create New Asset",
+ "you_need_at_least_a_location_to_create_an_assest": "You need at least a location to create an assest.",
+ "add_location": "Add Location",
+ "close_scanner": "Close Scanner",
+ "scan_asset_qr": "Scan Asset QR!",
+ "update": "Update",
+ "create": "Create",
+ "asset_name": "Asset Name",
+ "asset_location": "Asset Location",
+ "asset_type": "Asset Type",
+ "asset_class": "Asset Class",
+ "description": "Description",
+ "details_about_the_equipment": "Details about the equipment",
+ "working_status": "Working Status",
+ "why_the_asset_is_not_working": "Why the asset is not working?",
+ "describe_why_the_asset_is_not_working": "Describe why the asset is not working",
+ "asset_qr_id": "Asset QR ID",
+ "manufacturer": "Manufacturer",
+ "eg_xyz": "Eg. XYZ",
+ "eg_abc": "Eg. ABC",
+ "warranty_amc_expiry": "Warranty / AMC Expiry",
+ "customer_support_name": "Customer Support Name",
+ "customer_support_number": "Customer support number",
+ "customer_support_email": "Customer Support Email",
+ "eg_mail_example_com": "Eg. mail@example.com",
+ "vendor_name": "Vendor Name",
+ "serial_number": "Serial Number",
+ "last_serviced_on": "Last Serviced On",
+ "notes": "Notes",
+ "create_asset": "Create Asset",
+ "create_add_more": "Create & Add More"
}
diff --git a/src/Locale/en/Medicine.json b/src/Locale/en/Medicine.json
index 8cc80234b74..d32015e7618 100644
--- a/src/Locale/en/Medicine.json
+++ b/src/Locale/en/Medicine.json
@@ -33,7 +33,9 @@
"last_administered": "Last administered",
"modification_caution_note": "No modifications possible once added",
"discontinue_caution_note": "Are you sure you want to discontinue this prescription?",
+ "edit_caution_note": "A new prescription will be added to the consultation with the edited details and the current prescription will be discontinued.",
"reason_for_discontinuation": "Reason for discontinuation",
+ "reason_for_edit": "Reason for edit",
"PRESCRIPTION_ROUTE_ORAL": "Oral",
"PRESCRIPTION_ROUTE_IV": "IV",
"PRESCRIPTION_ROUTE_IM": "IM",
@@ -47,4 +49,4 @@
"PRESCRIPTION_FREQUENCY_Q4H": "4th hourly",
"PRESCRIPTION_FREQUENCY_QOD": "Alternate day",
"PRESCRIPTION_FREQUENCY_QWK": "Once a week"
-}
+}
\ No newline at end of file
diff --git a/src/Locale/en/Notifications.json b/src/Locale/en/Notifications.json
index a4770af5618..8c6255ca850 100644
--- a/src/Locale/en/Notifications.json
+++ b/src/Locale/en/Notifications.json
@@ -3,6 +3,8 @@
"mark_as_read": "Mark as Read",
"subscribe": "Subscribe",
"subscribe_on_this_device": "Subscribe on this device",
+ "show_unread_notifications": "Show Unread",
+ "show_all_notifications": "Show All",
"filter_by_category": "Filter by category",
"mark_all_as_read": "Mark all as Read",
"reload": "Reload",
diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx
index 1a5bd7e4fb1..26e006f4cea 100644
--- a/src/Redux/actions.tsx
+++ b/src/Redux/actions.tsx
@@ -501,7 +501,7 @@ export const sampleReport = (id: string, sampleId: string) => {
export const getTestList = (params: object) => {
return fireRequest("getTestSampleList", [], params);
};
-export const getTestSample = (id: number) => {
+export const getTestSample = (id: string) => {
return fireRequest("getTestSample", [id], {});
};
export const patchSample = (params: object, pathParam: object) => {
@@ -1003,7 +1003,7 @@ export const PrescriptionActions = (consultation_external_id: string) => {
const pathParams = { consultation_external_id };
return {
- list: (query?: Record ) => {
+ list: (query?: Record) => {
let altKey;
if (query?.is_prn !== undefined) {
altKey = query?.is_prn
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 1aa06c7e1bc..8a4ca5cf1df 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -1,6 +1,7 @@
import { IConfig } from "../Common/hooks/useConfig";
import { AssetData } from "../Components/Assets/AssetTypes";
import { LocationModel } from "../Components/Facility/models";
+import { Prescription } from "../Components/Medicine/models";
import { UserModel } from "../Components/Users/models";
import { PaginatedResponse } from "../Utils/request/types";
@@ -17,6 +18,11 @@ interface JwtTokenObtainPair {
refresh: string;
}
+interface LoginInput {
+ username: string;
+ password: string;
+}
+
const routes = {
config: {
path: import.meta.env.REACT_APP_CONFIG ?? "/config.json",
@@ -30,6 +36,8 @@ const routes = {
path: "/api/v1/auth/login/",
method: "POST",
noAuth: true,
+ TRes: Type(),
+ TBody: Type(),
},
token_refresh: {
@@ -47,16 +55,25 @@ const routes = {
checkResetToken: {
path: "/api/v1/password_reset/check/",
method: "POST",
+ noAuth: true,
+ TRes: Type>(),
+ TBody: Type<{ token: string }>(),
},
resetPassword: {
path: "/api/v1/password_reset/confirm/",
method: "POST",
+ noAuth: true,
+ TRes: Type>(),
+ TBody: Type<{ password: string; confirm: string }>(),
},
forgotPassword: {
path: "/api/v1/password_reset/",
method: "POST",
+ noAuth: true,
+ TRes: Type>(),
+ TBody: Type<{ username: string }>(),
},
updatePassword: {
@@ -972,6 +989,8 @@ const routes = {
createPrescription: {
path: "/api/v1/consultation/{consultation_external_id}/prescriptions/",
method: "POST",
+ TBody: Type(),
+ TRes: Type(),
},
listAdministrations: {
@@ -997,6 +1016,8 @@ const routes = {
discontinuePrescription: {
path: "/api/v1/consultation/{consultation_external_id}/prescriptions/{external_id}/discontinue/",
method: "POST",
+ TBody: Type<{ discontinued_reason: string }>(),
+ TRes: Type>(),
},
// HCX Endpoints
diff --git a/src/Routers/AppRouter.tsx b/src/Routers/AppRouter.tsx
index f1449f13bc2..0f6108b00e3 100644
--- a/src/Routers/AppRouter.tsx
+++ b/src/Routers/AppRouter.tsx
@@ -1,64 +1,9 @@
import { useRedirect, useRoutes, usePath, Redirect } from "raviger";
import { useState, useEffect } from "react";
-import { ConsultationDetails } from "../Components/Facility/ConsultationDetails";
-import TreatmentSummary from "../Components/Facility/TreatmentSummary";
-import { ConsultationForm } from "../Components/Facility/ConsultationForm";
-import { FacilityCreate } from "../Components/Facility/FacilityCreate";
-import { FacilityHome } from "../Components/Facility/FacilityHome";
-import { HospitalList } from "../Components/Facility/HospitalList";
-import { TriageForm } from "../Components/Facility/TriageForm";
-import { DailyRounds } from "../Components/Patient/DailyRounds";
-import { PatientManager } from "../Components/Patient/ManagePatients";
-import PatientNotes from "../Components/Patient/PatientNotes";
-import { PatientHome } from "../Components/Patient/PatientHome";
-import { PatientRegister } from "../Components/Patient/PatientRegister";
-import { SampleDetails } from "../Components/Patient/SampleDetails";
-import SampleReport from "../Components/Patient/SamplePreview";
-import { SampleTest } from "../Components/Patient/SampleTest";
-import SampleViewAdmin from "../Components/Patient/SampleViewAdmin";
-import ManageUsers from "../Components/Users/ManageUsers";
-import { UserAdd } from "../Components/Users/UserAdd";
-import InventoryList from "../Components/Facility/InventoryList";
-import InventoryLog from "../Components/Facility/InventoryLog";
-import { AddInventoryForm } from "../Components/Facility/AddInventoryForm";
-import { SetInventoryForm } from "../Components/Facility/SetInventoryForm";
-import MinQuantityList from "../Components/Facility/MinQuantityList";
-import { ShiftCreate } from "../Components/Patient/ShiftCreate";
-import UserProfile from "../Components/Users/UserProfile";
-import ShiftBoardView from "../Components/Shifting/BoardView";
-import ShiftListView from "../Components/Shifting/ListView";
-import ShiftDetails from "../Components/Shifting/ShiftDetails";
-import { ShiftDetailsUpdate } from "../Components/Shifting/ShiftDetailsUpdate";
-import ResourceCreate from "../Components/Resource/ResourceCreate";
-import ResourceBoardView from "../Components/Resource/ResourceBoardView";
-import ResourceListView from "../Components/Resource/ListView";
-import ResourceDetails from "../Components/Resource/ResourceDetails";
-import { ResourceDetailsUpdate } from "../Components/Resource/ResourceDetailsUpdate";
-import ResultList from "../Components/ExternalResult/ResultList";
-import ResultItem from "../Components/ExternalResult/ResultItem";
-import ExternalResultUpload from "../Components/ExternalResult/ExternalResultUpload";
-import ResultUpdate from "../Components/ExternalResult/ResultUpdate";
-import { FileUpload } from "../Components/Patient/FileUpload";
-import Investigation from "../Components/Facility/Investigations";
-import ShowInvestigation from "../Components/Facility/Investigations/ShowInvestigation";
-import InvestigationReports from "../Components/Facility/Investigations/Reports";
-import AssetCreate from "../Components/Facility/AssetCreate";
-import DeathReport from "../Components/DeathReport/DeathReport";
-import { make as CriticalCareRecording } from "../Components/CriticalCareRecording/CriticalCareRecording.bs";
+
import ShowPushNotification from "../Components/Notifications/ShowPushNotification";
import { NoticeBoard } from "../Components/Notifications/NoticeBoard";
-import { AddLocationForm } from "../Components/Facility/AddLocationForm";
-import { AddBedForm } from "../Components/Facility/AddBedForm";
-import LocationManagement from "../Components/Facility/LocationManagement";
-import { BedManagement } from "../Components/Facility/BedManagement";
-import AssetsList from "../Components/Assets/AssetsList";
-import AssetManage from "../Components/Assets/AssetManage";
-import AssetConfigure from "../Components/Assets/AssetConfigure";
-import { DailyRoundListDetails } from "../Components/Patient/DailyRoundListDetails";
import Error404 from "../Components/ErrorPages/404";
-import { DndProvider } from "react-dnd";
-import { HTML5Backend } from "react-dnd-html5-backend";
-import FacilityUsers from "../Components/Facility/FacilityUsers";
import {
DesktopSidebar,
MobileSidebar,
@@ -66,349 +11,55 @@ import {
SidebarShrinkContext,
} from "../Components/Common/Sidebar/Sidebar";
import { BLACKLISTED_PATHS, LocalStorageKeys } from "../Common/constants";
-import { UpdateFacilityMiddleware } from "../Components/Facility/UpdateFacilityMiddleware";
import useConfig from "../Common/hooks/useConfig";
-import ConsultationClaims from "../Components/Facility/ConsultationClaims";
import { handleSignOut } from "../Utils/utils";
import SessionExpired from "../Components/ErrorPages/SessionExpired";
-import ManagePrescriptions from "../Components/Medicine/ManagePrescriptions";
-import CentralNursingStation from "../Components/Facility/CentralNursingStation";
-export default function AppRouter() {
- const { main_logo, enable_hcx } = useConfig();
+import UserRoutes from "./routes/UserRoutes";
+import PatientRoutes from "./routes/PatientRoutes";
+import SampleRoutes from "./routes/SampleRoutes";
+import FacilityRoutes from "./routes/FacilityRoutes";
+import ConsultationRoutes from "./routes/ConsultationRoutes";
+import HCXRoutes from "./routes/HCXRoutes";
+import ShiftingRoutes from "./routes/ShiftingRoutes";
+import AssetRoutes from "./routes/AssetRoutes";
+import ResourceRoutes from "./routes/ResourceRoutes";
+import ExternalResultRoutes from "./routes/ExternalResultRoutes";
+import { DetailRoute } from "./types";
+
+const Routes = {
+ "/": () => ,
- const routes = {
- "/": () => ,
- "/users": () => ,
- "/users/add": () => ,
- "/user/profile": () => ,
- "/patients": () => ,
- "/patient/:id": ({ id }: any) => ,
- "/patient/:id/investigation_reports": ({ id }: any) => (
-
- ),
- "/sample": () => ,
- "/sample/:id": ({ id }: any) => ,
- "/patient/:patientId/test_sample/:sampleId/icmr_sample": ({
- patientId,
- sampleId,
- }: any) => ,
- "/facility": () => ,
- "/facility/create": () => ,
- "/facility/:facilityId/update": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/middleware/update": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/users": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/resource/new": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/triage": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/patient": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/patient/:id": ({ facilityId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:id/update": ({ facilityId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/sample-test": ({
- facilityId,
- patientId,
- }: any) => ,
- "/facility/:facilityId/patient/:patientId/sample/:id": ({ id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/notes": ({
- facilityId,
- patientId,
- }: any) => ,
- "/facility/:facilityId/patient/:patientId/files": ({
- facilityId,
- patientId,
- }: any) => (
-
- ),
- "/facility/:facilityId/triage/:id": ({ facilityId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation": ({
- facilityId,
- patientId,
- }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:id/update": ({
- facilityId,
- patientId,
- id,
- }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:id/files/": ({
- facilityId,
- patientId,
- id,
- }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/prescriptions":
- (path: any) => ,
- "/facility/:facilityId/patient/:patientId/consultation/:id/investigation":
- ({ facilityId, patientId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:id/investigation/:sessionId":
- ({ facilityId, patientId, id, sessionId }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:id/daily-rounds": ({
- facilityId,
- patientId,
- id,
- }: any) => (
-
- ),
- ...(enable_hcx
- ? {
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/claims":
- (pathParams: any) => ,
- }
- : {}),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id/update":
- ({ facilityId, patientId, consultationId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id":
- ({ facilityId, patientId, consultationId, id }: any) => (
-
- ),
+ ...AssetRoutes,
+ ...ConsultationRoutes,
+ ...ExternalResultRoutes,
+ ...FacilityRoutes,
+ ...PatientRoutes,
+ ...ResourceRoutes,
+ ...SampleRoutes,
+ ...ShiftingRoutes,
+ ...UserRoutes,
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id":
- ({ facilityId, patientId, consultationId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id/update":
- ({ facilityId, patientId, consultationId, id }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/shift/new": ({
- facilityId,
- patientId,
- }: any) => ,
- "/facility/:facilityId/inventory": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/location": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/location/:locationId/beds": ({
- facilityId,
- locationId,
- }: any) => (
-
- ),
- "/facility/:facilityId/inventory/add": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/location/add": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/location/:locationId/update": ({
- facilityId,
- locationId,
- }: any) => (
-
- ),
- "/facility/:facilityId/location/:locationId/beds/add": ({
- facilityId,
- locationId,
- }: any) => ,
- "/facility/:facilityId/location/:locationId/beds/:bedId/update": ({
- facilityId,
- locationId,
- bedId,
- }: any) => (
-
- ),
- "/facility/:facilityId/inventory/min_quantity/set": ({
- facilityId,
- }: any) => ,
- "/facility/:facilityId/inventory/min_quantity/list": ({
- facilityId,
- }: any) => ,
- "/facility/:facilityId/inventory/min_quantity": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/inventory/:inventoryId": ({
- facilityId,
- inventoryId,
- }: any) => (
-
- ),
- "/facility/:facilityId/assets/new": ({ facilityId }: any) => (
-
- ),
- "/facility/:facilityId/assets/:assetId/update": ({
- facilityId,
- assetId,
- }: any) => ,
- "/assets": () => ,
- "/facility/:facilityId/assets/:assetId": ({ assetId, facilityId }: any) => (
-
- ),
- "/facility/:facilityId/assets/:assetId/configure": ({
- assetId,
- facilityId,
- }: any) => ,
- "/facility/:facilityId/cns": ({ facilityId }: any) => (
-
- ),
+ "/notifications/:id": ({ id }: DetailRoute) => (
+
+ ),
+ "/notice_board": () => ,
+
+ "/session-expired": () => ,
+ "/not-found": () => ,
+};
+
+export default function AppRouter() {
+ const { main_logo, enable_hcx } = useConfig();
- "/shifting": () =>
- localStorage.getItem("defaultShiftView") === "list" ? (
-
- ) : (
-
-
-
- ),
- "/shifting/board-view": () => (
-
-
-
- ),
- "/shifting/list-view": () => ,
- "/shifting/:id": ({ id }: any) => ,
- "/shifting/:id/update": ({ id }: any) => ,
- "/resource": () =>
- localStorage.getItem("defaultResourceView") === "list" ? (
-
- ) : (
-
-
-
- ),
+ let routes = Routes;
- "/resource/board-view": () => (
-
-
-
- ),
- "/resource/list-view": () => ,
- "/resource/:id": ({ id }: any) => ,
- "/resource/:id/update": ({ id }: any) => ,
- "/external_results": () => ,
- "/external_results/upload": () => ,
- "/external_results/:id": ({ id }: any) => ,
- "/external_results/:id/update": ({ id }: any) => ,
- "/death_report/:id": ({ id }: any) => ,
- "/notifications/:id": (id: any) => (
-
- ),
- "/notice_board": () => ,
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId": ({
- facilityId,
- patientId,
- consultationId,
- }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/treatment-summary":
- ({ facilityId, patientId, consultationId }: any) => (
-
- ),
- "/facility/:facilityId/patient/:patientId/consultation/:consultationId/:tab":
- ({ facilityId, patientId, consultationId, tab }: any) => (
-
- ),
- "/session-expired": () => ,
- "/not-found": () => ,
- };
+ if (enable_hcx) {
+ routes = { ...routes, ...HCXRoutes };
+ }
- useRedirect("/", "/facility");
useRedirect("/user", "/users");
- const pages = useRoutes(routes as any) || ;
+ const pages = useRoutes(routes) || ;
const path = usePath();
const [sidebarOpen, setSidebarOpen] = useState(false);
diff --git a/src/Routers/routes/AssetRoutes.tsx b/src/Routers/routes/AssetRoutes.tsx
new file mode 100644
index 00000000000..d3bd96ca437
--- /dev/null
+++ b/src/Routers/routes/AssetRoutes.tsx
@@ -0,0 +1,21 @@
+import AssetConfigure from "../../Components/Assets/AssetConfigure";
+import AssetManage from "../../Components/Assets/AssetManage";
+import AssetsList from "../../Components/Assets/AssetsList";
+import AssetCreate from "../../Components/Facility/AssetCreate";
+
+export default {
+ "/assets": () => ,
+
+ "/facility/:facilityId/assets/new": (params: any) => (
+
+ ),
+ "/facility/:facilityId/assets/:assetId/update": (params: any) => (
+
+ ),
+ "/facility/:facilityId/assets/:assetId": (params: any) => (
+
+ ),
+ "/facility/:facilityId/assets/:assetId/configure": (params: any) => (
+
+ ),
+};
diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx
new file mode 100644
index 00000000000..4f1d6e7d75d
--- /dev/null
+++ b/src/Routers/routes/ConsultationRoutes.tsx
@@ -0,0 +1,141 @@
+import { ConsultationForm } from "../../Components/Facility/ConsultationForm";
+import Investigation from "../../Components/Facility/Investigations";
+import ShowInvestigation from "../../Components/Facility/Investigations/ShowInvestigation";
+import ManagePrescriptions from "../../Components/Medicine/ManagePrescriptions";
+import { DailyRoundListDetails } from "../../Components/Patient/DailyRoundListDetails";
+import { DailyRounds } from "../../Components/Patient/DailyRounds";
+import { FileUpload } from "../../Components/Patient/FileUpload";
+import { make as CriticalCareRecording } from "../../Components/CriticalCareRecording/CriticalCareRecording.bs";
+import { ConsultationDetails } from "../../Components/Facility/ConsultationDetails";
+import TreatmentSummary from "../../Components/Facility/TreatmentSummary";
+
+export default {
+ "/facility/:facilityId/patient/:patientId/consultation": ({
+ facilityId,
+ patientId,
+ }: any) => ,
+ "/facility/:facilityId/patient/:patientId/consultation/:id/update": ({
+ facilityId,
+ patientId,
+ id,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:id/files/": ({
+ facilityId,
+ patientId,
+ id,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/prescriptions":
+ (path: any) => ,
+ "/facility/:facilityId/patient/:patientId/consultation/:id/investigation": ({
+ facilityId,
+ patientId,
+ id,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:id/investigation/:sessionId":
+ ({ facilityId, patientId, id, sessionId }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:id/daily-rounds": ({
+ facilityId,
+ patientId,
+ id,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id/update":
+ ({ facilityId, patientId, consultationId, id }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id":
+ ({ facilityId, patientId, consultationId, id }: any) => (
+
+ ),
+
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id":
+ ({ facilityId, patientId, consultationId, id }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id/update":
+ ({ facilityId, patientId, consultationId, id }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId": ({
+ facilityId,
+ patientId,
+ consultationId,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/treatment-summary":
+ ({ facilityId, patientId, consultationId }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/:tab":
+ ({ facilityId, patientId, consultationId, tab }: any) => (
+
+ ),
+};
diff --git a/src/Routers/routes/ExternalResultRoutes.tsx b/src/Routers/routes/ExternalResultRoutes.tsx
new file mode 100644
index 00000000000..af4bf090d78
--- /dev/null
+++ b/src/Routers/routes/ExternalResultRoutes.tsx
@@ -0,0 +1,14 @@
+import ExternalResultUpload from "../../Components/ExternalResult/ExternalResultUpload";
+import ResultItem from "../../Components/ExternalResult/ResultItem";
+import ResultList from "../../Components/ExternalResult/ResultList";
+import ResultUpdate from "../../Components/ExternalResult/ResultUpdate";
+import { DetailRoute } from "../types";
+
+export default {
+ "/external_results": () => ,
+ "/external_results/upload": () => ,
+ "/external_results/:id": ({ id }: DetailRoute) => ,
+ "/external_results/:id/update": ({ id }: DetailRoute) => (
+
+ ),
+};
diff --git a/src/Routers/routes/FacilityInventoryRoutes.tsx b/src/Routers/routes/FacilityInventoryRoutes.tsx
new file mode 100644
index 00000000000..17e93b2bc60
--- /dev/null
+++ b/src/Routers/routes/FacilityInventoryRoutes.tsx
@@ -0,0 +1,24 @@
+import { Redirect } from "raviger";
+import InventoryList from "../../Components/Facility/InventoryList";
+import InventoryLog from "../../Components/Facility/InventoryLog";
+import MinQuantityList from "../../Components/Facility/MinQuantityList";
+import { SetInventoryForm } from "../../Components/Facility/SetInventoryForm";
+
+export default {
+ "/facility/:facilityId/inventory": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/inventory/min_quantity/set": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/inventory/min_quantity/list": ({
+ facilityId,
+ }: any) => ,
+ "/facility/:facilityId/inventory/min_quantity": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/inventory/:inventoryId": ({
+ facilityId,
+ inventoryId,
+ }: any) => ,
+};
diff --git a/src/Routers/routes/FacilityLocationRoutes.tsx b/src/Routers/routes/FacilityLocationRoutes.tsx
new file mode 100644
index 00000000000..c43673b60f5
--- /dev/null
+++ b/src/Routers/routes/FacilityLocationRoutes.tsx
@@ -0,0 +1,38 @@
+import { AddBedForm } from "../../Components/Facility/AddBedForm";
+import { AddInventoryForm } from "../../Components/Facility/AddInventoryForm";
+import { AddLocationForm } from "../../Components/Facility/AddLocationForm";
+import { BedManagement } from "../../Components/Facility/BedManagement";
+import LocationManagement from "../../Components/Facility/LocationManagement";
+
+export default {
+ "/facility/:facilityId/location": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/location/:locationId/beds": ({
+ facilityId,
+ locationId,
+ }: any) => ,
+ "/facility/:facilityId/inventory/add": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/location/add": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/location/:locationId/update": ({
+ facilityId,
+ locationId,
+ }: any) => (
+
+ ),
+ "/facility/:facilityId/location/:locationId/beds/add": ({
+ facilityId,
+ locationId,
+ }: any) => ,
+ "/facility/:facilityId/location/:locationId/beds/:bedId/update": ({
+ facilityId,
+ locationId,
+ bedId,
+ }: any) => (
+
+ ),
+};
diff --git a/src/Routers/routes/FacilityRoutes.tsx b/src/Routers/routes/FacilityRoutes.tsx
new file mode 100644
index 00000000000..77247df9189
--- /dev/null
+++ b/src/Routers/routes/FacilityRoutes.tsx
@@ -0,0 +1,45 @@
+import { FacilityConfigure } from "../../Components/Facility/FacilityConfigure";
+import { FacilityCreate } from "../../Components/Facility/FacilityCreate";
+import { FacilityHome } from "../../Components/Facility/FacilityHome";
+import FacilityUsers from "../../Components/Facility/FacilityUsers";
+import { HospitalList } from "../../Components/Facility/HospitalList";
+import { TriageForm } from "../../Components/Facility/TriageForm";
+import ResourceCreate from "../../Components/Resource/ResourceCreate";
+import CentralNursingStation from "../../Components/Facility/CentralNursingStation";
+import FacilityLocationRoutes from "./FacilityLocationRoutes";
+import FacilityInventoryRoutes from "./FacilityInventoryRoutes";
+
+export default {
+ "/facility": () => ,
+ "/facility/create": () => ,
+ "/facility/:facilityId/update": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/configure": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/cns": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId": ({ facilityId }: any) => (
+
+ ),
+
+ "/facility/:facilityId/users": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/resource/new": ({ facilityId }: any) => (
+
+ ),
+
+ // Triage related routes
+ "/facility/:facilityId/triage": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/triage/:id": ({ facilityId, id }: any) => (
+
+ ),
+
+ ...FacilityLocationRoutes,
+ ...FacilityInventoryRoutes,
+};
diff --git a/src/Routers/routes/HCXRoutes.tsx b/src/Routers/routes/HCXRoutes.tsx
new file mode 100644
index 00000000000..8a36e033c15
--- /dev/null
+++ b/src/Routers/routes/HCXRoutes.tsx
@@ -0,0 +1,6 @@
+import ConsultationClaims from "../../Components/Facility/ConsultationClaims";
+
+export default {
+ "/facility/:facilityId/patient/:patientId/consultation/:consultationId/claims":
+ (pathParams: any) => ,
+};
diff --git a/src/Routers/routes/PatientRoutes.tsx b/src/Routers/routes/PatientRoutes.tsx
new file mode 100644
index 00000000000..ae594d767ec
--- /dev/null
+++ b/src/Routers/routes/PatientRoutes.tsx
@@ -0,0 +1,46 @@
+import InvestigationReports from "../../Components/Facility/Investigations/Reports";
+import { FileUpload } from "../../Components/Patient/FileUpload";
+import { PatientManager } from "../../Components/Patient/ManagePatients";
+import { PatientHome } from "../../Components/Patient/PatientHome";
+import PatientNotes from "../../Components/Patient/PatientNotes";
+import { PatientRegister } from "../../Components/Patient/PatientRegister";
+import { DetailRoute } from "../types";
+import DeathReport from "../../Components/DeathReport/DeathReport";
+
+export default {
+ "/patients": () => ,
+ "/patient/:id": ({ id }: DetailRoute) => ,
+ "/patient/:id/investigation_reports": ({ id }: DetailRoute) => (
+
+ ),
+
+ // Facility Scoped Routes
+ "/facility/:facilityId/patient": ({ facilityId }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:id": ({ facilityId, id }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:id/update": ({ facilityId, id }: any) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/notes": ({
+ facilityId,
+ patientId,
+ }: any) => ,
+ "/facility/:facilityId/patient/:patientId/files": ({
+ facilityId,
+ patientId,
+ }: any) => (
+
+ ),
+ "/death_report/:id": ({ id }: any) => ,
+};
diff --git a/src/Routers/routes/ResourceRoutes.tsx b/src/Routers/routes/ResourceRoutes.tsx
new file mode 100644
index 00000000000..8408ab4d79d
--- /dev/null
+++ b/src/Routers/routes/ResourceRoutes.tsx
@@ -0,0 +1,25 @@
+import { DndProvider } from "react-dnd";
+import { HTML5Backend } from "react-dnd-html5-backend";
+import ResourceDetails from "../../Components/Resource/ResourceDetails";
+import { ResourceDetailsUpdate } from "../../Components/Resource/ResourceDetailsUpdate";
+import ListView from "../../Components/Resource/ListView";
+import BoardView from "../../Components/Resource/ResourceBoardView";
+import { Redirect } from "raviger";
+import { DetailRoute } from "../types";
+
+const getDefaultView = () =>
+ localStorage.getItem("defaultResourceView") === "list" ? "list" : "board";
+
+export default {
+ "/resource": () => ,
+ "/resource/board": () => (
+
+
+
+ ),
+ "/resource/list": () => ,
+ "/resource/:id": ({ id }: DetailRoute) => ,
+ "/resource/:id/update": ({ id }: DetailRoute) => (
+
+ ),
+};
diff --git a/src/Routers/routes/SampleRoutes.tsx b/src/Routers/routes/SampleRoutes.tsx
new file mode 100644
index 00000000000..290a34fd4eb
--- /dev/null
+++ b/src/Routers/routes/SampleRoutes.tsx
@@ -0,0 +1,25 @@
+import { SampleDetails } from "../../Components/Patient/SampleDetails";
+import SampleReport from "../../Components/Patient/SamplePreview";
+import { SampleTest } from "../../Components/Patient/SampleTest";
+import SampleViewAdmin from "../../Components/Patient/SampleViewAdmin";
+import { DetailRoute, RouteParams } from "../types";
+
+export default {
+ "/sample": () => ,
+ "/sample/:id": ({ id }: DetailRoute) => ,
+ "/patient/:patientId/test_sample/:sampleId/icmr_sample": ({
+ patientId,
+ sampleId,
+ }: RouteParams<"patientId" | "sampleId">) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/sample-test": ({
+ facilityId,
+ patientId,
+ }: RouteParams<"facilityId" | "patientId">) => (
+
+ ),
+ "/facility/:facilityId/patient/:patientId/sample/:id": ({
+ id,
+ }: DetailRoute) => ,
+};
diff --git a/src/Routers/routes/ShiftingRoutes.tsx b/src/Routers/routes/ShiftingRoutes.tsx
new file mode 100644
index 00000000000..9b20b4a1a0b
--- /dev/null
+++ b/src/Routers/routes/ShiftingRoutes.tsx
@@ -0,0 +1,27 @@
+import { DndProvider } from "react-dnd";
+import { HTML5Backend } from "react-dnd-html5-backend";
+import { ShiftCreate } from "../../Components/Patient/ShiftCreate";
+import ShiftDetails from "../../Components/Shifting/ShiftDetails";
+import { ShiftDetailsUpdate } from "../../Components/Shifting/ShiftDetailsUpdate";
+import ListView from "../../Components/Shifting/ListView";
+import BoardView from "../../Components/Shifting/BoardView";
+import { Redirect } from "raviger";
+
+const getDefaultView = () =>
+ localStorage.getItem("defaultShiftView") === "list" ? "list" : "board";
+
+export default {
+ "/shifting": () => ,
+ "/shifting/board": () => (
+
+
+
+ ),
+ "/shifting/list": () => ,
+ "/shifting/:id": ({ id }: any) => ,
+ "/shifting/:id/update": ({ id }: any) => ,
+ "/facility/:facilityId/patient/:patientId/shift/new": ({
+ facilityId,
+ patientId,
+ }: any) => ,
+};
diff --git a/src/Routers/routes/UserRoutes.tsx b/src/Routers/routes/UserRoutes.tsx
new file mode 100644
index 00000000000..56877ca4c78
--- /dev/null
+++ b/src/Routers/routes/UserRoutes.tsx
@@ -0,0 +1,9 @@
+import ManageUsers from "../../Components/Users/ManageUsers";
+import { UserAdd } from "../../Components/Users/UserAdd";
+import UserProfile from "../../Components/Users/UserProfile";
+
+export default {
+ "/users": () => ,
+ "/users/add": () => ,
+ "/user/profile": () => ,
+};
diff --git a/src/Routers/types.ts b/src/Routers/types.ts
new file mode 100644
index 00000000000..dc7138b8df7
--- /dev/null
+++ b/src/Routers/types.ts
@@ -0,0 +1,5 @@
+export type RouteParams = Record;
+
+export interface DetailRoute {
+ id: string;
+}
|