diff --git a/.env b/.env
index fcabed6db34..38fd950b0d8 100644
--- a/.env
+++ b/.env
@@ -1,11 +1,10 @@
# Whitelabelling envs
-REACT_APP_TITLE="CARE"
-REACT_APP_META_DESCRIPTION="CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations."
+REACT_APP_TITLE=CARE
+REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations.
REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg
REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg
-REACT_APP_CONFIG=""
-REACT_PUBLIC_URL="https://care.coronasafe.in"
+REACT_PUBLIC_URL=https://care.coronasafe.in
# Dev envs
ESLINT_NO_DEV_ERRORS=true
diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml
index 4f23fbe4937..f66f9a37bd8 100644
--- a/.github/workflows/cypress.yaml
+++ b/.github/workflows/cypress.yaml
@@ -25,12 +25,13 @@ jobs:
path: care
- name: Run docker compose up on care 🐳
- run: cd care && touch .env && make docker_config_file=docker-compose.pre-built.yaml up && cd .. && sleep 60s
- # Voluntarily kept 60 seconds delay to wait for migrations to complete.
-
- - name: Run Django collectstatic and load dummy data on care 🐍
run: |
- docker exec care python manage.py load_dummy_data
+ cd care
+ make docker_config_file=docker-compose.pre-built.yaml up
+ sleep 60s
+ docker compose exec backend bash -c "python manage.py load_dummy_data"
+ cd ..
+ # Voluntarily kept 60 seconds delay to wait for migrations to complete.
- name: Check care is up ♻
run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000
diff --git a/.gitignore b/.gitignore
index b97356ed20f..4ca589aab39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -54,6 +54,7 @@ public/build-meta.json
# Using NPM
yarn.lock
pnpm-lock.yaml
+bun.lockb
# Cypress
cypress/downloads
diff --git a/cypress/e2e/assets_spec/asset_tab.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts
similarity index 55%
rename from cypress/e2e/assets_spec/asset_tab.cy.ts
rename to cypress/e2e/assets_spec/asset_homepage.cy.ts
index 0abf11fcaf0..6e9ceb9676b 100644
--- a/cypress/e2e/assets_spec/asset_tab.cy.ts
+++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts
@@ -5,15 +5,21 @@ import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch";
import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan";
import { AssetPagination } from "../../pageobject/Asset/AssetPagination";
import { AssetFilters } from "../../pageobject/Asset/AssetFilters";
+import LoginPage from "../../pageobject/Login/LoginPage";
+import { v4 as uuidv4 } from "uuid";
describe("Asset Tab", () => {
const assetSearchPage = new AssetSearchPage();
const assetQRScanPage = new AssetQRScanPage();
const assetPagination = new AssetPagination();
const assetFilters = new AssetFilters();
+ const loginPage = new LoginPage();
+ const assetName = "Dummy Camera 10";
+ const qrCode = uuidv4();
+ const serialNumber = Math.floor(Math.random() * 10 ** 10).toString();
before(() => {
- cy.loginByApi("devdistrictadmin", "Coronasafe@123");
+ loginPage.loginAsDisctrictAdmin();
cy.saveLocalStorage();
});
@@ -24,11 +30,23 @@ describe("Asset Tab", () => {
// search for a element
- it("Search Asset Name", () => {
- const initialUrl = cy.url();
- assetSearchPage.typeSearchKeyword("dummy camera 30");
+ it("Search Asset Name/QR_ID/Serial_number", () => {
+ assetSearchPage.typeSearchKeyword(assetName);
assetSearchPage.pressEnter();
- assetSearchPage.verifyUrlChanged(initialUrl);
+ assetSearchPage.verifyBadgeContent(assetName);
+ assetSearchPage.clickAssetByName(assetName);
+ assetSearchPage.clickUpdateButton();
+ assetSearchPage.clearAndTypeQRCode(qrCode);
+ assetSearchPage.clearAndTypeSerialNumber(serialNumber);
+ assetSearchPage.clickAssetSubmitButton();
+ assetSearchPage.visitAssetsPage();
+ assetSearchPage.typeSearchKeyword(qrCode);
+ assetSearchPage.pressEnter();
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(qrCode);
+ assetSearchPage.typeSearchKeyword(serialNumber);
+ assetSearchPage.verifyAssetListContains(assetName);
+ assetSearchPage.verifyBadgeContent(serialNumber);
});
// scan a asset qr code
diff --git a/cypress/pageobject/Asset/AssetSearch.ts b/cypress/pageobject/Asset/AssetSearch.ts
index 315a414a62b..fea6a975983 100644
--- a/cypress/pageobject/Asset/AssetSearch.ts
+++ b/cypress/pageobject/Asset/AssetSearch.ts
@@ -1,16 +1,22 @@
export class AssetSearchPage {
typeSearchKeyword(keyword: string) {
- cy.get("[name='search']").type(keyword);
+ cy.get("#search").clear();
+ cy.get("#search").click().type(keyword);
}
pressEnter() {
cy.get("[name='search']").type("{enter}");
}
- verifyUrlChanged(initialUrl: string) {
- cy.url().should((currentUrl) => {
- expect(currentUrl).not.to.equal(initialUrl);
- });
+ clickAssetByName(assetName: string) {
+ cy.get("[data-testid='created-asset-list']").contains(assetName).click();
+ }
+
+ verifyBadgeContent(expectedText: string) {
+ cy.get("[data-testid='Name/Serial No./QR ID']").should(
+ "contain",
+ expectedText
+ );
}
verifyAssetIsPresent(assetName: string) {
@@ -18,4 +24,35 @@ export class AssetSearchPage {
.first()
.should("contain", assetName);
}
+
+ clickUpdateButton() {
+ cy.get("[data-testid='asset-update-button']").contains("Update").click();
+ }
+
+ clearAndTypeQRCode(qrCode: string) {
+ cy.get("#qr_code_id").clear();
+ cy.get("#qr_code_id").click().type(qrCode);
+ }
+
+ clearAndTypeSerialNumber(serialNumber: string) {
+ cy.get("#serial-number").clear();
+ cy.get("#serial-number").click().type(serialNumber);
+ }
+
+ clickAssetSubmitButton() {
+ cy.intercept("GET", "**/api/v1/asset/**").as("getAssets");
+ cy.get("#submit").click();
+ cy.wait("@getAssets").its("response.statusCode").should("eq", 200);
+ }
+
+ visitAssetsPage() {
+ cy.visit("/assets");
+ }
+
+ verifyAssetListContains(dummyCameraText: string) {
+ cy.get("[data-testid='created-asset-list']").should(
+ "contain",
+ dummyCameraText
+ );
+ }
}
diff --git a/src/Common/hooks/useAppHistory.ts b/src/Common/hooks/useAppHistory.ts
index 893b1a06cfd..70ad92692de 100644
--- a/src/Common/hooks/useAppHistory.ts
+++ b/src/Common/hooks/useAppHistory.ts
@@ -10,14 +10,12 @@ export default function useAppHistory() {
const resetHistory = useContext(ResetHistoryContext);
const goBack = (fallbackUrl?: string) => {
- if (history.length > 1)
- // Navigate to history present in the app navigation history stack.
- return navigate(history[1]);
-
if (fallbackUrl)
- // Otherwise, use provided fallback url if provided.
+ // use provided fallback url if provided.
return navigate(fallbackUrl);
-
+ if (history.length > 1)
+ // Otherwise, navigate to history present in the app navigation history stack.
+ return navigate(history[1]);
// Otherwise, fallback to browser's go back behaviour.
window.history.back();
};
diff --git a/src/Components/Assets/AssetFilter.tsx b/src/Components/Assets/AssetFilter.tsx
index b0a870d0e31..299d67e5ab0 100644
--- a/src/Components/Assets/AssetFilter.tsx
+++ b/src/Components/Assets/AssetFilter.tsx
@@ -108,9 +108,9 @@ function AssetFilter(props: any) {
const applyFilter = () => {
const data = {
facility: facilityId,
- asset_type: asset_type,
- asset_class: asset_class,
- status: asset_status,
+ asset_type: asset_type ?? "",
+ asset_class: asset_class ?? "",
+ status: asset_status ?? "",
location: locationId,
};
onChange(data);
diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx
index 825cee1c848..25f879ddd09 100644
--- a/src/Components/Assets/AssetManage.tsx
+++ b/src/Components/Assets/AssetManage.tsx
@@ -29,7 +29,7 @@ import { useTranslation } from "react-i18next";
const PageTitle = lazy(() => import("../Common/PageTitle"));
const Loading = lazy(() => import("../Common/Loading"));
import * as Notification from "../../Utils/Notifications.js";
-import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
+import AuthorizeFor, { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
import Uptime from "../Common/Uptime";
import useAuthUser from "../../Common/hooks/useAuthUser";
import dayjs from "dayjs";
@@ -452,7 +452,7 @@ const AssetManage = (props: AssetManageProps) => {
}
id="configure-asset"
data-testid="asset-configure-button"
- authorizeFor={NonReadOnlyUsers}
+ authorizeFor={AuthorizeFor(["DistrictAdmin", "StateAdmin"])}
>
{t("configure")}
diff --git a/src/Components/Facility/AddLocationForm.tsx b/src/Components/Facility/AddLocationForm.tsx
index d3c64ec9e04..f0dd7893aca 100644
--- a/src/Components/Facility/AddLocationForm.tsx
+++ b/src/Components/Facility/AddLocationForm.tsx
@@ -158,7 +158,7 @@ export const AddLocationForm = (props: LocationFormProps) => {
/>
-
+
navigate(`/facility/${facilityId}/location`, {
diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx
deleted file mode 100644
index 401f113a812..00000000000
--- a/src/Components/Facility/ConsultationDetails.tsx
+++ /dev/null
@@ -1,1324 +0,0 @@
-import { AssetBedModel, AssetClass, AssetData } from "../Assets/AssetTypes";
-import {
- CONSULTATION_TABS,
- DISCHARGE_REASONS,
- GENDER_TYPES,
- OptionsType,
- SYMPTOM_CHOICES,
-} from "../../Common/constants";
-import {
- BedModel,
- ConsultationModel,
- FacilityModel,
- ICD11DiagnosisModel,
-} from "./models";
-import {
- getConsultation,
- getPatient,
- getPermittedFacility,
- listAssetBeds,
-} from "../../Redux/actions";
-import { statusType, useAbortableEffect } from "../../Common/utils";
-import { lazy, useCallback, useEffect, useState } from "react";
-
-import { ABGPlots } from "./Consultations/ABGPlots";
-import ButtonV2 from "../Common/components/ButtonV2";
-import CareIcon from "../../CAREUI/icons/CareIcon";
-import Chip from "../../CAREUI/display/Chip";
-import { DailyRoundsList } from "./Consultations/DailyRoundsList";
-import { DialysisPlots } from "./Consultations/DialysisPlots";
-import DischargeModal from "./DischargeModal";
-import DischargeSummaryModal from "./DischargeSummaryModal";
-import DoctorVideoSlideover from "./DoctorVideoSlideover";
-import { Feed } from "./Consultations/Feed";
-import { FileUpload } from "../Patient/FileUpload";
-import HL7PatientVitalsMonitor from "../VitalsMonitor/HL7PatientVitalsMonitor";
-import InvestigationTab from "./Investigations/investigationsTab";
-import { make as Link } from "../Common/components/Link.bs";
-import { NeurologicalTable } from "./Consultations/NeurologicalTables";
-import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor";
-import { NursingPlot } from "./Consultations/NursingPlot";
-import { NutritionPlots } from "./Consultations/NutritionPlots";
-import PatientInfoCard from "../Patient/PatientInfoCard";
-import { PatientModel } from "../Patient/models";
-import PrescriptionsTable from "../Medicine/PrescriptionsTable";
-import { PressureSoreDiagrams } from "./Consultations/PressureSoreDiagrams";
-import { PrimaryParametersPlot } from "./Consultations/PrimaryParametersPlot";
-import ReadMore from "../Common/components/Readmore";
-import VentilatorPatientVitalsMonitor from "../VitalsMonitor/VentilatorPatientVitalsMonitor";
-import { VentilatorPlot } from "./Consultations/VentilatorPlot";
-import { formatDate, formatDateTime, relativeTime } from "../../Utils/utils";
-
-import { navigate } from "raviger";
-import { useDispatch } from "react-redux";
-import { useQueryParams } from "raviger";
-import { useTranslation } from "react-i18next";
-import { triggerGoal } from "../Common/Plausible";
-import useVitalsAspectRatioConfig from "../VitalsMonitor/useVitalsAspectRatioConfig";
-import useAuthUser from "../../Common/hooks/useAuthUser";
-import PrescriptionAdministrationsTable from "../Medicine/PrescriptionAdministrationsTable";
-
-const Loading = lazy(() => import("../Common/Loading"));
-const PageTitle = lazy(() => import("../Common/PageTitle"));
-const symptomChoices = [...SYMPTOM_CHOICES];
-
-export const ConsultationDetails = (props: any) => {
- const { t } = useTranslation();
- const { facilityId, patientId, consultationId } = props;
- const tab = props.tab.toUpperCase();
- const dispatch: any = useDispatch();
- const [isLoading, setIsLoading] = useState(false);
- const [showDoctors, setShowDoctors] = useState(false);
- const [qParams, _] = useQueryParams();
-
- const [consultationData, setConsultationData] = useState(
- {} as ConsultationModel
- );
- const [patientData, setPatientData] = useState({});
- const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
- useState(false);
- const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
- const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
-
- const getPatientGender = (patientData: any) =>
- GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
-
- const getPatientAddress = (patientData: any) =>
- `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
-
- const getPatientComorbidities = (patientData: any) => {
- if (patientData?.medical_history?.length) {
- const medHis = patientData.medical_history;
- return medHis.map((item: any) => item.disease).join(", ");
- } else {
- return "None";
- }
- };
-
- const [hl7SocketUrl, setHL7SocketUrl] = useState();
- const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
- const [monitorBedData, setMonitorBedData] = useState();
- const [ventilatorBedData, setVentilatorBedData] = useState();
- const authUser = useAuthUser();
-
- useEffect(() => {
- if (
- !consultationData.facility ||
- !consultationData.current_bed?.bed_object.id
- )
- return;
-
- const fetchData = async () => {
- const [facilityRes, assetBedRes] = await Promise.all([
- dispatch(getPermittedFacility(consultationData.facility as any)),
- dispatch(
- listAssetBeds({
- facility: consultationData.facility as any,
- bed: consultationData.current_bed?.bed_object.id,
- })
- ),
- ]);
-
- const { middleware_address } = facilityRes.data as FacilityModel;
- const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
-
- const monitorBedData = assetBeds?.find(
- (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
- );
- setMonitorBedData(monitorBedData);
- const assetDataForMonitor = monitorBedData?.asset_object;
- const hl7Meta = assetDataForMonitor?.meta;
- const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
- if (hl7Middleware && hl7Meta?.local_ip_address) {
- setHL7SocketUrl(
- `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
- );
- }
-
- const consultationBedVentilator =
- consultationData?.current_bed?.assets_objects?.find(
- (i) => i.asset_class === AssetClass.VENTILATOR
- );
- let ventilatorBedData;
- if (consultationBedVentilator) {
- ventilatorBedData = {
- asset_object: consultationBedVentilator,
- bed_object: consultationData?.current_bed?.bed_object,
- } as AssetBedModel;
- } else {
- ventilatorBedData = assetBeds?.find(
- (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
- );
- }
- setVentilatorBedData(ventilatorBedData);
- const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
- const ventilatorMiddleware =
- ventilatorMeta?.middleware_hostname || middleware_address;
- if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
- setVentilatorSocketUrl(
- `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
- );
- }
-
- if (
- !(hl7Middleware && hl7Meta?.local_ip_address) &&
- !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
- ) {
- setHL7SocketUrl(undefined);
- setVentilatorSocketUrl(undefined);
- }
- };
-
- fetchData();
- }, [consultationData]);
-
- const fetchData = useCallback(
- async (status: statusType) => {
- setIsLoading(true);
- const res = await dispatch(getConsultation(consultationId));
- if (!status.aborted) {
- if (res?.data) {
- const data: ConsultationModel = {
- ...res.data,
- symptoms_text: "",
- };
- if (res.data.symptoms?.length) {
- const symptoms = res.data.symptoms
- .filter((symptom: number) => symptom !== 9)
- .map((symptom: number) => {
- const option = symptomChoices.find((i) => i.id === symptom);
- return option ? option.text.toLowerCase() : symptom;
- });
- data.symptoms_text = symptoms.join(", ");
- }
- setConsultationData(data);
- const id = res.data.patient;
- const patientRes = await dispatch(getPatient({ id }));
- if (patientRes?.data) {
- const patientGender = getPatientGender(patientRes.data);
- const patientAddress = getPatientAddress(patientRes.data);
- const patientComorbidities = getPatientComorbidities(
- patientRes.data
- );
- const data = {
- ...patientRes.data,
- gender: patientGender,
- address: patientAddress,
- comorbidities: patientComorbidities,
- is_declared_positive: patientRes.data.is_declared_positive
- ? "Yes"
- : "No",
- is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
- };
- setPatientData(data);
- }
- } else {
- navigate("/not-found");
- }
- setIsLoading(false);
- }
- },
- [consultationId, dispatch, patientData.is_vaccinated]
- );
-
- useAbortableEffect((status: statusType) => {
- fetchData(status);
- triggerGoal("Patient Consultation Viewed", {
- facilityId: facilityId,
- consultationId: consultationId,
- userId: authUser.id,
- });
- }, []);
-
- const vitals = useVitalsAspectRatioConfig({
- default: undefined,
- md: 8 / 11,
- lg: 15 / 11,
- xl: 13 / 11,
- "2xl": 19 / 11,
- "3xl": 23 / 11,
- });
-
- if (isLoading) {
- return ;
- }
-
- const tabButtonClasses = (selected: boolean) =>
- `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
- selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
- }`;
-
- const ShowDiagnosis = ({
- diagnoses = [],
- label = "Diagnosis",
- nshow = 2,
- }: {
- diagnoses: ICD11DiagnosisModel[] | undefined;
- label: string;
- nshow?: number;
- }) => {
- const [showMore, setShowMore] = useState(false);
-
- return diagnoses.length ? (
-
- ) : null;
- };
-
- return (
-
-
setOpenDischargeSummaryDialog(false)}
- />
-
- setOpenDischargeDialog(false)}
- consultationData={consultationData}
- />
-
-
-
-
-
-
-
-
- {consultationData.admitted_to && (
-
-
- Patient
- {consultationData.discharge_date
- ? " Discharged from"
- : " Admitted to"}
-
- {consultationData.admitted_to}
-
-
- {(consultationData.admission_date ??
- consultationData.discharge_date) && (
-
- {relativeTime(
- consultationData.discharge_date
- ? consultationData.discharge_date
- : consultationData.admission_date
- )}
-
- )}
-
- {consultationData.admission_date &&
- formatDateTime(consultationData.admission_date)}
- {consultationData.discharge_date &&
- ` - ${formatDateTime(consultationData.discharge_date)}`}
-
-
- )}
-
-
-
-
- {/*consultationData.other_symptoms && (
-
-
- Other Symptoms:{" "}
-
- {consultationData.other_symptoms}
-
- )*/}
-
- {consultationData.icd11_principal_diagnosis && (
-
- d.id === consultationData.icd11_principal_diagnosis
- )!,
- ]}
- />
- )}
-
-
-
-
-
- {(consultationData.verified_by_object ||
- consultationData.deprecated_verified_by) && (
-
-
- Verified By:{" "}
-
- {consultationData.verified_by_object
- ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
- : consultationData.deprecated_verified_by}
-
-
- )}
-
-
- setOpenDischargeSummaryDialog(true)}>
-
- {t("discharge_summary")}
-
-
- setOpenDischargeDialog(true)}
- disabled={!!consultationData.discharge_date}
- >
-
- {t("discharge_from_care")}
-
-
-
-
-
-
- Created:
- {consultationData.created_date
- ? formatDateTime(consultationData.created_date)
- : "--:--"}{" "}
- |
-
- {consultationData.created_by && (
-
- {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
- {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
-
- )}
-
-
-
- Last Modified:
- {consultationData.modified_date
- ? formatDateTime(consultationData.modified_date)
- : "--:--"}{" "}
- |
-
- {consultationData.last_edited_by && (
-
- {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
- {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
-
- )}
-
-
-
-
-
-
-
-
-
-
-
-
- {tab === "UPDATES" && (
-
- {!consultationData.discharge_date &&
- hl7SocketUrl &&
- ventilatorSocketUrl && (
-
- )}
-
-
-
-
- {!consultationData.discharge_date &&
- ((hl7SocketUrl && !ventilatorSocketUrl) ||
- (!hl7SocketUrl && ventilatorSocketUrl)) && (
-
- {(hl7SocketUrl || ventilatorSocketUrl) && (
-
- {hl7SocketUrl && (
-
-
-
- )}
- {ventilatorSocketUrl && (
-
-
-
- )}
-
- )}
-
- )}
- {consultationData.discharge_date && (
-
-
-
- Discharge Information
-
-
-
- Reason {" - "}
-
- {DISCHARGE_REASONS.find(
- (d) =>
- d.id === consultationData.discharge_reason
- )?.text ?? "--"}
-
-
- {consultationData.discharge_reason === "REF" && (
-
- Referred Facility {" - "}
-
- {consultationData.referred_to_external ||
- consultationData.referred_to_object?.name ||
- "--"}
-
-
- )}
- {consultationData.discharge_reason === "REC" && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Advice {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
-
-
-
- )}
- {consultationData.discharge_reason === "EXP" && (
-
-
- Date of Death {" - "}
-
- {consultationData.death_datetime
- ? formatDateTime(
- consultationData.death_datetime
- )
- : "--:--"}
-
-
-
- Cause of death {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- Confirmed By {" - "}
-
- {consultationData.death_confirmed_doctor ??
- "--"}
-
-
-
- )}
- {["REF", "LAMA"].includes(
- consultationData.discharge_reason ?? ""
- ) && (
-
-
- Discharge Date {" - "}
-
- {consultationData.discharge_date
- ? formatDate(
- consultationData.discharge_date
- )
- : "--/--/----"}
-
-
-
- Notes {" - "}
-
- {consultationData.discharge_notes ?? "--"}
-
-
-
- )}
-
-
-
- )}
- {consultationData.symptoms_text && (
-
-
-
- Symptoms
-
-
-
- Last Daily Update
-
- {consultationData.last_daily_round
- ?.additional_symptoms && (
- <>
-
- {consultationData.last_daily_round?.additional_symptoms.map(
- (symptom: any, index: number) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.last_daily_round
- ?.other_symptoms && (
-
-
- Other Symptoms:
-
- {
- consultationData.last_daily_round
- ?.other_symptoms
- }
-
- )}
-
- from{" "}
- {formatDate(
- consultationData.last_daily_round.created_at
- )}
-
- >
- )}
-
-
- Consultation Update
-
-
- {consultationData.symptoms?.map(
- (symptom, index) => (
- choice.id === symptom
- )?.text ?? "Err. Unknown"
- }
- size="small"
- />
- )
- )}
-
- {consultationData.other_symptoms && (
-
-
- Other Symptoms:
-
- {consultationData.other_symptoms}
-
- )}
-
- from{" "}
- {consultationData.symptoms_onset_date
- ? formatDate(consultationData.symptoms_onset_date)
- : "--/--/----"}
-
-
-
-
- )}
-
- {consultationData.history_of_present_illness && (
-
-
-
- History of Present Illness
-
-
-
-
-
-
- )}
-
- {consultationData.examination_details && (
-
-
-
- Examination details and Clinical conditions:{" "}
-
-
-
-
-
-
- )}
- {consultationData.treatment_plan && (
-
-
-
- Treatment Summary
-
-
-
-
-
-
- )}
- {consultationData.consultation_notes && (
-
-
-
- General Instructions
-
-
-
-
-
-
- )}
-
- {(consultationData.operation ??
- consultationData.special_instruction) && (
-
-
-
- Notes
-
-
- {consultationData.operation && (
-
-
Operation
-
-
- )}
-
- {consultationData.special_instruction && (
-
-
Special Instruction
-
-
- )}
-
-
-
- )}
-
- {consultationData.procedure &&
- consultationData.procedure.length > 0 && (
-
-
-
-
-
-
- Procedure
- |
-
- Notes
- |
-
- Repetitive
- |
-
- Time / Frequency
- |
-
-
-
- {consultationData.procedure?.map(
- (procedure, index) => (
-
-
- {procedure.procedure}
- |
-
- {procedure.notes}
- |
-
- {procedure.repetitive ? "Yes" : "No"}
- |
-
- {procedure.repetitive
- ? procedure.frequency
- : formatDateTime(String(procedure.time))}
- |
-
- )
- )}
-
-
-
-
- )}
- {consultationData.intubation_start_date && (
-
-
-
- Date/Size/LL:{" "}
-
-
-
- Intubation Date{" - "}
-
- {formatDateTime(
- consultationData.intubation_start_date
- )}
-
-
-
- Extubation Date{" - "}
-
- {consultationData.intubation_end_date &&
- formatDateTime(
- consultationData.intubation_end_date
- )}
-
-
-
- ETT/TT (mmid){" - "}
-
- {consultationData.ett_tt}
-
-
-
- Cuff Pressure (mmhg){" - "}
-
- {consultationData.cuff_pressure}
-
-
-
-
-
- )}
-
- {consultationData.lines?.length > 0 && (
-
-
-
- Lines and Catheters
-
-
- {consultationData.lines?.map(
- (line: any, idx: number) => (
-
-
{line.type}
-
- Details:
-
- {line.other_type}
-
-
- Insertion Date:{" "}
-
- {formatDateTime(line.start_date)}
-
-
-
- Site/Level of Fixation:
-
- {line.site}
-
-
-
- )
- )}
-
-
-
- )}
-
-
-
-
- Body Details
-
-
-
- Gender {" - "}
-
- {patientData.gender ?? "-"}
-
-
-
- Age {" - "}
-
- {patientData.age ?? "-"}
-
-
-
- Weight {" - "}
-
- {consultationData.weight ?? "-"} Kg
-
-
-
- Height {" - "}
-
- {consultationData.height ?? "-"} cm
-
-
-
- Body Surface Area {" - "}
-
- {Math.sqrt(
- (Number(consultationData.weight) *
- Number(consultationData.height)) /
- 3600
- ).toFixed(2)}{" "}
- m2
-
-
-
- Blood Group {" - "}
-
- {patientData.blood_group ?? "-"}
-
-
-
-
-
-
-
-
-
-
- setShowAutomatedRounds((s) => !s)}
- />
-
-
-
-
-
-
-
- )}
- {tab === "FEED" && (
-
- )}
- {tab === "SUMMARY" && (
-
- )}
- {tab === "MEDICINES" && (
-
- )}
- {tab === "FILES" && (
-
-
-
- )}
-
- {tab === "ABG" && (
-
- )}
- {tab === "NURSING" && (
-
- )}
- {tab === "NEUROLOGICAL_MONITORING" && (
-
- )}
- {tab === "VENTILATOR" && (
-
- )}
- {tab === "NUTRITION" && (
-
- )}
- {tab === "PRESSURE_SORE" && (
-
- )}
- {tab === "DIALYSIS" && (
-
- )}
- {tab === "INVESTIGATIONS" && (
-
-
-
-
-
- navigate(
- `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/investigation/`
- )
- }
- >
-
- {t("log_lab_results")}
-
-
-
-
-
- )}
-
-
-
-
- );
-};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
new file mode 100644
index 00000000000..cf0ce7bd6af
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { ABGPlots } from "../Consultations/ABGPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationABGTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
new file mode 100644
index 00000000000..c3515a80953
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { DialysisPlots } from "../Consultations/DialysisPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationDialysisTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
new file mode 100644
index 00000000000..f4fbc08331c
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { Feed } from "../Consultations/Feed";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationFeedTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
new file mode 100644
index 00000000000..754d4bf7b86
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx
@@ -0,0 +1,18 @@
+import { ConsultationTabProps } from "./index";
+import { FileUpload } from "../../Patient/FileUpload";
+
+export const ConsultationFilesTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
new file mode 100644
index 00000000000..69b11a30ed5
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx
@@ -0,0 +1,39 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NonReadOnlyUsers } from "../../../Utils/AuthorizeFor";
+import ButtonV2 from "../../Common/components/ButtonV2";
+import { navigate } from "raviger";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import InvestigationTab from "../Investigations/investigationsTab";
+import { t } from "i18next";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+export const ConsultationInvestigationsTab = (props: ConsultationTabProps) => {
+ return (
+
+
+
+
+
+ navigate(
+ `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}/investigation/`
+ )
+ }
+ >
+
+ {t("log_lab_results")}
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
new file mode 100644
index 00000000000..19810102833
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx
@@ -0,0 +1,19 @@
+import { ConsultationTabProps } from "./index";
+import PrescriptionAdministrationsTable from "../../Medicine/PrescriptionAdministrationsTable";
+
+export const ConsultationMedicinesTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
new file mode 100644
index 00000000000..85f34f0ec63
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx
@@ -0,0 +1,24 @@
+import { lazy } from "react";
+import { NeurologicalTable } from "../Consultations/NeurologicalTables";
+import { ConsultationTabProps } from "./index";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeurologicalMonitoringTab = (
+ props: ConsultationTabProps
+) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
new file mode 100644
index 00000000000..69f130aca0d
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NutritionPlots } from "../Consultations/NutritionPlots";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNeutritionTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
new file mode 100644
index 00000000000..721ee18d13e
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
@@ -0,0 +1,18 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { NursingPlot } from "../Consultations/NursingPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationNursingTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
new file mode 100644
index 00000000000..05327ca6870
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx
@@ -0,0 +1,14 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PressureSoreDiagrams } from "../Consultations/PressureSoreDiagrams";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationPressureSoreTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
new file mode 100644
index 00000000000..69d20dd64c9
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { PrimaryParametersPlot } from "../Consultations/PrimaryParametersPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationSummaryTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
new file mode 100644
index 00000000000..705b3d9479a
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx
@@ -0,0 +1,690 @@
+import { lazy, useEffect, useState } from "react";
+import { ConsultationTabProps } from "./index";
+import { AssetBedModel, AssetClass, AssetData } from "../../Assets/AssetTypes";
+import { useDispatch } from "react-redux";
+import { getPermittedFacility, listAssetBeds } from "../../../Redux/actions";
+import { BedModel, FacilityModel } from "../models";
+import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor";
+import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor";
+import useVitalsAspectRatioConfig from "../../VitalsMonitor/useVitalsAspectRatioConfig";
+import { DISCHARGE_REASONS, SYMPTOM_CHOICES } from "../../../Common/constants";
+import PrescriptionsTable from "../../Medicine/PrescriptionsTable";
+import Chip from "../../../CAREUI/display/Chip";
+import { formatDate, formatDateTime } from "../../../Utils/utils";
+import ReadMore from "../../Common/components/Readmore";
+import { DailyRoundsList } from "../Consultations/DailyRoundsList";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
+ const dispatch: any = useDispatch();
+ const [showAutomatedRounds, setShowAutomatedRounds] = useState(true);
+ const [hl7SocketUrl, setHL7SocketUrl] = useState();
+ const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState();
+ const [monitorBedData, setMonitorBedData] = useState();
+ const [ventilatorBedData, setVentilatorBedData] = useState();
+
+ const vitals = useVitalsAspectRatioConfig({
+ default: undefined,
+ md: 8 / 11,
+ lg: 15 / 11,
+ xl: 13 / 11,
+ "2xl": 19 / 11,
+ "3xl": 23 / 11,
+ });
+
+ useEffect(() => {
+ if (
+ !props.consultationData.facility ||
+ !props.consultationData.current_bed?.bed_object.id
+ )
+ return;
+
+ const fetchData = async () => {
+ const [facilityRes, assetBedRes] = await Promise.all([
+ dispatch(getPermittedFacility(props.consultationData.facility as any)),
+ dispatch(
+ listAssetBeds({
+ facility: props.consultationData.facility as any,
+ bed: props.consultationData.current_bed?.bed_object.id,
+ })
+ ),
+ ]);
+
+ const { middleware_address } = facilityRes.data as FacilityModel;
+ const assetBeds = assetBedRes?.data?.results as AssetBedModel[];
+
+ const monitorBedData = assetBeds?.find(
+ (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR
+ );
+ setMonitorBedData(monitorBedData);
+ const assetDataForMonitor = monitorBedData?.asset_object;
+ const hl7Meta = assetDataForMonitor?.meta;
+ const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address;
+ if (hl7Middleware && hl7Meta?.local_ip_address) {
+ setHL7SocketUrl(
+ `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}`
+ );
+ }
+
+ const consultationBedVentilator =
+ props.consultationData?.current_bed?.assets_objects?.find(
+ (i) => i.asset_class === AssetClass.VENTILATOR
+ );
+ let ventilatorBedData;
+ if (consultationBedVentilator) {
+ ventilatorBedData = {
+ asset_object: consultationBedVentilator,
+ bed_object: props.consultationData?.current_bed?.bed_object,
+ } as AssetBedModel;
+ } else {
+ ventilatorBedData = assetBeds?.find(
+ (i) => i.asset_object.asset_class === AssetClass.VENTILATOR
+ );
+ }
+ setVentilatorBedData(ventilatorBedData);
+ const ventilatorMeta = ventilatorBedData?.asset_object?.meta;
+ const ventilatorMiddleware =
+ ventilatorMeta?.middleware_hostname || middleware_address;
+ if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) {
+ setVentilatorSocketUrl(
+ `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}`
+ );
+ }
+
+ if (
+ !(hl7Middleware && hl7Meta?.local_ip_address) &&
+ !(ventilatorMiddleware && ventilatorMeta?.local_ip_address)
+ ) {
+ setHL7SocketUrl(undefined);
+ setVentilatorSocketUrl(undefined);
+ }
+ };
+
+ fetchData();
+ }, [props.consultationData]);
+
+ return (
+
+ {!props.consultationData.discharge_date &&
+ hl7SocketUrl &&
+ ventilatorSocketUrl && (
+
+ )}
+
+
+
+
+ {!props.consultationData.discharge_date &&
+ ((hl7SocketUrl && !ventilatorSocketUrl) ||
+ (!hl7SocketUrl && ventilatorSocketUrl)) && (
+
+ {(hl7SocketUrl || ventilatorSocketUrl) && (
+
+ {hl7SocketUrl && (
+
+
+
+ )}
+ {ventilatorSocketUrl && (
+
+
+
+ )}
+
+ )}
+
+ )}
+ {props.consultationData.discharge_date && (
+
+
+
+ Discharge Information
+
+
+
+ Reason {" - "}
+
+ {DISCHARGE_REASONS.find(
+ (d) =>
+ d.id === props.consultationData.discharge_reason
+ )?.text ?? "--"}
+
+
+ {props.consultationData.discharge_reason === "REF" && (
+
+ Referred Facility {" - "}
+
+ {props.consultationData.referred_to_external ||
+ props.consultationData.referred_to_object?.name ||
+ "--"}
+
+
+ )}
+ {props.consultationData.discharge_reason === "REC" && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Advice {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+
+
+
+ )}
+ {props.consultationData.discharge_reason === "EXP" && (
+
+
+ Date of Death {" - "}
+
+ {props.consultationData.death_datetime
+ ? formatDateTime(
+ props.consultationData.death_datetime
+ )
+ : "--:--"}
+
+
+
+ Cause of death {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ Confirmed By {" - "}
+
+ {props.consultationData.death_confirmed_doctor ??
+ "--"}
+
+
+
+ )}
+ {["REF", "LAMA"].includes(
+ props.consultationData.discharge_reason ?? ""
+ ) && (
+
+
+ Discharge Date {" - "}
+
+ {props.consultationData.discharge_date
+ ? formatDate(
+ props.consultationData.discharge_date
+ )
+ : "--/--/----"}
+
+
+
+ Notes {" - "}
+
+ {props.consultationData.discharge_notes ?? "--"}
+
+
+
+ )}
+
+
+
+ )}
+ {props.consultationData.symptoms_text && (
+
+
+
+ Symptoms
+
+
+
+ Last Daily Update
+
+ {props.consultationData.last_daily_round
+ ?.additional_symptoms && (
+ <>
+
+ {props.consultationData.last_daily_round?.additional_symptoms.map(
+ (symptom: any, index: number) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.last_daily_round
+ ?.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {
+ props.consultationData.last_daily_round
+ ?.other_symptoms
+ }
+
+ )}
+
+ from{" "}
+ {formatDate(
+ props.consultationData.last_daily_round.created_at
+ )}
+
+ >
+ )}
+
+
+ Consultation Update
+
+
+ {props.consultationData.symptoms?.map(
+ (symptom, index) => (
+ choice.id === symptom
+ )?.text ?? "Err. Unknown"
+ }
+ size="small"
+ />
+ )
+ )}
+
+ {props.consultationData.other_symptoms && (
+
+
+ Other Symptoms:
+
+ {props.consultationData.other_symptoms}
+
+ )}
+
+ from{" "}
+ {props.consultationData.symptoms_onset_date
+ ? formatDate(props.consultationData.symptoms_onset_date)
+ : "--/--/----"}
+
+
+
+
+ )}
+
+ {props.consultationData.history_of_present_illness && (
+
+
+
+ History of Present Illness
+
+
+
+
+
+
+ )}
+
+ {props.consultationData.examination_details && (
+
+
+
+ Examination details and Clinical conditions:{" "}
+
+
+
+
+
+
+ )}
+ {props.consultationData.treatment_plan && (
+
+
+
+ Treatment Summary
+
+
+
+
+
+
+ )}
+ {props.consultationData.consultation_notes && (
+
+
+
+ General Instructions
+
+
+
+
+
+
+ )}
+
+ {(props.consultationData.operation ??
+ props.consultationData.special_instruction) && (
+
+
+
+ Notes
+
+
+ {props.consultationData.operation && (
+
+
Operation
+
+
+ )}
+
+ {props.consultationData.special_instruction && (
+
+
Special Instruction
+
+
+ )}
+
+
+
+ )}
+
+ {props.consultationData.procedure &&
+ props.consultationData.procedure.length > 0 && (
+
+
+
+
+
+
+ Procedure
+ |
+
+ Notes
+ |
+
+ Repetitive
+ |
+
+ Time / Frequency
+ |
+
+
+
+ {props.consultationData.procedure?.map(
+ (procedure, index) => (
+
+
+ {procedure.procedure}
+ |
+
+ {procedure.notes}
+ |
+
+ {procedure.repetitive ? "Yes" : "No"}
+ |
+
+ {procedure.repetitive
+ ? procedure.frequency
+ : formatDateTime(String(procedure.time))}
+ |
+
+ )
+ )}
+
+
+
+
+ )}
+ {props.consultationData.intubation_start_date && (
+
+
+
+ Date/Size/LL:{" "}
+
+
+
+ Intubation Date{" - "}
+
+ {formatDateTime(
+ props.consultationData.intubation_start_date
+ )}
+
+
+
+ Extubation Date{" - "}
+
+ {props.consultationData.intubation_end_date &&
+ formatDateTime(
+ props.consultationData.intubation_end_date
+ )}
+
+
+
+ ETT/TT (mmid){" - "}
+
+ {props.consultationData.ett_tt}
+
+
+
+ Cuff Pressure (mmhg){" - "}
+
+ {props.consultationData.cuff_pressure}
+
+
+
+
+
+ )}
+
+ {props.consultationData.lines?.length > 0 && (
+
+
+
+ Lines and Catheters
+
+
+ {props.consultationData.lines?.map(
+ (line: any, idx: number) => (
+
+
{line.type}
+
+ Details:
+
+ {line.other_type}
+
+
+ Insertion Date:{" "}
+
+ {formatDateTime(line.start_date)}
+
+
+
+ Site/Level of Fixation:
+
+ {line.site}
+
+
+
+ )
+ )}
+
+
+
+ )}
+
+
+
+
+ Body Details
+
+
+
+ Gender {" - "}
+
+ {props.patientData.gender ?? "-"}
+
+
+
+ Age {" - "}
+
+ {props.patientData.age ?? "-"}
+
+
+
+ Weight {" - "}
+
+ {props.consultationData.weight ?? "-"} Kg
+
+
+
+ Height {" - "}
+
+ {props.consultationData.height ?? "-"} cm
+
+
+
+ Body Surface Area {" - "}
+
+ {Math.sqrt(
+ (Number(props.consultationData.weight) *
+ Number(props.consultationData.height)) /
+ 3600
+ ).toFixed(2)}{" "}
+ m2
+
+
+
+ Blood Group {" - "}
+
+ {props.patientData.blood_group ?? "-"}
+
+
+
+
+
+
+
+
+
+
+ setShowAutomatedRounds((s) => !s)}
+ />
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
new file mode 100644
index 00000000000..d14b54cc096
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
@@ -0,0 +1,22 @@
+import { lazy } from "react";
+import { ConsultationTabProps } from "./index";
+import { VentilatorPlot } from "../Consultations/VentilatorPlot";
+
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+
+export const ConsultationVentilatorTab = (props: ConsultationTabProps) => {
+ return (
+
+ );
+};
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx
new file mode 100644
index 00000000000..affdb2756cd
--- /dev/null
+++ b/src/Components/Facility/ConsultationDetails/index.tsx
@@ -0,0 +1,501 @@
+import {
+ CONSULTATION_TABS,
+ GENDER_TYPES,
+ OptionsType,
+ SYMPTOM_CHOICES,
+} from "../../../Common/constants";
+import { ConsultationModel, ICD11DiagnosisModel } from "../models";
+import { getConsultation, getPatient } from "../../../Redux/actions";
+import { statusType, useAbortableEffect } from "../../../Common/utils";
+import { lazy, useCallback, useState } from "react";
+
+import ButtonV2 from "../../Common/components/ButtonV2";
+import CareIcon from "../../../CAREUI/icons/CareIcon";
+import DischargeModal from "../DischargeModal";
+import DischargeSummaryModal from "../DischargeSummaryModal";
+import DoctorVideoSlideover from "../DoctorVideoSlideover";
+import { make as Link } from "../../Common/components/Link.bs";
+import PatientInfoCard from "../../Patient/PatientInfoCard";
+import { PatientModel } from "../../Patient/models";
+import { formatDateTime, relativeTime } from "../../../Utils/utils";
+
+import { navigate } from "raviger";
+import { useDispatch } from "react-redux";
+import { useQueryParams } from "raviger";
+import { useTranslation } from "react-i18next";
+import { triggerGoal } from "../../Common/Plausible";
+import useAuthUser from "../../../Common/hooks/useAuthUser";
+import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab";
+import { ConsultationABGTab } from "./ConsultationABGTab";
+import { ConsultationNursingTab } from "./ConsultationNursingTab";
+import { ConsultationFeedTab } from "./ConsultationFeedTab";
+import { ConsultationSummaryTab } from "./ConsultationSummaryTab";
+import { ConsultationFilesTab } from "./ConsultationFilesTab";
+import { ConsultationMedicinesTab } from "./ConsultationMedicinesTab";
+import { ConsultationInvestigationsTab } from "./ConsultationInvestigationsTab";
+import { ConsultationVentilatorTab } from "./ConsultationVentilatorTab";
+import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab";
+import { ConsultationDialysisTab } from "./ConsultationDialysisTab";
+import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab";
+
+const Loading = lazy(() => import("../../Common/Loading"));
+const PageTitle = lazy(() => import("../../Common/PageTitle"));
+const symptomChoices = [...SYMPTOM_CHOICES];
+
+export interface ConsultationTabProps {
+ consultationId: string;
+ facilityId: string;
+ patientId: string;
+ consultationData: ConsultationModel;
+ patientData: PatientModel;
+}
+
+export const ConsultationDetails = (props: any) => {
+ const { t } = useTranslation();
+ const { facilityId, patientId, consultationId } = props;
+ const tab = props.tab.toUpperCase();
+ const dispatch: any = useDispatch();
+ const [isLoading, setIsLoading] = useState(false);
+ const [showDoctors, setShowDoctors] = useState(false);
+ const [qParams, _] = useQueryParams();
+
+ const [consultationData, setConsultationData] = useState(
+ {} as ConsultationModel
+ );
+ const [patientData, setPatientData] = useState({});
+ const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] =
+ useState(false);
+ const [openDischargeDialog, setOpenDischargeDialog] = useState(false);
+
+ const getPatientGender = (patientData: any) =>
+ GENDER_TYPES.find((i) => i.id === patientData.gender)?.text;
+
+ const getPatientAddress = (patientData: any) =>
+ `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`;
+
+ const getPatientComorbidities = (patientData: any) => {
+ if (patientData?.medical_history?.length) {
+ const medHis = patientData.medical_history;
+ return medHis.map((item: any) => item.disease).join(", ");
+ } else {
+ return "None";
+ }
+ };
+
+ const authUser = useAuthUser();
+
+ const fetchData = useCallback(
+ async (status: statusType) => {
+ setIsLoading(true);
+ const res = await dispatch(getConsultation(consultationId));
+ if (!status.aborted) {
+ if (res?.data) {
+ const data: ConsultationModel = {
+ ...res.data,
+ symptoms_text: "",
+ };
+ if (res.data.symptoms?.length) {
+ const symptoms = res.data.symptoms
+ .filter((symptom: number) => symptom !== 9)
+ .map((symptom: number) => {
+ const option = symptomChoices.find((i) => i.id === symptom);
+ return option ? option.text.toLowerCase() : symptom;
+ });
+ data.symptoms_text = symptoms.join(", ");
+ }
+ setConsultationData(data);
+ const id = res.data.patient;
+ const patientRes = await dispatch(getPatient({ id }));
+ if (patientRes?.data) {
+ const patientGender = getPatientGender(patientRes.data);
+ const patientAddress = getPatientAddress(patientRes.data);
+ const patientComorbidities = getPatientComorbidities(
+ patientRes.data
+ );
+ const data = {
+ ...patientRes.data,
+ gender: patientGender,
+ address: patientAddress,
+ comorbidities: patientComorbidities,
+ is_declared_positive: patientRes.data.is_declared_positive
+ ? "Yes"
+ : "No",
+ is_vaccinated: patientData.is_vaccinated ? "Yes" : "No",
+ };
+ setPatientData(data);
+ }
+ } else {
+ navigate("/not-found");
+ }
+ setIsLoading(false);
+ }
+ },
+ [consultationId, dispatch, patientData.is_vaccinated]
+ );
+
+ useAbortableEffect((status: statusType) => {
+ fetchData(status);
+ triggerGoal("Patient Consultation Viewed", {
+ facilityId: facilityId,
+ consultationId: consultationId,
+ userId: authUser.id,
+ });
+ }, []);
+
+ const TABS = {
+ UPDATES: ConsultationUpdatesTab,
+ FEED: ConsultationFeedTab,
+ SUMMARY: ConsultationSummaryTab,
+ MEDICINES: ConsultationMedicinesTab,
+ FILES: ConsultationFilesTab,
+ INVESTIGATIONS: ConsultationInvestigationsTab,
+ ABG: ConsultationABGTab,
+ NURSING: ConsultationNursingTab,
+ NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab,
+ VENTILATOR: ConsultationVentilatorTab,
+ NUTRITION: ConsultationNursingTab,
+ PRESSURE_SORE: ConsultationPressureSoreTab,
+ DIALYSIS: ConsultationDialysisTab,
+ };
+
+ const consultationTabProps: ConsultationTabProps = {
+ consultationId,
+ facilityId,
+ patientId,
+ consultationData,
+ patientData,
+ };
+
+ const SelectedTab = TABS[tab];
+
+ if (isLoading) {
+ return ;
+ }
+
+ const tabButtonClasses = (selected: boolean) =>
+ `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${
+ selected === true ? "border-primary-500 text-primary-600 border-b-2" : ""
+ }`;
+
+ const ShowDiagnosis = ({
+ diagnoses = [],
+ label = "Diagnosis",
+ nshow = 2,
+ }: {
+ diagnoses: ICD11DiagnosisModel[] | undefined;
+ label: string;
+ nshow?: number;
+ }) => {
+ const [showMore, setShowMore] = useState(false);
+
+ return diagnoses.length ? (
+
+ ) : null;
+ };
+
+ return (
+
+
setOpenDischargeSummaryDialog(false)}
+ />
+
+ setOpenDischargeDialog(false)}
+ consultationData={consultationData}
+ />
+
+
+
+
+
+
+
+
+ {consultationData.admitted_to && (
+
+
+ Patient
+ {consultationData.discharge_date
+ ? " Discharged from"
+ : " Admitted to"}
+
+ {consultationData.admitted_to}
+
+
+ {(consultationData.admission_date ??
+ consultationData.discharge_date) && (
+
+ {relativeTime(
+ consultationData.discharge_date
+ ? consultationData.discharge_date
+ : consultationData.admission_date
+ )}
+
+ )}
+
+ {consultationData.admission_date &&
+ formatDateTime(consultationData.admission_date)}
+ {consultationData.discharge_date &&
+ ` - ${formatDateTime(consultationData.discharge_date)}`}
+
+
+ )}
+
+
+
+
+ {/*consultationData.other_symptoms && (
+
+
+ Other Symptoms:{" "}
+
+ {consultationData.other_symptoms}
+
+ )*/}
+
+ {consultationData.icd11_principal_diagnosis && (
+
+ d.id === consultationData.icd11_principal_diagnosis
+ )!,
+ ]}
+ />
+ )}
+
+
+
+
+
+ {(consultationData.verified_by_object ||
+ consultationData.deprecated_verified_by) && (
+
+
+ Verified By:{" "}
+
+ {consultationData.verified_by_object
+ ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}`
+ : consultationData.deprecated_verified_by}
+
+
+ )}
+
+
+ setOpenDischargeSummaryDialog(true)}>
+
+ {t("discharge_summary")}
+
+
+ setOpenDischargeDialog(true)}
+ disabled={!!consultationData.discharge_date}
+ >
+
+ {t("discharge_from_care")}
+
+
+
+
+
+
+ Created:
+ {consultationData.created_date
+ ? formatDateTime(consultationData.created_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.created_by && (
+
+ {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `}
+ {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`}
+
+ )}
+
+
+
+ Last Modified:
+ {consultationData.modified_date
+ ? formatDateTime(consultationData.modified_date)
+ : "--:--"}{" "}
+ |
+
+ {consultationData.last_edited_by && (
+
+ {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `}
+ {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx
index d2e0ca415e3..8625d442f52 100644
--- a/src/Components/Users/ManageUsers.tsx
+++ b/src/Components/Users/ManageUsers.tsx
@@ -73,6 +73,8 @@ export default function ManageUsers() {
name: string;
}>({ show: false, username: "", name: "" });
+ const [weeklyHoursError, setWeeklyHoursError] = useState("");
+
const extremeSmallScreenBreakpoint = 320;
const isExtremeSmallScreen =
width <= extremeSmallScreenBreakpoint ? true : false;
@@ -144,7 +146,10 @@ export default function ManageUsers() {
const handleWorkingHourSubmit = async () => {
const username = selectedUser;
- if (!username || weeklyHours < 0 || weeklyHours > 168) return;
+ if (!username || !weeklyHours || weeklyHours < 0 || weeklyHours > 168) {
+ setWeeklyHoursError("Value should be between 0 and 168");
+ return;
+ }
const res = await dispatch(
partialUpdateUser(username, {
weekly_working_hours: weeklyHours,
@@ -163,6 +168,7 @@ export default function ManageUsers() {
});
}
setWeeklyHours(0);
+ setWeeklyHoursError("");
fetchData({ aborted: false });
};
@@ -493,13 +499,14 @@ export default function ManageUsers() {
{
+ setExpandWorkingHours(state);
+ setWeeklyHours(0);
+ setWeeklyHoursError("");
+ }}
slideFrom="right"
title="Average weekly working hours"
dialogClass="md:w-[400px]"
- onCloseClick={() => {
- setWeeklyHours(0);
- }}
>
@@ -512,11 +519,7 @@ export default function ManageUsers() {
onChange={(e) => {
setWeeklyHours(e.value);
}}
- error={
- weeklyHours < 0 || weeklyHours > 168
- ? "Average weekly working hours should be between 0 and 168"
- : ""
- }
+ error={weeklyHoursError}
required
label=""
type="number"
diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx
index 393b9372ca5..01a91d4ce3a 100644
--- a/src/Redux/api.tsx
+++ b/src/Redux/api.tsx
@@ -10,7 +10,7 @@ interface Routes {
const routes: Routes = {
config: {
- path: import.meta.env.REACT_APP_CONFIG || "/config.json",
+ path: import.meta.env.REACT_APP_CONFIG ?? "/config.json",
method: "GET",
noAuth: true,
},
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 529c2689aed..c899d453626 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -5,7 +5,7 @@ interface ImportMetaEnv {
readonly REACT_APP_META_DESCRIPTION: string;
readonly REACT_APP_COVER_IMAGE: string;
readonly REACT_APP_COVER_IMAGE_ALT: string;
- readonly REACT_APP_CONFIG: string;
+ readonly REACT_APP_CONFIG: string | undefined;
readonly REACT_PUBLIC_URL: string;
readonly REACT_APP_SITE_URL: string;
readonly REACT_APP_ANALYTICS_SERVER_URL: string;