From 8a01b543d365d3f1dc7d2c0052b060f596433f3f Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Tue, 27 Feb 2024 13:28:48 +0530 Subject: [PATCH 01/13] flaky test (#7282) --- cypress/e2e/facility_spec/facility_homepage.cy.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cypress/e2e/facility_spec/facility_homepage.cy.ts b/cypress/e2e/facility_spec/facility_homepage.cy.ts index 2ed666ac842..1832fa973b7 100644 --- a/cypress/e2e/facility_spec/facility_homepage.cy.ts +++ b/cypress/e2e/facility_spec/facility_homepage.cy.ts @@ -37,6 +37,9 @@ describe("Facility Homepage Function", () => { it("Verify the Facility card button redirection", () => { // view cns button + manageUserPage.typeFacilitySearch(facilityName); + facilityPage.verifyFacilityBadgeContent(facilityName); + manageUserPage.assertFacilityInCard(facilityName); facilityHome.clickViewCnsButton(); facilityHome.verifyCnsUrl(); facilityHome.navigateBack(); @@ -121,6 +124,8 @@ describe("Facility Homepage Function", () => { facilityPage.selectLocalBody(localBody); userPage.applyFilter(); // go to cns page in the facility details page + manageUserPage.typeFacilitySearch(facilityName); + facilityPage.verifyFacilityBadgeContent(facilityName); manageUserPage.assertFacilityInCard(facilityName); facilityHome.clickViewFacilityDetails(); facilityHome.clickFacilityCnsButton(); From bda15df327003b9bf741edc004fe1eb5d111f463 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 27 Feb 2024 19:53:26 +0530 Subject: [PATCH 02/13] Updated licence (#7291) --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 95ae6ba0216..eddd0436bc2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Corona Safe +Copyright (c) 2024 Open Healthcare Network Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From a097fb62b418f374d828698c124db1b7598eae16 Mon Sep 17 00:00:00 2001 From: Rashmik <146672184+rash-27@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:43:24 +0530 Subject: [PATCH 03/13] Fix Translations in login page (#7264) * Fix Translations in login page * remove console logs --------- Co-authored-by: Rithvik Nishad --- src/CAREUI/interactive/LegendInput.tsx | 10 +++++----- src/Components/Auth/Login.tsx | 10 ++++++---- src/Components/Common/LanguageSelectorLogin.tsx | 5 ++--- src/Locale/en/Auth.json | 1 + src/Locale/kn/Auth.json | 2 ++ src/Locale/ml/Auth.json | 2 ++ src/Locale/mr/Auth.json | 2 ++ src/Locale/ta/Auth.json | 2 ++ src/Providers/HistoryAPIProvider.tsx | 1 - 9 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/CAREUI/interactive/LegendInput.tsx b/src/CAREUI/interactive/LegendInput.tsx index eabff046353..789412d7635 100644 --- a/src/CAREUI/interactive/LegendInput.tsx +++ b/src/CAREUI/interactive/LegendInput.tsx @@ -1,7 +1,7 @@ import CareIcon from "../icons/CareIcon"; import { classNames } from "../../Utils/utils"; import { RefObject, useRef, useState, useEffect } from "react"; - +import { useTranslation } from "react-i18next"; type InputProps = { id?: string; name: string; @@ -19,7 +19,7 @@ type InputProps = { onKeyDown?: (e: any) => void; onKeyPress?: (e: any) => void; disabled?: boolean; - error?: boolean; + error?: string; className?: string; outerClassName?: string; size?: "small" | "medium" | "large"; @@ -30,6 +30,7 @@ export default function LegendInput(props: InputProps) { /** * Useful for small input forms. Should only be used in special cases. */ + const { t } = useTranslation(); const [showPassword, setShowPassword] = useState(false); const inputRef = useRef(null); const ref = props.ref || inputRef; @@ -66,7 +67,6 @@ export default function LegendInput(props: InputProps) { useEffect(() => { ref.current && testAutoFill(ref.current); }, [ref.current]); - return (
{props.label && ( @@ -149,8 +149,8 @@ export default function LegendInput(props: InputProps) { )}
- {props.error && ( -
{props.error}
+ {!!props.error && ( +
{t(props.error)}
)} ); diff --git a/src/Components/Auth/Login.tsx b/src/Components/Auth/Login.tsx index 62c9d632769..9e2e4f6686a 100644 --- a/src/Components/Auth/Login.tsx +++ b/src/Components/Auth/Login.tsx @@ -68,18 +68,19 @@ export const Login = (props: { forgot?: boolean }) => { ) { if (!form[key].match(/\w/)) { hasError = true; - err[key] = t("field_required"); + err[key] = "field_required"; } } if (!form[key]) { hasError = true; - err[key] = t("field_required"); + err[key] = "field_required"; } }); if (hasError) { setErrors(err); return false; } + return form; }; @@ -92,6 +93,7 @@ export const Login = (props: { forgot?: boolean }) => { const handleSubmit = async (e: any) => { e.preventDefault(); + setLoading(true); FiltersCache.invaldiateAll(); const validated = validateData(); @@ -111,12 +113,12 @@ export const Login = (props: { forgot?: boolean }) => { if (typeof form.username === "string") { if (!form.username.match(/\w/)) { hasError = true; - err.username = t("field_required"); + err.username = "field_required"; } } if (!form.username) { hasError = true; - err.username = t("field_required"); + err.username = "field_required"; } if (hasError) { diff --git a/src/Components/Common/LanguageSelectorLogin.tsx b/src/Components/Common/LanguageSelectorLogin.tsx index b6fd1ffb57e..51edc8b8320 100644 --- a/src/Components/Common/LanguageSelectorLogin.tsx +++ b/src/Components/Common/LanguageSelectorLogin.tsx @@ -4,8 +4,7 @@ import { LANGUAGE_NAMES } from "../../Locale/config"; import { classNames } from "../../Utils/utils"; export const LanguageSelectorLogin = () => { - const { i18n } = useTranslation(); - + const { i18n, t } = useTranslation(); useEffect(() => { document.documentElement.setAttribute("lang", i18n.language); }, [i18n]); @@ -20,7 +19,7 @@ export const LanguageSelectorLogin = () => { return (
- Available in: + {t("available_in")}
{Object.keys(LANGUAGE_NAMES).map((e: string) => ( diff --git a/src/Locale/en/Auth.json b/src/Locale/en/Auth.json index 89ba7cea4d6..e99cae72f7f 100644 --- a/src/Locale/en/Auth.json +++ b/src/Locale/en/Auth.json @@ -29,6 +29,7 @@ "password_reset_success": "Password Reset successfully", "password_reset_failure": "Password Reset Failed", "reset_password": "Reset Password", + "available_in":"Available in", "sign_out": "Sign Out", "back_to_login": "Back to login", "min_password_len_8": "Minimum password length 8", diff --git a/src/Locale/kn/Auth.json b/src/Locale/kn/Auth.json index ff13e8e15b1..fb36b254b38 100644 --- a/src/Locale/kn/Auth.json +++ b/src/Locale/kn/Auth.json @@ -22,6 +22,8 @@ "register_page_title": "ಆಸ್ಪತ್ರೆ ನಿರ್ವಾಹಕರಾಗಿ ನೋಂದಾಯಿಸಿ", "auth_login_title": "ಅಧಿಕೃತ ಲಾಗಿನ್", "forget_password": "ಪಾಸ್ವರ್ಡ್ ಮರೆತಿರಾ?", + "back_to_login": "ಲಾಗಿನ್ ಪುಟಕ್ಕೆ ಹಿಂತಿರುಗಿ", + "available_in":"ಲಭ್ಯವಿರುವ ಭಾಷೆಗಳು", "forget_password_instruction": "ನಿಮ್ಮ ಬಳಕೆದಾರ ಹೆಸರನ್ನು ನಮೂದಿಸಿ ಮತ್ತು ನಿಮ್ಮ ಪಾಸ್‌ವರ್ಡ್ ಅನ್ನು ಮರುಹೊಂದಿಸಲು ನಾವು ನಿಮಗೆ ಲಿಂಕ್ ಅನ್ನು ಕಳುಹಿಸುತ್ತೇವೆ.", "send_reset_link": "ಮರುಹೊಂದಿಸುವ ಲಿಂಕ್ ಕಳುಹಿಸಿ", "already_a_member": "ಈಗಾಗಲೇ ಸದಸ್ಯರೇ?", diff --git a/src/Locale/ml/Auth.json b/src/Locale/ml/Auth.json index 15b2e179aad..199bce684ec 100644 --- a/src/Locale/ml/Auth.json +++ b/src/Locale/ml/Auth.json @@ -21,6 +21,8 @@ "register_hospital": "ആശുപത്രി രജിസ്റ്റർ ചെയ്യുക", "register_page_title": "ആശുപത്രി അഡ്മിനിസ്ട്രേറ്ററായി രജിസ്റ്റർ ചെയ്യുക", "auth_login_title": "അംഗീകൃത ലോഗിൻ", + "back_to_login": "ലോഗിൻ പേജിലേക്ക് മടങ്ങുക", + "available_in":"ലഭ്യമായ ഭാഷകൾ", "forget_password": "പാസ്‌വേഡ് മറന്നോ?", "forget_password_instruction": "നിങ്ങളുടെ യൂസർനെയിം/ഉപയോക്തൃനാമം നൽകുക. പാസ്‌വേഡ് പുന: സജ്ജമാക്കാൻ ഞങ്ങൾ ഒരു ലിങ്ക് അയയ്‌ക്കുന്നതായിരിക്കും.", "send_reset_link": "പുന: സജ്ജീകരണ ലിങ്ക് അയയ്‌ക്കുക", diff --git a/src/Locale/mr/Auth.json b/src/Locale/mr/Auth.json index c0b356dd943..2154366c4f6 100644 --- a/src/Locale/mr/Auth.json +++ b/src/Locale/mr/Auth.json @@ -21,6 +21,8 @@ "register_hospital": "हॉस्पिटलचे नाव नोंदवा", "register_page_title": "हॉस्पिटल व्यवस्थापक म्हणून नोंदणी करा", "auth_login_title": "अधिकृत लॉगिन", + "back_to_login": "लॉगिन पृष्ठावर परत या", + "available_in":"उपलब्ध भाषा", "forget_password": "पासवर्ड विसरलात?", "forget_password_instruction": "युजरनेम प्रविष्ट करा आणि आम्ही तुम्हाला पासवर्ड रीसेट करण्यासाठी एक लिंक पाठवू.", "send_reset_link": "रीसेट लिंक पाठवा", diff --git a/src/Locale/ta/Auth.json b/src/Locale/ta/Auth.json index ef5260536e7..1d5e15f241a 100644 --- a/src/Locale/ta/Auth.json +++ b/src/Locale/ta/Auth.json @@ -21,6 +21,8 @@ "register_hospital": "மருத்துவமனை பதிவு", "register_page_title": "மருத்துவமனை நிர்வாகியாக பதிவு செய்யுங்கள்", "auth_login_title": "அங்கீகரிக்கப்பட்ட உள்நுழைவு", + "back_to_login": "உள்நுழைவு பக்கத்திற்குத் திரும்பு", + "available_in":"கிடைக்கும் மொழிகள்", "forget_password": "கடவுச்சொல்லை மறந்துவிட்டீர்களா?", "forget_password_instruction": "உங்கள் பயனர்பெயரை உள்ளிடவும், உங்கள் கடவுச்சொல்லை மீட்டமைக்க ஒரு இணைப்பை நாங்கள் உங்களுக்கு அனுப்புவோம்.", "send_reset_link": "மீட்டமை இணைப்பை அனுப்பவும்", diff --git a/src/Providers/HistoryAPIProvider.tsx b/src/Providers/HistoryAPIProvider.tsx index 3fa4b6fab86..77634b1e875 100644 --- a/src/Providers/HistoryAPIProvider.tsx +++ b/src/Providers/HistoryAPIProvider.tsx @@ -26,7 +26,6 @@ export default function HistoryAPIProvider(props: { children: ReactNode }) { }, { onInitial: true } ); - const resetHistory = () => setHistory((history) => history.slice(0, 1)); return ( From bfa368116606bbe64cf7c19256c8e1d6c453d435 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 28 Feb 2024 09:43:57 +0530 Subject: [PATCH 04/13] Fixes issues with redirection on login (#7237) * Fixes redirect not working upon login fixes #6900 * Add cypress tests --- cypress/e2e/auth_spec/redirect.cy.ts | 32 +++++++++++++++++++++++++++ cypress/pageobject/Login/LoginPage.ts | 14 ++++++++++++ src/Providers/AuthUserProvider.tsx | 5 ++++- 3 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 cypress/e2e/auth_spec/redirect.cy.ts diff --git a/cypress/e2e/auth_spec/redirect.cy.ts b/cypress/e2e/auth_spec/redirect.cy.ts new file mode 100644 index 00000000000..671a896a94f --- /dev/null +++ b/cypress/e2e/auth_spec/redirect.cy.ts @@ -0,0 +1,32 @@ +import { cy, describe, it, beforeEach, Cypress } from "local-cypress"; +import LoginPage from "../../pageobject/Login/LoginPage"; + +describe("redirect", () => { + const loginPage = new LoginPage(); + + beforeEach(() => { + cy.log("Logging in the user devdistrictadmin"); + }); + + it("Check if login redirects to the right url", () => { + cy.awaitUrl("/resource/board", true); + loginPage.loginManuallyAsDistrictAdmin(); + loginPage.ensureLoggedIn(); + cy.url().should("include", "/resource/board"); + }); + + it("Check if the redirect param works", () => { + const baseUrl = Cypress.config("baseUrl"); + cy.awaitUrl(`login?redirect=${baseUrl}/resource/board`, true); + loginPage.loginManuallyAsDistrictAdmin(); + loginPage.ensureLoggedIn(); + cy.url().should("include", "/resource/board"); + }); + + it("Check to ensure that redirect is the same origin", () => { + cy.awaitUrl("login?redirect=https://google.com", true); + loginPage.loginManuallyAsDistrictAdmin(); + loginPage.ensureLoggedIn(); + cy.url().should("include", "/facility"); + }); +}); diff --git a/cypress/pageobject/Login/LoginPage.ts b/cypress/pageobject/Login/LoginPage.ts index f4f188f11d6..94e52c33613 100644 --- a/cypress/pageobject/Login/LoginPage.ts +++ b/cypress/pageobject/Login/LoginPage.ts @@ -10,9 +10,23 @@ class LoginPage { cy.loginByApi("devdoctor", "Coronasafe@123"); } + loginAsStaff(): void { + cy.loginByApi("staffdev", "Coronasafe@123"); + } + + loginManuallyAsDistrictAdmin(): void { + cy.get("input[id='username']").type("devdistrictadmin"); + cy.get("input[id='password']").type("Coronasafe@123"); + cy.get("button").contains("Login").click(); + } + login(username: string, password: string): void { cy.loginByApi(username, password); } + + ensureLoggedIn(): void { + cy.get("p").contains("Sign Out").should("exist"); + } } export default LoginPage; diff --git a/src/Providers/AuthUserProvider.tsx b/src/Providers/AuthUserProvider.tsx index 5435b0f0b45..63da9474f2b 100644 --- a/src/Providers/AuthUserProvider.tsx +++ b/src/Providers/AuthUserProvider.tsx @@ -42,7 +42,10 @@ export default function AuthUserProvider({ children, unauthorized }: Props) { localStorage.setItem(LocalStorageKeys.refreshToken, query.data.refresh); await refetch(); - navigate(getRedirectOr("/")); + + if (location.pathname === "/" || location.pathname === "/login") { + navigate(getRedirectOr("/")); + } } return query; From b3f61460f5c1b51429e4be9246472661ebf4fc98 Mon Sep 17 00:00:00 2001 From: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:44:21 +0530 Subject: [PATCH 05/13] removed unused filters from notifications (#7235) --- src/Common/constants.tsx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 1d3e8c8db40..60530cb0634 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -565,11 +565,6 @@ export const NOTIFICATION_EVENTS: NotificationEvent[] = [ text: "Patient Updated", icon: "l-edit", }, - { - id: "PATIENT_DELETED", - text: "Patient Deleted", - icon: "l-user-minus", - }, { id: "PATIENT_CONSULTATION_CREATED", text: "Patient Consultation Created", @@ -580,11 +575,6 @@ export const NOTIFICATION_EVENTS: NotificationEvent[] = [ text: "Patient Consultation Updated", icon: "l-heart-medical", }, - { - id: "PATIENT_CONSULTATION_DELETED", - text: "Patient Consultation Deleted", - icon: "l-heartbeat", - }, { id: "INVESTIGATION_SESSION_CREATED", text: "Investigation Session Created", From b38789a628e127c94b3aac3a8fb6f77afaab4089 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:45:30 +0530 Subject: [PATCH 06/13] Add Uptime tracking to Location Management (#7230) * Location Uptime tracking * Add facilityId prop to Location component * Fix card alignment --- src/Components/Assets/AssetManage.tsx | 4 +++ src/Components/Common/Uptime.tsx | 29 ++++++++++--------- .../Facility/LocationManagement.tsx | 19 +++++++++++- src/Redux/api.tsx | 2 +- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx index b7197dd0562..661043ba697 100644 --- a/src/Components/Assets/AssetManage.tsx +++ b/src/Components/Assets/AssetManage.tsx @@ -503,6 +503,10 @@ const AssetManage = (props: AssetManageProps) => { Availability History
+ } + parentClassNames="mt-8 flex w-full flex-col bg-white p-4 shadow-sm sm:rounded-lg" /> )}
Service History
diff --git a/src/Components/Common/Uptime.tsx b/src/Components/Common/Uptime.tsx index e7272e2e1f4..4b12a13bd17 100644 --- a/src/Components/Common/Uptime.tsx +++ b/src/Components/Common/Uptime.tsx @@ -169,6 +169,9 @@ export default function Uptime( props: Readonly<{ route: QueryRoute>; params?: Record; + header?: React.ReactNode; + parentClassNames?: string; + centerInfoPanel?: boolean; }> ) { const [summary, setSummary] = useState<{ @@ -191,6 +194,8 @@ export default function Uptime( setNumDays(Math.min(newNumDays, 100)); }; + const centerInfoPanel = props.centerInfoPanel ?? false; + const setUptimeRecord = (records: AvailabilityRecord[]): void => { const recordsByDayBefore: { [key: number]: AvailabilityRecord[] } = {}; @@ -361,10 +366,10 @@ export default function Uptime( ); } else if (summary) { return ( -
-
+
+
-
Availability History
+ {props.header}
@@ -407,15 +412,13 @@ export default function Uptime( }`} >
- {hoveredDay === index && ( - <> - - + {hoveredDay === index && !centerInfoPanel && ( + )} ); @@ -432,7 +435,7 @@ export default function Uptime(
{hoveredDay !== -1 && ( -
+
import("../Common/Loading")); @@ -19,6 +20,7 @@ interface Props { } interface LocationProps extends LocationModel { + facilityId: string; setShowDeletePopup: (e: { open: boolean; name: string; id: string }) => void; } @@ -109,7 +111,11 @@ export default function LocationManagement({ facilityId }: Props) { className="my-8 grid gap-3 @4xl:grid-cols-2 @6xl:grid-cols-3 @[100rem]:grid-cols-4 lg:mx-8"> {(item) => ( - + )}
@@ -208,6 +214,7 @@ const Location = ({ modified_date, id, setShowDeletePopup, + facilityId, }: LocationProps) => (
@@ -241,6 +248,16 @@ const Location = ({ > {middleware_address || "-"}

+ + Middleware Uptime +

+ } + centerInfoPanel + />
>(), }, - getFacilityAssetLocationAvailability: { + listFacilityAssetLocationAvailability: { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/availability/", method: "GET", TRes: Type>(), From ab678269f76b18791edb84f9383eaecee88caa8d Mon Sep 17 00:00:00 2001 From: Devdeep Ghosh <63492939+thedevildude@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:46:00 +0530 Subject: [PATCH 07/13] Introduce (i) Button Popover Component in HL7Monitor and VentilatorPatientVitalsMonitor (#7228) * created a VitalsMonitorAssetPopover component * replaced asset name with (i) button popover * replaced DialogModal with headlessUI popover * minor mobile ui fix HL7Monitor * removed redundant code * minor css fix and dynamic icon feature added * spacing fix * local fix to improper gender type * reusable header for VitalsMonitor * minor css fix * minor popover css fix * added hideHeader optional prop to VitalsMonitor * hide header from VitalsMonitor in ConsultationUpdatesTab * created and implemented VitalsMonitorFooter component * hide header and footer from VitalsMonitor asset configure --------- Co-authored-by: Rithvik Nishad --- .../Assets/AssetType/HL7Monitor.tsx | 9 ++- .../Facility/CentralNursingStation.tsx | 3 +- .../ConsultationUpdatesTab.tsx | 4 + .../VitalsMonitor/HL7PatientVitalsMonitor.tsx | 59 ++------------ .../VentilatorPatientVitalsMonitor.tsx | 66 ++-------------- .../VitalsMonitorAssetPopover.tsx | 77 +++++++++++++++++++ .../VitalsMonitor/VitalsMonitorFooter.tsx | 17 ++++ .../VitalsMonitor/VitalsMonitorHeader.tsx | 56 ++++++++++++++ src/Components/VitalsMonitor/types.ts | 2 + 9 files changed, 179 insertions(+), 114 deletions(-) create mode 100644 src/Components/VitalsMonitor/VitalsMonitorAssetPopover.tsx create mode 100644 src/Components/VitalsMonitor/VitalsMonitorFooter.tsx create mode 100644 src/Components/VitalsMonitor/VitalsMonitorHeader.tsx diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index a7be23baad8..cca0a7571b8 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -131,12 +131,19 @@ const HL7Monitor = (props: HL7MonitorProps) => { )} {assetType === "HL7MONITOR" && ( - + )} {assetType === "VENTILATOR" && ( )}
diff --git a/src/Components/Facility/CentralNursingStation.tsx b/src/Components/Facility/CentralNursingStation.tsx index 186dbbd48e9..e063292cf2f 100644 --- a/src/Components/Facility/CentralNursingStation.tsx +++ b/src/Components/Facility/CentralNursingStation.tsx @@ -207,7 +207,8 @@ export default function CentralNursingStation({ facilityId }: Props) { ?.start_date } key={`${props.patientAssetBed?.bed.id}-${hash}`} - {...props} + patientAssetBed={props.patientAssetBed} + socketUrl={props.socketUrl || ""} config={config} />
diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index 1ccd6e0626d..fdbf4846329 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -111,6 +111,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { meta: monitorBedData?.asset_object?.meta, }} socketUrl={hl7SocketUrl} + hideHeader={true} />
@@ -122,6 +123,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { meta: ventilatorBedData?.asset_object?.meta, }} socketUrl={ventilatorSocketUrl} + hideHeader={true} />
@@ -157,6 +159,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { }} socketUrl={hl7SocketUrl} config={vitals.config} + hideHeader={true} />
)} @@ -173,6 +176,7 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { }} socketUrl={ventilatorSocketUrl} config={vitals.config} + hideHeader={true} />
)} diff --git a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx index d91b0c56b2f..268e6c91ffa 100644 --- a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx +++ b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx @@ -1,7 +1,5 @@ import { useEffect } from "react"; import useHL7VitalsMonitor from "./useHL7VitalsMonitor"; -import { Link } from "raviger"; -import { GENDER_TYPES } from "../../Common/constants"; import CareIcon from "../../CAREUI/icons/CareIcon"; import WaveformLabels from "./WaveformLabels"; import { classNames } from "../../Utils/utils"; @@ -9,6 +7,8 @@ import { IVitalsComponentProps, VitalsValueBase } from "./types"; import { triggerGoal } from "../../Integrations/Plausible"; import useAuthUser from "../../Common/hooks/useAuthUser"; import dayjs from "dayjs"; +import VitalsMonitorHeader from "./VitalsMonitorHeader"; +import VitalsMonitorFooter from "./VitalsMonitorFooter"; const minutesAgo = (timestamp: string) => { return `${dayjs().diff(dayjs(timestamp), "minute")}m ago`; @@ -18,7 +18,7 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) { const { connect, waveformCanvas, data, isOnline } = useHL7VitalsMonitor( props.config ); - const { patient, bed, asset } = props.patientAssetBed ?? {}; + const { bed, asset } = props.patientAssetBed ?? {}; const authUser = useAuthUser(); useEffect(() => { @@ -41,56 +41,8 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) { return (
- {props.patientAssetBed && ( -
-
- {patient ? ( - - {patient?.name} - - ) : ( - - - No Patient - - )} - {patient && ( - - {patient.age}y;{" "} - {GENDER_TYPES.find((g) => g.id === patient.gender)?.icon} - - )} -
-
- {asset && ( - - - {asset.name} - - )} - {bed && ( - - - - {bed.name} - - - - {bed.location_object?.name} - - - )} -
-
+ {props.hideHeader ? null : ( + )}
@@ -227,6 +179,7 @@ export default function HL7PatientVitalsMonitor(props: IVitalsComponentProps) {
+ {props.hideFooter ? null : }
); } diff --git a/src/Components/VitalsMonitor/VentilatorPatientVitalsMonitor.tsx b/src/Components/VitalsMonitor/VentilatorPatientVitalsMonitor.tsx index b03fe0645c6..ee3f471b22c 100644 --- a/src/Components/VitalsMonitor/VentilatorPatientVitalsMonitor.tsx +++ b/src/Components/VitalsMonitor/VentilatorPatientVitalsMonitor.tsx @@ -1,19 +1,18 @@ import { useEffect } from "react"; -import { Link } from "raviger"; -import { GENDER_TYPES } from "../../Common/constants"; import CareIcon from "../../CAREUI/icons/CareIcon"; import useVentilatorVitalsMonitor from "./useVentilatorVitalsMonitor"; import { IVitalsComponentProps, VitalsValueBase } from "./types"; import { classNames } from "../../Utils/utils"; import WaveformLabels from "./WaveformLabels"; import { VitalsNonWaveformContent } from "./HL7PatientVitalsMonitor"; +import VitalsMonitorHeader from "./VitalsMonitorHeader"; +import VitalsMonitorFooter from "./VitalsMonitorFooter"; export default function VentilatorPatientVitalsMonitor( props: IVitalsComponentProps ) { const { connect, waveformCanvas, data, isOnline } = useVentilatorVitalsMonitor(props.config); - const { patient, bed, asset } = props.patientAssetBed ?? {}; useEffect(() => { connect(props.socketUrl); @@ -21,62 +20,8 @@ export default function VentilatorPatientVitalsMonitor( return (
- {props.patientAssetBed && ( -
-
- {patient ? ( - - {patient?.name} - - ) : ( - - - No Patient - - )} - {patient && ( - - {patient.age}y;{" "} - {GENDER_TYPES.find((g) => g.id === patient.gender)?.icon} - - )} -
-
- {asset && ( -
- - - - {asset.name} - - -
- )} - {bed && ( -
- - - - {bed.name} - - - - {bed.location_object?.name} - - -
- )} -
-
+ {props.hideHeader ? null : ( + )}
@@ -138,6 +83,9 @@ export default function VentilatorPatientVitalsMonitor( />
+ {props.hideFooter ? null : ( + + )}
); } diff --git a/src/Components/VitalsMonitor/VitalsMonitorAssetPopover.tsx b/src/Components/VitalsMonitor/VitalsMonitorAssetPopover.tsx new file mode 100644 index 00000000000..cf56c3c5e50 --- /dev/null +++ b/src/Components/VitalsMonitor/VitalsMonitorAssetPopover.tsx @@ -0,0 +1,77 @@ +import CareIcon from "../../CAREUI/icons/CareIcon"; +import { AssetData, assetClassProps } from "../Assets/AssetTypes"; +import ButtonV2 from "../Common/components/ButtonV2"; +import { navigate } from "raviger"; +import { useTranslation } from "react-i18next"; +import { Popover, Transition } from "@headlessui/react"; +import { Fragment } from "react"; + +interface VitalsMonitorAssetPopoverProps { + asset?: AssetData; +} + +const VitalsMonitorAssetPopover = ({ + asset, +}: VitalsMonitorAssetPopoverProps) => { + const { t } = useTranslation(); + + return ( + + + + + + +
+
+ +

{asset?.name}

+
+
+

Middleware Hostname:

+

+ {asset?.resolved_middleware?.hostname} +

+
+
+

Local IP Address:

+

+ {asset?.meta?.local_ip_address} +

+
+ + navigate( + `/facility/${asset?.location_object.facility?.id}/assets/${asset?.id}/configure` + ) + } + id="configure-asset" + data-testid="asset-configure-button" + > + + {t("configure")} + +
+
+
+
+ ); +}; + +export default VitalsMonitorAssetPopover; diff --git a/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx b/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx new file mode 100644 index 00000000000..a9598f16965 --- /dev/null +++ b/src/Components/VitalsMonitor/VitalsMonitorFooter.tsx @@ -0,0 +1,17 @@ +import { AssetData } from "../Assets/AssetTypes"; +import VitalsMonitorAssetPopover from "./VitalsMonitorAssetPopover"; + +interface IVitalsMonitorFooterProps { + asset?: AssetData; +} + +const VitalsMonitorFooter = ({ asset }: IVitalsMonitorFooterProps) => { + return ( +
+

{asset?.name}

+ +
+ ); +}; + +export default VitalsMonitorFooter; diff --git a/src/Components/VitalsMonitor/VitalsMonitorHeader.tsx b/src/Components/VitalsMonitor/VitalsMonitorHeader.tsx new file mode 100644 index 00000000000..2f9df8607b0 --- /dev/null +++ b/src/Components/VitalsMonitor/VitalsMonitorHeader.tsx @@ -0,0 +1,56 @@ +import { PatientAssetBed } from "../Assets/AssetTypes"; +import { Link } from "raviger"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import { GENDER_TYPES } from "../../Common/constants"; + +interface VitalsMonitorHeaderProps { + patientAssetBed?: PatientAssetBed; +} + +const VitalsMonitorHeader = ({ patientAssetBed }: VitalsMonitorHeaderProps) => { + const { patient, bed } = patientAssetBed ?? {}; + return ( +
+
+ {patient ? ( + + {patient?.name} + + ) : ( + + + No Patient + + )} + {patient && ( + + {patient.age}y;{" "} + {GENDER_TYPES.find((g) => g.id === patient.gender)?.icon} + + )} +
+
+ {bed && ( + + + + {bed.name} + + + + {bed.location_object?.name} + + + )} +
+
+ ); +}; + +export default VitalsMonitorHeader; diff --git a/src/Components/VitalsMonitor/types.ts b/src/Components/VitalsMonitor/types.ts index c73d7399b59..d6812d273d6 100644 --- a/src/Components/VitalsMonitor/types.ts +++ b/src/Components/VitalsMonitor/types.ts @@ -50,4 +50,6 @@ export interface IVitalsComponentProps { patientAssetBed?: PatientAssetBed; socketUrl: string; config?: ReturnType; + hideHeader?: boolean; + hideFooter?: boolean; } From 9a53d70cf039728b75d156ce7d54b33a58876a0a Mon Sep 17 00:00:00 2001 From: Gampa Sri Harsh <114745442+sriharsh05@users.noreply.github.com> Date: Wed, 28 Feb 2024 09:46:35 +0530 Subject: [PATCH 08/13] Replace useDispatch with useQuery and request in PatientRegistration form. (#7216) * replace useDispatch with useQuery and request in PatientRegister.tsx * remove promise.all for states * replace useQuery for fetchData with request * replace complex useQuery with request --- src/Components/ExternalResult/models.ts | 3 + src/Components/Patient/PatientRegister.tsx | 473 ++++++++++----------- src/Redux/actions.tsx | 29 -- src/Redux/api.tsx | 8 + 4 files changed, 231 insertions(+), 282 deletions(-) diff --git a/src/Components/ExternalResult/models.ts b/src/Components/ExternalResult/models.ts index 7b136c76cef..bc143593019 100644 --- a/src/Components/ExternalResult/models.ts +++ b/src/Components/ExternalResult/models.ts @@ -20,6 +20,9 @@ export interface IExternalResult { sample_collection_date: string; patient_category: string; srf_id: string; + permanent_address: string; + test_id: string; + village: string; district_object: { id: number; name: string; diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index c6b260efe87..efc269fa00c 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -8,19 +8,6 @@ import { TEST_TYPE, VACCINES, } from "../../Common/constants"; -import { - HCXActions, - createPatient, - externalResult, - getAnyFacility, - getDistrictByState, - getLocalbodyByDistrict, - getPatient, - getStates, - getWardByLocalBody, - searchPatient, - updatePatient, -} from "../../Redux/actions"; import { dateQueryString, getPincodeDetails, @@ -67,10 +54,12 @@ import { debounce } from "lodash-es"; import useAppHistory from "../../Common/hooks/useAppHistory"; import useConfig from "../../Common/hooks/useConfig"; -import { useDispatch } from "react-redux"; import { validatePincode } from "../../Common/validation"; import { FormContextValue } from "../Form/FormContext.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; +import useQuery from "../../Utils/request/useQuery.js"; +import routes from "../../Redux/api.js"; +import request from "../../Utils/request/request.js"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -184,7 +173,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { const authUser = useAuthUser(); const { goBack } = useAppHistory(); const { gov_data_api_key, enable_hcx, enable_abdm } = useConfig(); - const dispatchAction: any = useDispatch(); const { facilityId, id } = props; const [state, dispatch] = useReducer(patientFormReducer, initialState); const [showAlertMessage, setAlertMessage] = useState({ @@ -202,11 +190,9 @@ export const PatientRegister = (props: PatientRegisterProps) => { }); const [careExtId, setCareExtId] = useState(""); const [formField, setFormField] = useState(); - const [isStateLoading, setIsStateLoading] = useState(false); const [isDistrictLoading, setIsDistrictLoading] = useState(false); const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false); const [isWardLoading, setIsWardLoading] = useState(false); - const [states, setStates] = useState([]); const [districts, setDistricts] = useState([]); const [localBody, setLocalBody] = useState([]); const [ward, setWard] = useState([]); @@ -215,7 +201,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { transfer?: boolean; patientList: Array; }>({ patientList: [] }); - const [facilityName, setFacilityName] = useState(""); const [patientName, setPatientName] = useState(""); const [{ extId }, setQuery] = useQueryParams(); const [showLinkAbhaNumberModal, setShowLinkAbhaNumberModal] = useState(false); @@ -236,50 +221,47 @@ export const PatientRegister = (props: PatientRegisterProps) => { const headerText = !id ? "Add Details of Patient" : "Update Patient Details"; const buttonText = !id ? "Add Patient" : "Save Details"; - const fetchDistricts = useCallback( - async (id: number) => { - if (id > 0) { - setIsDistrictLoading(true); - const districtList = await dispatchAction(getDistrictByState({ id })); - if (districtList) { - setDistricts(districtList.data); - } - setIsDistrictLoading(false); - return districtList ? [...districtList.data] : []; + const fetchDistricts = useCallback(async (id: number) => { + if (id > 0) { + setIsDistrictLoading(true); + const { res, data } = await request(routes.getDistrictByState, { + pathParams: { id }, + }); + if (res?.ok && data) { + setDistricts(data); } - }, - [dispatchAction] - ); + setIsDistrictLoading(false); + return data ? [...data] : []; + } + }, []); - const fetchLocalBody = useCallback( - async (id: string) => { - if (Number(id) > 0) { - setIsLocalbodyLoading(true); - const localBodyList = await dispatchAction( - getLocalbodyByDistrict({ id }) - ); - setIsLocalbodyLoading(false); - setLocalBody(localBodyList.data); - } else { - setLocalBody([]); - } - }, - [dispatchAction] - ); + const fetchLocalBody = useCallback(async (id: string) => { + if (Number(id) > 0) { + setIsLocalbodyLoading(true); + const { data } = await request(routes.getLocalbodyByDistrict, { + pathParams: { id }, + }); + setIsLocalbodyLoading(false); + setLocalBody(data || []); + } else { + setLocalBody([]); + } + }, []); - const fetchWards = useCallback( - async (id: string) => { - if (Number(id) > 0) { - setIsWardLoading(true); - const wardList = await dispatchAction(getWardByLocalBody({ id })); - setIsWardLoading(false); - setWard(wardList.data.results); - } else { - setWard([]); + const fetchWards = useCallback(async (id: string) => { + if (Number(id) > 0) { + setIsWardLoading(true); + const { data } = await request(routes.getWardByLocalBody, { + pathParams: { id }, + }); + setIsWardLoading(false); + if (data) { + setWard(data.results); } - }, - [dispatchAction] - ); + } else { + setWard([]); + } + }, []); const parseGenderFromExt = (gender: any, defaultValue: any) => { switch (gender.toLowerCase()) { @@ -297,97 +279,95 @@ export const PatientRegister = (props: PatientRegisterProps) => { const fetchExtResultData = async (e: any, field: any) => { if (e) e.preventDefault(); if (!careExtId) return; - const res = await dispatchAction(externalResult({ id: careExtId })); + const { res, data } = await request(routes.externalResult, { + pathParams: { id: careExtId }, + }); - if (res?.data) { + if (res?.ok && data) { field.onChange({ name: "name", - value: res.data.name ? res.data.name : state.form.name, + value: data.name ? data.name : state.form.name, }); field.onChange({ name: "address", - value: res.data.address ? res.data.address : state.form.address, + value: data.address ? data.address : state.form.address, }); field.onChange({ name: "permanent_address", - value: res.data.permanent_address - ? res.data.permanent_address + value: data.permanent_address + ? data.permanent_address : state.form.permanent_address, }); field.onChange({ name: "gender", - value: res.data.gender - ? parseGenderFromExt(res.data.gender, state.form.gender) + value: data.gender + ? parseGenderFromExt(data.gender, state.form.gender) : state.form.gender, }); field.onChange({ name: "test_id", - value: res.data.test_id ? res.data.test_id : state.form.test_id, + value: data.test_id ? data.test_id : state.form.test_id, }); field.onChange({ name: "srf_id", - value: res.data.srf_id ? res.data.srf_id : state.form.srf_id, + value: data.srf_id ? data.srf_id : state.form.srf_id, }); field.onChange({ name: "state", - value: res.data.district_object - ? res.data.district_object.state + value: data.district_object + ? data.district_object.state : state.form.state, }); field.onChange({ name: "district", - value: res.data.district ? res.data.district : state.form.district, + value: data.district ? data.district : state.form.district, }); field.onChange({ name: "local_body", - value: res.data.local_body - ? res.data.local_body - : state.form.local_body, + value: data.local_body ? data.local_body : state.form.local_body, }); field.onChange({ name: "ward", - value: res.data.ward ? res.data.ward : state.form.ward, + value: data.ward ? data.ward : state.form.ward, }); field.onChange({ name: "village", - value: res.data.village ? res.data.village : state.form.village, + value: data.village ? data.village : state.form.village, }); field.onChange({ name: "disease_status", - value: res.data.result - ? res.data.result.toUpperCase() + value: data.result + ? data.result.toUpperCase() : state.form.disease_status, }); field.onChange({ name: "test_type", - value: res.data.test_type - ? res.data.test_type.toUpperCase() + value: data.test_type + ? data.test_type.toUpperCase() : state.form.test_type, }); field.onChange({ name: "date_of_test", - value: res.data.sample_collection_date - ? res.data.sample_collection_date + value: data.sample_collection_date + ? data.sample_collection_date : state.form.date_of_test, }); field.onChange({ name: "date_of_result", - value: res.data.result_date - ? res.data.result_date - : state.form.date_of_result, + value: data.result_date ? data.result_date : state.form.date_of_result, }); field.onChange({ name: "phone_number", - value: res.data.mobile_number - ? "+91" + res.data.mobile_number + value: data.mobile_number + ? "+91" + data.mobile_number : state.form.phone_number, }); Promise.all([ - fetchDistricts(res.data.district_object.state), - fetchLocalBody(res.data.district), - fetchWards(res.data.local_body), - duplicateCheck(res.data.mobile_number), + fetchDistricts(data.district_object.state), + fetchLocalBody(String(data.district)), + fetchWards(String(data.local_body)), // Convert data.local_body to a string + duplicateCheck(data.mobile_number), ]); setShowImport({ show: false, @@ -399,93 +379,93 @@ export const PatientRegister = (props: PatientRegisterProps) => { const fetchData = useCallback( async (status: statusType) => { setIsLoading(true); - const res = await dispatchAction(getPatient({ id })); + const { res, data } = await request(routes.getPatient, { + pathParams: { id: id ? id : 0 }, + }); if (!status.aborted) { - if (res?.data) { - setFacilityName(res.data.facility_object.name); - setPatientName(res.data.name); - console.log(res.data); + if (res?.ok && data) { + setPatientName(data.name || ""); const formData = { - ...res.data, - health_id_number: res.data.abha_number_object?.abha_number || "", - health_id: res.data.abha_number_object?.health_id || "", - nationality: res.data.nationality ? res.data.nationality : "India", - gender: res.data.gender ? res.data.gender : "", - cluster_name: res.data.cluster_name ? res.data.cluster_name : "", - state: res.data.state ? res.data.state : "", - district: res.data.district ? res.data.district : "", - blood_group: res.data.blood_group - ? res.data.blood_group === "UNKNOWN" + ...data, + health_id_number: data.abha_number_object?.abha_number || "", + health_id: data.abha_number_object?.health_id || "", + nationality: data.nationality ? data.nationality : "India", + gender: data.gender ? data.gender : undefined, + cluster_name: data.cluster_name ? data.cluster_name : "", + state: data.state ? data.state : "", + district: data.district ? data.district : "", + blood_group: data.blood_group + ? data.blood_group === "UNKNOWN" ? "UNK" - : res.data.blood_group + : data.blood_group : "", - local_body: res.data.local_body ? res.data.local_body : "", - ward: res.data.ward_object ? res.data.ward_object.id : undefined, - village: res.data.village ? res.data.village : "", + local_body: data.local_body ? data.local_body : "", + ward: data.ward_object ? data.ward_object.id : undefined, + village: data.village ? data.village : "", medical_history: [], - is_antenatal: String(!!res.data.is_antenatal), - allergies: res.data.allergies ? res.data.allergies : "", - pincode: res.data.pincode ? res.data.pincode : "", - ongoing_medication: res.data.ongoing_medication - ? res.data.ongoing_medication + is_antenatal: String(!!data.is_antenatal), + allergies: data.allergies ? data.allergies : "", + pincode: data.pincode ? data.pincode : "", + ongoing_medication: data.ongoing_medication + ? data.ongoing_medication : "", - is_declared_positive: res.data.is_declared_positive - ? String(res.data.is_declared_positive) + is_declared_positive: data.is_declared_positive + ? String(data.is_declared_positive) : "false", - designation_of_health_care_worker: res.data - .designation_of_health_care_worker - ? res.data.designation_of_health_care_worker + designation_of_health_care_worker: + data.designation_of_health_care_worker + ? data.designation_of_health_care_worker + : "", + instituion_of_health_care_worker: + data.instituion_of_health_care_worker + ? data.instituion_of_health_care_worker + : "", + + number_of_primary_contacts: data.number_of_primary_contacts + ? data.number_of_primary_contacts : "", - instituion_of_health_care_worker: res.data - .instituion_of_health_care_worker - ? res.data.instituion_of_health_care_worker + number_of_secondary_contacts: data.number_of_secondary_contacts + ? data.number_of_secondary_contacts : "", - - number_of_primary_contacts: res.data.number_of_primary_contacts - ? res.data.number_of_primary_contacts - : "", - number_of_secondary_contacts: res.data.number_of_secondary_contacts - ? res.data.number_of_secondary_contacts - : "", - contact_with_confirmed_carrier: res.data - .contact_with_confirmed_carrier - ? String(res.data.contact_with_confirmed_carrier) + contact_with_confirmed_carrier: data.contact_with_confirmed_carrier + ? String(data.contact_with_confirmed_carrier) : "false", - contact_with_suspected_carrier: res.data - .contact_with_suspected_carrier - ? String(res.data.contact_with_suspected_carrier) + contact_with_suspected_carrier: data.contact_with_suspected_carrier + ? String(data.contact_with_suspected_carrier) : "false", - is_vaccinated: String(res.data.is_vaccinated), - number_of_doses: res.data.number_of_doses - ? String(res.data.number_of_doses) + is_vaccinated: String(data.is_vaccinated), + number_of_doses: data.number_of_doses + ? String(data.number_of_doses) : "0", - vaccine_name: res.data.vaccine_name ? res.data.vaccine_name : null, - last_vaccinated_date: res.data.last_vaccinated_date - ? res.data.last_vaccinated_date + vaccine_name: data.vaccine_name ? data.vaccine_name : null, + last_vaccinated_date: data.last_vaccinated_date + ? data.last_vaccinated_date : null, }; - - formData.sameAddress = - res.data.address === res.data.permanent_address; - res.data.medical_history.forEach((i: any) => { - const medicalHistory = MEDICAL_HISTORY_CHOICES.find( - (j: any) => - String(j.text).toLowerCase() === String(i.disease).toLowerCase() - ); - if (medicalHistory) { - formData.medical_history.push(medicalHistory.id); - formData[`medical_history_${medicalHistory.id}`] = i.details; + formData.sameAddress = data.address === data.permanent_address; + (data.medical_history ? data.medical_history : []).forEach( + (i: any) => { + const medicalHistory = MEDICAL_HISTORY_CHOICES.find( + (j) => + String(j.text).toLowerCase() === + String(i.disease).toLowerCase() + ); + if (medicalHistory) { + formData.medical_history.push(Number(medicalHistory.id)); + formData[`medical_history_${String(medicalHistory.id)}`] = + i.details; + } } - }); + ); dispatch({ type: "set_form", form: formData, }); Promise.all([ - fetchDistricts(res.data.state), - fetchLocalBody(res.data.district), - fetchWards(res.data.local_body), + fetchDistricts(data.state ?? 0), + fetchLocalBody(data.district ? String(data.district) : ""), + fetchWards(data.local_body ? String(data.local_body) : ""), // Convert data.local_body to string ]); } else { goBack(); @@ -493,37 +473,25 @@ export const PatientRegister = (props: PatientRegisterProps) => { setIsLoading(false); } }, - [dispatchAction, fetchDistricts, fetchLocalBody, fetchWards, id] + [id] ); - useEffect(() => { - const fetchPatientInsuranceDetails = async () => { - if (!id) { + useQuery(routes.listHCXPolicies, { + query: { + patient: id, + }, + prefetch: !!id, + onResponse: ({ data }) => { + if (data) { + setInsuranceDetails(data.results); + } else { setInsuranceDetails([]); - return; } - - const res = await dispatchAction( - HCXActions.policies.list({ patient: id }) - ); - if (res?.data) { - setInsuranceDetails(res.data.results); - } - }; - - fetchPatientInsuranceDetails(); - }, [dispatchAction, id]); - - const fetchStates = useCallback( - async (status: statusType) => { - setIsStateLoading(true); - const statesRes = await dispatchAction(getStates()); - if (!status.aborted && statesRes.data.results) { - setStates(statesRes.data.results); - } - setIsStateLoading(false); }, - [dispatchAction] + }); + + const { data: stateData, loading: isStateLoading } = useQuery( + routes.statesList ); useAbortableEffect( @@ -531,24 +499,14 @@ export const PatientRegister = (props: PatientRegisterProps) => { if (id) { fetchData(status); } - fetchStates(status); }, [dispatch, fetchData] ); - useEffect(() => { - async function fetchFacilityName() { - if (facilityId && !id) { - const res = await dispatchAction(getAnyFacility(facilityId)); - - setFacilityName(res?.data?.name || ""); - } else { - setFacilityName(""); - } - } - - fetchFacilityName(); - }, [dispatchAction, facilityId]); + const { data: facilityObject } = useQuery(routes.getAnyFacility, { + pathParams: { id: facilityId }, + prefetch: !!facilityId, + }); const validateForm = (form: any) => { const errors: Partial> = {}; @@ -701,7 +659,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { const pincodeDetails = await getPincodeDetails(e.value, gov_data_api_key); if (!pincodeDetails) return; - const matchedState = states?.find((state) => { + const matchedState = stateData?.results?.find((state) => { return includesIgnoreCase(state.name, pincodeDetails.statename); }); if (!matchedState) return; @@ -715,9 +673,9 @@ export const PatientRegister = (props: PatientRegisterProps) => { if (!matchedDistrict) return; setField({ name: "state", value: matchedState.id }); - setField({ name: "district", value: matchedDistrict.id }); + setField({ name: "district", value: matchedDistrict.id.toString() }); // Convert matchedDistrict.id to string - fetchLocalBody(matchedDistrict.id); + fetchLocalBody(matchedDistrict.id.toString()); // Convert matchedDistrict.id to string setShowAutoFilledPincode(true); setTimeout(() => { setShowAutoFilledPincode(false); @@ -834,37 +792,45 @@ export const PatientRegister = (props: PatientRegisterProps) => { medical_history, is_active: true, }; - const res = await dispatchAction( - id - ? updatePatient(data, { id }) - : createPatient({ ...data, facility: facilityId }) - ); - if (res && res.data && res.status != 400) { + const { res, data: requestData } = id + ? await request(routes.updatePatient, { + pathParams: { id }, + body: data, + }) + : await request(routes.addPatient, { + body: { ...data, facility: facilityId }, + }); + if (res?.ok && requestData) { await Promise.all( insuranceDetails.map(async (obj) => { const policy = { ...obj, - patient: res.data.id, + patient: requestData.id, insurer_id: obj.insurer_id || undefined, insurer_name: obj.insurer_name || undefined, }; - const policyRes = await (policy.id - ? dispatchAction( - HCXActions.policies.update(policy.id, policy as HCXPolicyModel) - ) - : dispatchAction( - HCXActions.policies.create(policy as HCXPolicyModel) - )); - - if (enable_hcx) { - const eligibilityCheckRes = await dispatchAction( - HCXActions.checkEligibility(policyRes.data.id) - ); - if (eligibilityCheckRes.status === 200) { - Notification.Success({ msg: "Checking Policy Eligibility..." }); - } else { - Notification.Error({ msg: "Something Went Wrong..." }); - } + const { data: policyData } = policy.id + ? await request(routes.updateHCXPolicy, { + pathParams: { external_id: policy.id }, + body: policy, + }) + : await request(routes.createHCXPolicy, { + body: policy, + }); + + if (enable_hcx && policyData?.id) { + await request(routes.hcxCheckEligibility, { + body: { policy: policyData?.id }, + onResponse: ({ res }) => { + if (res?.ok) { + Notification.Success({ + msg: "Checking Policy Eligibility...", + }); + } else { + Notification.Error({ msg: "Something Went Wrong..." }); + } + }, + }); } }) ); @@ -873,10 +839,12 @@ export const PatientRegister = (props: PatientRegisterProps) => { if (!id) { setAlertMessage({ show: true, - message: `Please note down patient name: ${formData.name} and patient ID: ${res.data.id}`, + message: `Please note down patient name: ${formData.name} and patient ID: ${requestData.id}`, title: "Patient Added Successfully", }); - navigate(`/facility/${facilityId}/patient/${res.data.id}/consultation`); + navigate( + `/facility/${facilityId}/patient/${requestData.id}/consultation` + ); } else { Notification.Success({ msg: "Patient updated successfully", @@ -970,33 +938,32 @@ export const PatientRegister = (props: PatientRegisterProps) => { }); }; - const duplicateCheck = useCallback( - debounce(async (phoneNo: string) => { - if ( - phoneNo && - PhoneNumberValidator()(parsePhoneNumber(phoneNo) ?? "") === undefined - ) { - const query = { - phone_number: parsePhoneNumber(phoneNo), - }; - const res = await dispatchAction(searchPatient(query)); - if (res?.data?.results) { - const duplicateList = !id - ? res.data.results - : res.data.results.filter( - (item: DupPatientModel) => item.patient_id !== id - ); - if (duplicateList.length) { - setStatusDialog({ - show: true, - patientList: duplicateList, - }); - } + const duplicateCheck = debounce(async (phoneNo: string) => { + if ( + phoneNo && + PhoneNumberValidator()(parsePhoneNumber(phoneNo) ?? "") === undefined + ) { + const query = { + phone_number: parsePhoneNumber(phoneNo), + }; + const { res, data } = await request(routes.searchPatient, { + query, + }); + if (res?.ok && data?.results) { + const duplicateList = !id + ? data.results + : data.results.filter( + (item: DupPatientModel) => item.patient_id !== id + ); + if (duplicateList.length) { + setStatusDialog({ + show: true, + patientList: duplicateList, + }); } } - }, 300), - [] - ); + } + }, 300); const handleDialogClose = (action: string) => { if (action === "transfer") { @@ -1080,7 +1047,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { } }} crumbsReplacements={{ - [facilityId]: { name: facilityName }, + [facilityId]: { name: facilityObject?.name }, [id ?? "????"]: { name: patientName }, }} /> @@ -1424,7 +1391,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { label="State" required placeholder="Choose State" - options={states} + options={stateData ? stateData.results : []} optionLabel={(o: any) => o.name} optionValue={(o: any) => o.id} onChange={(e: any) => { diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index c11a5f9de59..73e1a839519 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -62,50 +62,21 @@ export const downloadFacilityTriage = () => { }; //Patient -export const searchPatient = (params: object) => { - return fireRequest("searchPatient", [], params); -}; export const getAllPatient = (params: object, altKey: string) => { return fireRequest("patientList", [], params, null, altKey); }; -export const createPatient = (params: object) => { - return fireRequest("addPatient", [], params); -}; export const getPatient = (pathParam: object) => { return fireRequest("getPatient", [], {}, pathParam); }; -export const updatePatient = (params: object, pathParam: object) => { - return fireRequest("updatePatient", [], params, pathParam); -}; - -export const transferPatient = (params: object, pathParam: object) => { - return fireRequest("transferPatient", [], params, pathParam); -}; - export const patchPatient = (params: object, pathParam: object) => { return fireRequest("patchPatient", [], params, pathParam); }; -export const getStates = () => { - return fireRequest("statesList", []); -}; - // District/State/Local body/ward -export const getDistrictByState = (pathParam: object) => { - return fireRequest("getDistrictByState", [], {}, pathParam); -}; export const getDistrictByName = (params: object) => { return fireRequest("getDistrictByName", [], params, null); }; -export const getLocalbodyByDistrict = (pathParam: object) => { - return fireRequest("getLocalbodyByDistrict", [], {}, pathParam); -}; - -export const getWardByLocalBody = (pathParam: object) => { - return fireRequest("getWardByLocalBody", [], {}, pathParam); -}; - // Sample Test export const downloadSampleTests = (params: object) => { return fireRequest("getTestSampleList", [], { ...params, csv: 1 }); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 3bed047a4fe..d566fb91c86 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -57,6 +57,7 @@ import { InventoryLogResponse, InventoryItemsModel, PatientTransferResponse, + DupPatientModel, } from "../Components/Facility/models"; import { IDeleteBedCapacity, @@ -665,6 +666,7 @@ const routes = { searchPatient: { path: "/api/v1/patient/search", + TRes: Type>(), }, patientList: { path: "/api/v1/patient/", @@ -674,15 +676,18 @@ const routes = { addPatient: { path: "/api/v1/patient/", method: "POST", + TRes: Type(), }, getPatient: { path: "/api/v1/patient/{id}/", method: "GET", + TBody: Type(), TRes: Type(), }, updatePatient: { path: "/api/v1/patient/{id}/", method: "PUT", + TRes: Type(), }, patchPatient: { path: "/api/v1/patient/{id}/", @@ -1417,6 +1422,7 @@ const routes = { hcxCheckEligibility: { path: "/api/v1/hcx/check_eligibility/", method: "POST", + TRes: Type(), }, listHCXPolicies: { @@ -1428,6 +1434,7 @@ const routes = { createHCXPolicy: { path: "/api/v1/hcx/policy/", method: "POST", + TRes: Type(), }, getHCXPolicy: { @@ -1438,6 +1445,7 @@ const routes = { updateHCXPolicy: { path: "/api/v1/hcx/policy/{external_id}/", method: "PUT", + TRes: Type(), }, partialUpdateHCXPolicy: { From aa7b41d254cf4ebc9553f3795b5a495e0856d5de Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 28 Feb 2024 13:04:03 +0530 Subject: [PATCH 09/13] Fixes abort controller not working in `useQuery` (#7267) Fixes #7265; --- src/Utils/request/useQuery.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/request/useQuery.ts b/src/Utils/request/useQuery.ts index b80d7c52a13..7c474c447ea 100644 --- a/src/Utils/request/useQuery.ts +++ b/src/Utils/request/useQuery.ts @@ -31,7 +31,7 @@ export default function useQuery( : overrides ?? options; setLoading(true); - const response = await request(route, resolvedOptions); + const response = await request(route, { ...resolvedOptions, controller }); setResponse(response); setLoading(false); return response; From 490da758f7f656025bb20b09b7f66b46fe6c1311 Mon Sep 17 00:00:00 2001 From: Abhiuday Gupta <77210185+aeswibon@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:04:56 +0530 Subject: [PATCH 10/13] fix: Replace useDispatch w. useQuery/request for Investigations module (src/Components/Facility/Investigations) (#7172) * fix(investigation): replaced useDispatch with useQuery/request * fix(investigation): resloved comments * fix(reports): pushing buggy code * fix(reports): fixed buggy code * fix(investigation): replaced any with types * fix(consultation): fixed investigation suggestion --------- Co-authored-by: Rithvik Nishad --- .../InvestigationSuggestions.tsx | 53 +++-- .../Facility/Investigations/Reports/index.tsx | 189 ++++++++---------- .../Investigations/ShowInvestigation.tsx | 149 ++++++-------- .../Investigations/ViewInvestigations.tsx | 11 +- .../Facility/Investigations/index.tsx | 137 ++++++------- .../Investigations/investigationsTab.tsx | 82 ++------ src/Components/Facility/models.tsx | 12 +- src/Locale/en/Common.json | 4 +- src/Locale/en/Consultation.json | 21 +- src/Redux/api.tsx | 94 +++++---- 10 files changed, 333 insertions(+), 419 deletions(-) diff --git a/src/Components/Facility/Investigations/InvestigationSuggestions.tsx b/src/Components/Facility/Investigations/InvestigationSuggestions.tsx index 5cee941765d..5e43d13e07e 100644 --- a/src/Components/Facility/Investigations/InvestigationSuggestions.tsx +++ b/src/Components/Facility/Investigations/InvestigationSuggestions.tsx @@ -1,53 +1,52 @@ -import { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { useTranslation } from "react-i18next"; import CareIcon from "../../../CAREUI/icons/CareIcon"; -import { getConsultation } from "../../../Redux/actions"; +import routes from "../../../Redux/api"; +import dayjs from "../../../Utils/dayjs"; +import useQuery from "../../../Utils/request/useQuery"; +import Loading from "../../Common/Loading"; import ButtonV2 from "../../Common/components/ButtonV2"; -import { InvestigationType } from "../../Common/prescription-builder/InvestigationBuilder"; import { InvestigationResponse } from "./Reports/types"; -import dayjs from "../../../Utils/dayjs"; export default function ViewInvestigationSuggestions(props: { - consultationId: any; + consultationId: string; logUrl?: string; investigations?: InvestigationResponse; }) { + const { t } = useTranslation(); const { consultationId, logUrl, investigations: previousInvestigations, } = props; - const dispatch = useDispatch(); - const [investigations, setInvestigations] = useState< - InvestigationType[] | null - >(null); + const { data: investigations, loading } = useQuery(routes.getConsultation, { + pathParams: { + id: consultationId, + }, + }); - useEffect(() => { - getConsultationData(); - }, [consultationId]); + if (loading) { + return ; + } - const getConsultationData = async () => { - const res = (await dispatch(getConsultation(consultationId))) as any; - setInvestigations(res.data.investigation || []); - }; + console.log("Investigations: ", investigations); return (
-

Investigations Suggested

+

{t("investigations_suggested")}

- - - {logUrl && } + + + {logUrl && } - {Array.isArray(investigations) ? ( - investigations.map((investigation, index) => { + {investigations?.investigation && + Array.isArray(investigations?.investigation) ? ( + investigations.investigation.map((investigation, index) => { let nextFurthestInvestigation: any = undefined; - return ( )} @@ -202,7 +201,7 @@ export default function ViewInvestigationSuggestions(props: {
InvestigationsTo be conductedLog Report{t("investigations")}{t("to_be_conducted")}{t("log_report")}
@@ -194,7 +193,7 @@ export default function ViewInvestigationSuggestions(props: { ) : (
- No Investigation Suggestions + {t("no_investigation_suggestions")}
{Array.isArray(investigations) ? ( - investigations.map((investigation, index) => { + investigations.investigation?.map((investigation, index) => { let nextFurthestInvestigation: any = undefined; return ( @@ -315,7 +314,7 @@ export default function ViewInvestigationSuggestions(props: { }) ) : (
- No Investigation Suggestions + {t("no_investigation_suggestions")}
)}
diff --git a/src/Components/Facility/Investigations/Reports/index.tsx b/src/Components/Facility/Investigations/Reports/index.tsx index 6800a6208aa..36a553f9f52 100644 --- a/src/Components/Facility/Investigations/Reports/index.tsx +++ b/src/Components/Facility/Investigations/Reports/index.tsx @@ -1,24 +1,21 @@ -import * as Notification from "../../../../Utils/Notifications"; -import _ from "lodash-es"; +import { useCallback, useReducer, useState } from "react"; import { InvestigationGroup, InvestigationType } from ".."; -import { - getPatient, - getPatientInvestigation, - listInvestigationGroups, - listInvestigations, -} from "../../../../Redux/actions"; -import { useCallback, useEffect, useReducer, useState } from "react"; -import AutocompleteMultiSelectFormField from "../../../Form/FormFields/AutocompleteMultiselect"; +import _ from "lodash"; +import { useTranslation } from "react-i18next"; +import routes from "../../../../Redux/api"; +import * as Notification from "../../../../Utils/Notifications"; +import request from "../../../../Utils/request/request"; +import { PaginatedResponse } from "../../../../Utils/request/types"; +import useQuery from "../../../../Utils/request/useQuery"; +import Loading from "../../../Common/Loading"; import ButtonV2 from "../../../Common/components/ButtonV2"; import CircularProgress from "../../../Common/components/CircularProgress"; -import { FieldChangeEvent } from "../../../Form/FormFields/Utils"; -import { InvestigationResponse } from "./types"; -import Loading from "../../../Common/Loading"; import Page from "../../../Common/components/Page"; +import AutocompleteMultiSelectFormField from "../../../Form/FormFields/AutocompleteMultiselect"; +import { FieldChangeEvent } from "../../../Form/FormFields/Utils"; import ReportTable from "./ReportTable"; -import { useDispatch } from "react-redux"; -import { useRef } from "react"; +import { Investigation, InvestigationResponse } from "./types"; const RESULT_PER_PAGE = 14; interface InitialState { @@ -91,16 +88,10 @@ const investigationReportsReducer = (state = initialState, action: any) => { }; const InvestigationReports = ({ id }: any) => { - const dispatchAction: any = useDispatch(); + const { t } = useTranslation(); const [page, setPage] = useState(1); const [sessionPage, setSessionPage] = useState(1); const [isNextSessionDisabled, setIsNextSessionDisabled] = useState(false); - const [patientDetails, setPatientDetails] = useState<{ - name: string; - age: number; - date_of_birth: string; - hospitalName: string; - }>({ name: "", age: -1, date_of_birth: "", hospitalName: "" }); const [state, dispatch] = useReducer( investigationReportsReducer, initialState @@ -115,17 +106,21 @@ const InvestigationReports = ({ id }: any) => { selectedInvestigations, } = state as InitialState; + console.log("state", state); + const fetchInvestigationsData = useCallback( - ( - onSuccess: (data: any, pageNo: number) => void, + async ( + onSuccess: ( + data: PaginatedResponse, + pageNo: number + ) => void, curPage = 1, curSessionPage = 1 ) => { dispatch({ type: "set_loading", - payload: { ...isLoading, tableData: true }, + payload: { ...isLoading, investigationLoading: true }, }); - const pageStart = ((curPage || 1) - 1) * RESULT_PER_PAGE; const investigationsParams = ( selectedInvestigations.length @@ -141,31 +136,27 @@ const InvestigationReports = ({ id }: any) => { }); dispatch({ type: "set_loading", - payload: { ...isLoading, tableData: false }, + payload: { ...isLoading, investigationLoading: false }, }); return; } - - dispatchAction( - getPatientInvestigation( - { - investigations: investigationsParams, - session_page: curSessionPage, - }, - id - ) - ).then((res: any) => { - dispatch({ - type: "set_loading", - payload: { ...isLoading, tableData: false }, - }); - if (res?.data?.results) { - onSuccess(res.data, curPage); - setPage(curPage + 1); - } + const data = await request(routes.getPatientInvestigation, { + pathParams: { patient_external_id: id }, + query: { + investigations: investigationsParams, + session_page: curSessionPage, + }, }); + dispatch({ + type: "set_loading", + payload: { ...isLoading, investigationLoading: false }, + }); + if (data && data.data?.results) { + onSuccess(data.data, curPage); + setPage(curPage + 1); + } }, - [dispatchAction, id, investigations, isLoading, selectedInvestigations] + [id, investigations, isLoading, selectedInvestigations] ); const fetchInvestigation = useCallback(async () => { @@ -175,14 +166,15 @@ const InvestigationReports = ({ id }: any) => { }); const data = await Promise.all( - selectedGroup.map((group, i) => { - return dispatchAction( - listInvestigations({ group: group }, `listInvestigations_${i}`) - ).then((res: any) => res.data && res.data.results); - }) + selectedGroup.map((group) => + request(routes.listInvestigations, { + query: { group: group }, + }) + ) ); const investigationList = _.chain(data) + .flatMap((i) => i?.data?.results) .compact() .flatten() .map((i) => ({ @@ -197,52 +189,25 @@ const InvestigationReports = ({ id }: any) => { type: "set_loading", payload: { ...isLoading, investigationLoading: false }, }); - }, [dispatchAction, isLoading, selectedGroup]); + }, [isLoading, selectedGroup]); - const fetchInvestigationGroups = useRef(() => { - dispatch({ - type: "set_loading", - payload: { ...isLoading, investigationLoading: false }, - }); - - dispatchAction(listInvestigationGroups({})).then((res: any) => { + useQuery(routes.listInvestigationGroups, { + onResponse: (res) => { if (res && res.data) { dispatch({ type: "set_investigation_groups", payload: res.data.results, }); } - - dispatch({ - type: "set_loading", - payload: { ...isLoading, investigationGroupLoading: false }, - }); - }); + }, }); - useEffect(() => { - async function fetchPatientName() { - if (id) { - const res = await dispatchAction(getPatient({ id: id })); - if (res.data) { - setPatientDetails({ - name: res.data.name, - age: res.data.age, - date_of_birth: res.data.date_of_birth, - hospitalName: res.data.facility_object.name, - }); - } - } else { - setPatientDetails({ - name: "", - age: -1, - date_of_birth: "", - hospitalName: "", - }); - } + const { data: patientData, loading: patientLoading } = useQuery( + routes.getPatient, + { + pathParams: { id: id }, } - fetchPatientName(); - }, [dispatchAction, id]); + ); const handleGroupSelect = ({ value }: FieldChangeEvent) => { dispatch({ type: "set_investigations", payload: [] }); @@ -252,13 +217,11 @@ const InvestigationReports = ({ id }: any) => { dispatch({ type: "set_selected_group", payload: value }); }; - useEffect(() => { - fetchInvestigationGroups.current(); - }, []); - - // eslint-disable-next-line const handleLoadMore = () => { - const onSuccess = (data: any, pageNo: number) => { + const onSuccess = ( + data: PaginatedResponse, + pageNo: number + ) => { if (data.results.length === 0 && pageNo + 1 <= totalPage) { fetchInvestigationsData(onSuccess, pageNo + 1, sessionPage); } else { @@ -268,13 +231,12 @@ const InvestigationReports = ({ id }: any) => { }); } }; - fetchInvestigationsData(onSuccess, page, sessionPage); }; const handleGenerateReports = useCallback( (curSessionPage = 1) => { - const onSuccess = (data: any) => { + const onSuccess = (data: PaginatedResponse) => { if (curSessionPage > 1 && !data.results.length) { setSessionPage(curSessionPage - 1); setIsNextSessionDisabled(true); @@ -289,10 +251,8 @@ const InvestigationReports = ({ id }: any) => { }); } } - document.getElementById("reports_section")?.scrollIntoView(); }; - fetchInvestigationsData(onSuccess, 1, curSessionPage); }, [fetchInvestigationsData] @@ -321,12 +281,16 @@ const InvestigationReports = ({ id }: any) => { const prevSessionDisabled = sessionPage <= 1 || isLoading.tableData; const nextSessionDisabled = isNextSessionDisabled || isLoading.tableData; + if (patientLoading) { + return ; + } + return ( {!isLoading.investigationGroupLoading ? ( @@ -335,14 +299,14 @@ const InvestigationReports = ({ id }: any) => { option.name} optionValue={(option) => option.external_id} isLoading={isLoading.investigationLoading} - placeholder="Select Groups" + placeholder={t("select_groups")} selectAll />
@@ -353,7 +317,7 @@ const InvestigationReports = ({ id }: any) => { variant="primary" className="my-2.5" > - Get Tests + {t("get_tests")} )} {!!isLoading.investigationLoading && ( @@ -365,7 +329,7 @@ const InvestigationReports = ({ id }: any) => { @@ -377,7 +341,7 @@ const InvestigationReports = ({ id }: any) => { optionLabel={(option) => option.name} optionValue={(option) => option} isLoading={isLoading.investigationLoading} - placeholder="Select Investigations" + placeholder={t("select_investigations")} />
@@ -390,7 +354,7 @@ const InvestigationReports = ({ id }: any) => { variant="primary" className="my-2.5" > - Generate Report + {t("generate_report")} )} @@ -407,20 +371,25 @@ const InvestigationReports = ({ id }: any) => { onClick={() => handleSessionPage("NEXT")} disabled={prevSessionDisabled} > - {isLoading.tableData ? "Loading..." : "Next Sessions"} + {isLoading.tableData ? "Loading..." : t("next_sessions")} handleSessionPage("PREV")} disabled={nextSessionDisabled} > - {isLoading.tableData ? "Loading..." : "Prev Sessions"} + {isLoading.tableData ? "Loading..." : t("prev_sessions")} {!loadMoreDisabled && ( @@ -430,7 +399,7 @@ const InvestigationReports = ({ id }: any) => { className="my-2.5 w-full" variant="primary" > - Load More + {t("load_more")} )} diff --git a/src/Components/Facility/Investigations/ShowInvestigation.tsx b/src/Components/Facility/Investigations/ShowInvestigation.tsx index 00196bb6678..c193148f1d9 100644 --- a/src/Components/Facility/Investigations/ShowInvestigation.tsx +++ b/src/Components/Facility/Investigations/ShowInvestigation.tsx @@ -1,17 +1,13 @@ -import { useCallback, useReducer, useState, useEffect, lazy } from "react"; -import { useDispatch } from "react-redux"; -import { statusType, useAbortableEffect } from "../../../Common/utils"; -import { - editInvestigation, - getInvestigation, - getPatient, -} from "../../../Redux/actions"; -import PageTitle from "../../Common/PageTitle"; -import InvestigationTable from "./InvestigationTable"; -import _ from "lodash-es"; -import { set } from "lodash-es"; +import _, { set } from "lodash-es"; import { navigate } from "raviger"; +import { lazy, useCallback, useReducer } from "react"; +import { useTranslation } from "react-i18next"; +import routes from "../../../Redux/api"; import * as Notification from "../../../Utils/Notifications.js"; +import request from "../../../Utils/request/request"; +import useQuery from "../../../Utils/request/useQuery"; +import PageTitle from "../../Common/PageTitle"; +import InvestigationTable from "./InvestigationTable"; const Loading = lazy(() => import("../../Common/Loading")); @@ -40,73 +36,49 @@ const updateFormReducer = (state = initialState, action: any) => { }; export default function ShowInvestigation(props: any) { + const { t } = useTranslation(); const { consultationId, patientId, facilityId, sessionId } = props; - const dispatchAction: any = useDispatch(); - const [isLoading, setIsLoading] = useState(false); const [state, dispatch] = useReducer(updateFormReducer, initialState); - const [facilityName, setFacilityName] = useState(""); - const [patientName, setPatientName] = useState(""); - - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction( - getInvestigation({ session: sessionId }, consultationId) - ); - if (!status.aborted) { - if (res && res?.data?.results) { - const valueMap = res.data.results.reduce( - (acc: any, cur: { id: any }) => ({ ...acc, [cur.id]: cur }), - {} - ); + const { loading: investigationLoading } = useQuery(routes.getInvestigation, { + pathParams: { + consultation_external_id: consultationId, + }, + query: { + session: sessionId, + }, + onResponse: (res) => { + if (res && res.data) { + const valueMap = res.data.results.reduce( + (acc: any, cur: { id: any }) => ({ ...acc, [cur.id]: cur }), + {} + ); - const changedValues = res.data.results.reduce( - (acc: any, cur: any) => ({ - ...acc, - [cur.id]: { - id: cur?.id, - initialValue: cur?.notes || cur?.value || null, - value: cur?.value || null, - notes: cur?.notes || null, - }, - }), - {} - ); + const changedValues = res.data.results.reduce( + (acc: any, cur: any) => ({ + ...acc, + [cur.id]: { + id: cur?.id, + initialValue: cur?.notes || cur?.value || null, + value: cur?.value || null, + notes: cur?.notes || null, + }, + }), + {} + ); - dispatch({ type: "set_initial_values", initialValues: valueMap }); - dispatch({ - type: "set_changed_fields", - changedFields: changedValues, - }); - } - setIsLoading(false); + dispatch({ type: "set_initial_values", initialValues: valueMap }); + dispatch({ type: "set_changed_fields", changedFields: changedValues }); } }, - [consultationId, dispatchAction, sessionId] - ); - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] - ); + }); - useEffect(() => { - async function fetchPatientName() { - if (patientId) { - const res = await dispatchAction(getPatient({ id: patientId })); - if (res.data) { - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); - } - } else { - setPatientName(""); - setFacilityName(""); - } + const { data: patientData, loading: patientLoading } = useQuery( + routes.getPatient, + { + pathParams: { id: patientId }, } - fetchPatientName(); - }, [dispatchAction, patientId]); + ); const handleValueChange = (value: any, name: string) => { const changedFields = { ...state.changedFields }; @@ -127,9 +99,10 @@ export default function ShowInvestigation(props: any) { })); if (data.length) { - const res = await dispatchAction( - editInvestigation({ investigations: data }, consultationId) - ); + const { res } = await request(routes.editInvestigation, { + pathParams: { consultation_external_id: consultationId }, + body: { investigations: data }, + }); if (res && res.status === 204) { Notification.Success({ msg: "Investigation Updated successfully!", @@ -159,29 +132,29 @@ export default function ShowInvestigation(props: any) { dispatch({ type: "set_changed_fields", changedFields: changedValues }); }, [state.initialValues]); + if (patientLoading || investigationLoading) { + return ; + } + return (
- {isLoading ? ( - - ) : ( - - )} +
); } diff --git a/src/Components/Facility/Investigations/ViewInvestigations.tsx b/src/Components/Facility/Investigations/ViewInvestigations.tsx index 7c420522c44..7d0bd8c3598 100644 --- a/src/Components/Facility/Investigations/ViewInvestigations.tsx +++ b/src/Components/Facility/Investigations/ViewInvestigations.tsx @@ -1,20 +1,23 @@ import { navigate } from "raviger"; import ReportTable from "./Reports/ReportTable"; +import { lazy } from "react"; +import { useTranslation } from "react-i18next"; import { formatDateTime } from "../../../Utils/utils"; +import { InvestigationResponse } from "./Reports/types"; import { InvestigationSessionType } from "./investigationsTab"; -import { lazy } from "react"; const Loading = lazy(() => import("../../Common/Loading")); export default function ViewInvestigations(props: { isLoading: boolean; - investigations: any; + investigations: InvestigationResponse; investigationSessions: InvestigationSessionType[]; facilityId: string; patientId: string; consultationId: string; }) { + const { t } = useTranslation(); const { isLoading, investigations, @@ -34,7 +37,7 @@ export default function ViewInvestigations(props: {
{investigations.length > 0 && (
-

Summary

+

{t("summary")}

- No Investigation Reports Found + {t("no_investigation")}
)} {investigationSessions.map((investigationSession) => { diff --git a/src/Components/Facility/Investigations/index.tsx b/src/Components/Facility/Investigations/index.tsx index 95147ec4c89..0f60057d6de 100644 --- a/src/Components/Facility/Investigations/index.tsx +++ b/src/Components/Facility/Investigations/index.tsx @@ -1,20 +1,16 @@ +import { navigate, useQueryParams } from "raviger"; import { lazy, useEffect, useReducer, useState } from "react"; -import { TestTable } from "./Table"; -import { useDispatch } from "react-redux"; -import { - createInvestigation, - listInvestigationGroups, - listInvestigations, - getPatient, -} from "../../../Redux/actions"; import * as Notification from "../../../Utils/Notifications.js"; -import { navigate, useQueryParams } from "raviger"; +import { TestTable } from "./Table"; import { useTranslation } from "react-i18next"; +import Card from "../../../CAREUI/display/Card"; +import routes from "../../../Redux/api"; +import request from "../../../Utils/request/request"; +import useQuery from "../../../Utils/request/useQuery"; +import { Submit } from "../../Common/components/ButtonV2"; import Page from "../../Common/components/Page"; import AutocompleteMultiSelectFormField from "../../Form/FormFields/AutocompleteMultiselect"; -import { Submit } from "../../Common/components/ButtonV2"; -import Card from "../../../CAREUI/display/Card"; const Loading = lazy(() => import("../../Common/Loading")); @@ -102,33 +98,45 @@ const Investigation = (props: { } ); - const dispatch: any = useDispatch(); const [selectedGroup, setSelectedGroup] = useState([]); const [state, setState] = useReducer(testFormReducer, initialState); - const [investigations, setInvestigations] = useState([]); - const [investigationGroups, setInvestigationGroups] = useState< - InvestigationGroup[] - >([]); const [selectedInvestigations, setSelectedInvestigations] = useState< InvestigationType[] >([]); - const [isLoading, setIsLoading] = useState({ - investigationLoading: false, - investigationGroupLoading: false, - }); const [saving, setSaving] = useState(false); const [session, setSession] = useState(""); const [selectedItems, selectItems] = useState([]); - const [facilityName, setFacilityName] = useState(""); - const [patientName, setPatientName] = useState(""); - const searchOptions = [...investigationGroups, ...investigations]; + + const { data: investigations, loading: listInvestigationDataLoading } = + useQuery(routes.listInvestigations, {}); + + const { + data: investigationGroups, + loading: listInvestigationGroupDataLoading, + } = useQuery(routes.listInvestigationGroups, {}); + + const { data: patientData, loading: patientLoading } = useQuery( + routes.getPatient, + { + pathParams: { id: patientId }, + onResponse: (res) => { + if (res.data) { + setSession(new Date().toString()); + } + }, + } + ); useEffect(() => { - if (investigations.length > 0) { + if ( + investigations?.results && + investigationGroups?.results && + investigations?.results.length > 0 + ) { const prefilledGroups = preselectedInvestigations .filter((inv: any) => inv.isGroup) .map((inv: any) => - investigationGroups.find((group) => group.name === inv.name) + investigationGroups.results.find((group) => group.name === inv.name) ) .map((group: any) => { return { @@ -140,7 +148,7 @@ const Investigation = (props: { const prefilledInvestigations = preselectedInvestigations .filter((inv: any) => !inv.isGroup) .map((inv: any) => { - const investigation = investigations.find( + const investigation = investigations.results.find( (investigation) => investigation.name === inv.name ); // check if investigation contains all groups @@ -170,51 +178,9 @@ const Investigation = (props: { } }, [investigations, investigationGroups]); - const fetchInvestigations = () => { - setIsLoading({ ...isLoading, investigationLoading: true }); - dispatch(listInvestigations({})).then((res: any) => { - if (res && res.data) { - setInvestigations(res.data.results); - } - setIsLoading({ ...isLoading, investigationLoading: false }); - }); - }; - - const fetchInvestigationGroups = () => { - setIsLoading({ ...isLoading, investigationGroupLoading: true }); - dispatch(listInvestigationGroups({})).then((res: any) => { - if (res && res.data) { - setInvestigationGroups(res.data.results); - } - setIsLoading({ ...isLoading, investigationGroupLoading: false }); - }); - }; - - useEffect(() => { - async function fetchPatientName() { - if (patientId) { - const res = await dispatch(getPatient({ id: patientId })); - if (res.data) { - setPatientName(res.data.name); - setFacilityName(res.data.facility_object.name); - } - } else { - setPatientName(""); - setFacilityName(""); - } - } - fetchPatientName(); - }, [dispatch, patientId]); - - useEffect(() => { - fetchInvestigationGroups(); - fetchInvestigations(); - setSession(new Date().toString()); - }, [props.consultationId]); - const initialiseForm = () => { const investigationsArray = selectedGroup.map((group_id: string) => { - return listOfInvestigations(group_id, investigations); + return listOfInvestigations(group_id, investigations?.results || []); }); const flatInvestigations = investigationsArray.flat(); @@ -240,7 +206,6 @@ const Investigation = (props: { initialiseForm(); if (!saving) { setSaving(true); - const keys = Object.keys(state.form); const data = keys .map((k) => { @@ -256,9 +221,12 @@ const Investigation = (props: { ); if (data.length) { - const res = await dispatch( - createInvestigation({ investigations: data }, props.consultationId) - ); + const { res } = await request(routes.createInvestigation, { + pathParams: { consultation_external_id: props.consultationId }, + body: { + investigations: data, + }, + }); if (res && res.status === 204) { setSaving(false); Notification.Success({ @@ -279,7 +247,11 @@ const Investigation = (props: { } }; - if (isLoading.investigationGroupLoading || isLoading.investigationLoading) { + if ( + listInvestigationDataLoading || + listInvestigationGroupDataLoading || + patientLoading + ) { return ; } @@ -287,16 +259,19 @@ const Investigation = (props: {
option.name} optionValue={(option) => option} @@ -328,8 +303,8 @@ const Investigation = (props: { ); const filteredInvestigations = currentGroupsInvestigations.length ? currentGroupsInvestigations - : listOfInvestigations(group_id, investigations); - const group = findGroup(group_id, investigationGroups); + : listOfInvestigations(group_id, investigations?.results || []); + const group = findGroup(group_id, investigationGroups?.results || []); return (
diff --git a/src/Components/Facility/Investigations/investigationsTab.tsx b/src/Components/Facility/Investigations/investigationsTab.tsx index 9821db0e98f..6b10e1d8c44 100644 --- a/src/Components/Facility/Investigations/investigationsTab.tsx +++ b/src/Components/Facility/Investigations/investigationsTab.tsx @@ -1,13 +1,7 @@ -import { useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; -import { statusType, useAbortableEffect } from "../../../Common/utils"; -import { - getInvestigation, - getInvestigationSessions, -} from "../../../Redux/actions"; +import routes from "../../../Redux/api"; +import useQuery from "../../../Utils/request/useQuery"; import { PatientModel } from "../../Patient/models"; import ViewInvestigationSuggestions from "./InvestigationSuggestions"; -import { InvestigationResponse } from "./Reports/types"; import ViewInvestigations from "./ViewInvestigations"; export interface InvestigationSessionType { @@ -22,72 +16,34 @@ export default function InvestigationTab(props: { patientData: PatientModel; }) { const { consultationId, patientId, facilityId, patientData } = props; - - const [isLoading, setIsLoading] = useState(false); - const dispatchAction: any = useDispatch(); - const [investigations, setInvestigations] = useState( - [] - ); - const [investigationSessions, setInvestigationSessions] = useState< - InvestigationSessionType[] - >([]); - - const fetchInvestigations = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction(getInvestigation({}, consultationId)); - if (!status.aborted) { - if (res && res.data) { - setInvestigations(res.data.results); - } - setIsLoading(false); - } - }, - [dispatchAction, consultationId] - ); - - useAbortableEffect( - (status: statusType) => { - fetchInvestigations(status); - }, - [fetchInvestigations] + const { data: investigations, loading: investigationLoading } = useQuery( + routes.getInvestigation, + { + pathParams: { + consultation_external_id: consultationId, + }, + } ); - const fetchInvestigationSessions = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction( - getInvestigationSessions({}, consultationId) - ); - if (!status.aborted) { - if (res && res.data) { - setInvestigationSessions(res.data.reverse()); - } - setIsLoading(false); - } - }, - [dispatchAction, consultationId] - ); - - useAbortableEffect( - (status: statusType) => { - fetchInvestigationSessions(status); - }, - [fetchInvestigationSessions] - ); + const { data: investigationSessions, loading: investigationSessionLoading } = + useQuery(routes.getInvestigationSessions, { + pathParams: { + consultation_external_id: consultationId, + }, + }); return ( <> >(), + TBody: Type<{ + investigations: { + investigation: string; + value: number; + notes: string; + session: string; + }[]; + }>(), }, getInvestigationSessions: { path: "/api/v1/consultation/{consultation_external_id}/investigation/get_sessions/", method: "GET", + TRes: Type(), }, getInvestigation: { path: "/api/v1/consultation/{consultation_external_id}/investigation/", @@ -1075,10 +1084,19 @@ const routes = { getPatientInvestigation: { path: "/api/v1/patient/{patient_external_id}/investigation/", method: "GET", + TRes: Type>(), }, editInvestigation: { path: "/api/v1/consultation/{consultation_external_id}/investigation/batchUpdate/", method: "PUT", + TRes: Type>(), + TBody: Type<{ + investigations: { + external_id: string; + value: number; + notes: string; + }[]; + }>(), }, // ICD11 From 51a0eff31b40f1ec239991e7650305478acf1fd3 Mon Sep 17 00:00:00 2001 From: Ashutosh Rai <85889617+Ashutosh0602@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:05:50 +0530 Subject: [PATCH 11/13] fix: Irregular flow of responsive design in patient details (#7266) * fixes responsive design in patient details * fixes responsive design in patient details --- src/Components/Common/RelativeDateUserMention.tsx | 2 +- src/Components/Facility/ConsultationCard.tsx | 12 +++++++----- src/Components/Patient/SampleTestCard.tsx | 11 ++++++----- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Components/Common/RelativeDateUserMention.tsx b/src/Components/Common/RelativeDateUserMention.tsx index 6c5a78c7f09..2af92268987 100644 --- a/src/Components/Common/RelativeDateUserMention.tsx +++ b/src/Components/Common/RelativeDateUserMention.tsx @@ -9,7 +9,7 @@ function RelativeDateUserMention(props: { withoutSuffix?: boolean; }) { return ( -
+
{
{
- Created :{" "} -
+
Created :
+
{
}
- Last Modified :{" "} -
+
Last Modified :
+
{ `/facility/${itemData.facility}/patient/${itemData.patient}/consultation/${itemData.id}/daily-rounds` ) } - disabled={itemData.discharge_date} + disabled={ + (itemData.discharge_date as string | undefined) != undefined + } authorizeFor={NonReadOnlyUsers} > Add Consultation Updates diff --git a/src/Components/Patient/SampleTestCard.tsx b/src/Components/Patient/SampleTestCard.tsx index 77c923c4163..ffb8b28926d 100644 --- a/src/Components/Patient/SampleTestCard.tsx +++ b/src/Components/Patient/SampleTestCard.tsx @@ -138,7 +138,7 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
-
+
@@ -156,16 +156,17 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
-
- Created:{" "} +
+
Created:
+
-
- Last Modified:{" "} +
+
Last Modified:
Date: Wed, 28 Feb 2024 13:06:23 +0530 Subject: [PATCH 12/13] fix: overflow of fields in the user management cards (#7261) * fix: overflow of fields in the user management cards * refactored code * UPDATE: inline styles to tailwind --- src/Components/Common/UserDetailsComponet.tsx | 23 +++++++++++++ src/Components/Users/ManageUsers.tsx | 34 ++++++++++--------- 2 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 src/Components/Common/UserDetailsComponet.tsx diff --git a/src/Components/Common/UserDetailsComponet.tsx b/src/Components/Common/UserDetailsComponet.tsx new file mode 100644 index 00000000000..44aff851917 --- /dev/null +++ b/src/Components/Common/UserDetailsComponet.tsx @@ -0,0 +1,23 @@ +import UserDetails from "./UserDetails"; + +const UserDetailComponent = ({ + id, + title, + value, +}: { + id: string; + title: string; + value: string; +}) => ( +
+ +
+
+ {value} +
+
+
+
+); + +export default UserDetailComponent; diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 1a39b57ec53..391e38386f4 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -14,6 +14,7 @@ import { USER_TYPES } from "../../Common/constants"; import UnlinkFacilityDialog from "./UnlinkFacilityDialog"; import UserDeleteDialog from "./UserDeleteDialog"; import UserDetails from "../Common/UserDetails"; +import UserDetailComponent from "../Common/UserDetailsComponet.js"; import UserFilter from "./UserFilter"; import { classNames, @@ -253,22 +254,18 @@ export default function ManageUsers() { } gap-2 md:grid md:grid-cols-2`} > {user.user_type && ( -
- -
- {user.user_type} -
-
-
+ )} {user.district_object && ( -
- -
- {user.district_object.name} -
-
-
+ )} {user.user_type === "Doctor" && ( <> @@ -336,8 +333,13 @@ export default function ManageUsers() { {user.created_by && (
-
- {user.created_by} +
+
+ {user.created_by} +
From 5f8fb3aab739ad43b87096266399a6bef40d7373 Mon Sep 17 00:00:00 2001 From: Uday Sagar <111575806+UdaySagar-Git@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:06:52 +0530 Subject: [PATCH 13/13] Improves Input Field UI for phone number input (#7250) * ui changes * minor fix * decresed width of select --- src/Common/static/countryPhoneAndFlags.json | 12 +- .../Form/FormFields/PhoneNumberFormField.tsx | 176 ++++++++++++++---- 2 files changed, 137 insertions(+), 51 deletions(-) diff --git a/src/Common/static/countryPhoneAndFlags.json b/src/Common/static/countryPhoneAndFlags.json index 87d963444b5..98a43e4e043 100644 --- a/src/Common/static/countryPhoneAndFlags.json +++ b/src/Common/static/countryPhoneAndFlags.json @@ -57,11 +57,7 @@ "DJ": { "flag": "🇩🇯", "name": "Djibouti", "code": "253" }, "DK": { "flag": "🇩🇰", "name": "Denmark", "code": "45" }, "DM": { "flag": "🇩🇲", "name": "Dominica", "code": "1-767" }, - "DO": { - "flag": "🇩🇴", - "name": "Dominican Republic", - "code": "1" - }, + "DO": { "flag": "🇩🇴", "name": "Dominican Republic", "code": "1" }, "DZ": { "flag": "🇩🇿", "name": "Algeria", "code": "213" }, "EC": { "flag": "🇪🇨", "name": "Ecuador", "code": "593" }, "EE": { "flag": "🇪🇪", "name": "Estonia", "code": "372" }, @@ -105,11 +101,7 @@ "IL": { "flag": "🇮🇱", "name": "Israel", "code": "972" }, "IM": { "flag": "🇮🇲", "name": "Isle of Man", "code": "44-1624" }, "IN": { "flag": "🇮🇳", "name": "India", "code": "91" }, - "IO": { - "flag": "🇮🇴", - "name": "British Indian Ocean Territory", - "code": "246" - }, + "IO": { "flag": "🇮🇴", "name": "British Indian Ocean Territory", "code": "246" }, "IQ": { "flag": "🇮🇶", "name": "Iraq", "code": "964" }, "IR": { "flag": "🇮🇷", "name": "Iran", "code": "98" }, "IS": { "flag": "🇮🇸", "name": "Iceland", "code": "354" }, diff --git a/src/Components/Form/FormFields/PhoneNumberFormField.tsx b/src/Components/Form/FormFields/PhoneNumberFormField.tsx index b6af2bfaa24..93ff4cf21eb 100644 --- a/src/Components/Form/FormFields/PhoneNumberFormField.tsx +++ b/src/Components/Form/FormFields/PhoneNumberFormField.tsx @@ -1,6 +1,6 @@ import { FormFieldBaseProps, useFormFieldPropsResolver } from "./Utils"; import FormField from "./FormField"; -import { useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { classNames, parsePhoneNumber, @@ -27,7 +27,13 @@ interface Props extends FormFieldBaseProps { export default function PhoneNumberFormField(props: Props) { const field = useFormFieldPropsResolver(props as any); - const [error, setError] = useState(); + const [error, setError] = useState(); + const [country, setCountry] = useState({ + flag: "🇮🇳", + name: "India", + code: "91", + }); + const [isOpen, setIsOpen] = useState(false); const validator = useMemo( () => PhoneNumberValidator(props.types), @@ -51,19 +57,44 @@ export default function PhoneNumberFormField(props: Props) { [props.disableValidation] ); - const setValue = (value: string) => { - value = value.replaceAll(/[^0-9+]/g, ""); - if (value.length > 12 && value.startsWith("+910")) { - value = "+91" + value.slice(4); - } + const setValue = useCallback( + (value: string) => { + value = value.replaceAll(/[^0-9+]/g, ""); + if (value.length > 12 && value.startsWith("+910")) { + value = "+91" + value.slice(4); + } - const error = validate(value, "change"); - field.handleChange(value); + const error = validate(value, "change"); + field.handleChange(value); - setError(error); + setError(error); + }, + [field, validate] + ); + + const handleCountryChange = (value: CountryData): void => { + setCountry(value); + setValue(conditionPhoneCode(value.code)); + setIsOpen(false); }; - useEffect(() => setValue(field.value || "+91"), []); + useEffect(() => { + if (field.value && field.value.length > 0) { + if (field.value.startsWith("1800")) { + setCountry({ flag: "📞", name: "Support", code: "1800" }); + return; + } + if (field.value === "+") { + setCountry({ flag: "🌍", name: "Other", code: "+" }); + return; + } + setCountry(phoneCodes[getCountryCode(field.value)!]); + } + }, [setValue]); + + useEffect(() => { + setValue(field.value || "+91"); + }, []); return (
+
setIsOpen(!isOpen)} + > + + {country?.flag ?? "🇮🇳"} + + {isOpen ? ( + + ) : ( + + )} +
+ setError(validate(field.value, "blur"))} /> -
- - -
+ {isOpen && ( + + )}
); @@ -170,3 +189,78 @@ const formatPhoneNumber = (value: string, types: PhoneNumberType[]) => { const phoneNumber = parsePhoneNumber(value); return phoneNumber ? formatPhoneNumberUtil(phoneNumber) : value; }; + +const CountryCodesList = ({ handleCountryChange }: any) => { + const [searchValue, setSearchValue] = useState(""); + + return ( +
+
+ + setSearchValue(e.target.value)} + /> +
+ +
    + {Object.entries(phoneCodes) + .filter(([country, { flag, name, code }]) => { + if (searchValue === "") { + return true; + } + return ( + name.toLowerCase().includes(searchValue.toLowerCase()) || + code.includes(searchValue) || + country.toLowerCase().includes(searchValue.toLowerCase()) || + flag.includes(searchValue) + ); + }) + .map(([country, { flag, name, code }]) => ( +
  • { + handleCountryChange({ flag, name, code }); + }} + > + {flag} + {name} + + {" "} + ({conditionPhoneCode(code)}) + +
  • + ))} +
  • { + handleCountryChange({ flag: "📞", name: "Support", code: "1800" }); + }} + > + 📞 + Support + (1800) +
  • +
  • { + handleCountryChange({ flag: "🌍", name: "Other", code: "+" }); + }} + > + 🌍 + Other + (+) +
  • +
+
+ ); +};