diff --git a/cypress/e2e/assets_spec/asset_homepage.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts index e1fcf278442..61f7d8a52c0 100644 --- a/cypress/e2e/assets_spec/asset_homepage.cy.ts +++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts @@ -90,7 +90,7 @@ describe("Asset Tab", () => { assetPage.selectImportOption(); assetPage.selectImportFacility("Dummy Facility 1"); assetPage.importAssetFile(); - assetPage.selectImportLocation("Camera Loc"); + assetPage.selectImportLocation("Camera Locations"); assetPage.clickImportAsset(); }); diff --git a/cypress/e2e/assets_spec/assets_manage.cy.ts b/cypress/e2e/assets_spec/assets_manage.cy.ts index 34c554b374e..8b440f68761 100644 --- a/cypress/e2e/assets_spec/assets_manage.cy.ts +++ b/cypress/e2e/assets_spec/assets_manage.cy.ts @@ -5,6 +5,12 @@ import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; +function addDaysToDate(numberOfDays: number) { + const inputDate = new Date(); + inputDate.setDate(inputDate.getDate() + numberOfDays); + return inputDate.toISOString().split("T")[0]; +} + describe("Asset", () => { const assetPage = new AssetPage(); const loginPage = new LoginPage(); @@ -26,6 +32,33 @@ describe("Asset", () => { cy.awaitUrl("/assets"); }); + it("Verify Asset Warranty Expiry Label", () => { + assetSearchPage.typeSearchKeyword(assetname); + assetSearchPage.pressEnter(); + assetSearchPage.verifyBadgeContent(assetname); + assetSearchPage.clickAssetByName(assetname); + assetPage.clickupdatedetailbutton(); + assetPage.scrollintoWarrantyDetails(); + assetPage.enterWarrantyExpiryDate(addDaysToDate(100)); // greater than 3 months + assetPage.clickassetupdatebutton(); + assetPage.verifyWarrantyExpiryLabel(""); + assetPage.clickupdatedetailbutton(); + assetPage.scrollintoWarrantyDetails(); + assetPage.enterWarrantyExpiryDate(addDaysToDate(80)); // less than 3 months + assetPage.clickassetupdatebutton(); + assetPage.verifyWarrantyExpiryLabel("3 months"); + assetPage.clickupdatedetailbutton(); + assetPage.scrollintoWarrantyDetails(); + assetPage.enterWarrantyExpiryDate(addDaysToDate(20)); // less than 1 month + assetPage.clickassetupdatebutton(); + assetPage.verifyWarrantyExpiryLabel("1 month"); + assetPage.clickupdatedetailbutton(); + assetPage.scrollintoWarrantyDetails(); + assetPage.enterWarrantyExpiryDate(addDaysToDate(100)); // check for greater than 3 months again to verify the label is removed + assetPage.clickassetupdatebutton(); + assetPage.verifyWarrantyExpiryLabel(""); + }); + it("Create & Edit a service history and verify reflection", () => { assetSearchPage.typeSearchKeyword(assetname); assetSearchPage.pressEnter(); diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts index 45356a1a4e7..93bbc87c9ab 100644 --- a/cypress/pageobject/Asset/AssetCreation.ts +++ b/cypress/pageobject/Asset/AssetCreation.ts @@ -150,7 +150,7 @@ export class AssetPage { configureVitalAsset(hostName: string, localIp: string) { cy.get("[data-testid=asset-configure-button]").click(); - cy.get("#middlewareHostname").type(hostName); + cy.get("#middleware_hostname").type(hostName); cy.get("#localipAddress").type(localIp); } @@ -285,16 +285,39 @@ export class AssetPage { cy.get("#notes").scrollIntoView(); } - enterAssetNotes(text) { + enterAssetNotes(text: string) { cy.get("#notes").click().clear(); cy.get("#notes").click().type(text); } - enterAssetservicedate(text) { + enterAssetservicedate(text: string) { cy.get("input[name='last_serviced_on']").click(); cy.get("#date-input").click().type(text); } + scrollintoWarrantyDetails() { + cy.get("#warranty-details").scrollIntoView(); + } + + enterWarrantyExpiryDate(text: string) { + cy.get("#WarrantyAMCExpiry").click(); + cy.get("#WarrantyAMCExpiry").click().type(text); + } + + verifyWarrantyExpiryLabel(duration: string) { + if (duration === "") { + cy.get("#warranty-amc-expired-red").should("not.exist"); + cy.get("#warranty-amc-expiring-soon-orange").should("not.exist"); + cy.get("#warranty-amc-expiring-soon-yellow").should("not.exist"); + } else if (duration === "expired") { + cy.get("#warranty-amc-expired-red").should("be.visible"); + } else if (duration === "1 month") { + cy.get("#warranty-amc-expiring-soon-orange").should("be.visible"); + } else if (duration === "3 months") { + cy.get("#warranty-amc-expiring-soon-yellow").should("be.visible"); + } + } + clickassetupdatebutton() { cy.get("#submit").click(); } diff --git a/package-lock.json b/package-lock.json index a53c3ee91c1..0eb65fca5bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "events": "^3.3.0", "i18next": "^23.2.7", "i18next-browser-languagedetector": "^7.1.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "postcss-loader": "^7.3.3", "qrcode.react": "^3.1.0", "raviger": "^4.1.2", @@ -74,7 +74,7 @@ "@types/cypress": "^1.1.3", "@types/echarts": "^4.9.18", "@types/google.maps": "^3.53.4", - "@types/lodash": "^4.14.195", + "@types/lodash-es": "^4.17.9", "@types/lodash.get": "^4.4.7", "@types/node": "^20.4.0", "@types/prop-types": "*", @@ -5484,6 +5484,15 @@ "integrity": "sha512-22y3o88f4a94mKljsZcanlNWPzO0uBsBdzLAngf2tp533LzZcQzb6+eZPJ+vCTt+bqF2XnvT9gejTLsAcJAJyQ==", "dev": true }, + "node_modules/@types/lodash-es": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.9.tgz", + "integrity": "sha512-ZTcmhiI3NNU7dEvWLZJkzG6ao49zOIjEgIE0RgV7wbPxU0f2xT3VSAHw2gmst8swH6V0YkLRGp4qPlX/6I90MQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/lodash.get": { "version": "4.4.7", "resolved": "https://registry.npmjs.org/@types/lodash.get/-/lodash.get-4.4.7.tgz", @@ -12455,6 +12464,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "node_modules/lodash.castarray": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", diff --git a/package.json b/package.json index 4216ade3c4a..4b25aa28963 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "events": "^3.3.0", "i18next": "^23.2.7", "i18next-browser-languagedetector": "^7.1.0", - "lodash": "^4.17.21", + "lodash-es": "^4.17.21", "postcss-loader": "^7.3.3", "qrcode.react": "^3.1.0", "raviger": "^4.1.2", @@ -114,7 +114,7 @@ "@types/cypress": "^1.1.3", "@types/echarts": "^4.9.18", "@types/google.maps": "^3.53.4", - "@types/lodash": "^4.14.195", + "@types/lodash-es": "^4.17.9", "@types/lodash.get": "^4.4.7", "@types/node": "^20.4.0", "@types/prop-types": "*", diff --git a/public/config.json b/public/config.json index 8bd3179ec13..c3abfd442f5 100644 --- a/public/config.json +++ b/public/config.json @@ -5,12 +5,12 @@ "site_url": "care.coronasafe.in", "analytics_server_url": "https://plausible.10bedicu.in", "header_logo": { - "light":"https://cdn.coronasafe.network/header_logo.png", - "dark":"https://cdn.coronasafe.network/header_logo.png" + "light": "https://cdn.coronasafe.network/header_logo.png", + "dark": "https://cdn.coronasafe.network/header_logo.png" }, "main_logo": { - "light":"https://cdn.coronasafe.network/light-logo.svg", - "dark":"https://cdn.coronasafe.network/black-logo.svg" + "light": "https://cdn.coronasafe.network/light-logo.svg", + "dark": "https://cdn.coronasafe.network/black-logo.svg" }, "gmaps_api_key": "AIzaSyDsBAc3y7deI5ZO3NtK5GuzKwtUzQNJNUk", "gov_data_api_key": "579b464db66ec23bdd000001cdd3946e44ce4aad7209ff7b23ac571b", @@ -25,4 +25,5 @@ "enable_abdm": true, "jwt_token_refresh_interval": 300000, "use_webrtc": true + "enable_abdm": true } \ No newline at end of file diff --git a/src/CAREUI/display/Chip.tsx b/src/CAREUI/display/Chip.tsx index abc21711551..7bcbd6078e4 100644 --- a/src/CAREUI/display/Chip.tsx +++ b/src/CAREUI/display/Chip.tsx @@ -11,6 +11,7 @@ interface Props { text: string; tooltip?: string; className?: string; + id?: string; } export default function Chip({ @@ -21,6 +22,7 @@ export default function Chip({ }: Props) { return ( () { return ctx as PaginatedListContext; } -interface Props extends QueryOptions { +interface Props extends QueryOptions> { route: QueryRoute>; perPage?: number; children: (ctx: PaginatedListContext) => JSX.Element | JSX.Element[]; diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index e51494f6bac..0b08b4b9a42 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -499,6 +499,7 @@ export const TELEMEDICINE_ACTIONS = [ { id: 60, text: "COMPLETE", desc: "Complete" }, { id: 70, text: "REVIEW", desc: "Review" }, { id: 80, text: "NOT_REACHABLE", desc: "Not Reachable" }, + { id: 90, text: "DISCHARGE_RECOMMENDED", desc: "Discharge Recommended" }, ]; export const FRONTLINE_WORKER = [ diff --git a/src/Common/hooks/useAsyncOptions.ts b/src/Common/hooks/useAsyncOptions.ts index f0a1c895430..2f3f68d5c3a 100644 --- a/src/Common/hooks/useAsyncOptions.ts +++ b/src/Common/hooks/useAsyncOptions.ts @@ -1,4 +1,4 @@ -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { useMemo, useState } from "react"; import { useDispatch } from "react-redux"; diff --git a/src/Components/ABDM/ABHAProfileModal.tsx b/src/Components/ABDM/ABHAProfileModal.tsx index 0d6b7d9b90b..cc16fd45b8d 100644 --- a/src/Components/ABDM/ABHAProfileModal.tsx +++ b/src/Components/ABDM/ABHAProfileModal.tsx @@ -5,9 +5,9 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import DialogModal from "../Common/Dialog"; import QRCode from "qrcode.react"; import { formatDateTime } from "../../Utils/utils"; -import { getAbhaCard } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; import { useRef } from "react"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; interface IProps { patientId?: string; @@ -18,21 +18,24 @@ interface IProps { const ABHAProfileModal = ({ patientId, show, onClose, abha }: IProps) => { const printRef = useRef(null); - const dispatch = useDispatch(); const downloadAbhaCard = async (type: "pdf" | "png") => { if (!patientId) return; - const response = await dispatch(getAbhaCard(patientId, type)); + const { res, data } = await request(routes.abha.getAbhaCard, { + body: { + patient: patientId, + type: type, + }, + }); - if (response.status === 200 && response.data) { + if (res?.status === 200 && data) { if (type === "png") { const downloadLink = document.createElement("a"); - downloadLink.href = - "data:application/octet-stream;base64," + response.data; + downloadLink.href = "data:application/octet-stream;base64," + data; downloadLink.download = "abha.png"; downloadLink.click(); } else { - const htmlPopup = ``; + const htmlPopup = ``; const printWindow = window.open("", "PDF"); printWindow?.document.write(htmlPopup); diff --git a/src/Components/ABDM/ConfigureHealthFacility.tsx b/src/Components/ABDM/ConfigureHealthFacility.tsx index 025a3f0e862..a2b8f254bcb 100644 --- a/src/Components/ABDM/ConfigureHealthFacility.tsx +++ b/src/Components/ABDM/ConfigureHealthFacility.tsx @@ -1,12 +1,12 @@ -import { lazy, useCallback, useEffect, useReducer, useState } from "react"; -import { useDispatch } from "react-redux"; - -import { healthFacilityActions } from "../../Redux/actions"; +import { lazy, useEffect, useReducer, useState } from "react"; import * as Notification from "../../Utils/Notifications.js"; import { navigate } from "raviger"; import { Cancel, Submit } from "../Common/components/ButtonV2"; import TextFormField from "../Form/FormFields/TextFormField"; import { classNames } from "../../Utils/utils"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import request from "../../Utils/request/request"; const Loading = lazy(() => import("../Common/Loading")); const initForm = { @@ -40,30 +40,28 @@ const FormReducer = (state = initialState, action: any) => { export const ConfigureHealthFacility = (props: any) => { const [state, dispatch] = useReducer(FormReducer, initialState); const { facilityId } = props; - const dispatchAction: any = useDispatch(); const [isLoading, setIsLoading] = useState(false); - const fetchData = useCallback(async () => { - if (facilityId) { - setIsLoading(true); - const res = await dispatchAction(healthFacilityActions.read(facilityId)); - - if (res?.status === 200 && res?.data) { - const formData = { - ...state.form, - hf_id: res.data.hf_id, - health_facility: res.data, - }; - dispatch({ type: "set_form", form: formData }); - } + const { + data: health_facility, + loading, + refetch, + } = useQuery(routes.abha.getHealthFacility, { + pathParams: { facility__external_id: facilityId }, + }); - setIsLoading(false); - } - }, [dispatchAction, facilityId]); + useEffect(() => { + const formData = { + ...state.form, + hf_id: health_facility?.hf_id, + health_facility: health_facility, + }; + dispatch({ type: "set_form", form: formData }); + }, [health_facility]); useEffect(() => { - fetchData(); - }, [dispatch, fetchData]); + refetch(); + }, [dispatch, refetch]); const handleSubmit = async (e: any) => { e.preventDefault(); @@ -78,27 +76,43 @@ export const ConfigureHealthFacility = (props: any) => { return; } - let res = null; + let response = null; + let responseData = null; if (state.form.health_facility) { - res = await dispatchAction( - healthFacilityActions.partialUpdate(facilityId, { - hf_id: state.form.hf_id, - }) + const { res, data } = await request( + routes.abha.partialUpdateHealthFacility, + { + pathParams: { + facility__external_id: facilityId, + }, + body: { + hf_id: state.form.hf_id, + }, + } ); + response = res; + responseData = data; } else if (state.form.hf_id === state.form.health_facility?.hf_id) { - res = await dispatchAction( - healthFacilityActions.registerService(facilityId) + const { res, data } = await request( + routes.abha.registerHealthFacilityAsService, + { + pathParams: { + facility__external_id: facilityId, + }, + } ); + response = res; + responseData = data; - if (res?.status === 200 && res?.data) { - if (res.data?.registered) { + if (response?.status === 200 && responseData) { + if (responseData?.registered) { dispatch({ type: "set_form", form: { ...state.form, health_facility: { ...state.form?.health_facility, - registered: res.data.registered, + registered: responseData.registered, }, }, }); @@ -112,24 +126,26 @@ export const ConfigureHealthFacility = (props: any) => { }); return; } else { - res = await dispatchAction( - healthFacilityActions.create({ + const { res, data } = await request(routes.abha.createHealthFacility, { + body: { facility: facilityId, hf_id: state.form.hf_id, - }) - ); + }, + }); + response = res; + responseData = data; } setIsLoading(false); - if (res && res.data) { + if (response && responseData) { Notification.Success({ msg: "Health Facility config updated successfully", }); navigate(`/facility/${facilityId}`); } else { - if (res?.data) + if (responseData) Notification.Error({ - msg: "Something went wrong: " + (res.data.detail || ""), + msg: "Something went wrong: " + (responseData.detail || ""), }); } setIsLoading(false); @@ -142,7 +158,7 @@ export const ConfigureHealthFacility = (props: any) => { }); }; - if (isLoading) { + if (loading || isLoading) { return ; } diff --git a/src/Components/ABDM/LinkABHANumberModal.tsx b/src/Components/ABDM/LinkABHANumberModal.tsx index 30b1ff4afa3..367c9815db1 100644 --- a/src/Components/ABDM/LinkABHANumberModal.tsx +++ b/src/Components/ABDM/LinkABHANumberModal.tsx @@ -1,19 +1,6 @@ import * as Notify from "../../Utils/Notifications"; import Dropdown, { DropdownItem } from "../Common/components/Menu"; -import { - checkAndGenerateMobileOtp, - confirmWithAadhaarOtp, - confirmWithMobileOtp, - createHealthId, - generateAadhaarOtp, - initiateAbdmAuthentication, - linkViaQR, - resentAadhaarOtp, - searchByHealthId, - verifyAadhaarOtp, - verifyMobileOtp, -} from "../../Redux/actions"; import { useEffect, useState } from "react"; import ButtonV2 from "../Common/components/ButtonV2"; @@ -24,7 +11,8 @@ import OtpFormField from "../Form/FormFields/OtpFormField"; import QRScanner from "../Common/QRScanner"; import TextFormField from "../Form/FormFields/TextFormField"; import { classNames } from "../../Utils/utils"; -import { useDispatch } from "react-redux"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; export const validateRule = ( condition: boolean, @@ -144,8 +132,6 @@ const ScanABHAQRSection = ({ onSuccess, closeModal, }: ScanABHAQRSectionProps) => { - const dispatch = useDispatch(); - const [qrValue, setQrValue] = useState(""); const [authMethods, setAuthMethods] = useState([]); const [selectedAuthMethod, setSelectedAuthMethod] = useState(""); @@ -187,23 +173,23 @@ const ScanABHAQRSection = ({ try { const abha = JSON.parse(value); - const res = await dispatch(linkViaQR(abha, patientId)); + const { res, data } = await request(routes.abha.linkViaQR, { + body: { ...abha, patientId }, + }); if (res?.status === 200 || res?.status === 202) { Notify.Success({ msg: "Request sent successfully" }); onSuccess?.({ - ...res.data, + ...data, abha_profile: { - ...res.data?.abha_profile, - healthIdNumber: res.data?.abha_profile?.abha_number, - healthId: res.data?.abha_profile?.health_id, + ...data?.abha_profile, + healthIdNumber: data?.abha_profile?.abha_number, + healthId: data?.abha_profile?.health_id, mobile: abha?.mobile, monthOfBirth: - res.data?.abha_profile?.date_of_birth?.split("-")[1], - dayOfBirth: - res.data?.abha_profile?.date_of_birth?.split("-")[2], - yearOfBirth: - res.data?.abha_profile?.date_of_birth?.split("-")[0], + data?.abha_profile?.date_of_birth?.split("-")[1], + dayOfBirth: data?.abha_profile?.date_of_birth?.split("-")[2], + yearOfBirth: data?.abha_profile?.date_of_birth?.split("-")[0], }, }); } else { @@ -256,29 +242,55 @@ const ScanABHAQRSection = ({ disabled={otp.length !== 6} onClick={async () => { let response = null; + let Rdata = null; + let Rerror = null; switch (selectedAuthMethod) { case "MOBILE_OTP": - response = await dispatch( - confirmWithMobileOtp(txnId, otp, patientId) - ); + { + const { res, data, error } = await request( + routes.abha.confirmWithMobileOtp, + { + body: { + otp: otp, + txnId: txnId, + patientId: patientId, + }, + } + ); + response = res; + Rdata = data; + Rerror = error; + } break; case "AADHAAR_OTP": - response = await dispatch( - confirmWithAadhaarOtp(txnId, otp, patientId) - ); + { + const { res, data, error } = await request( + routes.abha.confirmWithAadhaarOtp, + { + body: { + otp: otp, + txnId: txnId, + patientId: patientId, + }, + } + ); + response = res; + Rdata = data; + Rerror = error; + } break; } - if (response.status === 200) { - onSuccess?.(response.data); + if (response?.status === 200) { + onSuccess?.(Rdata); Notify.Success({ msg: "ABHA Number linked successfully", }); } else { Notify.Error({ - msg: response?.message ?? "Something went wrong!", + msg: Rerror ?? "Something went wrong!", }); } }} @@ -290,13 +302,14 @@ const ScanABHAQRSection = ({ {authMethods.map((method) => ( { - const response = await dispatch( - initiateAbdmAuthentication(method, qrValue) + const { res, data } = await request( + routes.abha.initiateAbdmAuthentication, + { body: { authMethod: method, healthid: qrValue } } ); - if (response.status === 200 && response?.data?.txnId) { + if (res?.status === 200 && data?.txnId) { setSelectedAuthMethod(method); - setTxnId(response.data.txnId); + setTxnId(data.txnId); } }} > @@ -308,11 +321,18 @@ const ScanABHAQRSection = ({ { - const response = await dispatch(searchByHealthId(qrValue)); - - if (response.status === 200 && response?.data?.authMethods) { + const { res, data } = await request( + routes.abha.searchByHealthId, + { + body: { + healthId: qrValue, + }, + } + ); + + if (res?.status === 200 && data?.authMethods) { setAuthMethods( - response.data.authMethods?.filter?.((method: string) => + data.authMethods?.filter?.((method: string) => supportedAuthMethods.find( (supported) => supported === method ) @@ -339,8 +359,6 @@ const VerifyAadhaarSection = ({ onVerified, onSignin, }: VerifyAadhaarSectionProps) => { - const dispatch = useDispatch(); - const [aadhaarNumber, setAadhaarNumber] = useState(""); const [aadhaarNumberError, setAadhaarNumberError] = useState(); @@ -382,17 +400,22 @@ const VerifyAadhaarSection = ({ if (!validateAadhaar()) return; setIsSendingOtp(true); - const res = await dispatch(generateAadhaarOtp(aadhaarNumber)); + + const { res, data } = await request(routes.abha.generateAadhaarOtp, { + body: { + aadhaar: aadhaarNumber, + }, + }); setIsSendingOtp(false); - if (res.status === 200 && res.data) { - const { txnId } = res.data; + if (res?.status === 200 && data) { + const { txnId } = data; setTxnId(txnId); Notify.Success({ msg: "OTP has been sent to the mobile number registered with the Aadhar number.", }); } else { - Notify.Error({ msg: JSON.stringify(res.data) }); + Notify.Error({ msg: JSON.stringify(data) }); } }; @@ -400,16 +423,20 @@ const VerifyAadhaarSection = ({ if (!validateAadhaar() || !txnId) return; setIsSendingOtp(true); - const res = await dispatch(resentAadhaarOtp(txnId)); + const { res, data } = await request(routes.abha.generateAadhaarOtp, { + body: { + txnId: txnId, + }, + }); setIsSendingOtp(false); - if (res.status === 200 && res.data.txnId) { - setTxnId(res.data.txnId); + if (res?.status === 200 && data?.txnId) { + setTxnId(data.txnId); Notify.Success({ msg: "OTP has been resent to the mobile number registered with the Aadhar number.", }); } else { - Notify.Error({ msg: JSON.stringify(res.data) }); + Notify.Error({ msg: JSON.stringify(data) }); } }; @@ -430,11 +457,16 @@ const VerifyAadhaarSection = ({ if (!validateOtp() || !txnId) return; setIsVerifyingOtp(true); - const res = await dispatch(verifyAadhaarOtp(txnId, otp)); + const { res, data } = await request(routes.abha.verifyAadhaarOtp, { + body: { + otp: otp, + txnId: txnId, + }, + }); setIsVerifyingOtp(false); - if (res.status === 200 && res.data.txnId) { - setTxnId(res.data.txnId); + if (res?.status === 200 && data?.txnId) { + setTxnId(data.txnId); Notify.Success({ msg: "OTP verified" }); setIsVerified(true); } else { @@ -563,8 +595,6 @@ const VerifyMobileSection = ({ onVerified, patientMobile, }: VerifyMobileSectionProps) => { - const dispatch = useDispatch(); - const [mobile, setMobile] = useState(() => patientMobile || ""); const [mobileError, setMobileError] = useState(); @@ -602,11 +632,16 @@ const VerifyMobileSection = ({ setOtpDispatched(false); setIsSendingOtp(true); - const res = await dispatch(checkAndGenerateMobileOtp(txnId, mobile)); + const { res, data } = await request(routes.abha.checkAndGenerateMobileOtp, { + body: { + mobile: mobile, + txnId: txnId, + }, + }); setIsSendingOtp(false); - if (res.status === 200 && res.data) { - const { txnId, mobileLinked } = res.data; + if (res?.status === 200 && data) { + const { txnId, mobileLinked } = data; setTxnId(txnId); if (mobileLinked) { @@ -621,7 +656,7 @@ const VerifyMobileSection = ({ }); } } else { - Notify.Error({ msg: JSON.stringify(res.data) }); + Notify.Error({ msg: JSON.stringify(data) }); } }; @@ -642,11 +677,16 @@ const VerifyMobileSection = ({ if (!validateOtp()) return; setIsVerifyingOtp(true); - const res = await dispatch(verifyMobileOtp(txnId, otp)); + const { res, data } = await request(routes.abha.verifyMobileOtp, { + body: { + txnId: txnId, + otp: otp, + }, + }); setIsVerifyingOtp(false); - if (res.status === 200 && res.data.txnId) { - setTxnId(res.data.txnId); + if (res?.status === 200 && data?.txnId) { + setTxnId(data.txnId); Notify.Success({ msg: "OTP verified" }); setIsVerified(true); } else { @@ -718,21 +758,24 @@ const CreateHealthIDSection = ({ onCreateSuccess, patientId, }: CreateHealthIDSectionProps) => { - const dispatch = useDispatch(); const [healthId, setHealthId] = useState(""); const [isCreating, setIsCreating] = useState(false); const [isHealthIdInputInFocus, setIsHealthIdInputInFocus] = useState(false); const handleCreateHealthId = async () => { setIsCreating(true); - const res = await dispatch( - createHealthId({ txnId: transactionId, patientId, healthId }) - ); - if (res.status === 200) { + const { res, data } = await request(routes.abha.createHealthId, { + body: { + healthId: healthId, + txnId: transactionId, + patientId: patientId, + }, + }); + if (res?.status === 200) { Notify.Success({ msg: "Abha Address created" }); - onCreateSuccess(res.data); + onCreateSuccess(data); } else { - Notify.Error({ msg: JSON.stringify(res.data) }); + Notify.Error({ msg: JSON.stringify(data) }); } setIsCreating(false); }; diff --git a/src/Components/ABDM/LinkCareContextModal.tsx b/src/Components/ABDM/LinkCareContextModal.tsx index fc0b488954d..36749ff3d23 100644 --- a/src/Components/ABDM/LinkCareContextModal.tsx +++ b/src/Components/ABDM/LinkCareContextModal.tsx @@ -5,9 +5,9 @@ import DateFormField from "../Form/FormFields/DateFormField"; import DialogModal from "../Common/Dialog"; import { PatientModel } from "../Patient/models"; import TextFormField from "../Form/FormFields/TextFormField"; -import { linkCareContext } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; import { useState } from "react"; +import routes from "../../Redux/api.js"; +import request from "../../Utils/request/request.js"; interface IProps { consultationId: string; @@ -25,8 +25,6 @@ const LinkCareContextModal = ({ const [acceptedDisclaimer, setAcceptedDisclaimer] = useState(false); const [isLinkingCareContext, setIsLinkingCareContext] = useState(false); - const dispatch = useDispatch(); - return ( { setIsLinkingCareContext(true); - const res = await dispatch( - linkCareContext(consultationId, { + const { res } = await request(routes.abha.linkCareContext, { + body: { + consultation: consultationId, name: patient?.abha_number_object?.name, gender: patient?.abha_number_object?.gender, dob: patient?.abha_number_object?.date_of_birth, - }) - ); - - if (res.status === 202) { + }, + reattempts: 0, + }); + if (res?.status === 202) { Notification.Success({ msg: "Care Context sucessfully linked!", }); diff --git a/src/Components/ABDM/models.ts b/src/Components/ABDM/models.ts index b01579fba88..f992cc9df4f 100644 --- a/src/Components/ABDM/models.ts +++ b/src/Components/ABDM/models.ts @@ -17,3 +17,84 @@ export interface ICreateHealthIdResponse { healthId?: string; healthIdNumber: string; } + +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 ILinkABHANumber { + abha_profile: { + abha_number: string; + health_id: string; + date_of_birth: string; + }; +} + +export interface IConfirmMobileOtp { + otp: string; + txnId: string; + patientId?: string; + message?: string; +} + +export interface IHealthId { + authMethods?: string[]; +} + +export interface IAadhaarOtp { + txnId: string; +} + +export interface ICheckAndGenerateMobileOtp { + mobileLinked: boolean; + txnId: string; +} + +export interface IAadhaarOtpTBody { + aadhaar?: string; + txnId?: string; +} + +export interface IVerifyAadhaarOtpTBody { + consultation?: string; + name?: string; + gender?: "M" | "F" | "O"; + dob?: string; + otp?: string; + txnId?: string; +} + +export interface IGenerateMobileOtpTBody { + mobile: string; + txnId: string; +} + +export interface ISearchByHealthIdTBody { + healthId: string; +} + +export interface IinitiateAbdmAuthenticationTBody { + authMethod: string; + healthid: string; +} + +export interface IgetAbhaCardTBody { + patient: string; + type: "pdf" | "png"; +} + +export interface IcreateHealthFacilityTBody { + facility: string; + hf_id: string; +} + +export interface IpartialUpdateHealthFacilityTBody { + hf_id: string; +} diff --git a/src/Components/Assets/AssetFilter.tsx b/src/Components/Assets/AssetFilter.tsx index 4dca7dfecdb..8b250088d1a 100644 --- a/src/Components/Assets/AssetFilter.tsx +++ b/src/Components/Assets/AssetFilter.tsx @@ -1,19 +1,14 @@ import { useState, useEffect, useCallback } from "react"; -import { useAbortableEffect, statusType } from "../../Common/utils"; import { navigate, useQueryParams } from "raviger"; import { FacilitySelect } from "../Common/FacilitySelect"; import { FacilityModel } from "../Facility/models"; -import { useDispatch } from "react-redux"; -import { - getFacilityAssetLocation, - getPermittedFacility, -} from "../../Redux/actions"; -import * as Notification from "../../Utils/Notifications.js"; import { LocationSelect } from "../Common/LocationSelect"; import { AssetClass, AssetLocationObject } from "./AssetTypes"; import { FieldLabel } from "../Form/FormFields/FormField"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; import dayjs from "dayjs"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; @@ -35,7 +30,6 @@ const getDate = (value: any) => function AssetFilter(props: any) { const { filter, onChange, closeFilter } = props; - const dispatch: any = useDispatch(); const [facility, setFacility] = useState({ name: "" }); const [location, setLocation] = useState(initialLocation); @@ -46,7 +40,7 @@ function AssetFilter(props: any) { const [asset_class, setAssetClass] = useState( filter.asset_class || "" ); - const [facilityId, setFacilityId] = useState(filter.facility); + const [facilityId, setFacilityId] = useState(filter.facility); const [locationId, setLocationId] = useState(filter.location); const [warrantyExpiry, setWarrantyExpiry] = useState({ before: filter.warranty_amc_end_of_validity_before || null, @@ -54,8 +48,31 @@ function AssetFilter(props: any) { }); const [qParams, _] = useQueryParams(); + useQuery(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setFacility(data); + } + }, + prefetch: !!facilityId, + }); + + useQuery(routes.getFacilityAssetLocation, { + pathParams: { + facilityId: String(facilityId), + locationId: String(locationId), + }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setLocation(data); + } + }, + prefetch: !!(facilityId && locationId), + }); + useEffect(() => { - setFacilityId(facility?.id ? facility?.id : ""); + setFacilityId(facility?.id ? `${facility?.id}` : ""); setLocationId( facility?.id === qParams.facility ? qParams.location ?? "" : "" ); @@ -75,48 +92,6 @@ function AssetFilter(props: any) { else navigate("/assets"); }, [qParams]); - const fetchFacility = useCallback( - async (status: statusType) => { - if (facilityId) { - const facilityData: any = await dispatch( - getPermittedFacility(facilityId) - ); - if (!status.aborted) { - setFacility(facilityData?.data); - } - } - }, - [filter.facility] - ); - - const fetchLocation = useCallback( - async (status: statusType) => { - if (locationId && facilityId) { - const [locationData]: any = await Promise.all([ - dispatch( - getFacilityAssetLocation(String(facilityId), String(locationId)) - ), - ]); - if (!status.aborted && locationData !== undefined) { - if (!locationData.data) - Notification.Error({ - msg: "Something went wrong..!", - }); - else { - setLocation(locationData.data); - } - } - } else { - setLocation(initialLocation); - } - }, - [filter.location] - ); - - useAbortableEffect((status: statusType) => { - filter.facility && fetchFacility(status); - filter.location && fetchLocation(status); - }, []); const applyFilter = () => { const data = { facility: facilityId, diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx index 54ac25e2ac8..f8ab7cbc65f 100644 --- a/src/Components/Assets/AssetImportModal.tsx +++ b/src/Components/Assets/AssetImportModal.tsx @@ -5,8 +5,6 @@ import { FacilityModel } from "../Facility/models"; import { AssetData } from "./AssetTypes"; import * as Notification from "../../Utils/Notifications.js"; import { Cancel, Submit } from "../Common/components/ButtonV2"; -import { listFacilityAssetLocation } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; import { Link } from "raviger"; import readXlsxFile from "read-excel-file"; import { @@ -16,6 +14,8 @@ import { import { parseCsvFile } from "../../Utils/utils"; import useConfig from "../../Common/hooks/useConfig"; import DialogModal from "../Common/Dialog"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; interface Props { @@ -34,7 +34,6 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => { location: "", }); const [locations, setLocations] = useState([]); - const dispatchAction: any = useDispatch(); const { sample_format_asset_import } = useConfig(); const [locationsLoading, setLocationsLoading] = useState(false); @@ -43,18 +42,14 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => { setSelectedFile(undefined); onClose && onClose(); }; - - useEffect(() => { - setLocationsLoading(true); - dispatchAction( - listFacilityAssetLocation({}, { facility_external_id: facility.id }) - ).then(({ data }: any) => { - setLocationsLoading(false); - if (data.count > 0) { + useQuery(routes.listFacilityAssetLocation, { + pathParams: { facility_external_id: `${facility.id}` }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { setLocations(data.results); } - }); - }, []); + }, + }); useEffect(() => { const readFile = async () => { @@ -362,6 +357,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => { {isImporting ? ( diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx index e6d1836e27b..23098bff22f 100644 --- a/src/Components/Assets/AssetManage.tsx +++ b/src/Components/Assets/AssetManage.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useEffect, ReactElement, lazy } from "react"; +import { useState, useEffect, ReactElement, lazy } from "react"; import { AssetClass, assetClassProps, @@ -6,14 +6,6 @@ import { AssetService, AssetTransaction, } from "./AssetTypes"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { useDispatch } from "react-redux"; -import { - deleteAsset, - getAsset, - listAssetService, - listAssetTransaction, -} from "../../Redux/actions"; import Pagination from "../Common/Pagination"; import { navigate } from "raviger"; import QRCode from "qrcode.react"; @@ -36,6 +28,9 @@ import RelativeDateUserMention from "../Common/RelativeDateUserMention"; import { AssetServiceEditModal } from "./AssetServiceEditModal"; import { warrantyAmcValidityChip } from "./AssetsList"; import Page from "../Common/components/Page"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; interface AssetManageProps { assetId: string; @@ -51,80 +46,60 @@ const checkAuthority = (type: string, cutoff: string) => { const AssetManage = (props: AssetManageProps) => { const { t } = useTranslation(); const { assetId, facilityId } = props; - const [asset, setAsset] = useState(); const [isPrintMode, setIsPrintMode] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [offset, setOffset] = useState(0); const [totalCount, setTotalCount] = useState(0); - const [transactions, setTransactions] = useState([]); const [transactionDetails, setTransactionDetails] = useState< ReactElement | ReactElement[] >(); - const [services, setServices] = useState([]); const [servicesDetails, setServiceDetails] = useState< ReactElement | ReactElement[] >(); - const [isLoading, setIsLoading] = useState(false); - const dispatch = useDispatch(); const limit = 14; const authUser = useAuthUser(); const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [serviceEditData, setServiceEditData] = useState< AssetService & { open: boolean; viewOnly?: boolean } >(); + const [transactionFilter, setTransactionFilter] = useState({}); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const assetData = await dispatch(getAsset(assetId)); - if (!status.aborted) { - setIsLoading(false); - if (assetData?.data) { - setAsset(assetData.data); - - const transactionFilter = assetData.qr_code_id - ? { qr_code_id: assetData.qr_code_id } - : { external_id: assetId }; - - const [transactionsData, servicesData] = await Promise.all([ - dispatch( - listAssetTransaction({ - ...transactionFilter, - limit, - offset, - }) - ), - dispatch(listAssetService({}, assetId)), - ]); - - if (transactionsData?.data) { - setTransactions(transactionsData.data.results); - setTotalCount(transactionsData.data.count); - } else { - Notification.Error({ - msg: "Error fetching transactions", - }); - } - if (servicesData?.data) { - setServices(servicesData.data.results); - } else { - Notification.Error({ - msg: "Error fetching service logs", - }); - } - } else { - navigate("/not-found"); - } + const { data: asset, loading } = useQuery(routes.getAsset, { + pathParams: { + external_id: assetId, + }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setTransactionFilter( + data.qr_code_id + ? { qr_code_id: data.qr_code_id } + : { external_id: assetId } + ); } }, - [dispatch, assetId, offset] - ); + }); - useAbortableEffect( - (status: statusType) => { - fetchData(status); + const { data: transactions } = useQuery(routes.listAssetTransaction, { + prefetch: !!asset, + query: { + ...transactionFilter, + limit, + offset, }, - [dispatch, fetchData] + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setTotalCount(data.count); + } + }, + }); + + const { data: services, refetch: serviceRefetch } = useQuery( + routes.listAssetService, + { + pathParams: { + asset_external_id: assetId, + }, + } ); const handlePagination = (page: number, limit: number) => { @@ -158,7 +133,7 @@ const AssetManage = (props: AssetManageProps) => { const populateTableRows = (txns: AssetTransaction[]) => { if (txns.length > 0) { setTransactionDetails( - transactions.map((transaction: AssetTransaction) => ( + transactions?.results.map((transaction: AssetTransaction) => ( @@ -201,7 +176,7 @@ const AssetManage = (props: AssetManageProps) => { const populateServiceTableRows = (txns: AssetService[]) => { if (txns.length > 0) { setServiceDetails( - services.map((service: AssetService) => ( + services?.results.map((service: AssetService) => ( @@ -276,14 +251,14 @@ const AssetManage = (props: AssetManageProps) => { }; useEffect(() => { - populateTableRows(transactions); + if (transactions) populateTableRows(transactions.results); }, [transactions]); useEffect(() => { - populateServiceTableRows(services); + if (services) populateServiceTableRows(services?.results); }, [services]); - if (isLoading) return ; + if (loading) return ; if (isPrintMode) return ; const assetClassProp = @@ -324,13 +299,17 @@ const AssetManage = (props: AssetManageProps) => { const handleDelete = async () => { if (asset) { - const response = await dispatch(deleteAsset(asset.id)); - if (response && response.status === 204) { - Notification.Success({ - msg: "Asset deleted successfully", - }); - navigate("/assets"); - } + await request(routes.deleteAsset, { + pathParams: { + external_id: asset.id, + }, + onResponse: () => { + Notification.Success({ + msg: "Asset deleted successfully", + }); + navigate("/assets"); + }, + }); } }; @@ -595,7 +574,7 @@ const AssetManage = (props: AssetManageProps) => { handleClose={() => setServiceEditData({ ...serviceEditData, open: false }) } - handleUpdate={() => fetchData({ aborted: false })} + handleUpdate={() => serviceRefetch()} show={serviceEditData.open} viewOnly={serviceEditData.viewOnly} /> diff --git a/src/Components/Assets/AssetServiceEditModal.tsx b/src/Components/Assets/AssetServiceEditModal.tsx index 690524e471b..3964dfe0770 100644 --- a/src/Components/Assets/AssetServiceEditModal.tsx +++ b/src/Components/Assets/AssetServiceEditModal.tsx @@ -1,6 +1,4 @@ import { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; -import { updateAssetService } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; import ButtonV2, { Cancel, Submit } from "../Common/components/ButtonV2"; import DialogModal from "../Common/Dialog"; @@ -9,6 +7,8 @@ import dayjs from "dayjs"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import { formatDate, formatDateTime } from "../../Utils/utils"; import CareIcon from "../../CAREUI/icons/CareIcon"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; import DateFormField from "../Form/FormFields/DateFormField"; import { t } from "i18next"; @@ -24,23 +24,25 @@ export const AssetServiceEditModal = (props: { serviced_on: props.service_record?.serviced_on, note: props.service_record?.note, }); - const dispatchAction: any = useDispatch(); const [isLoading, setIsLoading] = useState(false); const [editRecord, setEditRecord] = useState(); const handleSubmit = async (e: any) => { e.preventDefault(); setIsLoading(true); - const data = { + const body = { serviced_on: form.serviced_on, note: form.note, }; - - const res = await dispatchAction( - updateAssetService(props.asset?.id ?? "", props.service_record.id, data) - ); + const { data } = await request(routes.updateAssetService, { + pathParams: { + asset_external_id: props.asset?.id ?? "", + external_id: props.service_record.id, + }, + body: body, + }); setIsLoading(false); - if (res?.data) { + if (data) { Notification.Success({ msg: "Asset service record updated successfully", }); diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index 55f4d0c258e..b4fefbc90b2 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -1,10 +1,5 @@ import { SyntheticEvent, useEffect, useState } from "react"; import { AssetData } from "../AssetTypes"; -import { useDispatch } from "react-redux"; -import { - partialUpdateAsset, - getPermittedFacility, -} from "../../../Redux/actions"; import * as Notification from "../../../Utils/Notifications.js"; import MonitorConfigure from "../configure/MonitorConfigure"; import Loading from "../../Common/Loading"; @@ -16,6 +11,9 @@ import TextFormField from "../../Form/FormFields/TextFormField"; import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor"; import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor"; import useAuthUser from "../../../Common/hooks/useAuthUser"; +import request from "../../../Utils/request/request"; +import routes from "../../../Redux/api"; +import useQuery from "../../../Utils/request/useQuery"; interface HL7MonitorProps { assetId: string; @@ -33,19 +31,14 @@ const HL7Monitor = (props: HL7MonitorProps) => { const [localipAddress, setLocalIPAddress] = useState(""); const [ipadrdress_error, setIpAddress_error] = useState(""); const authUser = useAuthUser(); - const dispatch = useDispatch(); - - useEffect(() => { - const fetchFacility = async () => { - const res = await dispatch(getPermittedFacility(facilityId)); - - if (res.status === 200 && res.data) { - setFacilityMiddlewareHostname(res.data.middleware_address); + const { data: facility, loading } = useQuery(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data && data.middleware_address) { + setFacilityMiddlewareHostname(data.middleware_address); } - }; - - if (facilityId) fetchFacility(); - }, [dispatch, facilityId]); + }, + }); useEffect(() => { setAssetType(asset?.asset_class); @@ -65,26 +58,28 @@ const HL7Monitor = (props: HL7MonitorProps) => { local_ip_address: localipAddress, }, }; - const res: any = await Promise.resolve( - dispatch(partialUpdateAsset(assetId, data)) - ); + const { res } = await request(routes.partialUpdateAsset, { + pathParams: { external_id: assetId }, + body: data, + }); if (res?.status === 200) { Notification.Success({ msg: "Asset Configured Successfully", }); } else { Notification.Error({ - msg: "Something went wrong..!", + msg: "Something went wrong!", }); } } else { - setIpAddress_error("Please Enter a Valid IP address !!"); + setIpAddress_error("IP address is invalid"); } }; - const middleware = middlewareHostname || facilityMiddlewareHostname; + const fallbackMiddleware = + asset?.location_object?.middleware_address || facilityMiddlewareHostname; - if (isLoading) return ; + if (isLoading || loading || !facility) return ; return (
@@ -93,11 +88,29 @@ const HL7Monitor = (props: HL7MonitorProps) => {

Connection

-
+
+

Middleware Hostname

+ {!middlewareHostname && ( +
+ + + Middleware hostname sourced from{" "} + {asset?.location_object?.middleware_address + ? "asset location" + : "asset facility"} + +
+ )} +
+ } + placeholder={fallbackMiddleware} value={middlewareHostname} onChange={(e) => setMiddlewareHostname(e.value)} errorClassName="hidden" @@ -127,12 +140,16 @@ const HL7Monitor = (props: HL7MonitorProps) => { {assetType === "HL7MONITOR" && ( )} {assetType === "VENTILATOR" && ( )}
diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index 4720c876010..eb6ebb9abfd 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -1,11 +1,5 @@ import { useEffect, useState } from "react"; import { AssetData } from "../AssetTypes"; -import { useDispatch } from "react-redux"; -import { - partialUpdateAsset, - createAssetBed, - getPermittedFacility, -} from "../../../Redux/actions"; import * as Notification from "../../../Utils/Notifications.js"; import { BedModel } from "../../Facility/models"; import axios from "axios"; @@ -18,6 +12,13 @@ import { Submit } from "../../Common/components/ButtonV2"; import { SyntheticEvent } from "react"; import useAuthUser from "../../../Common/hooks/useAuthUser"; +import request from "../../../Utils/request/request"; +import routes from "../../../Redux/api"; +import useQuery from "../../../Utils/request/useQuery"; + +import CareIcon from "../../../CAREUI/icons/CareIcon"; + + interface Props { assetId: string; facilityId: string; @@ -43,19 +44,15 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const [refreshPresetsHash, setRefreshPresetsHash] = useState( Number(new Date()) ); - const dispatch = useDispatch(); + const { data: facility, loading } = useQuery(routes.getPermittedFacility, { + pathParams: { id: facilityId }, + }); const authUser = useAuthUser(); useEffect(() => { - const fetchFacility = async () => { - const res = await dispatch(getPermittedFacility(facilityId)); - - if (res.status === 200 && res.data) { - setFacilityMiddlewareHostname(res.data.middleware_address); - } - }; - - if (facilityId) fetchFacility(); - }, [dispatch, facilityId]); + if (facility?.middleware_address) { + setFacilityMiddlewareHostname(facility.middleware_address); + } + }, [facility, facilityId]); useEffect(() => { if (asset) { @@ -83,18 +80,19 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { camera_access_key: `${username}:${password}:${streamUuid}`, }, }; - const res: any = await Promise.resolve( - dispatch(partialUpdateAsset(assetId, data)) - ); + const { res } = await request(routes.partialUpdateAsset, { + pathParams: { external_id: assetId }, + body: data, + }); if (res?.status === 200) { Notification.Success({ msg: "Asset Configured Successfully" }); onUpdated?.(); } else { - Notification.Error({ msg: "Something went wrong..!" }); + Notification.Error({ msg: "Something went wrong!" }); } setLoadingSetConfiguration(false); } else { - setIpAddress_error("Please Enter a Valid Camera address !!"); + setIpAddress_error("IP address is invalid"); } }; @@ -110,15 +108,14 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { const presetData = await axios.get( `https://${facilityMiddlewareHostname}/status?hostname=${config.hostname}&port=${config.port}&username=${config.username}&password=${config.password}` ); - const res: any = await Promise.resolve( - dispatch( - createAssetBed( - { meta: { ...data, ...presetData.data } }, - assetId, - bed?.id as string - ) - ) - ); + + const { res } = await request(routes.createAssetBed, { + body: { + meta: { ...data, ...presetData.data }, + asset: assetId, + bed: bed?.id as string, + }, + }); if (res?.status === 201) { Notification.Success({ msg: "Preset Added Successfully", @@ -138,8 +135,11 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { } setLoadingAddPreset(false); }; + + if (isLoading || loading || !facility) return ; - if (isLoading) return ; + const fallbackMiddleware = + asset?.location_object?.middleware_address || facilityMiddlewareHostname; return (
@@ -148,8 +148,26 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => {
+

Middleware Hostname

+ {!middlewareHostname && ( +
+ + + Middleware hostname sourced from{" "} + {asset?.location_object?.middleware_address + ? "asset location" + : "asset facility"} + +
+ )} +
+ } + placeholder={fallbackMiddleware} value={middlewareHostname} onChange={({ value }) => setMiddlewareHostname(value)} /> diff --git a/src/Components/Assets/AssetTypes.tsx b/src/Components/Assets/AssetTypes.tsx index 52c7e45003c..8b96b6beeb7 100644 --- a/src/Components/Assets/AssetTypes.tsx +++ b/src/Components/Assets/AssetTypes.tsx @@ -91,6 +91,8 @@ export interface AssetData { }; } +export type AssetUpdate = Partial; + export interface AssetsResponse { count: number; next?: string; @@ -137,8 +139,12 @@ export interface AssetBedModel { created_date: string; modified_date: string; meta: Record; + asset?: string; + bed?: string; } +export type AssetBedBody = Partial; + export interface AssetServiceEdit { id: string; asset_service: AssetService; @@ -166,3 +172,9 @@ export interface PatientAssetBed { patient?: PatientModel; meta?: Record; } + +export interface AssetServiceUpdate { + external_id: string; + serviced_on: string; + note: string; +} diff --git a/src/Components/Assets/AssetsList.tsx b/src/Components/Assets/AssetsList.tsx index 46b4da5283d..4bcb2698a9a 100644 --- a/src/Components/Assets/AssetsList.tsx +++ b/src/Components/Assets/AssetsList.tsx @@ -1,15 +1,8 @@ -import { useDispatch } from "react-redux"; import QrReader from "react-qr-reader"; -import { statusType, useAbortableEffect } from "../../Common/utils"; import * as Notification from "../../Utils/Notifications.js"; -import { - getAnyFacility, - listAssets, - getFacilityAssetLocation, - getAsset, -} from "../../Redux/actions"; +import { listAssets } from "../../Redux/actions"; import { assetClassProps, AssetData } from "./AssetTypes"; -import { useState, useCallback, useEffect, lazy } from "react"; +import { useState, useEffect, lazy } from "react"; import { Link, navigate } from "raviger"; import AssetFilter from "./AssetFilter"; import { parseQueryParams } from "../../Utils/primitives"; @@ -28,6 +21,9 @@ import AssetImportModal from "./AssetImportModal"; import Page from "../Common/components/Page"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import { useTranslation } from "react-i18next"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; const Loading = lazy(() => import("../Common/Loading")); @@ -52,66 +48,45 @@ const AssetsList = () => { const [status, setStatus] = useState(); const [facilityName, setFacilityName] = useState(); const [asset_class, setAssetClass] = useState(); - const [locationName, setLocationName] = useState(); const [importAssetModalOpen, setImportAssetModalOpen] = useState(false); - const dispatch: any = useDispatch(); const assetsExist = assets.length > 0 && Object.keys(assets[0]).length > 0; const [showFacilityDialog, setShowFacilityDialog] = useState(false); const [selectedFacility, setSelectedFacility] = useState({ name: "", }); + const params = { + limit: resultsPerPage, + page: qParams.page, + offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, + search_text: qParams.search || "", + facility: qParams.facility || "", + asset_type: qParams.asset_type || "", + asset_class: qParams.asset_class || "", + location: qParams.facility ? qParams.location || "" : "", + status: qParams.status || "", + }; - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const params = { - limit: resultsPerPage, - page: qParams.page, - offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, - search_text: qParams.search || "", - facility: qParams.facility || "", - asset_type: qParams.asset_type || "", - asset_class: qParams.asset_class || "", - location: qParams.facility ? qParams.location || "" : "", - status: qParams.status || "", - warranty_amc_end_of_validity_before: - qParams.warranty_amc_end_of_validity_before || "", - warranty_amc_end_of_validity_after: - qParams.warranty_amc_end_of_validity_after || "", - }; - const { data } = await dispatch(listAssets(params)); - if (!status.aborted) { - setIsLoading(false); - if (!data) - Notification.Error({ - msg: "Something went wrong..!", - }); - else { - setAssets(data.results); - setTotalCount(data.count); - if (qParams.facility) { - const fetchFacility = await dispatch( - getAnyFacility(qParams.facility) - ); - setSelectedFacility(fetchFacility.data as FacilityModel); - } - } + useQuery(routes.listAssets, { + query: params, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setAssets(data.results); + setTotalCount(data.count); } }, - [ - resultsPerPage, - qParams.page, - qParams.search, - qParams.facility, - qParams.asset_type, - qParams.asset_class, - qParams.location, - qParams.status, - qParams.warranty_amc_end_of_validity_before, - qParams.warranty_amc_end_of_validity_after, - dispatch, - ] - ); + }); + + useQuery(routes.getAnyFacility, { + pathParams: { id: qParams.facility }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data) { + setFacility(data); + setSelectedFacility(data); + setFacilityName(data.name); + } + }, + prefetch: !!qParams.facility, + }); useEffect(() => { setAssetType(qParams.asset_type); @@ -125,56 +100,13 @@ const AssetsList = () => { setAssetClass(qParams.asset_class); }, [qParams.asset_class]); - useAbortableEffect( - (status: statusType) => { - fetchData(status); + const { data: location } = useQuery(routes.getFacilityAssetLocation, { + pathParams: { + facility_external_id: String(qParams.facility), + external_id: String(qParams.location), }, - [dispatch, fetchData] - ); - useEffect(() => { - async function fetchFacilityName() { - if (!qParams.facility) return setFacilityName(""); - const res = await dispatch(getAnyFacility(qParams.facility, "facility")); - setFacilityName(res?.data?.name); - } - fetchFacilityName(); - }, [dispatch, qParams.facility]); - - const fetchFacility = useCallback( - async (status: statusType) => { - if (!qParams.facility) return setFacility(undefined); - setIsLoading(true); - const res = await dispatch(getAnyFacility(qParams.facility)); - if (!status.aborted) { - setFacility(res?.data); - setIsLoading(false); - } - }, - [dispatch, qParams.facility] - ); - const fetchLocationName = useCallback( - async (status: statusType) => { - if (!qParams.location || !qParams.facility) - return setLocationName(undefined); - setIsLoading(true); - const res = await dispatch( - getFacilityAssetLocation(qParams.facility, qParams.location) - ); - if (!status.aborted) { - setLocationName(res?.data?.name); - setIsLoading(false); - } - }, - [dispatch, qParams.facility, qParams.location] - ); - - useAbortableEffect( - (status: statusType) => { - fetchFacility(status); - fetchLocationName(status); - }, - [fetchFacility, fetchLocationName] - ); + prefetch: !!(qParams.facility && qParams.location), + }); const getAssetIdFromQR = async (assetUrl: string) => { try { @@ -184,8 +116,10 @@ const AssetsList = () => { // QR Maybe searchParams "asset" or "assetQR" const assetId = params.asset || params.assetQR; if (assetId) { - const { data } = await dispatch(listAssets({ qr_code_id: assetId })); - return data.results[0].id; + const { data } = await request(routes.listAssets, { + query: { qr_code_id: assetId }, + }); + return data?.results[0].id; } } catch (err) { console.log(err); @@ -193,11 +127,13 @@ const AssetsList = () => { }; const checkValidAssetId = async (assetId: string) => { - const assetData = await dispatch(getAsset(assetId)); + const { data: assetData } = await request(routes.getAsset, { + pathParams: { id: assetId }, + }); try { - if (assetData.data) { + if (assetData) { navigate( - `/facility/${assetData.data.location_object.facility.id}/assets/${assetId}` + `/facility/${assetData.location_object.facility.id}/assets/${assetId}` ); } } catch (err) { @@ -434,7 +370,7 @@ const AssetsList = () => { value("Asset Type", "asset_type", asset_type ?? ""), value("Asset Class", "asset_class", asset_class ?? ""), value("Status", "status", status?.replace(/_/g, " ") ?? ""), - value("Location", "location", locationName ?? ""), + value("Location", "location", location?.name ?? ""), value( "Warranty AMC End Of Validity Before", "warranty_amc_end_of_validity_before", @@ -516,6 +452,7 @@ export const warrantyAmcValidityChip = ( if (warrantyAmcEndDate < today) { return ( ) => { - dispatch(createAssetBed({}, assetId, bedId)); +const saveLink = async (assetId: string, bedId: string) => { + await request(routes.createAssetBed, { + body: { + asset: assetId, + bed: bedId, + }, + }); Notification.Success({ msg: "AssetBed Link created successfully" }); }; -const update_Link = ( +const update_Link = async ( assetbedId: string, assetId: string, - bed: BedModel, - assetBed: any, - dispatch: Dispatch + bed: BedModel ) => { - dispatch( - partialUpdateAssetBed( - { - asset: assetId, - bed: bed.id, - }, - assetbedId - ) - ); + await request(routes.partialUpdateAssetBed, { + pathParams: { external_id: assetbedId }, + body: { + asset: assetId, + bed: bed.id ?? "", + }, + }); Notification.Success({ msg: "AssetBed Link updated successfully" }); }; export default function MonitorConfigure({ asset }: { asset: AssetData }) { const [bed, setBed] = useState({}); const [updateLink, setUpdateLink] = useState(false); - const [assetBed, setAssetBed] = useState(); - const dispatch: any = useDispatch(); - - const getAssetBeds = async (id: string) => { - const assetBeds = await dispatch(listAssetBeds({ asset: id })); - if (assetBeds.data?.results?.length > 0) { - setUpdateLink(true); - setAssetBed(assetBeds.data.results[0]); - setBed(assetBeds.data.results[0].bed_object); - } else { - setUpdateLink(false); - } - }; - - useEffect(() => { - if (asset.id) { - getAssetBeds(asset.id); - } - }, [asset]); + const { data: assetBed } = useQuery(routes.listAssetBeds, { + query: { asset: asset.id }, + onResponse: ({ res, data }) => { + if (res?.status === 200 && data && data.results.length > 0) { + setBed(data.results[0].bed_object); + setUpdateLink(true); + } + }, + }); return ( diff --git a/src/Components/Common/Export.tsx b/src/Components/Common/Export.tsx index 3595b3c84a0..f991c476aac 100644 --- a/src/Components/Common/Export.tsx +++ b/src/Components/Common/Export.tsx @@ -44,7 +44,7 @@ export const ExportMenu = ({ } + icon={} className="tooltip border-primary-500 bg-white text-primary-500 hover:bg-primary-100 enabled:border" > {exportItems.map((item) => ( diff --git a/src/Components/Common/Uptime.tsx b/src/Components/Common/Uptime.tsx index ce60e7b7f7e..6f6966b9053 100644 --- a/src/Components/Common/Uptime.tsx +++ b/src/Components/Common/Uptime.tsx @@ -4,7 +4,7 @@ import { listAssetAvailability } from "../../Redux/actions"; import { useDispatch } from "react-redux"; import * as Notification from "../../Utils/Notifications.js"; import { AssetStatus, AssetUptimeRecord } from "../Assets/AssetTypes"; -import { reverse } from "lodash"; +import { reverse } from "lodash-es"; import { classNames } from "../../Utils/utils"; import dayjs from "../../Utils/dayjs"; diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 5f39cb213a4..a3b6dd2fbf9 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -1,22 +1,21 @@ -import _ from "lodash"; import { navigate } from "raviger"; import { lazy, useState } from "react"; import CSVReader from "react-csv-reader"; -import { useDispatch } from "react-redux"; import useConfig from "../../Common/hooks/useConfig"; -import { externalResultUploadCsv } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; const PageTitle = lazy(() => import("../Common/PageTitle")); import { useTranslation } from "react-i18next"; import { Cancel, Submit } from "../Common/components/ButtonV2"; import useAppHistory from "../../Common/hooks/useAppHistory"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; +import { IExternalResult } from "./models"; export default function ExternalResultUpload() { const { sample_format_external_result_import } = useConfig(); - const dispatch: any = useDispatch(); // for disabling save button once clicked const [loading, setLoading] = useState(false); - const [csvData, setCsvData] = useState(new Array()); + const [csvData, setCsvData] = useState(new Array()); const [errors, setErrors] = useState([]); const handleForce = (data: any) => { setCsvData(data); @@ -32,26 +31,35 @@ export default function ExternalResultUpload() { header.toLowerCase().replace(/\W/g, "_"), }; - const handleSubmit = (e: any) => { + const handleSubmit = async (e: any) => { e.preventDefault(); setLoading(true); const valid = true; - if (csvData.length !== 0) { - const data = { - sample_tests: csvData, - }; + if (csvData.length !== 0) { if (valid) { setErrors([]); - dispatch(externalResultUploadCsv(data)).then((resp: any) => { - if (resp && resp.status === 202) { + + try { + const { res, data } = await request(routes.externalResultUploadCsv, { + body: { + sample_tests: csvData, + }, + }); + + if (res && res.status === 202) { setLoading(false); navigate("/external_results"); } else { - setErrors(resp.data.map((err: any) => Object.entries(err))); + if (data) { + setErrors(data.map((err: any) => Object.entries(err))); + } setLoading(false); } - }); + } catch (error) { + console.error("An error occurred:", error); + setLoading(false); + } } else { setLoading(false); } @@ -117,13 +125,13 @@ export default function ExternalResultUpload() {
{index + 1}
{data.name}
-
+
{errors && errors.length !== 0 ? errors.map((error: any) => { return (
- {_.startCase(_.camelCase(error[0][0]))} -{" "} - {error[0][1]} + {error[0][0].toLowerCase()} -{" "} + {error[0][1].toLowerCase()}
); }) diff --git a/src/Components/ExternalResult/ListFilter.tsx b/src/Components/ExternalResult/ListFilter.tsx index 91b75e535d2..75358196ffd 100644 --- a/src/Components/ExternalResult/ListFilter.tsx +++ b/src/Components/ExternalResult/ListFilter.tsx @@ -1,6 +1,4 @@ -import { useEffect, useState } from "react"; -import { getAllLocalBodyByDistrict } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; +import { useState } from "react"; import useMergeState from "../../Common/hooks/useMergeState"; import { navigate } from "raviger"; import { useTranslation } from "react-i18next"; @@ -11,6 +9,9 @@ import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; import dayjs from "dayjs"; import { dateQueryString } from "../../Utils/utils"; import useAuthUser from "../../Common/hooks/useAuthUser"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import Loading from "../Common/Loading"; const clearFilterState = { created_date_before: "", @@ -29,10 +30,8 @@ export default function ListFilter(props: any) { const { filter, onChange, closeFilter, dataList } = props; const [wardList, setWardList] = useState([]); const [lsgList, setLsgList] = useState([]); - const [wards, setWards] = useState([]); const [selectedLsgs, setSelectedLsgs] = useState([]); - const dispatch: any = useDispatch(); const authUser = useAuthUser(); const [filterState, setFilterState] = useMergeState({ created_date_before: filter.created_date_before || null, @@ -45,6 +44,57 @@ export default function ListFilter(props: any) { }); const { t } = useTranslation(); + const { loading } = useQuery(routes.getAllLocalBodyByDistrict, { + pathParams: { id: String(authUser.district) }, + onResponse: ({ res, data }) => { + if (res && data) { + let allWards: any[] = []; + let allLsgs: any[] = []; + if (res && data) { + data.forEach((local: any) => { + allLsgs = [...allLsgs, { id: local.id, name: local.name }]; + if (local.wards) { + local.wards.forEach((ward: any) => { + allWards = [ + ...allWards, + { + id: ward.id, + name: ward.number + ": " + ward.name, + panchayath: local.name, + number: ward.number, + local_body_id: local.id, + }, + ]; + }); + } + }); + } + + sortByName(allWards); + sortByName(allLsgs); + setWardList(allWards || []); + setLsgList(allLsgs || []); + const filteredWard = filter?.wards?.split(",").map(Number); + const selectedWards: any = + filteredWard && allWards + ? allWards.filter(({ id }: { id: number }) => { + return filteredWard.includes(id); + }) + : []; + setWards(selectedWards); + + const filteredLsgs = filter?.local_bodies?.split(",").map(Number); + const selectedLsgs: any = + filteredLsgs && allLsgs + ? allLsgs.filter(({ id }: { id: number }) => { + return filteredLsgs.includes(id); + }) + : []; + setSelectedLsgs(selectedLsgs); + } + }, + }); + const handleDateRangeChange = ( startDateId: string, endDateId: string, @@ -118,59 +168,10 @@ export default function ListFilter(props: any) { }); }; - useEffect(() => { - async function getWardList() { - const id = authUser.district; - const res = await dispatch(getAllLocalBodyByDistrict({ id })); - let allWards: any[] = []; - let allLsgs: any[] = []; - res?.data?.forEach((local: any) => { - allLsgs = [...allLsgs, { id: local.id, name: local.name }]; - if (local.wards) { - local.wards.forEach((ward: any) => { - allWards = [ - ...allWards, - { - id: ward.id, - name: ward.number + ": " + ward.name, - panchayath: local.name, - number: ward.number, - local_body_id: local.id, - }, - ]; - }); - } - }); - sortByName(allWards); - sortByName(allLsgs); - setWardList(allWards || []); - setLsgList(allLsgs || []); - const filteredWard = filter?.wards?.split(",").map(Number); - const selectedWards: any = - filteredWard && allWards - ? allWards.filter(({ id }: { id: number }) => { - return filteredWard.includes(id); - }) - : []; - setWards(selectedWards); - - const filteredLsgs = filter?.local_bodies?.split(",").map(Number); - const selectedLsgs: any = - filteredLsgs && allLsgs - ? allLsgs.filter(({ id }: { id: number }) => { - return filteredLsgs.includes(id); - }) - : []; - setSelectedLsgs(selectedLsgs); - } - getWardList(); - }, []); - const filterWards = () => { const selectedLsgIds: any = selectedLsgs.map((e) => { return e.id; }); - const selectedwards: any = selectedLsgIds.length === 0 ? wardList @@ -183,13 +184,15 @@ export default function ListFilter(props: any) { const handleChange = (event: any) => { const { name, value } = event.target; - const filterData: any = { ...filterState }; filterData[name] = value; - setFilterState(filterData); }; + if (loading) { + ; + } + return ( import("../Common/Loading")); export default function ResultItem(props: any) { - const dispatch: any = useDispatch(); - const initialData: any = {}; - const [data, setData] = useState(initialData); - const [isLoading, setIsLoading] = useState(true); const [showDeleteAlert, setShowDeleteAlert] = useState(false); const { t } = useTranslation(); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatch(externalResult({ id: props.id })); - if (!status.aborted) { - if (res && res.data) { - setData(res.data); - } - setIsLoading(false); - } - }, - [props.id, dispatch] - ); + const { data: resultItemData, loading } = useQuery(routes.externalResult, { + pathParams: { id: props.id }, + }); const handleDelete = async () => { - const res = await dispatch(deleteExternalResult(props.id)); - if (res?.status === 204) { - Notification.Success({ - msg: t("record_has_been_deleted_successfully"), + if (showDeleteAlert) { + const { res, data } = await request(routes.deleteExternalResult, { + pathParams: { id: props.id }, }); - } else { - Notification.Error({ - msg: - t("error_while_deleting_record") + ": " + (res?.data?.detail || ""), - }); - } - setShowDeleteAlert(false); - navigate("/external_results"); + if (res?.status === 204) { + Notification.Success({ + msg: t("record_has_been_deleted_successfully"), + }); + } else { + Notification.Error({ + msg: t("error_while_deleting_record") + ": " + (data?.detail || ""), + }); + } + setShowDeleteAlert(false); + navigate("/external_results"); + } }; - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] - ); - - if (isLoading) { + if (loading || !resultItemData) { return ; } @@ -69,14 +50,18 @@ export default function ResultItem(props: any) { variant="danger" action={t("delete")} show={showDeleteAlert} - onConfirm={() => handleDelete()} + onConfirm={() => { + handleDelete(); + }} onClose={() => setShowDeleteAlert(false)} />
diff --git a/src/Components/ExternalResult/ResultList.tsx b/src/Components/ExternalResult/ResultList.tsx index 74fbf8430b0..d148b26e3f5 100644 --- a/src/Components/ExternalResult/ResultList.tsx +++ b/src/Components/ExternalResult/ResultList.tsx @@ -1,12 +1,10 @@ import ButtonV2 from "../Common/components/ButtonV2"; import { navigate } from "raviger"; -import { lazy, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useState } from "react"; import { externalResultList } from "../../Redux/actions"; import ListFilter from "./ListFilter"; import FacilitiesSelectDialogue from "./FacilitiesSelectDialogue"; import { FacilityModel } from "../Facility/models"; -import { parsePhoneNumber } from "../../Utils/utils"; import SearchInput from "../Form/SearchInput"; import useFilters from "../../Common/hooks/useFilters"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -15,14 +13,12 @@ import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; import CountBlock from "../../CAREUI/display/Count"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import Page from "../Common/components/Page"; - +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; +import { parsePhoneNumber } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); export default function ResultList() { - const dispatch: any = useDispatch(); - const [data, setData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [totalCount, setTotalCount] = useState(0); const { qParams, updateQuery, @@ -57,61 +53,31 @@ export default function ResultList() { setPhoneNumberError("Enter a valid number"); }; + const params = { + page: qParams.page || 1, + name: qParams.name || "", + mobile_number: qParams.mobile_number + ? parsePhoneNumber(qParams.mobile_number) ?? "" + : "", + wards: qParams.wards || undefined, + local_bodies: qParams.local_bodies || undefined, + created_date_before: qParams.created_date_before || undefined, + created_date_after: qParams.created_date_after || undefined, + result_date_before: qParams.result_date_before || undefined, + result_date_after: qParams.result_date_after || undefined, + sample_collection_date_after: + qParams.sample_collection_date_after || undefined, + sample_collection_date_before: + qParams.sample_collection_date_before || undefined, + offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, + srf_id: qParams.srf_id || undefined, + }; - let manageResults: any = null; - useEffect(() => { - setIsLoading(true); - const params = { - page: qParams.page || 1, - name: qParams.name || "", - mobile_number: qParams.mobile_number - ? parsePhoneNumber(qParams.mobile_number) ?? "" - : "", - wards: qParams.wards || undefined, - local_bodies: qParams.local_bodies || undefined, - created_date_before: qParams.created_date_before || undefined, - created_date_after: qParams.created_date_after || undefined, - result_date_before: qParams.result_date_before || undefined, - result_date_after: qParams.result_date_after || undefined, - sample_collection_date_after: - qParams.sample_collection_date_after || undefined, - sample_collection_date_before: - qParams.sample_collection_date_before || undefined, - offset: (qParams.page ? qParams.page - 1 : 0) * resultsPerPage, - srf_id: qParams.srf_id || undefined, - }; - - dispatch(externalResultList(params, "externalResultList")) - .then((res: any) => { - if (res && res.data) { - setData(res.data.results); - setTotalCount(res.data.count); - setIsLoading(false); - } - }) - .catch(() => { - setIsLoading(false); - }); + const { data, loading } = useQuery(routes.externalResultList, { + query: params, + }); - if (!params.mobile_number) { - setPhoneNum("+91"); - } - }, [ - dispatch, - qParams.name, - qParams.page, - qParams.mobile_number, - qParams.wards, - qParams.created_date_before, - qParams.created_date_after, - qParams.result_date_before, - qParams.result_date_after, - qParams.sample_collection_date_after, - qParams.sample_collection_date_before, - qParams.local_bodies, - qParams.srf_id, - dataList, - ]); + let manageResults: any = null; const removeLSGFilter = (paramKey: any, id: any) => { const updatedLsgList = dataList.lsgList.filter((x: any) => x.id !== id); @@ -158,8 +124,8 @@ export default function ResultList() { }; let resultList: any[] = []; - if (data && data.length) { - resultList = data.map((result: any) => { + if (data?.results.length) { + resultList = data.results.map((result: any) => { const resultUrl = `/external_results/${result.id}`; return ( @@ -173,7 +139,7 @@ export default function ResultList() { className="group inline-flex space-x-2 text-sm leading-5" >

- {result.name} - {result.age} {result.age_in} + {`${result.name}`} - {result.age} {result.age_in}

@@ -214,7 +180,7 @@ export default function ResultList() { }); } - if (isLoading || !data) { + if (loading) { manageResults = ( @@ -222,9 +188,9 @@ export default function ResultList() { ); - } else if (data && data.length) { + } else if (data?.results.length) { manageResults = <>{resultList}; - } else if (data && data.length === 0) { + } else if (data?.results.length === 0) { manageResults = ( @@ -286,8 +252,8 @@ export default function ResultList() {
@@ -358,7 +324,7 @@ export default function ResultList() {
- + import("../Common/Loading")); @@ -63,79 +57,70 @@ export default function UpdateResult(props: any) { const { id } = props; const { goBack } = useAppHistory(); - const dispatchAction: any = useDispatch(); const [state, dispatch] = useReducer(FormReducer, initialState); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false); const [isWardLoading, setIsWardLoading] = useState(false); const [localBody, setLocalBody] = useState(initialLocalbodies); const [ward, setWard] = useState(initialLocalbodies); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatchAction(externalResult({ id: id })); - if (!status.aborted) { - if (res && res.data) { - const form = { ...state.form }; - form["name"] = res.data.name; - form["age"] = res.data.age; - form["age_in"] = res.data.age_in; - form["srf_id"] = res.data.srf_id; - form["address"] = res.data.address; - form["district"] = res.data.district_object.name; - form["local_body"] = String(res.data.local_body); - form["ward"] = String(res.data.ward); - form["patient_created"] = String(res.data.patient_created); + const { loading } = useQuery(routes.externalResult, { + pathParams: { id }, + onResponse: async ({ res, data }) => { + if (res && data) { + const form = { ...state.form }; + form["name"] = data.name; + form["age"] = data.age; + form["age_in"] = data.age_in; + form["srf_id"] = data.srf_id; + form["address"] = data.address; + form["district"] = data.district_object.name; + form["local_body"] = String(data.local_body); + form["ward"] = String(data.ward); + form["patient_created"] = String(data.patient_created); - dispatch({ type: "set_form", form }); + dispatch({ type: "set_form", form }); - Promise.all([ - fetchLocalBody(res.data.district), - fetchWards(res.data.local_body), - ]); - } + Promise.all([ + fetchLocalBody(data.district), + fetchWards(data.local_body), + ]); setIsLoading(false); } }, - [props.id, dispatchAction] - ); + }); - const fetchLocalBody = useCallback( - async (id: string) => { - if (Number(id) > 0) { - setIsLocalbodyLoading(true); - const localBodyList = await dispatchAction( - getLocalbodyByDistrict({ id }) - ); + const fetchLocalBody = async (id: number) => { + if (Number(id) > 0) { + setIsLocalbodyLoading(true); + const { res, data } = await request(routes.getLocalbodyByDistrict, { + pathParams: { id: String(id) }, + }); + if (res && data) { setIsLocalbodyLoading(false); - setLocalBody([...initialLocalbodies, ...localBodyList.data]); - } else { - setLocalBody(initialLocalbodies); + setLocalBody([...initialLocalbodies, ...data]); } - }, - [dispatchAction] - ); + } else { + setLocalBody(initialLocalbodies); + } + }; const fetchWards = useCallback( - async (id: string) => { + async (id: number) => { if (Number(id) > 0) { setIsWardLoading(true); - const wardList = await dispatchAction(getWardByLocalBody({ id })); + const { res, data } = await request(routes.getWardByLocalBody, { + pathParams: { id: String(id) }, + }); + if (res && data) { + setWard([...initialWard, ...data.results]); + } setIsWardLoading(false); - setWard([...initialWard, ...wardList.data.results]); } else { setWard(initialLocalbodies); } }, - [dispatchAction] - ); - - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] + [props.id] ); const validateForm = () => { @@ -195,15 +180,20 @@ export default function UpdateResult(props: any) { const validForm = validateForm(); if (validForm) { setIsLoading(true); - const data = { + const rdata = { address: state.form.address ? state.form.address : undefined, local_body: state.form.local_body ? state.form.local_body : undefined, ward: state.form.ward, patient_created: state.form.patient_created === "true", }; - const res = await dispatchAction(partialUpdateExternalResult(id, data)); + + const { res, data } = await request(routes.partialUpdateExternalResult, { + pathParams: { id }, + body: rdata, + }); + setIsLoading(false); - if (res && res.data) { + if (res && data) { dispatch({ type: "set_form", form: initForm }); Notification.Success({ msg: "External Result updated successfully", @@ -213,7 +203,7 @@ export default function UpdateResult(props: any) { } }; - if (isLoading) { + if (isLoading || loading) { return ; } @@ -262,10 +252,7 @@ export default function UpdateResult(props: any) { options={localBody} optionLabel={(localBody) => localBody.name} optionValue={(localBody) => localBody.id} - onChange={(e) => [ - handleChange(e), - fetchWards(String(e.value)), - ]} + onChange={(e) => [handleChange(e), fetchWards(e.value)]} error={state.errors.local_body} /> )} diff --git a/src/Components/ExternalResult/models.ts b/src/Components/ExternalResult/models.ts new file mode 100644 index 00000000000..8ccaba04d05 --- /dev/null +++ b/src/Components/ExternalResult/models.ts @@ -0,0 +1,72 @@ +export interface IExternalResultUploadCsv { + sample_tests: any[]; +} + +export interface IExternalResult { + id: number; + name: string; + age: number; + age_in: string; + test_type: string; + result: string; + result_date: string; + patient_created: boolean; + gender: string; + source: string; + is_repeat: boolean; + mobile_number: string; + patient_status: string; + sample_type: string; + sample_collection_date: string; + patient_category: string; + srf_id: string; + district_object: { + id: number; + name: string; + state: number; + }; + district: number; + ward: number; + local_body: number; + address: string; + ward_object: { + id: number; + number: number; + name: string; + }; + local_body_object: { + id: number; + name: string; + }; +} + +export interface ILocalBodies { + id: number; + name: string; + state: number; + number: number; + body_type: number; + localbody_code: string; + district: number; +} + +export interface IDeleteExternalResult { + detail: string; +} + +export interface IPartialUpdateExternalResult { + address: string; + ward: number; + local_body: number; + patient_created: boolean; +} + +export interface ILocalBodyByDistrict { + id: number; + name: string; + state: number; +} + +export interface IExternalResultCsv { + sample_tests: Partial[]; +} diff --git a/src/Components/Facility/AddLocationForm.tsx b/src/Components/Facility/AddLocationForm.tsx index f0dd7893aca..e71b68cc95c 100644 --- a/src/Components/Facility/AddLocationForm.tsx +++ b/src/Components/Facility/AddLocationForm.tsx @@ -25,12 +25,14 @@ export const AddLocationForm = (props: LocationFormProps) => { const dispatchAction: any = useDispatch(); const [isLoading, setIsLoading] = useState(false); const [name, setName] = useState(""); + const [middlewareAddress, setMiddlewareAddress] = useState(""); const [description, setDescription] = useState(""); const [facilityName, setFacilityName] = useState(""); const [locationName, setLocationName] = useState(""); const [errors, setErrors] = useState({ name: "", description: "", + middlewareAddress: "", }); const headerText = !locationId ? "Add Location" : "Update Location"; const buttonText = !locationId ? "Add Location" : "Update Location"; @@ -51,6 +53,7 @@ export const AddLocationForm = (props: LocationFormProps) => { setName(res?.data?.name || ""); setLocationName(res?.data?.name || ""); setDescription(res?.data?.description || ""); + setMiddlewareAddress(res?.data?.middleware_address || ""); } setIsLoading(false); } @@ -62,6 +65,7 @@ export const AddLocationForm = (props: LocationFormProps) => { const error = { name: "", description: "", + middlewareAddress: "", }; if (name.trim().length === 0) { @@ -69,6 +73,16 @@ export const AddLocationForm = (props: LocationFormProps) => { formValid = false; } + if ( + middlewareAddress && + middlewareAddress.match( + /^(?!https?:\/\/)[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)*\.[a-zA-Z]{2,}$/ + ) === null + ) { + error.middlewareAddress = "Invalid Middleware Address"; + formValid = false; + } + setErrors(error); return formValid; }; @@ -83,6 +97,7 @@ export const AddLocationForm = (props: LocationFormProps) => { const data = { name, description, + middleware_address: middlewareAddress, }; const res = await dispatchAction( @@ -157,8 +172,18 @@ export const AddLocationForm = (props: LocationFormProps) => { error={errors.description} />
+
+ setMiddlewareAddress(e.value)} + error={errors.middlewareAddress} + /> +
-
+
navigate(`/facility/${facilityId}/location`, { diff --git a/src/Components/Facility/Consultations/NeurologicalTables.tsx b/src/Components/Facility/Consultations/NeurologicalTables.tsx index 89e9d598604..d33c7d6a311 100644 --- a/src/Components/Facility/Consultations/NeurologicalTables.tsx +++ b/src/Components/Facility/Consultations/NeurologicalTables.tsx @@ -28,10 +28,7 @@ const DataTable = (props: any) => { Right
-
+
{data.map((x: any, i: any) => { return (
{
Level Of Consciousness
-
+
{locData.map((x: any, i: any) => (
{ Total
-
+
{glasgowData.map((x: any, i: any) => { return (
{ handleChange(e)} error={state.errors?.middleware_address} diff --git a/src/Components/Facility/Investigations/InvestigationTable.tsx b/src/Components/Facility/Investigations/InvestigationTable.tsx index 8149e18496b..54e7e50bdc7 100644 --- a/src/Components/Facility/Investigations/InvestigationTable.tsx +++ b/src/Components/Facility/Investigations/InvestigationTable.tsx @@ -2,7 +2,6 @@ import ButtonV2 from "../../Common/components/ButtonV2"; import CareIcon from "../../../CAREUI/icons/CareIcon"; import { SelectFormField } from "../../Form/FormFields/SelectFormField"; import TextFormField from "../../Form/FormFields/TextFormField"; -import _ from "lodash"; import { classNames } from "../../../Utils/utils"; import { useState } from "react"; diff --git a/src/Components/Facility/Investigations/Reports/index.tsx b/src/Components/Facility/Investigations/Reports/index.tsx index ab5380c62ca..2db928386f1 100644 --- a/src/Components/Facility/Investigations/Reports/index.tsx +++ b/src/Components/Facility/Investigations/Reports/index.tsx @@ -17,7 +17,7 @@ import { InvestigationResponse } from "./types"; import Loading from "../../../Common/Loading"; import Page from "../../../Common/components/Page"; import ReportTable from "./ReportTable"; -import _ from "lodash"; +import { chain } from "lodash-es"; import { useDispatch } from "react-redux"; import { useRef } from "react"; @@ -175,7 +175,7 @@ const InvestigationReports = ({ id }: any) => { }) ); - const investigationList = _.chain(data) + const investigationList = chain(data) .compact() .flatten() .map((i) => ({ diff --git a/src/Components/Facility/Investigations/Reports/utils.tsx b/src/Components/Facility/Investigations/Reports/utils.tsx index 485a3e1d5f4..46b95800339 100644 --- a/src/Components/Facility/Investigations/Reports/utils.tsx +++ b/src/Components/Facility/Investigations/Reports/utils.tsx @@ -1,20 +1,20 @@ -import _ from "lodash"; +import { memoize, chain, findIndex } from "lodash-es"; import { InvestigationResponse } from "./types"; -export const transformData = _.memoize((data: InvestigationResponse) => { - const sessions = _.chain(data) +export const transformData = memoize((data: InvestigationResponse) => { + const sessions = chain(data) .map((value) => value.session_object) .uniqBy("session_external_id") .orderBy("session_created_date", "desc") .value(); - const groupByInvestigation = _.chain(data) + const groupByInvestigation = chain(data) .groupBy("investigation_object.external_id") .values() .value(); const reqData = groupByInvestigation.map((value) => { const sessionValues = Array.from({ length: sessions.length }); value.forEach((val) => { - const sessionIndex = _.findIndex(sessions, [ + const sessionIndex = findIndex(sessions, [ "session_external_id", val.session_object.session_external_id, ]); @@ -55,7 +55,7 @@ export const transformData = _.memoize((data: InvestigationResponse) => { return { sessions, data: reqData }; }); -export const getColorIndex = _.memoize( +export const getColorIndex = memoize( ({ max, min, value }: { min?: number; max?: number; value?: number }) => { if (!max && min && value) { // 1 => yellow color diff --git a/src/Components/Facility/Investigations/ShowInvestigation.tsx b/src/Components/Facility/Investigations/ShowInvestigation.tsx index 4c2826f6eea..4b8d1d065a7 100644 --- a/src/Components/Facility/Investigations/ShowInvestigation.tsx +++ b/src/Components/Facility/Investigations/ShowInvestigation.tsx @@ -9,7 +9,7 @@ import { import PageTitle from "../../Common/PageTitle"; import InvestigationTable from "./InvestigationTable"; -import _ from "lodash"; +import { set, chain } from "lodash-es"; import { navigate } from "raviger"; import * as Notification from "../../../Utils/Notifications.js"; @@ -110,7 +110,7 @@ export default function ShowInvestigation(props: any) { const handleValueChange = (value: any, name: string) => { const changedFields = { ...state.changedFields }; - _.set(changedFields, name, value); + set(changedFields, name, value); dispatch({ type: "set_changed_fields", changedFields }); }; @@ -147,7 +147,7 @@ export default function ShowInvestigation(props: any) { }; const handleUpdateCancel = useCallback(() => { - const changedValues = _.chain(state.initialValues) + const changedValues = chain(state.initialValues) .map((val: any, _key: string) => ({ id: val?.id, initialValue: val?.notes || val?.value || null, diff --git a/src/Components/Facility/Investigations/Table.tsx b/src/Components/Facility/Investigations/Table.tsx index 4d6ce4c2340..4bd7e841543 100644 --- a/src/Components/Facility/Investigations/Table.tsx +++ b/src/Components/Facility/Investigations/Table.tsx @@ -1,7 +1,7 @@ import { FieldChangeEvent } from "../../Form/FormFields/Utils"; import { SelectFormField } from "../../Form/FormFields/SelectFormField"; import TextFormField from "../../Form/FormFields/TextFormField"; -import _ from "lodash"; +import { set } from "lodash-es"; import { useState } from "react"; const TestRow = ({ data, value, onChange, i }: any) => { @@ -59,7 +59,7 @@ export const TestTable = ({ title, data, state, dispatch }: any) => { const handleValueChange = (value: any, name: string) => { const form = { ...state }; - _.set(form, name, value); + set(form, name, value); dispatch({ type: "set_form", form }); }; diff --git a/src/Components/Facility/LegacyFacilityCNS.tsx b/src/Components/Facility/LegacyFacilityCNS.tsx index b3a933b9a9f..5b0005c9daf 100644 --- a/src/Components/Facility/LegacyFacilityCNS.tsx +++ b/src/Components/Facility/LegacyFacilityCNS.tsx @@ -16,7 +16,7 @@ import Pagination from "../Common/Pagination"; import { PatientModel } from "../Patient/models"; import { FacilityModel } from "./models"; import AutocompleteFormField from "../Form/FormFields/Autocomplete"; -import { uniqBy } from "lodash"; +import { uniqBy } from "lodash-es"; import DialogModal from "../Common/Dialog"; import { LegacyMonitorCard } from "./LegacyMonitorCard"; diff --git a/src/Components/Facility/LocationManagement.tsx b/src/Components/Facility/LocationManagement.tsx index 38dcfc1f389..c018a7c222b 100644 --- a/src/Components/Facility/LocationManagement.tsx +++ b/src/Components/Facility/LocationManagement.tsx @@ -65,12 +65,24 @@ export default function LocationManagement({ facilityId }: Props) { ); } -const Location = ({ name, description, id }: LocationModel) => ( +const Location = ({ + name, + description, + middleware_address, + id, +}: LocationModel) => (
-
-

{name}

-

{description}

+
+

+ {name} +

+ {description || "-"} +

+

+

+ {middleware_address} +

diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index de2c6af698a..439194e85de 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -186,6 +186,7 @@ export interface LocationModel { id?: string; name?: string; description?: string; + middleware_address?: string; facility?: { name: string; }; diff --git a/src/Components/Form/AutoCompleteAsync.tsx b/src/Components/Form/AutoCompleteAsync.tsx index 5f33c6388c5..db2d1e888df 100644 --- a/src/Components/Form/AutoCompleteAsync.tsx +++ b/src/Components/Form/AutoCompleteAsync.tsx @@ -1,6 +1,6 @@ import { useEffect, useState, useMemo } from "react"; import { Combobox } from "@headlessui/react"; -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { DropdownTransition } from "../Common/components/HelperComponents"; import CareIcon from "../../CAREUI/icons/CareIcon"; import { diff --git a/src/Components/Form/Form.tsx b/src/Components/Form/Form.tsx index e934c4ffe0e..66383616034 100644 --- a/src/Components/Form/Form.tsx +++ b/src/Components/Form/Form.tsx @@ -1,4 +1,4 @@ -import { isEmpty, omitBy } from "lodash"; +import { isEmpty, omitBy } from "lodash-es"; import { useEffect, useMemo, useState } from "react"; import { classNames } from "../../Utils/utils"; import { Cancel, Submit } from "../Common/components/ButtonV2"; diff --git a/src/Components/Notifications/NotificationsList.tsx b/src/Components/Notifications/NotificationsList.tsx index 5e3aa65b3ec..5f124516a14 100644 --- a/src/Components/Notifications/NotificationsList.tsx +++ b/src/Components/Notifications/NotificationsList.tsx @@ -353,15 +353,17 @@ export default function NotificationsList({ manageResults = ( <> {data - .filter((notification: any) => showUnread ? notification.read_at === null : true) - .map((result: any) => ( - - ))} + .filter((notification: any) => + showUnread ? notification.read_at === null : true + ) + .map((result: any) => ( + + ))} {isLoading && (
diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index a84ad4a3e8b..ff749aed29e 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -1,40 +1,40 @@ import { navigate } from "raviger"; -import { useCallback, useState, useEffect, lazy } from "react"; +import dayjs from "dayjs"; +import { lazy, useCallback, useEffect, useState } from "react"; import { useDispatch } from "react-redux"; import { - TELEMEDICINE_ACTIONS, + PATIENT_CATEGORIES, REVIEW_AT_CHOICES, RHYTHM_CHOICES, - PATIENT_CATEGORIES, + TELEMEDICINE_ACTIONS, } from "../../Common/constants"; +import useAppHistory from "../../Common/hooks/useAppHistory"; import { statusType, useAbortableEffect } from "../../Common/utils"; import { createDailyReport, getConsultationDailyRoundsDetails, getDailyReport, - updateDailyReport, getPatient, + updateDailyReport, } from "../../Redux/actions"; +import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; import * as Notification from "../../Utils/Notifications"; import { formatDateTime } from "../../Utils/utils"; -import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; -import { Cancel, Submit } from "../Common/components/ButtonV2"; -import useAppHistory from "../../Common/hooks/useAppHistory"; -import { DraftSection, useAutoSaveReducer } from "../../Utils/AutoSave"; -import Page from "../Common/components/Page"; -import { FieldChangeEvent } from "../Form/FormFields/Utils"; -import TextFormField from "../Form/FormFields/TextFormField"; -import { SelectFormField } from "../Form/FormFields/SelectFormField"; -import PatientCategorySelect from "./PatientCategorySelect"; -import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; -import { SymptomsSelect } from "../Common/SymptomsSelect"; -import RangeAutocompleteFormField from "../Form/FormFields/RangeAutocompleteFormField"; import BloodPressureFormField, { meanArterialPressure, } from "../Common/BloodPressureFormField"; +import { SymptomsSelect } from "../Common/SymptomsSelect"; import TemperatureFormField from "../Common/TemperatureFormField"; -import dayjs from "dayjs"; +import { Cancel, Submit } from "../Common/components/ButtonV2"; +import Page from "../Common/components/Page"; +import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; +import RangeAutocompleteFormField from "../Form/FormFields/RangeAutocompleteFormField"; +import { SelectFormField } from "../Form/FormFields/SelectFormField"; +import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; +import TextFormField from "../Form/FormFields/TextFormField"; +import { FieldChangeEvent } from "../Form/FormFields/Utils"; +import PatientCategorySelect from "./PatientCategorySelect"; const Loading = lazy(() => import("../Common/Loading")); const initForm: any = { @@ -44,8 +44,7 @@ const initForm: any = { other_details: "", patient_category: "", current_health: 0, - recommend_discharge: false, - action: null, + actions: null, review_interval: 0, admitted_to: "", taken_at: null, @@ -274,7 +273,6 @@ export const DailyRounds = (props: any) => { physical_examination_info: state.form.physical_examination_info, other_details: state.form.other_details, consultation: consultationId, - recommend_discharge: JSON.parse(state.form.recommend_discharge), action: prevAction, review_interval: Number(prevReviewInterval), }; @@ -516,12 +514,6 @@ export const DailyRounds = (props: any) => { }} /> - - {state.form.rounds_type === "NORMAL" && ( <>

Vitals

diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index 6e83eb9bc91..aa3a837647d 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -815,7 +815,7 @@ export const PatientManager = () => { }} className="mr-5 w-full lg:w-fit" > - + Export ) : ( diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index 701b76b5e3a..2d297642abb 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -5,23 +5,24 @@ import { DISCHARGE_REASONS, PATIENT_CATEGORIES, RESPIRATORY_SUPPORT, + TELEMEDICINE_ACTIONS, } from "../../Common/constants"; import { ConsultationModel, PatientCategory } from "../Facility/models"; -import ABHAProfileModal from "../ABDM/ABHAProfileModal"; -import Beds from "../Facility/Consultations/Beds"; -import ButtonV2 from "../Common/components/ButtonV2"; -import CareIcon from "../../CAREUI/icons/CareIcon"; -import DialogModal from "../Common/Dialog"; import { Link } from "raviger"; +import { useState } from "react"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import useConfig from "../../Common/hooks/useConfig"; +import { getDimensionOrDash } from "../../Common/utils"; +import dayjs from "../../Utils/dayjs"; +import { formatDate, formatDateTime } from "../../Utils/utils.js"; +import ABHAProfileModal from "../ABDM/ABHAProfileModal"; import LinkABHANumberModal from "../ABDM/LinkABHANumberModal"; import LinkCareContextModal from "../ABDM/LinkCareContextModal"; +import DialogModal from "../Common/Dialog"; +import ButtonV2 from "../Common/components/ButtonV2"; +import Beds from "../Facility/Consultations/Beds"; import { PatientModel } from "./models"; -import { getDimensionOrDash } from "../../Common/utils"; -import useConfig from "../../Common/hooks/useConfig"; -import { useState } from "react"; -import { formatAge, formatDate, formatDateTime } from "../../Utils/utils.js"; -import dayjs from "../../Utils/dayjs"; export default function PatientInfoCard(props: { patient: PatientModel; @@ -170,29 +171,44 @@ export default function PatientInfoCard(props: { Discharged from CARE

)} -

- {formatAge(patient.age, patient.date_of_birth, true)} - - {patient.gender} - {consultation?.suggestion === "DC" && ( - <> - - - - Domiciliary Care - - - )} - {consultation?.is_readmission && ( - <> - - - - Readmitted - - - )} -

+
+
+ {patient.action && patient.action != 10 && ( +
+
+ + {" "} + { + TELEMEDICINE_ACTIONS.find( + (i) => i.id === patient.action + )?.desc + } + +
+
+ )} +
+
+ Age: {patient.age} years +
+
+
+
+ Gender: {patient.gender} +
+
+ {consultation?.suggestion === "DC" && ( +
+
+
+ Domiciliary Care + +
+
+
+ )} +
+
{[ ["Blood Group", patient.blood_group, patient.blood_group], diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index c515e7584c1..4fc6fef7fbe 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -61,7 +61,7 @@ import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import TransferPatientDialog from "../Facility/TransferPatientDialog"; import countryList from "../../Common/static/countries.json"; -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import useAppHistory from "../../Common/hooks/useAppHistory"; import useConfig from "../../Common/hooks/useConfig"; diff --git a/src/Components/Patient/SampleDetails.tsx b/src/Components/Patient/SampleDetails.tsx index e29df92f47f..672f3905792 100644 --- a/src/Components/Patient/SampleDetails.tsx +++ b/src/Components/Patient/SampleDetails.tsx @@ -7,7 +7,6 @@ import ButtonV2 from "../Common/components/ButtonV2"; import Card from "../../CAREUI/display/Card"; import { FileUpload } from "./FileUpload"; import Page from "../Common/components/Page"; -import _ from "lodash"; import { formatAge, formatDateTime } from "../../Utils/utils"; import { getTestSample } from "../../Redux/actions"; @@ -258,12 +257,16 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Status: {" "} - {_.startCase(_.camelCase(flow.status))} + + Status:{" "} + {" "} + {flow.status}
- Label:{" "} - {_.capitalize(flow.notes)} + + Label: + {" "} + {flow.notes?.toLowerCase()}
Created On :{" "} @@ -301,12 +304,16 @@ export const SampleDetails = ({ id }: DetailRoute) => {
- Status: - {_.startCase(_.camelCase(sampleDetails.status))} + + Status:{" "} + + {sampleDetails.status}
- Result: - {_.startCase(_.camelCase(sampleDetails.result))} + + Result:{" "} + + {sampleDetails.result}
Patient: @@ -339,11 +346,11 @@ export const SampleDetails = ({ id }: DetailRoute) => {
)} {sampleDetails.doctor_name && ( -
+
Doctor's Name:{" "} - {_.startCase(_.camelCase(sampleDetails.doctor_name))} + {sampleDetails.doctor_name}
)} {sampleDetails.diagnosis && ( @@ -423,10 +430,10 @@ export const SampleDetails = ({ id }: DetailRoute) => { )} {sampleDetails.sample_type && (
- + Sample Type:{" "} - {_.startCase(_.camelCase(sampleDetails.sample_type))} + {sampleDetails.sample_type}
)}
diff --git a/src/Components/Patient/SampleTestCard.tsx b/src/Components/Patient/SampleTestCard.tsx index 847be365181..0c299f0a1b5 100644 --- a/src/Components/Patient/SampleTestCard.tsx +++ b/src/Components/Patient/SampleTestCard.tsx @@ -6,7 +6,6 @@ import { SAMPLE_TEST_STATUS } from "../../Common/constants"; import { patchSample } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications"; import UpdateStatusDialog from "./UpdateStatusDialog"; -import _ from "lodash"; import { formatDateTime } from "../../Utils/utils"; import ButtonV2 from "../Common/components/ButtonV2"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; @@ -91,8 +90,8 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Status{" "}
-
- {_.startCase(_.camelCase(itemData.status))} +
+ {itemData.status?.toLowerCase()}
@@ -101,10 +100,11 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Sample Type{" "}
-
- {itemData.sample_type !== "OTHER TYPE" +
+ {(itemData.sample_type !== "OTHER TYPE" ? itemData.sample_type - : itemData.sample_type_other} + : itemData.sample_type_other + )?.toLowerCase()}
@@ -125,8 +125,8 @@ export const SampleTestCard = (props: SampleDetailsProps) => {
Result{" "}
-
- {_.startCase(_.camelCase(itemData.result))} +
+ {itemData.result?.toLowerCase()}
diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx index 5c9e24d548b..341e13e3c80 100644 --- a/src/Components/Patient/models.tsx +++ b/src/Components/Patient/models.tsx @@ -46,6 +46,7 @@ export interface AbhaObject { export interface PatientModel { test_id?: string; id?: string; + action?: number; name?: string; age?: number; allow_transfer?: boolean; diff --git a/src/Providers/AuthUserProvider.tsx b/src/Providers/AuthUserProvider.tsx index a64b38f630c..d515149341e 100644 --- a/src/Providers/AuthUserProvider.tsx +++ b/src/Providers/AuthUserProvider.tsx @@ -28,7 +28,7 @@ export default function AuthUserProvider({ children, unauthorized }: Props) { updateRefreshToken(true); setInterval( () => updateRefreshToken(), - jwt_token_refresh_interval ?? 5 * 60 * 3000 + jwt_token_refresh_interval ?? 5 * 60 * 1000 ); }, [data, jwt_token_refresh_interval]); diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index 26e006f4cea..1d2d6f4f7b6 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -6,8 +6,6 @@ import { } from "../Components/Medicine/models"; import { fireRequest, fireRequestForFiles } from "./fireRequest"; -import { ICreateHealthIdRequest } from "../Components/ABDM/models"; - export const getConfig = () => { return fireRequestForFiles("config"); }; @@ -668,13 +666,6 @@ export const externalResultList = (params: object, altKey: string) => { export const externalResult = (pathParam: object) => { return fireRequest("externalResult", [], {}, pathParam); }; -export const externalResultUploadCsv = (params: object) => { - return fireRequest("externalResultUploadCsv", [], params); -}; - -export const deleteExternalResult = (id: string) => { - return fireRequest("deleteExternalResult", [id], {}); -}; export const updateExternalResult = (id: number, params: object) => { return fireRequest("updateExternalResult", [], params, { id }); @@ -841,96 +832,23 @@ export const listAssets = (params: object) => fireRequest("listAssets", [], params); export const createAsset = (params: object) => fireRequest("createAsset", [], params); -export const getAssetUserLocation = (params: object) => - fireRequest("getAssetUserLocation", [], params); -export const createAssetUserLocation = (params: object) => - fireRequest("createAssetUserLocation", [], params); export const getAsset = (id: string) => fireRequest("getAsset", [], {}, { external_id: id }); export const deleteAsset = (id: string) => fireRequest("deleteAsset", [], {}, { external_id: id }); export const updateAsset = (id: string, params: object) => fireRequest("updateAsset", [], params, { external_id: id }); -export const partialUpdateAsset = (id: string, params: object) => - fireRequest("partialUpdateAsset", [], params, { external_id: id }); export const operateAsset = (id: string, params: object) => fireRequest("operateAsset", [], params, { external_id: id }); -export const listAssetTransaction = (params: object) => - fireRequest("listAssetTransaction", [], params); -export const getAssetTransaction = (id: string) => - fireRequest("getAssetTransaction", [], {}, { id }); - -export const listAssetService = (params: object, asset_external_id: string) => - fireRequest("listAssetService", [], params, { asset_external_id }); -export const getAssetService = ( - params: object, - asset_external_id: string, - external_id: string -) => - fireRequest("getAssetService", [], params, { - asset_external_id, - external_id, - }); -export const updateAssetService = ( - asset_external_id: string, - external_id: string, - params: object -) => - fireRequest("updateAssetService", [], params, { - asset_external_id, - external_id, - }); - // ABDM related -export const generateAadhaarOtp = (aadhaar: string) => - fireRequest("generateAadhaarOtp", [], { aadhaar }); export const resentAadhaarOtp = (txnId: string) => fireRequest("resendAadhaarOtp", [], { txnId }); -export const verifyAadhaarOtp = (txnId: string, otp: string) => - fireRequest("verifyAadhaarOtp", [], { txnId, otp }); - export const generateMobileOtp = (txnId: string, mobile: string) => fireRequest("generateMobileOtp", [], { txnId, mobile }); -export const checkAndGenerateMobileOtp = (txnId: string, mobile: string) => - fireRequest("checkAndGenerateMobileOtp", [], { txnId, mobile }); - -export const verifyMobileOtp = (txnId: string, otp: string) => - fireRequest("verifyMobileOtp", [], { txnId, otp }); - -export const createHealthId = (data: ICreateHealthIdRequest) => - fireRequest("createHealthId", [], data); - -export const searchByHealthId = (healthId: string) => - fireRequest("searchByHealthId", [], { healthId }); - -export const initiateAbdmAuthentication = ( - authMethod: string, - healthid: string -) => fireRequest("initiateAbdmAuthentication", [], { authMethod, healthid }); - -export const confirmWithAadhaarOtp = ( - txnId: string, - otp: string, - patientId?: string -) => fireRequest("confirmWithAadhaarOtp", [], { txnId, otp, patientId }); - -export const confirmWithMobileOtp = ( - txnId: string, - otp: string, - patientId?: string -) => fireRequest("confirmWithMobileOtp", [], { txnId, otp, patientId }); - -export const linkViaQR = (abha_details: any, patientId?: string) => { - return fireRequest("linkViaQR", [], { - ...abha_details, - patientId, - }); -}; - export const linkCareContext = ( consultationId: string, data: { name?: string; gender?: "M" | "F" | "O"; dob?: string } @@ -941,53 +859,16 @@ export const linkCareContext = ( }); }; -export const getAbhaCard = (patient: string, type: "pdf" | "png") => { - return fireRequest("getAbhaCard", [], { - patient, - type, - }); -}; - export const healthFacilityActions = { list: (params: object) => { return fireRequest("listHealthFacilities", [], params); }, - create: (data: object) => { - return fireRequest("createHealthFacility", [], data); - }, - - read: (id: string) => { - return fireRequest( - "getHealthFacility", - [], - {}, - { facility_id: id }, - undefined, - true - ); - }, - update: (id: string, data: object) => { return fireRequest("updateHealthFacility", [], data, { facility_id: id, }); }, - - partialUpdate: (id: string, data: object) => { - return fireRequest("partialUpdateHealthFacility", [], data, { - facility_id: id, - }); - }, - - registerService: (id: string) => { - return fireRequest( - "registerHealthFacilityAsService", - [], - {}, - { facility_id: id } - ); - }, }; export const listAssetAvailability = (params: object) => diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 8a4ca5cf1df..7e1ef24c058 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -1,6 +1,42 @@ import { IConfig } from "../Common/hooks/useConfig"; +import { + IAadhaarOtp, + IAadhaarOtpTBody, + ICheckAndGenerateMobileOtp, + IConfirmMobileOtp, + ICreateHealthIdRequest, + ICreateHealthIdResponse, + IGenerateMobileOtpTBody, + IHealthFacility, + IHealthId, + ILinkABHANumber, + ISearchByHealthIdTBody, + IVerifyAadhaarOtpTBody, + IcreateHealthFacilityTBody, + IgetAbhaCardTBody, + IinitiateAbdmAuthenticationTBody, + IpartialUpdateHealthFacilityTBody, +} from "../Components/ABDM/models"; import { AssetData } from "../Components/Assets/AssetTypes"; -import { LocationModel } from "../Components/Facility/models"; +import { + AssetBedBody, + AssetBedModel, + AssetData, + AssetLocationObject, + AssetService, + AssetServiceUpdate, + AssetTransaction, + AssetUpdate, +} from "../Components/Assets/AssetTypes"; +import { FacilityModel, LocationModel, WardModel } from "../Components/Facility/models"; +import { + IDeleteExternalResult, + IExternalResult, + IExternalResultCsv, + ILocalBodies, + ILocalBodyByDistrict, + IPartialUpdateExternalResult, +} from "../Components/ExternalResult/models"; import { Prescription } from "../Components/Medicine/models"; import { UserModel } from "../Components/Users/models"; import { PaginatedResponse } from "../Utils/request/types"; @@ -189,10 +225,14 @@ const routes = { getPermittedFacility: { path: "/api/v1/facility/{id}/", + method: "GET", + TRes: Type(), }, getAnyFacility: { path: "/api/v1/getallfacilities/{id}/", + method: "GET", + TRes: Type(), }, updateFacility: { @@ -226,6 +266,7 @@ const routes = { getFacilityAssetLocation: { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/", method: "GET", + TRes: Type(), }, updateFacilityAssetLocation: { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/", @@ -240,10 +281,13 @@ const routes = { listAssetBeds: { path: "/api/v1/assetbed/", method: "GET", + TRes: Type>(), }, createAssetBed: { path: "/api/v1/assetbed/", method: "POST", + TRes: Type(), + TBody: Type(), }, getAssetBed: { path: "/api/v1/assetbed/{external_id}/", @@ -256,6 +300,8 @@ const routes = { partialUpdateAssetBed: { path: "/api/v1/assetbed/{external_id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, deleteAssetBed: { path: "/api/v1/assetbed/{external_id}/", @@ -501,18 +547,25 @@ const routes = { // External Results externalResultList: { path: "/api/v1/external_result/", + method: "GET", + TRes: Type>(), }, externalResult: { path: "/api/v1/external_result/{id}/", + method: "GET", + TRes: Type(), }, externalResultUploadCsv: { path: "/api/v1/external_result/bulk_upsert/", method: "POST", + TBody: Type(), + TRes: Type(), }, deleteExternalResult: { - path: "/api/v1/external_result", + path: "/api/v1/external_result/{id}/", method: "DELETE", + TRes: Type(), }, updateExternalResult: { @@ -523,6 +576,8 @@ const routes = { partialUpdateExternalResult: { path: "/api/v1/external_result/{id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, // States @@ -547,9 +602,13 @@ const routes = { }, getAllLocalBodyByDistrict: { path: "/api/v1/district/{id}/get_all_local_body/", + method: "GET", + TRes: Type(), }, getLocalbodyByDistrict: { path: "/api/v1/district/{id}/local_bodies/", + method: "GET", + TRes: Type(), }, // Local Body @@ -572,6 +631,8 @@ const routes = { }, getWardByLocalBody: { path: "/api/v1/ward/?local_body={id}", + method: "GET", + TRes: Type>(), }, // Sample Test @@ -807,6 +868,7 @@ const routes = { listAssets: { path: "/api/v1/asset", method: "GET", + TRes: Type>(), }, createAsset: { path: "/api/v1/asset/", @@ -828,6 +890,7 @@ const routes = { deleteAsset: { path: "/api/v1/asset/{external_id}/", method: "DELETE", + TRes: Type(), }, updateAsset: { path: "/api/v1/asset/{external_id}/", @@ -836,6 +899,8 @@ const routes = { partialUpdateAsset: { path: "/api/v1/asset/{external_id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, // Asset transaction endpoints @@ -843,6 +908,7 @@ const routes = { listAssetTransaction: { path: "/api/v1/asset_transaction/", method: "GET", + TRes: Type>(), }, getAssetTransaction: { path: "/api/v1/asset_transaction/{id}", @@ -854,6 +920,7 @@ const routes = { listAssetService: { path: "/api/v1/asset/{asset_external_id}/service_records/", method: "GET", + TRes: Type>(), }, getAssetService: { path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}", @@ -862,110 +929,151 @@ const routes = { updateAssetService: { path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}", method: "PUT", - }, - - // ABDM HealthID endpoints - generateAadhaarOtp: { - path: "/api/v1/abdm/healthid/generate_aadhaar_otp/", - method: "POST", - }, - - resendAadhaarOtp: { - path: "/api/v1/abdm/healthid/resend_aadhaar_otp/", - method: "POST", - }, - - verifyAadhaarOtp: { - path: "/api/v1/abdm/healthid/verify_aadhaar_otp/", - method: "POST", - }, - - generateMobileOtp: { - path: "/api/v1/abdm/healthid/generate_mobile_otp/", - method: "POST", - }, - - checkAndGenerateMobileOtp: { - path: "/api/v1/abdm/healthid/check_and_generate_mobile_otp/", - method: "POST", - }, - - // TODO: resend mobile otp - verifyMobileOtp: { - path: "/api/v1/abdm/healthid/verify_mobile_otp/", - method: "POST", - }, - - createHealthId: { - path: "/api/v1/abdm/healthid/create_health_id/", - method: "POST", - }, - - searchByHealthId: { - path: "/api/v1/abdm/healthid/search_by_health_id/", - method: "POST", - }, - - initiateAbdmAuthentication: { - path: "/api/v1/abdm/healthid/auth_init/", - method: "POST", - }, - - confirmWithAadhaarOtp: { - path: "/api/v1/abdm/healthid/confirm_with_aadhaar_otp/", - method: "POST", - }, - - confirmWithMobileOtp: { - path: "/api/v1/abdm/healthid/confirm_with_mobile_otp/", - method: "POST", - }, - - linkViaQR: { - path: "/api/v1/abdm/healthid/link_via_qr/", - method: "POST", - }, - - linkCareContext: { - path: "/api/v1/abdm/healthid/add_care_context/", - method: "POST", - }, - - getAbhaCard: { - path: "/api/v1/abdm/healthid/get_abha_card/", - method: "POST", - }, - - // ABDM Health Facility - - listHealthFacility: { - path: "/api/v1/abdm/health_facility/", - method: "GET", - }, - - createHealthFacility: { - path: "/api/v1/abdm/health_facility/", - method: "POST", - }, - - getHealthFacility: { - path: "/api/v1/abdm/health_facility/{facility_id}/", - method: "GET", - }, - - updateHealthFacility: { - path: "/api/v1/abdm/health_facility/{facility_id}/", - method: "PUT", - }, - - partialUpdateHealthFacility: { - path: "/api/v1/abdm/health_facility/{facility_id}/", - method: "PATCH", - }, - - registerHealthFacilityAsService: { - path: "/api/v1/abdm/health_facility/{facility_id}/register_service/", - method: "POST", + TRes: Type(), + TBody: Type(), + }, + + abha: { + // ABDM HealthID endpoints + generateAadhaarOtp: { + path: "/api/v1/abdm/healthid/generate_aadhaar_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + resendAadhaarOtp: { + path: "/api/v1/abdm/healthid/resend_aadhaar_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + verifyAadhaarOtp: { + path: "/api/v1/abdm/healthid/verify_aadhaar_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + generateMobileOtp: { + path: "/api/v1/abdm/healthid/generate_mobile_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + checkAndGenerateMobileOtp: { + path: "/api/v1/abdm/healthid/check_and_generate_mobile_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + // TODO: resend mobile otp + verifyMobileOtp: { + path: "/api/v1/abdm/healthid/verify_mobile_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + createHealthId: { + path: "/api/v1/abdm/healthid/create_health_id/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + searchByHealthId: { + path: "/api/v1/abdm/healthid/search_by_health_id/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + initiateAbdmAuthentication: { + path: "/api/v1/abdm/healthid/auth_init/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + confirmWithAadhaarOtp: { + path: "/api/v1/abdm/healthid/confirm_with_aadhaar_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + confirmWithMobileOtp: { + path: "/api/v1/abdm/healthid/confirm_with_mobile_otp/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + linkViaQR: { + path: "/api/v1/abdm/healthid/link_via_qr/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + linkCareContext: { + path: "/api/v1/abdm/healthid/add_care_context/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + getAbhaCard: { + path: "/api/v1/abdm/healthid/get_abha_card/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + // ABDM Health Facility + + listHealthFacility: { + path: "/api/v1/abdm/health_facility/", + method: "GET", + }, + + createHealthFacility: { + path: "/api/v1/abdm/health_facility/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, + + getHealthFacility: { + path: "/api/v1/abdm/health_facility/{facility_id}/", + method: "GET", + TRes: Type(), + }, + + updateHealthFacility: { + path: "/api/v1/abdm/health_facility/{facility_id}/", + method: "PUT", + TRes: Type(), + TBody: Type(), + }, + + partialUpdateHealthFacility: { + path: "/api/v1/abdm/health_facility/{facility_id}/", + method: "PATCH", + TRes: Type(), + TBody: Type(), + }, + + registerHealthFacilityAsService: { + path: "/api/v1/abdm/health_facility/{facility_id}/register_service/", + method: "POST", + TRes: Type(), + TBody: Type(), + }, }, // Asset Availability endpoints diff --git a/src/Redux/fireRequest.tsx b/src/Redux/fireRequest.tsx index 9859ea540b6..bf083e23a92 100644 --- a/src/Redux/fireRequest.tsx +++ b/src/Redux/fireRequest.tsx @@ -1,6 +1,6 @@ import * as Notification from "../Utils/Notifications.js"; -import { isEmpty, omitBy } from "lodash"; +import { isEmpty, omitBy } from "lodash-es"; import { LocalStorageKeys } from "../Common/constants"; import api from "./api"; diff --git a/src/Utils/Notifications.js b/src/Utils/Notifications.js index b4393049531..298622c35ca 100644 --- a/src/Utils/Notifications.js +++ b/src/Utils/Notifications.js @@ -1,6 +1,6 @@ import { alert, Stack, defaultModules } from "@pnotify/core"; import * as PNotifyMobile from "@pnotify/mobile"; -import _ from "lodash"; +import { startCase, camelCase } from "lodash-es"; defaultModules.set(PNotifyMobile, {}); @@ -43,7 +43,7 @@ const notifyError = (error) => { errorMsg = error.detail; } else { for (let [key, value] of Object.entries(error)) { - let keyName = _.startCase(_.camelCase(key)); + let keyName = startCase(camelCase(key)); if (Array.isArray(value)) { const uniques = [...new Set(value)]; errorMsg += `${keyName} - ${uniques.splice(0, 5).join(", ")}`; diff --git a/src/Utils/request/request.ts b/src/Utils/request/request.ts index 2d735734aae..7cfb647d4b7 100644 --- a/src/Utils/request/request.ts +++ b/src/Utils/request/request.ts @@ -38,7 +38,8 @@ export default async function request( try { const res = await fetch(url, options); - const data: TData = await res.json(); + + const data = await getResponseBody(res); result = { res, @@ -61,3 +62,21 @@ export default async function request( ); return result; } + +async function getResponseBody(res: Response): Promise { + if (!(res.headers.get("content-length") !== "0")) { + return null as TData; + } + + const isJson = res.headers.get("content-type")?.includes("application/json"); + + if (!isJson) { + return (await res.text()) as TData; + } + + try { + return await res.json(); + } catch { + return (await res.text()) as TData; + } +}