From 6e37cae82b6f09f2cf7fd4991b25657f5f9b0885 Mon Sep 17 00:00:00 2001 From: Yaswanth Kumar Bethu Date: Sun, 17 Nov 2024 20:03:24 +0530 Subject: [PATCH 01/33] Use Log Update instead of daily rounds (#9089) --- .../e2e/patient_spec/PatientLogUpdate.cy.ts | 24 +++++++++---------- .../pageobject/Patient/PatientConsultation.ts | 4 ++-- public/locale/en.json | 4 ++++ src/Routers/routes/ConsultationRoutes.tsx | 10 ++++---- src/common/constants.tsx | 4 ++-- src/components/Facility/ConsultationCard.tsx | 9 +++---- .../ConsultationUpdatesTab.tsx | 10 ++++---- .../ConsultationDetails/Events/EventsList.tsx | 2 +- .../DefaultLogUpdateCard.tsx | 12 +++++----- .../LoadingCard.tsx | 0 .../LogUpdateCardAttribute.tsx | 0 .../VirtualNursingAssistantLogUpdateCard.tsx | 2 +- ...yRoundsFilter.tsx => LogUpdatesFilter.tsx} | 2 +- ...DailyRoundsList.tsx => LogUpdatesList.tsx} | 10 ++++---- .../LogUpdate/CriticalCareEditor.tsx | 4 ++-- .../LogUpdate/CriticalCarePreview.tsx | 2 +- .../Notifications/NotificationsList.tsx | 4 ++-- .../Notifications/ShowPushNotification.tsx | 4 ++-- .../Patient/DailyRoundListDetails.tsx | 6 ++--- src/components/Patient/DailyRounds.tsx | 8 +++---- src/components/Patient/PatientInfoCard.tsx | 2 +- tailwind.config.js | 6 ++++- 22 files changed, 69 insertions(+), 60 deletions(-) rename src/components/Facility/Consultations/{DailyRounds => LogUpdates}/DefaultLogUpdateCard.tsx (88%) rename src/components/Facility/Consultations/{DailyRounds => LogUpdates}/LoadingCard.tsx (100%) rename src/components/Facility/Consultations/{DailyRounds => LogUpdates}/LogUpdateCardAttribute.tsx (100%) rename src/components/Facility/Consultations/{DailyRounds => LogUpdates}/VirtualNursingAssistantLogUpdateCard.tsx (97%) rename src/components/Facility/Consultations/{DailyRoundsFilter.tsx => LogUpdatesFilter.tsx} (98%) rename src/components/Facility/Consultations/{DailyRoundsList.tsx => LogUpdatesList.tsx} (93%) diff --git a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts index b6d7ecbc173..b6399ff02ce 100644 --- a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts +++ b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts @@ -130,9 +130,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { cy.verifyNotification("Progress Note Log Update filed successfully"); cy.closeNotification(); // Verify the data reflection - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); cy.verifyContentPresence("#consultation-preview", [ @@ -180,9 +180,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { cy.submitButton("Complete"); cy.verifyNotification("Detailed Log Update filed successfully"); cy.closeNotification(); - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); cy.verifyContentPresence("#respiratory-support", [ @@ -195,9 +195,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { ]); // Go back and edit the data on a third section patientLogupdate.clickGoBackConsultation(); - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateUpdateLog( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); patientLogupdate.selectCriticalCareSection("Dialysis"); @@ -210,9 +210,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { cy.verifyNotification("Detailed Log Update filed successfully"); cy.closeNotification(); //Reverify the editted and newly added data - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); cy.verifyContentPresence("#respiratory-support", [ @@ -285,9 +285,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { cy.verifyNotification("Brief Update created successfully"); cy.closeNotification(); // edit the card and verify the data. - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); cy.verifyContentPresence("#consultation-preview", [ @@ -309,9 +309,9 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.typeDiastolic(patientModifiedDiastolic); cy.submitButton("Continue"); cy.verifyNotification("Brief Update updated successfully"); - cy.contains("button", "Daily Rounds").click(); + cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( - "#dailyround-entry", + "#logupdate-entry", patientCategory, ); cy.verifyContentPresence("#consultation-preview", [ diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts index 6f4f994b395..642eaf9e4d6 100644 --- a/cypress/pageobject/Patient/PatientConsultation.ts +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -115,8 +115,8 @@ export class PatientConsultationPage { clickViewConsultationButton() { cy.verifyAndClickElement( - "#view_consultation_updates", - "View Consultation / Consultation Updates", + "#view_consultation_and_log_updates", + "View Consultation / Log Updates", ); } diff --git a/public/locale/en.json b/public/locale/en.json index 2e8469a3667..36c25a06682 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -828,6 +828,8 @@ "location_management": "Location Management", "log_lab_results": "Log Lab Results", "log_report": "Log Report", + "log_update": "Log Update", + "log_updates": "Log Updates", "login": "Login", "longitude_invalid": "Longitude must be between -180 and 180", "lsg": "Lsg", @@ -901,6 +903,7 @@ "no_investigation_suggestions": "No Investigation Suggestions", "no_linked_facilities": "No Linked Facilities", "no_log_update_delta": "No changes since previous log update", + "no_log_updates": "No log updates found", "no_notices_for_you": "No notices for you.", "no_patients_to_show": "No patients to show.", "no_policy_added": "No Insurance Policy Added", @@ -1309,6 +1312,7 @@ "view_abdm_records": "View ABDM Records", "view_asset": "View Assets", "view_cns": "View CNS", + "view_consultation_and_log_updates": "View Consultation / Log Updates", "view_details": "View Details", "view_faciliy": "View Facility", "view_patients": "View Patients", diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 2d283df27d7..da3c30b5093 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -81,7 +81,7 @@ const consultationRoutes: AppRoutes = { sessionId={sessionId} /> ), - "/facility/:facilityId/patient/:patientId/consultation/:id/daily-rounds": ({ + "/facility/:facilityId/patient/:patientId/consultation/:id/log_updates": ({ facilityId, patientId, id, @@ -92,7 +92,7 @@ const consultationRoutes: AppRoutes = { consultationId={id} /> ), - "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id/update": + "/facility/:facilityId/patient/:patientId/consultation/:consultationId/log_updates/:id/update": ({ facilityId, patientId, consultationId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily-rounds/:id": + "/facility/:facilityId/patient/:patientId/consultation/:consultationId/log_updates/:id": ({ facilityId, patientId, consultationId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id": + "/facility/:facilityId/patient/:patientId/consultation/:consultationId/log_updates/:id/critical_care": ({ facilityId, patientId, consultationId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/consultation/:consultationId/daily_rounds/:id/update": + "/facility/:facilityId/patient/:patientId/consultation/:consultationId/log_updates/:id/critical_care/update": ({ facilityId, patientId, consultationId, id }) => ( {
navigate( @@ -177,7 +178,7 @@ export const ConsultationCard = (props: ConsultationProps) => { ) } > - View Consultation / Consultation Updates + {t("view_consultation_and_log_updates")} { setOpen(true); } else { navigate( - `/facility/${itemData.facility}/patient/${itemData.patient}/consultation/${itemData.id}/daily-rounds`, + `/facility/${itemData.facility}/patient/${itemData.patient}/consultation/${itemData.id}/log_updates/`, ); } }} disabled={!!itemData.discharge_date} authorizeFor={NonReadOnlyUsers} > - Add Consultation Updates + {t("add") + " " + t("log_update")} )}
diff --git a/src/components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index e70b142d663..ea7448d37b2 100644 --- a/src/components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -21,8 +21,8 @@ import ReadMore from "@/components/Common/Readmore"; import Tabs from "@/components/Common/Tabs"; import EventsList from "@/components/Facility/ConsultationDetails/Events/EventsList"; import { ConsultationTabProps } from "@/components/Facility/ConsultationDetails/index"; -import DailyRoundsFilter from "@/components/Facility/Consultations/DailyRoundsFilter"; -import DailyRoundsList from "@/components/Facility/Consultations/DailyRoundsList"; +import LogUpdatesFilter from "@/components/Facility/Consultations/LogUpdatesFilter"; +import LogUpdatesList from "@/components/Facility/Consultations/LogUpdatesList"; import { BedModel } from "@/components/Facility/models"; import PrescriptionsTable from "@/components/Medicine/PrescriptionsTable"; import EncounterSymptomsCard from "@/components/Symptoms/SymptomsCard"; @@ -688,7 +688,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { ), value: 1, }, - { text: t("daily_rounds"), value: 0 }, + { text: t("log_updates"), value: 0 }, ]} onTabChange={(v) => setShowEvents(!!v)} currentTab={showEvents ? 1 : 0} @@ -756,7 +756,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { {showEvents ? ( ) : ( - @@ -773,7 +773,7 @@ function DailyRoundsSortDropdown({ setDailyRoundsQuery: (query: QueryParams) => void; }) { return ( - { setDailyRoundsQuery(query); }} diff --git a/src/components/Facility/ConsultationDetails/Events/EventsList.tsx b/src/components/Facility/ConsultationDetails/Events/EventsList.tsx index 47c68636a1a..e89f8dd8b2f 100644 --- a/src/components/Facility/ConsultationDetails/Events/EventsList.tsx +++ b/src/components/Facility/ConsultationDetails/Events/EventsList.tsx @@ -6,7 +6,7 @@ import PaginatedList from "@/CAREUI/misc/PaginatedList"; import GenericEvent from "@/components/Facility/ConsultationDetails/Events/GenericEvent"; import { getEventIcon } from "@/components/Facility/ConsultationDetails/Events/iconMap"; import { EventGeneric } from "@/components/Facility/ConsultationDetails/Events/types"; -import LoadingLogUpdateCard from "@/components/Facility/Consultations/DailyRounds/LoadingCard"; +import LoadingLogUpdateCard from "@/components/Facility/Consultations/LogUpdates/LoadingCard"; import { useSlugs } from "@/hooks/useSlug"; diff --git a/src/components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx b/src/components/Facility/Consultations/LogUpdates/DefaultLogUpdateCard.tsx similarity index 88% rename from src/components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx rename to src/components/Facility/Consultations/LogUpdates/DefaultLogUpdateCard.tsx index 8a436c07393..31a685cdb59 100644 --- a/src/components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard.tsx +++ b/src/components/Facility/Consultations/LogUpdates/DefaultLogUpdateCard.tsx @@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import ButtonV2 from "@/components/Common/ButtonV2"; -import LogUpdateCardAttribute from "@/components/Facility/Consultations/DailyRounds/LogUpdateCardAttribute"; +import LogUpdateCardAttribute from "@/components/Facility/Consultations/LogUpdates/LogUpdateCardAttribute"; import { ConsultationModel } from "@/components/Facility/models"; import { DailyRoundsModel } from "@/components/Patient/models"; @@ -26,7 +26,7 @@ const DefaultLogUpdateCard = ({ round, ...props }: Props) => { return (
{ ["NORMAL", "TELEMEDICINE", "DOCTORS_LOG"].includes( round.rounds_type!, ) - ? `${consultationUrl}/daily-rounds/${round.id}` - : `${consultationUrl}/daily_rounds/${round.id}` + ? `${consultationUrl}/log_updates/${round.id}` + : `${consultationUrl}/log_updates/${round.id}/critical_care` } > @@ -81,8 +81,8 @@ const DefaultLogUpdateCard = ({ round, ...props }: Props) => { "DOCTORS_LOG", "COMMUNITY_NURSES_LOG", ].includes(round.rounds_type!) - ? `${consultationUrl}/daily-rounds/${round.id}/update` - : `${consultationUrl}/daily_rounds/${round.id}/update` + ? `${consultationUrl}/log_updates/${round.id}/update` + : `${consultationUrl}/log_updates/${round.id}/critical_care/update` } > diff --git a/src/components/Facility/Consultations/DailyRounds/LoadingCard.tsx b/src/components/Facility/Consultations/LogUpdates/LoadingCard.tsx similarity index 100% rename from src/components/Facility/Consultations/DailyRounds/LoadingCard.tsx rename to src/components/Facility/Consultations/LogUpdates/LoadingCard.tsx diff --git a/src/components/Facility/Consultations/DailyRounds/LogUpdateCardAttribute.tsx b/src/components/Facility/Consultations/LogUpdates/LogUpdateCardAttribute.tsx similarity index 100% rename from src/components/Facility/Consultations/DailyRounds/LogUpdateCardAttribute.tsx rename to src/components/Facility/Consultations/LogUpdates/LogUpdateCardAttribute.tsx diff --git a/src/components/Facility/Consultations/DailyRounds/VirtualNursingAssistantLogUpdateCard.tsx b/src/components/Facility/Consultations/LogUpdates/VirtualNursingAssistantLogUpdateCard.tsx similarity index 97% rename from src/components/Facility/Consultations/DailyRounds/VirtualNursingAssistantLogUpdateCard.tsx rename to src/components/Facility/Consultations/LogUpdates/VirtualNursingAssistantLogUpdateCard.tsx index b8a8e9fd814..e7229634266 100644 --- a/src/components/Facility/Consultations/DailyRounds/VirtualNursingAssistantLogUpdateCard.tsx +++ b/src/components/Facility/Consultations/LogUpdates/VirtualNursingAssistantLogUpdateCard.tsx @@ -1,6 +1,6 @@ import { useTranslation } from "react-i18next"; -import LogUpdateCardAttribute from "@/components/Facility/Consultations/DailyRounds/LogUpdateCardAttribute"; +import LogUpdateCardAttribute from "@/components/Facility/Consultations/LogUpdates/LogUpdateCardAttribute"; import { DailyRoundsModel } from "@/components/Patient/models"; // TODO: remove this method once events module is ready diff --git a/src/components/Facility/Consultations/DailyRoundsFilter.tsx b/src/components/Facility/Consultations/LogUpdatesFilter.tsx similarity index 98% rename from src/components/Facility/Consultations/DailyRoundsFilter.tsx rename to src/components/Facility/Consultations/LogUpdatesFilter.tsx index 90e7908ac16..22cb357936c 100644 --- a/src/components/Facility/Consultations/DailyRoundsFilter.tsx +++ b/src/components/Facility/Consultations/LogUpdatesFilter.tsx @@ -26,7 +26,7 @@ interface Props { onApply: (filter: FilterState) => void; } -export default function DailyRoundsFilter(props: Props) { +export default function LogUpdatesFilter(props: Props) { const { t } = useTranslation(); const [filter, setFilter] = useState({}); diff --git a/src/components/Facility/Consultations/DailyRoundsList.tsx b/src/components/Facility/Consultations/LogUpdatesList.tsx similarity index 93% rename from src/components/Facility/Consultations/DailyRoundsList.tsx rename to src/components/Facility/Consultations/LogUpdatesList.tsx index 037556e6dda..911d32dca25 100644 --- a/src/components/Facility/Consultations/DailyRoundsList.tsx +++ b/src/components/Facility/Consultations/LogUpdatesList.tsx @@ -3,9 +3,9 @@ import { useTranslation } from "react-i18next"; import Timeline, { TimelineNode } from "@/CAREUI/display/Timeline"; import PaginatedList from "@/CAREUI/misc/PaginatedList"; -import DefaultLogUpdateCard from "@/components/Facility/Consultations/DailyRounds/DefaultLogUpdateCard"; -import LoadingLogUpdateCard from "@/components/Facility/Consultations/DailyRounds/LoadingCard"; -import VirtualNursingAssistantLogUpdateCard from "@/components/Facility/Consultations/DailyRounds/VirtualNursingAssistantLogUpdateCard"; +import DefaultLogUpdateCard from "@/components/Facility/Consultations/LogUpdates/DefaultLogUpdateCard"; +import LoadingLogUpdateCard from "@/components/Facility/Consultations/LogUpdates/LoadingCard"; +import VirtualNursingAssistantLogUpdateCard from "@/components/Facility/Consultations/LogUpdates/VirtualNursingAssistantLogUpdateCard"; import { ConsultationModel } from "@/components/Facility/models"; import { DailyRoundsModel } from "@/components/Patient/models"; @@ -21,7 +21,7 @@ interface Props { query: QueryParams; } -export default function DailyRoundsList({ consultation, query }: Props) { +export default function LogUpdatesList({ consultation, query }: Props) { const [consultationId] = useSlugs("consultation"); const { t } = useTranslation(); @@ -37,7 +37,7 @@ export default function DailyRoundsList({ consultation, query }: Props) {
- {t("no_consultation_updates")} + {t("no_log_updates")} diff --git a/src/components/LogUpdate/CriticalCareEditor.tsx b/src/components/LogUpdate/CriticalCareEditor.tsx index 42de82b51b8..c6b48f24909 100644 --- a/src/components/LogUpdate/CriticalCareEditor.tsx +++ b/src/components/LogUpdate/CriticalCareEditor.tsx @@ -95,7 +95,7 @@ export default function CriticalCareEditor(props: Props) { ghost className="w-full bg-primary-100/50 py-3" border - href={`${consultationDashboardUrl}/daily-rounds/${props.id}/update`} + href={`${consultationDashboardUrl}/log_updates/${props.id}/update`} > { - const [consultationId, id] = useSlugs("consultation", "daily_rounds"); + const [consultationId, id] = useSlugs("consultation", "log_updates"); const [diff, setDiff] = useState>({}); const [isProcessing, setIsProcessing] = useState(false); diff --git a/src/components/LogUpdate/CriticalCarePreview.tsx b/src/components/LogUpdate/CriticalCarePreview.tsx index 1c95f051526..e131e43f405 100644 --- a/src/components/LogUpdate/CriticalCarePreview.tsx +++ b/src/components/LogUpdate/CriticalCarePreview.tsx @@ -61,7 +61,7 @@ export default function CriticalCarePreview(props: Props) {

- Consultation Updates + {t("log_updates")}
{t(`ROUNDS_TYPE__${data.rounds_type}`)}
diff --git a/src/components/Notifications/NotificationsList.tsx b/src/components/Notifications/NotificationsList.tsx index 0153017c5ed..17033d67623 100644 --- a/src/components/Notifications/NotificationsList.tsx +++ b/src/components/Notifications/NotificationsList.tsx @@ -72,9 +72,9 @@ const NotificationTile = ({ case "PATIENT_CONSULTATION_UPDATED": return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}`; case "PATIENT_CONSULTATION_UPDATE_CREATED": - return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}/daily-rounds/${data.daily_round}`; + return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}/log_updates/${data.daily_round}`; case "PATIENT_CONSULTATION_UPDATE_UPDATED": - return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}/daily-rounds/${data.daily_round}`; + return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}/log_updates/${data.daily_round}`; case "INVESTIGATION_SESSION_CREATED": return `/facility/${data.facility}/patient/${data.patient}/consultation/${data.consultation}/investigation/${data.session}`; case "PATIENT_NOTE_ADDED": diff --git a/src/components/Notifications/ShowPushNotification.tsx b/src/components/Notifications/ShowPushNotification.tsx index 490c44ea649..b1bc7c34397 100644 --- a/src/components/Notifications/ShowPushNotification.tsx +++ b/src/components/Notifications/ShowPushNotification.tsx @@ -24,9 +24,9 @@ export default function ShowPushNotification({ id }: { id: string }) { case "PATIENT_CONSULTATION_UPDATED": return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}`; case "PATIENT_CONSULTATION_UPDATE_CREATED": - return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}/daily-rounds/${caused_objects?.daily_round}`; + return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}/log_updates/${caused_objects?.daily_round}`; case "PATIENT_CONSULTATION_UPDATE_UPDATED": - return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}/daily-rounds/${caused_objects?.daily_round}`; + return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}/log_updates/${caused_objects?.daily_round}`; case "INVESTIGATION_SESSION_CREATED": return `/facility/${caused_objects?.facility}/patient/${caused_objects?.patient}/consultation/${caused_objects?.consultation}/investigation/${caused_objects?.session}`; case "PATIENT_NOTE_ADDED": diff --git a/src/components/Patient/DailyRoundListDetails.tsx b/src/components/Patient/DailyRoundListDetails.tsx index b7bc8964f65..ec568ba56d2 100644 --- a/src/components/Patient/DailyRoundListDetails.tsx +++ b/src/components/Patient/DailyRoundListDetails.tsx @@ -31,8 +31,8 @@ export const DailyRoundListDetails = (props: any) => { return (
{
Update Details diff --git a/src/components/Patient/DailyRounds.tsx b/src/components/Patient/DailyRounds.tsx index c12621e410f..d9bcb1366d5 100644 --- a/src/components/Patient/DailyRounds.tsx +++ b/src/components/Patient/DailyRounds.tsx @@ -144,7 +144,7 @@ export const DailyRounds = (props: any) => { const [diagnoses, setDiagnoses] = useState(); const [showDiscontinuedPrescriptions, setShowDiscontinuedPrescriptions] = useState(false); - const headerText = !id ? "Add Consultation Update" : "Info"; + const headerText = !id ? t("add") + " " + t("log_update") : "Info"; const buttonText = !id ? !["VENTILATOR", "DOCTORS_LOG"].includes(state.form.rounds_type) ? t("save") @@ -411,7 +411,7 @@ export const DailyRounds = (props: any) => { ); } else { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${obj.id}/update`, + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/log_updates/${obj.id}/update`, ); } } @@ -439,7 +439,7 @@ export const DailyRounds = (props: any) => { ); } else { navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${obj.id}/update`, + `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/log_updates/${obj.id}/critical_care/update`, ); } } @@ -553,7 +553,7 @@ export const DailyRounds = (props: any) => { }} backUrl={ id - ? `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily-rounds` + ? `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/log_updates` : `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}` } className="mx-auto max-w-4xl" diff --git a/src/components/Patient/PatientInfoCard.tsx b/src/components/Patient/PatientInfoCard.tsx index 544c9585e09..e415d928a0d 100644 --- a/src/components/Patient/PatientInfoCard.tsx +++ b/src/components/Patient/PatientInfoCard.tsx @@ -582,7 +582,7 @@ export default function PatientInfoCard(props: PatientInfoCardProps) { href={ consultation?.admitted && !consultation?.current_bed ? undefined - : `/facility/${patient.facility}/patient/${patient.id}/consultation/${consultation?.id}/daily-rounds` + : `/facility/${patient.facility}/patient/${patient.id}/consultation/${consultation?.id}/log_updates` } onClick={() => { if ( diff --git a/tailwind.config.js b/tailwind.config.js index 520c1c4b5da..6594264d903 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -83,7 +83,11 @@ module.exports = { }, }, }, - content: ["./src/**/*.{html,md,js,jsx,ts,tsx}", "./apps/**/*.{html,md,js,jsx,ts,tsx}", "./index.html"], + content: [ + "./src/**/*.{html,md,js,jsx,ts,tsx}", + "./apps/**/*.{html,md,js,jsx,ts,tsx}", + "./index.html", + ], plugins: [ require("@tailwindcss/forms"), require("@tailwindcss/typography"), From 2eaf15ef9688463752d63560e922ef7a8e12c3c2 Mon Sep 17 00:00:00 2001 From: Srayash Singh <146334722+Srayash@users.noreply.github.com> Date: Sun, 17 Nov 2024 20:05:33 +0530 Subject: [PATCH 02/33] Remove redundant "Add Consultation" button (#9095) --- src/components/Patient/PatientHome.tsx | 73 -------------------------- 1 file changed, 73 deletions(-) diff --git a/src/components/Patient/PatientHome.tsx b/src/components/Patient/PatientHome.tsx index 3b5924009bb..9d3ab238e5b 100644 --- a/src/components/Patient/PatientHome.tsx +++ b/src/components/Patient/PatientHome.tsx @@ -242,14 +242,6 @@ export const PatientHome = (props: any) => { ); }; - const isPatientEligibleForNewConsultation = (patientData: PatientModel) => { - return patientData.is_active && - (!patientData?.last_consultation || - patientData?.last_consultation?.discharge_date) - ? true - : false; - }; - return ( {
-
- isPatientEligibleForNewConsultation(patientData) && - navigate( - `/facility/${patientData?.facility}/patient/${id}/consultation`, - ) - } - > -
-
- - - -
- -
-

- Add Consultation -

-
-
-
navigate(`/patient/${id}/investigation_reports`)} @@ -1290,28 +1239,6 @@ export const PatientHome = (props: any) => {
-
- - navigate( - `/facility/${patientData?.facility}/patient/${id}/consultation`, - ) - } - > - - - Add Consultation - - -
Date: Mon, 18 Nov 2024 15:59:37 +0530 Subject: [PATCH 03/33] fixed the condition in cypress video artefact upload (#9149) --- .github/workflows/cypress.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index ecd06f922c0..506c2e56f66 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -116,7 +116,6 @@ jobs: group: "UI-Chrome" env: CYPRESS_SPLIT_TESTS: "true" - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max_old_space_size=4096 COMMIT_INFO_MESSAGE: ${{github.event.pull_request.title}} @@ -133,7 +132,7 @@ jobs: - name: Upload cypress videos 📹 uses: actions/upload-artifact@v3 - if: steps.pr_origin.outputs.is_forked == 'true' + if: ${{ failure() && steps.pr_origin.outputs.is_forked == 'true' }} with: name: cypress-videos path: cypress/videos \ No newline at end of file From 5a85fb3afe4a14c3e94c8b9ab64f02098c6356ff Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:24:19 +0530 Subject: [PATCH 04/33] Fixed the flaky test in user skill management cypress test (#9148) Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- cypress/e2e/users_spec/UsersManage.cy.ts | 9 ++------- cypress/pageobject/Hcx/HcxClaims.ts | 3 +++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/cypress/e2e/users_spec/UsersManage.cy.ts b/cypress/e2e/users_spec/UsersManage.cy.ts index f3a04ca4410..22da938542d 100644 --- a/cypress/e2e/users_spec/UsersManage.cy.ts +++ b/cypress/e2e/users_spec/UsersManage.cy.ts @@ -65,14 +65,9 @@ describe("Manage User", () => { manageUserPage.clicklinkedskillbutton(); manageUserPage.selectSkillFromDropdown(linkedskill); manageUserPage.clickAddSkillButton(); - manageUserPage.clickCloseSlideOver(); - cy.wait(5000); // temporary hack to fix the failure - manageUserPage.clicklinkedskillbutton(); + cy.verifyNotification("Skill added successfully"); + cy.closeNotification(); manageUserPage.assertSkillInAddedUserSkills(linkedskill); - manageUserPage.clickUnlinkSkill(); - manageUserPage.clickSubmit(); - manageUserPage.selectSkillFromDropdown(linkedskill); - manageUserPage.clickAddSkillButton(); manageUserPage.clickCloseSlideOver(); // verifying the doctor connect manageUserPage.navigateToFacility(); diff --git a/cypress/pageobject/Hcx/HcxClaims.ts b/cypress/pageobject/Hcx/HcxClaims.ts index 9a915a05196..b93862e6d13 100644 --- a/cypress/pageobject/Hcx/HcxClaims.ts +++ b/cypress/pageobject/Hcx/HcxClaims.ts @@ -1,5 +1,8 @@ export class HcxClaims { selectEligiblePolicy(policy: string) { + cy.get("#select-insurance-policy", { timeout: 10000 }) + .should("be.visible") + .and("not.be.disabled"); cy.clickAndSelectOption("#select-insurance-policy", policy); } From c4f54a84bbbe885da149fcf1799e0c5a98f8218d Mon Sep 17 00:00:00 2001 From: Jacob John Jeevan <40040905+Jacobjeevan@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:36:08 +0530 Subject: [PATCH 05/33] Respiratory support table/graph: render no data points with no mode/modality (#9139) --- .../Facility/Consultations/VentilatorPlot.tsx | 60 +++++++------------ .../Consultations/VentilatorTable.tsx | 21 ++++--- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/components/Facility/Consultations/VentilatorPlot.tsx b/src/components/Facility/Consultations/VentilatorPlot.tsx index 38948f165ea..2de4afe36a4 100644 --- a/src/components/Facility/Consultations/VentilatorPlot.tsx +++ b/src/components/Facility/Consultations/VentilatorPlot.tsx @@ -110,63 +110,47 @@ export const VentilatorPlot = ({ case "ventilator_tidal_volume": case "ventilator_peep": condition = - (currentRound.ventilator_interface === "INVASIVE" || - currentRound.ventilator_interface === "NON_INVASIVE") && - !!currentRound.ventilator_mode; + currentRound.ventilator_interface === "INVASIVE" || + currentRound.ventilator_interface === "NON_INVASIVE"; 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_interface === "INVASIVE" || + currentRound.ventilator_interface === "NON_INVASIVE" || + (currentRound.ventilator_interface === "OXYGEN_SUPPORT" && currentRound.ventilator_oxygen_modality === "HIGH_FLOW_NASAL_CANNULA"); break; + case "ventilator_spo2": + condition = currentRound.ventilator_interface !== "UNKNOWN"; + break; case "etco2": case "ventilator_oxygen_modality_flow_rate": condition = - !!currentRound.ventilator_mode || - !!currentRound.ventilator_oxygen_modality || - false; + currentRound.ventilator_interface === "OXYGEN_SUPPORT" && + currentRound.ventilator_oxygen_modality === "HIGH_FLOW_NASAL_CANNULA"; 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"); + currentRound.ventilator_oxygen_modality !== "HIGH_FLOW_NASAL_CANNULA"; break; } switch (currentRound.ventilator_interface) { case "OXYGEN_SUPPORT": - legend = - t( - `OXYGEN_MODALITY__${currentRound.ventilator_oxygen_modality}_short`, - ) + - " (" + - t("RESPIRATORY_SUPPORT_SHORT__OXYGEN_SUPPORT") + - ")"; + legend = currentRound.ventilator_oxygen_modality + ? `${t(`OXYGEN_MODALITY__${currentRound.ventilator_oxygen_modality}_short`)} (${t("RESPIRATORY_SUPPORT_SHORT__OXYGEN_SUPPORT")})` + : t("RESPIRATORY_SUPPORT_SHORT__OXYGEN_SUPPORT"); break; case "INVASIVE": - legend = - t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`) + - " (" + - t("RESPIRATORY_SUPPORT_SHORT__INVASIVE") + - ")"; + legend = currentRound.ventilator_mode + ? `${t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`)} (${t("RESPIRATORY_SUPPORT_SHORT__INVASIVE")})` + : t("RESPIRATORY_SUPPORT_SHORT__INVASIVE"); break; case "NON_INVASIVE": - legend = - t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`) + - " (" + - t("RESPIRATORY_SUPPORT_SHORT__NON_INVASIVE") + - ")"; + legend = currentRound.ventilator_mode + ? `${t(`VENTILATOR_MODE__${currentRound.ventilator_mode}_short`)} (${t("RESPIRATORY_SUPPORT_SHORT__NON_INVASIVE")})` + : t("RESPIRATORY_SUPPORT_SHORT__NON_INVASIVE"); break; } return { condition, legend }; @@ -178,9 +162,9 @@ export const VentilatorPlot = ({ switch (ventilatorInterface) { case "INVASIVE": case "NON_INVASIVE": - return round.ventilator_mode; + return round.ventilator_mode ?? "None"; case "OXYGEN_SUPPORT": - return round.ventilator_oxygen_modality; + return round.ventilator_oxygen_modality ?? "None"; default: return null; } diff --git a/src/components/Facility/Consultations/VentilatorTable.tsx b/src/components/Facility/Consultations/VentilatorTable.tsx index 2059f14bac0..9e4084cbaea 100644 --- a/src/components/Facility/Consultations/VentilatorTable.tsx +++ b/src/components/Facility/Consultations/VentilatorTable.tsx @@ -30,9 +30,13 @@ export default function VentilatorTable(props: VentilatorTableProps) { switch (ventilator_interface) { case "INVASIVE": case "NON_INVASIVE": - return t(`VENTILATOR_MODE__${ventilator_mode}`); + return ventilator_mode + ? t(`VENTILATOR_MODE__${ventilator_mode}`) + : "None"; case "OXYGEN_SUPPORT": - return t(`OXYGEN_MODALITY__${ventilator_oxygen_modality}`); + return ventilator_oxygen_modality + ? t(`OXYGEN_MODALITY__${ventilator_oxygen_modality}`) + : "None"; default: return null; } @@ -55,9 +59,9 @@ export default function VentilatorTable(props: VentilatorTableProps) { switch (ventilatorInterface) { case "INVASIVE": case "NON_INVASIVE": - return round.ventilator_mode; + return round.ventilator_mode ?? "None"; case "OXYGEN_SUPPORT": - return round.ventilator_oxygen_modality; + return round.ventilator_oxygen_modality ?? "None"; default: return null; } @@ -74,8 +78,9 @@ export default function VentilatorTable(props: VentilatorTableProps) { const nextInterfaceOrModality = getModeOrModality(nextRound); if ( nextInterfaceOrModality && - currentRound.ventilator_interface == nextRound.ventilator_interface && - currentInterfaceOrModality == nextInterfaceOrModality + currentRound.ventilator_interface === + nextRound.ventilator_interface && + currentInterfaceOrModality === nextInterfaceOrModality ) { index += 1; } else { @@ -105,6 +110,8 @@ export default function VentilatorTable(props: VentilatorTableProps) { const sortedData: DailyRoundsModel[] = dailyRoundsList.sort( compareByDateString("taken_at"), ); + const tableBody = VentilatorTableBody(sortedData); + if (!tableBody.length) return null; return (
@@ -122,7 +129,7 @@ export default function VentilatorTable(props: VentilatorTableProps) { - {VentilatorTableBody(sortedData)} + {tableBody}
); From 294f6b03a1313bf31f840111a4b5c717626f2ed9 Mon Sep 17 00:00:00 2001 From: Nithish Kumar Siliveru Date: Mon, 18 Nov 2024 19:04:31 +0530 Subject: [PATCH 06/33] fixed doctor calling button size and added types (#9130) --- public/locale/en.json | 1 + src/components/Patient/ManagePatients.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 36c25a06682..21fa1bea6ba 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -905,6 +905,7 @@ "no_log_update_delta": "No changes since previous log update", "no_log_updates": "No log updates found", "no_notices_for_you": "No notices for you.", + "no_patients_found": "No Patients Found", "no_patients_to_show": "No patients to show.", "no_policy_added": "No Insurance Policy Added", "no_policy_found": "No Insurance Policy Found for this Patient", diff --git a/src/components/Patient/ManagePatients.tsx b/src/components/Patient/ManagePatients.tsx index d42f0072794..0664532b0a1 100644 --- a/src/components/Patient/ManagePatients.tsx +++ b/src/components/Patient/ManagePatients.tsx @@ -392,7 +392,7 @@ export const PatientManager = () => { ); const LastAdmittedToTypeBadges = () => { - const badge = (key: string, value: any, id: string) => { + const badge = (key: string, value: string | undefined, id: string) => { return ( value && ( { }; const HasConsentTypesBadges = () => { - const badge = (key: string, value: any, id: string) => { + const badge = (key: string, value: string | undefined, id: string) => { return ( value && ( { managePatients = (

- No Patients Found + {t("no_patients_found")}

); @@ -875,6 +875,7 @@ export const PatientManager = () => { /> {!!params.facility && ( { triggerGoal("Doctor Connect Clicked", { From a8beedcf981089650a82bf120a287981d765ff7a Mon Sep 17 00:00:00 2001 From: Anvesh Nalimela <151531961+AnveshNalimela@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:06:06 +0530 Subject: [PATCH 07/33] Design of List View layout in Resource Page (#9096) --- public/locale/en.json | 4 + src/components/Resource/ResourceList.tsx | 174 ++++++++++++++++++++--- 2 files changed, 160 insertions(+), 18 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 21fa1bea6ba..e281d8239bf 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -5,6 +5,7 @@ "APPETITE__NO_TASTE_FOR_FOOD": "No taste for food", "APPETITE__REDUCED": "Reduced", "APPETITE__SATISFACTORY": "Satisfactory", + "APPROVED": "Approved", "AUTOMATED": "Automated", "BED_WITH_OXYGEN_SUPPORT": "Bed with Oxygen Support", "BLADDER_DRAINAGE__CONDOM_CATHETER": "Condom Catheter", @@ -140,6 +141,7 @@ "OXYGEN_MODALITY__NON_REBREATHING_MASK_short": "NRM", "OXYGEN_MODALITY__SIMPLE_FACE_MASK": "Simple Face Mask", "OXYGEN_MODALITY__SIMPLE_FACE_MASK_short": "SFM", + "PENDING": "Pending", "PRESCRIPTION_FREQUENCY_BD": "Twice daily", "PRESCRIPTION_FREQUENCY_HS": "Night only", "PRESCRIPTION_FREQUENCY_OD": "Once daily", @@ -205,6 +207,7 @@ "SORT_OPTIONS__taken_at": "Oldest taken date first", "Submit": "Submit", "TELEMEDICINE": "Telemedicine", + "TRANSPORTATION TO BE ARRANGED": "Transportation", "URINATION_FREQUENCY__DECREASED": "Decreased", "URINATION_FREQUENCY__INCREASED": "Increased", "URINATION_FREQUENCY__NORMAL": "Normal", @@ -1103,6 +1106,7 @@ "resource_approving_facility": "Resource approving facility", "resource_origin_facility": "Origin Facility", "resource_request": "Resource Request", + "resource_status": "Resource Status", "result": "Result", "result_date": "Result Date", "result_details": "Result details", diff --git a/src/components/Resource/ResourceList.tsx b/src/components/Resource/ResourceList.tsx index 8a12545d7d1..6eecf7fcef2 100644 --- a/src/components/Resource/ResourceList.tsx +++ b/src/components/Resource/ResourceList.tsx @@ -1,6 +1,7 @@ -import { navigate } from "raviger"; +import { Link, navigate } from "raviger"; import { useTranslation } from "react-i18next"; +import Chip from "@/CAREUI/display/Chip"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; @@ -11,7 +12,6 @@ import Page from "@/components/Common/Page"; import { ResourceModel } from "@/components/Facility/models"; import SearchInput from "@/components/Form/SearchInput"; import BadgesList from "@/components/Resource/ResourceBadges"; -import ResourceBlock from "@/components/Resource/ResourceBlock"; import { formatFilter } from "@/components/Resource/ResourceCommons"; import ListFilter from "@/components/Resource/ResourceFilter"; @@ -20,6 +20,7 @@ import useFilters from "@/hooks/useFilters"; import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useQuery from "@/Utils/request/useQuery"; +import { formatDateTime } from "@/Utils/utils"; export default function ListView() { const { @@ -48,15 +49,140 @@ export default function ListView() { const showResourceCardList = (data: ResourceModel[]) => { if (data && !data.length) { return ( -
+
{t("no_results_found")}
); } - return data.map((resource, i) => ( -
- + return data.map((resource: ResourceModel, i) => ( +
+
+
+
{resource.title}
+
+ +
+
+
+ +
+ {resource.category || ""} +
+ +
+
+
+ {resource.sub_category || "--"} +
+
+
+ +
+
+ {resource.status === "TRANSPORTATION TO BE ARRANGED" ? ( +
+ +
+ ) : ( +
+ +
+ )} + +
+ {resource.emergency && ( + + {t("emergency")} + + )} +
+
+ +
+
+ +
+ {formatDateTime(resource.modified_date) || "--"} +
+ +
+
+ +
+
+ +
+ {resource.origin_facility_object?.name} +
+ + +
+ +
+ {resource.approving_facility_object?.name} +
+ + +
+ +
+ {resource.assigned_facility_object?.name || + t("yet_to_be_decided")} +
+ +
+
+ + {t("all_details")} + +
+
)); }; @@ -79,7 +205,8 @@ export default function ListView() { breadcrumbs={false} options={ <> -
+
+
-
- {/* dummy div to align space as per board view */} -
-
- - - {t("board_view")} - +
advancedFilter.setShow(true)} /> + + + {t("board_view")} +
} @@ -123,10 +247,24 @@ export default function ListView() { {t("refresh_list")}
- -
- {data?.results && showResourceCardList(data?.results)} +
+
+ {t("resource")} +
+
+ {t("LOG_UPDATE_FIELD_LABEL__patient_category")} +
+
+ {t("consent__status")} +
+
+ {t("facilities")} +
+
+ {t("LOG_UPDATE_FIELD_LABEL__action")} +
+
{showResourceCardList(data?.results || [])}
From e49c274892e6e7d5a1263c0a59507efbe7cacbea Mon Sep 17 00:00:00 2001 From: Srayash Singh <146334722+Srayash@users.noreply.github.com> Date: Mon, 18 Nov 2024 19:06:36 +0530 Subject: [PATCH 08/33] disable "Save Button" if no file is selected (#9111) --- src/components/Common/AvatarEditModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Common/AvatarEditModal.tsx b/src/components/Common/AvatarEditModal.tsx index 38b54b1f1d2..bcc61819fde 100644 --- a/src/components/Common/AvatarEditModal.tsx +++ b/src/components/Common/AvatarEditModal.tsx @@ -292,7 +292,7 @@ const AvatarEditModal = ({ {isProcessing ? ( Date: Mon, 18 Nov 2024 19:11:14 +0530 Subject: [PATCH 09/33] Fixed:Show more monitors in CNS on very large displays #6503 (#9013) Co-authored-by: Rithvik Nishad Co-authored-by: rithviknishad --- .../Facility/CentralNursingStation.tsx | 50 ++++++++++--------- src/hooks/useBreakpoints.ts | 4 +- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/components/Facility/CentralNursingStation.tsx b/src/components/Facility/CentralNursingStation.tsx index 6c5d1381064..f5c17b52360 100644 --- a/src/components/Facility/CentralNursingStation.tsx +++ b/src/components/Facility/CentralNursingStation.tsx @@ -21,14 +21,13 @@ import HL7PatientVitalsMonitor from "@/components/VitalsMonitor/HL7PatientVitals import useVitalsAspectRatioConfig from "@/components/VitalsMonitor/useVitalsAspectRatioConfig"; import { getVitalsMonitorSocketUrl } from "@/components/VitalsMonitor/utils"; +import useBreakpoints from "@/hooks/useBreakpoints"; import useFilters from "@/hooks/useFilters"; import useFullscreen from "@/hooks/useFullscreen"; import routes from "@/Utils/request/api"; import useQuery from "@/Utils/request/useQuery"; -const PER_PAGE_LIMIT = 6; - const SORT_OPTIONS: SortOption[] = [ { isAscending: true, value: "bed__name" }, { isAscending: false, value: "-bed__name" }, @@ -41,31 +40,34 @@ interface Props { } export default function CentralNursingStation({ facilityId }: Props) { + const perPageLimit = useBreakpoints({ + default: 6, + "4xl": 9, + "4k": 24, + }); const { t } = useTranslation(); const [isFullscreen, setFullscreen] = useFullscreen(); const { qParams, updateQuery, removeFilter, updatePage } = useFilters({ - limit: PER_PAGE_LIMIT, + limit: perPageLimit, }); const query = useQuery(routes.listPatientAssetBeds, { pathParams: { facility_external_id: facilityId }, query: { ...qParams, page: qParams.page || 1, - limit: PER_PAGE_LIMIT, - offset: (qParams.page ? qParams.page - 1 : 0) * PER_PAGE_LIMIT, + limit: perPageLimit, + offset: (qParams.page ? qParams.page - 1 : 0) * perPageLimit, asset_class: "HL7MONITOR", ordering: qParams.ordering || "bed__name", bed_is_occupied: qParams.monitors_without_patient === "true" ? undefined : "true", }, }); - const totalCount = query.data?.count ?? 0; const data = query.data?.results.map((obj) => ({ patientAssetBed: obj, socketUrl: getVitalsMonitorSocketUrl(obj.asset), })); - const { config, hash } = useVitalsAspectRatioConfig({ default: 6 / 11, sm: 17 / 11, @@ -88,7 +90,7 @@ export default function CentralNursingStation({ facilityId }: Props) { updatePage(page)} /> @@ -216,21 +218,23 @@ export default function CentralNursingStation({ facilityId }: Props) { {t("no_vitals_present")}
) : ( -
- {data.map((props, i) => ( -
- -
- ))} +
+
+ {data.map((props, i) => ( +
+ +
+ ))} +
)} diff --git a/src/hooks/useBreakpoints.ts b/src/hooks/useBreakpoints.ts index bba811ab88e..2b36e4664eb 100644 --- a/src/hooks/useBreakpoints.ts +++ b/src/hooks/useBreakpoints.ts @@ -1,6 +1,6 @@ import useWindowDimensions from "@/hooks/useWindowDimensions"; -type Breakpoints = "sm" | "md" | "lg" | "xl" | "2xl" | "3xl"; +type Breakpoints = "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "4k"; // Ensure that the breakpoint widths are sorted in ascending order. const BREAKPOINT_WIDTH: Record = { @@ -10,6 +10,8 @@ const BREAKPOINT_WIDTH: Record = { xl: 1280, "2xl": 1536, "3xl": 1920, + "4xl": 2560, + "4k": 3840, }; /** From b17eb221aac972911ef6c3ae15cdc84c837b469a Mon Sep 17 00:00:00 2001 From: JavidSumra <112365664+JavidSumra@users.noreply.github.com> Date: Tue, 19 Nov 2024 06:26:17 +0530 Subject: [PATCH 10/33] Add Cypress Test Suite for Sample Test Request Workflow (#8977) Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- .../e2e/facility_spec/FacilityHomepage.cy.ts | 4 +- .../e2e/facility_spec/FacilityLocation.cy.ts | 4 +- cypress/e2e/hcx_spec/HcxClaims.cy.ts | 4 +- .../PatientConsultationCreation.cy.ts | 22 ++-- .../PatientConsultationDischarge.cy.ts | 18 +-- .../patient_spec/PatientInvestigation.cy.ts | 2 +- .../e2e/patient_spec/PatientLogUpdate.cy.ts | 40 +++--- .../patient_spec/PatientPrescription.cy.ts | 26 ++-- .../sample_test_spec/SampleTestRequest.cy.ts | 88 ++++++++++++++ cypress/e2e/users_spec/UsersManage.cy.ts | 1 + .../pageobject/Facility/FacilityCreation.ts | 2 +- cypress/pageobject/Login/LoginPage.ts | 4 +- .../pageobject/Patient/PatientConsultation.ts | 8 ++ cypress/pageobject/Patient/PatientCreation.ts | 4 +- .../pageobject/Patient/PatientFileupload.ts | 4 +- cypress/pageobject/Sample/SampleTestCreate.ts | 114 ++++++++++++++++++ cypress/support/commands.ts | 7 +- cypress/support/index.ts | 3 +- src/components/Patient/PatientHome.tsx | 1 + src/components/Patient/SampleDetails.tsx | 16 ++- src/components/Patient/SampleTest.tsx | 33 ++++- src/components/Patient/SampleTestCard.tsx | 21 +++- src/components/Patient/SampleViewAdmin.tsx | 7 +- 23 files changed, 350 insertions(+), 83 deletions(-) create mode 100644 cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts create mode 100644 cypress/pageobject/Sample/SampleTestCreate.ts diff --git a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts index 08ea39ee2c9..2c5699ec76c 100644 --- a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts +++ b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts @@ -152,7 +152,7 @@ describe("Facility Homepage Function", () => { facilityNotify.verifyFacilityName(facilityName); facilityNotify.fillNotifyText(notificationMessage); facilityNotify.interceptPostNotificationReq(); - cy.submitButton("Notify"); + cy.clickSubmitButton("Notify"); facilityNotify.verifyPostNotificationReq(); cy.verifyNotification("Facility Notified"); cy.closeNotification(); @@ -160,7 +160,7 @@ describe("Facility Homepage Function", () => { // Verify the frontend error on empty message facilityHome.clickFacilityNotifyButton(); facilityNotify.verifyFacilityName(facilityName); - cy.submitButton("Notify"); + cy.clickSubmitButton("Notify"); facilityNotify.verifyErrorMessage(notificationErrorMsg); // close pop-up and verify facilityHome.verifyAndCloseNotifyModal(); diff --git a/cypress/e2e/facility_spec/FacilityLocation.cy.ts b/cypress/e2e/facility_spec/FacilityLocation.cy.ts index b700f5c75ae..7d0c626a93b 100644 --- a/cypress/e2e/facility_spec/FacilityLocation.cy.ts +++ b/cypress/e2e/facility_spec/FacilityLocation.cy.ts @@ -194,14 +194,14 @@ describe("Location Management Section", () => { facilityLocation.clickAddNewLocationButton(); facilityLocation.enterLocationName("Test Location with Beds"); facilityLocation.selectLocationType("OTHER"); - cy.submitButton("Add Location"); + cy.clickSubmitButton("Add Location"); cy.verifyNotification("Location created successfully"); cy.closeNotification(); facilityLocation.clickManageBedButton(); facilityLocation.clickAddBedButton(); facilityLocation.enterBedName("Bed 1"); facilityLocation.selectBedType("Regular"); - cy.submitButton("Add Bed(s)"); + cy.clickSubmitButton("Add Bed(s)"); cy.verifyNotification("1 Bed created successfully"); cy.closeNotification(); facilityLocation.loadLocationManagementPage("Dummy Shifting Center"); diff --git a/cypress/e2e/hcx_spec/HcxClaims.cy.ts b/cypress/e2e/hcx_spec/HcxClaims.cy.ts index 2c4a6b45df9..869afd6fcbb 100644 --- a/cypress/e2e/hcx_spec/HcxClaims.cy.ts +++ b/cypress/e2e/hcx_spec/HcxClaims.cy.ts @@ -48,7 +48,7 @@ describe("HCX Claims configuration and approval workflow", () => { firstInsuranceIdentifier, patientInsurerName, ); - cy.submitButton("Save Details"); + cy.clickSubmitButton("Save Details"); cy.verifyNotification("Patient updated successfully"); cy.closeNotification(); // Navigate to Consultation View and capture dynamic consultation ID @@ -80,6 +80,8 @@ describe("HCX Claims configuration and approval workflow", () => { // Raise a HCX Pre-auth patientConsultationPage.clickManagePatientButton(); patientConsultationPage.clickClaimsButton(); + cy.verifyAndClickElement("#edit-insurance-policy", "Edit Insurance Policy"); + cy.clickCancelButton(); hcxClaims.selectEligiblePolicy(patientInsurerName); hcxClaims.verifyPolicyEligibility(); cy.verifyNotification("Checking Policy Eligibility"); diff --git a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts index 4c84f7fad8f..bd6e7c0d967 100644 --- a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts +++ b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts @@ -91,11 +91,11 @@ describe("Patient Consultation in multiple combination", () => { patientTreatmentPlan.typePatientGeneralInstruction(generalInstruction); patientTreatmentPlan.typeSpecialInstruction(specialInstruction); patientTreatmentPlan.fillTreatingPhysican(doctorName); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); // the above submit should fail as IP number is missing patientConsultationPage.typePatientNumber(patientIpNumber); patientConsultationPage.selectBed("Dummy Bed 6"); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); cy.verifyNotification("Consultation created successfully"); // Below code for the prescription module only present while creating a new consultation patientPrescription.clickAddPrescription(); @@ -104,7 +104,7 @@ describe("Patient Consultation in multiple combination", () => { patientPrescription.selectMedicine(medicineOne); patientPrescription.enterDosage("3"); patientPrescription.selectDosageFrequency("Twice daily"); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.wait(2000); cy.verifyNotification("Medicine prescribed"); patientPrescription.clickReturnToDashboard(); @@ -189,7 +189,7 @@ describe("Patient Consultation in multiple combination", () => { "#encounter_date", "220220241230", ); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); cy.verifyNotification( "Create Diagnoses - Atleast one diagnosis is required", ); @@ -198,7 +198,7 @@ describe("Patient Consultation in multiple combination", () => { diagnosis4, "add-icd11-diagnosis-as-confirmed", ); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); cy.verifyNotification("Consultation created successfully"); // verify the data and death report patientConsultationPage.verifyTextInConsultation( @@ -215,7 +215,7 @@ describe("Patient Consultation in multiple combination", () => { "#cause_of_death", "Cause of Death", ); - cy.submitButton("Preview"); + cy.clickSubmitButton("Preview"); cy.preventPrint(); patientDeathReport.clickPrintDeathReport(); cy.get("@verifyPrevent").should("be.called"); @@ -261,7 +261,7 @@ describe("Patient Consultation in multiple combination", () => { // add telemedicine patientTreatmentPlan.clickTelemedicineCheckbox(); patientTreatmentPlan.assignTelemedicineDoctor(doctorName); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); cy.verifyNotification("Consultation created successfully"); // verify the data reflection - patientConsultationPage.verifyTextInConsultation( @@ -312,7 +312,7 @@ describe("Patient Consultation in multiple combination", () => { patientTreatmentPlan.typePatientGeneralInstruction(generalInstruction); // no review after and no action patientTreatmentPlan.fillTreatingPhysican(doctorName); - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); cy.verifyNotification("Patient discharged successfully"); // verify the Discharge Reason, Diagnosis, treatment physican patientConsultationPage.verifyTextInConsultation( @@ -364,13 +364,13 @@ describe("Patient Consultation in multiple combination", () => { patientTreatmentPlan.typePatientGeneralInstruction(generalInstruction); patientTreatmentPlan.fillTreatingPhysican(doctorName); // no review after and no action - cy.submitButton("Create Consultation"); + cy.clickSubmitButton("Create Consultation"); // Create a shifting request cy.closeNotification(); shiftCreation.typeCurrentFacilityPerson("Current Facility Person"); shiftCreation.typeCurrentFacilityPhone("9999999999"); shiftCreation.typeShiftReason("reason for shift"); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Shift request created successfully"); }); @@ -386,7 +386,7 @@ describe("Patient Consultation in multiple combination", () => { cy.get("#condition-verification-status-menu").click(); cy.get("#add-icd11-diagnosis-as-entered-in-error").click(); }); - cy.submitButton("Update Consultation"); + cy.clickSubmitButton("Update Consultation"); cy.verifyNotification("Consultation updated successfully"); cy.get("#diagnoses-view").should("not.contain.text", diagnosis5); patientConsultationPage.verifyTextInConsultation( diff --git a/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts b/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts index e2403fd56bd..6a3d2863818 100644 --- a/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts +++ b/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts @@ -34,8 +34,8 @@ describe("Patient Discharge based on multiple reason", () => { patientPage.visitPatient("Dummy Patient 12"); patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason4); - cy.submitButton("Confirm Discharge"); - cy.submitButton("Acknowledge & Submit"); + cy.clickSubmitButton("Confirm Discharge"); + cy.clickSubmitButton("Acknowledge & Submit"); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); // Verify the consultation dashboard reflection @@ -52,8 +52,8 @@ describe("Patient Discharge based on multiple reason", () => { patientDischarge.selectDischargeReason(patientDischargeReason3); patientDischarge.typeDischargeNote(patientDeathCause); patientDischarge.typeDoctorName(doctorName); - cy.submitButton("Confirm Discharge"); - cy.submitButton("Acknowledge & Submit"); + cy.clickSubmitButton("Confirm Discharge"); + cy.clickSubmitButton("Acknowledge & Submit"); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); // Verify the consultation dashboard reflection @@ -77,8 +77,8 @@ describe("Patient Discharge based on multiple reason", () => { // select a non-registered facility and perform the discharge patientDischarge.typeReferringFacility(referringFreetextFacility); cy.wait(2000); - cy.submitButton("Confirm Discharge"); - cy.submitButton("Acknowledge & Submit"); + cy.clickSubmitButton("Confirm Discharge"); + cy.clickSubmitButton("Acknowledge & Submit"); cy.wait(2000); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); @@ -104,13 +104,13 @@ describe("Patient Discharge based on multiple reason", () => { patientPrescription.selectMedicine(patientMedicine); patientPrescription.enterDosage("4"); patientPrescription.selectDosageFrequency("Twice daily"); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.wait(2000); cy.closeNotification(); // submit the discharge pop-up - cy.submitButton("Confirm Discharge"); - cy.submitButton("Acknowledge & Submit"); + cy.clickSubmitButton("Confirm Discharge"); + cy.clickSubmitButton("Acknowledge & Submit"); cy.wait(2000); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); diff --git a/cypress/e2e/patient_spec/PatientInvestigation.cy.ts b/cypress/e2e/patient_spec/PatientInvestigation.cy.ts index 7426890d5a6..262759bd360 100644 --- a/cypress/e2e/patient_spec/PatientInvestigation.cy.ts +++ b/cypress/e2e/patient_spec/PatientInvestigation.cy.ts @@ -28,7 +28,7 @@ describe("Patient Investigation Creation from Patient consultation page", () => "Haematology", "Urine Test", ]); - cy.submitButton("Save Investigation"); + cy.clickSubmitButton("Save Investigation"); cy.verifyNotification("Please Enter at least one value"); cy.closeNotification(); // Temporary workflow for investigation since we dont have dummy data and moving away from existing module diff --git a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts index b6399ff02ce..05958bdf3f0 100644 --- a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts +++ b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts @@ -55,7 +55,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientPage.visitPatient(domicilaryPatient); patientConsultationPage.clickEditConsultationButton(); patientConsultationPage.selectPatientSuggestion("Domiciliary Care"); - cy.submitButton("Update Consultation"); + cy.clickSubmitButton("Update Consultation"); cy.verifyNotification("Consultation updated successfully"); cy.closeNotification(); patientLogupdate.clickLogupdate(); @@ -74,7 +74,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectRhythm(patientRhythmType); patientLogupdate.typeRhythm(patientRhythm); cy.get("#consciousness_level-option-RESPONDS_TO_PAIN").click(); - cy.submitButton("Save"); + cy.clickSubmitButton("Save"); cy.verifyNotification("Tele-medicine Log created successfully"); }); @@ -108,11 +108,11 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientPrescription.selectMedicine("DOLO"); patientPrescription.enterDosage("4"); patientPrescription.selectDosageFrequency("Twice daily"); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); // Submit the doctors log update - cy.submitButton("Save and Continue"); + cy.clickSubmitButton("Save and Continue"); cy.wait(2000); cy.verifyNotification("Progress Note created successfully"); cy.closeNotification(); @@ -120,13 +120,13 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectCriticalCareSection("Neurological Monitoring"); cy.get("#consciousness_level-option-RESPONDS_TO_PAIN").click(); cy.get("#left_pupil_light_reaction-option-FIXED").click(); - cy.submitButton("Update Details"); + cy.clickSubmitButton("Update Details"); cy.verifyNotification( "Neurological Monitoring details succesfully updated.", ); cy.closeNotification(); // Final Submission of the form - cy.submitButton("Complete"); + cy.clickSubmitButton("Complete"); cy.verifyNotification("Progress Note Log Update filed successfully"); cy.closeNotification(); // Verify the data reflection @@ -143,7 +143,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.clickUpdateDetail(); patientLogupdate.typeSystolic(patientModifiedSystolic); patientLogupdate.typeDiastolic(patientModifiedDiastolic); - cy.submitButton("Continue"); + cy.clickSubmitButton("Continue"); cy.verifyNotification("Progress Note updated successfully"); }); @@ -156,7 +156,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.clickLogupdate(); patientLogupdate.selectRoundType("Detailed Update"); patientLogupdate.selectPatientCategory(patientCategory); - cy.submitButton("Save and Continue"); + cy.clickSubmitButton("Save and Continue"); cy.verifyNotification("Detailed Update created successfully"); cy.closeNotification(); // Select two Section - First One is Respiratory Support @@ -166,7 +166,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectOxygenSupport(); patientLogupdate.typeOxygenFlowRate(patientOxygenFlowRate); patientLogupdate.typeVentilatorSpo2(patientSpo2); - cy.submitButton("Update Details"); + cy.clickSubmitButton("Update Details"); cy.verifyNotification("Respiratory Support details succesfully updated."); cy.closeNotification(); // Second Section will be Blood Sugar @@ -174,10 +174,10 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.typeBloodSugar(patientBloodSugar); patientLogupdate.typeInsulinDosage(patientInsulinDosage); cy.get("#insulin_intake_frequency-option-BD").click(); - cy.submitButton("Update Details"); + cy.clickSubmitButton("Update Details"); cy.verifyNotification("Blood Sugar details succesfully updated."); // Submit the form and verify the details - cy.submitButton("Complete"); + cy.clickSubmitButton("Complete"); cy.verifyNotification("Detailed Log Update filed successfully"); cy.closeNotification(); cy.contains("button", "Log Updates").click(); @@ -203,10 +203,10 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectCriticalCareSection("Dialysis"); patientLogupdate.typeFluidBalance(patientFluidBalance); patientLogupdate.typeNetBalance(patientNetBalance); - cy.submitButton("Update Details"); + cy.clickSubmitButton("Update Details"); cy.verifyNotification("Dialysis details succesfully updated."); cy.closeNotification(); - cy.submitButton("Complete"); + cy.clickSubmitButton("Complete"); cy.verifyNotification("Detailed Log Update filed successfully"); cy.closeNotification(); //Reverify the editted and newly added data @@ -251,7 +251,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectRhythm(patientRhythmType); patientLogupdate.typeRhythm(patientRhythm); cy.get("#consciousness_level-option-RESPONDS_TO_PAIN").click(); - cy.submitButton("Save"); + cy.clickSubmitButton("Save"); cy.wait(2000); cy.verifyNotification("Brief Update created successfully"); // Verify the card content @@ -263,7 +263,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientPage.visitPatient(domicilaryPatient); patientConsultationPage.clickEditConsultationButton(); patientConsultationPage.selectPatientSuggestion("Domiciliary Care"); - cy.submitButton("Update Consultation"); + cy.clickSubmitButton("Update Consultation"); cy.verifyNotification("Consultation updated successfully"); cy.closeNotification(); patientLogupdate.clickLogupdate(); @@ -281,7 +281,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.selectRhythm(patientRhythmType); patientLogupdate.typeRhythm(patientRhythm); cy.get("#consciousness_level-option-RESPONDS_TO_PAIN").click(); - cy.submitButton("Save"); + cy.clickSubmitButton("Save"); cy.verifyNotification("Brief Update created successfully"); cy.closeNotification(); // edit the card and verify the data. @@ -307,7 +307,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.typeSystolic(patientModifiedSystolic); patientLogupdate.clearIntoElementById("#diastolic"); patientLogupdate.typeDiastolic(patientModifiedDiastolic); - cy.submitButton("Continue"); + cy.clickSubmitButton("Continue"); cy.verifyNotification("Brief Update updated successfully"); cy.contains("button", "Log Updates").click(); patientLogupdate.clickLogUpdateViewDetails( @@ -324,7 +324,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientPage.visitPatient(domicilaryPatient); patientConsultationPage.clickEditConsultationButton(); patientConsultationPage.selectPatientSuggestion("Domiciliary Care"); - cy.submitButton("Update Consultation"); + cy.clickSubmitButton("Update Consultation"); cy.verifyNotification("Consultation updated successfully"); cy.closeNotification(); patientLogupdate.clickLogupdate(); @@ -336,7 +336,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.typeTemperature(patientTemperature); patientLogupdate.typeRespiratory(patientRespiratory); cy.get("#consciousness_level-option-RESPONDS_TO_PAIN").click(); - cy.submitButton("Save"); + cy.clickSubmitButton("Save"); cy.verifyNotification("Brief Update created successfully"); cy.closeNotification(); cy.verifyContentPresence("#consultation-buttons", ["9"]); @@ -346,7 +346,7 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { patientLogupdate.typeSystolic(patientSystolic); patientLogupdate.typeDiastolic(patientDiastolic); patientLogupdate.typePulse(patientPulse); - cy.submitButton("Save"); + cy.clickSubmitButton("Save"); cy.verifyNotification("Brief Update created successfully"); cy.closeNotification(); cy.verifyContentPresence("#consultation-buttons", ["-"]); diff --git a/cypress/e2e/patient_spec/PatientPrescription.cy.ts b/cypress/e2e/patient_spec/PatientPrescription.cy.ts index 53e67324199..1011c2f4f4d 100644 --- a/cypress/e2e/patient_spec/PatientPrescription.cy.ts +++ b/cypress/e2e/patient_spec/PatientPrescription.cy.ts @@ -36,7 +36,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.selectMedicine(medicineNameOne); patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.selectDosageFrequency(medicineFrequency); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); // Edit the existing medicine & Verify they are properly moved to discontinue position @@ -44,16 +44,16 @@ describe("Patient Medicine Administration", () => { patientPrescription.visitMedicineTab(); cy.verifyAndClickElement("#0", medicineNameOne); cy.verifyContentPresence("#submit", ["Discontinue"]); // To verify the pop-up is open - cy.submitButton("Edit"); + cy.clickSubmitButton("Edit"); patientPrescription.enterDosage(medicineTargetDosage); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Prescription edited successfully"); cy.closeNotification(); // Discontinue a medicine & Verify the notification cy.verifyAndClickElement("#0", medicineNameOne); - cy.submitButton("Discontinue"); + cy.clickSubmitButton("Discontinue"); patientPrescription.enterDiscontinueReason("Medicine is been discontinued"); - cy.submitButton("Confirm Discontinue"); + cy.clickSubmitButton("Confirm Discontinue"); cy.verifyNotification("Prescription discontinued"); cy.closeNotification(); // verify the discontinue medicine view @@ -73,7 +73,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.selectMedicine(medicineNameOne); patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.enterIndicator(medicineIndicator); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); // Add Second Medicine @@ -83,7 +83,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.selectMedicine(medicineNameTwo); patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.enterIndicator(medicineIndicator); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); patientPrescription.clickReturnToDashboard(); @@ -108,7 +108,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.enterTargetDosage(medicineTargetDosage); patientPrescription.selectDosageFrequency(medicineFrequency); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); // Administer the medicine in edit form @@ -116,7 +116,7 @@ describe("Patient Medicine Administration", () => { cy.wait(2000); patientPrescription.enterAdministerDosage(medicineBaseDosage); patientPrescription.enterAdministerNotes(medicineAdministerNote); - cy.submitButton("Administer Medicine"); + cy.clickSubmitButton("Administer Medicine"); cy.verifyNotification("Medicine(s) administered"); cy.closeNotification(); // Verify the Reflection on the Medicine @@ -129,9 +129,9 @@ describe("Patient Medicine Administration", () => { // Go to medicine tab and administer it again patientPrescription.visitMedicineTab(); cy.verifyAndClickElement("#0", medicineNameOne); - cy.submitButton("Administer"); + cy.clickSubmitButton("Administer"); patientPrescription.enterAdministerDosage(medicineBaseDosage); - cy.submitButton("Administer Medicine"); + cy.clickSubmitButton("Administer Medicine"); cy.verifyNotification("Medicine(s) administered"); }); @@ -145,7 +145,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.selectMedicine(medicineNameOne); patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.selectDosageFrequency(medicineFrequency); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); // verify the duplicate medicine error message @@ -155,7 +155,7 @@ describe("Patient Medicine Administration", () => { patientPrescription.selectMedicine(medicineNameOne); patientPrescription.enterDosage(medicineBaseDosage); patientPrescription.selectDosageFrequency(medicineFrequency); - cy.submitButton("Submit"); + cy.clickSubmitButton("Submit"); cy.verifyNotification( "Medicine - This medicine is already prescribed to this patient. Please discontinue the existing prescription to prescribe again.", ); diff --git a/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts b/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts new file mode 100644 index 00000000000..a0f30ebd6db --- /dev/null +++ b/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts @@ -0,0 +1,88 @@ +import LoginPage from "pageobject/Login/LoginPage"; +import { PatientConsultationPage } from "pageobject/Patient/PatientConsultation"; +import { PatientPage } from "pageobject/Patient/PatientCreation"; +import { SampleTestPage } from "pageobject/Sample/SampleTestCreate"; + +describe("Sample Test", () => { + const sampleTestPage = new SampleTestPage(); + const patientPage = new PatientPage(); + const loginPage = new LoginPage(); + const patientConsultationPage = new PatientConsultationPage(); + const patientName = "Dummy Patient 11"; + const sampleTestType = "BA/ETA"; + const icmrCategory = "Cat 0"; + const icmrLabel = "Test Icmr Label"; + const doctorName = "Dr John Doe"; + const atypicalDetails = "Patient showing unusual symptoms"; + const diagnosis = "Suspected respiratory infection"; + const etiologyIdentified = "Bacterial infection suspected"; + const differentialDiagnosis = "Possibly a viral infection"; + const fastTrackReason = + "The patient has a high risk of complications and requires immediate testing."; + const sampleTestStatus = "Request Submitted"; + const expectedSampleTestType = "ba/eta"; + const sampleTestResult = "Awaiting"; + + before(() => { + loginPage.loginAsDistrictAdmin(); + cy.saveLocalStorage(); + }); + + beforeEach(() => { + cy.restoreLocalStorage(); + cy.clearLocalStorage(/filters--.+/); + }); + + it("should request a new sample test", () => { + // Ensure patient list API is loaded before proceeding + cy.awaitUrl("/patients"); + patientPage.visitPatient(patientName); + patientConsultationPage.interceptPatientDetailsAPI(); + patientConsultationPage.clickPatientDetails(); + patientConsultationPage.verifyPatientDetailsResponse(); + // Visit SampleRequest Page + sampleTestPage.visitSampleRequestPage(); + // Fill Sample Test Request Form + sampleTestPage.selectSampleType(sampleTestType); + sampleTestPage.selectIcmrCategory(icmrCategory); + sampleTestPage.fillIcmrLabel(icmrLabel); + sampleTestPage.fillFastTrackReason(fastTrackReason); + sampleTestPage.fillDoctorName(doctorName); + sampleTestPage.fillAtypicalPresentation(atypicalDetails); + sampleTestPage.fillDiagnosis(diagnosis); + sampleTestPage.fillEtiology(etiologyIdentified); + sampleTestPage.fillDiffDiagnosis(differentialDiagnosis); + sampleTestPage.checkHasSari(); + sampleTestPage.checkHasAri(); + sampleTestPage.checkIsUnusualCourse(); + sampleTestPage.interceptSampleTestReq(); + // Submit the form and verify notification + cy.clickSubmitButton("Confirm your request to send sample for testing"); + sampleTestPage.verifySampleTestReq(); + cy.verifyNotification("Sample test created successfully"); + // Check the updated request history + sampleTestPage.checkRequestHistory( + sampleTestStatus, + expectedSampleTestType, + fastTrackReason, + sampleTestResult, + ); + // Checking Reflection on Sample Page + cy.awaitUrl("/sample"); + sampleTestPage.interceptGetSampleTestReq(); + sampleTestPage.searchPatientSample(patientName); + sampleTestPage.verifyGetSampleTestReq(); + sampleTestPage.verifyPatientName(patientName); + sampleTestPage.interceptGetSampleTestReq(); + sampleTestPage.clickOnSampleDetailsBtn(); + sampleTestPage.verifyGetSampleTestReq(); + sampleTestPage.verifyPatientTestDetails( + patientName, + fastTrackReason, + doctorName, + diagnosis, + differentialDiagnosis, + etiologyIdentified, + ); + }); +}); diff --git a/cypress/e2e/users_spec/UsersManage.cy.ts b/cypress/e2e/users_spec/UsersManage.cy.ts index 22da938542d..40d436be0a6 100644 --- a/cypress/e2e/users_spec/UsersManage.cy.ts +++ b/cypress/e2e/users_spec/UsersManage.cy.ts @@ -60,6 +60,7 @@ describe("Manage User", () => { advanceFilters.clickAdvancedFiltersButton(); userPage.typeInFirstName(firstNameUserSkill); userPage.typeInLastName(lastNameUserSkill); + userPage.selectHomeFacility(facilitytolinkskill); advanceFilters.applySelectedFilter(); userPage.checkUsernameText(usernameToLinkSkill); manageUserPage.clicklinkedskillbutton(); diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index 6ce0cc6270e..908431e3a65 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -276,7 +276,7 @@ class FacilityPage { } confirmDeleteFacility() { - cy.submitButton("Delete"); + cy.clickSubmitButton("Delete"); } selectLocation(location: string) { diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts index 07eb4486d17..714004023f7 100644 --- a/cypress/pageobject/Login/LoginPage.ts +++ b/cypress/pageobject/Login/LoginPage.ts @@ -20,13 +20,13 @@ class LoginPage { } else { cy.get("input[id='password']").type("Corona"); } - cy.submitButton("Login"); + cy.clickSubmitButton("Login"); } loginManuallyAsNurse(): void { cy.get("input[id='username']").click().type("dummynurse1"); cy.get("input[id='password']").click().type("Coronasafe@123"); - cy.submitButton("Login"); + cy.clickSubmitButton("Login"); } login(username: string, password: string): void { diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts index 642eaf9e4d6..c8046a39a9e 100644 --- a/cypress/pageobject/Patient/PatientConsultation.ts +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -113,6 +113,14 @@ export class PatientConsultationPage { cy.wait(3000); } + interceptPatientDetailsAPI(): void { + cy.intercept("GET", "**/api/v1/patient/**").as("patientDetails"); + } + + verifyPatientDetailsResponse(): void { + cy.wait("@patientDetails").its("response.statusCode").should("eq", 200); + } + clickViewConsultationButton() { cy.verifyAndClickElement( "#view_consultation_and_log_updates", diff --git a/cypress/pageobject/Patient/PatientCreation.ts b/cypress/pageobject/Patient/PatientCreation.ts index 8f46dec5560..a38b8ab6a5c 100644 --- a/cypress/pageobject/Patient/PatientCreation.ts +++ b/cypress/pageobject/Patient/PatientCreation.ts @@ -23,7 +23,7 @@ export class PatientPage { selectFacility(facilityName: string) { cy.typeAndSelectOption("input[name='facilities']", facilityName); - cy.submitButton("Select"); + cy.clickSubmitButton("Select"); } interceptCreatePatientAPI() { @@ -57,7 +57,7 @@ export class PatientPage { typePatientAge(age: string) { cy.clickAndSelectOption("#patientAge", "Age"); - cy.submitButton("Confirm"); + cy.clickSubmitButton("Confirm"); cy.get("#age").clear().type(age); } diff --git a/cypress/pageobject/Patient/PatientFileupload.ts b/cypress/pageobject/Patient/PatientFileupload.ts index 0616a19729c..6cde7d78568 100644 --- a/cypress/pageobject/Patient/PatientFileupload.ts +++ b/cypress/pageobject/Patient/PatientFileupload.ts @@ -58,7 +58,7 @@ export class PatientFileUpload { clickSaveArchiveFile() { cy.intercept("PATCH", "**/api/v1/files/**").as("saveArchiveFile"); - cy.submitButton("Proceed"); + cy.clickSubmitButton("Proceed"); cy.wait("@saveArchiveFile").its("response.statusCode").should("eq", 200); } @@ -89,7 +89,7 @@ export class PatientFileUpload { clickSaveFileName() { cy.intercept("PATCH", "**/api/v1/files/**").as("saveFileName"); - cy.submitButton("Proceed"); + cy.clickSubmitButton("Proceed"); cy.wait("@saveFileName").its("response.statusCode").should("eq", 200); } } diff --git a/cypress/pageobject/Sample/SampleTestCreate.ts b/cypress/pageobject/Sample/SampleTestCreate.ts new file mode 100644 index 00000000000..cd2e93e5072 --- /dev/null +++ b/cypress/pageobject/Sample/SampleTestCreate.ts @@ -0,0 +1,114 @@ +export class SampleTestPage { + visitSampleRequestPage(): void { + cy.verifyAndClickElement("#sample-request-btn", "Request Sample Test"); + cy.url().should("include", "/sample-test"); + } + + selectSampleType(option: string): void { + cy.clickAndSelectOption("#sample-type", option); + } + + selectIcmrCategory(option: string): void { + cy.clickAndSelectOption("#icmr-category", option); + } + + fillIcmrLabel(label: string): void { + cy.get("#icmr-label").should("be.visible").type(label); + } + + fillFastTrackReason(value: string): void { + cy.get("#is_fast_track").should("be.visible").check(); + cy.get("#fast_track").should("be.visible").type(value); + } + + fillDoctorName(value: string): void { + cy.get("#doctor_name").should("be.visible").type(value); + } + + fillAtypicalPresentation(value: string): void { + cy.get("#is_atypical_presentation").should("be.visible").check(); + cy.get("#atypical_presentation").should("be.visible").type(value); + } + + fillDiagnosis(value: string): void { + cy.get("#diagnosis").should("be.visible").type(value); + } + + fillEtiology(value: string): void { + cy.get("#etiology_identified").should("be.visible").type(value); + } + + fillDiffDiagnosis(value: string): void { + cy.get("#diff_diagnosis").should("be.visible").type(value); + } + + checkHasSari(): void { + cy.get("#has_sari").should("be.visible").check(); + } + + checkHasAri(): void { + cy.get("#has_ari").should("be.visible").check(); + } + + checkIsUnusualCourse(): void { + cy.get("#is_unusual_course").should("be.visible").check(); + } + + checkRequestHistory( + sampleTestStatus: string, + sampleTestType: string, + fastTrack: string, + sampleTestResult: string, + ): void { + cy.verifyContentPresence("#sample-test-status", [sampleTestStatus]); + cy.verifyContentPresence("#sample-test-type", [sampleTestType]); + cy.verifyContentPresence("#sample-test-fast-track", [fastTrack]); + cy.verifyContentPresence("#sample-test-result", [sampleTestResult]); + } + + searchPatientSample(patientName: string): void { + cy.get("#search_patient_name").should("be.visible").type(patientName); + } + + verifyPatientName(patientName: string): void { + cy.verifyContentPresence("#sample-test-patient-name", [patientName]); + } + + clickOnSampleDetailsBtn(): void { + cy.get("#sample-details-btn").should("be.visible").first().click(); + } + + verifyPatientTestDetails( + patientName: string, + fastTrackReason: string, + doctorName: string, + diagnosis: string, + differentialDiagnosis: string, + etiologyIdentified: string, + ): void { + cy.verifyContentPresence("#patient_name", [patientName]); + cy.verifyContentPresence("#fast_track_reason", [fastTrackReason]); + cy.verifyContentPresence("#doctor_name", [doctorName]); + cy.verifyContentPresence("#diagnosis", [diagnosis]); + cy.verifyContentPresence("#diff_diagnosis", [differentialDiagnosis]); + cy.verifyContentPresence("#etiology_identified", [etiologyIdentified]); + } + + interceptSampleTestReq(): void { + cy.intercept("POST", "**/api/v1/patient/*/test_sample/").as( + "sampleDetails", + ); + } + + verifySampleTestReq(): void { + cy.wait("@sampleDetails").its("response.statusCode").should("eq", 201); + } + + interceptGetSampleTestReq(): void { + cy.intercept("GET", "**/api/v1/test_sample/**").as("getSampleTestReq"); + } + + verifyGetSampleTestReq(): void { + cy.wait("@getSampleTestReq").its("response.statusCode").should("eq", 200); + } +} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 9af5f97e5d4..d97bb52732f 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -121,11 +121,16 @@ Cypress.Commands.add("clearAllFilters", () => { return cy.get("#clear-all-filters").click(); }); -Cypress.Commands.add("submitButton", (buttonText = "Submit") => { +Cypress.Commands.add("clickSubmitButton", (buttonText = "Submit") => { cy.get("button[type='submit']").contains(buttonText).scrollIntoView(); cy.get("button[type='submit']").contains(buttonText).click(); }); +Cypress.Commands.add("clickCancelButton", (buttonText = "Cancel") => { + cy.get("#cancel").contains(buttonText).scrollIntoView(); + cy.get("#cancel").contains(buttonText).click(); +}); + Cypress.Commands.add( "typeAndSelectOption", (element: string, referance: string) => { diff --git a/cypress/support/index.ts b/cypress/support/index.ts index b2895871872..6429ef5710f 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -14,7 +14,8 @@ declare global { ): Chainable; getAttached(selector: string): Chainable; clearAllFilters(): Chainable; - submitButton(buttonText?: string): Chainable; + clickSubmitButton(buttonText?: string): Chainable; + clickCancelButton(buttonText?: string): Chainable; typeAndSelectOption( element: string, referance: string, diff --git a/src/components/Patient/PatientHome.tsx b/src/components/Patient/PatientHome.tsx index 9d3ab238e5b..a7b08bdea58 100644 --- a/src/components/Patient/PatientHome.tsx +++ b/src/components/Patient/PatientHome.tsx @@ -1295,6 +1295,7 @@ export const PatientHome = (props: any) => { ) } authorizeFor={NonReadOnlyUsers} + id="sample-request-btn" > diff --git a/src/components/Patient/SampleDetails.tsx b/src/components/Patient/SampleDetails.tsx index c16ef1c0e86..c5282a65c1f 100644 --- a/src/components/Patient/SampleDetails.tsx +++ b/src/components/Patient/SampleDetails.tsx @@ -331,7 +331,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("patient")}:{" "} - {sampleDetails?.patient_name} + {sampleDetails?.patient_name}
{sampleDetails?.facility_object && (
@@ -362,7 +362,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("fast_track_testing_reason")}:{" "} - {sampleDetails.fast_track} + {sampleDetails.fast_track}
)} {sampleDetails?.doctor_name && ( @@ -370,7 +370,9 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("doctors_name")}:{" "} - {startCase(camelCase(sampleDetails.doctor_name))} + + {startCase(camelCase(sampleDetails.doctor_name))} +
)} {sampleDetails?.diagnosis && ( @@ -378,7 +380,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("diagnosis")}:{" "} - {sampleDetails.diagnosis} + {sampleDetails.diagnosis}
)} {sampleDetails?.diff_diagnosis && ( @@ -386,7 +388,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("differential_diagnosis")}:{" "} - {sampleDetails?.diff_diagnosis} + {sampleDetails?.diff_diagnosis}
)} {sampleDetails?.etiology_identified && ( @@ -394,7 +396,9 @@ export const SampleDetails = ({ id }: DetailRoute) => { {t("etiology_identified")}:{" "} - {sampleDetails.etiology_identified} + + {sampleDetails.etiology_identified} +
)}
diff --git a/src/components/Patient/SampleTest.tsx b/src/components/Patient/SampleTest.tsx index 0714de28492..0933d03761e 100644 --- a/src/components/Patient/SampleTest.tsx +++ b/src/components/Patient/SampleTest.tsx @@ -216,6 +216,7 @@ export const SampleTest = ({ facilityId, patientId }: any) => { options={SAMPLE_TYPE_CHOICES} optionLabel={(option) => option.text} optionValue={(option) => option.id} + id="sample-type" /> {state.form.sample_type === "9" && ( @@ -230,6 +231,7 @@ export const SampleTest = ({ facilityId, patientId }: any) => { options={ICMR_CATEGORY} optionLabel={(option) => option} optionValue={(option) => option} + id="icmr-category" />

@@ -255,7 +257,11 @@ export const SampleTest = ({ facilityId, patientId }: any) => {

- +
Testing Facility { />
{state.form.isFastTrack && ( )} - + {state.form.is_atypical_presentation && (
{ />
)} - +
goBack()} /> - +
diff --git a/src/components/Patient/SampleTestCard.tsx b/src/components/Patient/SampleTestCard.tsx index fef2c9e4d08..a0303705eb7 100644 --- a/src/components/Patient/SampleTestCard.tsx +++ b/src/components/Patient/SampleTestCard.tsx @@ -79,6 +79,7 @@ export const SampleTestCard = (props: SampleDetailsProps) => { return (
{
Status{" "}
-
+
{startCase(camelCase(itemData.status))}
@@ -110,7 +114,10 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Sample Type{" "}
-
+
{itemData.sample_type?.toLowerCase()}
@@ -121,7 +128,10 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Fast-Track{" "}
-
+
{itemData.fast_track}
@@ -132,7 +142,10 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Result{" "}
-
+
{startCase(camelCase(itemData.result))}
diff --git a/src/components/Patient/SampleViewAdmin.tsx b/src/components/Patient/SampleViewAdmin.tsx index 43d03ad1d12..37d66bfb815 100644 --- a/src/components/Patient/SampleViewAdmin.tsx +++ b/src/components/Patient/SampleViewAdmin.tsx @@ -155,7 +155,10 @@ export default function SampleViewAdmin() {
-
+
{item.patient_name}
@@ -274,6 +277,7 @@ export default function SampleViewAdmin() { )} From 570ed1ea258c8f121e7b30b88109aa6a246a0067 Mon Sep 17 00:00:00 2001 From: Aditya Jindal Date: Tue, 19 Nov 2024 12:27:18 +0530 Subject: [PATCH 13/33] Fix: Bed capacity pop up errors (#9123) --- src/components/Facility/BedCapacity.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/Facility/BedCapacity.tsx b/src/components/Facility/BedCapacity.tsx index 0bb405670fd..156dc6adce6 100644 --- a/src/components/Facility/BedCapacity.tsx +++ b/src/components/Facility/BedCapacity.tsx @@ -77,6 +77,8 @@ export const BedCapacity = (props: BedCapacityProps) => { const existingData = capacityQuery.data?.results; // if all options are diabled if (existingData.length === BED_TYPES.length) { + setBedTypes([]); + setIsLoading(false); return; } // disable existing bed types @@ -277,18 +279,20 @@ export const BedCapacity = (props: BedCapacityProps) => {
- {!isLastOptionType && headerText === "Add Bed Capacity" && ( + {headerText === "Add Bed Capacity" && ( handleSubmit(e, "Save and Exit")} label="Save Bed Capacity" /> )} - handleSubmit(e)} - label={buttonText} - /> + {!isLastOptionType && ( + handleSubmit(e)} + label={buttonText} + /> + )}
)} From 46105634b64da62c8247a5c455fafb2761f3ee2f Mon Sep 17 00:00:00 2001 From: Alok Date: Tue, 19 Nov 2024 15:55:28 +0530 Subject: [PATCH 14/33] Enhanced care config to validate env during build process (#9032) --- package-lock.json | 447 ++++++++++++++++++++++++++++++++- package.json | 4 +- src/Integrations/Plausible.tsx | 10 + src/Integrations/Sentry.tsx | 5 +- vite.config.mts | 33 ++- 5 files changed, 490 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5edb998e4e..5c703af8fe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -60,6 +60,7 @@ "xlsx": "^0.18.5" }, "devDependencies": { + "@julr/vite-plugin-validate-env": "^1.1.1", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", @@ -106,7 +107,8 @@ "vite": "^5.4.10", "vite-plugin-checker": "^0.8.0", "vite-plugin-pwa": "^0.20.5", - "vite-plugin-static-copy": "^2.0.0" + "vite-plugin-static-copy": "^2.0.0", + "zod": "^3.23.8" }, "engines": { "node": ">=22.11.0" @@ -196,6 +198,16 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/utils": { + "version": "0.7.10", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-0.7.10.tgz", + "integrity": "sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@babel/code-frame": { "version": "7.25.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", @@ -2836,6 +2848,33 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@julr/vite-plugin-validate-env": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@julr/vite-plugin-validate-env/-/vite-plugin-validate-env-1.1.1.tgz", + "integrity": "sha512-MiAvuLrolS6xkzG/B4ofOCFUzmOEnC32pvD5fn9g6b3SK4nVruGZp5PU/WG+F+hw9fhsw0ES13L7GVzAeDJaYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/cliui": "^6.3.0", + "@poppinss/validator-lite": "^1.0.3", + "unconfig": "^0.3.11" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/Julien-R44" + }, + "peerDependencies": { + "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0", + "zod": "^3.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", @@ -3266,6 +3305,327 @@ "@pnotify/core": "^5.2.0" } }, + "node_modules/@poppinss/cliui": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@poppinss/cliui/-/cliui-6.4.1.tgz", + "integrity": "sha512-tdV3QpAfrPFRLPOh98F8QxWBvwYF3ziWGGtpVqfZtFNTFkC7nQnVQlUW55UtQ7rkeMmFohxfDI+2JNWScGJ1jQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.3", + "cli-boxes": "^3.0.0", + "cli-table3": "^0.6.4", + "cli-truncate": "^4.0.0", + "log-update": "^6.0.0", + "pretty-hrtime": "^1.0.3", + "string-width": "^7.1.0", + "supports-color": "^9.4.0", + "terminal-size": "^4.0.0", + "wordwrap": "^1.0.0" + }, + "engines": { + "node": ">=18.16.0" + } + }, + "node_modules/@poppinss/cliui/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@poppinss/cliui/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@poppinss/cliui/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@poppinss/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@poppinss/cliui/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@poppinss/colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.3.tgz", + "integrity": "sha512-A0FjJ6x14donWDN3bHAFFjJaPWTwM2PgWT834+bPKVK6Xukf25CscoRqCPYI939a8yuJFX9PYWWnVbUVI0E2Cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + }, + "engines": { + "node": ">=18.16.0" + } + }, + "node_modules/@poppinss/validator-lite": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@poppinss/validator-lite/-/validator-lite-1.0.3.tgz", + "integrity": "sha512-u4dmT7PDHwNtxY3q1jHVp/u+hMEEcBlkzd37QwwM4tVt/0mLlEDttSfPQ+TT7sqPG4VEtWKwVSlMInwPUYyJpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "validator": "^13.9.0" + } + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", @@ -6957,6 +7317,19 @@ "node": ">=6" } }, + "node_modules/cli-boxes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -7686,6 +8059,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -15041,6 +15421,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/proc-log": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", @@ -17411,6 +17801,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/terminal-size": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-size/-/terminal-size-4.0.0.tgz", + "integrity": "sha512-rcdty1xZ2/BkWa4ANjWRp4JGpda2quksXIHgn5TMjNBPZfwzJIgR68DKfSYiTL+CZWowDX/sbOo5ME/FRURvYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.36.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.36.0.tgz", @@ -17907,6 +18310,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unconfig": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/unconfig/-/unconfig-0.3.13.tgz", + "integrity": "sha512-N9Ph5NC4+sqtcOjPfHrRcHekBCadCXWTBzp2VYYbySOHW0PfD9XLCeXshTXjkPYwLrBr9AtSeU0CZmkYECJhng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/utils": "^0.7.7", + "defu": "^6.1.4", + "jiti": "^1.21.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/undici": { "version": "5.28.4", "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", @@ -18462,6 +18880,16 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -19742,6 +20170,13 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/workbox-background-sync": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.1.0.tgz", @@ -20381,6 +20816,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zrender": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.0.tgz", diff --git a/package.json b/package.json index 60250c2209e..020d73f5e65 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "xlsx": "^0.18.5" }, "devDependencies": { + "@julr/vite-plugin-validate-env": "^1.1.1", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.9", "@tailwindcss/typography": "^0.5.15", @@ -145,7 +146,8 @@ "vite": "^5.4.10", "vite-plugin-checker": "^0.8.0", "vite-plugin-pwa": "^0.20.5", - "vite-plugin-static-copy": "^2.0.0" + "vite-plugin-static-copy": "^2.0.0", + "zod": "^3.23.8" }, "browserslist": { "production": [ diff --git a/src/Integrations/Plausible.tsx b/src/Integrations/Plausible.tsx index dfbb2942e5d..89d0572e9e4 100644 --- a/src/Integrations/Plausible.tsx +++ b/src/Integrations/Plausible.tsx @@ -10,6 +10,16 @@ export default function Plausible() { }); useEffect(() => { + const missingConfig = []; + if (!careConfig.plausible.domain) missingConfig.push("domain"); + if (!careConfig.plausible.server) missingConfig.push("server"); + if (missingConfig.length > 0) { + console.error( + `Plausible analytics disabled. Missing configuration: ${missingConfig.join(", ")}`, + ); + return; + } + plausible("pageview"); }, []); diff --git a/src/Integrations/Sentry.tsx b/src/Integrations/Sentry.tsx index 90a8e78f3a9..1fa3d7e6da1 100644 --- a/src/Integrations/Sentry.tsx +++ b/src/Integrations/Sentry.tsx @@ -7,7 +7,10 @@ interface Props { export default function Sentry({ disabled }: Props) { useEffect(() => { - if (disabled || !careConfig.sentry.dsn) { + if (disabled || !careConfig.sentry.dsn || !careConfig.sentry.environment) { + console.error( + "Sentry is not configured correctly. Please check your environment variables.", + ); return; } diff --git a/vite.config.mts b/vite.config.mts index 3f6df95c290..ca92aa50954 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -1,14 +1,17 @@ +import { ValidateEnv } from "@julr/vite-plugin-validate-env"; +import react from "@vitejs/plugin-react-swc"; +import DOMPurify from "dompurify"; import fs from "fs"; +import { JSDOM } from "jsdom"; +import { marked } from "marked"; +import { createRequire } from "node:module"; import path from "path"; import { defineConfig, loadEnv } from "vite"; -import { createRequire } from "node:module"; -import { VitePWA } from "vite-plugin-pwa"; -import react from "@vitejs/plugin-react-swc"; import checker from "vite-plugin-checker"; +import { VitePWA } from "vite-plugin-pwa"; import { viteStaticCopy } from "vite-plugin-static-copy"; -import DOMPurify from "dompurify"; -import { JSDOM } from "jsdom"; -import { marked } from "marked"; +import { z } from "zod"; + import { treeShakeCareIcons } from "./plugins/treeShakeCareIcons"; const pdfWorkerPath = path.join( @@ -100,6 +103,24 @@ export default defineConfig(({ mode }) => { ), }, plugins: [ + ValidateEnv({ + validator: "zod", + schema: { + REACT_CARE_API_URL: z.string().url(), + + REACT_SENTRY_DSN: z.string().url().optional(), + REACT_SENTRY_ENVIRONMENT: z.string().optional(), + + REACT_PLAUSIBLE_SITE_DOMAIN: z.string().url().optional(), + REACT_PLAUSIBLE_SERVER_URL: z.string().url().optional(), + REACT_CDN_URLS: z + .string() + .optional() + .transform((val) => val?.split(" ")) + .pipe(z.array(z.string().url()).optional()) + .describe("Optional: Space-separated list of CDN URLs"), + }, + }), viteStaticCopy({ targets: [ { From b0305a496721fe7a48da9386b068af7dcacb9b14 Mon Sep 17 00:00:00 2001 From: Noufal Rahim <120470585+noufalrahim@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:56:56 +0530 Subject: [PATCH 15/33] Added SpO2 field in community log update for nurses (#8907) --- public/locale/en.json | 2 ++ src/components/Patient/DailyRounds.tsx | 27 +++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/public/locale/en.json b/public/locale/en.json index e281d8239bf..ad656dc6631 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -746,6 +746,7 @@ "hi__record_not_fetched_title": "This record hasn't been fetched yet", "hi__waiting_for_record": "Waiting for the Host HIP to send the record.", "hide": "Hide", + "high": "High", "home_facility": "Home Facility", "hubs": "Hub Facilities", "i_declare": "I hereby declare that:", @@ -835,6 +836,7 @@ "log_updates": "Log Updates", "login": "Login", "longitude_invalid": "Longitude must be between -180 and 180", + "low": "Low", "lsg": "Lsg", "make_multiple_beds_label": "Do you want to make multiple beds?", "manage_bed_presets": "Manage Presets of Bed", diff --git a/src/components/Patient/DailyRounds.tsx b/src/components/Patient/DailyRounds.tsx index d9bcb1366d5..2afd2b0a95d 100644 --- a/src/components/Patient/DailyRounds.tsx +++ b/src/components/Patient/DailyRounds.tsx @@ -59,6 +59,7 @@ import request from "@/Utils/request/request"; import { formatDateTime } from "@/Utils/utils"; import { scrollTo } from "@/Utils/utils"; +import RangeAutocompleteFormField from "../Form/FormFields/RangeAutocompleteFormField"; import TextFormField from "../Form/FormFields/TextFormField"; export const DailyRounds = (props: any) => { @@ -702,6 +703,31 @@ export const DailyRounds = (props: any) => { }, ]} /> + + )} @@ -819,7 +845,6 @@ export const DailyRounds = (props: any) => { /> )} - {state.form.rounds_type === "COMMUNITY_NURSES_LOG" && (

From ea713dfee1dcfd80d594080e7b194e8b348f9574 Mon Sep 17 00:00:00 2001 From: Khavin Shankar Date: Tue, 19 Nov 2024 15:58:40 +0530 Subject: [PATCH 16/33] added a missing translation (#9131) --- public/locale/en.json | 1 + 1 file changed, 1 insertion(+) diff --git a/public/locale/en.json b/public/locale/en.json index ad656dc6631..2eb4822c27e 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -259,6 +259,7 @@ "abha_link_options__link_with_demographics__title": "Link with Demographics", "abha_link_options__link_with_otp__description": "Link Existing ABHA Number Using Mobile or Aadhaar OTP", "abha_link_options__link_with_otp__title": "Link with OTP", + "abha_link_options__link_with_qr__description": "Link Existing ABHA Number by Scanning the ABHA QR", "abha_link_options__link_with_qr__title": "Link with ABHA QR", "abha_number": "ABHA Number", "abha_number_exists": "ABHA Number already exists", From ff63195d38f5bdfb684c2f6c429cffde217063ec Mon Sep 17 00:00:00 2001 From: Tanuj Nainwal <125687187+Tanuj1718@users.noreply.github.com> Date: Wed, 20 Nov 2024 07:44:38 +0530 Subject: [PATCH 17/33] fix: Resolve creation of patient name despite having numeric values (#9152) --- src/components/Patient/PatientRegister.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Patient/PatientRegister.tsx b/src/components/Patient/PatientRegister.tsx index 5cc958d44de..a7a7743aee4 100644 --- a/src/components/Patient/PatientRegister.tsx +++ b/src/components/Patient/PatientRegister.tsx @@ -67,7 +67,7 @@ import { } from "@/common/constants"; import countryList from "@/common/static/countries.json"; import { statusType, useAbortableEffect } from "@/common/utils"; -import { validatePincode } from "@/common/validation"; +import { validateName, validatePincode } from "@/common/validation"; import { PLUGIN_Component } from "@/PluginEngine"; import { RestoreDraftButton } from "@/Utils/AutoSave"; @@ -421,6 +421,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { switch (field) { case "address": case "name": + if (!validateName(form[field])) { + errors[field] = "Please enter valid name"; + } + return; case "gender": errors[field] = RequiredFieldValidator()(form[field]); return; From 41ed5be0b175f501f54bcc57060af4cb6aaa37c7 Mon Sep 17 00:00:00 2001 From: Aditya Jindal Date: Wed, 20 Nov 2024 07:45:48 +0530 Subject: [PATCH 18/33] Adjusted SpO2 thresholds and labels in DailyRounds component to match medical standards (#9118) --- public/locale/en.json | 4 ++++ src/components/Patient/DailyRounds.tsx | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/public/locale/en.json b/public/locale/en.json index 2eb4822c27e..5faa645b133 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -205,6 +205,10 @@ "SORT_OPTIONS__name": "Patient name A-Z", "SORT_OPTIONS__review_time": "Oldest review date first", "SORT_OPTIONS__taken_at": "Oldest taken date first", + "SPO2_LEVEL_MILD_HYPOXEMIA": "Mild Hypoxemia", + "SPO2_LEVEL_MODERATE_HYPOXEMIA": "Moderate Hypoxemia", + "SPO2_LEVEL_NORMAL": "Normal", + "SPO2_LEVEL_SEVERE_HYPOXEMIA": "Severe Hypoxemia", "Submit": "Submit", "TELEMEDICINE": "Telemedicine", "TRANSPORTATION TO BE ARRANGED": "Transportation", diff --git a/src/components/Patient/DailyRounds.tsx b/src/components/Patient/DailyRounds.tsx index 2afd2b0a95d..4e8573d6273 100644 --- a/src/components/Patient/DailyRounds.tsx +++ b/src/components/Patient/DailyRounds.tsx @@ -800,17 +800,22 @@ export const DailyRounds = (props: any) => { { value: 0, className: "text-danger-500", - label: "Low", + label: t("SPO2_LEVEL_SEVERE_HYPOXEMIA"), }, { - value: 90, - className: "text-primary-500", - label: "Normal", + value: 86, + className: "text-danger-500", + label: t("SPO2_LEVEL_MODERATE_HYPOXEMIA"), }, { - value: 100, - className: "text-danger-500", - label: "High", + value: 91, + className: "text-warning-400", + label: t("SPO2_LEVEL_MILD_HYPOXEMIA"), + }, + { + value: 95, + className: "text-primary-500", + label: t("SPO2_LEVEL_NORMAL"), }, ]} /> From b076d4f7a3179eb7ef783cb48b7d6cecf349cf6c Mon Sep 17 00:00:00 2001 From: Aditya Jindal Date: Wed, 20 Nov 2024 07:46:07 +0530 Subject: [PATCH 19/33] Fix: Year Of Birth Field Validation Condition in Patient transfer form (#9133) --- .../Facility/TransferPatientDialog.tsx | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/components/Facility/TransferPatientDialog.tsx b/src/components/Facility/TransferPatientDialog.tsx index 530bc513f0d..153ea17fa92 100644 --- a/src/components/Facility/TransferPatientDialog.tsx +++ b/src/components/Facility/TransferPatientDialog.tsx @@ -75,22 +75,38 @@ const TransferPatientDialog = (props: Props) => { const maxYear = new Date().getFullYear(); const handleChange = (e: FieldChangeEvent) => { - if ( - e.name === "year_of_birth" && - parseInt((e.value as string) || "0") > maxYear - ) { + const value = String(e.value); + + if (e.name === "year_of_birth") { + if (value.length <= 4) { + dispatch({ + type: "set_form", + form: { ...state.form, [e.name]: e.value }, + }); + } + } else { dispatch({ - type: "set_error", - errors: { - ...state.errors, - [e.name]: `Cannot be greater than ${maxYear}`, - }, + type: "set_form", + form: { ...state.form, [e.name]: e.value }, }); - return; + } + }; + + const handleOnBlur = (e: React.FocusEvent) => { + const yearValue = Number(state.form.year_of_birth); + if (!state.form.year_of_birth) return; + let errorMessage = ""; + if (yearValue > maxYear) { + errorMessage = `Cannot be greater than ${maxYear}`; + } else if (yearValue < 1900) { + errorMessage = `Cannot be smaller than 1900`; } dispatch({ - type: "set_form", - form: { ...state.form, [e.name]: e.value }, + type: "set_error", + errors: { + ...state.errors, + [e.target.name]: errorMessage, + }, }); }; @@ -115,6 +131,11 @@ const TransferPatientDialog = (props: Props) => { errors[field] = `Cannot be greater than ${maxYear}`; invalidForm = true; } + + if (parseInt(state.form[field] || "0") < 1900) { + errors[field] = `Cannot be smaller than 1900`; + invalidForm = true; + } return; default: return; @@ -193,9 +214,8 @@ const TransferPatientDialog = (props: Props) => { label="Year of birth" labelClassName="text-sm" value={state.form.year_of_birth} - min="1900" - max={maxYear} onChange={handleChange} + onBlur={handleOnBlur} placeholder="Enter year of birth" error={state.errors.year_of_birth} /> From 64ed2a32feba89beeab031377afc9cb27373f518 Mon Sep 17 00:00:00 2001 From: Aditya Jindal Date: Wed, 20 Nov 2024 07:47:48 +0530 Subject: [PATCH 20/33] Fix: TypeError in notification list (#8935) --- .../Notifications/NotificationsList.tsx | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/src/components/Notifications/NotificationsList.tsx b/src/components/Notifications/NotificationsList.tsx index 17033d67623..f9b65b35201 100644 --- a/src/components/Notifications/NotificationsList.tsx +++ b/src/components/Notifications/NotificationsList.tsx @@ -244,7 +244,10 @@ export default function NotificationsList({ const handleSubscribeClick = () => { const status = isSubscribed; - if (status === "NotSubscribed" || status === "SubscribedOnAnotherDevice") { + if (!navigator.serviceWorker) { + return; + } + if (["NotSubscribed", "SubscribedOnAnotherDevice"].includes(status)) { if (Notification.permission === "denied") { Warn({ msg: t("notification_permission_denied"), @@ -286,49 +289,47 @@ export default function NotificationsList({ let manageResults: any = null; - const unsubscribe = () => { - navigator.serviceWorker.ready - .then(function (reg) { - setIsSubscribing(true); - reg.pushManager - .getSubscription() - .then(function (subscription) { - subscription - ?.unsubscribe() - .then(async function (_successful) { - const data = { - pf_endpoint: "", - pf_p256dh: "", - pf_auth: "", - }; - - await request(routes.updateUserPnconfig, { - pathParams: { username: username }, - body: data, - }); - - Warn({ - msg: t("unsubscribed_successfully"), - }); - - setIsSubscribed("NotSubscribed"); - setIsSubscribing(false); - }) - .catch(function (_e) { - Error({ - msg: t("unsubscribe_failed"), - }); - }); - }) - .catch(function (_e) { - Error({ msg: t("subscription_error") }); + const unsubscribe = async () => { + try { + const reg = await navigator.serviceWorker.ready; + + if (!reg.pushManager) { + Error({ msg: t("unsubscribe_failed") }); + return; + } + + setIsSubscribing(true); + + const subscription = await reg.pushManager.getSubscription(); + + if (subscription) { + try { + await subscription.unsubscribe(); + + await request(routes.updateUserPnconfig, { + pathParams: { username }, + body: { + pf_endpoint: "", + pf_p256dh: "", + pf_auth: "", + }, }); - }) - .catch(function (_e) { - Sentry.captureException(_e); - }); - }; + setIsSubscribed("NotSubscribed"); + Warn({ + msg: t("unsubscribed_successfully"), + }); + } catch (e) { + Error({ msg: t("unsubscribe_failed") }); + } + } + } catch (e) { + Sentry.captureException(e); + Error({ msg: t("subscription_error") }); + } finally { + setIsSubscribing(false); + } + }; async function subscribe() { setIsSubscribing(true); try { From f79a2a4be651ffa3b7f9f107f8f08b480b467c0b Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Wed, 20 Nov 2024 07:49:15 +0530 Subject: [PATCH 21/33] Enhancements to the nursing care procedures and routines tables (#9079) --- .../ConsultationNursingTab.tsx | 203 ++++++++++-------- .../Consultations/LogUpdateAnalyseTable.tsx | 93 ++++++++ .../Facility/Consultations/NursingPlot.tsx | 132 ------------ src/components/Facility/models.tsx | 5 +- 4 files changed, 213 insertions(+), 220 deletions(-) create mode 100644 src/components/Facility/Consultations/LogUpdateAnalyseTable.tsx delete mode 100644 src/components/Facility/Consultations/NursingPlot.tsx diff --git a/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx index 81078197a45..6a7e33c6c95 100644 --- a/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx +++ b/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx @@ -5,42 +5,18 @@ import Loading from "@/components/Common/Loading"; import PageTitle from "@/components/Common/PageTitle"; import Pagination from "@/components/Common/Pagination"; import { ConsultationTabProps } from "@/components/Facility/ConsultationDetails/index"; -import { NursingPlot } from "@/components/Facility/Consultations/NursingPlot"; +import LogUpdateAnalyseTable from "@/components/Facility/Consultations/LogUpdateAnalyseTable"; import { + NursingPlotFields, + NursingPlotRes, RoutineAnalysisRes, RoutineFields, } from "@/components/Facility/models"; -import { PAGINATION_LIMIT } from "@/common/constants"; +import { NURSING_CARE_PROCEDURES, PAGINATION_LIMIT } from "@/common/constants"; import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; -import { classNames, formatDate, formatTime } from "@/Utils/utils"; - -export default function ConsultationNursingTab(props: ConsultationTabProps) { - const { t } = useTranslation(); - return ( -
- -
-

{t("routine")}

- -
-
-

{t("nursing_care")}

- -
-
- ); -} const REVERSE_CHOICES = { appetite: { @@ -114,6 +90,92 @@ const ROUTINE_ROWS = [ { subField: true, field: "appetite" } as const, ]; +const NursingPlot = ({ consultationId }: ConsultationTabProps) => { + const { t } = useTranslation(); + const [results, setResults] = useState<{ [date: string]: NursingPlotRes }>( + {}, + ); + const [currentPage, setCurrentPage] = useState(1); + const [totalCount, setTotalCount] = useState(0); + + useEffect(() => { + const fetchDailyRounds = async ( + currentPage: number, + consultationId: string, + ) => { + const { res, data } = await request(routes.dailyRoundsAnalyse, { + body: { page: currentPage, fields: NursingPlotFields }, + pathParams: { consultationId }, + }); + if (res?.ok && data) { + setResults(data.results as { [date: string]: NursingPlotRes }); + setTotalCount(data.count); + } + }; + + fetchDailyRounds(currentPage, consultationId); + }, [consultationId, currentPage]); + + const handlePagination = (page: number) => setCurrentPage(page); + + let fieldsToDisplay = new Set(); + + /** + * Transforms nursing procedure results into a structured format where dates are mapped to procedures and their descriptions. + * Groups nursing data by date, collecting unique procedures and their corresponding descriptions. + */ + const tableData = Object.entries(results).reduce( + (acc: Record>, [date, result]) => { + if ("nursing" in result) { + result.nursing.forEach((field) => { + if (field.procedure && !acc[date]) acc[date] = {}; + acc[date][field.procedure] = field.description; + // Add procedure to the set of procedures to display + fieldsToDisplay.add(field.procedure); + }); + } + return acc; + }, + {}, + ); + + fieldsToDisplay = fieldsToDisplay.intersection( + new Set(NURSING_CARE_PROCEDURES), + ); + + const rows = Array.from(fieldsToDisplay).map((procedure) => ({ + field: procedure, + title: t(`NURSING_CARE_PROCEDURE__${procedure}`), + })); + + return ( +
+
+ {fieldsToDisplay.size == 0 ? ( +
+
+ {t("no_data_found")} +
+
+ ) : ( + + )} +
+ + {totalCount > PAGINATION_LIMIT && fieldsToDisplay.size > 0 && ( +
+ +
+ )} +
+ ); +}; + const RoutineSection = ({ consultationId }: ConsultationTabProps) => { const { t } = useTranslation(); const [page, setPage] = useState(1); @@ -158,65 +220,11 @@ const RoutineSection = ({ consultationId }: ConsultationTabProps) => { return (
-
- - - - - ))} - - - - {ROUTINE_ROWS.map((row) => ( - - - {row.field && - Object.values(results).map((obj, idx) => ( - - ))} - - ))} - -
- {Object.keys(results).map((date) => ( - -

{formatDate(date)}

-

{formatTime(date)}

-
- {row.title ?? t(`LOG_UPDATE_FIELD_LABEL__${row.field!}`)} - - {(() => { - const value = obj[row.field]; - if (value == null) { - return "-"; - } - if (typeof value === "boolean") { - return t(value ? "yes" : "no"); - } - const choices = REVERSE_CHOICES[row.field]; - const choice = `${row.field.toUpperCase()}__${choices[value as keyof typeof choices]}`; - return t(choice); - })()} -
-
+ {totalCount != null && totalCount > PAGINATION_LIMIT && (
@@ -231,3 +239,24 @@ const RoutineSection = ({ consultationId }: ConsultationTabProps) => {
); }; + +export default function ConsultationNursingTab(props: ConsultationTabProps) { + const { t } = useTranslation(); + return ( +
+ +
+

{t("routine")}

+ +
+
+

{t("nursing_care")}

+ +
+
+ ); +} diff --git a/src/components/Facility/Consultations/LogUpdateAnalyseTable.tsx b/src/components/Facility/Consultations/LogUpdateAnalyseTable.tsx new file mode 100644 index 00000000000..43e59bebe7d --- /dev/null +++ b/src/components/Facility/Consultations/LogUpdateAnalyseTable.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; + +import { classNames, formatDate, formatTime } from "@/Utils/utils"; + +interface SharedSectionTableProps { + data: Record>; + rows: Array<{ title?: string; field?: string; subField?: boolean }>; + choices?: Record>; +} + +const LogUpdateAnalyseTable: React.FC = ({ + data, + rows, + choices = {}, +}) => { + const { t } = useTranslation(); + + const dataValues = React.useMemo(() => Object.values(data), [data]); + + const getDisplayValue = ( + value: string | boolean | null | undefined, + field?: string, + ): string => { + if (typeof value === "boolean") { + return t(value ? "yes" : "no"); + } + + if (field && choices[field]) { + const choiceMap = choices[field]; + const choice = + typeof value === "string" || typeof value === "number" + ? choiceMap[value] + : undefined; + return choice ? t(`${field.toUpperCase()}__${choice}`) : "-"; + } + + return typeof value === "string" ? value : "-"; + }; + + return ( +
+ + + + + {Object.keys(data).map((date) => ( + + ))} + + + + {rows.map((row) => ( + + + {dataValues.map((obj, idx) => { + const value = row.field ? obj[row.field] : undefined; + return ( + + ); + })} + + ))} + +
+

{formatDate(date)}

+

{formatTime(date)}

+
+ {row.title ?? t(`LOG_UPDATE_FIELD_LABEL__${row.field!}`)} + + {row.field ? getDisplayValue(value, row.field) : "-"} +
+
+ ); +}; + +export default LogUpdateAnalyseTable; diff --git a/src/components/Facility/Consultations/NursingPlot.tsx b/src/components/Facility/Consultations/NursingPlot.tsx deleted file mode 100644 index 13f5bb64201..00000000000 --- a/src/components/Facility/Consultations/NursingPlot.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import Pagination from "@/components/Common/Pagination"; -import { NursingPlotFields } from "@/components/Facility/models"; - -import { NURSING_CARE_PROCEDURES, PAGINATION_LIMIT } from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import { formatDateTime } from "@/Utils/utils"; - -export const NursingPlot = ({ consultationId }: any) => { - const { t } = useTranslation(); - const [results, setResults] = useState({}); - const [currentPage, setCurrentPage] = useState(1); - const [totalCount, setTotalCount] = useState(0); - - useEffect(() => { - const fetchDailyRounds = async ( - currentPage: number, - consultationId: string, - ) => { - const { res, data } = await request(routes.dailyRoundsAnalyse, { - body: { page: currentPage, fields: NursingPlotFields }, - pathParams: { - consultationId, - }, - }); - if (res && res.ok && data) { - setResults(data.results); - setTotalCount(data.count); - } - }; - - fetchDailyRounds(currentPage, consultationId); - }, [consultationId, currentPage]); - - const handlePagination = (page: number) => { - setCurrentPage(page); - }; - - const data = Object.entries(results).map((key: any) => { - return { - date: formatDateTime(key[0]), - nursing: key[1]["nursing"], - }; - }); - - const dataToDisplay = data - .map((x) => - x.nursing.map((f: any) => { - f["date"] = x.date; - return f; - }), - ) - .reduce((accumulator, value) => accumulator.concat(value), []); - - const filterEmpty = (field: (typeof NURSING_CARE_PROCEDURES)[number]) => { - const filtered = dataToDisplay.filter((i: any) => i.procedure === field); - return filtered.length > 0; - }; - - const areFieldsEmpty = () => { - let emptyFieldCount = 0; - for (const field of NURSING_CARE_PROCEDURES) { - if (!filterEmpty(field)) emptyFieldCount++; - } - if (emptyFieldCount === NURSING_CARE_PROCEDURES.length) return true; - else return false; - }; - - return ( -
-
-
-
- {areFieldsEmpty() && ( -
-
- {t("no_data_found")} -
-
- )} - {NURSING_CARE_PROCEDURES.map( - (f) => - filterEmpty(f) && ( -
-
-
-

- {t(`NURSING_CARE_PROCEDURE__${f}`)} -

-
-
-
- {dataToDisplay - .filter((i: any) => i.procedure === f) - .map((care: any, index: number) => ( -
-
- {care.date} -
-
- {care.description} -
-
- ))} -
-
- ), - )} -
-
-
- - {!areFieldsEmpty() && totalCount > PAGINATION_LIMIT && ( -
- -
- )} -
- ); -}; diff --git a/src/components/Facility/models.tsx b/src/components/Facility/models.tsx index 79272ac3de7..a984efe6283 100644 --- a/src/components/Facility/models.tsx +++ b/src/components/Facility/models.tsx @@ -391,7 +391,10 @@ export const NursingPlotFields = [ ] as const satisfies (keyof DailyRoundsModel)[]; export type NursingPlotRes = { - nursing: any[]; + nursing: Array<{ + procedure: string; + description: string; + }>; }; export const RoutineFields = [ From 85a445c4dfe0720c26523b35831d55c5ce16604c Mon Sep 17 00:00:00 2001 From: JavidSumra <112365664+JavidSumra@users.noreply.github.com> Date: Wed, 20 Nov 2024 15:34:55 +0530 Subject: [PATCH 22/33] Add function to authorize user to register patient (#9000) --- src/components/Facility/FacilityHome.tsx | 26 +++++---- src/components/Patient/PatientRegister.tsx | 61 +++++++++++++--------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx index 02c3f5386cb..e38c187590e 100644 --- a/src/components/Facility/FacilityHome.tsx +++ b/src/components/Facility/FacilityHome.tsx @@ -48,6 +48,8 @@ import uploadFile from "@/Utils/request/uploadFile"; import useQuery from "@/Utils/request/useQuery"; import { sleep } from "@/Utils/utils"; +import { patientRegisterAuth } from "../Patient/PatientRegister"; + type Props = { facilityId: string; }; @@ -458,17 +460,19 @@ export const FacilityHome = ({ facilityId }: Props) => { {CameraFeedPermittedUserTypes.includes(authUser.user_type) && ( )} - navigate(`/facility/${facilityId}/patient`)} - authorizeFor={NonReadOnlyUsers} - > - - {t("add_details_of_patient")} - + {patientRegisterAuth(authUser, facilityData, facilityId) && ( + navigate(`/facility/${facilityId}/patient`)} + authorizeFor={NonReadOnlyUsers} + > + + {t("add_details_of_patient")} + + )} { return ; } - const PatientRegisterAuth = () => { - const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"]; - if ( - !showAllFacilityUsers.includes(authUser.user_type) && - authUser.home_facility_object?.id === facilityId - ) { - return true; - } - if ( - authUser.user_type === "DistrictAdmin" && - authUser.district === facilityObject?.district - ) { - return true; - } - if ( - authUser.user_type === "StateAdmin" && - authUser.state === facilityObject?.state - ) { - return true; - } - - return false; - }; - - if (!isLoading && facilityId && facilityObject && !PatientRegisterAuth()) { + if ( + !isLoading && + facilityId && + facilityObject && + !patientRegisterAuth(authUser, facilityObject, facilityId) + ) { return ; } @@ -1717,3 +1700,31 @@ export const PatientRegister = (props: PatientRegisterProps) => { ); }; + +export function patientRegisterAuth( + authUser: UserModel, + facilityObject: FacilityModel | undefined, + facilityId: string, +) { + const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"]; + if ( + !showAllFacilityUsers.includes(authUser.user_type) && + authUser.home_facility_object?.id === facilityId + ) { + return true; + } + if ( + authUser.user_type === "DistrictAdmin" && + authUser.district === facilityObject?.district + ) { + return true; + } + if ( + authUser.user_type === "StateAdmin" && + authUser.state === facilityObject?.state + ) { + return true; + } + + return false; +} From 2d08896504f0b4a9abaf1621cfe0a0f7edb143bb Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Wed, 20 Nov 2024 18:47:24 +0530 Subject: [PATCH 23/33] Refactored the pagination component in cypress (#9163) --- cypress/e2e/assets_spec/AssetHomepage.cy.ts | 11 +++++------ .../e2e/facility_spec/FacilityHomepage.cy.ts | 12 ++++++------ .../e2e/facility_spec/FacilityLocation.cy.ts | 7 +++---- cypress/e2e/hcx_spec/HcxClaims.cy.ts | 2 +- .../patient_spec/PatientBedManagement.cy.ts | 4 ++-- .../PatientConsultationCreation.cy.ts | 2 +- .../PatientConsultationDischarge.cy.ts | 8 ++++---- .../patient_spec/PatientDoctorConnect.cy.ts | 4 ++-- .../e2e/patient_spec/PatientDoctorNotes.cy.ts | 2 +- cypress/e2e/patient_spec/PatientFileUpload.ts | 6 +++--- .../e2e/patient_spec/PatientHomepage.cy.ts | 7 ++++--- .../patient_spec/PatientInvestigation.cy.ts | 2 +- .../e2e/patient_spec/PatientLogUpdate.cy.ts | 8 ++++---- .../patient_spec/PatientPrescription.cy.ts | 8 ++++---- .../patient_spec/PatientRegistration.cy.ts | 4 ++-- .../sample_test_spec/SampleTestRequest.cy.ts | 2 +- cypress/e2e/users_spec/UsersHomepage.cy.ts | 11 ++++++----- cypress/pageobject/Asset/AssetPagination.ts | 19 ------------------- cypress/pageobject/Patient/PatientHome.ts | 12 ------------ cypress/pageobject/Users/UserSearch.ts | 12 ------------ cypress/pageobject/utils/paginationHelpers.ts | 13 +++++++++++++ 21 files changed, 63 insertions(+), 93 deletions(-) create mode 100644 cypress/pageobject/utils/paginationHelpers.ts diff --git a/cypress/e2e/assets_spec/AssetHomepage.cy.ts b/cypress/e2e/assets_spec/AssetHomepage.cy.ts index 67ad50e832f..dfb3249c243 100644 --- a/cypress/e2e/assets_spec/AssetHomepage.cy.ts +++ b/cypress/e2e/assets_spec/AssetHomepage.cy.ts @@ -1,9 +1,9 @@ import { advanceFilters } from "pageobject/utils/advanceFilterHelpers"; +import { pageNavigation } from "pageobject/utils/paginationHelpers"; import { v4 as uuidv4 } from "uuid"; import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; -import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan"; import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import LoginPage from "../../pageobject/Login/LoginPage"; @@ -11,7 +11,6 @@ import LoginPage from "../../pageobject/Login/LoginPage"; describe("Asset Tab", () => { const assetSearchPage = new AssetSearchPage(); const assetQRScanPage = new AssetQRScanPage(); - const assetPagination = new AssetPagination(); const assetFilters = new AssetFilters(); const assetPage = new AssetPage(); const loginPage = new LoginPage(); @@ -79,10 +78,10 @@ describe("Asset Tab", () => { // Verify the pagination in the page it("Next/Previous Page", () => { - assetPagination.navigateToNextPage(); - assetPagination.verifyNextUrl(); - assetPagination.navigateToPreviousPage(); - assetPagination.verifyPreviousUrl(); + pageNavigation.navigateToNextPage(); + pageNavigation.verifyCurrentPageNumber(2); + pageNavigation.navigateToPreviousPage(); + pageNavigation.verifyCurrentPageNumber(1); }); it("Import new asset", () => { diff --git a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts index 2c5699ec76c..f4c7aaa90a0 100644 --- a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts +++ b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts @@ -1,5 +1,6 @@ // FacilityCreation -import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; +import { pageNavigation } from "pageobject/utils/paginationHelpers"; + import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; import FacilityNotify from "../../pageobject/Facility/FacilityNotify"; @@ -15,7 +16,6 @@ describe("Facility Homepage Function", () => { const facilityPage = new FacilityPage(); const manageUserPage = new ManageUserPage(); const userPage = new UserPage(); - const assetPagination = new AssetPagination(); const facilitiesAlias = "downloadFacilitiesCSV"; const doctorsAlias = "downloadDoctorsCSV"; const triagesAlias = "downloadTriagesCSV"; @@ -83,10 +83,10 @@ describe("Facility Homepage Function", () => { it("Search a facility in homepage and pagination", () => { // pagination of the facility page - assetPagination.navigateToNextPage(); - assetPagination.verifyNextUrl(); - assetPagination.navigateToPreviousPage(); - assetPagination.verifyPreviousUrl(); + pageNavigation.navigateToNextPage(); + pageNavigation.verifyCurrentPageNumber(2); + pageNavigation.navigateToPreviousPage(); + pageNavigation.verifyCurrentPageNumber(1); // search for a facility manageUserPage.typeFacilitySearch(facilityName); facilityPage.verifyFacilityBadgeContent(facilityName); diff --git a/cypress/e2e/facility_spec/FacilityLocation.cy.ts b/cypress/e2e/facility_spec/FacilityLocation.cy.ts index 7d0c626a93b..9ac85e9ba4c 100644 --- a/cypress/e2e/facility_spec/FacilityLocation.cy.ts +++ b/cypress/e2e/facility_spec/FacilityLocation.cy.ts @@ -1,7 +1,7 @@ +import { pageNavigation } from "pageobject/utils/paginationHelpers"; import { v4 as uuidv4 } from "uuid"; import { AssetPage } from "../../pageobject/Asset/AssetCreation"; -import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; import FacilityLocation from "../../pageobject/Facility/FacilityLocation"; @@ -12,7 +12,6 @@ describe("Location Management Section", () => { const userCreationPage = new UserCreationPage(); const facilityPage = new FacilityPage(); const facilityLocation = new FacilityLocation(); - const assetPagination = new AssetPagination(); const facilityHome = new FacilityHome(); const EXPECTED_LOCATION_ERROR_MESSAGES = [ @@ -172,8 +171,8 @@ describe("Location Management Section", () => { facilityLocation.setMultipleBeds(numberOfModifiedBeds); assetPage.clickassetupdatebutton(); // pagination - assetPagination.navigateToNextPage(); - assetPagination.navigateToPreviousPage(); + pageNavigation.navigateToNextPage(); + pageNavigation.navigateToPreviousPage(); facilityLocation.closeNotification(); }); diff --git a/cypress/e2e/hcx_spec/HcxClaims.cy.ts b/cypress/e2e/hcx_spec/HcxClaims.cy.ts index 869afd6fcbb..b698bb7ffd8 100644 --- a/cypress/e2e/hcx_spec/HcxClaims.cy.ts +++ b/cypress/e2e/hcx_spec/HcxClaims.cy.ts @@ -11,7 +11,7 @@ describe("HCX Claims configuration and approval workflow", () => { const patientConsultationPage = new PatientConsultationPage(); const patientInsurance = new PatientInsurance(); const hcxClaims = new HcxClaims(); - const hcxPatientName = "Dummy Patient 14"; + const hcxPatientName = "Dummy Patient Thirteen"; const firstInsuranceIdentifier = "insurance-details-0"; const patientMemberId = "001"; const patientPolicyId = "100"; diff --git a/cypress/e2e/patient_spec/PatientBedManagement.cy.ts b/cypress/e2e/patient_spec/PatientBedManagement.cy.ts index 65adf131c87..94f0869db9b 100644 --- a/cypress/e2e/patient_spec/PatientBedManagement.cy.ts +++ b/cypress/e2e/patient_spec/PatientBedManagement.cy.ts @@ -11,8 +11,8 @@ describe("Patient swtich bed functionality", () => { const switchBedOne = "Dummy Bed 4"; const switchBedTwo = "Dummy Bed 1"; const switchBedThree = "Dummy Bed 7"; - const switchPatientOne = "Dummy Patient 6"; - const switchPatientTwo = "Dummy Patient 7"; + const switchPatientOne = "Dummy Patient Six"; + const switchPatientTwo = "Dummy Patient Seven"; before(() => { loginPage.loginAsDistrictAdmin(); diff --git a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts index bd6e7c0d967..3eacd9b718f 100644 --- a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts +++ b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts @@ -375,7 +375,7 @@ describe("Patient Consultation in multiple combination", () => { }); it("Edit created consultation to existing patient", () => { - patientPage.visitPatient("Dummy Patient 13"); + patientPage.visitPatient("Dummy Patient Thirteen"); patientConsultationPage.clickEditConsultationButton(); patientConsultationPage.typePatientIllnessHistory("editted"); patientConsultationPage.selectPatientDiagnosis( diff --git a/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts b/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts index 6a3d2863818..9e234c860da 100644 --- a/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts +++ b/cypress/e2e/patient_spec/PatientConsultationDischarge.cy.ts @@ -31,7 +31,7 @@ describe("Patient Discharge based on multiple reason", () => { }); it("Discharge a LAMA patient in the consultation", () => { - patientPage.visitPatient("Dummy Patient 12"); + patientPage.visitPatient("Discharge Patient One"); patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason4); cy.clickSubmitButton("Confirm Discharge"); @@ -47,7 +47,7 @@ describe("Patient Discharge based on multiple reason", () => { }); it("Discharge a expired patient in the consultation", () => { - patientPage.visitPatient("Dummy Patient 13"); + patientPage.visitPatient("Discharge Patient Two"); patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason3); patientDischarge.typeDischargeNote(patientDeathCause); @@ -67,7 +67,7 @@ describe("Patient Discharge based on multiple reason", () => { }); it("Discharge patient with referred reason to a facility", () => { - patientPage.visitPatient("Dummy Patient 16"); + patientPage.visitPatient("Discharge Patient Three"); patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason2); patientDischarge.typeDischargeNote(patientDischargeAdvice); @@ -93,7 +93,7 @@ describe("Patient Discharge based on multiple reason", () => { }); it("Discharge a recovered patient with all relevant fields", () => { - patientPage.visitPatient("Dummy Patient 15"); + patientPage.visitPatient("Discharge Patient Four"); patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason1); patientDischarge.typeDischargeNote(patientDischargeAdvice); diff --git a/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts b/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts index dd626f619ce..474af286b21 100644 --- a/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts +++ b/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts @@ -7,10 +7,10 @@ describe("Patient Doctor Connect in consultation page", () => { const loginPage = new LoginPage(); const patientPage = new PatientPage(); const doctorconnect = new DoctorConnect(); - const patientName = "Dummy Patient 11"; + const patientName = "Dummy Patient Eleven"; const doctorUser = "Dev Doctor"; const nurseUser = "Dev Staff"; - const teleIcuUser = "Dev Doctor Two"; + const teleIcuUser = "Tester Doctor"; before(() => { loginPage.loginAsDistrictAdmin(); diff --git a/cypress/e2e/patient_spec/PatientDoctorNotes.cy.ts b/cypress/e2e/patient_spec/PatientDoctorNotes.cy.ts index e35d3eb8961..d227d68ddcb 100644 --- a/cypress/e2e/patient_spec/PatientDoctorNotes.cy.ts +++ b/cypress/e2e/patient_spec/PatientDoctorNotes.cy.ts @@ -6,7 +6,7 @@ describe("Patient Discussion notes in the consultation page", () => { const loginPage = new LoginPage(); const patientPage = new PatientPage(); const patientDoctorNotes = new PatientDoctorNotes(); - const patientName = "Dummy Patient 4"; + const patientName = "Dummy Patient Four"; const patientNurseNote = "Test nurse Notes"; const patientNurseReplyNote = "Test nurse reply Notes"; const discussionNotesSubscribeWarning = diff --git a/cypress/e2e/patient_spec/PatientFileUpload.ts b/cypress/e2e/patient_spec/PatientFileUpload.ts index 110631551a1..dd3098163ec 100644 --- a/cypress/e2e/patient_spec/PatientFileUpload.ts +++ b/cypress/e2e/patient_spec/PatientFileUpload.ts @@ -14,9 +14,9 @@ function runTests( const cypressAudioName = "cypress audio"; const cypressFileName = "cypress name"; const newFileName = "cypress modified name"; - const patientNameOne = "Dummy Patient 3"; - const patientNameTwo = "Dummy Patient 4"; - const patientNameThree = "Dummy Patient 5"; + const patientNameOne = "Dummy Patient Three"; + const patientNameTwo = "Dummy Patient Four"; + const patientNameThree = "Dummy Patient Five"; before(() => { loginPage.loginAsDistrictAdmin(); cy.saveLocalStorage(); diff --git a/cypress/e2e/patient_spec/PatientHomepage.cy.ts b/cypress/e2e/patient_spec/PatientHomepage.cy.ts index e4e0f927952..e2c32098f1c 100644 --- a/cypress/e2e/patient_spec/PatientHomepage.cy.ts +++ b/cypress/e2e/patient_spec/PatientHomepage.cy.ts @@ -1,4 +1,5 @@ import { advanceFilters } from "pageobject/utils/advanceFilterHelpers"; +import { pageNavigation } from "pageobject/utils/paginationHelpers"; import LoginPage from "../../pageobject/Login/LoginPage"; import PatientHome from "../../pageobject/Patient/PatientHome"; @@ -163,15 +164,15 @@ describe("Patient Homepage present functionalities", () => { .invoke("text") .then((patientOne: string) => { firstPatientPageOne = patientOne.trim(); - patientHome.clickNextPage(); - patientHome.verifySecondPageUrl(); + pageNavigation.navigateToNextPage(); + pageNavigation.verifyCurrentPageNumber(2); cy.get('[data-cy="patient"]') .first() .invoke("text") .then((patientTwo: string) => { const firstPatientPageTwo = patientTwo.trim(); expect(firstPatientPageOne).not.to.eq(firstPatientPageTwo); - patientHome.clickPreviousPage(); + pageNavigation.navigateToPreviousPage(); }); }); }); diff --git a/cypress/e2e/patient_spec/PatientInvestigation.cy.ts b/cypress/e2e/patient_spec/PatientInvestigation.cy.ts index 262759bd360..350614d0c9a 100644 --- a/cypress/e2e/patient_spec/PatientInvestigation.cy.ts +++ b/cypress/e2e/patient_spec/PatientInvestigation.cy.ts @@ -7,7 +7,7 @@ describe("Patient Investigation Creation from Patient consultation page", () => const loginPage = new LoginPage(); const patientPage = new PatientPage(); const patientInvestigation = new PatientInvestigation(); - const patientName = "Dummy Patient 14"; + const patientName = "Dummy Patient Thirteen"; before(() => { loginPage.loginAsDistrictAdmin(); diff --git a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts index 05958bdf3f0..cfb739c8343 100644 --- a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts +++ b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts @@ -32,13 +32,13 @@ describe("Patient Log Update in Normal, Critical and TeleIcu", () => { const patientInsulinDosage = "56"; const patientFluidBalance = "500"; const patientNetBalance = "1000"; - const patientOne = "Dummy Patient 9"; + const patientOne = "Dummy Patient Nine"; const bedOne = "Dummy Bed 5"; - const patientTwo = "Dummy Patient 10"; + const patientTwo = "Dummy Patient Ten"; const bedTwo = "Dummy Bed 2"; - const patientThree = "Dummy Patient 8"; + const patientThree = "Dummy Patient Eight"; const bedThree = "Dummy Bed 3"; - const domicilaryPatient = "Dummy Patient 11"; + const domicilaryPatient = "Dummy Patient Eleven"; before(() => { loginPage.loginAsDistrictAdmin(); diff --git a/cypress/e2e/patient_spec/PatientPrescription.cy.ts b/cypress/e2e/patient_spec/PatientPrescription.cy.ts index 1011c2f4f4d..96e6e23b1aa 100644 --- a/cypress/e2e/patient_spec/PatientPrescription.cy.ts +++ b/cypress/e2e/patient_spec/PatientPrescription.cy.ts @@ -26,7 +26,7 @@ describe("Patient Medicine Administration", () => { }); it("Add a new medicine | Verify the Edit and Discontinue Medicine workflow |", () => { - patientPage.visitPatient("Dummy Patient 9"); + patientPage.visitPatient("Dummy Patient Nine"); patientPrescription.visitMedicineTab(); patientPrescription.visitEditPrescription(); // Add a normal Medicine to the patient @@ -63,7 +63,7 @@ describe("Patient Medicine Administration", () => { }); it("Add a PRN Prescription medicine | Group Administrate it |", () => { - patientPage.visitPatient("Dummy Patient 6"); + patientPage.visitPatient("Dummy Patient Six"); patientPrescription.visitMedicineTab(); patientPrescription.visitEditPrescription(); // Add First Medicine @@ -97,7 +97,7 @@ describe("Patient Medicine Administration", () => { }); it("Add a new titrated medicine for a patient | Individual Administeration |", () => { - patientPage.visitPatient("Dummy Patient 5"); + patientPage.visitPatient("Dummy Patient Five"); patientPrescription.visitMedicineTab(); patientPrescription.visitEditPrescription(); patientPrescription.clickAddPrescription(); @@ -136,7 +136,7 @@ describe("Patient Medicine Administration", () => { }); it("Add a new medicine for a patient and verify the duplicate medicine validation", () => { - patientPage.visitPatient("Dummy Patient 4"); + patientPage.visitPatient("Dummy Patient Four"); patientPrescription.visitMedicineTab(); patientPrescription.visitEditPrescription(); patientPrescription.clickAddPrescription(); diff --git a/cypress/e2e/patient_spec/PatientRegistration.cy.ts b/cypress/e2e/patient_spec/PatientRegistration.cy.ts index 91810ffd273..c3647ceac3a 100644 --- a/cypress/e2e/patient_spec/PatientRegistration.cy.ts +++ b/cypress/e2e/patient_spec/PatientRegistration.cy.ts @@ -64,7 +64,7 @@ describe("Patient Creation with consultation", () => { const patientOneSecondInsurerName = "Care Payor"; const patientTransferPhoneNumber = "9849511866"; const patientTransferFacility = "Dummy Shifting Center"; - const patientTransferName = "Dummy Patient 10"; + const patientTransferName = "Dummy Patient Twelve"; const patientOccupation = "Student"; before(() => { @@ -243,7 +243,7 @@ describe("Patient Creation with consultation", () => { patientTransfer.clickTransferPatientYOB(yearOfBirth); patientTransfer.clickTransferSubmitButton(); cy.verifyNotification( - "Patient Dummy Patient 10 (Male) transferred successfully", + `Patient ${patientTransferName} (Male) transferred successfully`, ); patientTransfer.clickConsultationCancelButton(); // allow the transfer button of a patient diff --git a/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts b/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts index a0f30ebd6db..da4d8aabd20 100644 --- a/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts +++ b/cypress/e2e/sample_test_spec/SampleTestRequest.cy.ts @@ -8,7 +8,7 @@ describe("Sample Test", () => { const patientPage = new PatientPage(); const loginPage = new LoginPage(); const patientConsultationPage = new PatientConsultationPage(); - const patientName = "Dummy Patient 11"; + const patientName = "Dummy Patient Eleven"; const sampleTestType = "BA/ETA"; const icmrCategory = "Cat 0"; const icmrLabel = "Test Icmr Label"; diff --git a/cypress/e2e/users_spec/UsersHomepage.cy.ts b/cypress/e2e/users_spec/UsersHomepage.cy.ts index d5825f58f20..b32bb859e4e 100644 --- a/cypress/e2e/users_spec/UsersHomepage.cy.ts +++ b/cypress/e2e/users_spec/UsersHomepage.cy.ts @@ -1,4 +1,5 @@ import { advanceFilters } from "pageobject/utils/advanceFilterHelpers"; +import { pageNavigation } from "pageobject/utils/paginationHelpers"; import LoginPage from "../../pageobject/Login/LoginPage"; import { UserPage } from "../../pageobject/Users/UserSearch"; @@ -16,7 +17,7 @@ describe("User Homepage", () => { const altPhoneNumber = "8878825662"; const homeFacility = "Dummy Facility 40"; const nurseUserName = "dummynurse1"; - const doctorUserName = "devdoctor1"; + const doctorUserName = "dev-doctor2"; before(() => { loginPage.loginAsDistrictAdmin(); @@ -84,10 +85,10 @@ describe("User Homepage", () => { }); it("Next/Previous Page Navigation", () => { - userPage.navigateToNextPage(); - userPage.verifyCurrentPageNumber(2); - userPage.navigateToPreviousPage(); - userPage.verifyCurrentPageNumber(1); + pageNavigation.navigateToNextPage(); + pageNavigation.verifyCurrentPageNumber(2); + pageNavigation.navigateToPreviousPage(); + pageNavigation.verifyCurrentPageNumber(1); }); afterEach(() => { diff --git a/cypress/pageobject/Asset/AssetPagination.ts b/cypress/pageobject/Asset/AssetPagination.ts index f2a4103b065..e69de29bb2d 100644 --- a/cypress/pageobject/Asset/AssetPagination.ts +++ b/cypress/pageobject/Asset/AssetPagination.ts @@ -1,19 +0,0 @@ -export class AssetPagination { - navigateToNextPage() { - // only works for desktop mode - cy.get("button#next-pages").click(); - } - - verifyNextUrl() { - cy.url().should("include", "page=2"); - } - - navigateToPreviousPage() { - // only works for desktop mode - cy.get("button#prev-pages").click(); - } - - verifyPreviousUrl() { - cy.url().should("include", "page=1"); - } -} diff --git a/cypress/pageobject/Patient/PatientHome.ts b/cypress/pageobject/Patient/PatientHome.ts index bc27977d561..36cad15f0e5 100644 --- a/cypress/pageobject/Patient/PatientHome.ts +++ b/cypress/pageobject/Patient/PatientHome.ts @@ -1,16 +1,4 @@ class PatientHome { - clickNextPage() { - cy.get("#next-pages").click(); - } - - verifySecondPageUrl() { - cy.url().should("include", "/patients?page=2"); - } - - clickPreviousPage() { - cy.get("#prev-pages").click(); - } - clickPatientExport() { cy.get("#patient-export").click(); } diff --git a/cypress/pageobject/Users/UserSearch.ts b/cypress/pageobject/Users/UserSearch.ts index 85575398d0d..6b727aa2040 100644 --- a/cypress/pageobject/Users/UserSearch.ts +++ b/cypress/pageobject/Users/UserSearch.ts @@ -76,18 +76,6 @@ export class UserPage { cy.get(`[data-testid="${testId}"]`).should("not.be.visible"); } - navigateToNextPage() { - cy.get("button#next-pages").click(); - } - - navigateToPreviousPage() { - cy.get("button#prev-pages").click(); - } - - verifyCurrentPageNumber(pageNumber: number) { - cy.url().should("include", `page=${pageNumber}`); - } - verifyMultipleBadgesWithSameId(alreadylinkedusersviews: string[]) { cy.get("#user-view-name").then(($elements) => { const userViews = $elements diff --git a/cypress/pageobject/utils/paginationHelpers.ts b/cypress/pageobject/utils/paginationHelpers.ts new file mode 100644 index 00000000000..edbabec5523 --- /dev/null +++ b/cypress/pageobject/utils/paginationHelpers.ts @@ -0,0 +1,13 @@ +export const pageNavigation = { + navigateToNextPage() { + cy.get("button#next-pages").click(); + }, + + verifyCurrentPageNumber(pageNumber: number) { + cy.url().should("include", `page=${pageNumber}`); + }, + + navigateToPreviousPage() { + cy.get("button#prev-pages").click(); + }, +}; From 10369c59abf6e75c8de71a7a133b5ca43c0e54ab Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 20 Nov 2024 19:16:47 +0530 Subject: [PATCH 24/33] fix plausible site domain env validation (#9168) --- vite.config.mts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/vite.config.mts b/vite.config.mts index ca92aa50954..3fc64d66829 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -111,7 +111,12 @@ export default defineConfig(({ mode }) => { REACT_SENTRY_DSN: z.string().url().optional(), REACT_SENTRY_ENVIRONMENT: z.string().optional(), - REACT_PLAUSIBLE_SITE_DOMAIN: z.string().url().optional(), + REACT_PLAUSIBLE_SITE_DOMAIN: z + .string() + .regex(/^[a-zA-Z0-9][a-zA-Z0-9-_.]*\.[a-zA-Z]{2,}$/) + .optional() + .describe("Domain name without protocol (e.g., sub.domain.com)"), + REACT_PLAUSIBLE_SERVER_URL: z.string().url().optional(), REACT_CDN_URLS: z .string() From 2fdffa22adf8e9898b579faf2cf6c2d877a7b3d8 Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Fri, 22 Nov 2024 17:27:30 +0530 Subject: [PATCH 25/33] Fixed the cypress env config file and removed hardcoded fallback (#9184) --- cypress.config.ts | 5 ++++- .../{PatientFileUpload.ts => PatientFileUpload.cy.ts} | 0 cypress/pageobject/Patient/PatientFileupload.ts | 4 +++- package-lock.json | 2 +- src/components/Files/AudioCaptureDialog.tsx | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) rename cypress/e2e/patient_spec/{PatientFileUpload.ts => PatientFileUpload.cy.ts} (100%) diff --git a/cypress.config.ts b/cypress.config.ts index b6e8fadd462..4da5d989a88 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,7 +1,10 @@ import { defineConfig } from "cypress"; import cypressSplit from "cypress-split"; +import * as dotenv from "dotenv"; import fs from "fs"; +dotenv.config(); + export default defineConfig({ projectId: "wf7d2m", defaultCommandTimeout: 10000, @@ -32,7 +35,7 @@ export default defineConfig({ requestTimeout: 15000, }, env: { - API_URL: process.env.REACT_CARE_API_URL ?? "http://localhost:9000", + API_URL: process.env.REACT_CARE_API_URL, ENABLE_HCX: process.env.REACT_ENABLE_HCX ?? false, }, }); diff --git a/cypress/e2e/patient_spec/PatientFileUpload.ts b/cypress/e2e/patient_spec/PatientFileUpload.cy.ts similarity index 100% rename from cypress/e2e/patient_spec/PatientFileUpload.ts rename to cypress/e2e/patient_spec/PatientFileUpload.cy.ts diff --git a/cypress/pageobject/Patient/PatientFileupload.ts b/cypress/pageobject/Patient/PatientFileupload.ts index 6cde7d78568..c70170a744d 100644 --- a/cypress/pageobject/Patient/PatientFileupload.ts +++ b/cypress/pageobject/Patient/PatientFileupload.ts @@ -20,7 +20,9 @@ export class PatientFileUpload { recordAudio() { cy.get("#record-audio").click(); - cy.wait(5000); + cy.wait(2000); + cy.get("#start-recording").click(); + cy.wait(2000); cy.get("#stop-recording").click(); cy.wait(1000); cy.get("#save-recording").click(); diff --git a/package-lock.json b/package-lock.json index 5c703af8fe1..63af1164ef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20862,4 +20862,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/components/Files/AudioCaptureDialog.tsx b/src/components/Files/AudioCaptureDialog.tsx index 4d60f64a135..5512fad9dc6 100644 --- a/src/components/Files/AudioCaptureDialog.tsx +++ b/src/components/Files/AudioCaptureDialog.tsx @@ -133,7 +133,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) {

{t("audio__record")}

{t("audio__record_helper")}
-
+
- advancedFilter.setShow(true)} - /> navigate("/shifting/board", { query: qParams })} @@ -284,6 +281,9 @@ export default function ListView() { {t("board_view")} + advancedFilter.setShow(true)} + />
} From b7c467fcc4a7690b9702ee4b3d3672b4050d5265 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 27 Nov 2024 14:09:08 +0530 Subject: [PATCH 31/33] Patient Home Page redesign (#9203) Co-authored-by: mahendar Co-authored-by: vinu tv --- .../patient_spec/PatientRegistration.cy.ts | 5 +- .../pageobject/Patient/PatientConsultation.ts | 3 +- cypress/pageobject/Patient/PatientCreation.ts | 4 +- .../Patient/PatientMedicalHistory.ts | 4 +- cypress/pageobject/Sample/SampleTestCreate.ts | 2 + public/locale/en.json | 91 + public/locale/hi.json | 1 + public/locale/kn.json | 1 + public/locale/ml.json | 1 + public/locale/mr.json | 34 +- src/CAREUI/display/Chip.tsx | 2 +- src/Routers/AppRouter.tsx | 6 +- src/Routers/routes/PatientRoutes.tsx | 12 +- src/common/constants.tsx | 2 + src/components/Common/Breadcrumbs.tsx | 20 +- src/components/Common/Menu.tsx | 2 +- src/components/Common/Page.tsx | 4 +- src/components/Facility/ConsultationCard.tsx | 231 +-- .../Facility/ConsultationDetails/index.tsx | 179 +- src/components/Patient/InsuranceDetails.tsx | 4 +- .../Patient/InsuranceDetailsCard.tsx | 96 +- .../Patient/PatientDetailsTab/Demography.tsx | 454 +++++ .../PatientDetailsTab/EncounterHistory.tsx | 90 + .../HealthProfileSummary.tsx | 135 ++ .../PatientDetailsTab/ImmunisationRecords.tsx | 124 ++ .../Patient/PatientDetailsTab/Notes.tsx | 184 ++ .../PatientDetailsTab/SampleTestHistory.tsx | 107 ++ .../PatientDetailsTab/ShiftingHistory.tsx | 73 + .../Patient/PatientDetailsTab/index.tsx | 45 + src/components/Patient/PatientHome.tsx | 1663 +++++------------ src/components/Patient/PatientInfoCard.tsx | 2 +- src/components/Patient/PatientRegister.tsx | 35 +- src/components/Patient/SampleTestCard.tsx | 4 +- src/components/Patient/models.tsx | 2 +- src/components/Shifting/ShiftingBlock.tsx | 43 +- src/components/Shifting/ShiftingList.tsx | 228 +-- src/components/Shifting/ShiftingTable.tsx | 261 +++ src/components/Users/ManageUsers.tsx | 2 +- 38 files changed, 2390 insertions(+), 1766 deletions(-) create mode 100644 src/components/Patient/PatientDetailsTab/Demography.tsx create mode 100644 src/components/Patient/PatientDetailsTab/EncounterHistory.tsx create mode 100644 src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx create mode 100644 src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx create mode 100644 src/components/Patient/PatientDetailsTab/Notes.tsx create mode 100644 src/components/Patient/PatientDetailsTab/SampleTestHistory.tsx create mode 100644 src/components/Patient/PatientDetailsTab/ShiftingHistory.tsx create mode 100644 src/components/Patient/PatientDetailsTab/index.tsx create mode 100644 src/components/Shifting/ShiftingTable.tsx diff --git a/cypress/e2e/patient_spec/PatientRegistration.cy.ts b/cypress/e2e/patient_spec/PatientRegistration.cy.ts index 2058760f6d2..2da35c840e0 100644 --- a/cypress/e2e/patient_spec/PatientRegistration.cy.ts +++ b/cypress/e2e/patient_spec/PatientRegistration.cy.ts @@ -132,6 +132,7 @@ describe("Patient Creation with consultation", () => { "Middle Class", "Family member", ); + patientMedicalHistory.verifyPatientMedicalDetails( patientOnePresentHealth, patientOneOngoingMedication, @@ -216,11 +217,9 @@ describe("Patient Creation with consultation", () => { patientMedicalHistory.verifyNoSymptosPresent("Diabetes"); // verify insurance details and dedicatd page cy.get("[data-testid=patient-details]") - .contains("member id") + .contains("Member ID") .scrollIntoView(); cy.wait(2000); - patientInsurance.clickPatientInsuranceViewDetail(); - cy.wait(3000); patientInsurance.verifyPatientPolicyDetails( patientOneFirstSubscriberId, patientOneFirstPolicyId, diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts index c8046a39a9e..edd8ae135a4 100644 --- a/cypress/pageobject/Patient/PatientConsultation.ts +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -122,9 +122,10 @@ export class PatientConsultationPage { } clickViewConsultationButton() { + cy.get("a").contains("Encounters").click(); cy.verifyAndClickElement( "#view_consultation_and_log_updates", - "View Consultation / Log Updates", + "View Updates", ); } diff --git a/cypress/pageobject/Patient/PatientCreation.ts b/cypress/pageobject/Patient/PatientCreation.ts index a38b8ab6a5c..1f915f5474e 100644 --- a/cypress/pageobject/Patient/PatientCreation.ts +++ b/cypress/pageobject/Patient/PatientCreation.ts @@ -181,7 +181,7 @@ export class PatientPage { expect($dashboard).to.contain(patientName); expect($dashboard).to.contain(phoneNumber); expect($dashboard).to.contain(emergencyPhoneNumber); - expect($dashboard).to.contain(yearOfBirth); + //expect($dashboard).to.contain(yearOfBirth); //Commented out because new proposed UI does not have DOB. Can change later. expect($dashboard).to.contain(bloodGroup); expect($dashboard).to.contain(occupation); socioeconomicStatus && expect($dashboard).to.contain(socioeconomicStatus); @@ -221,7 +221,7 @@ export class PatientPage { } clickPatientUpdateDetails() { - cy.verifyAndClickElement("#update-patient-details", "Update Details"); + cy.verifyAndClickElement("#update-patient-details", "Edit Profile"); } interceptFacilities() { diff --git a/cypress/pageobject/Patient/PatientMedicalHistory.ts b/cypress/pageobject/Patient/PatientMedicalHistory.ts index 93fdd1b38b3..bf2296b4471 100644 --- a/cypress/pageobject/Patient/PatientMedicalHistory.ts +++ b/cypress/pageobject/Patient/PatientMedicalHistory.ts @@ -33,7 +33,9 @@ class PatientMedicalHistory { patientSymptoms6: string, patientSymptoms7: string, ) { - cy.get("[data-testid=patient-details]").then(($dashboard) => { + cy.get("a").contains("Health Profile").click(); + cy.wait(2000); + cy.get("[data-test-id=patient-health-profile]").then(($dashboard) => { cy.url().should("include", "/facility/"); expect($dashboard).to.contain(patientPresentHealth); expect($dashboard).to.contain(patientOngoingMedication); diff --git a/cypress/pageobject/Sample/SampleTestCreate.ts b/cypress/pageobject/Sample/SampleTestCreate.ts index cd2e93e5072..445d08732c3 100644 --- a/cypress/pageobject/Sample/SampleTestCreate.ts +++ b/cypress/pageobject/Sample/SampleTestCreate.ts @@ -1,5 +1,6 @@ export class SampleTestPage { visitSampleRequestPage(): void { + cy.get("a").contains("Service Request").click(); cy.verifyAndClickElement("#sample-request-btn", "Request Sample Test"); cy.url().should("include", "/sample-test"); } @@ -60,6 +61,7 @@ export class SampleTestPage { fastTrack: string, sampleTestResult: string, ): void { + cy.get("a").contains("Service Request").click(); cy.verifyContentPresence("#sample-test-status", [sampleTestStatus]); cy.verifyContentPresence("#sample-test-type", [sampleTestType]); cy.verifyContentPresence("#sample-test-fast-track", [fastTrack]); diff --git a/public/locale/en.json b/public/locale/en.json index 8a4a36a1a5c..56f84a1883a 100644 --- a/public/locale/en.json +++ b/public/locale/en.json @@ -274,6 +274,7 @@ "accept_all": "Accept All", "access_level": "Access Level", "action_irreversible": "This action is irreversible", + "actions": "Actions", "active": "Active", "active_prescriptions": "Active Prescriptions", "add": "Add", @@ -281,7 +282,10 @@ "add_attachments": "Add Attachments", "add_beds": "Add Bed(s)", "add_beds_to_configure_presets": "Add beds to this location to configure presets for them.", + "add_consultation": "Add consultation", + "add_consultation_update": "Add Consultation Update", "add_details_of_patient": "Add Details of Patient", + "add_insurance_details": "Add Insurance Details", "add_location": "Add Location", "add_new_beds": "Add New Bed(s)", "add_new_user": "Add New User", @@ -301,11 +305,14 @@ "administered_on": "Administered on", "administration_dosage_range_error": "Dosage should be between start and target dosage", "administration_notes": "Administration Notes", + "admitted": "Admitted", + "admitted_on": "Admitted On", "advanced_filters": "Advanced Filters", "age": "Age", "all_changes_have_been_saved": "All changes have been saved", "all_details": "All Details", "allergies": "Allergies", + "allow_transfer": "Allow Transfer", "allowed_formats_are": "Allowed formats are", "already_a_member": "Already a member?", "ambulance_driver_name": "Name of ambulance driver", @@ -316,6 +323,7 @@ "any_id_description": "Currently we support: Aadhaar Number / Mobile Number", "any_other_comments": "Any other comments", "apply": "Apply", + "approve": "Approve", "approved_by_district_covid_control_room": "Approved by District COVID Control Room", "approving_facility": "Name of Approving Facility", "archive": "Archive", @@ -332,8 +340,14 @@ "asset_qr_id": "Asset QR ID", "asset_type": "Asset Type", "assets": "Assets", + "assign": "Assign", + "assign_a_volunteer_to": "Assign a volunteer to {{name}}", + "assign_bed": "Assign Bed", + "assign_to_volunteer": "Assign to a Volunteer", + "assigned_doctor": "Assigned Doctor", "assigned_facility": "Facility assigned", "assigned_to": "Assigned to", + "assigned_volunteer": "Assigned Volunteer", "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", @@ -365,6 +379,7 @@ "bed_capacity": "Bed Capacity", "bed_created_notification_one": "{{count}} Bed created successfully", "bed_created_notification_other": "{{count}} Beds created successfully", + "bed_history": "Bed History", "bed_not_linked_to_camera": "This bed has not been linked to this camera.", "bed_search_placeholder": "Search by beds name", "bed_type": "Bed Type", @@ -397,6 +412,7 @@ "central_nursing_station": "Central Nursing Station", "change_file": "Change File", "change_password": "Change Password", + "chat_on_whatsapp": "Chat on Whatsapp", "check_eligibility": "Check Eligibility", "check_for_available_update": "Check for available update", "check_for_update": "Check for Update", @@ -493,6 +509,7 @@ "consent_request_rejected": "Patient has rejected the consent request", "consent_request_waiting_approval": "Waiting for the Patient to approve the consent request", "consent_requested_successfully": "Consent requested successfully!", + "consultation_history": "Consultation History", "consultation_missing_warning": "You have not created a consultation for the patient in", "consultation_not_filed": "You have not filed a consultation for this patient yet.", "consultation_not_filed_description": "Please file a consultation for this patient to continue.", @@ -516,6 +533,7 @@ "countries_travelled": "Countries travelled", "covid_19_cat_gov": "Covid_19 Clinical Category as per Govt. of Kerala guideline (A/B/C)", "covid_19_death_reporting_form_1": "Covid-19 Death Reporting : Form 1", + "covid_details": "Covid Details", "create": "Create", "create_abha_address": "Create ABHA Address", "create_add_more": "Create & Add More", @@ -533,6 +551,7 @@ "created_date": "Created Date", "created_on": "Created On", "csv_file_in_the_specified_format": "Select a CSV file in the specified format", + "current_address": "Current Address", "current_password": "Current Password", "customer_support_email": "Customer Support Email", "customer_support_name": "Customer Support Name", @@ -546,13 +565,16 @@ "date_of_birth": "Date of birth", "date_of_positive_covid_19_swab": "Date of Positive Covid 19 Swab", "date_of_result": "Covid confirmation date", + "date_of_return": "Date of Return", "date_of_test": "Date of sample collection for Covid testing", "days": "Days", + "death_report": "Death Report", "delete": "Delete", "delete_facility": "Delete Facility", "delete_item": "Delete {{name}}", "delete_record": "Delete Record", "deleted_successfully": "{{name}} deleted successfully", + "demography": "Demography", "denied_on": "Denied On", "describe_why_the_asset_is_not_working": "Describe why the asset is not working", "description": "Description", @@ -573,6 +595,7 @@ "diastolic": "Diastolic", "differential": "Differential", "differential_diagnosis": "Differential diagnosis", + "disable_transfer": "Disable Transfer", "discard": "Discard", "discharge": "Discharge", "discharge_from_care": "Discharge from CARE", @@ -580,6 +603,7 @@ "discharge_summary": "Discharge Summary", "discharge_summary_not_ready": "Discharge summary is not ready yet.", "discharged": "Discharged", + "discharged_on": "Discharged On", "discharged_patients": "Discharged Patients", "discharged_patients_empty": "No discharged patients present in this facility", "disclaimer": "Disclaimer", @@ -614,6 +638,7 @@ "edit_policy": "Edit Insurance Policy", "edit_policy_description": "Add or edit patient's insurance details", "edit_prescriptions": "Edit Prescriptions", + "edit_profile": "Edit Profile", "edit_user_profile": "Edit Profile", "edited_by": "Edited by", "edited_on": "Edited on", @@ -627,7 +652,11 @@ "email_discharge_summary_description": "Enter your valid email address to receive the discharge summary", "email_success": "We will be sending an email shortly. Please check your inbox.", "emergency": "Emergency", + "emergency_contact": "Emergency Contact", "emergency_contact_number": "Emergency Contact Number", + "emergency_contact_person_name": "Emergency Contact Person Name", + "emergency_contact_person_name_volunteer": "Emergency Contact Person Name (Volunteer)", + "emergency_contact_volunteer": "Emergency Contact (Volunteer)", "empty_date_time": "--:-- --; --/--/----", "encounter_date_field_label__A": "Date & Time of Admission to the Facility", "encounter_date_field_label__DC": "Date & Time of Domiciliary Care commencement", @@ -643,6 +672,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", + "encounters": "Encounters", "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", @@ -664,6 +694,7 @@ "events": "Events", "expand_sidebar": "Expand Sidebar", "expected_burn_rate": "Expected Burn Rate", + "expired": "Expired", "expired_on": "Expired On", "expires_on": "Expires On", "facilities": "Facilities", @@ -721,6 +752,7 @@ "granted_on": "Granted On", "has_domestic_healthcare_support": "Has domestic healthcare support?", "has_sari": "Has SARI (Severe Acute Respiratory illness)?", + "health-profile": "Health Profile", "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", @@ -757,12 +789,17 @@ "i_declare": "I hereby declare that:", "icd11_as_recommended": "As per ICD-11 recommended by WHO", "icmr_specimen_referral_form": "ICMR Specimen Referral Form", + "immunisation-records": "Immunisation", "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", "indicator": "Indicator", "inidcator_event": "Indicator Event", "instruction_on_titration": "Instruction on titration", + "insurance__insurer_id": "Insurer ID", + "insurance__insurer_name": "Insurer Name", + "insurance__member_id": "Member ID", + "insurance__policy_name": "Policy ID / Policy Name", "insurer_name_required": "Insurer Name is required", "international_mobile": "International Mobile", "invalid_asset_id_msg": "Oops! The asset ID you entered does not appear to be valid.", @@ -771,6 +808,7 @@ "invalid_link_msg": "It appears that the password reset link you have used is either invalid or expired. Please request a new password reset link.", "invalid_password": "Password doesn't meet the requirements", "invalid_password_reset_link": "Invalid password reset link", + "invalid_patient_data": "Invalid Patient Data", "invalid_phone": "Please enter valid phone number", "invalid_phone_number": "Invalid Phone Number", "invalid_pincode": "Invalid Pincode", @@ -789,6 +827,8 @@ "investigations__result": "Result", "investigations__unit": "Unit", "investigations_suggested": "Investigations Suggested", + "investigations_summary": "Investigations Summary", + "ip_encounter": "IP Encounter", "is": "Is", "is_antenatal": "Is Antenatal", "is_atypical_presentation": "Is Atypical presentation", @@ -796,21 +836,26 @@ "is_emergency": "Is emergency", "is_emergency_case": "Is emergency case", "is_it_upshift": "is it upshift", + "is_pregnant": "Is pregnant", "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", + "kasp_enabled_date": "{{kasp_string}} enabled date", "label": "Label", "landline": "Indian landline", "language_selection": "Language Selection", "last_administered": "Last administered", + "last_discharge_reason": "Last Discharge Reason", "last_edited": "Last Edited", "last_modified": "Last Modified", "last_name": "Last Name", "last_online": "Last Online", "last_serviced_on": "Last Serviced On", + "last_updated_by": "Last updated by", + "last_vaccinated_on": "Last Vaccinated on", "latitude_invalid": "Latitude must be between -90 and 90", "left": "Left", "length": "Length ({{unit}})", @@ -859,6 +904,7 @@ "max_size_for_image_uploaded_should_be": "Max size for image uploaded should be", "measured_after": "Measured after", "measured_before": "Measured before", + "medical": "Medical", "medical_council_registration": "Medical Council Registration", "medical_worker": "Medical Worker", "medicine": "Medicine", @@ -903,6 +949,8 @@ "no_beds_available": "No beds available", "no_changes": "No changes", "no_changes_made": "No changes made", + "no_consultation_filed": "No consultation filed", + "no_consultation_history": "No consultation history available", "no_consultation_updates": "No consultation updates", "no_data_found": "No data found", "no_duplicate_facility": "You should not create duplicate facilities", @@ -915,6 +963,7 @@ "no_linked_facilities": "No Linked Facilities", "no_log_update_delta": "No changes since previous log update", "no_log_updates": "No log updates found", + "no_medical_history_available": "No Medical History Available", "no_notices_for_you": "No notices for you.", "no_patients_found": "No Patients Found", "no_patients_to_show": "No patients to show.", @@ -924,6 +973,7 @@ "no_records_found": "No Records Found", "no_remarks": "No remarks", "no_results_found": "No Results Found", + "no_social_profile_details_available": "No Social Profile Details Available", "no_staff": "No staff found", "no_tests_taken": "No tests taken", "no_treating_physicians_available": "This facility does not have any home facility doctors. Please contact your admin.", @@ -943,6 +993,7 @@ "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", + "number_of_covid_vaccine_doses": "Number of Covid vaccine doses", "nursing_care": "Nursing Care", "nursing_information": "Nursing Information", "nutrition": "Nutrition", @@ -952,6 +1003,8 @@ "on": "On", "ongoing_medications": "Ongoing Medications", "only_indian_mobile_numbers_supported": "Currently only Indian numbers are supported", + "op_encounter": "OP Encounter", + "op_file_closed": "OP file closed", "open": "Open", "open_camera": "Open Camera", "open_live_monitoring": "Open Live Monitoring", @@ -974,6 +1027,11 @@ "password_reset_success": "Password Reset successfully", "password_sent": "Password Reset Email Sent", "patient": "Patient", + "patient-notes": "Notes", + "patient__general-info": "General Info", + "patient__insurance-details": "Insurance Details", + "patient__social-profile": "Social Profile", + "patient__volunteer-contact": "Volunteer Contact", "patient_address": "Patient Address", "patient_body": "Patient Body", "patient_category": "Patient Category", @@ -997,6 +1055,8 @@ "patient_notes_thread__Doctors": "Doctor's Discussions", "patient_notes_thread__Nurses": "Nurse's Discussions", "patient_phone_number": "Patient Phone Number", + "patient_profile": "Patient Profile", + "patient_profile_created_by": "Patient profile created by", "patient_registration__address": "Address", "patient_registration__age": "Age", "patient_registration__comorbidities": "Comorbidities", @@ -1009,12 +1069,15 @@ "patient_status": "Patient Status", "patient_transfer_birth_match_note": "Note: Year of birth must match the patient to process the transfer request.", "patients": "Patients", + "permanent_address": "Permanent Address", + "permission_denied": "You do not have permission to perform this action", "personal_information": "Personal Information", "phone": "Phone", "phone_no": "Phone no.", "phone_number": "Phone Number", "phone_number_at_current_facility": "Phone Number of Contact person at current Facility", "pincode": "Pincode", + "please_assign_bed_to_patient": "Please assign a bed to this patient", "please_enter_a_reason_for_the_shift": "Please enter a reason for the shift.", "please_select_a_facility": "Please select a facility", "please_select_breathlessness_level": "Please select Breathlessness Level", @@ -1036,6 +1099,7 @@ "policy__subscriber_id__example": "SUB001", "policy_id_required": "Policy Id or Policy Name is required", "position": "Position", + "post_partum": "Post-partum", "post_your_comment": "Post Your Comment", "powered_by": "Powered By", "preferred_facility_type": "Preferred Facility Type", @@ -1050,6 +1114,7 @@ "prescriptions__medicine": "Medicine", "prescriptions__route": "Route", "prescriptions__start_date": "Prescribed On", + "present_health": "Present Health", "preset_deleted": "Preset deleted", "preset_name_placeholder": "Specify an identifiable name for the new preset", "preset_updated": "Preset updated", @@ -1100,9 +1165,11 @@ "req_atleast_one_lowercase": "Require at least one lower case letter", "req_atleast_one_symbol": "Require at least one symbol", "req_atleast_one_uppercase": "Require at least one upper case", + "request-sample-test": "Service Request", "request_consent": "Request Consent", "request_description": "Description of Request", "request_description_placeholder": "Type your description here", + "request_sample_test": "Request Sample Test", "request_title": "Request Title", "request_title_placeholder": "Type your title here", "required": "Required", @@ -1126,6 +1193,8 @@ "return_to_login": "Return to Login", "return_to_password_reset": "Return to Password Reset", "return_to_patient_dashboard": "Return to Patient Dashboard", + "review_before": "Review Before", + "review_missed": "Review Missed", "revoked_on": "Revoked On", "right": "Right", "route": "Route", @@ -1173,6 +1242,8 @@ "send_otp_error": "Failed to send OTP. Please try again later.", "send_otp_success": "OTP has been sent to the respective mobile number", "send_reset_link": "Send Reset Link", + "send_sample_to_collection_centre_description": "Are you sure you want to send the sample to Collection Centre?", + "send_sample_to_collection_centre_title": "Send sample to collection centre", "serial_number": "Serial Number", "serviced_on": "Serviced on", "session_expired": "Session Expired", @@ -1181,6 +1252,8 @@ "set_your_local_language": "Set your local language", "settings_and_filters": "Settings and Filters", "severity_of_breathlessness": "Severity of Breathlessness", + "sex": "Sex", + "shift": "Shift Patient", "shift_request_updated_successfully": "Shift request updated successfully", "shifting": "Shifting", "shifting_approval_facility": "Shifting approval facility", @@ -1188,6 +1261,7 @@ "shifting_approving_facility_can_not_be_empty": "Shifting approving facility can not be empty.", "shifting_deleted": "Shifting record has been deleted successfully.", "shifting_details": "Shifting details", + "shifting_history": "Shifting History", "shifting_status": "Shifting status", "show_abha_profile": "Show ABHA Profile", "show_all": "Show all", @@ -1197,6 +1271,7 @@ "show_unread_notifications": "Show Unread", "sign_out": "Sign Out", "skills": "Skills", + "social_profile": "Social Profile", "socioeconomic_status": "Socioeconomic status", "software_update": "Software Update", "something_went_wrong": "Something went wrong..!", @@ -1225,9 +1300,11 @@ "subscription_error": "Subscription Error", "subscription_failed": "Subscription Failed", "suggested_investigations": "Suggested Investigations", + "suggestion": "Suggestion", "summary": "Summary", "support": "Support", "switch": "Switch", + "switch_bed": "Switch Bed", "switch_camera_is_not_available": "Switch camera is not available.", "symptoms": "Symptoms", "systolic": "Systolic", @@ -1244,7 +1321,10 @@ "total_users": "Total Users", "transcript_edit_info": "You can update this if we made an error", "transcript_information": "This is what we heard", + "transfer_allowed": "Transfer Allowed", + "transfer_blocked": "Transfer Blocked", "transfer_in_progress": "TRANSFER IN PROGRESS", + "transfer_status_updated": "Transfer Status Updated", "transfer_to_receiving_facility": "Transfer to receiving facility", "travel_within_last_28_days": "Domestic/international Travel (within last 28 days)", "treating_doctor": "Treating Doctor", @@ -1305,6 +1385,8 @@ "username": "Username", "users": "Users", "vacant": "Vacant", + "vaccinated": "Vaccinated", + "vaccine_name": "Vaccine name", "vehicle_preference": "Vehicle preference", "vendor_name": "Vendor Name", "ventilator_interface": "Respiratory Support Type", @@ -1320,21 +1402,30 @@ "verify_otp_success": "OTP has been verified successfully.", "verify_patient_identifier": "Please verify the patient identifier", "verify_using": "Verify Using", + "video_call": "Video Call", "video_conference_link": "Video Conference Link", "view": "View", "view_abdm_records": "View ABDM Records", + "view_all_details": "View All Details", "view_asset": "View Assets", "view_cns": "View CNS", "view_consultation_and_log_updates": "View Consultation / Log Updates", "view_details": "View Details", "view_faciliy": "View Facility", + "view_files": "View Files", "view_patients": "View Patients", + "view_update_patient_files": "View/Update patient files", + "view_updates": "View Updates", "view_users": "View Users", + "village": "Village", "virtual_nursing_assistant": "Virtual Nursing Assistant", "vitals": "Vitals", "vitals_monitor": "Vitals Monitor", "vitals_present": "Vitals Monitor present", "voice_autofill": "Voice Autofill", + "volunteer_assigned": "Volunteer assigned successfuly", + "volunteer_contact": "Volunteer Contact", + "volunteer_unassigned": "Volunteer unassigned successfuly", "ward": "Ward", "warranty_amc_expiry": "Warranty / AMC Expiry", "what_facility_assign_the_patient_to": "What facility would you like to assign the patient to", diff --git a/public/locale/hi.json b/public/locale/hi.json index 0eb0cc28cfd..b3c172021dd 100644 --- a/public/locale/hi.json +++ b/public/locale/hi.json @@ -567,6 +567,7 @@ "patient_name": "मरीज का नाम", "patient_no": "ओपी/आईपी संख्या", "patient_phone_number": "मरीज़ का फ़ोन नंबर", + "patient_profile": "रोगी प्रोफ़ाइल", "patient_registration__address": "पता", "patient_registration__age": "आयु", "patient_registration__comorbidities": "comorbidities", diff --git a/public/locale/kn.json b/public/locale/kn.json index be18432e0ce..faf50da3a23 100644 --- a/public/locale/kn.json +++ b/public/locale/kn.json @@ -568,6 +568,7 @@ "patient_name": "ರೋಗಿಯ ಹೆಸರು", "patient_no": "OP/IP ಸಂ", "patient_phone_number": "ರೋಗಿಯ ಫೋನ್ ಸಂಖ್ಯೆ", + "patient_profile": "ರೋಗಿ ಪ್ರೊಫೈಲ್", "patient_registration__address": "ವಿಳಾಸ", "patient_registration__age": "ವಯಸ್ಸು", "patient_registration__comorbidities": "ಸಹವರ್ತಿ ರೋಗಗಳು", diff --git a/public/locale/ml.json b/public/locale/ml.json index cf381c32c0e..33dadcf9fb7 100644 --- a/public/locale/ml.json +++ b/public/locale/ml.json @@ -567,6 +567,7 @@ "patient_name": "രോഗിയുടെ പേര്", "patient_no": "OP/IP നമ്പർ", "patient_phone_number": "രോഗിയുടെ ഫോൺ നമ്പർ", + "patient_profile": "രോഗിയുടെ പ്രൊഫൈൽ", "patient_registration__address": "വിലാസം", "patient_registration__age": "പ്രായം", "patient_registration__comorbidities": "കോമോർബിഡിറ്റികൾ", diff --git a/public/locale/mr.json b/public/locale/mr.json index 06e9c5bbbde..543278bf867 100644 --- a/public/locale/mr.json +++ b/public/locale/mr.json @@ -49,6 +49,7 @@ "new_password": "नवीन पासवर्ड", "no_duplicate_facility": "बनावट हॉस्पिटल सुविधा मुळीच तयार करू नका", "no_facilities": "कोणतेही हॉस्पिटल नाही", + "patient_profile": "रुग्ण प्रोफाइल", "password": "पासवर्ड", "password_mismatch": "\"पासवर्ड\" आणि \"पासवर्डची खात्री करा\" दोन्ही सारखे हवेत.", "password_reset_failure": "पासवर्ड रिसेट झाला नाही", @@ -62,5 +63,36 @@ "send_reset_link": "रीसेट लिंक पाठवा", "sign_out": "साइन आउट", "something_wrong": "काहीतरी चूक झाली! पुन्हा प्रयत्न करा", - "username": "युजरनेम" + "username": "युजरनेम", + "general_info": "सामान्य माहिती", + "phone": "फोन", + "date_of_birth": "जन्म तारीख", + "sex": "लिंग", + "emergency_contact": "आपत्कालीन संपर्क", + "emergency_contact_person_name": "आपत्कालीन संपर्क व्यक्तीचे नाव", + "covid_details": "कोविड तपशील", + "number_of_covid_vaccine_doses": "कोविड लसीचे डोस", + "vaccine_name": "लस नाव", + "last_vaccinated_on": "शेवटचा लसीकरणाचा दिवस", + "countries_travelled": "प्रवास केलेले देश", + "date_of_return": "परतण्याची तारीख", + "social_profile": "सामाजिक प्रोफाइल", + "no_social_profile_details_available": "कोणतीही सामाजिक प्रोफाइल माहिती उपलब्ध नाही", + "location": "स्थान", + "current_address": "सध्याचा पत्ता", + "permanent_address": "स्थायी पत्ता", + "nationality": "राष्ट्रीयत्व", + "state": "राज्य", + "local_body": "स्थानिक संस्था", + "ward": "वॉर्ड", + "village": "गाव", + "volunteer_contact": "स्वयंसेवक संपर्क", + "emergency_contact_volunteer": "आपत्कालीन संपर्क (स्वयंसेवक)", + "emergency_contact_person_name_volunteer": "आपत्कालीन संपर्क व्यक्तीचे नाव (स्वयंसेवक)", + "medical": "वैद्यकीय", + "no_medical_history_available": "कोणताही वैद्यकीय इतिहास उपलब्ध नाही", + "present_health": "सध्याचे आरोग्य", + "ongoing_medications": "चालू औषधे", + "allergies": "अॅलर्जी", + "is_pregnant": "गर्भवती आहे" } diff --git a/src/CAREUI/display/Chip.tsx b/src/CAREUI/display/Chip.tsx index a2bacf8bd61..e501bf26f90 100644 --- a/src/CAREUI/display/Chip.tsx +++ b/src/CAREUI/display/Chip.tsx @@ -26,7 +26,7 @@ export default function Chip({ -
+
<>
{" "} @@ -121,7 +121,7 @@ export default function AppRouter() {
-
+
@@ -91,32 +91,26 @@ export default function Breadcrumbs({ +
{
+
+ +
+
- + + {showPatientNotesPopup && ( + - - {showPatientNotesPopup && ( - - )} -
+ )} ); }; diff --git a/src/components/Patient/InsuranceDetails.tsx b/src/components/Patient/InsuranceDetails.tsx index 248c7304bfa..3efe7866316 100644 --- a/src/components/Patient/InsuranceDetails.tsx +++ b/src/components/Patient/InsuranceDetails.tsx @@ -1,7 +1,7 @@ import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; import { HCXPolicyModel } from "@/components/HCX/models"; -import { InsuranceDetialsCard } from "@/components/Patient/InsuranceDetailsCard"; +import { InsuranceDetailsCard } from "@/components/Patient/InsuranceDetailsCard"; import routes from "@/Utils/request/api"; import useQuery from "@/Utils/request/useQuery"; @@ -53,7 +53,7 @@ export const InsuranceDetails = (props: IProps) => { data-testid="patient-details" > {insuranceDetials?.results.map((data: HCXPolicyModel) => ( - + ))}
)} diff --git a/src/components/Patient/InsuranceDetailsCard.tsx b/src/components/Patient/InsuranceDetailsCard.tsx index 62098fc6a07..6685e7a99a2 100644 --- a/src/components/Patient/InsuranceDetailsCard.tsx +++ b/src/components/Patient/InsuranceDetailsCard.tsx @@ -1,79 +1,53 @@ -import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; -import ButtonV2 from "@/components/Common/ButtonV2"; import { HCXPolicyModel } from "@/components/HCX/models"; interface InsuranceDetails { - data?: HCXPolicyModel; - showViewAllDetails?: boolean; + data: HCXPolicyModel; } -export const InsuranceDetialsCard = (props: InsuranceDetails) => { - const { data, showViewAllDetails } = props; +export const InsuranceDetailsCard = (props: InsuranceDetails) => { + const { data } = props; + + const { t } = useTranslation(); return (
-
-
- Policy Details -
- {data ? ( -
-
-
- Member ID -
-
- {data.subscriber_id || ""} -
+
+
+
+
+ {t("insurance__member_id")}
-
-
- Policy ID / Policy Name -
-
- {data.policy_id || ""} -
+
+ {data.subscriber_id || ""}
-
-
- Insurer ID -
-
- {data.insurer_id || ""} -
+
+
+
+ {t("insurance__policy_name")}
-
-
- Insurer Name -
-
- {data.insurer_name || ""} -
+
+ {data.policy_id || ""}
- {showViewAllDetails && ( -
-
- { - navigate( - `/facility/${data.patient_object?.facility_object?.id}/patient/${data.patient_object?.id}/insurance`, - ); - }} - className="h-auto whitespace-pre-wrap border border-secondary-500 bg-white text-black hover:bg-secondary-300" - > - View All Details - -
-
- )}
- ) : ( -
- No Insurance Details Available +
+
+ {t("insurance__insurer_id")} +
+
+ {data.insurer_id || ""} +
- )} +
+
+ {t("insurance__insurer_name")} +
+
+ {data.insurer_name || ""} +
+
+
); diff --git a/src/components/Patient/PatientDetailsTab/Demography.tsx b/src/components/Patient/PatientDetailsTab/Demography.tsx new file mode 100644 index 00000000000..7ac854b8ee6 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/Demography.tsx @@ -0,0 +1,454 @@ +import dayjs from "dayjs"; +import { navigate } from "raviger"; +import { Fragment, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import Chip from "@/CAREUI/display/Chip"; +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import ButtonV2 from "@/components/Common/ButtonV2"; + +import useAuthUser from "@/hooks/useAuthUser"; + +import { GENDER_TYPES } from "@/common/constants"; + +import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; +import routes from "@/Utils/request/api"; +import useQuery from "@/Utils/request/useQuery"; +import { formatName, formatPatientAge } from "@/Utils/utils"; + +import { PatientProps } from "."; +import * as Notification from "../../../Utils/Notifications"; +import { InsuranceDetailsCard } from "../InsuranceDetailsCard"; +import { parseOccupation } from "../PatientHome"; +import { AssignedToObjectModel } from "../models"; + +export const Demography = (props: PatientProps) => { + const { patientData, facilityId, id } = props; + const authUser = useAuthUser(); + const { t } = useTranslation(); + const [_assignedVolunteerObject, setAssignedVolunteerObject] = + useState(); + + const [activeSection, setActiveSection] = useState(null); + + useEffect(() => { + setAssignedVolunteerObject(patientData.assigned_to_object); + + const observedSections: Element[] = []; + const sections = document.querySelectorAll("div[id]"); + const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + setActiveSection(entry.target.id); + } + }); + }, + { + threshold: 0.6, + }, + ); + + sections.forEach((section) => { + observer.observe(section); + observedSections.push(section); + }); + + return () => { + observedSections.forEach((section) => observer.unobserve(section)); + }; + }, [patientData.assigned_to_object]); + + const { data: insuranceDetials } = useQuery(routes.hcx.policies.list, { + query: { + patient: id, + }, + }); + + const patientGender = GENDER_TYPES.find( + (i) => i.id === patientData.gender, + )?.text; + + const scrollToSection = (sectionId: string) => { + const section = document.getElementById(sectionId); + if (section) { + section.scrollIntoView({ behavior: "smooth" }); + } + }; + + const handleEditClick = (sectionId: string) => { + navigate( + `/facility/${facilityId}/patient/${id}/update?section=${sectionId}`, + ); + }; + + const hasEditPermission = () => { + const showAllFacilityUsers = ["DistrictAdmin", "StateAdmin"]; + if ( + !showAllFacilityUsers.includes(authUser.user_type) && + authUser.home_facility_object?.id !== patientData.facility + ) { + Notification.Error({ + msg: "Oops! Non-Home facility users don't have permission to perform this action.", + }); + return false; + } + return true; + }; + + const EmergencyContact = (props: { number?: string; name?: string }) => ( +
+
+ {/* Emergency Contact Section */} +
+
+ {t("emergency_contact")} +
+ +
+ + {props.number && ( + + )} +
+
+ +
+
+ {t("emergency_contact_person_name")} +
+
+ {props.name || "-"} +
+
+
+
+ ); + + const withPermissionCheck = (action: () => void) => () => { + if (!hasEditPermission()) { + Notification.Error({ + msg: t("permission_denied"), + }); + return; + } + action(); + }; + + type Data = { + id: string; + hidden?: boolean; + allowEdit?: boolean; + details: (React.ReactNode | { label: string; value: React.ReactNode })[]; + }; + + const data: Data[] = [ + { + id: "general-info", + allowEdit: true, + details: [ + { label: t("full_name"), value: patientData.name }, + { + label: t("phone_number"), + value: ( + + ), + }, + { + label: t("date_of_birth"), + value: ( + <> + {dayjs(patientData.date_of_birth).format("DD MMM YYYY")} ( + {formatPatientAge(patientData, true)}) + + ), + }, + { + label: t("sex"), + value: patientGender, + }, + , + { + label: t("current_address"), + value: patientData.address, + }, + { + label: t("permanent_address"), + value: patientData.permanent_address, + }, + { + label: t("nationality"), + value: patientData.nationality, + }, + { + label: t("state"), + value: patientData.state, + }, + { + label: t("district"), + value: patientData.district_object?.name, + }, + { + label: t("local_body"), + value: patientData.local_body_object?.name, + }, + { + label: t("ward"), + value: ( + <> + {(patientData.ward_object && + patientData.ward_object.number + + ", " + + patientData.ward_object.name) || + "-"} + + ), + }, + { + label: t("village"), + value: patientData.village, + }, + ], + }, + { + id: "social-profile", + allowEdit: true, + details: [ + { + label: t("occupation"), + value: parseOccupation(patientData.meta_info?.occupation), + }, + { + label: t("ration_card_category"), + value: + !!patientData.ration_card_category && + t(`ration_card__${patientData.ration_card_category}`), + }, + { + label: t("socioeconomic_status"), + value: + patientData.meta_info?.socioeconomic_status && + t( + `SOCIOECONOMIC_STATUS__${patientData.meta_info?.socioeconomic_status}`, + ), + }, + { + label: t("domestic_healthcare_support"), + value: + patientData.meta_info?.domestic_healthcare_support && + t( + `DOMESTIC_HEALTHCARE_SUPPORT__${patientData.meta_info?.domestic_healthcare_support}`, + ), + }, + ], + }, + { + id: "volunteer-contact", + hidden: !patientData.assigned_to_object, + details: [ + , + ], + }, + { + id: "insurance-details", + details: [ +
+ {insuranceDetials?.results.map((insurance) => ( + + ))} + {!!insuranceDetials?.results && + insuranceDetials.results.length === 0 && ( +
+ {t("no_data_found")} +
+ )} + + handleEditClick("insurance-details"), + )} + > + + {t("add_insurance_details")} + +
, + ], + }, + ]; + + return ( +
+
+
+ {data + .filter((s) => !s.hidden) + .map((subtab, i) => ( + + ))} +
+ +
+
+
+
+ {t("patient_status")} +
+
+ +
+
+
+ + navigate( + `/facility/${patientData?.facility}/patient/${id}/update`, + ), + )} + > + + {t("edit_profile")} + +
+
+ {/*
+ {[ + { label: t("abha_number"), value: "-" }, + { label: t("abha_address"), value: "-" }, + ].map((info, i) => ( +
+

+ {info.label}: +

+

+ {info.value} +

+
+ ))} +
*/} +
+ {data + .filter((s) => !s.hidden) + .map((subtab, i) => ( +
+
+
+

{t(`patient__${subtab.id}`)}

+ {subtab.allowEdit && ( + + )} +
+
+ {subtab.details.map((detail, j) => + detail && + typeof detail === "object" && + "label" in detail ? ( +
+
+ {detail.label} +
+
+ {detail.value || "-"} +
+
+ ) : ( + {detail} + ), + )} +
+
+ ))} +
+
+
+
+ ); +}; diff --git a/src/components/Patient/PatientDetailsTab/EncounterHistory.tsx b/src/components/Patient/PatientDetailsTab/EncounterHistory.tsx new file mode 100644 index 00000000000..71865483800 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/EncounterHistory.tsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import PaginatedList from "@/CAREUI/misc/PaginatedList"; + +import CircularProgress from "@/components/Common/CircularProgress"; +import Loading from "@/components/Common/Loading"; +import { ConsultationCard } from "@/components/Facility/ConsultationCard"; +import { ConsultationModel } from "@/components/Facility/models"; + +import useAuthUser from "@/hooks/useAuthUser"; + +import { triggerGoal } from "@/Integrations/Plausible"; +import routes from "@/Utils/request/api"; +import useQuery from "@/Utils/request/useQuery"; + +import { PatientProps } from "."; +import { PatientModel } from "../models"; + +const EncounterHistory = (props: PatientProps) => { + const { patientData: initialPatientData, facilityId, id } = props; + const [patientData, setPatientData] = + useState(initialPatientData); + const authUser = useAuthUser(); + + useEffect(() => { + setPatientData(initialPatientData); + }, [initialPatientData]); + + const { t } = useTranslation(); + + const { loading: isLoading, refetch } = useQuery(routes.getPatient, { + pathParams: { + id, + }, + onResponse: ({ res, data }) => { + if (res?.ok && data) { + setPatientData(data); + } + triggerGoal("Patient Profile Viewed", { + facilityId: facilityId, + userId: authUser.id, + }); + }, + }); + + if (isLoading) { + return ; + } + + return ( + + {(_) => ( +
+ + + + +
+
+ {t("no_consultation_history")} +
+
+
+ > + {(item) => ( + + )} + +
+ +
+
+ )} +
+ ); +}; + +export default EncounterHistory; diff --git a/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx b/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx new file mode 100644 index 00000000000..3fb6b60d330 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/HealthProfileSummary.tsx @@ -0,0 +1,135 @@ +import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { UserModel } from "@/components/Users/models"; + +import useAuthUser from "@/hooks/useAuthUser"; + +import { ADMIN_USER_TYPES } from "@/common/constants"; + +import { PatientProps } from "."; +import * as Notification from "../../../Utils/Notifications"; +import { PatientModel } from "../models"; + +export const HealthProfileSummary = (props: PatientProps) => { + const { patientData, facilityId, id } = props; + + const authUser = useAuthUser(); + const { t } = useTranslation(); + + const handleEditClick = (sectionId: string) => { + navigate( + `/facility/${facilityId}/patient/${id}/update?section=${sectionId}`, + ); + }; + + let patientMedHis: JSX.Element[] = []; + + if (patientData?.medical_history?.length) { + const medHis = patientData.medical_history; + patientMedHis = medHis + .filter((item) => item.disease !== "NO") + .map((item, idx) => ( +
+
+ {item.disease} +
+
+ {item.details} +
+
+ )); + } + + const canEditPatient = (authUser: UserModel, patientData: PatientModel) => { + return ( + ADMIN_USER_TYPES.includes( + authUser.user_type as (typeof ADMIN_USER_TYPES)[number], + ) || authUser.home_facility_object?.id === patientData.facility + ); + }; + + return ( +
+
+
+
+
+
+ {t("medical")} +
+ +
+ +
+
+
+ {t("present_health")} +
+
+ {patientData.present_health || "-"} +
+
+ +
+
+ {t("ongoing_medications")} +
+
+ {patientData.ongoing_medication || "-"} +
+
+ +
+
+ {t("allergies")} +
+
+ {patientData.allergies || "-"} +
+
+ + {patientData.gender === 2 && patientData.is_antenatal && ( +
+
+ {t("is_pregnant")} +
+
+ {t("yes")} +
+
+ )} + {patientMedHis} +
+
+
+
+ ); +}; diff --git a/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx b/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx new file mode 100644 index 00000000000..eb298737f3c --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/ImmunisationRecords.tsx @@ -0,0 +1,124 @@ +import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import { UserModel } from "@/components/Users/models"; + +import useAuthUser from "@/hooks/useAuthUser"; + +import { ADMIN_USER_TYPES } from "@/common/constants"; + +import { formatDateTime } from "@/Utils/utils"; + +import { PatientProps } from "."; +import * as Notification from "../../../Utils/Notifications"; +import { PatientModel } from "../models"; + +export const ImmunisationRecords = (props: PatientProps) => { + const { patientData, facilityId, id } = props; + + const authUser = useAuthUser(); + const { t } = useTranslation(); + + const handleEditClick = (sectionId: string) => { + navigate( + `/facility/${facilityId}/patient/${id}/update?section=${sectionId}`, + ); + }; + + const canEditPatient = (authUser: UserModel, patientData: PatientModel) => { + return ( + ADMIN_USER_TYPES.includes( + authUser.user_type as (typeof ADMIN_USER_TYPES)[number], + ) || authUser.home_facility_object?.id === patientData.facility + ); + }; + + return ( +
+
+
+
+
+

{t("covid_details")}

+ +
+ +
+
+
+ {t("number_of_covid_vaccine_doses")} +
+
+ {patientData.is_vaccinated && patientData.number_of_doses + ? patientData.number_of_doses + : "-"} +
+
+ +
+
+ {t("vaccine_name")} +
+
+ {patientData.is_vaccinated && patientData.vaccine_name + ? patientData.vaccine_name + : "-"} +
+
+ +
+
+ {t("last_vaccinated_on")} +
+
+ {patientData.is_vaccinated && patientData.last_vaccinated_date + ? formatDateTime(patientData.last_vaccinated_date) + : "-"} +
+
+ +
+
+ {t("countries_travelled")} +
+
+ {patientData.countries_travelled && + patientData.countries_travelled.length > 0 + ? patientData.countries_travelled.join(", ") + : "-"} +
+
+ +
+
+ {t("date_of_return")} +
+
+ {patientData.date_of_return + ? formatDateTime(patientData.date_of_return) + : "-"} +
+
+
+
+
+
+ ); +}; diff --git a/src/components/Patient/PatientDetailsTab/Notes.tsx b/src/components/Patient/PatientDetailsTab/Notes.tsx new file mode 100644 index 00000000000..646e97d3bd5 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/Notes.tsx @@ -0,0 +1,184 @@ +import { t } from "i18next"; +import { useEffect, useState } from "react"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import ButtonV2 from "@/components/Common/ButtonV2"; +import DoctorNoteReplyPreviewCard from "@/components/Facility/DoctorNoteReplyPreviewCard"; +import PatientNotesList from "@/components/Facility/PatientNotesList"; +import { + PatientNoteStateType, + PatientNotesModel, +} from "@/components/Facility/models"; +import AutoExpandingTextInputFormField from "@/components/Form/FormFields/AutoExpandingTextInputFormField"; + +import useAuthUser from "@/hooks/useAuthUser"; +import { useMessageListener } from "@/hooks/useMessageListener"; + +import { PATIENT_NOTES_THREADS } from "@/common/constants"; + +import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; +import routes from "@/Utils/request/api"; +import request from "@/Utils/request/request"; +import { classNames, keysOf } from "@/Utils/utils"; + +import * as Notification from "../../../Utils/Notifications"; + +interface PatientNotesProps { + id: string; + facilityId: string; +} + +const PatientNotes = (props: PatientNotesProps) => { + const { id: patientId, facilityId } = props; + + const authUser = useAuthUser(); + const [thread, setThread] = useState( + authUser.user_type === "Nurse" + ? PATIENT_NOTES_THREADS.Nurses + : PATIENT_NOTES_THREADS.Doctors, + ); + + const [patientActive, setPatientActive] = useState(true); + const [noteField, setNoteField] = useState(""); + const [reload, setReload] = useState(false); + const [reply_to, setReplyTo] = useState( + undefined, + ); + + const initialData: PatientNoteStateType = { + notes: [], + cPage: 1, + totalPages: 1, + }; + const [state, setState] = useState(initialData); + + const onAddNote = async () => { + if (!/\S+/.test(noteField)) { + Notification.Error({ + msg: "Note Should Contain At Least 1 Character", + }); + return; + } + + try { + const { res } = await request(routes.addPatientNote, { + pathParams: { patientId: patientId }, + body: { + note: noteField, + thread, + reply_to: reply_to?.id, + }, + }); + if (res?.status === 201) { + setNoteField(""); + setReload(!reload); + setState({ ...state, cPage: 1 }); + setReplyTo(undefined); + Notification.Success({ msg: "Note added successfully" }); + } + } catch (error) { + Notification.Error({ + msg: "Failed to add note. Please try again.", + }); + } + }; + + useEffect(() => { + async function fetchPatientName() { + if (patientId) { + try { + const { data } = await request(routes.getPatient, { + pathParams: { id: patientId }, + }); + if (data) { + setPatientActive(data.is_active ?? true); + } + } catch (error) { + Notification.Error({ + msg: "Failed to fetch patient status", + }); + } + } + } + fetchPatientName(); + }, [patientId]); + + useMessageListener((data) => { + const message = data?.message; + if ( + (message?.from == "patient/doctor_notes/create" || + message?.from == "patient/doctor_notes/edit") && + message?.facility_id == facilityId && + message?.patient_id == patientId + ) { + setReload(true); + } + }); + + return ( +
+
+
+ {keysOf(PATIENT_NOTES_THREADS).map((current) => ( + + ))} +
+ + setReplyTo(undefined)} + > +
+ setNoteField(e.value)} + className="w-full grow" + errorClassName="hidden" + innerClassName="pr-10" + placeholder={t("notes_placeholder")} + disabled={!patientActive} + /> + + + +
+
+
+
+ ); +}; + +export default PatientNotes; diff --git a/src/components/Patient/PatientDetailsTab/SampleTestHistory.tsx b/src/components/Patient/PatientDetailsTab/SampleTestHistory.tsx new file mode 100644 index 00000000000..b7a907fe662 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/SampleTestHistory.tsx @@ -0,0 +1,107 @@ +import { navigate } from "raviger"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; +import PaginatedList from "@/CAREUI/misc/PaginatedList"; + +import ButtonV2 from "@/components/Common/ButtonV2"; +import CircularProgress from "@/components/Common/CircularProgress"; + +import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; +import routes from "@/Utils/request/api"; + +import { PatientProps } from "."; +import { SampleTestCard } from "../SampleTestCard"; +import { PatientModel, SampleTestModel } from "../models"; + +export const SampleTestHistory = (props: PatientProps) => { + const { patientData, facilityId, id } = props; + const [_selectedStatus, setSelectedStatus] = useState<{ + status: number; + sample: SampleTestModel | null; + }>({ status: 0, sample: null }); + const [_showAlertMessage, setShowAlertMessage] = useState(false); + + const isPatientInactive = (patientData: PatientModel, facilityId: string) => { + if (!patientData) return true; + return ( + !patientData.is_active || + !( + patientData?.last_consultation && + patientData.last_consultation.facility === facilityId + ) + ); + }; + + const confirmApproval = (status: number, sample: SampleTestModel) => { + setSelectedStatus({ status, sample }); + setShowAlertMessage(true); + }; + + const { t } = useTranslation(); + + return ( +
+
+
+

+ {t("sample_test_history")} +

+ + navigate( + `/facility/${patientData?.facility}/patient/${id}/sample-test`, + ) + } + authorizeFor={NonReadOnlyUsers} + id="sample-request-btn" + > + + + {t("request_sample_test")} + + +
+
+ + + {(_, query) => ( +
+ + + + +
+
+ {t("no_records_found")} +
+
+
+ > + {(item) => ( + + )} + +
+ +
+
+ )} +
+
+ ); +}; diff --git a/src/components/Patient/PatientDetailsTab/ShiftingHistory.tsx b/src/components/Patient/PatientDetailsTab/ShiftingHistory.tsx new file mode 100644 index 00000000000..b7ef154bf4d --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/ShiftingHistory.tsx @@ -0,0 +1,73 @@ +import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import ButtonV2 from "@/components/Common/ButtonV2"; +import { formatFilter } from "@/components/Resource/ResourceCommons"; +import ShiftingTable from "@/components/Shifting/ShiftingTable"; + +import useFilters from "@/hooks/useFilters"; + +import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; +import routes from "@/Utils/request/api"; +import useQuery from "@/Utils/request/useQuery"; + +import { PatientProps } from "."; +import { PatientModel } from "../models"; + +const ShiftingHistory = (props: PatientProps) => { + const { patientData, facilityId, id } = props; + const { t } = useTranslation(); + const { qParams, Pagination, resultsPerPage } = useFilters({ + cacheBlacklist: ["patient_name"], + }); + + const isPatientInactive = (patientData: PatientModel, facilityId: string) => { + return ( + !patientData.is_active || + !(patientData?.last_consultation?.facility === facilityId) + ); + }; + + const { data: shiftData, loading } = useQuery(routes.listShiftRequests, { + query: { + ...formatFilter({ + ...qParams, + offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, + }), + patient: id, + }, + prefetch: !!id, + }); + + return ( +
+
+

+ {t("shifting_history")} +

+ + navigate(`/facility/${facilityId}/patient/${id}/shift/new`) + } + authorizeFor={NonReadOnlyUsers} + > + + + {t("shift")} + + +
+ +
+ +
{" "} +
+ ); +}; + +export default ShiftingHistory; diff --git a/src/components/Patient/PatientDetailsTab/index.tsx b/src/components/Patient/PatientDetailsTab/index.tsx new file mode 100644 index 00000000000..effc9b667f8 --- /dev/null +++ b/src/components/Patient/PatientDetailsTab/index.tsx @@ -0,0 +1,45 @@ +import { PatientModel } from "../models"; +import { Demography } from "./Demography"; +import EncounterHistory from "./EncounterHistory"; +import { HealthProfileSummary } from "./HealthProfileSummary"; +import { ImmunisationRecords } from "./ImmunisationRecords"; +import PatientNotes from "./Notes"; +import { SampleTestHistory } from "./SampleTestHistory"; +import ShiftingHistory from "./ShiftingHistory"; + +export interface PatientProps { + facilityId: string; + id: string; + patientData: PatientModel; +} + +export const patientTabs = [ + { + route: "demography", + component: Demography, + }, + { + route: "encounters", + component: EncounterHistory, + }, + { + route: "health-profile", + component: HealthProfileSummary, + }, + { + route: "immunisation-records", + component: ImmunisationRecords, + }, + { + route: "shift", + component: ShiftingHistory, + }, + { + route: "request-sample-test", + component: SampleTestHistory, + }, + { + route: "patient-notes", + component: PatientNotes, + }, +]; diff --git a/src/components/Patient/PatientHome.tsx b/src/components/Patient/PatientHome.tsx index a7b08bdea58..d6b9844b699 100644 --- a/src/components/Patient/PatientHome.tsx +++ b/src/components/Patient/PatientHome.tsx @@ -1,26 +1,9 @@ -import { navigate } from "raviger"; +import { Link, navigate } from "raviger"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import Chip from "@/CAREUI/display/Chip"; -import CareIcon from "@/CAREUI/icons/CareIcon"; -import PaginatedList from "@/CAREUI/misc/PaginatedList"; - -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; - -import ButtonV2 from "@/components/Common/ButtonV2"; -import CircularProgress from "@/components/Common/CircularProgress"; import ConfirmDialog from "@/components/Common/ConfirmDialog"; -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; -import RelativeDateUserMention from "@/components/Common/RelativeDateUserMention"; import UserAutocomplete from "@/components/Common/UserAutocompleteFormField"; -import { ConsultationCard } from "@/components/Facility/ConsultationCard"; -import { ConsultationModel, ShiftingModel } from "@/components/Facility/models"; -import { InsuranceDetialsCard } from "@/components/Patient/InsuranceDetailsCard"; -import { SampleTestCard } from "@/components/Patient/SampleTestCard"; -import { PatientModel, SampleTestModel } from "@/components/Patient/models"; import useAuthUser from "@/hooks/useAuthUser"; @@ -31,98 +14,67 @@ import { SAMPLE_TEST_STATUS, } from "@/common/constants"; -import { triggerGoal } from "@/Integrations/Plausible"; -import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; -import * as Notification from "@/Utils/Notifications"; import dayjs from "@/Utils/dayjs"; import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import useQuery from "@/Utils/request/useQuery"; + +import Chip from "../../CAREUI/display/Chip"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import { triggerGoal } from "../../Integrations/Plausible"; +import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; +import * as Notification from "../../Utils/Notifications"; +import request from "../../Utils/request/request"; +import useQuery from "../../Utils/request/useQuery"; import { - classNames, - formatDate, formatDateTime, formatName, formatPatientAge, + humanizeStrings, isAntenatal, isPostPartum, -} from "@/Utils/utils"; + relativeDate, +} from "../../Utils/utils"; +import { Avatar } from "../Common/Avatar"; +import ButtonV2 from "../Common/ButtonV2"; +import Loading from "../Common/Loading"; +import Page from "../Common/Page"; +import { SkillModel, UserBareMinimum } from "../Users/models"; +import { patientTabs } from "./PatientDetailsTab"; +import { isPatientMandatoryDataFilled } from "./Utils"; +import { AssignedToObjectModel, PatientModel, SampleTestModel } from "./models"; export const parseOccupation = (occupation: string | undefined) => { return OCCUPATION_TYPES.find((i) => i.value === occupation)?.text; }; -export const PatientHome = (props: any) => { - const { facilityId, id } = props; - const [showShifts, setShowShifts] = useState(false); - const [isShiftClicked, setIsShiftClicked] = useState(false); +export const PatientHome = (props: { + facilityId?: string; + id: string; + page: (typeof patientTabs)[0]["route"]; +}) => { + const { facilityId, id, page } = props; const [patientData, setPatientData] = useState({}); - const [assignedVolunteerObject, setAssignedVolunteerObject] = - useState(null); + const authUser = useAuthUser(); const { t } = useTranslation(); - const [selectedStatus, setSelectedStatus] = useState<{ + const [selectedStatus, _setSelectedStatus] = useState<{ status: number; - sample: any; + sample: SampleTestModel | null; }>({ status: 0, sample: null }); - const [showAlertMessage, setShowAlertMessage] = useState(false); - const [modalFor, setModalFor] = useState(); - const [openAssignVolunteerDialog, setOpenAssignVolunteerDialog] = - useState(false); - const initErr: any = {}; - const errors = initErr; + const [assignedVolunteer, setAssignedVolunteer] = useState< + AssignedToObjectModel | undefined + >(patientData.assigned_to_object); useEffect(() => { - setAssignedVolunteerObject(patientData.assigned_to_object); + setAssignedVolunteer(patientData.assigned_to_object); }, [patientData.assigned_to_object]); - const handleTransferComplete = async (shift: ShiftingModel) => { - if (!shift) return; - await request(routes.completeTransfer, { - pathParams: { externalId: shift.external_id }, - }); - navigate( - `/facility/${shift.assigned_facility}/patient/${shift.patient}/consultation`, - ); - }; - - const { data: insuranceDetials } = useQuery(routes.hcx.policies.list, { - query: { - patient: id, - limit: 1, - }, - }); - - const handlePatientTransfer = async (value: boolean) => { - const dummyPatientData = Object.assign({}, patientData); - dummyPatientData["allow_transfer"] = value; - - await request(routes.patchPatient, { - pathParams: { - id: patientData.id as string, - }, - - body: { allow_transfer: value }, - - onResponse: ({ res }) => { - if ((res || {}).status === 200) { - const dummyPatientData = Object.assign({}, patientData); - dummyPatientData["allow_transfer"] = value; - setPatientData(dummyPatientData); - - Notification.Success({ - msg: "Transfer status updated.", - }); - } - }, - }); - }; - - const handleVolunteerSelect = (volunteer: any) => { - setAssignedVolunteerObject(volunteer.value); - }; + const [showAlertMessage, setShowAlertMessage] = useState(false); + const [openAssignVolunteerDialog, setOpenAssignVolunteerDialog] = + useState(false); + const initErr: any = {}; + const errors = initErr; const { loading: isLoading, refetch } = useQuery(routes.getPatient, { pathParams: { id, @@ -144,20 +96,18 @@ export const PatientHome = (props: any) => { id: patientData.id as string, }, body: { - assigned_to: assignedVolunteerObject - ? assignedVolunteerObject.id - : null, + assigned_to: (assignedVolunteer as UserBareMinimum)?.id || null, }, }); if (res?.ok && data) { setPatientData(data); - if (assignedVolunteerObject) { + if (!assignedVolunteer) { Notification.Success({ - msg: "Volunteer assigned successfully.", + msg: t("volunteer_assigned"), }); } else { Notification.Success({ - msg: "Volunteer unassigned successfully.", + msg: t("volunteer_unassigned"), }); } refetch(); @@ -166,34 +116,41 @@ export const PatientHome = (props: any) => { if (errors["assignedVolunteer"]) delete errors["assignedVolunteer"]; }; - const { loading: isShiftDataLoading, data: activeShiftingData } = useQuery( - routes.listShiftRequests, - { - query: { - patient: id, - }, - prefetch: isShiftClicked, + const consultation = patientData?.last_consultation; + const skillsQuery = useQuery(routes.userListSkill, { + pathParams: { + username: consultation?.treating_physician_object?.username ?? "", }, - ); + prefetch: !!consultation?.treating_physician_object?.username, + }); + const formatSkills = (arr: SkillModel[]) => { + const skills = arr.map((skill) => skill.skill_object.name); + + if (skills.length === 0) { + return ""; + } - const confirmApproval = (status: number, sample: any) => { - setSelectedStatus({ status, sample }); - setShowAlertMessage(true); + if (skills.length <= 3) { + return humanizeStrings(skills); + } + + const [first, second, ...rest] = skills; + return `${first}, ${second} and ${rest.length} other skills...`; }; const handleApproval = async () => { const { status, sample } = selectedStatus; const sampleData = { - id: sample.id, + id: sample?.id, status: status.toString(), - consultation: sample.consultation, + consultation: sample?.consultation, }; const statusName = SAMPLE_TEST_STATUS.find((i) => i.id === status)?.desc; await request(routes.patchSample, { body: sampleData, pathParams: { - id: sample.id, + id: sample?.id || "", }, onResponse: ({ res }) => { if (res?.ok) { @@ -214,351 +171,390 @@ export const PatientHome = (props: any) => { (i) => i.id === patientData.gender, )?.text; - let patientMedHis: any[] = []; - if ( - patientData && - patientData.medical_history && - patientData.medical_history.length - ) { - const medHis = patientData.medical_history; - patientMedHis = medHis - .filter((item) => item.disease !== "NO") - .map((item, idx) => ( -
-
- {item.disease} -
-
- {item.details} -
-
- )); - } - - const isPatientInactive = (patientData: PatientModel, facilityId: string) => { - return ( - !patientData.is_active || - !(patientData?.last_consultation?.facility === facilityId) - ); + const handlePatientTransfer = async (value: boolean) => { + await request(routes.patchPatient, { + pathParams: { + id: patientData.id as string, + }, + body: { allow_transfer: value }, + onResponse: ({ res }) => { + if (res?.status === 200) { + setPatientData((prev) => ({ + ...prev, + allow_transfer: value, + })); + Notification.Success({ + msg: t("transfer_status_updated"), + }); + } + }, + }); }; + const Tab = patientTabs.find((t) => t.route === page)?.component; + return ( handleApproval()} onClose={() => setShowAlertMessage(false)} /> -
-
-
-
- {patientData?.last_consultation?.assigned_to_object && ( -

- - Assigned Doctor: - {formatName( - patientData.last_consultation.assigned_to_object, - )} - - {patientData?.last_consultation?.assigned_to_object - .alt_phone_number && ( - - Video Call - - )} -

- )} - {patientData.assigned_to_object && ( -

- - Assigned Volunteer: - {formatName(patientData.assigned_to_object)} - -

- )} -
-
-
- {(patientData?.facility != patientData?.last_consultation?.facility || - (patientData.is_active && - patientData?.last_consultation?.discharge_date)) && ( - -
- -
- - {t("consultation_not_filed")} - - - - {t("consultation_not_filed_description")} - - +
+
+
+
+
+
+
+
+ +
+
+

+ {patientData.name} +

+

+ {formatPatientAge(patientData, true)},{" "} + {patientGender},{" "} {patientData.blood_group || "-"} +

+
+
+
+
+
+
+ {patientData?.is_active && + (!patientData?.last_consultation || + patientData?.last_consultation?.discharge_date) && ( +
+ + navigate( + `/facility/${patientData?.facility}/patient/${id}/consultation`, + ) + } + > + + + {t("add_consultation")} + + +
+ )} +
+
+
-
- - - )} -
-
-
-
-

- {patientData.name} - {formatPatientAge(patientData, true)} -

-
- {patientData.is_vaccinated && ( - - )} - {patientData.allow_transfer ? ( - - ) : ( - +
+ {isPatientMandatoryDataFilled(patientData) && + (!patientData.last_consultation || + patientData.last_consultation?.facility !== + patientData.facility || + (patientData.last_consultation?.discharge_date && + patientData.is_active)) && ( + + + + + + + )} - {patientData.gender === 2 && ( - <> - {patientData.is_antenatal && - isAntenatal( - patientData.last_menstruation_start_date, - ) && ( - - )} - {isPostPartum(patientData.date_of_delivery) && ( + {patientData.is_vaccinated && ( + + )} + {patientData.allow_transfer ? ( + + ) : ( + + )} + + {patientData.gender === 2 && ( + <> + {patientData.is_antenatal && + isAntenatal( + patientData.last_menstruation_start_date, + ) && ( )} - - )} - {patientData.last_consultation?.is_telemedicine && ( - - )} -
+ {isPostPartum(patientData.date_of_delivery) && ( + + )} + + )} + {patientData.last_consultation?.is_telemedicine && ( + + )} + {patientData.allergies && ( + + )}
-

- - {patientData.facility_object?.name || "-"} -

-

- {patientGender} | {patientData.blood_group || "-"} | Born on{" "} - {patientData.date_of_birth - ? formatDate(patientData.date_of_birth) - : patientData.year_of_birth} -

-
- -
-
- {t("patient_registration__contact")} -
- + +
+
+

+ {t("facility")}: +

+

+ {patientData.facility_object?.name || "-"} +

- {patientData.date_of_return && ( -
-
- Date of Return -
-
- {formatDateTime(patientData.date_of_return)} -
-
- )} - {patientData.is_vaccinated && !!patientData.number_of_doses && ( -
-
- Number of vaccine doses -
-
- {patientData.number_of_doses} -
-
- )} - {patientData.is_vaccinated && patientData.vaccine_name && ( -
-
- Vaccine name -
-
- {patientData.vaccine_name} + + {patientData?.last_consultation?.treating_physician_object && ( +
+

+ {t("treating_doctor")}: +

+
+

+ {formatName( + patientData.last_consultation + .treating_physician_object, + )} +

+

+ {!!skillsQuery.data?.results?.length && + formatSkills(skillsQuery.data?.results)} +

)} - {patientData.is_vaccinated && - patientData.last_vaccinated_date && ( -
-
- Last Vaccinated on -
-
- {formatDateTime(patientData.last_vaccinated_date)} -
-
- )} - {patientData.countries_travelled && - !!patientData.countries_travelled.length && ( -
-
- Countries travelled -
-
- {patientData.countries_travelled.join(", ")} -
-
- )} - {patientData.meta_info?.occupation && ( -
-
- {t("occupation")} -
-
- {parseOccupation(patientData.meta_info.occupation)} + {patientData?.last_consultation?.assigned_to_object && ( +
+

+ {t("assigned_doctor")}: +

+
+

+ {formatName( + patientData.last_consultation.assigned_to_object, + )} +

+ {patientData?.last_consultation?.assigned_to_object + .alt_phone_number && ( + + {" "} + {t("video_call")} + + )}
)} - {patientData.ration_card_category && ( -
-
- {t("ration_card_category")} -
-
- {t(`ration_card__${patientData.ration_card_category}`)} -
+ + {patientData.assigned_to_object && ( +
+

+ {t("assigned_volunteer")}: +

+

+ {formatName(patientData.assigned_to_object)} +

)} - {patientData.meta_info?.socioeconomic_status && ( -
-
- {t("socioeconomic_status")} -
-
- {t( - `SOCIOECONOMIC_STATUS__${patientData.meta_info.socioeconomic_status}`, - )} +
+
+
+
+ +
+
+ {patientTabs.map((tab) => ( + + {t(tab.route)} + + ))} +
+
+ +
+
+ {Tab && ( + + )} +
+
+
+
+
+ {t("actions")} +
+
+
+
+ + navigate(`/patient/${id}/investigation_reports`) + } + > + + + {t("investigations_summary")} + +
-
- )} - {patientData.meta_info?.domestic_healthcare_support && ( -
-
- {t("domestic_healthcare_support")} +
+ + navigate( + `/facility/${patientData?.facility}/patient/${id}/files`, + ) + } + > + + + {t("view_update_patient_files")} + +
-
- {t( - `DOMESTIC_HEALTHCARE_SUPPORT__${patientData.meta_info.domestic_healthcare_support}`, - )} + + {NonReadOnlyUsers && ( +
+ setOpenAssignVolunteerDialog(true)} + disabled={false} + authorizeFor={NonReadOnlyUsers} + className="w-full bg-white font-semibold text-green-800 hover:bg-secondary-200" + size="large" + > + + {" "} + {t("assign_to_volunteer")} + + +
+ )} + +
+ + handlePatientTransfer(!patientData.allow_transfer) + } + authorizeFor={NonReadOnlyUsers} + > + + + {patientData.allow_transfer + ? t("disable_transfer") + : t("allow_transfer")} + +
- )} +
-
-
-
+
+
{patientData.review_time && @@ -567,7 +563,7 @@ export const PatientHome = (props: any) => { 0 && (
{

{(dayjs().isBefore(patientData.review_time) - ? "Review before: " - : "Review Missed: ") + + ? t("review_before") + : t("review_missed")) + + ": " + formatDateTime(patientData.review_time)}

)} -
-
-
-
- Status -
-
- {patientData.is_active ? "LIVE" : "DISCHARGED"} -
-
-
-
- Last Discharged Reason + +
+
+
+
+ {t("last_discharge_reason")}
-
+
{patientData.is_active ? ( "-" ) : !patientData.last_consultation ?.new_discharge_reason ? ( {patientData?.last_consultation?.suggestion === "OP" - ? "OP file closed" - : "UNKNOWN"} + ? t("op_file_closed") + : t("unknown")} ) : patientData.last_consultation ?.new_discharge_reason === DISCHARGE_REASONS.find((i) => i.text == "Expired") ?.id ? ( - EXPIRED + + {t("expired")} + ) : ( DISCHARGE_REASONS.find( (reason) => @@ -623,31 +615,46 @@ export const PatientHome = (props: any) => {
-
-
-
- Created -
-
-
- +
+
+
+ {t("last_updated_by")}{" "} + + {patientData.last_edited?.first_name}{" "} + {patientData.last_edited?.last_name} + +
+
+
+ + {patientData.modified_date + ? formatDateTime(patientData.modified_date) + : "--:--"} + + {patientData.modified_date + ? relativeDate(patientData.modified_date) + : "--:--"}
-
-
- Last Edited -
-
-
- +
+
+ {t("patient_profile_created_by")}{" "} + + {patientData.created_by?.first_name}{" "} + {patientData.created_by?.last_name} + +
+
+
+ + {patientData.created_date + ? formatDateTime(patientData.created_date) + : "--:--"} + + {patientData.modified_date + ? relativeDate(patientData.modified_date) + : "--:--"}
@@ -659,784 +666,40 @@ export const PatientHome = (props: any) => {
navigate(`/death_report/${id}`)} > - Death Report + {t("death_report")}
)} -
- { - const showAllFacilityUsers = [ - "DistrictAdmin", - "StateAdmin", - ]; - if ( - !showAllFacilityUsers.includes(authUser.user_type) && - authUser.home_facility_object?.id !== - patientData.facility - ) { - Notification.Error({ - msg: "Oops! Non-Home facility users don't have permission to perform this action.", - }); - } else { - navigate( - `/facility/${patientData?.facility}/patient/${id}/update`, - ); - } - }} - > - - Update Details - -
-
- - handlePatientTransfer(!patientData.allow_transfer) - } - authorizeFor={NonReadOnlyUsers} - > - - {patientData.allow_transfer - ? "Disable Transfer" - : "Allow Transfer"} - -
-
-
-
-
-
-
{ - setShowShifts(!showShifts); - setIsShiftClicked(true); - }} - > -
{t("shifting")}
- {showShifts ? ( - - ) : ( - - )} -
-
- {activeShiftingData?.count ? ( - activeShiftingData.results.map((shift: ShiftingModel) => ( -
-
-
-
-
-
- {shift.emergency && ( - - Emergency - - )} -
-
-
-
-
- -
- {shift.status} -
- -
-
-
- -
- {(shift.origin_facility_object || {})?.name} -
- -
-
-
- -
- { - ( - shift.shifting_approving_facility_object || - {} - )?.name - } -
- -
-
-
- -
- {(shift.assigned_facility_object || {})?.name || - "Yet to be decided"} -
- -
- -
-
- -
- {formatDateTime(shift.modified_date) || "--"} -
- -
-
-
- -
- - navigate(`/shifting/${shift.external_id}`) - } - > - - All Details - -
- {shift.status === "COMPLETED" && - shift.assigned_facility && ( -
- setModalFor(shift)} - > - {t("transfer_to_receiving_facility")} - - setModalFor(undefined)} - onConfirm={() => handleTransferComplete(shift)} - /> -
- )} -
-
-
- )) - ) : ( -
- {isShiftDataLoading ? "Loading..." : "No Shifting Records!"} -
- )} -
-
- -
-
-
-
- {t("location")} -
-
-
-
- {t("address")} -
-
- {patientData.address || "-"} -
-
-
-
- {t("district")} -
-
- {patientData.district_object?.name || "-"} -
-
-
-
- Village -
-
- {patientData.village || "-"} -
-
-
-
- {t("ward")} -
-
- {(patientData.ward_object && - patientData.ward_object.number + - ", " + - patientData.ward_object.name) || - "-"} -
-
-
-
- State, Country - Pincode -
-
- {patientData?.state_object?.name}, - {patientData.nationality || "-"} - {patientData.pincode} -
-
-
-
- {t("local_body")} -
-
- {patientData.local_body_object?.name || "-"} -
-
-
-
-
- Medical -
- {!patientData.present_health && - !patientData.allergies && - !patientData.ongoing_medication && - !(patientData.gender === 2 && patientData.is_antenatal) && - !patientData.medical_history?.some( - (history) => history.disease !== "NO", - ) && ( -
- No Medical History Available -
- )} -
- {patientData.present_health && ( -
-
- Present Health -
-
- {patientData.present_health} -
-
- )} - {patientData.ongoing_medication && ( -
-
- Ongoing Medications -
-
- {patientData.ongoing_medication} -
-
- )} - {patientData.allergies && ( -
-
- Allergies -
-
- {patientData.allergies} -
-
- )} - {patientData.gender === 2 && patientData.is_antenatal && ( -
-
- Is pregnant -
-
- Yes -
-
- )} - {patientMedHis} -
-
-
- - 1 - } - /> -
-
-
-
-
navigate(`/patient/${id}/investigation_reports`)} - > -
-
- - - -
-
-

- Investigations Summary -

-
-
-
-
- navigate( - `/facility/${patientData?.facility}/patient/${id}/files/`, - ) - } - > -
-
- - - -
-
-

- View/Upload Patient Files -

-
-
-
-
{ - if (!isPatientInactive(patientData, facilityId)) { - navigate(`/facility/${facilityId}/patient/${id}/shift/new`); - } - }} - > -
-
- - - -
- -
-

- Shift Patient -

-
-
-
-
{ - if (!isPatientInactive(patientData, facilityId)) { - navigate( - `/facility/${patientData?.facility}/patient/${id}/sample-test`, - ); - } - }} - > -
-
- - - -
-
-

- Request Sample Test -

-
-
-
-
- navigate( - `/facility/${patientData?.facility}/patient/${id}/notes`, - ) - } - > -
-
- - - -
-
-

- View Patient Notes -

-
-
-
-
{ - if (!isPatientInactive(patientData, facilityId)) { - setOpenAssignVolunteerDialog(true); - } - }} - > -
-
- - - -
-
-

- Assign to a volunteer -

-
-
-
-
-
-
-
-
-
- - navigate(`/patient/${id}/investigation_reports`) - } - > - - - Investigations Summary - - -
-
- - navigate( - `/facility/${patientData?.facility}/patient/${id}/files`, - ) - } - > - - - View/Upload Patient Files - - -
-
- - navigate( - `/facility/${facilityId}/patient/${id}/shift/new`, - ) - } - authorizeFor={NonReadOnlyUsers} - > - - - Shift Patient - - -
-
- - navigate( - `/facility/${patientData?.facility}/patient/${id}/sample-test`, - ) - } - authorizeFor={NonReadOnlyUsers} - id="sample-request-btn" - > - - - Request Sample Test - - -
-
- - navigate( - `/facility/${patientData?.facility}/patient/${id}/notes`, - ) - } - > - - - View Patient Notes - - -
-
- setOpenAssignVolunteerDialog(true)} - disabled={false} - authorizeFor={NonReadOnlyUsers} - > - - - Assign to a volunteer - - -
-
-
-
-
+
setOpenAssignVolunteerDialog(false)} description={
setAssignedVolunteer(user.value)} userType={"Volunteer"} name={"assign_volunteer"} error={errors.assignedVolunteer} />
} - action="Assign" + action={t("assign")} onConfirm={handleAssignedVolunteer} /> - -
-

- Consultation History -

- - - {(_) => ( -
- - - - -
-
- No Consultation History Available -
-
-
- > - {(item) => ( - - )} - -
- -
-
- )} -
-
- -
-

- Sample Test History -

- - {(_, query) => ( -
- - - - -
-
- No Sample Test History Available -
-
-
- > - {(item) => ( - - )} - -
- -
-
- )} -
-
); }; diff --git a/src/components/Patient/PatientInfoCard.tsx b/src/components/Patient/PatientInfoCard.tsx index e415d928a0d..486e8b103fd 100644 --- a/src/components/Patient/PatientInfoCard.tsx +++ b/src/components/Patient/PatientInfoCard.tsx @@ -538,7 +538,7 @@ export default function PatientInfoCard(props: PatientInfoCardProps) { )} {!!consultation?.discharge_date && (
-
+
Discharge Reason
diff --git a/src/components/Patient/PatientRegister.tsx b/src/components/Patient/PatientRegister.tsx index 35d93214385..5828517adf9 100644 --- a/src/components/Patient/PatientRegister.tsx +++ b/src/components/Patient/PatientRegister.tsx @@ -2,7 +2,7 @@ import careConfig from "@careConfig"; import { startCase, toLower } from "lodash-es"; import { debounce } from "lodash-es"; import { navigate } from "raviger"; -import { useCallback, useReducer, useRef, useState } from "react"; +import { useCallback, useEffect, useReducer, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; @@ -232,6 +232,25 @@ export const PatientRegister = (props: PatientRegisterProps) => { const headerText = !id ? "Add Details of Patient" : "Update Patient Details"; const buttonText = !id ? "Add Patient" : "Save Details"; + useEffect(() => { + const getQueryParams = () => { + const params = new URLSearchParams(window.location.search); + return { + section: params.get("section"), + }; + }; + + const { section } = getQueryParams(); + if (section) { + setTimeout(() => { + const element = document.getElementById(section); + if (element) { + element.scrollIntoView({ behavior: "smooth" }); + } + }, 2000); + } + }, []); + const fetchDistricts = useCallback(async (id: number) => { if (id > 0) { setIsDistrictLoading(true); @@ -1402,7 +1421,7 @@ export const PatientRegister = (props: PatientRegisterProps) => {
{field("nationality").value === "India" && ( -
+
{
)} -
+
{
-
+

Medical History

@@ -1661,7 +1683,10 @@ export const PatientRegister = (props: PatientRegisterProps) => {
-
+

Insurance Details diff --git a/src/components/Patient/SampleTestCard.tsx b/src/components/Patient/SampleTestCard.tsx index a0303705eb7..c22155c494e 100644 --- a/src/components/Patient/SampleTestCard.tsx +++ b/src/components/Patient/SampleTestCard.tsx @@ -16,8 +16,8 @@ import request from "@/Utils/request/request"; import { formatDateTime } from "@/Utils/utils"; interface SampleDetailsProps { - facilityId: number; - patientId: number; + facilityId: string; + patientId: string; itemData: SampleTestModel; refetch: () => void; handleApproval: (status: number, sample: SampleTestModel) => void; diff --git a/src/components/Patient/models.tsx b/src/components/Patient/models.tsx index 15063be4e9c..85614e707d5 100644 --- a/src/components/Patient/models.tsx +++ b/src/components/Patient/models.tsx @@ -136,7 +136,7 @@ export interface PatientModel { is_declared_positive?: boolean; last_edited?: UserBareMinimum; created_by?: UserBareMinimum; - assigned_to?: { first_name?: string; username?: string; last_name?: string }; + assigned_to?: number | null; assigned_to_object?: AssignedToObjectModel; occupation?: Occupation; meta_info?: PatientMeta; diff --git a/src/components/Shifting/ShiftingBlock.tsx b/src/components/Shifting/ShiftingBlock.tsx index 09bbcfb3b9f..db173b2c6d8 100644 --- a/src/components/Shifting/ShiftingBlock.tsx +++ b/src/components/Shifting/ShiftingBlock.tsx @@ -13,7 +13,7 @@ import { classNames, formatDateTime, formatName } from "@/Utils/utils"; export default function ShiftingBlock(props: { shift: ShiftingModel; - onTransfer: () => unknown; + onTransfer?: () => unknown; }) { const { shift, onTransfer } = props; const { t } = useTranslation(); @@ -123,25 +123,28 @@ export default function ShiftingBlock(props: { {t("all_details")} - {shift.status === "COMPLETED" && shift.assigned_facility && ( - <> - - - )} + {shift.status === "COMPLETED" && + shift.assigned_facility && + onTransfer && ( + <> + + + )}

); diff --git a/src/components/Shifting/ShiftingList.tsx b/src/components/Shifting/ShiftingList.tsx index db7ec691f79..7161ea29441 100644 --- a/src/components/Shifting/ShiftingList.tsx +++ b/src/components/Shifting/ShiftingList.tsx @@ -1,29 +1,25 @@ -import careConfig from "@careConfig"; import { navigate } from "raviger"; -import { useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { AdvancedFilterButton } from "@/CAREUI/interactive/FiltersSlideover"; import ButtonV2 from "@/components/Common/ButtonV2"; -import ConfirmDialog from "@/components/Common/ConfirmDialog"; import { ExportButton } from "@/components/Common/Export"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; -import { ShiftingModel } from "@/components/Facility/models"; import SearchInput from "@/components/Form/SearchInput"; import BadgesList from "@/components/Shifting/ShiftingBadges"; import { formatFilter } from "@/components/Shifting/ShiftingCommons"; import ListFilter from "@/components/Shifting/ShiftingFilters"; -import useAuthUser from "@/hooks/useAuthUser"; import useFilters from "@/hooks/useFilters"; import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useQuery from "@/Utils/request/useQuery"; -import { formatDateTime } from "@/Utils/utils"; + +import ShiftingTable from "./ShiftingTable"; export default function ListView() { const { @@ -35,31 +31,7 @@ export default function ListView() { resultsPerPage, } = useFilters({ cacheBlacklist: ["patient_name"] }); - const [modalFor, setModalFor] = useState<{ - externalId: string | undefined; - loading: boolean; - }>({ - externalId: undefined, - loading: false, - }); - - const authUser = useAuthUser(); const { t } = useTranslation(); - - const handleTransferComplete = async (shift: ShiftingModel) => { - setModalFor({ ...modalFor, loading: true }); - try { - await request(routes.completeTransfer, { - pathParams: { externalId: shift.external_id }, - }); - navigate( - `/facility/${shift.assigned_facility}/patient/${shift.patient}/consultation`, - ); - } catch (error) { - setModalFor({ externalId: undefined, loading: false }); - } - }; - const { data: shiftData, loading, @@ -71,179 +43,6 @@ export default function ListView() { }), }); - const showShiftingCardList = (data: ShiftingModel[]) => { - if (loading) { - return ; - } - if (data && !data.length) { - return ( -
- {t("no_patients_to_show")} -
- ); - } - - return data.map((shift: ShiftingModel) => ( -
-
-
-
- {shift.patient_object.name} -
-
- {shift.patient_object.age} -
-
- -
-
-
- -
- {shift.patient_object.phone_number || ""} -
- -
-
-
- -
- {shift.patient_object.address || "--"} -
- -
-
- -
-
-
- -
{shift.status}
- - -
- {shift.emergency && ( - - {t("emergency")} - - )} -
-
- -
-
- -
- {formatDateTime(shift.modified_date) || "--"} -
- -
-
- -
-
- -
- {shift.origin_facility_object?.name} -
- - - {careConfig.wartimeShifting && ( -
- -
- {shift.shifting_approving_facility_object?.name} -
- - )} - -
- -
- {shift.assigned_facility_external || - shift.assigned_facility_object?.name || - t("yet_to_be_decided")} -
- -
-
- navigate(`/shifting/${shift.external_id}`)} - variant="secondary" - border - className="w-full" - > - {t("all_details")} - - {shift.status === "COMPLETED" && shift.assigned_facility && ( -
- - setModalFor({ - externalId: shift.external_id, - loading: false, - }) - } - > - {t("transfer_to_receiving_facility")} - - - setModalFor({ externalId: undefined, loading: false }) - } - onConfirm={() => handleTransferComplete(shift)} - /> -
- )} -
-
-
- )); - }; - return ( +
{loading ? ( @@ -307,27 +107,7 @@ export default function ListView() { {t("refresh_list")}
-
-
-
- {t("patients")} -
-
- {t("contact_info")} -
-
- {t("consent__status")} -
-
- {t("facilities")} -
-
- {t("LOG_UPDATE_FIELD_LABEL__action")} -
-
-
{showShiftingCardList(shiftData?.results || [])}
-
- +
diff --git a/src/components/Shifting/ShiftingTable.tsx b/src/components/Shifting/ShiftingTable.tsx new file mode 100644 index 00000000000..de186a13d56 --- /dev/null +++ b/src/components/Shifting/ShiftingTable.tsx @@ -0,0 +1,261 @@ +import careConfig from "@careConfig"; +import { navigate } from "raviger"; +import { useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { cn } from "@/lib/utils"; + +import CareIcon from "@/CAREUI/icons/CareIcon"; + +import useAuthUser from "@/hooks/useAuthUser"; + +import routes from "@/Utils/request/api"; +import request from "@/Utils/request/request"; +import { formatDateTime } from "@/Utils/utils"; + +import ButtonV2 from "../Common/ButtonV2"; +import ConfirmDialog from "../Common/ConfirmDialog"; +import Loading from "../Common/Loading"; +import { ShiftingModel } from "../Facility/models"; + +export default function ShiftingTable(props: { + data?: ShiftingModel[]; + loading?: boolean; + hidePatient?: boolean; +}) { + const { data, loading, hidePatient } = props; + + const { t } = useTranslation(); + const authUser = useAuthUser(); + const [modalFor, setModalFor] = useState<{ + externalId: string | undefined; + loading: boolean; + }>({ + externalId: undefined, + loading: false, + }); + + const handleTransferComplete = async (shift: ShiftingModel) => { + setModalFor({ ...modalFor, loading: true }); + try { + await request(routes.completeTransfer, { + pathParams: { externalId: shift.external_id }, + }); + navigate( + `/facility/${shift.assigned_facility}/patient/${shift.patient}/consultation`, + ); + } catch (error) { + setModalFor({ externalId: undefined, loading: false }); + } + }; + + if (loading) { + return ; + } + if (data && !data.length) { + return ( +
+ {t("no_results_found")} +
+ ); + } + + return ( +
+
+ {!hidePatient && ( +
+ {t("patients")} +
+ )} +
+ {t("contact_info")} +
+
+ {t("consent__status")} +
+
+ {t("facilities")} +
+
+ {t("LOG_UPDATE_FIELD_LABEL__action")} +
+
+
+ {data?.map((shift: ShiftingModel) => ( +
+
+ {!hidePatient && ( +
+
+ {shift.patient_object.name} +
+
+ {shift.patient_object.age} +
+
+ )} + +
+
+
+ +
+ {shift.patient_object.phone_number || ""} +
+ +
+
+
+ +
+ {shift.patient_object.address || "--"} +
+ +
+
+ +
+
+
+ +
{shift.status}
+ + +
+ {shift.emergency && ( + + {t("emergency")} + + )} +
+
+ +
+
+ +
+ {formatDateTime(shift.modified_date) || "--"} +
+ +
+
+ +
+
+ +
+ {shift.origin_facility_object?.name} +
+ + + {careConfig.wartimeShifting && ( +
+ +
+ {shift.shifting_approving_facility_object?.name} +
+ + )} + +
+ +
+ {shift.assigned_facility_external || + shift.assigned_facility_object?.name || + t("yet_to_be_decided")} +
+ +
+
+ navigate(`/shifting/${shift.external_id}`)} + variant="secondary" + border + className="w-full" + > + {t("all_details")} + + {shift.status === "COMPLETED" && shift.assigned_facility && ( +
+ + setModalFor({ + externalId: shift.external_id, + loading: false, + }) + } + > + {t("transfer_to_receiving_facility")} + + + setModalFor({ externalId: undefined, loading: false }) + } + onConfirm={() => handleTransferComplete(shift)} + /> +
+ )} +
+
+
+ ))} +
+
+ ); +} diff --git a/src/components/Users/ManageUsers.tsx b/src/components/Users/ManageUsers.tsx index 0b6a9149dbb..a1c00e5eaef 100644 --- a/src/components/Users/ManageUsers.tsx +++ b/src/components/Users/ManageUsers.tsx @@ -250,7 +250,7 @@ export default function ManageUsers() {
{formatName(user)} From e309fcd9f9da1de5c2c1e542c362c6d17ce4a044 Mon Sep 17 00:00:00 2001 From: JavidSumra <112365664+JavidSumra@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:41:19 +0530 Subject: [PATCH 32/33] Fix unable to use camera switch button (#9159) --- src/components/Files/CameraCaptureDialog.tsx | 66 ++++++++++++-------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/src/components/Files/CameraCaptureDialog.tsx b/src/components/Files/CameraCaptureDialog.tsx index 91b158bcb44..acfbaf96ec4 100644 --- a/src/components/Files/CameraCaptureDialog.tsx +++ b/src/components/Files/CameraCaptureDialog.tsx @@ -7,7 +7,7 @@ import CareIcon from "@/CAREUI/icons/CareIcon"; import ButtonV2, { Submit } from "@/components/Common/ButtonV2"; import DialogModal from "@/components/Common/Dialog"; -import useWindowDimensions from "@/hooks/useWindowDimensions"; +import useBreakpoints from "@/hooks/useBreakpoints"; import * as Notify from "@/Utils/Notifications"; @@ -19,34 +19,56 @@ export interface CameraCaptureDialogProps { export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { const { show, onHide, onCapture } = props; + const isLaptopScreen = useBreakpoints({ lg: true, default: false }); - const [cameraFacingFront, setCameraFacingFront] = useState(true); + const [cameraFacingMode, setCameraFacingMode] = useState( + isLaptopScreen ? "user" : "environment", + ); const [previewImage, setPreviewImage] = useState(null); const webRef = useRef(null); const videoConstraints = { width: { ideal: 4096 }, height: { ideal: 2160 }, - facingMode: "user", + facingMode: cameraFacingMode, }; useEffect(() => { if (!show) return; - navigator.mediaDevices.getUserMedia({ video: true }).catch(() => { - Notify.Warn({ - msg: t("camera_permission_denied"), + let stream: MediaStream | null = null; + + navigator.mediaDevices + .getUserMedia({ video: { facingMode: cameraFacingMode } }) + .then((mediaStream) => { + stream = mediaStream; + }) + .catch(() => { + Notify.Warn({ + msg: t("camera_permission_denied"), + }); + onHide(); }); - onHide(); - }); - }, [show]); - const handleSwitchCamera = useCallback(() => { - const supportedConstraints = - navigator.mediaDevices.getSupportedConstraints(); - if ( - !isLaptopScreen && - typeof supportedConstraints.facingMode === "string" && - (supportedConstraints.facingMode as string).includes("environment") - ) { - setCameraFacingFront((prevState) => !prevState); + + return () => { + if (stream) { + stream.getTracks().forEach((track) => { + track.stop(); + }); + } + }; + }, [show, cameraFacingMode, onHide]); + + const handleSwitchCamera = useCallback(async () => { + const devices = await navigator.mediaDevices.enumerateDevices(); + const videoInputs = devices.filter( + (device) => device.kind === "videoinput", + ); + const backCamera = videoInputs.some((device) => + device.label.toLowerCase().includes("back"), + ); + if (!isLaptopScreen && backCamera) { + setCameraFacingMode((prevMode) => + prevMode === "environment" ? "user" : "environment", + ); } else { Notify.Warn({ msg: t("switch_camera_is_not_available"), @@ -54,10 +76,6 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { } }, []); - const { width } = useWindowDimensions(); - const LaptopScreenBreakpoint = 640; - const isLaptopScreen = width >= LaptopScreenBreakpoint ? true : false; - const captureImage = () => { setPreviewImage(webRef.current.getScreenshot()); const canvas = webRef.current.getCanvas(); @@ -70,10 +88,6 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { }); }; - const cameraFacingMode = cameraFacingFront - ? "user" - : { exact: "environment" }; - return ( Date: Wed, 27 Nov 2024 15:04:43 +0530 Subject: [PATCH 33/33] Fix Camera Capture on Retake and Close (#9180) --- src/components/Files/AudioCaptureDialog.tsx | 13 +++++++++++-- src/components/Files/CameraCaptureDialog.tsx | 7 ++++++- src/hooks/useFileUpload.tsx | 11 +++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/components/Files/AudioCaptureDialog.tsx b/src/components/Files/AudioCaptureDialog.tsx index 5512fad9dc6..d198ac7db16 100644 --- a/src/components/Files/AudioCaptureDialog.tsx +++ b/src/components/Files/AudioCaptureDialog.tsx @@ -1,5 +1,5 @@ import { Link } from "raviger"; -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; @@ -26,6 +26,7 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) { const { show, onHide, onCapture, autoRecord = false } = props; const [status, setStatus] = useState(null); + const mediaStreamRef = useRef(null); const { t } = useTranslation(); const { audioURL, resetRecording, startRecording, stopRecording } = @@ -42,7 +43,8 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) { const handleStartRecording = () => { navigator.mediaDevices .getUserMedia({ audio: true }) - .then(() => { + .then((stream) => { + mediaStreamRef.current = stream; setStatus("RECORDING"); startRecording(); timer.start(); @@ -105,6 +107,13 @@ export default function AudioCaptureDialog(props: AudioCaptureDialogProps) { if (autoRecord && show && status === "RECORDING") { handleStartRecording(); } + + return () => { + if (mediaStreamRef.current) { + mediaStreamRef.current.getTracks().forEach((track) => track.stop()); + mediaStreamRef.current = null; + } + }; }, [autoRecord, status, show]); return ( diff --git a/src/components/Files/CameraCaptureDialog.tsx b/src/components/Files/CameraCaptureDialog.tsx index acfbaf96ec4..f81d854c559 100644 --- a/src/components/Files/CameraCaptureDialog.tsx +++ b/src/components/Files/CameraCaptureDialog.tsx @@ -15,10 +15,11 @@ export interface CameraCaptureDialogProps { show: boolean; onHide: () => void; onCapture: (file: File, fileName: string) => void; + onResetCapture: () => void; } export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { - const { show, onHide, onCapture } = props; + const { show, onHide, onCapture, onResetCapture } = props; const isLaptopScreen = useBreakpoints({ lg: true, default: false }); const [cameraFacingMode, setCameraFacingMode] = useState( @@ -160,6 +161,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { { setPreviewImage(null); + onResetCapture(); }} className="m-2" > @@ -183,6 +185,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { variant="secondary" onClick={() => { setPreviewImage(null); + onResetCapture(); onHide(); }} className="m-2" @@ -221,6 +224,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { { setPreviewImage(null); + onResetCapture(); }} > {t("retake")} @@ -242,6 +246,7 @@ export default function CameraCaptureDialog(props: CameraCaptureDialogProps) { variant="secondary" onClick={() => { setPreviewImage(null); + onResetCapture(); onHide(); }} > diff --git a/src/hooks/useFileUpload.tsx b/src/hooks/useFileUpload.tsx index 56f0d337a5e..d580ad7d557 100644 --- a/src/hooks/useFileUpload.tsx +++ b/src/hooks/useFileUpload.tsx @@ -249,6 +249,11 @@ export default function useFileUpload( setUploadFileNames([]); }; + const clearFiles = () => { + setFiles([]); + setUploadFileNames([]); + }; + const Dialogues = ( <> { setFiles((prev) => [...prev, file]); }} + onResetCapture={clearFiles} /> prev.filter((_, i) => i !== index)); setUploadFileNames((prev) => prev.filter((_, i) => i !== index)); }, - clearFiles: () => { - setFiles([]); - setUploadFileNames([]); - }, + clearFiles, uploading, }; }