diff --git a/public/locale/en.json b/public/locale/en.json
index b6ccb84de08..ce1d79aa3dd 100644
--- a/public/locale/en.json
+++ b/public/locale/en.json
@@ -133,9 +133,13 @@
"ORAL_ISSUE__NO_ISSUE": "No issues",
"ORAL_ISSUE__ODYNOPHAGIA": "Odynophagia",
"OXYGEN_MODALITY__HIGH_FLOW_NASAL_CANNULA": "High Flow Nasal Cannula",
+ "OXYGEN_MODALITY__HIGH_FLOW_NASAL_CANNULA_short": "HFNC",
"OXYGEN_MODALITY__NASAL_PRONGS": "Nasal Prongs",
+ "OXYGEN_MODALITY__NASAL_PRONGS_short": "NP",
"OXYGEN_MODALITY__NON_REBREATHING_MASK": "Non Rebreathing Mask",
+ "OXYGEN_MODALITY__NON_REBREATHING_MASK_short": "NRM",
"OXYGEN_MODALITY__SIMPLE_FACE_MASK": "Simple Face Mask",
+ "OXYGEN_MODALITY__SIMPLE_FACE_MASK_short": "SFM",
"PRESCRIPTION_FREQUENCY_BD": "Twice daily",
"PRESCRIPTION_FREQUENCY_HS": "Night only",
"PRESCRIPTION_FREQUENCY_OD": "Once daily",
@@ -206,12 +210,19 @@
"URINATION_FREQUENCY__NORMAL": "Normal",
"VENTILATOR": "Detailed Update",
"VENTILATOR_MODE__CMV": "Control Mechanical Ventilation (CMV)",
+ "VENTILATOR_MODE__CMV_short": "CMV",
"VENTILATOR_MODE__PCV": "Pressure Control Ventilation (PCV)",
+ "VENTILATOR_MODE__PCV_short": "PCV",
"VENTILATOR_MODE__PC_SIMV": "Pressure Controlled SIMV (PC-SIMV)",
+ "VENTILATOR_MODE__PC_SIMV_short": "PC-SIMV",
"VENTILATOR_MODE__PSV": "C-PAP / Pressure Support Ventilation (PSV)",
+ "VENTILATOR_MODE__PSV_short": "C-PAP/PSV",
"VENTILATOR_MODE__SIMV": "Synchronised Intermittent Mandatory Ventilation (SIMV)",
+ "VENTILATOR_MODE__SIMV_short": "SIMV",
"VENTILATOR_MODE__VCV": "Volume Control Ventilation (VCV)",
+ "VENTILATOR_MODE__VCV_short": "VCV",
"VENTILATOR_MODE__VC_SIMV": "Volume Controlled SIMV (VC-SIMV)",
+ "VENTILATOR_MODE__VC_SIMV_short": "VC-SIMV",
"View Facility": "View Facility",
"aadhaar_number": "Aadhaar Number",
"aadhaar_number_will_not_be_stored": "Aadhaar number will not be stored by CARE",
@@ -303,6 +314,7 @@
"are_you_still_watching": "Are you still watching?",
"are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?",
"are_you_sure_want_to_delete_this_record": "Are you sure want to delete this record?",
+ "ari": "ARI - Acute Respiratory illness",
"asset_class": "Asset Class",
"asset_location": "Asset Location",
"asset_name": "Asset Name",
@@ -313,6 +325,7 @@
"assigned_facility": "Facility assigned",
"assigned_to": "Assigned to",
"async_operation_warning": "This operation may take some time. Please check back later.",
+ "atypical_presentation_details": "Atypical presentation details",
"audio__allow_permission": "Please allow microphone permission in site settings",
"audio__allow_permission_button": "Click here to know how to allow",
"audio__allow_permission_helper": "You might have denied microphone access in the past.",
@@ -479,6 +492,8 @@
"contact_person_at_the_facility": "Contact person at the current facility",
"contact_person_number": "Contact person number",
"contact_phone": "Contact Person Number",
+ "contact_with_confirmed_carrier": "Contact with confirmed carrier",
+ "contact_with_suspected_carrier": "Contact with suspected carrier",
"contact_your_admin_to_add_skills": "Contact your admin to add skills",
"continue": "Continue",
"continue_watching": "Continue watching",
@@ -543,6 +558,7 @@
"diagnosis_at_discharge": "Diagnosis at Discharge",
"diastolic": "Diastolic",
"differential": "Differential",
+ "differential_diagnosis": "Differential diagnosis",
"discard": "Discard",
"discharge": "Discharge",
"discharge_from_care": "Discharge from CARE",
@@ -560,7 +576,9 @@
"district": "District",
"district_program_management_supporting_unit": "District Program Management Supporting Unit",
"doctor_s_medical_council_registration": "Doctor's Medical Council Registration",
+ "doctors_name": "Doctor's Name",
"domestic_healthcare_support": "Domestic healthcare support",
+ "domestic_international_travel": "Domestic/international Travel (within last 28 days)",
"done": "Done",
"dosage": "Dosage",
"down": "Down",
@@ -611,6 +629,7 @@
"encounter_suggestion__OP": "Out-patient visit",
"encounter_suggestion__R": "Consultation",
"encounter_suggestion_edit_disallowed": "Not allowed to switch to this option in edit consultation",
+ "end_datetime": "End Date/Time",
"enter_aadhaar_number": "Enter a 12-digit Aadhaar ID",
"enter_aadhaar_otp": "Enter OTP sent to the registered mobile with Aadhaar",
"enter_abha_address": "Enter ABHA Address",
@@ -627,6 +646,7 @@
"error_while_deleting_record": "Error while deleting record",
"escape": "Escape",
"estimated_contact_date": "Estimated contact date",
+ "etiology_identified": "Etiology identified",
"events": "Events",
"expand_sidebar": "Expand Sidebar",
"expected_burn_rate": "Expected Burn Rate",
@@ -640,6 +660,7 @@
"facility_search_placeholder": "Search by Facility / District Name",
"facility_type": "Facility Type",
"failed_to_link_abha_number": "Failed to link ABHA Number. Please try again later.",
+ "fast_track_testing_reason": "Fast track testing reason",
"features": "Features",
"feed_configurations": "Feed Configurations",
"feed_is_currently_not_live": "Feed is currently not live",
@@ -685,6 +706,7 @@
"goal": "Our goal is to continuously improve the quality and accessibility of public healthcare services using digital tools.",
"granted_on": "Granted On",
"has_domestic_healthcare_support": "Has domestic healthcare support?",
+ "has_sari": "Has SARI (Severe Acute Respiratory illness)?",
"health_facility__config_registration_error": "Health ID registration failed",
"health_facility__config_update_error": "Health Facility config update failed",
"health_facility__config_update_success": "Health Facility config updated successfully",
@@ -718,6 +740,7 @@
"hubs": "Hub Facilities",
"i_declare": "I hereby declare that:",
"icd11_as_recommended": "As per ICD-11 recommended by WHO",
+ "icmr_specimen_referral_form": "ICMR Specimen Referral Form",
"incomplete_patient_details_warning": "Patient details are incomplete. Please update the details before proceeding.",
"inconsistent_dosage_units_error": "Dosage units must be same",
"indian_mobile": "Indian Mobile",
@@ -752,15 +775,18 @@
"investigations_suggested": "Investigations Suggested",
"is": "Is",
"is_antenatal": "Is Antenatal",
+ "is_atypical_presentation": "Is Atypical presentation",
"is_declared_positive": "Whether declared positive",
"is_emergency": "Is emergency",
"is_emergency_case": "Is emergency case",
"is_it_upshift": "is it upshift",
"is_this_an_emergency": "Is this an emergency?",
"is_this_an_upshift": "Is this an upshift?",
+ "is_unusual_course": "Is unusual course",
"is_up_shift": "Is up shift",
"is_upshift_case": "Is upshift case",
"is_vaccinated": "Whether vaccinated",
+ "label": "Label",
"landline": "Indian landline",
"language_selection": "Language Selection",
"last_administered": "Last administered",
@@ -890,7 +916,7 @@
"notice_board": "Notice Board",
"notification_permission_denied": "Notification permission denied",
"notification_permission_granted": "Notification permission granted",
- "number_of_aged_dependents_above_60": "Number Of Aged Dependents (Above 60)",
+ "number_of_aged_dependents": "Number of Aged Dependents (Above 60)",
"number_of_beds": "Number of beds",
"number_of_beds_out_of_range_error": "Number of beds cannot be greater than 100",
"number_of_chronic_diseased_dependents": "Number Of Chronic Diseased Dependents",
@@ -924,6 +950,7 @@
"password_reset_failure": "Password Reset Failed",
"password_reset_success": "Password Reset successfully",
"password_sent": "Password Reset Email Sent",
+ "patient": "Patient",
"patient_address": "Patient Address",
"patient_body": "Patient Body",
"patient_category": "Patient Category",
@@ -1065,6 +1092,7 @@
"result": "Result",
"result_date": "Result Date",
"result_details": "Result details",
+ "result_on": "Result on",
"resume": "Resume",
"retake": "Retake",
"return_to_care": "Return to CARE",
@@ -1078,7 +1106,11 @@
"sample_collection_date": "Sample Collection Date",
"sample_format": "Sample Format",
"sample_test": "Sample Test",
+ "sample_test_details": "Sample Test Details",
+ "sample_test_history": "Sample Test History",
"sample_type": "Sample Type",
+ "sample_type_description": "Sample Type Description",
+ "sari": "SARI - Severe Acute Respiratory illness",
"save": "Save",
"save_and_continue": "Save and Continue",
"save_investigation": "Save Investigation",
@@ -1145,6 +1177,7 @@
"spokes": "Spoke Facilities",
"srf_id": "SRF ID",
"staff_list": "Staff List",
+ "start_datetime": "Start Date/Time",
"start_dosage": "Start Dosage",
"state": "State",
"status": "Status",
@@ -1167,6 +1200,7 @@
"tachycardia": "Tachycardia",
"target_dosage": "Target Dosage",
"test_type": "Type of test done",
+ "tested_on": "Tested on",
"third_party_software_licenses": "Third Party Software Licenses",
"titrate_dosage": "Titrate Dosage",
"to_be_conducted": "To be conducted",
@@ -1236,6 +1270,13 @@
"vacant": "Vacant",
"vehicle_preference": "Vehicle preference",
"vendor_name": "Vendor Name",
+ "ventilator_interface": "Respiratory Support Type",
+ "ventilator_log": "Ventilator Log",
+ "ventilator_modality": "Modality",
+ "ventilator_mode": "Ventilator Mode",
+ "ventilator_oxygen_modality": "Oxygen Modality",
+ "ventilator_oxygen_modality_oxygen_rate": "Oxygen Flow Rate",
+ "ventilator_spo2": "SpO₂",
"verify_and_link": "Verify and Link",
"verify_otp": "Verify OTP",
"verify_otp_error": "Failed to verify OTP. Please try again later.",
diff --git a/src/components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx b/src/components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
index 1ded0ba7684..b26ea6e0e53 100644
--- a/src/components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
+++ b/src/components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx
@@ -1,8 +1,30 @@
+import Loading from "@/components/Common/Loading";
import PageTitle from "@/components/Common/PageTitle";
import { ConsultationTabProps } from "@/components/Facility/ConsultationDetails/index";
import { VentilatorPlot } from "@/components/Facility/Consultations/VentilatorPlot";
+import VentilatorTable from "@/components/Facility/Consultations/VentilatorTable";
+
+import useFilters from "@/hooks/useFilters";
+
+import routes from "@/Utils/request/api";
+import useQuery from "@/Utils/request/useQuery";
export const ConsultationVentilatorTab = (props: ConsultationTabProps) => {
+ const { consultationId } = props;
+ const { qParams, Pagination, resultsPerPage } = useFilters({ limit: 36 });
+
+ const { loading: isLoading, data } = useQuery(routes.getDailyReports, {
+ pathParams: { consultationId },
+ query: {
+ limit: resultsPerPage,
+ offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage,
+ },
+ });
+
+ if (isLoading) {
+ return ;
+ }
+
return (
{
hideBack={true}
breadcrumbs={false}
/>
-
+
+
+ {Boolean(data?.count && data.count > 0) && (
+
+ )}
);
};
diff --git a/src/components/Facility/ConsultationDetails/Events/EventsList.tsx b/src/components/Facility/ConsultationDetails/Events/EventsList.tsx
index e3f7072391e..47c68636a1a 100644
--- a/src/components/Facility/ConsultationDetails/Events/EventsList.tsx
+++ b/src/components/Facility/ConsultationDetails/Events/EventsList.tsx
@@ -68,6 +68,12 @@ export default function EventsList({ query }: { query: QueryParams }) {
}
const values = Object.fromEntries(entries);
+ if (
+ values.ventilator_interface === "INVASIVE" ||
+ values.ventilator_interface === "NON_INVASIVE"
+ ) {
+ values.ventilator_interface += " VENTILATOR";
+ }
switch (item.event_type.name) {
case "INTERNAL_TRANSFER":
diff --git a/src/components/Facility/Consultations/VentilatorPlot.tsx b/src/components/Facility/Consultations/VentilatorPlot.tsx
index 6991fd02b8d..38948f165ea 100644
--- a/src/components/Facility/Consultations/VentilatorPlot.tsx
+++ b/src/components/Facility/Consultations/VentilatorPlot.tsx
@@ -1,14 +1,11 @@
import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
-import Pagination from "@/components/Common/Pagination";
+import Loading from "@/components/Common/Loading";
import BinaryChronologicalChart from "@/components/Facility/Consultations/components/BinaryChronologicalChart";
import { LinePlot } from "@/components/Facility/Consultations/components/LinePlot";
-import { VentilatorPlotFields } from "@/components/Facility/models";
+import { DailyRoundsModel } from "@/components/Patient/models";
-import { PAGINATION_LIMIT } from "@/common/constants";
-
-import routes from "@/Utils/request/api";
-import request from "@/Utils/request/request";
import { formatDateTime } from "@/Utils/utils";
/*
@@ -31,48 +28,222 @@ const modality: Array = [
];
*/
-export const VentilatorPlot = (props: any) => {
- const { consultationId } = props;
- const [results, setResults] = useState({});
- const [currentPage, setCurrentPage] = useState(1);
- const [totalCount, setTotalCount] = useState(0);
+interface graphDataProps {
+ [key: string]: {
+ bilateral_air_entry?: boolean;
+ etco2?: number;
+ id?: string;
+ ventilator_fio2?: number;
+ ventilator_mean_airway_pressure?: number;
+ ventilator_oxygen_modality_flow_rate?: number;
+ ventilator_oxygen_modality_oxygen_rate?: number;
+ ventilator_peep?: number | null;
+ ventilator_pip?: number;
+ ventilator_pressure_support?: number;
+ ventilator_resp_rate?: number;
+ ventilator_spo2?: number;
+ ventilator_tidal_volume?: number;
+ };
+}
- useEffect(() => {
- const fetchDailyRounds = async (
- currentPage: number,
- consultationId: string,
- ) => {
- const { res, data } = await request(routes.dailyRoundsAnalyse, {
- body: { page: currentPage, fields: VentilatorPlotFields },
- pathParams: {
- consultationId,
- },
+export const VentilatorPlot = ({
+ dailyRoundsList,
+}: {
+ dailyRoundsList?: DailyRoundsModel[];
+}) => {
+ const [results, setResults] = useState({});
+ const { t } = useTranslation();
+
+ const getGraphData = (dailyRoundsData?: DailyRoundsModel[]) => {
+ const graphData: graphDataProps = {};
+ const graphDataCount = dailyRoundsData?.length ?? 0;
+ if (dailyRoundsData) {
+ dailyRoundsData.forEach((currentRound: DailyRoundsModel) => {
+ // @ts-expect-error taken_at should always be available
+ graphData[currentRound.taken_at] = {
+ bilateral_air_entry: currentRound.bilateral_air_entry,
+ etco2: currentRound.etco2,
+ id: currentRound.id,
+ ventilator_fio2: currentRound.ventilator_fio2,
+ ventilator_mean_airway_pressure:
+ currentRound.ventilator_mean_airway_pressure,
+ ventilator_oxygen_modality_flow_rate:
+ currentRound.ventilator_oxygen_modality_flow_rate,
+ ventilator_oxygen_modality_oxygen_rate:
+ currentRound.ventilator_oxygen_modality_oxygen_rate,
+ ventilator_peep: currentRound.ventilator_peep
+ ? Number(currentRound.ventilator_peep)
+ : null,
+ ventilator_pip: currentRound.ventilator_pip,
+ ventilator_pressure_support: currentRound.ventilator_pressure_support,
+ ventilator_resp_rate: currentRound.ventilator_resp_rate,
+ ventilator_spo2: currentRound.ventilator_spo2,
+ ventilator_tidal_volume: currentRound.ventilator_tidal_volume,
+ };
});
- if (res && res.ok && data) {
- setResults(data.results);
- setTotalCount(data.count);
- }
- };
+ }
+ return { graphData, graphDataCount };
+ };
+
+ useEffect(() => {
+ const { graphData } = getGraphData(dailyRoundsList);
+ setResults(graphData);
+ }, [dailyRoundsList]);
- fetchDailyRounds(currentPage, consultationId);
- }, [consultationId, currentPage]);
+ if (!dailyRoundsList) {
+ return ;
+ }
- const handlePagination = (page: number) => {
- setCurrentPage(page);
+ const dates = Object.keys(results).map((p: string) => formatDateTime(p));
+
+ const getConditionAndLegend = (
+ name: string,
+ currentRound: DailyRoundsModel,
+ ) => {
+ let condition = false;
+ let legend = "";
+ switch (name) {
+ case "ventilator_pip":
+ case "ventilator_mean_airway_pressure":
+ case "ventilator_resp_rate":
+ case "ventilator_pressure_support":
+ case "ventilator_tidal_volume":
+ case "ventilator_peep":
+ condition =
+ (currentRound.ventilator_interface === "INVASIVE" ||
+ currentRound.ventilator_interface === "NON_INVASIVE") &&
+ !!currentRound.ventilator_mode;
+ break;
+ case "ventilator_fio2":
+ condition =
+ currentRound.ventilator_interface === "OXYGEN_SUPPORT" &&
+ currentRound.ventilator_oxygen_modality === "HIGH_FLOW_NASAL_CANNULA";
+ break;
+ case "ventilator_spo2":
+ condition =
+ currentRound.ventilator_interface === "OXYGEN_SUPPORT" &&
+ (currentRound.ventilator_oxygen_modality === "NASAL_PRONGS" ||
+ currentRound.ventilator_oxygen_modality === "SIMPLE_FACE_MASK" ||
+ currentRound.ventilator_oxygen_modality ===
+ "NON_REBREATHING_MASK" ||
+ currentRound.ventilator_oxygen_modality ===
+ "HIGH_FLOW_NASAL_CANNULA");
+ break;
+ case "etco2":
+ case "ventilator_oxygen_modality_flow_rate":
+ condition =
+ !!currentRound.ventilator_mode ||
+ !!currentRound.ventilator_oxygen_modality ||
+ false;
+ break;
+ case "ventilator_oxygen_modality_oxygen_rate":
+ condition =
+ currentRound.ventilator_interface === "OXYGEN_SUPPORT" &&
+ (currentRound.ventilator_oxygen_modality === "NASAL_PRONGS" ||
+ currentRound.ventilator_oxygen_modality === "SIMPLE_FACE_MASK" ||
+ currentRound.ventilator_oxygen_modality === "NON_REBREATHING_MASK");
+ break;
+ }
+ switch (currentRound.ventilator_interface) {
+ case "OXYGEN_SUPPORT":
+ legend =
+ t(
+ `OXYGEN_MODALITY__${currentRound.ventilator_oxygen_modality}_short`,
+ ) +
+ " (" +
+ t("RESPIRATORY_SUPPORT_SHORT__OXYGEN_SUPPORT") +
+ ")";
+ break;
+ case "INVASIVE":
+ legend =
+ t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`) +
+ " (" +
+ t("RESPIRATORY_SUPPORT_SHORT__INVASIVE") +
+ ")";
+ break;
+ case "NON_INVASIVE":
+ legend =
+ t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`) +
+ " (" +
+ t("RESPIRATORY_SUPPORT_SHORT__NON_INVASIVE") +
+ ")";
+ break;
+ }
+ return { condition, legend };
+ };
+
+ const getModeOrModality = (round: DailyRoundsModel) => {
+ const ventilatorInterface = round.ventilator_interface;
+ if (!ventilatorInterface) return null;
+ switch (ventilatorInterface) {
+ case "INVASIVE":
+ case "NON_INVASIVE":
+ return round.ventilator_mode;
+ case "OXYGEN_SUPPORT":
+ return round.ventilator_oxygen_modality;
+ default:
+ return null;
+ }
};
- const dates = Object.keys(results)
- .map((p: string) => formatDateTime(p))
- .reverse();
+ const getMarkLineData = (name: string) => {
+ const markLineData = [];
+ if (!dailyRoundsList) return [];
+ let index = 0;
+ while (index < dailyRoundsList.length) {
+ const currentRound = dailyRoundsList[index];
+ const { condition, legend } = getConditionAndLegend(name, currentRound);
+ const currentInterfaceOrModality = getModeOrModality(currentRound);
+ if (condition) {
+ const startIndex = dates.findIndex(
+ (element) => element === formatDateTime(currentRound.taken_at),
+ );
+ if (startIndex !== -1) {
+ let nextIndex = index + 1;
+ while (nextIndex < dailyRoundsList.length) {
+ const nextRound = dailyRoundsList[nextIndex];
+ const nextInterfaceOrModality = getModeOrModality(nextRound);
+ if (
+ currentRound.ventilator_interface ===
+ nextRound.ventilator_interface &&
+ currentInterfaceOrModality === nextInterfaceOrModality
+ ) {
+ nextIndex += 1;
+ } else {
+ break;
+ }
+ }
+ const position =
+ startIndex === 0 ? "insideMiddleBottom" : "insideMiddleTop";
+ markLineData.push({
+ name: legend,
+ xAxis: dates[startIndex],
+ label: {
+ show: true,
+ position,
+ formatter: "{b}",
+ color: "#000000",
+ textBorderColor: "#ffffff",
+ textBorderWidth: 2,
+ },
+ });
+ index = nextIndex;
+ } else {
+ index += 1;
+ }
+ } else {
+ index += 1;
+ }
+ }
+ return markLineData;
+ };
- const yAxisData = (name: string) => {
- return Object.values(results)
- .map((p: any) => p[name])
- .reverse();
+ const yAxisData = (name: keyof graphDataProps[string]) => {
+ return Object.values(results).map((p) => p[name]);
};
const bilateral = Object.values(results)
- .map((p: any, i) => {
+ .map((p, i) => {
return {
value: p.bilateral_air_entry,
timestamp: Object.keys(results)[i],
@@ -91,6 +262,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_pip")}
low={12}
high={30}
+ verticalMarkerData={getMarkLineData("ventilator_pip")}
/>
@@ -101,6 +273,9 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_mean_airway_pressure")}
low={12}
high={25}
+ verticalMarkerData={getMarkLineData(
+ "ventilator_mean_airway_pressure",
+ )}
/>
@@ -111,6 +286,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_resp_rate")}
low={12}
high={20}
+ verticalMarkerData={getMarkLineData("ventilator_resp_rate")}
/>
@@ -121,6 +297,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_pressure_support")}
low={5}
high={15}
+ verticalMarkerData={getMarkLineData("ventilator_pressure_support")}
/>
@@ -129,6 +306,7 @@ export const VentilatorPlot = (props: any) => {
name="Tidal Volume"
xData={dates}
yData={yAxisData("ventilator_tidal_volume")}
+ verticalMarkerData={getMarkLineData("ventilator_tidal_volume")}
/>
@@ -139,6 +317,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_peep")}
low={5}
high={10}
+ verticalMarkerData={getMarkLineData("ventilator_peep")}
/>
@@ -149,6 +328,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_fio2")}
low={21}
high={60}
+ verticalMarkerData={getMarkLineData("ventilator_fio2")}
/>
@@ -159,6 +339,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("ventilator_spo2")}
low={90}
high={100}
+ verticalMarkerData={getMarkLineData("ventilator_spo2")}
/>
@@ -169,6 +350,7 @@ export const VentilatorPlot = (props: any) => {
yData={yAxisData("etco2")}
low={35}
high={45}
+ verticalMarkerData={getMarkLineData("etco2")}
/>
@@ -185,6 +367,9 @@ export const VentilatorPlot = (props: any) => {
name="Oxygen Flow Rate"
xData={dates}
yData={yAxisData("ventilator_oxygen_modality_oxygen_rate")}
+ verticalMarkerData={getMarkLineData(
+ "ventilator_oxygen_modality_oxygen_rate",
+ )}
/>
@@ -193,20 +378,12 @@ export const VentilatorPlot = (props: any) => {
name="Flow Rate"
xData={dates}
yData={yAxisData("ventilator_oxygen_modality_flow_rate")}
+ verticalMarkerData={getMarkLineData(
+ "ventilator_oxygen_modality_flow_rate",
+ )}
/>
-
- {totalCount > PAGINATION_LIMIT && (
-
- )}
);
};
diff --git a/src/components/Facility/Consultations/VentilatorTable.tsx b/src/components/Facility/Consultations/VentilatorTable.tsx
new file mode 100644
index 00000000000..2059f14bac0
--- /dev/null
+++ b/src/components/Facility/Consultations/VentilatorTable.tsx
@@ -0,0 +1,129 @@
+import { useTranslation } from "react-i18next";
+
+import { compareByDateString, formatDateTime } from "@/Utils/utils";
+
+import { DailyRoundsModel } from "../../Patient/models";
+
+type VentilatorTableProps = {
+ dailyRoundsList?: DailyRoundsModel[];
+};
+
+export default function VentilatorTable(props: VentilatorTableProps) {
+ const { t } = useTranslation();
+ const { dailyRoundsList } = props;
+
+ const VentilatorTableRow = ({
+ dailyRound,
+ start_date,
+ end_date,
+ }: {
+ dailyRound: DailyRoundsModel;
+ start_date: string;
+ end_date: string;
+ }) => {
+ const getModeText = () => {
+ const {
+ ventilator_interface,
+ ventilator_mode,
+ ventilator_oxygen_modality,
+ } = dailyRound;
+ switch (ventilator_interface) {
+ case "INVASIVE":
+ case "NON_INVASIVE":
+ return t(`VENTILATOR_MODE__${ventilator_mode}`);
+ case "OXYGEN_SUPPORT":
+ return t(`OXYGEN_MODALITY__${ventilator_oxygen_modality}`);
+ default:
+ return null;
+ }
+ };
+ return (
+
+ {start_date} |
+ {end_date} |
+
+ {t(`RESPIRATORY_SUPPORT__${dailyRound?.ventilator_interface}`)}
+ |
+ {getModeText()} |
+
+ );
+ };
+
+ const getModeOrModality = (round: DailyRoundsModel) => {
+ const ventilatorInterface = round.ventilator_interface;
+ if (!ventilatorInterface) return null;
+ switch (ventilatorInterface) {
+ case "INVASIVE":
+ case "NON_INVASIVE":
+ return round.ventilator_mode;
+ case "OXYGEN_SUPPORT":
+ return round.ventilator_oxygen_modality;
+ default:
+ return null;
+ }
+ };
+
+ const VentilatorTableBody = (dailyRoundsList: DailyRoundsModel[]) => {
+ const rows = [];
+ for (let index = 0; index < dailyRoundsList.length; index++) {
+ const currentRound = dailyRoundsList[index];
+ const currentInterfaceOrModality = getModeOrModality(currentRound);
+ if (!currentInterfaceOrModality) continue;
+ while (index < dailyRoundsList.length - 1) {
+ const nextRound = dailyRoundsList[index + 1];
+ const nextInterfaceOrModality = getModeOrModality(nextRound);
+ if (
+ nextInterfaceOrModality &&
+ currentRound.ventilator_interface == nextRound.ventilator_interface &&
+ currentInterfaceOrModality == nextInterfaceOrModality
+ ) {
+ index += 1;
+ } else {
+ break;
+ }
+ }
+ const end_date =
+ index + 1 < dailyRoundsList.length
+ ? formatDateTime(dailyRoundsList[index + 1].taken_at)
+ : "";
+ const start_date = formatDateTime(currentRound.taken_at);
+ rows.push(
+ ,
+ );
+ }
+ return rows;
+ };
+
+ if (!dailyRoundsList?.length) {
+ return;
+ }
+ const sortedData: DailyRoundsModel[] = dailyRoundsList.sort(
+ compareByDateString("taken_at"),
+ );
+
+ return (
+
+
+
+ {t("ventilator_log")}
+
+
+
+ {t("start_datetime")} |
+ {t("end_datetime")} |
+ {t("ventilator_modality")} |
+
+ {`${t("ventilator_mode")} / ${t("ventilator_oxygen_modality")}`}
+ |
+
+
+ {VentilatorTableBody(sortedData)}
+
+
+ );
+}
diff --git a/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx b/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx
index 82eb7f3da4d..6183f19368d 100644
--- a/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx
+++ b/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx
@@ -4,7 +4,7 @@ import { formatDateTime } from "@/Utils/utils";
export default function BinaryChronologicalChart(props: {
data: {
- value: boolean;
+ value: boolean | undefined;
timestamp: string;
notes?: string;
}[];
diff --git a/src/components/Facility/Consultations/components/LinePlot.tsx b/src/components/Facility/Consultations/components/LinePlot.tsx
index 0829527faae..e9f1adf731f 100644
--- a/src/components/Facility/Consultations/components/LinePlot.tsx
+++ b/src/components/Facility/Consultations/components/LinePlot.tsx
@@ -12,12 +12,21 @@ export const LinePlot = (props: any) => {
const {
title,
name,
- xData,
- yData,
low = null,
high = null,
defaultSpace,
+ verticalMarkerData = null,
} = props;
+ let { xData, yData } = props;
+ const yDatacount = yData.filter(
+ (item: number | null): item is number =>
+ item !== null && !Number.isNaN(item),
+ ).length;
+ if (yDatacount === 0) {
+ yData = [];
+ xData = [];
+ }
+
let generalOptions: any = {
grid: {
top: "40px",
@@ -106,6 +115,25 @@ export const LinePlot = (props: any) => {
],
};
+ if (verticalMarkerData && yDatacount > 0) {
+ let series = generalOptions.series[0];
+ series = {
+ ...series,
+ markLine: {
+ silent: true,
+ data: verticalMarkerData,
+ symbol: "none",
+ lineStyle: {
+ color: "#000000",
+ },
+ },
+ };
+ generalOptions = {
+ ...generalOptions,
+ series,
+ };
+ }
+
if (props.type && props.type === "WAVEFORM") {
generalOptions = {
...generalOptions,
diff --git a/src/components/Facility/Consultations/components/ReactEcharts.tsx b/src/components/Facility/Consultations/components/ReactEcharts.tsx
index 326bd23661a..4215b34fa4d 100644
--- a/src/components/Facility/Consultations/components/ReactEcharts.tsx
+++ b/src/components/Facility/Consultations/components/ReactEcharts.tsx
@@ -5,6 +5,7 @@ import {
DataZoomComponent,
GridComponent,
LegendComponent,
+ MarkLineComponent,
TitleComponent,
ToolboxComponent,
TooltipComponent,
@@ -27,6 +28,7 @@ echarts.use([
TooltipComponent,
VisualMapComponent,
VisualMapPiecewiseComponent,
+ MarkLineComponent,
]);
interface ReactEchartsProps extends EChartsReactProps {
diff --git a/src/components/Patient/SampleDetails.tsx b/src/components/Patient/SampleDetails.tsx
index c556177ec13..c16ef1c0e86 100644
--- a/src/components/Patient/SampleDetails.tsx
+++ b/src/components/Patient/SampleDetails.tsx
@@ -1,5 +1,6 @@
import { camelCase, capitalize, startCase } from "lodash-es";
import { navigate } from "raviger";
+import { useTranslation } from "react-i18next";
import Card from "@/CAREUI/display/Card";
@@ -17,6 +18,7 @@ import useQuery from "@/Utils/request/useQuery";
import { formatDateTime, formatPatientAge } from "@/Utils/utils";
export const SampleDetails = ({ id }: DetailRoute) => {
+ const { t } = useTranslation();
const { loading: isLoading, data: sampleDetails } = useQuery(
routes.getTestSample,
{
@@ -33,9 +35,9 @@ export const SampleDetails = ({ id }: DetailRoute) => {
const yesOrNoBadge = (param: any) =>
param ? (
- Yes
+ {t("yes")}
) : (
- No
+ {t("no")}
);
const showPatientCard = (patientData: any) => {
@@ -51,20 +53,24 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Name:
+
+ {t("name")}:{" "}
+
{patientData?.name}
{patientData?.is_medical_worker && (
- Medical Worker:{" "}
+ {t("medical_worker")}:{" "}
+
+
+ {t("yes")}
- Yes
)}
- Disease Status:{" "}
+ {t("disease_status")}:{" "}
{patientData?.disease_status}
@@ -72,16 +78,20 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- SRF ID:
+
+ {t("srf_id")}:{" "}
+
{(patientData?.srf_id && patientData?.srf_id) || "-"}
- Test Type:
+
+ {t("test_type")}:{" "}
+
{(patientData?.test_type && testType) || "-"}
- Date of Test:{" "}
+ {t("date_of_test")}:{" "}
{(patientData?.date_of_test &&
formatDateTime(patientData?.date_of_test)) ||
@@ -89,35 +99,43 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Facility:
+
+ {t("facility")}:{" "}
+
{patientData?.facility_object?.name || "-"}
{patientData?.date_of_birth ? (
- Date of birth:{" "}
+ {t("date_of_birth")}:{" "}
{patientData?.date_of_birth}
) : (
- Age:
+
+ {t("age")}:{" "}
+
{formatPatientAge(patientData)}
)}
- Gender:
+
+ {t("gender")}:{" "}
+
{patientGender}
- Nationality:{" "}
+ {t("nationality")}:{" "}
{patientData?.nationality || "-"}
@@ -125,14 +143,14 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Blood Group:{" "}
+ {t("blood_group")}:{" "}
{patientData?.blood_group || "-"}
{patientData?.nationality !== "India" && (
- Passport Number:{" "}
+ {t("passport_number")}:{" "}
{patientData?.passport_no || "-"}
@@ -140,56 +158,60 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{patientData?.nationality === "India" && (
<>
- State:
+
+ {t("state")}:{" "}
+
{patientData?.state_object?.name}
- District:{" "}
+ {t("district")}:{" "}
{patientData?.district_object?.name || "-"}
- Local Body:{" "}
+ {t("local_body")}:{" "}
{patientData?.local_body_object?.name || "-"}
>
)}
- Address:
+
+ {t("address")}:{" "}
+
{patientData?.address || "-"}
- Contact with confirmed carrier:{" "}
+ {t("contact_with_confirmed_carrier")}:{" "}
{yesOrNoBadge(patientData?.contact_with_confirmed_carrier)}
- Contact with suspected carrier:{" "}
+ {t("contact_with_suspected_carrier")}:{" "}
{yesOrNoBadge(patientData?.contact_with_suspected_carrier)}
{patientData?.estimated_contact_date && (
- Estimated contact date:{" "}
+ {t("estimated_contact_date")}:{" "}
{formatDateTime(patientData?.estimated_contact_date)}
)}
- Has SARI (Severe Acute Respiratory illness)?:{" "}
+ {t("has_sari")}:{" "}
{yesOrNoBadge(patientData?.has_SARI)}
- Domestic/international Travel (within last 28 days):{" "}
+ {t("domestic_international_travel")}:{" "}
{yesOrNoBadge(patientData?.past_travel)}
@@ -197,7 +219,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
!!patientData?.countries_travelled.length && (
- Countries travelled:{" "}
+ {t("countries_travelled")}:{" "}
{patientData?.countries_travelled.join(", ")}
@@ -205,7 +227,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{patientData?.ongoing_medication && (
- Ongoing Medications{" "}
+ {t("ongoing_medications")}{" "}
{patientData?.ongoing_medication}
@@ -213,7 +235,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{patientData?.allergies && (
- Allergies:{" "}
+ {t("allergies")}:{" "}
{patientData?.allergies}
@@ -221,7 +243,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{!!patientData?.number_of_aged_dependents && (
- Number Of Aged Dependents (Above 60):{" "}
+ {t("number_of_aged_dependents")}:{" "}
{patientData?.number_of_aged_dependents}
@@ -229,7 +251,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{!!patientData?.number_of_chronic_diseased_dependents && (
- Number Of Chronic Diseased Dependents:{" "}
+ {t("number_of_chronic_diseased_dependents")}:{" "}
{patientData?.number_of_chronic_diseased_dependents}
@@ -245,19 +267,25 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Status: {" "}
+
+ {t("status")}:{" "}
+ {" "}
{startCase(camelCase(flow.status))}
- Label:{" "}
+ {t("label")}:{" "}
{capitalize(flow.notes)}
- Created On :{" "}
+
+ {t("created_on")}:
+ {" "}
{flow.created_date ? formatDateTime(flow.created_date) : "-"}
- Modified on:{" "}
+
+ {t("modified_on")}:
+ {" "}
{flow.modified_date ? formatDateTime(flow.modified_date) : "-"}
@@ -271,7 +299,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
return (
{
- ICMR Specimen Referral Form
+ {t("icmr_specimen_referral_form")}
)
@@ -289,34 +317,42 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Status:{" "}
+ {t("status")}:{" "}
{sampleDetails?.status}
- Result:{" "}
+ {t("result")}:{" "}
{sampleDetails?.result}
- Patient:
+
+ {t("patient")}:{" "}
+
{sampleDetails?.patient_name}
{sampleDetails?.facility_object && (
- Facility:
+
+ {t("facility")}:{" "}
+
{sampleDetails?.facility_object.name}
)}
- Tested on:
+
+ {t("tested_on")}:{" "}
+
{sampleDetails?.date_of_result
? formatDateTime(sampleDetails.date_of_result)
: "-"}
-
Result on:
+
+ {t("result_on")}:{" "}
+
{sampleDetails?.date_of_result
? formatDateTime(sampleDetails.date_of_result)
: "-"}
@@ -324,7 +360,7 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{sampleDetails?.fast_track && (
- Fast track testing reason:{" "}
+ {t("fast_track_testing_reason")}:{" "}
{sampleDetails.fast_track}
@@ -332,21 +368,23 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{sampleDetails?.doctor_name && (
- Doctor's Name:{" "}
+ {t("doctors_name")}:{" "}
{startCase(camelCase(sampleDetails.doctor_name))}
)}
{sampleDetails?.diagnosis && (
- Diagnosis:
+
+ {t("diagnosis")}:{" "}
+
{sampleDetails.diagnosis}
)}
{sampleDetails?.diff_diagnosis && (
- Differential diagnosis:{" "}
+ {t("differential_diagnosis")}:{" "}
{sampleDetails?.diff_diagnosis}
@@ -354,52 +392,48 @@ export const SampleDetails = ({ id }: DetailRoute) => {
{sampleDetails?.etiology_identified && (
- Etiology identified:{" "}
+ {t("etiology_identified")}:{" "}
{sampleDetails.etiology_identified}
)}
- Is Atypical presentation{" "}
+ {t("is_atypical_presentation")}{" "}
{yesOrNoBadge(sampleDetails?.is_atypical_presentation)}
- Is unusual course{" "}
+ {t("is_unusual_course")}{" "}
{yesOrNoBadge(sampleDetails?.is_unusual_course)}
{sampleDetails?.atypical_presentation && (
- Atypical presentation details:{" "}
+ {t("atypical_presentation_details")}:{" "}
{sampleDetails.atypical_presentation}
)}
-
- SARI - Severe Acute Respiratory illness{" "}
-
+ {t("sari")}
{yesOrNoBadge(sampleDetails?.has_sari)}
-
- ARI - Acute Respiratory illness{" "}
-
+ {t("ari")}
{yesOrNoBadge(sampleDetails?.has_ari)}
- Contact with confirmed carrier{" "}
+ {t("contact_with_confirmed_carrier")}{" "}
{yesOrNoBadge(sampleDetails?.patient_has_confirmed_contact)}
- Contact with suspected carrier{" "}
+ {t("contact_with_suspected_carrier")}{" "}
{yesOrNoBadge(sampleDetails?.patient_has_suspected_contact)}
@@ -407,29 +441,37 @@ export const SampleDetails = ({ id }: DetailRoute) => {
sampleDetails.patient_travel_history.length !== 0 && (
- Countries travelled:{" "}
+ {t("countries_travelled")}:{" "}
{sampleDetails.patient_travel_history}
)}
{sampleDetails?.sample_type && (
-
+
- Sample Type:{" "}
+ {t("sample_type")}:{" "}
{startCase(camelCase(sampleDetails.sample_type))}
)}
+ {sampleDetails?.sample_type === "OTHER TYPE" && (
+
+
+ {t("sample_type_description")}:{" "}
+
+ {sampleDetails?.sample_type_other}
+
+ )}
-
Details of patient
+ {t("details_of_patient")}
{showPatientCard(sampleDetails?.patient_object)}
-
Sample Test History
+ {t("sample_test_history")}
{sampleDetails?.flow &&
sampleDetails.flow.map((flow: FlowModel) => renderFlow(flow))}
diff --git a/src/components/Patient/SampleTest.tsx b/src/components/Patient/SampleTest.tsx
index afddb81b654..0714de28492 100644
--- a/src/components/Patient/SampleTest.tsx
+++ b/src/components/Patient/SampleTest.tsx
@@ -218,7 +218,7 @@ export const SampleTest = ({ facilityId, patientId }: any) => {
optionValue={(option) => option.id}
/>
- {state.form.sample_type === "OTHER TYPE" && (
+ {state.form.sample_type === "9" && (
{
Sample Type{" "}
- {(itemData.sample_type !== "OTHER TYPE"
- ? itemData.sample_type
- : itemData.sample_type_other
- )?.toLowerCase()}
+ {itemData.sample_type?.toLowerCase()}