diff --git a/cypress/e2e/assets_spec/AssetHomepage.cy.ts b/cypress/e2e/assets_spec/AssetHomepage.cy.ts index bda4abdfae3..906cd9b2edc 100644 --- a/cypress/e2e/assets_spec/AssetHomepage.cy.ts +++ b/cypress/e2e/assets_spec/AssetHomepage.cy.ts @@ -1,10 +1,11 @@ -import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; -import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan"; -import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; -import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; +import { v4 as uuidv4 } from "uuid"; + import { AssetPage } from "../../pageobject/Asset/AssetCreation"; +import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; +import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; +import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan"; +import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import LoginPage from "../../pageobject/Login/LoginPage"; -import { v4 as uuidv4 } from "uuid"; describe("Asset Tab", () => { const assetSearchPage = new AssetSearchPage(); diff --git a/cypress/e2e/assets_spec/AssetsCreation.cy.ts b/cypress/e2e/assets_spec/AssetsCreation.cy.ts index 16a4fd050fb..61c6fe9b517 100644 --- a/cypress/e2e/assets_spec/AssetsCreation.cy.ts +++ b/cypress/e2e/assets_spec/AssetsCreation.cy.ts @@ -1,7 +1,8 @@ -import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import { v4 as uuidv4 } from "uuid"; -import LoginPage from "../../pageobject/Login/LoginPage"; + +import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; +import LoginPage from "../../pageobject/Login/LoginPage"; describe("Asset", () => { const assetPage = new AssetPage(); diff --git a/cypress/e2e/assets_spec/AssetsManage.cy.ts b/cypress/e2e/assets_spec/AssetsManage.cy.ts index 756d3b261a6..17ee99bdb62 100644 --- a/cypress/e2e/assets_spec/AssetsManage.cy.ts +++ b/cypress/e2e/assets_spec/AssetsManage.cy.ts @@ -1,8 +1,8 @@ import { AssetPage } from "../../pageobject/Asset/AssetCreation"; -import LoginPage from "../../pageobject/Login/LoginPage"; +import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; +import LoginPage from "../../pageobject/Login/LoginPage"; function addDaysToDate(numberOfDays: number) { const inputDate = new Date(); diff --git a/cypress/e2e/facility_spec/FacilityCreation.cy.ts b/cypress/e2e/facility_spec/FacilityCreation.cy.ts index 4961ae4a4cb..fff1311fdd6 100644 --- a/cypress/e2e/facility_spec/FacilityCreation.cy.ts +++ b/cypress/e2e/facility_spec/FacilityCreation.cy.ts @@ -1,6 +1,6 @@ import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import LoginPage from "../../pageobject/Login/LoginPage"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; +import LoginPage from "../../pageobject/Login/LoginPage"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; import { UserCreationPage } from "../../pageobject/Users/UserCreation"; diff --git a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts index e6caf645f7a..bc84aea4882 100644 --- a/cypress/e2e/facility_spec/FacilityHomepage.cy.ts +++ b/cypress/e2e/facility_spec/FacilityHomepage.cy.ts @@ -1,11 +1,10 @@ // FacilityCreation - -import LoginPage from "../../pageobject/Login/LoginPage"; +import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; +import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; +import LoginPage from "../../pageobject/Login/LoginPage"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; -import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import { UserPage } from "../../pageobject/Users/UserSearch"; -import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; describe("Facility Homepage Function", () => { const loginPage = new LoginPage(); diff --git a/cypress/e2e/facility_spec/FacilityInventory.cy.ts b/cypress/e2e/facility_spec/FacilityInventory.cy.ts index cdada75ee06..a9f32984efc 100644 --- a/cypress/e2e/facility_spec/FacilityInventory.cy.ts +++ b/cypress/e2e/facility_spec/FacilityInventory.cy.ts @@ -1,6 +1,6 @@ import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import LoginPage from "../../pageobject/Login/LoginPage"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; +import LoginPage from "../../pageobject/Login/LoginPage"; describe("Inventory Management Section", () => { const facilityPage = new FacilityPage(); diff --git a/cypress/e2e/facility_spec/FacilityManage.cy.ts b/cypress/e2e/facility_spec/FacilityManage.cy.ts index 1f0d2b66108..02c14958ccf 100644 --- a/cypress/e2e/facility_spec/FacilityManage.cy.ts +++ b/cypress/e2e/facility_spec/FacilityManage.cy.ts @@ -1,7 +1,6 @@ -import LoginPage from "../../pageobject/Login/LoginPage"; -import FacilityManage from "../../pageobject/Facility/FacilityManage"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import { v4 as uuidv4 } from "uuid"; +import FacilityManage from "../../pageobject/Facility/FacilityManage"; +import LoginPage from "../../pageobject/Login/LoginPage"; describe("Facility Manage Functions", () => { const loginPage = new LoginPage(); @@ -13,11 +12,10 @@ describe("Facility Manage Functions", () => { const facilityUpdatedMiddleware = "updated.coronasafe.live"; const facilityMiddlewareSuccessfullNotification = "Facility middleware updated successfully"; - const facilityHfridUpdateButton = "Link Health Facility"; - const facilityHfridToastNotificationText = - /Health Facility config updated successfully|Health ID registration failed/; - const facilityHfrId = "IN180000018"; - const facilityUpdatedHfrId = uuidv4(); + // const facilityHfridUpdateButton = "Link Health Facility"; + // const facilityHfridToastNotificationText = /Health Facility config updated successfully|Health ID registration failed/; + // const facilityHfrId = "IN180000018"; + // const facilityUpdatedHfrId = uuidv4(); const doctorCapacity = "5"; const doctorModifiedCapacity = "7"; const totalCapacity = "100"; @@ -78,36 +76,37 @@ describe("Facility Manage Functions", () => { facilityManage.verifyMiddlewareAddressValue(facilityUpdatedMiddleware); }); - it("Configure Facility Health ID", () => { - facilityPage.clickManageFacilityDropdown(); - facilityManage.clickFacilityConfigureButton(); - // verify mandatory field error message - facilityManage.clearHfrId(); - facilityManage.clickButtonWithText(facilityHfridUpdateButton); - facilityManage.checkErrorMessageVisibility( - "Health Facility Id is required", - ); - // add facility health ID and verify notification - facilityManage.typeHfrId(facilityHfrId); - facilityManage.clickButtonWithText(facilityHfridUpdateButton); - facilityManage.verifySuccessMessageVisibilityAndContent( - facilityHfridToastNotificationText, - true, - ); - // update the existing middleware - facilityPage.clickManageFacilityDropdown(); - facilityManage.clickFacilityConfigureButton(); - facilityManage.typeHfrId(facilityUpdatedHfrId); - facilityManage.clickButtonWithText(facilityHfridUpdateButton); - facilityManage.verifySuccessMessageVisibilityAndContent( - facilityHfridToastNotificationText, - true, - ); - // verify its reflection - facilityPage.clickManageFacilityDropdown(); - facilityManage.clickFacilityConfigureButton(); - facilityManage.verifyHfrIdValue(facilityUpdatedHfrId); - }); + // TODO: enable this test after configuring testing specs for plugs + // it("Configure Facility Health ID", () => { + // facilityPage.clickManageFacilityDropdown(); + // facilityManage.clickFacilityConfigureButton(); + // // verify mandatory field error message + // facilityManage.clearHfrId(); + // facilityManage.clickButtonWithText(facilityHfridUpdateButton); + // facilityManage.checkErrorMessageVisibility( + // "Health Facility Id is required", + // ); + // // add facility health ID and verify notification + // facilityManage.typeHfrId(facilityHfrId); + // facilityManage.clickButtonWithText(facilityHfridUpdateButton); + // facilityManage.verifySuccessMessageVisibilityAndContent( + // facilityHfridToastNotificationText, + // true, + // ); + // // update the existing middleware + // facilityPage.clickManageFacilityDropdown(); + // facilityManage.clickFacilityConfigureButton(); + // facilityManage.typeHfrId(facilityUpdatedHfrId); + // facilityManage.clickButtonWithText(facilityHfridUpdateButton); + // facilityManage.verifySuccessMessageVisibilityAndContent( + // facilityHfridToastNotificationText, + // true, + // ); + // // verify its reflection + // facilityPage.clickManageFacilityDropdown(); + // facilityManage.clickFacilityConfigureButton(); + // facilityManage.verifyHfrIdValue(facilityUpdatedHfrId); + // }); it("Modify doctor capacity in Facility detail page", () => { // Add a doctor capacity diff --git a/cypress/e2e/patient_spec/PatientBedManagement.cy.ts b/cypress/e2e/patient_spec/PatientBedManagement.cy.ts index 84ef2f4800e..65adf131c87 100644 --- a/cypress/e2e/patient_spec/PatientBedManagement.cy.ts +++ b/cypress/e2e/patient_spec/PatientBedManagement.cy.ts @@ -1,7 +1,7 @@ import LoginPage from "../../pageobject/Login/LoginPage"; +import { PatientConsultationPage } from "../../pageobject/Patient/PatientConsultation"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; import PatientLogupdate from "../../pageobject/Patient/PatientLogupdate"; -import { PatientConsultationPage } from "../../pageobject/Patient/PatientConsultation"; describe("Patient swtich bed functionality", () => { const loginPage = new LoginPage(); diff --git a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts index ee38b24d746..4c84f7fad8f 100644 --- a/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts +++ b/cypress/e2e/patient_spec/PatientConsultationCreation.cy.ts @@ -1,12 +1,12 @@ import LoginPage from "../../pageobject/Login/LoginPage"; -import { PatientPage } from "../../pageobject/Patient/PatientCreation"; import { PatientConsultationPage } from "../../pageobject/Patient/PatientConsultation"; -import PatientPredefined from "../../pageobject/Patient/PatientPredefined"; -import ShiftCreation from "../../pageobject/Shift/ShiftCreation"; -import PatientInvestigation from "../../pageobject/Patient/PatientInvestigation"; -import PatientTreatmentPlan from "../../pageobject/Patient/PatientTreatmentPlan"; +import { PatientPage } from "../../pageobject/Patient/PatientCreation"; import PatientDeathReport from "../../pageobject/Patient/PatientDeathReport"; +import PatientInvestigation from "../../pageobject/Patient/PatientInvestigation"; +import PatientPredefined from "../../pageobject/Patient/PatientPredefined"; import PatientPrescription from "../../pageobject/Patient/PatientPrescription"; +import PatientTreatmentPlan from "../../pageobject/Patient/PatientTreatmentPlan"; +import ShiftCreation from "../../pageobject/Shift/ShiftCreation"; describe("Patient Consultation in multiple combination", () => { const patientConsultationPage = new PatientConsultationPage(); diff --git a/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts b/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts index 9197cac2d90..dd626f619ce 100644 --- a/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts +++ b/cypress/e2e/patient_spec/PatientDoctorConnect.cy.ts @@ -1,4 +1,5 @@ import { DoctorConnect } from "pageobject/Patient/PatientDoctorConnect"; + import LoginPage from "../../pageobject/Login/LoginPage"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; diff --git a/cypress/e2e/patient_spec/PatientFileUpload.ts b/cypress/e2e/patient_spec/PatientFileUpload.ts index cc94943fd6f..110631551a1 100644 --- a/cypress/e2e/patient_spec/PatientFileUpload.ts +++ b/cypress/e2e/patient_spec/PatientFileUpload.ts @@ -1,6 +1,7 @@ import LoginPage from "../../pageobject/Login/LoginPage"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; import { PatientFileUpload } from "../../pageobject/Patient/PatientFileupload"; + const loginPage = new LoginPage(); const patientPage = new PatientPage(); const patientFileUpload = new PatientFileUpload(); diff --git a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts index b32990872e2..b6d7ecbc173 100644 --- a/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts +++ b/cypress/e2e/patient_spec/PatientLogUpdate.cy.ts @@ -1,8 +1,8 @@ import LoginPage from "../../pageobject/Login/LoginPage"; import { PatientConsultationPage } from "../../pageobject/Patient/PatientConsultation"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; -import PatientLogupdate from "../../pageobject/Patient/PatientLogupdate"; import PatientInvestigation from "../../pageobject/Patient/PatientInvestigation"; +import PatientLogupdate from "../../pageobject/Patient/PatientLogupdate"; import PatientPrescription from "../../pageobject/Patient/PatientPrescription"; describe("Patient Log Update in Normal, Critical and TeleIcu", () => { diff --git a/cypress/e2e/patient_spec/PatientPrescription.cy.ts b/cypress/e2e/patient_spec/PatientPrescription.cy.ts index f5febc85fdc..53e67324199 100644 --- a/cypress/e2e/patient_spec/PatientPrescription.cy.ts +++ b/cypress/e2e/patient_spec/PatientPrescription.cy.ts @@ -1,6 +1,6 @@ -import PatientPrescription from "../../pageobject/Patient/PatientPrescription"; import LoginPage from "../../pageobject/Login/LoginPage"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; +import PatientPrescription from "../../pageobject/Patient/PatientPrescription"; const patientPrescription = new PatientPrescription(); const loginPage = new LoginPage(); diff --git a/cypress/e2e/patient_spec/PatientRegistration.cy.ts b/cypress/e2e/patient_spec/PatientRegistration.cy.ts index d774a90ceb8..cb84fa06674 100644 --- a/cypress/e2e/patient_spec/PatientRegistration.cy.ts +++ b/cypress/e2e/patient_spec/PatientRegistration.cy.ts @@ -1,10 +1,10 @@ +import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import LoginPage from "../../pageobject/Login/LoginPage"; import { PatientPage } from "../../pageobject/Patient/PatientCreation"; -import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import { generatePhoneNumber } from "../../pageobject/utils/constants"; -import PatientTransfer from "../../pageobject/Patient/PatientTransfer"; import PatientInsurance from "../../pageobject/Patient/PatientInsurance"; import PatientMedicalHistory from "../../pageobject/Patient/PatientMedicalHistory"; +import PatientTransfer from "../../pageobject/Patient/PatientTransfer"; +import { generatePhoneNumber } from "../../pageobject/utils/constants"; const yearOfBirth = "2001"; const isHCXEnabled = Cypress.env("ENABLE_HCX"); diff --git a/cypress/e2e/resource_spec/ResourcesHomepage.cy.ts b/cypress/e2e/resource_spec/ResourcesHomepage.cy.ts index 8dc526a68c3..299d753d720 100644 --- a/cypress/e2e/resource_spec/ResourcesHomepage.cy.ts +++ b/cypress/e2e/resource_spec/ResourcesHomepage.cy.ts @@ -1,6 +1,6 @@ +import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import LoginPage from "../../pageobject/Login/LoginPage"; import ResourcePage from "../../pageobject/Resource/ResourcePage"; -import FacilityPage from "../../pageobject/Facility/FacilityCreation"; describe("Resource Page", () => { let createdResource: string; diff --git a/cypress/e2e/users_spec/UsersCreation.cy.ts b/cypress/e2e/users_spec/UsersCreation.cy.ts index 683a2131b2f..f495a136d97 100644 --- a/cypress/e2e/users_spec/UsersCreation.cy.ts +++ b/cypress/e2e/users_spec/UsersCreation.cy.ts @@ -1,11 +1,11 @@ -import LoginPage from "../../pageobject/Login/LoginPage"; import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; -import { UserPage } from "../../pageobject/Users/UserSearch"; +import LoginPage from "../../pageobject/Login/LoginPage"; import { UserCreationPage } from "../../pageobject/Users/UserCreation"; +import { UserPage } from "../../pageobject/Users/UserSearch"; import { - generatePhoneNumber, generateEmergencyPhoneNumber, + generatePhoneNumber, } from "../../pageobject/utils/constants"; describe("User Creation", () => { diff --git a/cypress/e2e/users_spec/UsersManage.cy.ts b/cypress/e2e/users_spec/UsersManage.cy.ts index 98c2d564f6d..9ed4fe34ec7 100644 --- a/cypress/e2e/users_spec/UsersManage.cy.ts +++ b/cypress/e2e/users_spec/UsersManage.cy.ts @@ -1,7 +1,7 @@ import LoginPage from "../../pageobject/Login/LoginPage"; -import { UserPage } from "../../pageobject/Users/UserSearch"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; import { UserCreationPage } from "../../pageobject/Users/UserCreation"; +import { UserPage } from "../../pageobject/Users/UserSearch"; describe("Manage User", () => { const loginPage = new LoginPage(); diff --git a/src/App.tsx b/src/App.tsx index 62a173c8409..d3fec38dbed 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,23 +11,27 @@ import HistoryAPIProvider from "@/Providers/HistoryAPIProvider"; import Routers from "@/Routers"; import { FeatureFlagsProvider } from "@/Utils/featureFlags"; +import { PubSubProvider } from "./Utils/pubsubContext"; + const App = () => { return ( }> - - - }> - - - - + + + + }> + + + + - {/* Integrations */} - - - - - + {/* Integrations */} + + + + + + ); }; diff --git a/src/Routers/AppRouter.tsx b/src/Routers/AppRouter.tsx index c8f6fbec83e..686094c9be1 100644 --- a/src/Routers/AppRouter.tsx +++ b/src/Routers/AppRouter.tsx @@ -4,8 +4,6 @@ import { useEffect, useState } from "react"; import IconIndex from "@/CAREUI/icons/Index"; -import ABDMFacilityRecords from "@/components/ABDM/ABDMFacilityRecords"; -import HealthInformation from "@/components/ABDM/HealthInformation"; import { DesktopSidebar, MobileSidebar, @@ -60,13 +58,6 @@ const Routes: AppRoutes = { "/notifications/:id": ({ id }) => , "/notice_board": () => , - "/abdm/health-information/:id": ({ id }) => ( - - ), - "/facility/:facilityId/abdm": ({ facilityId }) => ( - - ), - "/session-expired": () => , "/not-found": () => , "/icons": () => , diff --git a/src/Utils/pubsubContext.tsx b/src/Utils/pubsubContext.tsx new file mode 100644 index 00000000000..f1e76850913 --- /dev/null +++ b/src/Utils/pubsubContext.tsx @@ -0,0 +1,81 @@ +import { createContext, useContext, useState } from "react"; + +export type Handler = (message: unknown) => Promise; +export type PubSubContext = { + subscribe: (topic: string, handler: Handler) => void; + unsubscribe: (topic: string, handler: Handler) => void; + publish: (topic: string, message: unknown) => void; + subscribers: Record>; + setSubscribers: React.Dispatch< + React.SetStateAction>> + >; +}; + +const PubSubContext = createContext(null); + +export const PubSubProvider = ({ children }: { children: React.ReactNode }) => { + const [subscribers, setSubscribers] = useState>>( + {}, + ); + + const subscribe = (topic: string, handler: Handler) => { + setSubscribers((prev) => ({ + ...prev, + [topic]: new Set([...(prev[topic] || []), handler]), + })); + }; + + const unsubscribe = (topic: string, handler: Handler) => { + setSubscribers((prev) => { + const handlers = prev[topic]; + if (!handlers) { + return prev; + } + + const newHandlers = new Set(handlers); + newHandlers.delete(handler); + + if (newHandlers.size === 0) { + const { [topic]: _, ...rest } = prev; + return rest; + } + + return { + ...prev, + [topic]: newHandlers, + }; + }); + }; + + const publish = (topic: string, message: unknown) => { + if (!subscribers[topic]) { + return; + } + + subscribers[topic].forEach(async (handler) => { + try { + await handler(message); + } catch (error) { + console.error(`Handler failed for topic ${topic}:`, error); + } + }); + }; + + return ( + + {children} + + ); +}; + +export const usePubSub = () => { + const context = useContext(PubSubContext); + + if (!context) { + throw new Error("usePubSub must be used within PubSubProvider"); + } + + return context; +}; diff --git a/src/Utils/request/api.tsx b/src/Utils/request/api.tsx index 441b9a3d8c8..097a5212ed9 100644 --- a/src/Utils/request/api.tsx +++ b/src/Utils/request/api.tsx @@ -1,14 +1,3 @@ -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import { - ConsentRequestModel, - CreateConsentTBody, -} from "@/components/ABDM/types/consent"; -import { - IHealthFacility, - IcreateHealthFacilityTBody, - IpartialUpdateHealthFacilityTBody, -} from "@/components/ABDM/types/health-facility"; -import { HealthInformationModel } from "@/components/ABDM/types/health-information"; import { AssetBedBody, AssetBedModel, @@ -1359,246 +1348,6 @@ const routes = { TBody: Type(), }, - abdm: { - consent: { - list: { - path: "/api/abdm/consent/", - method: "GET", - TRes: Type>(), - }, - - create: { - path: "/api/abdm/consent/", - method: "POST", - TRes: Type(), - TBody: Type(), - }, - - get: { - path: "/api/abdm/consent/{id}/", - method: "GET", - }, - - checkStatus: { - path: "/api/abdm/v3/hiu/consent_request_status/", - method: "POST", - TBody: Type<{ - consent_request: string; - }>(), - TRes: Type<{ - detail: string; - }>(), - }, - }, - - healthInformation: { - get: { - path: "/api/abdm/health_information/{artefactId}", - method: "GET", - TRes: Type(), - }, - }, - - healthFacility: { - list: { - path: "/api/abdm/health_facility/", - method: "GET", - }, - - create: { - path: "/api/abdm/health_facility/", - method: "POST", - TRes: Type(), - TBody: Type(), - }, - - get: { - path: "/api/abdm/health_facility/{facility_id}/", - method: "GET", - TRes: Type(), - }, - - update: { - path: "/api/abdm/health_facility/{facility_id}/", - method: "PUT", - TRes: Type(), - TBody: Type(), - }, - - partialUpdate: { - path: "/api/abdm/health_facility/{facility_id}/", - method: "PATCH", - TRes: Type(), - TBody: Type(), - }, - - registerAsService: { - path: "/api/abdm/health_facility/{facility_id}/register_service/", - method: "POST", - TRes: Type(), - TBody: Type(), - }, - }, - - abhaNumber: { - get: { - path: "/api/abdm/abha_number/{abhaNumberId}/", - method: "GET", - TRes: Type(), - }, - create: { - path: "/api/abdm/abha_number/", - method: "POST", - TBody: Type>(), - TRes: Type(), - }, - }, - - healthId: { - abhaCreateSendAadhaarOtp: { - path: "/api/abdm/v3/health_id/create/send_aadhaar_otp/", - method: "POST", - TBody: Type<{ - aadhaar: string; - transaction_id?: string; - }>(), - TRes: Type<{ - transaction_id: string; - detail: string; - }>(), - }, - - abhaCreateVerifyAadhaarOtp: { - path: "/api/abdm/v3/health_id/create/verify_aadhaar_otp/", - method: "POST", - TBody: Type<{ - transaction_id: string; - otp: string; - mobile: string; - }>(), - TRes: Type<{ - transaction_id: string; - detail: string; - is_new: boolean; - abha_number: AbhaNumberModel; - }>(), - }, - - abhaCreateLinkMobileNumber: { - path: "/api/abdm/v3/health_id/create/link_mobile_number/", - method: "POST", - TBody: Type<{ - transaction_id: string; - mobile: string; - }>(), - TRes: Type<{ - transaction_id: string; - detail: string; - }>(), - }, - - abhaCreateVerifyMobileNumber: { - path: "/api/abdm/v3/health_id/create/verify_mobile_otp/", - method: "POST", - TBody: Type<{ - transaction_id: string; - otp: string; - }>(), - TRes: Type<{ - transaction_id: string; - detail: string; - }>(), - }, - - abhaCreateAbhaAddressSuggestion: { - path: "/api/abdm/v3/health_id/create/abha_address_suggestion/", - method: "POST", - TBody: Type<{ - transaction_id: string; - }>(), - TRes: Type<{ - transaction_id: string; - abha_addresses: string[]; - }>(), - }, - - abhaCreateEnrolAbhaAddress: { - path: "/api/abdm/v3/health_id/create/enrol_abha_address/", - method: "POST", - TBody: Type<{ - transaction_id: string; - abha_address: string; - }>(), - TRes: Type<{ - detail?: string; - transaction_id: string; - health_id: string; - preferred_abha_address: string; - abha_number: AbhaNumberModel; - }>(), - }, - - linkAbhaNumberAndPatient: { - path: "/api/abdm/v3/health_id/link_patient/", - method: "POST", - TBody: Type<{ - abha_number: string; - patient: string; - }>(), - TRes: Type<{ - detail: string; - }>(), - }, - - abhaLoginCheckAuthMethods: { - path: "/api/abdm/v3/health_id/login/check_auth_methods/", - method: "POST", - TBody: Type<{ - abha_address: string; - }>(), - TRes: Type<{ - abha_number: string; - auth_methods: string[]; - }>(), - }, - - abhaLoginSendOtp: { - path: "/api/abdm/v3/health_id/login/send_otp/", - method: "POST", - TBody: Type<{ - type: "abha-number" | "abha-address" | "mobile" | "aadhaar"; - value: string; - otp_system: "abdm" | "aadhaar"; - }>(), - TRes: Type<{ - transaction_id: string; - detail: string; - }>(), - }, - - abhaLoginVerifyOtp: { - path: "/api/abdm/v3/health_id/login/verify_otp/", - method: "POST", - TBody: Type<{ - type: "abha-number" | "abha-address" | "mobile" | "aadhaar"; - otp: string; - transaction_id: string; - otp_system: "abdm" | "aadhaar"; - }>(), - TRes: Type<{ - abha_number: AbhaNumberModel; - created: boolean; - }>(), - }, - - getAbhaCard: { - path: "/api/abdm/v3/health_id/abha_card", - method: "GET", - TRes: Type(), - }, - }, - }, - // Prescription endpoints listPrescriptions: { diff --git a/src/common/constants.tsx b/src/common/constants.tsx index b917ea1aa68..857796d7c88 100644 --- a/src/common/constants.tsx +++ b/src/common/constants.tsx @@ -1,6 +1,5 @@ import { IconName } from "@/CAREUI/icons/CareIcon"; -import { ConsentHIType, ConsentPurpose } from "@/components/ABDM/types/consent"; import { SortOption } from "@/components/Common/SortDropdown"; import { PatientCategory, @@ -8,10 +7,10 @@ import { } from "@/components/Facility/models"; import { PhoneNumberValidator } from "@/components/Form/FieldValidators"; -import { SchemaType } from "@/common/schemaParser"; - import { dateQueryString } from "@/Utils/utils"; +import { SchemaType } from "./schemaParser"; + export const RESULTS_PER_PAGE_LIMIT = 14; export const PAGINATION_LIMIT = 36; @@ -1142,26 +1141,6 @@ export const AssetImportSchema: SchemaType = { }, }; -// ABDM -export const ABDM_CONSENT_PURPOSE = [ - "CAREMGT", - "BTG", - "PUBHLTH", - "HPAYMT", - "DSRCH", - "PATRQT", -] as ConsentPurpose[]; - -export const ABDM_HI_TYPE = [ - "Prescription", - "DiagnosticReport", - "OPConsultation", - "DischargeSummary", - "ImmunizationRecord", - "HealthDocumentRecord", - "WellnessRecord", -] as ConsentHIType[]; - export const USER_TYPES_MAP = { Pharmacist: "Pharmacist", Volunteer: "Volunteer", diff --git a/src/components/ABDM/ABDMFacilityRecords.tsx b/src/components/ABDM/ABDMFacilityRecords.tsx deleted file mode 100644 index ef3a1da8ab4..00000000000 --- a/src/components/ABDM/ABDMFacilityRecords.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import { Link } from "raviger"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import ButtonV2 from "@/components/Common/ButtonV2"; -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; - -import routes from "@/Utils/request/api"; -import useQuery from "@/Utils/request/useQuery"; -import { classNames, formatDateTime } from "@/Utils/utils"; - -interface IProps { - facilityId: string; -} - -const TableHeads = [ - "consent__patient", - "consent__status", - "created_on", - "updated_on", - "consent__hi_range", - "expires_on", - "consent__hi_types", -]; - -export default function ABDMFacilityRecords({ facilityId }: IProps) { - const { t } = useTranslation(); - - const { - data: consentsResult, - loading, - refetch, - } = useQuery(routes.abdm.consent.list, { - query: { facility: facilityId, ordering: "-created_date" }, - }); - - if (loading) { - return ; - } - - return ( - -
-
-
-
-
-
- - - - {TableHeads.map((head) => ( - - ))} - - - - - {consentsResult?.results.map((consent) => ( - - - - - - - - - - - - - - - - - - ))} - -
- {t(head)} - - refetch()} - ghost - className="max-w-2xl text-sm text-secondary-700 hover:text-secondary-900" - > - {t("refresh")} - - {t("view")} -
- {consent.patient_abha_object?.name} -

- ({consent.patient_abha}) -

-
- {new Date( - consent.consent_artefacts?.[0]?.expiry ?? - consent.expiry, - ) < new Date() - ? t("consent__status__EXPIRED") - : t( - `consent__status__${ - consent.consent_artefacts?.[0]?.status ?? - consent.status - }`, - )} - - {formatDateTime(consent.created_date)} - - {consent.status === "EXPIRED" || - new Date( - consent.consent_artefacts?.[0]?.expiry ?? - consent.expiry, - ) < new Date() ? ( -

- {formatDateTime( - consent.consent_artefacts?.[0]?.expiry ?? - consent.expiry, - )} - - {t("expired_on")} - -

- ) : consent.status === "REQUESTED" ? ( - "-" - ) : ( -

- {formatDateTime(consent.modified_date)} - - {t(`${consent.status.toLowerCase()}_on`)} - -

- )} -
- {formatDateTime( - consent.consent_artefacts?.[0]?.from_time ?? - consent.from_time, - )}{" "} -
- {formatDateTime( - consent.consent_artefacts?.[0]?.to_time ?? - consent.to_time, - )} -
- {formatDateTime( - consent.consent_artefacts?.[0]?.expiry ?? - consent.expiry, - )} - -
- {( - consent.consent_artefacts?.[0]?.hi_types ?? - consent.hi_types - )?.map((hiType) => ( - - {t(`consent__hi_type__${hiType}`)} - - ))} -
-
-
- new Date() - ? "cursor-pointer text-primary-600 hover:text-primary-900" - : "pointer-events-none cursor-not-allowed text-secondary-600 opacity-70", - )} - > - {t("view")} - -
-
-
-
- {consentsResult?.results.length === 0 && ( -
-

- {t("no_records_found")} -

-
- )} -
-
-
-
- ); -} diff --git a/src/components/ABDM/ABDMRecordsTab.tsx b/src/components/ABDM/ABDMRecordsTab.tsx deleted file mode 100644 index 3b6f2493fb4..00000000000 --- a/src/components/ABDM/ABDMRecordsTab.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import dayjs from "dayjs"; -import { Link } from "raviger"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { - ConsentArtefactModel, - ConsentRequestModel, -} from "@/components/ABDM/types/consent"; -import ButtonV2 from "@/components/Common/ButtonV2"; -import Loading from "@/components/Common/Loading"; - -import * as Notification from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import useQuery from "@/Utils/request/useQuery"; -import { classNames, formatName } from "@/Utils/utils"; - -interface IConsentArtefactCardProps { - artefact: ConsentArtefactModel; -} - -function ConsentArtefactCard({ artefact }: IConsentArtefactCardProps) { - const { t } = useTranslation(); - - return ( - -
-
-
- {artefact.hip} -
-

- {t("created_on")} {dayjs(artefact.created_date).fromNow()} -

-
-
-
- {artefact.status} -
-
- {dayjs(artefact.from_time).format("MMM DD YYYY")} -{" "} - {dayjs(artefact.to_time).format("MMM DD YYYY")} -
-

- {t("expires_on")} {dayjs(artefact.expiry).fromNow()} -

-
-
-
- {artefact.hi_types.map((hiType) => { - return ( -
- {t(`consent__hi_type__${hiType}`)} -
- ); - })} -
- - ); -} - -interface IConsentRequestCardProps { - consent: ConsentRequestModel; -} - -function ConsentRequestCard({ consent }: IConsentRequestCardProps) { - const { t } = useTranslation(); - - return ( -
-
-
-
- {t(`consent__purpose__${consent.purpose}`)} -
-
- {formatName(consent.requester)} -
-
-
-
- {dayjs(consent.from_time).format("MMM DD YYYY")} -{" "} - {dayjs(consent.to_time).format("MMM DD YYYY")} -
-

- {t("expires_on")} {dayjs(consent.expiry).fromNow()} -

-
-
- { - const { res, data } = await request( - routes.abdm.consent.checkStatus, - { - body: { - consent_request: consent.id, - }, - }, - ); - - if (res?.status === 202) { - Notification.Success({ - msg: data?.detail ?? t("checking_consent_status"), - }); - Notification.Warn({ - msg: t("async_operation_warning"), - }); - } - }} - ghost - className="max-w-2xl text-sm text-secondary-700 hover:text-secondary-900" - > - {t("check_status")} - -

- {t("created_on")} {dayjs(consent.created_date).fromNow()} -

-

- {t("modified_on")} {dayjs(consent.modified_date).fromNow()} -

-
-
- {consent.consent_artefacts?.length ? ( -
- {consent.consent_artefacts?.map((artefact) => ( - - ))} -
- ) : ( -
-

- {consent.status === "REQUESTED" - ? t("consent_request_waiting_approval") - : t("consent_request_rejected")} -

-
- )} -
- {consent.hi_types.map((hiType) => { - return ( -
- {t(`consent__hi_type__${hiType}`)} -
- ); - })} -
-
- ); -} - -interface IProps { - patientId: string; -} - -export default function ABDMRecordsTab({ patientId }: IProps) { - const { t } = useTranslation(); - - const { data, loading } = useQuery(routes.abdm.consent.list, { - query: { - patient: patientId, - ordering: "-created_date", - }, - }); - - if (loading) { - ; - } - - if (!data?.results.length) { - return ( -
-

- {t("no_records_found")} -

-

- {t("raise_consent_request")} -

-
- ); - } - - return ( -
- {data?.results.map((record) => { - return ; - })} -
- ); -} diff --git a/src/components/ABDM/ABHAProfileModal.tsx b/src/components/ABDM/ABHAProfileModal.tsx deleted file mode 100644 index 1e348a40778..00000000000 --- a/src/components/ABDM/ABHAProfileModal.tsx +++ /dev/null @@ -1,164 +0,0 @@ -import QRCode from "qrcode.react"; -import { useRef } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import DialogModal from "@/components/Common/Dialog"; - -import * as Notify from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import { formatDateTime } from "@/Utils/utils"; - -interface IProps { - patientId?: string; - abha?: AbhaNumberModel; - show: boolean; - onClose: () => void; -} - -const ABHAProfileModal = ({ patientId, show, onClose, abha }: IProps) => { - const { t } = useTranslation(); - - const printRef = useRef(null); - - const downloadAbhaCard = async (type: "pdf" | "png") => { - if (!patientId || !abha?.abha_number) return; - - Notify.Success({ msg: t("downloading_abha_card") }); - - const { res, data } = await request(routes.abdm.healthId.getAbhaCard, { - query: { abha_id: abha?.abha_number }, - }); - - if (res?.status === 200 && data) { - const imageUrl = URL.createObjectURL(data); - - if (type === "png") { - const downloadLink = document.createElement("a"); - downloadLink.href = imageUrl; - downloadLink.download = "abha.png"; - downloadLink.click(); - URL.revokeObjectURL(imageUrl); - } else { - const printWindow = window.open("", "_blank"); - const htmlPopup = ` - - - Print Image - - - - - - - `; - printWindow?.document.write(htmlPopup); - printWindow?.document.close(); - printWindow?.addEventListener("load", () => { - printWindow?.print(); - URL.revokeObjectURL(imageUrl); - }); - } - } - }; - - return ( - -

{t("abha_profile")}

-
- downloadAbhaCard("pdf")} - icon="l-print" - className="cursor-pointer" - /> - downloadAbhaCard("png")} - icon="l-import" - className="cursor-pointer" - /> -
- - } - show={show} - onClose={onClose} - className="max-w-[500px]" - fixedWidth={false} - > -
-
- -
-
- {[ - { - label: t("full_name"), - value: - abha?.name || - `${abha?.first_name} ${abha?.middle_name} ${abha?.last_name}`, - }, - { label: t("date_of_birth"), value: abha?.date_of_birth }, - { label: t("gender"), value: abha?.gender }, - { label: t("abha_number"), value: abha?.abha_number }, - { - label: t("abha_address"), - value: abha?.health_id?.split("@")[0], - }, - { label: t("email"), value: abha?.email }, - ].map((item, index) => - item.value ? ( -
-
{item.label}
-
{item.value}
-
- ) : null, - )} -
-
- -
- {abha?.created_date && ( -
- {t("created_on")}: - {formatDateTime(abha.created_date)} -
- )} - {abha?.modified_date && ( -
- {t("modified_on")}: - {formatDateTime(abha.modified_date)} -
- )} -
-
- ); -}; - -export default ABHAProfileModal; diff --git a/src/components/ABDM/ConfigureHealthFacility.tsx b/src/components/ABDM/ConfigureHealthFacility.tsx deleted file mode 100644 index ba3eb325dec..00000000000 --- a/src/components/ABDM/ConfigureHealthFacility.tsx +++ /dev/null @@ -1,243 +0,0 @@ -import { navigate } from "raviger"; -import { useReducer, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import { IHealthFacility } from "@/components/ABDM/types/health-facility"; -import { Submit } from "@/components/Common/ButtonV2"; -import Loading from "@/components/Common/Loading"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; - -import * as Notification from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import useQuery from "@/Utils/request/useQuery"; -import { classNames } from "@/Utils/utils"; - -const initForm = { - health_facility: null as IHealthFacility | null, - hf_id: "", -}; - -const initialState = { - form: { ...initForm }, - errors: {} as Partial>, -}; - -const FormReducer = ( - state = initialState, - action: - | { - type: "set_form"; - form: typeof initialState.form; - } - | { - type: "set_error"; - errors: typeof initialState.errors; - }, -) => { - switch (action.type) { - case "set_form": { - return { - ...state, - form: action.form, - }; - } - case "set_error": { - return { - ...state, - errors: action.errors, - }; - } - default: - return state; - } -}; - -export interface IConfigureHealthFacilityProps { - facilityId: string; -} - -export const ConfigureHealthFacility = ( - props: IConfigureHealthFacilityProps, -) => { - const { t } = useTranslation(); - - const [state, dispatch] = useReducer(FormReducer, initialState); - const { facilityId } = props; - const [isLoading, setIsLoading] = useState(false); - - const { loading } = useQuery(routes.abdm.healthFacility.get, { - pathParams: { facility_id: facilityId }, - silent: true, - onResponse(res) { - if (res.data) { - dispatch({ - type: "set_form", - form: { - ...state.form, - health_facility: res.data, - hf_id: res.data.hf_id, - }, - }); - } - }, - }); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setIsLoading(true); - - if (!state.form.hf_id) { - dispatch({ - type: "set_error", - errors: { hf_id: t("health_facility__validation__hf_id_required") }, - }); - setIsLoading(false); - return; - } - - let response = null; - let responseData = null; - if (state.form.hf_id === state.form.health_facility?.hf_id) { - const { res, data } = await request( - routes.abdm.healthFacility.registerAsService, - { - pathParams: { - facility_id: facilityId, - }, - }, - ); - response = res; - responseData = data; - } else if (state.form.health_facility) { - const { res, data } = await request( - routes.abdm.healthFacility.partialUpdate, - { - pathParams: { - facility_id: facilityId, - }, - body: { - hf_id: state.form.hf_id, - }, - }, - ); - response = res; - responseData = data; - } else { - const { res, data } = await request(routes.abdm.healthFacility.create, { - body: { - facility: facilityId, - hf_id: state.form.hf_id, - }, - silent: true, - }); - response = res; - responseData = data; - } - - if (response?.ok && responseData?.registered) { - Notification.Success({ - msg: t("health_facility__config_update_success"), - }); - navigate(`/facility/${facilityId}`); - } else { - if (responseData?.registered === false) { - Notification.Warn({ - msg: - responseData?.detail || - t("health_facility__config_registration_error"), - }); - navigate(`/facility/${facilityId}`); - } else { - Notification.Error({ - msg: - responseData?.detail || t("health_facility__config_update_error"), - }); - } - } - setIsLoading(false); - }; - - const handleChange = (e: FieldChangeEvent) => { - dispatch({ - type: "set_form", - form: { ...state.form, [e.name]: e.value }, - }); - }; - - if (loading || isLoading) { - return ; - } - - return ( -
-
-
-
- - {state.form.health_facility?.registered ? ( - <> -
- - {t("health_facility__registered_1.1")}{" "} - - {t("health_facility__registered_1.2")} - - - - {t("health_facility__registered_2")} - -
- {t("health_facility__registered_3")} - - ) : ( - <> -
- - {t("health_facility__not_registered_1.1")}{" "} - - {t("health_facility__not_registered_1.2")} - - - - {t("health_facility__not_registered_2")} - - {t("health_facility__not_registered_3")} -
- - )} -

- } - required - value={state.form.hf_id} - onChange={handleChange} - error={state.errors?.hf_id} - /> -
-
-
- -
-
-
- ); -}; diff --git a/src/components/ABDM/FetchRecordsModal.tsx b/src/components/ABDM/FetchRecordsModal.tsx deleted file mode 100644 index 9433374f931..00000000000 --- a/src/components/ABDM/FetchRecordsModal.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import dayjs from "dayjs"; -import { navigate } from "raviger"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; - -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import { ConsentHIType, ConsentPurpose } from "@/components/ABDM/types/consent"; -import ButtonV2 from "@/components/Common/ButtonV2"; -import DialogModal from "@/components/Common/Dialog"; -import DateFormField from "@/components/Form/FormFields/DateFormField"; -import DateRangeFormField from "@/components/Form/FormFields/DateRangeFormField"; -import { - MultiSelectFormField, - SelectFormField, -} from "@/components/Form/FormFields/SelectFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; - -import { useMessageListener } from "@/hooks/useMessageListener"; - -import { ABDM_CONSENT_PURPOSE, ABDM_HI_TYPE } from "@/common/constants"; - -import * as Notification from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; - -const getDate = (value: string | Date) => - (value && dayjs(value).isValid() && dayjs(value).toDate()) || undefined; - -interface IProps { - abha?: AbhaNumberModel; - show: boolean; - onClose: () => void; -} - -export default function FetchRecordsModal({ abha, show, onClose }: IProps) { - const { t } = useTranslation(); - - const [idVerificationStatus, setIdVerificationStatus] = useState< - "pending" | "in-progress" | "verified" | "failed" - >("verified"); - const [purpose, setPurpose] = useState("CAREMGT"); - const [fromDate, setFromDate] = useState( - dayjs().subtract(30, "day").toDate(), - ); - const [toDate, setToDate] = useState(dayjs().toDate()); - const [isMakingConsentRequest, setIsMakingConsentRequest] = useState(false); - const [hiTypes, setHiTypes] = useState([]); - const [expiryDate, setExpiryDate] = useState( - dayjs().add(30, "day").toDate(), - ); - const [errors, setErrors] = useState>({}); - // const notificationSubscriptionState = useNotificationSubscriptionState([ - // show, - // ]); - - useMessageListener((data) => { - if (data.type === "MESSAGE" && data.from === "patients/on_find") { - if (data.message?.patient?.id === abha?.health_id) { - setIdVerificationStatus("verified"); - setErrors({ - ...errors, - health_id: "", - }); - } - } - }); - - return ( - - {/* {["unsubscribed", "subscribed_on_other_device"].includes( - notificationSubscriptionState, - ) && ( -

- {" "} - Notifications needs to be enabled on this device to verify the - patient. -

- )} */} - -
- null} - disabled - label={t("consent_request__patient_identifier")} - name="health_id" - error={errors.health_id} - className="flex-1" - /> - - {/* { - const { res } = await request(routes.abha.findPatient, { - body: { - id: abha?.health_id, - }, - reattempts: 0, - }); - - if (res?.status) { - setIdVerificationStatus("in-progress"); - } - }} - loading={idVerificationStatus === "in-progress"} - ghost={idVerificationStatus === "verified"} - disabled={ - idVerificationStatus === "verified" || - ["unsubscribed", "subscribed_on_other_device"].includes( - notificationSubscriptionState, - ) - } - className={classNames( - "mt-1.5 !py-3", - idVerificationStatus === "verified" && - "disabled:cursor-auto disabled:bg-transparent disabled:text-primary-600", - )} - > - {idVerificationStatus === "in-progress" && ( - - )} - {idVerificationStatus === "verified" && } - { - { - pending: "Verify Patient", - "in-progress": "Verifying", - verified: "Verified", - failed: "Retry", - }[idVerificationStatus] - } - */} -
- t(`consent__purpose__${o}`)} - optionValue={(o) => o} - value={purpose} - onChange={({ value }) => setPurpose(value)} - required - /> - - { - setFromDate(e.value.start!); - setToDate(e.value.end!); - }} - label={t("consent_request__date_range")} - required - /> - - { - setHiTypes(ABDM_HI_TYPE); - }} - > - {t("select_all")} - - ) - } - value={hiTypes} - optionLabel={(option) => t(`consent__hi_type__${option}`)} - optionValue={(option) => option} - onChange={(e) => setHiTypes(e.value)} - required - /> - - setExpiryDate(e.value!)} - label={t("consent_request__expiry")} - required - disablePast - /> - -
- { - if (idVerificationStatus !== "verified") { - setErrors({ - ...errors, - health_id: t("verify_patient_identifier"), - }); - - return; - } - - setIsMakingConsentRequest(true); - const { res } = await request(routes.abdm.consent.create, { - body: { - patient_abha: abha?.health_id as string, - hi_types: hiTypes, - purpose, - from_time: fromDate, - to_time: toDate, - expiry: expiryDate, - }, - }); - - if (res?.status === 201) { - Notification.Success({ - msg: t("consent_requested_successfully"), - }); - - navigate( - `/facility/${abha?.patient_object?.facility}/abdm`, - // ?? `/facility/${abha?.patient_object?.facility}/patient/${abha?.patient_object?.id}/consultation/${abha?.patient_object?.last_consultation?.id}/abdm`, - ); - } - setIsMakingConsentRequest(false); - onClose(); - }} - disabled={idVerificationStatus !== "verified"} - loading={isMakingConsentRequest} - > - {t("request_consent")} - -
-
- ); -} diff --git a/src/components/ABDM/HealthInformation.tsx b/src/components/ABDM/HealthInformation.tsx deleted file mode 100644 index e147f050131..00000000000 --- a/src/components/ABDM/HealthInformation.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { HIProfile } from "hi-profiles"; -import { useTranslation } from "react-i18next"; - -import Loading from "@/components/Common/Loading"; -import Page from "@/components/Common/Page"; - -import routes from "@/Utils/request/api"; -import useQuery from "@/Utils/request/useQuery"; - -interface IProps { - artefactId: string; -} - -export default function HealthInformation({ artefactId }: IProps) { - const { t } = useTranslation(); - - const { data, loading, error } = useQuery(routes.abdm.healthInformation.get, { - pathParams: { artefactId }, - silent: true, - }); - - if (loading) { - return ; - } - - const parseData = (data: string) => { - try { - return JSON.parse(data); - } catch (e) { - return JSON.parse( - data.replace(/"/g, '\\"').replace(/'/g, '"'), // eslint-disable-line - ); - } - }; - - return ( - -
- {!!error?.is_archived && ( - <> -

- {t("hi__record_archived__title")} -

-
- {t("hi__record_archived_description")} -
-

- {t("hi__record_archived_on")}{" "} - {new Date(error?.archived_time as string).toLocaleString()} -{" "} - {error?.archived_reason as string} -

- - )} - {error && !error?.is_archived && ( - <> -

- {t("hi__record_not_fetched_title")} -

-
- {t("hi__record_not_fetched_description")} -
-

- {t("hi__waiting_for_record")} -

- - )} - {data?.data.map((item) => ( - - ))} -
-
- ); -} diff --git a/src/components/ABDM/LinkAbhaNumber/CreateWithAadhaar.tsx b/src/components/ABDM/LinkAbhaNumber/CreateWithAadhaar.tsx deleted file mode 100644 index d2697633bea..00000000000 --- a/src/components/ABDM/LinkAbhaNumber/CreateWithAadhaar.tsx +++ /dev/null @@ -1,740 +0,0 @@ -import { useEffect, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import useMultiStepForm, { - InjectedStepProps, -} from "@/components/ABDM/LinkAbhaNumber/useMultiStepForm"; -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import ButtonV2, { ButtonWithTimer } from "@/components/Common/ButtonV2"; -import CheckBoxFormField from "@/components/Form/FormFields/CheckBoxFormField"; -import OtpFormField from "@/components/Form/FormFields/OtpFormField"; -import PhoneNumberFormField from "@/components/Form/FormFields/PhoneNumberFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import { validateRule } from "@/components/Users/UserAdd"; - -import * as Notify from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import { classNames } from "@/Utils/utils"; - -const MAX_OTP_RESEND_ALLOWED = 2; - -type ICreateWithAadhaarProps = { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -type Memory = { - aadhaarNumber: string; - mobileNumber: string; - - isLoading: boolean; - validationError: string; - - transactionId: string; - abhaNumber: AbhaNumberModel | null; - - resendOtpCount: number; -}; - -export default function CreateWithAadhaar({ - onSuccess, -}: ICreateWithAadhaarProps) { - const { currentStep } = useMultiStepForm( - [ - , - , - , - , - , - , - ], - { - aadhaarNumber: "", - mobileNumber: "+91", - isLoading: false, - validationError: "", - transactionId: "", - abhaNumber: null, - resendOtpCount: 0, - }, - ); - - return
{currentStep}
; -} - -type IEnterAadhaarProps = InjectedStepProps; - -function EnterAadhaar({ memory, setMemory, next }: IEnterAadhaarProps) { - const { t } = useTranslation(); - const [disclaimerAccepted, setDisclaimerAccepted] = useState([ - false, - false, - false, - false, - ]); - - const validateAadhaar = () => { - if ( - memory?.aadhaarNumber.length !== 12 && - memory?.aadhaarNumber.length !== 16 - ) { - setMemory((prev) => ({ - ...prev, - validationError: t("aadhaar_validation_length_error"), - })); - return false; - } - - if (memory?.aadhaarNumber.includes(" ")) { - setMemory((prev) => ({ - ...prev, - validationError: t("aadhaar_validation_space_error"), - })); - return false; - } - - return true; - }; - - const handleSubmit = async () => { - if (!validateAadhaar()) return; - - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateSendAadhaarOtp, - { - body: { - aadhaar: memory!.aadhaarNumber, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ ...prev, transactionId: data.transaction_id })); - Notify.Success({ - msg: data.detail ?? t("aadhaar_otp_send_success"), - }); - next(); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- - setMemory((prev) => ({ ...prev, aadhaarNumber: value })) - } - error={memory?.validationError} - /> - - {t("aadhaar_number_will_not_be_stored")} - -
- -
- {disclaimerAccepted.map((isAccepted, i) => ( - { - setDisclaimerAccepted( - disclaimerAccepted.map((v, j) => (j === i ? e.value : v)), - ); - }} - className="mr-2 rounded border-gray-700" - labelClassName="text-xs text-gray-800" - errorClassName="hidden" - /> - ))} -
- -
- !v) || - memory?.aadhaarNumber.length === 0 - } - onClick={handleSubmit} - > - {t("send_otp")} - -
-
- ); -} - -type IVerifyAadhaarProps = InjectedStepProps; - -function VerifyAadhaar({ memory, setMemory, next }: IVerifyAadhaarProps) { - const { t } = useTranslation(); - const [otp, setOtp] = useState(""); - - const validateMobileNumber = () => { - const phone = memory?.mobileNumber.replace("+91", "").replace(/ /g, ""); - if (phone?.length !== 10) { - setMemory((prev) => ({ - ...prev, - validationError: t("mobile_number_validation_error"), - })); - return false; - } - - return true; - }; - - const handleSubmit = async () => { - if (!validateMobileNumber()) return; - - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateVerifyAadhaarOtp, - { - body: { - otp: otp, - transaction_id: memory?.transactionId, - mobile: memory?.mobileNumber.replace("+91", "").replace(/ /g, ""), - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - abhaNumber: data.abha_number, - resendOtpCount: 0, - })); - Notify.Success({ - msg: data.detail ?? t("otp_verification_success"), - }); - next(); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - const handleResendOtp = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateSendAadhaarOtp, - { - body: { - aadhaar: memory!.aadhaarNumber, - // transaction_id: memory?.transactionId, - }, - silent: true, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - resendOtpCount: prev.resendOtpCount + 1, - })); - Notify.Success({ - msg: data.detail ?? t("aadhaar_otp_send_success"), - }); - } else { - setMemory((prev) => ({ - ...prev, - resendOtpCount: Infinity, - })); - Notify.Success({ - msg: t("aadhaar_otp_send_error"), - }); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- - setMemory((prev) => ({ ...prev, aadhaarNumber: value })) - } - /> - - {t("aadhaar_number_will_not_be_stored")} - -
- -
- setOtp(value as string)} - value={otp} - label={t("enter_aadhaar_otp")} - disabled={memory?.isLoading} - /> -
- -
- } - name="mobile_number" - value={memory?.mobileNumber} - onChange={(e) => { - if (!memory?.mobileNumber.startsWith("+91")) { - setMemory((prev) => ({ - ...prev, - validationError: t("only_indian_mobile_numbers_supported"), - })); - return; - } - - setMemory((prev) => ({ ...prev, mobileNumber: e.value })); - }} - error={memory?.validationError} - errorClassName="text-xs text-red-500" - types={["mobile"]} - /> -
- -
- 6 || memory?.mobileNumber.length === 0} - onClick={handleSubmit} - > - {t("verify_otp")} - - - {(memory?.resendOtpCount ?? 0) < MAX_OTP_RESEND_ALLOWED && ( - - {t("resend_otp")} - - )} -
-
- ); -} - -type IHandleExistingAbhaNumberProps = InjectedStepProps & { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -function HandleExistingAbhaNumber({ - memory, - onSuccess, - next, -}: IHandleExistingAbhaNumberProps) { - const { t } = useTranslation(); - - // skip this step for new abha number - useEffect(() => { - if (memory?.abhaNumber?.new) { - next(); - } - }, [memory?.abhaNumber, memory?.mobileNumber]); // eslint-disable-line - - return ( -
-

- {t("abha_number_exists")} -

-

- {t("abha_number_exists_description")} -

-
- - {t("create_new_abha_address")} - - onSuccess(memory?.abhaNumber as AbhaNumberModel)} - > - {t("use_existing_abha_address")} - -

- {memory?.abhaNumber?.health_id} -

-
-
- ); -} - -type ILinkMobileNumberProps = InjectedStepProps; - -function LinkMobileNumber({ - memory, - goTo, - setMemory, - next, -}: ILinkMobileNumberProps) { - const { t } = useTranslation(); - - useEffect(() => { - if ( - memory?.abhaNumber?.mobile === - memory?.mobileNumber.replace("+91", "").replace(/ /g, "") - ) { - goTo(5); // skip linking mobile number - } - }, [memory?.abhaNumber, memory?.mobileNumber]); // eslint-disable-line - - const handleSubmit = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateLinkMobileNumber, - { - body: { - mobile: memory?.mobileNumber.replace("+91", "").replace(/ /g, ""), - transaction_id: memory?.transactionId, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - })); - Notify.Success({ - msg: data.detail ?? t("mobile_otp_send_success"), - }); - next(); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- } - name="mobile_number" - value={memory?.mobileNumber} - disabled={true} - onChange={() => null} - types={["mobile"]} - /> -
- -

- {t("mobile_number_different_from_aadhaar_mobile_number")} -

- -
- - {t("send_otp")} - -
-
- ); -} - -type IVerifyMobileNumberProps = InjectedStepProps; - -function VerifyMobileNumber({ - memory, - setMemory, - next, -}: IVerifyMobileNumberProps) { - const { t } = useTranslation(); - const [otp, setOtp] = useState(""); - - const handleSubmit = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateVerifyMobileNumber, - { - body: { - transaction_id: memory?.transactionId, - otp: otp, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - resendOtpCount: 0, - })); - Notify.Success({ - msg: data.detail ?? t("mobile_otp_verify_success"), - }); - next(); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - const handleResendOtp = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateLinkMobileNumber, - { - body: { - mobile: memory?.mobileNumber.replace("+91", "").replace(/ /g, ""), - transaction_id: memory?.transactionId, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - resendOtpCount: prev.resendOtpCount + 1, - })); - Notify.Success({ - msg: data.detail ?? t("mobile_otp_send_success"), - }); - } else { - setMemory((prev) => ({ - ...prev, - resendOtpCount: Infinity, - })); - Notify.Success({ - msg: t("mobile_otp_send_error"), - }); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- } - name="mobile_number" - value={memory?.mobileNumber} - disabled={true} - onChange={() => null} - types={["mobile"]} - /> -
- -
- setOtp(value as string)} - value={otp} - label={t("enter_mobile_otp")} - disabled={memory?.isLoading} - /> -
- -
- - {t("verify_otp")} - - - {(memory?.resendOtpCount ?? 0) < MAX_OTP_RESEND_ALLOWED && ( - - {t("resend_otp")} - - )} -
-
- ); -} - -type IChooseAbhaAddressProps = InjectedStepProps & { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -function ChooseAbhaAddress({ - memory, - setMemory, - onSuccess, -}: IChooseAbhaAddressProps) { - const { t } = useTranslation(); - const [healthId, setHealthId] = useState(""); - const [suggestions, setSuggestions] = useState([]); - - useEffect(() => { - const fetchSuggestions = async () => { - const { res, data } = await request( - routes.abdm.healthId.abhaCreateAbhaAddressSuggestion, - { - body: { - transaction_id: memory?.transactionId, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ ...prev, transactionId: data.transaction_id })); - setSuggestions(data.abha_addresses); - } - }; - - fetchSuggestions(); - }, [healthId, memory?.transactionId, setMemory]); - - const handleSubmit = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaCreateEnrolAbhaAddress, - { - body: { - abha_address: healthId, - transaction_id: memory?.transactionId, - }, - }, - ); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - abhaNumber: data.abha_number, - })); - Notify.Success({ - msg: data.detail ?? t("abha_address_created_success"), - }); - onSuccess(data.abha_number); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
- { - setHealthId(value); - }} - /> - -
- {validateRule( - healthId.length >= 4, - t("abha_address_validation_length_error"), - false, - )} - {validateRule( - Number.isNaN(Number(healthId[0])) && healthId[0] !== ".", - t("abha_address_validation_start_error"), - false, - )} - {validateRule( - healthId[healthId.length - 1] !== ".", - t("abha_address_validation_end_error"), - false, - )} - {validateRule( - /^[0-9a-zA-Z._]+$/.test(healthId), - t("abha_address_validation_character_error"), - false, - )} -
- - {suggestions.length > 0 && ( -
-

- {t("abha_address_suggestions")} -

-
- {suggestions - .filter((suggestion) => suggestion !== healthId) - .map((suggestion) => ( -

setHealthId(suggestion)} - className="cursor-pointer rounded-md bg-primary-400 px-2.5 py-1 text-xs text-white" - > - {suggestion} -

- ))} -
-
- )} - -
- - {t("create_abha_address")} - -
-
- ); -} diff --git a/src/components/ABDM/LinkAbhaNumber/LinkWithOtp.tsx b/src/components/ABDM/LinkAbhaNumber/LinkWithOtp.tsx deleted file mode 100644 index 9b8150dee86..00000000000 --- a/src/components/ABDM/LinkAbhaNumber/LinkWithOtp.tsx +++ /dev/null @@ -1,350 +0,0 @@ -import { useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import useMultiStepForm, { - InjectedStepProps, -} from "@/components/ABDM/LinkAbhaNumber/useMultiStepForm"; -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import ButtonV2, { ButtonWithTimer } from "@/components/Common/ButtonV2"; -import Dropdown, { DropdownItem } from "@/components/Common/Menu"; -import CheckBoxFormField from "@/components/Form/FormFields/CheckBoxFormField"; -import OtpFormField from "@/components/Form/FormFields/OtpFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; - -import * as Notify from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import { classNames } from "@/Utils/utils"; - -const MAX_OTP_RESEND_ALLOWED = 2; - -type ILoginWithOtpProps = { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -type Memory = { - id: string; - - isLoading: boolean; - validationError: string; - - transactionId: string; - type: "aadhaar" | "mobile" | "abha-number" | "abha-address"; - otp_system: "abdm" | "aadhaar"; - abhaNumber: AbhaNumberModel | null; - - resendOtpCount: number; -}; - -export default function LinkWithOtp({ onSuccess }: ILoginWithOtpProps) { - const { currentStep } = useMultiStepForm( - [ - , - , - ], - { - id: "", - isLoading: false, - validationError: "", - transactionId: "", - type: "aadhaar", - otp_system: "aadhaar", - abhaNumber: null, - resendOtpCount: 0, - }, - ); - - return
{currentStep}
; -} - -type IEnterIdProps = InjectedStepProps; - -const supportedAuthMethods = ["AADHAAR_OTP", "MOBILE_OTP"]; - -function EnterId({ memory, setMemory, next }: IEnterIdProps) { - const { t } = useTranslation(); - const [disclaimerAccepted, setDisclaimerAccepted] = useState([ - false, - false, - false, - ]); - const [authMethods, setAuthMethods] = useState([]); - - const valueType = useMemo(() => { - const id = memory?.id; - const isNumeric = !isNaN(Number(id?.trim())); - - if (isNumeric && (id?.length === 12 || id?.length === 16)) { - return "aadhaar"; - } else if (isNumeric && id?.length === 10) { - return "mobile"; - } else if (isNumeric && id?.length === 14) { - return "abha-number"; - } else { - return "abha-address"; - } - }, [memory?.id]); - - const handleGetAuthMethods = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - if (valueType === "aadhaar") { - setAuthMethods(["AADHAAR_OTP"]); - } else if (valueType === "mobile") { - setAuthMethods(["MOBILE_OTP"]); - } else { - const { res, data, error } = await request( - routes.abdm.healthId.abhaLoginCheckAuthMethods, - { - body: { - abha_address: memory?.id.replace(/-/g, "").replace(/ /g, ""), - }, - silent: true, - }, - ); - - if (res?.status === 200 && data) { - const methods = data.auth_methods.filter((method: string) => - supportedAuthMethods.find((supported) => supported === method), - ); - - if (methods.length === 0) { - Notify.Warn({ msg: t("get_auth_mode_error") }); - } - } else { - Notify.Error({ msg: error?.message ?? t("get_auth_mode_error") }); - } - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - const handleSendOtp = async (authMethod: string) => { - if (!supportedAuthMethods.includes(authMethod)) { - Notify.Warn({ msg: t("auth_method_unsupported") }); - return; - } - - const otp_system: "aadhaar" | "abdm" = - authMethod === "AADHAAR_OTP" ? "aadhaar" : "abdm"; - - setMemory((prev) => ({ - ...prev, - isLoading: true, - type: valueType, - otp_system, - })); - - const { res, data } = await request(routes.abdm.healthId.abhaLoginSendOtp, { - body: { - value: memory?.id, - type: valueType, - otp_system, - }, - }); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - })); - Notify.Success({ msg: data.detail ?? t("send_otp_success") }); - next(); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- { - setMemory((prev) => ({ ...prev, id: value })); - setAuthMethods([]); - }} - error={memory?.validationError} - /> - - {t("any_id_description")} - -
- -
- {disclaimerAccepted.map((isAccepted, i) => ( - { - setDisclaimerAccepted( - disclaimerAccepted.map((v, j) => (j === i ? e.value : v)), - ); - }} - className="mr-2 rounded border-gray-700" - labelClassName="text-xs text-gray-800" - errorClassName="hidden" - /> - ))} -
- -
- {authMethods.length === 0 ? ( - !v) || memory?.id.length === 0 - } - onClick={handleGetAuthMethods} - > - {t("get_auth_methods")} - - ) : ( - - {authMethods.map((method) => ( - handleSendOtp(method)}> - {t(`abha__auth_method__${method}`)} - - ))} - - )} -
-
- ); -} - -type IVerifyIdProps = InjectedStepProps & { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -function VerifyId({ memory, setMemory, onSuccess }: IVerifyIdProps) { - const { t } = useTranslation(); - const [otp, setOtp] = useState(""); - - const handleSubmit = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request( - routes.abdm.healthId.abhaLoginVerifyOtp, - { - body: { - type: memory?.type, - transaction_id: memory?.transactionId, - otp, - otp_system: memory?.otp_system, - }, - }, - ); - - if (res?.status === 200 && data) { - Notify.Success({ msg: t("verify_otp_success") }); - onSuccess(data.abha_number); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - const handleResendOtp = async () => { - setMemory((prev) => ({ ...prev, isLoading: true })); - - const { res, data } = await request(routes.abdm.healthId.abhaLoginSendOtp, { - body: { - value: memory?.id, - type: memory?.type, - otp_system: memory?.otp_system, - }, - }); - - if (res?.status === 200 && data) { - setMemory((prev) => ({ - ...prev, - transactionId: data.transaction_id, - resendOtpCount: (prev.resendOtpCount ?? 0) + 1, - })); - Notify.Success({ msg: data.detail ?? t("send_otp_success") }); - } else { - setMemory((prev) => ({ - ...prev, - resendOtpCount: Infinity, - })); - Notify.Error({ msg: t("send_otp_error") }); - } - - setMemory((prev) => ({ ...prev, isLoading: false })); - }; - - return ( -
-
- null} - /> - - {t("any_id_description")} - -
- -
- setOtp(value as string)} - value={otp} - label={t("enter_otp")} - disabled={memory?.isLoading} - /> -
- -
- - {t("verify_and_link")} - - - {(memory?.resendOtpCount ?? 0) < MAX_OTP_RESEND_ALLOWED && ( - - {t("resend_otp")} - - )} -
-
- ); -} diff --git a/src/components/ABDM/LinkAbhaNumber/LinkWithQr.tsx b/src/components/ABDM/LinkAbhaNumber/LinkWithQr.tsx deleted file mode 100644 index 712d28943b4..00000000000 --- a/src/components/ABDM/LinkAbhaNumber/LinkWithQr.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { IDetectedBarcode, Scanner } from "@yudiel/react-qr-scanner"; -import { useState } from "react"; -import { useTranslation } from "react-i18next"; - -import { ABHAQRContent, AbhaNumberModel } from "@/components/ABDM/types/abha"; - -import * as Notification from "@/Utils/Notifications"; -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; - -type ILoginWithQrProps = { - onSuccess: (abhaNumber: AbhaNumberModel) => void; -}; - -export default function LinkWithQr({ onSuccess }: ILoginWithQrProps) { - const { t } = useTranslation(); - const [isLoading, setIsLoading] = useState(false); - - return ( -
- { - if (detectedCodes.length === 0) return; - - const scannedValue = detectedCodes[0].rawValue; - if (!scannedValue || isLoading) return; - - try { - const qrData = JSON.parse(scannedValue) as ABHAQRContent; - - setIsLoading(true); - const { res, data } = await request(routes.abdm.abhaNumber.create, { - body: { - abha_number: qrData.hidn, - health_id: qrData.hid || qrData.phr, - name: qrData.name, - gender: qrData.gender, - date_of_birth: qrData.dob, - address: qrData.address, - district: qrData.district_name || qrData["dist name"], - state: qrData.state_name || qrData["state name"], - mobile: qrData.mobile, - }, - }); - - if (res?.status === 201 && data) { - onSuccess(data); - } - setIsLoading(false); - } catch (e) { - Notification.Error({ - msg: t("abha__qr_scanning_error"), - }); - } - }} - onError={(e: unknown) => { - const errorMessage = e instanceof Error ? e.message : "Unknown error"; - Notification.Error({ - msg: errorMessage, - }); - }} - scanDelay={3000} - constraints={{ - facingMode: "environment", - }} - /> -
- ); -} diff --git a/src/components/ABDM/LinkAbhaNumber/index.tsx b/src/components/ABDM/LinkAbhaNumber/index.tsx deleted file mode 100644 index 45cd0b34d02..00000000000 --- a/src/components/ABDM/LinkAbhaNumber/index.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CreateWithAadhaar from "@/components/ABDM/LinkAbhaNumber/CreateWithAadhaar"; -import LinkWithOtp from "@/components/ABDM/LinkAbhaNumber/LinkWithOtp"; -import LinkWithQr from "@/components/ABDM/LinkAbhaNumber/LinkWithQr"; -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import ButtonV2 from "@/components/Common/ButtonV2"; -import DialogModal from "@/components/Common/Dialog"; - -import { classNames } from "@/Utils/utils"; - -interface ILinkAbhaNumberProps { - show: boolean; - onClose: () => void; - onSuccess: (abhaNumber: AbhaNumberModel) => void; -} - -const ABHA_LINK_OPTIONS = { - create_with_aadhaar: { - title: "abha_link_options__create_with_aadhaar__title", - description: "abha_link_options__create_with_aadhaar__description", - disabled: false, - value: "create_with_aadhaar", - create: true, - }, - link_with_otp: { - title: "abha_link_options__link_with_otp__title", - description: "abha_link_options__link_with_otp__description", - disabled: false, - value: "link_with_otp", - create: false, - }, - create_with_driving_license: { - title: "abha_link_options__create_with_driving_license__title", - description: "abha_link_options__create_with_driving_license__description", - disabled: true, - value: "create_with_driving_license", - create: true, - }, - link_with_demographics: { - title: "abha_link_options__link_with_demographics__title", - description: "abha_link_options__link_with_demographics__description", - disabled: true, - value: "link_with_demographics", - create: false, - }, - link_with_qr: { - title: "abha_link_options__link_with_qr__title", - description: "abha_link_options__link_with_qr__description", - disabled: false, - value: "link_with_qr", - create: false, - }, -}; - -export default function LinkAbhaNumber({ - show, - onClose, - onSuccess, -}: ILinkAbhaNumberProps) { - const { t } = useTranslation(); - const [currentAbhaLinkOption, setCurrentAbhaLinkOption] = useState< - keyof typeof ABHA_LINK_OPTIONS - >("create_with_aadhaar"); - - return ( - - {currentAbhaLinkOption === "create_with_aadhaar" && ( - - )} - - {currentAbhaLinkOption === "link_with_otp" && ( - - )} - - {currentAbhaLinkOption === "link_with_qr" && ( - - )} - -
-

- setCurrentAbhaLinkOption( - ABHA_LINK_OPTIONS[currentAbhaLinkOption].create - ? "link_with_otp" - : "create_with_aadhaar", - ) - } - className="cursor-pointer text-center text-sm text-blue-800" - > - {ABHA_LINK_OPTIONS[currentAbhaLinkOption].create - ? t("link_existing_abha_profile") - : t("create_new_abha_profile")} -

-
- -
-

- {t("try_different_abha_linking_option")} -

-
- {Object.values(ABHA_LINK_OPTIONS) - .filter( - (option) => - option.value !== currentAbhaLinkOption && - ABHA_LINK_OPTIONS[currentAbhaLinkOption]?.create === - option.create, - ) - .sort((a) => (a.disabled ? 1 : -1)) - .map((option) => ( - - setCurrentAbhaLinkOption( - option.value as keyof typeof ABHA_LINK_OPTIONS, - ) - } - ghost - tooltip={ - option.disabled - ? t("abha_link_options__disabled_tooltip") - : t(option.description) - } - disabled={option.disabled} - tooltipClassName="top-full mt-1" - className={classNames( - "w-full border border-gray-400 text-secondary-800", - !option.disabled && "hover:border-primary-100", - )} - > - {t(option.title)} - - ))} -
-
-
- ); -} diff --git a/src/components/ABDM/LinkAbhaNumber/useMultiStepForm.ts b/src/components/ABDM/LinkAbhaNumber/useMultiStepForm.ts deleted file mode 100644 index 79bc9ec71c6..00000000000 --- a/src/components/ABDM/LinkAbhaNumber/useMultiStepForm.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - Dispatch, - ReactElement, - SetStateAction, - cloneElement, - useCallback, - useMemo, - useState, -} from "react"; - -export interface InjectedStepProps { - currentStepIndex: number; - isFirstStep: boolean; - isLastStep: boolean; - next: () => void; - prev: () => void; - goTo: (step: number) => void; - memory: T | null; - setMemory: Dispatch>; -} - -export default function useMultiStepForm( - steps: ReactElement[], - initialValues?: T, -) { - const [currentStepIndex, setCurrentStepIndex] = useState(0); - const [memory, setMemory] = useState(initialValues as T); - - const next = useCallback( - () => - setCurrentStepIndex((prev) => - steps.length - 1 > prev ? prev + 1 : prev, - ), - [steps.length], - ); - - const prev = useCallback( - () => setCurrentStepIndex((prev) => (prev > 0 ? prev - 1 : prev)), - [], - ); - - const goTo = useCallback( - (step: number) => - setCurrentStepIndex((prev) => - step >= 0 && step <= steps.length - 1 ? step : prev, - ), - [steps.length], - ); - - const options = useMemo( - () => ({ - currentStepIndex, - isFirstStep: currentStepIndex === 0, - isLastStep: currentStepIndex === steps.length - 1, - next, - prev, - goTo, - memory, - setMemory, - }), - [currentStepIndex, memory, next, prev, goTo, steps.length], - ); - - const currentStep = cloneElement(steps[currentStepIndex], { - ...options, - ...steps[currentStepIndex].props, - }); - - return { currentStep, ...options }; -} diff --git a/src/components/ABDM/types/abha.ts b/src/components/ABDM/types/abha.ts deleted file mode 100644 index f189bd111cb..00000000000 --- a/src/components/ABDM/types/abha.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { PatientModel } from "@/components/Patient/models"; - -export type AbhaNumberModel = { - id: number; - external_id: string; - created_date: string; - modified_date: string; - abha_number: string; - health_id: string; - name: string; - first_name: string | null; - middle_name: string | null; - last_name: string | null; - gender: "F" | "M" | "O"; - date_of_birth: string | null; - address: string | null; - district: string | null; - state: string | null; - pincode: string | null; - mobile: string | null; - email: string | null; - profile_photo: string | null; - new: boolean; - patient: string | null; - patient_object: PatientModel | null; -}; - -export type ABHAQRContent = { - hidn: string; - name: string; - gender: "M" | "F" | "O"; - dob: string; - mobile: string; - address: string; - distlgd: string; - statelgd: string; -} & ({ hid: string; phr?: never } | { phr: string; hid?: never }) & - ( - | { district_name: string; "dist name"?: never } - | { "dist name": string; district_name?: never } - ) & - ( - | { state_name: string; "state name"?: never } - | { "state name": string; state_name?: never } - ); diff --git a/src/components/ABDM/types/consent.ts b/src/components/ABDM/types/consent.ts deleted file mode 100644 index a5041f5767d..00000000000 --- a/src/components/ABDM/types/consent.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; -import { UserBaseModel } from "@/components/Users/models"; - -export type ConsentPurpose = - | "CAREMGT" - | "BTG" - | "PUBHLTH" - | "HPAYMT" - | "DSRCH" - | "PATRQT"; - -export type ConsentStatus = - | "REQUESTED" - | "GRANTED" - | "DENIED" - | "EXPIRED" - | "REVOKED"; - -export type ConsentHIType = - | "Prescription" - | "DiagnosticReport" - | "OPConsultation" - | "DischargeSummary" - | "ImmunizationRecord" - | "HealthDocumentRecord" - | "WellnessRecord"; - -export type ConsentAccessMode = "VIEW" | "STORE" | "QUERY" | "STREAM"; - -export type ConsentFrequencyUnit = "HOUR" | "DAY" | "WEEK" | "MONTH" | "YEAR"; - -export type ConsentCareContext = { - patientReference: string; - careContextReference: string; -}; - -export type ConsentModel = { - id: string; - consent_id: null | string; - - patient_abha: string; - care_contexts: ConsentCareContext[]; - - status: ConsentStatus; - purpose: ConsentPurpose; - hi_types: ConsentHIType[]; - - access_mode: ConsentAccessMode; - from_time: string; - to_time: string; - expiry: string; - - frequency_unit: ConsentFrequencyUnit; - frequency_value: number; - frequency_repeats: number; - - hip: null | string; - hiu: null | string; - - created_date: string; - modified_date: string; -}; - -export type CreateConsentTBody = { - patient_abha: string; - hi_types: ConsentHIType[]; - purpose: ConsentPurpose; - from_time: Date | string; - to_time: Date | string; - expiry: Date | string; - - access_mode?: ConsentAccessMode; - frequency_unit?: ConsentFrequencyUnit; - frequency_value?: number; - frequency_repeats?: number; - hip?: null | string; -}; - -export type ConsentArtefactModel = { - consent_request: string; - - cm: null | string; -} & ConsentModel; - -export type ConsentRequestModel = { - requester: UserBaseModel; - patient_abha_object: AbhaNumberModel; - consent_artefacts: ConsentArtefactModel[]; -} & ConsentModel; diff --git a/src/components/ABDM/types/health-facility.ts b/src/components/ABDM/types/health-facility.ts deleted file mode 100644 index 419003dbf8e..00000000000 --- a/src/components/ABDM/types/health-facility.ts +++ /dev/null @@ -1,19 +0,0 @@ -export interface IHealthFacility { - id: string; - registered: boolean; - external_id: string; - created_date: string; - modified_date: string; - hf_id: string; - facility: string; - detail?: string; -} - -export interface IcreateHealthFacilityTBody { - facility: string; - hf_id: string; -} - -export interface IpartialUpdateHealthFacilityTBody { - hf_id: string; -} diff --git a/src/components/ABDM/types/health-information.ts b/src/components/ABDM/types/health-information.ts deleted file mode 100644 index eeb1c53a4fb..00000000000 --- a/src/components/ABDM/types/health-information.ts +++ /dev/null @@ -1,6 +0,0 @@ -export type HealthInformationModel = { - data: { - content: string; - care_context_reference: string; - }[]; -}; diff --git a/src/components/Facility/ConsultationDetails/ConsultationContext.tsx b/src/components/Facility/ConsultationDetails/ConsultationContext.tsx new file mode 100644 index 00000000000..174b20536a5 --- /dev/null +++ b/src/components/Facility/ConsultationDetails/ConsultationContext.tsx @@ -0,0 +1,73 @@ +import { ReactNode, createContext, useContext, useState } from "react"; + +import { ConsultationModel } from "@/components/Facility/models"; +import { PatientModel } from "@/components/Patient/models"; + +import { PLUGIN_Component } from "@/PluginEngine"; + +interface ConsultationContextBase { + consultation?: ConsultationModel; + patient?: PatientModel; +} + +type ConsultationContextType = ConsultationContextBase & + T & { + setValue: ( + key: K, + value: (ConsultationContextBase & T)[K], + ) => void; + }; + +const ConsultationContext = createContext< + ConsultationContextType | undefined +>(undefined); + +export const useConsultation = () => { + const context = useContext(ConsultationContext); + + if (!context) { + throw new Error( + "'useConsultation' must be used within 'ConsultationProvider' only", + ); + } + + return context as ConsultationContextType; +}; + +interface ConsultationProviderProps { + children: ReactNode; + initialContext?: Partial; +} + +export const ConsultationProvider = ({ + children, + initialContext = {}, +}: ConsultationProviderProps) => { + const [state, setState] = useState( + initialContext as ConsultationContextBase & T, + ); + + const setValue = ( + key: K, + value: (ConsultationContextBase & T)[K], + ) => { + setState((prevState) => ({ + ...prevState, + [key]: value, + })); + }; + + return ( + + } + > + + {children} + + ); +}; diff --git a/src/components/Facility/ConsultationDetails/index.tsx b/src/components/Facility/ConsultationDetails/index.tsx index 496fd8f5d02..ec26dfe1a1d 100644 --- a/src/components/Facility/ConsultationDetails/index.tsx +++ b/src/components/Facility/ConsultationDetails/index.tsx @@ -1,9 +1,7 @@ -import { Link, navigate, useQueryParams } from "raviger"; +import { Link, navigate } from "raviger"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import ABDMRecordsTab from "@/components/ABDM/ABDMRecordsTab"; -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; import Loading from "@/components/Common/Loading"; import PageTitle from "@/components/Common/PageTitle"; import RelativeDateUserMention from "@/components/Common/RelativeDateUserMention"; @@ -29,6 +27,7 @@ import PatientInfoCard from "@/components/Patient/PatientInfoCard"; import { PatientModel } from "@/components/Patient/models"; import useAuthUser from "@/hooks/useAuthUser"; +import { useCareAppConsultationTabs } from "@/hooks/useCareApps"; import { GENDER_TYPES } from "@/common/constants"; @@ -44,6 +43,8 @@ import { relativeTime, } from "@/Utils/utils"; +import { ConsultationProvider } from "./ConsultationContext"; + export interface ConsultationTabProps { consultationId: string; facilityId: string; @@ -52,7 +53,7 @@ export interface ConsultationTabProps { patientData: PatientModel; } -const TABS = { +const defaultTabs = { UPDATES: ConsultationUpdatesTab, FEED: ConsultationFeedTab, SUMMARY: ConsultationSummaryTab, @@ -66,20 +67,27 @@ const TABS = { NUTRITION: ConsultationNutritionTab, PRESSURE_SORE: ConsultationPressureSoreTab, DIALYSIS: ConsultationDialysisTab, - ABDM: ABDMRecordsTab, -}; +} as Record>; export const ConsultationDetails = (props: any) => { const { facilityId, patientId, consultationId } = props; const { t } = useTranslation(); + const [tabs, setTabs] = + useState>>(defaultTabs); + const pluginTabs = useCareAppConsultationTabs(); + + useEffect(() => { + if (pluginTabs) { + setTabs((prev) => ({ ...prev, ...pluginTabs })); + } + }, [pluginTabs]); + let tab = undefined; - if (Object.keys(TABS).includes(props.tab.toUpperCase())) { - tab = props.tab.toUpperCase() as keyof typeof TABS; + if (Object.keys(tabs).includes(props.tab.toUpperCase())) { + tab = props.tab.toUpperCase(); } const [showDoctors, setShowDoctors] = useState(false); - const [qParams, _] = useQueryParams(); const [patientData, setPatientData] = useState(); - const [abhaNumberData, setAbhaNumberData] = useState(); const [activeShiftingData, setActiveShiftingData] = useState>([]); const getPatientGender = (patientData: any) => @@ -144,16 +152,6 @@ export const ConsultationDetails = (props: any) => { const fetchData = useCallback( async (id: string) => { - // Get abha number data - const { data: abhaNumberData } = await request( - routes.abdm.abhaNumber.get, - { - pathParams: { abhaNumberId: id ?? "" }, - silent: true, - }, - ); - setAbhaNumberData(abhaNumberData); - // Get shifting data const shiftRequestsQuery = await request(routes.listShiftRequests, { query: { patient: id }, @@ -194,7 +192,7 @@ export const ConsultationDetails = (props: any) => { return ; } - const SelectedTab = TABS[tab]; + const SelectedTab = tabs[tab]; const tabButtonClasses = (selected: boolean) => `capitalize min-w-max-content cursor-pointer font-bold whitespace-nowrap ${ @@ -204,217 +202,222 @@ export const ConsultationDetails = (props: any) => { }`; return ( -
+
- -
-
- { - consultationQuery.refetch(); - patientDataQuery.refetch(); +
+ +
+
+ { + consultationQuery.refetch(); + patientDataQuery.refetch(); + }} + consultationId={consultationId} + activeShiftingData={activeShiftingData} + /> -
- {consultationData.admitted_to && ( -
-
- Patient - {consultationData.discharge_date - ? " Discharged from" - : " Admitted to"} - - {consultationData.admitted_to} - -
- {(consultationData.discharge_date ?? - consultationData.encounter_date) && ( -
- {relativeTime( - consultationData.discharge_date - ? consultationData.discharge_date - : consultationData.encounter_date, - )} +
+ {consultationData.admitted_to && ( +
+
+ Patient + {consultationData.discharge_date + ? " Discharged from" + : " Admitted to"} + + {consultationData.admitted_to} + +
+ {(consultationData.discharge_date ?? + consultationData.encounter_date) && ( +
+ {relativeTime( + consultationData.discharge_date + ? consultationData.discharge_date + : consultationData.encounter_date, + )} +
+ )} +
+ {consultationData.encounter_date && + formatDateTime(consultationData.encounter_date)} + {consultationData.discharge_date && + ` - ${formatDateTime(consultationData.discharge_date)}`}
- )} -
- {consultationData.encounter_date && - formatDateTime(consultationData.encounter_date)} - {consultationData.discharge_date && - ` - ${formatDateTime(consultationData.discharge_date)}`}
-
- )} -
-
-
-
- Created:   - -
+ )}
-
-
- Last Modified: -   - +
+
+
+ Created:   + +
+
+
+
+ Last Modified: +   + +
-
- {!!consultationData.diagnoses?.length && ( -
-
- + {!!consultationData.diagnoses?.length && ( +
+
+ +
-
- )} -
-
-
- +
+
- -
- - - {showPatientNotesPopup && ( - - )} -
+ + {showPatientNotesPopup && ( + + )} +
+ ); }; diff --git a/src/components/Facility/FacilityConfigure.tsx b/src/components/Facility/FacilityConfigure.tsx index 78d2edd3058..1cb7c7d69cf 100644 --- a/src/components/Facility/FacilityConfigure.tsx +++ b/src/components/Facility/FacilityConfigure.tsx @@ -2,13 +2,13 @@ import { t } from "i18next"; import { navigate } from "raviger"; import { useReducer, useState } from "react"; -import { ConfigureHealthFacility } from "@/components/ABDM/ConfigureHealthFacility"; import { Submit } from "@/components/Common/ButtonV2"; import Loading from "@/components/Common/Loading"; import Page from "@/components/Common/Page"; import TextFormField from "@/components/Form/FormFields/TextFormField"; import { FieldChangeEvent } from "@/components/Form/FormFields/Utils"; +import { PLUGIN_Component } from "@/PluginEngine"; import * as Notification from "@/Utils/Notifications"; import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; @@ -163,7 +163,10 @@ export const FacilityConfigure = (props: IProps) => {
- +
); diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx index 843b958c67b..02c3f5386cb 100644 --- a/src/components/Facility/FacilityHome.tsx +++ b/src/components/Facility/FacilityHome.tsx @@ -38,6 +38,7 @@ import { USER_TYPES, } from "@/common/constants"; +import { PLUGIN_Component } from "@/PluginEngine"; import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; import * as Notification from "@/Utils/Notifications"; import { CameraFeedPermittedUserTypes } from "@/Utils/permissions"; @@ -423,13 +424,10 @@ export const FacilityHome = ({ facilityId }: Props) => { > {t("view_users")} - navigate(`/facility/${facilityId}/abdm`)} - icon={} - > - {t("view_abdm_records")} - + {hasPermissionToDeleteFacility ? ( { return `${skills[0]}, ${skills[1]} and ${skills.length - 2} other skills...`; }; -export default function PatientInfoCard(props: { +export interface PatientInfoCardProps { patient: PatientModel; consultation?: ConsultationModel; - abhaNumber?: AbhaNumberModel; fetchPatientData?: (state: { aborted: boolean }) => void; activeShiftingData: any; consultationId: string; - showAbhaProfile?: boolean; -}) { +} + +export default function PatientInfoCard(props: PatientInfoCardProps) { const authUser = useAuthUser(); const { t } = useTranslation(); const [open, setOpen] = useState(false); - const [showLinkABHANumber, setShowLinkABHANumber] = useState(false); - const [showABHAProfile, setShowABHAProfile] = useState( - !!props.showAbhaProfile, - ); - const [showFetchABDMRecords, setShowFetchABDMRecords] = useState(false); const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = useState(false); const [openDischargeDialog, setOpenDischargeDialog] = useState(false); @@ -149,11 +130,6 @@ export default function PatientInfoCard(props: { prefetch: !!consultation?.treating_physician_object?.username, }); - const { data: healthFacility } = useQuery(routes.abdm.healthFacility.get, { - pathParams: { facility_id: patient.facility ?? "" }, - silent: true, - }); - return ( <> -
- {careConfig.abdm.enabled && - (props.abhaNumber ? ( - <> - - {({ close }) => ( - <> -
{ - close(); - setShowABHAProfile(true); - triggerGoal("Patient Card Button Clicked", { - buttonName: t("show_abha_profile"), - consultationId: consultation?.id, - userId: authUser?.id, - }); - }} - > - - {t("show_abha_profile")} -
-
{ - close(); - setShowFetchABDMRecords(true); - triggerGoal("Patient Card Button Clicked", { - buttonName: t("hi__fetch_records"), - consultationId: consultation?.id, - userId: authUser?.id, - }); - }} - > - - {t("hi__fetch_records")} -
- - )} -
- - ) : ( - - - - - {({ close, disabled }) => ( -
{ - close(); - setShowLinkABHANumber(true); - }} - > - - -

{t("generate_link_abha")}

-
-
- )} -
-
- - {!healthFacility && ( - - {t("abha_disabled_due_to_no_health_facility")} - - )} -
-
- ))} -
{!consultation?.discharge_date && ( @@ -966,46 +858,8 @@ export default function PatientInfoCard(props: {
- setShowLinkABHANumber(false)} - onSuccess={async (abhaProfile) => { - const { res, data } = await request( - routes.abdm.healthId.linkAbhaNumberAndPatient, - { - body: { - patient: patient.id, - abha_number: abhaProfile.external_id, - }, - }, - ); - - if (res?.status === 200 && data) { - Notification.Success({ - msg: t("abha_number_linked_successfully"), - }); - props.fetchPatientData?.({ aborted: false }); - setShowLinkABHANumber(false); - setShowABHAProfile(true); - } else { - Notification.Error({ - msg: t("failed_to_link_abha_number"), - }); - } - }} - /> - setShowABHAProfile(false)} - /> - setShowFetchABDMRecords(false)} - /> + ); } diff --git a/src/components/Patient/PatientRegister.tsx b/src/components/Patient/PatientRegister.tsx index 4dc8b4965bc..5cc958d44de 100644 --- a/src/components/Patient/PatientRegister.tsx +++ b/src/components/Patient/PatientRegister.tsx @@ -7,16 +7,6 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; -import { Button } from "@/components/ui/button"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; - -import LinkAbhaNumber from "@/components/ABDM/LinkAbhaNumber/index"; -import { AbhaNumberModel } from "@/components/ABDM/types/abha"; import AccordionV2 from "@/components/Common/AccordionV2"; import ButtonV2 from "@/components/Common/ButtonV2"; import CollapseV2 from "@/components/Common/CollapseV2"; @@ -40,7 +30,6 @@ import { RequiredFieldValidator, } from "@/components/Form/FieldValidators"; import Form from "@/components/Form/Form"; -import { FormContextValue } from "@/components/Form/FormContext"; import AutocompleteFormField from "@/components/Form/FormFields/Autocomplete"; import CheckBoxFormField from "@/components/Form/FormFields/CheckBoxFormField"; import DateFormField from "@/components/Form/FormFields/DateFormField"; @@ -80,8 +69,10 @@ import countryList from "@/common/static/countries.json"; import { statusType, useAbortableEffect } from "@/common/utils"; import { validatePincode } from "@/common/validation"; +import { PLUGIN_Component } from "@/PluginEngine"; import { RestoreDraftButton } from "@/Utils/AutoSave"; import * as Notification from "@/Utils/Notifications"; +import { usePubSub } from "@/Utils/pubsubContext"; import routes from "@/Utils/request/api"; import request from "@/Utils/request/request"; import useQuery from "@/Utils/request/useQuery"; @@ -94,7 +85,7 @@ import { scrollTo, } from "@/Utils/utils"; -type PatientForm = PatientModel & +export type PatientForm = PatientModel & PatientMeta & { age?: number; is_postpartum?: boolean }; interface PatientRegisterProps extends PatientModel { @@ -156,7 +147,6 @@ const initForm: any = { number_of_doses: "0", vaccine_name: null, last_vaccinated_date: null, - abha_number: null, ...medicalHistoryChoices, ration_card_category: null, }; @@ -226,7 +216,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { patientList: Array; }>({ patientList: [] }); const [patientName, setPatientName] = useState(""); - const [showLinkAbhaNumberModal, setShowLinkAbhaNumberModal] = useState(false); const [showAutoFilledPincode, setShowAutoFilledPincode] = useState(false); const [insuranceDetails, setInsuranceDetails] = useState( [], @@ -236,6 +225,8 @@ export const PatientRegister = (props: PatientRegisterProps) => { const [insuranceDetailsError, setInsuranceDetailsError] = useState(); + const { publish } = usePubSub(); + const headerText = !id ? "Add Details of Patient" : "Update Patient Details"; const buttonText = !id ? "Add Patient" : "Save Details"; @@ -287,13 +278,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { const { res, data } = await request(routes.getPatient, { pathParams: { id: id ? id : 0 }, }); - const { data: abhaNumberData } = await request( - routes.abdm.abhaNumber.get, - { - pathParams: { abhaNumberId: id ?? "" }, - silent: true, - }, - ); if (!status.aborted) { if (res?.ok && data) { @@ -306,8 +290,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { age: data.year_of_birth ? new Date().getFullYear() - data.year_of_birth : "", - health_id_number: abhaNumberData?.abha_number || "", - health_id: abhaNumberData?.health_id || "", nationality: data.nationality ? data.nationality : "India", gender: data.gender ? data.gender : undefined, state: data.state ? data.state : "", @@ -392,11 +374,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { [id], ); - const { data: healthFacility } = useQuery(routes.abdm.healthFacility.get, { - pathParams: { facility_id: facilityId }, - silent: true, - }); - useQuery(routes.hcx.policies.list, { query: { patient: id, @@ -708,27 +685,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { controllerRef: submitController, }); if (res?.ok && requestData) { - if (state.form.abha_number) { - const { res, data } = await request( - routes.abdm.healthId.linkAbhaNumberAndPatient, - { - body: { - patient: requestData.id, - abha_number: state.form.abha_number, - }, - }, - ); - - if (res?.status === 200 && data) { - Notification.Success({ - msg: t("abha_number_linked_successfully"), - }); - } else { - Notification.Error({ - msg: t("failed_to_link_abha_number"), - }); - } - } + publish("patient:upsert", requestData); await Promise.all( insuranceDetails.map(async (obj) => { @@ -769,68 +726,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { setIsLoading(false); }; - const populateAbhaValues = ( - abhaProfile: AbhaNumberModel, - field: FormContextValue, - ) => { - const values = { - abha_number: abhaProfile.external_id, - health_id_number: abhaProfile.abha_number, - health_id: abhaProfile.health_id, - }; - - if (abhaProfile.name) - field("name").onChange({ - name: "name", - value: abhaProfile.name, - }); - - if (abhaProfile.mobile) { - field("phone_number").onChange({ - name: "phone_number", - value: parsePhoneNumber(abhaProfile.mobile, "IN"), - }); - - field("emergency_phone_number").onChange({ - name: "emergency_phone_number", - value: parsePhoneNumber(abhaProfile.mobile, "IN"), - }); - } - - if (abhaProfile.gender) - field("gender").onChange({ - name: "gender", - value: { M: "1", F: "2", O: "3" }[abhaProfile.gender], - }); - - if (abhaProfile.date_of_birth) - field("date_of_birth").onChange({ - name: "date_of_birth", - value: new Date(abhaProfile.date_of_birth), - }); - - if (abhaProfile.pincode) - field("pincode").onChange({ - name: "pincode", - value: abhaProfile.pincode, - }); - - if (abhaProfile.address) { - field("address").onChange({ - name: "address", - value: abhaProfile.address, - }); - - field("permanent_address").onChange({ - name: "permanent_address", - value: abhaProfile.address, - }); - } - - dispatch({ type: "set_form", form: { ...state.form, ...values } }); - setShowLinkAbhaNumberModal(false); - }; - const handleMedicalCheckboxChange = (e: any, id: number, field: any) => { const values = field("medical_history").value ?? []; if (e.value) { @@ -1038,32 +933,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { result in duplication of patient records.

- {!state.form.abha_number && ( -
- - - - - - {!healthFacility && ( - - {t("abha_disabled_due_to_no_health_facility")} - - )} - - -
- )} {showAlertMessage.show && ( { show /> )} - {careConfig.abdm.enabled && ( -
- {showLinkAbhaNumberModal && ( - setShowLinkAbhaNumberModal(false)} - onSuccess={(data) => { - if (id) { - Notification.Warn({ - msg: "To link Abha Number, please save the patient details", - }); - } - - populateAbhaValues(data, field); - }} - /> - )} - {state.form.abha_number && ( -
-
- null} - disabled={true} - error="" - /> -
-
- {state.form.health_id ? ( - null} - disabled={true} - error="" - /> - ) : ( -
- No Abha Address Associated with this ABHA Number -
- )} -
-
- )} -
- )} +

Personal Details diff --git a/src/hooks/useCareApps.ts b/src/hooks/useCareApps.ts index 5d0de6f3b9f..6870736861a 100644 --- a/src/hooks/useCareApps.ts +++ b/src/hooks/useCareApps.ts @@ -24,6 +24,14 @@ export const useCareAppNavItems = () => { return navItems; }; +export const useCareAppConsultationTabs = () => { + const careApps = useCareApps(); + + return careApps.reduce((acc, app) => { + return { ...acc, ...(app.consultationTabs ?? {}) }; + }, {}); +}; + // If required; Reduce plugin.routes to a single pluginRoutes object of type Record JSX.Element> export function usePluginRoutes() { const careApps = useCareApps(); diff --git a/src/pluginTypes.ts b/src/pluginTypes.ts index 13886b5b7d5..6a10784926e 100644 --- a/src/pluginTypes.ts +++ b/src/pluginTypes.ts @@ -1,12 +1,16 @@ import { LazyExoticComponent } from "react"; import { INavItem } from "@/components/Common/Sidebar/Sidebar"; -import { ConsultationModel } from "@/components/Facility/models"; -import { PatientModel } from "@/components/Patient/models"; +import { ConsultationModel, FacilityModel } from "@/components/Facility/models"; import { UserAssignedModel } from "@/components/Users/models"; -import { AppRoutes } from "@/Routers/AppRouter"; -import { pluginMap } from "@/pluginMap"; +import { AppRoutes } from "./Routers/AppRouter"; +import { ConsultationTabProps } from "./components/Facility/ConsultationDetails"; +import { FormContextValue } from "./components/Form/FormContext"; +import { PatientInfoCardProps } from "./components/Patient/PatientInfoCard"; +import { PatientForm } from "./components/Patient/PatientRegister"; +import { PatientModel } from "./components/Patient/models"; +import { pluginMap } from "./pluginMap"; // Define the available plugins export type AvailablePlugin = "@apps/care_livekit_fe" | "@apps/care_hcx_fe"; @@ -19,6 +23,8 @@ export type DoctorConnectButtonComponentType = React.FC<{ user: UserAssignedModel; }>; +export type ExtendPatientInfoCardComponentType = React.FC; + export type ManagePatientOptionsComponentType = React.FC<{ consultation: ConsultationModel | undefined; patient: PatientModel; @@ -28,11 +34,39 @@ export type AdditionalDischargeProceduresComponentType = React.FC<{ consultation: ConsultationModel; }>; +export type ManageFacilityOptionsComponentType = React.FC<{ + facility?: FacilityModel; +}>; + +export type ExtendFacilityConfigureComponentType = React.FC<{ + facilityId: string; +}>; + +export type ExtendPatientRegisterFormComponentType = React.FC<{ + facilityId: string; + patientId?: string; + state: { + form: { + [key: string]: any; + }; + errors: { + [key: string]: string; + }; + }; + dispatch: React.Dispatch; + field: FormContextValue; +}>; + // Define supported plugin components export type SupportedPluginComponents = { DoctorConnectButtons: DoctorConnectButtonComponentType; + ExtendPatientInfoCard: ExtendPatientInfoCardComponentType; ManagePatientOptions: ManagePatientOptionsComponentType; AdditionalDischargeProcedures: AdditionalDischargeProceduresComponentType; + ManageFacilityOptions: ManageFacilityOptionsComponentType; + ConsultationContextEnabler: React.FC; + ExtendFacilityConfigure: ExtendFacilityConfigureComponentType; + ExtendPatientRegisterForm: ExtendPatientRegisterFormComponentType; }; // Create a type for lazy-loaded components @@ -56,6 +90,10 @@ export type PluginManifest = { extends: SupportedPluginExtensions[]; components: PluginComponentMap; navItems: INavItem[]; + consultationTabs?: Record< + string, + LazyComponent> + >; }; // Create a type that ensures only available plugins can be used