From 73f49f969f0f6daea77e907f9e774b13dc58a254 Mon Sep 17 00:00:00 2001 From: Onkar Jadhav Date: Sat, 7 Oct 2023 12:38:38 +0530 Subject: [PATCH 1/9] Optimize lodash. Fixes #6006 --- package-lock.json | 18 ++++++++++++++++-- package.json | 4 ++-- src/Common/hooks/useAsyncOptions.ts | 2 +- src/Components/Common/Uptime.tsx | 2 +- .../ExternalResult/ExternalResultUpload.tsx | 4 ++-- .../Investigations/InvestigationTable.tsx | 1 - .../Facility/Investigations/Reports/index.tsx | 4 ++-- .../Facility/Investigations/Reports/utils.tsx | 12 ++++++------ .../Investigations/ShowInvestigation.tsx | 6 +++--- .../Facility/Investigations/Table.tsx | 4 ++-- src/Components/Facility/LegacyFacilityCNS.tsx | 2 +- src/Components/Form/AutoCompleteAsync.tsx | 2 +- src/Components/Form/Form.tsx | 2 +- src/Components/Patient/PatientRegister.tsx | 2 +- src/Components/Patient/SampleDetails.tsx | 10 +++++----- src/Components/Patient/SampleTestCard.tsx | 6 +++--- src/Redux/fireRequest.tsx | 2 +- src/Utils/Notifications.js | 4 ++-- 18 files changed, 50 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2a9fe3f520f..3a92ede81e6 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", @@ -73,7 +73,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": "*", @@ -5483,6 +5483,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", @@ -12454,6 +12463,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 246734dc3d4..08400b51f55 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", @@ -113,7 +113,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/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/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..48c5bb0433c 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -1,4 +1,4 @@ -import _ from "lodash"; +import { startCase, camelCase } from "lodash-es"; import { navigate } from "raviger"; import { lazy, useState } from "react"; import CSVReader from "react-csv-reader"; @@ -122,7 +122,7 @@ export default function ExternalResultUpload() { ? errors.map((error: any) => { return (
- {_.startCase(_.camelCase(error[0][0]))} -{" "} + {startCase(camelCase(error[0][0]))} -{" "} {error[0][1]}
); 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/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/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..518fae2e728 100644 --- a/src/Components/Patient/SampleDetails.tsx +++ b/src/Components/Patient/SampleDetails.tsx @@ -7,7 +7,7 @@ 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 { startCase, camelCase } from "lodash-es"; import { formatAge, formatDateTime } from "../../Utils/utils"; import { getTestSample } from "../../Redux/actions"; @@ -259,11 +259,11 @@ export const SampleDetails = ({ id }: DetailRoute) => {
Status: {" "} - {_.startCase(_.camelCase(flow.status))} + {startCase(camelCase(flow.status))}
Label:{" "} - {_.capitalize(flow.notes)} + {capitalize(flow.notes)}
Created On :{" "} @@ -343,7 +343,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { Doctor's Name:{" "} - {_.startCase(_.camelCase(sampleDetails.doctor_name))} + {startCase(camelCase(sampleDetails.doctor_name))}
)} {sampleDetails.diagnosis && ( @@ -426,7 +426,7 @@ export const SampleDetails = ({ id }: DetailRoute) => { Sample Type:{" "} - {_.startCase(_.camelCase(sampleDetails.sample_type))} + {startCase(camelCase(sampleDetails.sample_type))}
)} diff --git a/src/Components/Patient/SampleTestCard.tsx b/src/Components/Patient/SampleTestCard.tsx index 847be365181..5387154dd35 100644 --- a/src/Components/Patient/SampleTestCard.tsx +++ b/src/Components/Patient/SampleTestCard.tsx @@ -6,7 +6,7 @@ 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 { startCase, camelCase } from "lodash-es"; import { formatDateTime } from "../../Utils/utils"; import ButtonV2 from "../Common/components/ButtonV2"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; @@ -92,7 +92,7 @@ export const SampleTestCard = (props: SampleDetailsProps) => { Status{" "}
- {_.startCase(_.camelCase(itemData.status))} + {startCase(camelCase(itemData.status))}
@@ -126,7 +126,7 @@ export const SampleTestCard = (props: SampleDetailsProps) => { Result{" "}
- {_.startCase(_.camelCase(itemData.result))} + {startCase(camelCase(itemData.result))}
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(", ")}`; From be98542fd1985e7801886f9723211355a84b967b Mon Sep 17 00:00:00 2001 From: Onkar Jadhav Date: Thu, 12 Oct 2023 15:39:18 +0530 Subject: [PATCH 2/9] Replace startCase, camelCase with css capitalize --- .../ExternalResult/ExternalResultUpload.tsx | 7 ++-- src/Components/Patient/SampleDetails.tsx | 33 +++++++++++-------- src/Components/Patient/SampleTestCard.tsx | 16 ++++----- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 48c5bb0433c..c8bfef1a89c 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -1,4 +1,3 @@ -import { startCase, camelCase } from "lodash-es"; import { navigate } from "raviger"; import { lazy, useState } from "react"; import CSVReader from "react-csv-reader"; @@ -117,13 +116,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/Patient/SampleDetails.tsx b/src/Components/Patient/SampleDetails.tsx index 518fae2e728..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 { startCase, camelCase } from "lodash-es"; 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 5387154dd35..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 { startCase, camelCase } from "lodash-es"; 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()}
From 5e3012a2d4532aaddd806a8fc231b3bcd1467a5e Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Thu, 12 Oct 2023 15:52:26 +0530 Subject: [PATCH 3/9] fixes nutrition tab showing nursing analysis (#6437) --- ...sultationNeutritionTab.tsx => ConsultationNutritionTab.tsx} | 2 +- src/Components/Facility/ConsultationDetails/index.tsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) rename src/Components/Facility/ConsultationDetails/{ConsultationNeutritionTab.tsx => ConsultationNutritionTab.tsx} (86%) diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNutritionTab.tsx similarity index 86% rename from src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx rename to src/Components/Facility/ConsultationDetails/ConsultationNutritionTab.tsx index 69f130aca0d..74e250f0577 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationNutritionTab.tsx @@ -4,7 +4,7 @@ import { NutritionPlots } from "../Consultations/NutritionPlots"; const PageTitle = lazy(() => import("../../Common/PageTitle")); -export const ConsultationNeutritionTab = (props: ConsultationTabProps) => { +export const ConsultationNutritionTab = (props: ConsultationTabProps) => { return (
diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index 403f1752704..3d3271b16d5 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -41,6 +41,7 @@ import { ConsultationVentilatorTab } from "./ConsultationVentilatorTab"; import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab"; import { ConsultationDialysisTab } from "./ConsultationDialysisTab"; import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab"; +import { ConsultationNutritionTab } from "./ConsultationNutritionTab"; const Loading = lazy(() => import("../../Common/Loading")); const PageTitle = lazy(() => import("../../Common/PageTitle")); @@ -65,7 +66,7 @@ const TABS = { NURSING: ConsultationNursingTab, NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab, VENTILATOR: ConsultationVentilatorTab, - NUTRITION: ConsultationNursingTab, + NUTRITION: ConsultationNutritionTab, PRESSURE_SORE: ConsultationPressureSoreTab, DIALYSIS: ConsultationDialysisTab, }; From 362003cb361d6e199fb7e47e03946091ce3ef32d Mon Sep 17 00:00:00 2001 From: Abhiuday Gupta <77210185+cp-Coder@users.noreply.github.com> Date: Tue, 17 Oct 2023 06:29:10 +0530 Subject: [PATCH 4/9] fix: add action and recommend discharge data on patient consultation page (#5347) * fix(daily_round): added recommend_discharge as action * fix: rebased the branch --- src/Common/constants.tsx | 1 + src/Components/Patient/DailyRounds.tsx | 44 +++++------- src/Components/Patient/PatientInfoCard.tsx | 82 +++++++++++++--------- src/Components/Patient/models.tsx | 1 + 4 files changed, 69 insertions(+), 59 deletions(-) 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/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/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/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; From dd16bfad7e913fbad5421667587a934bc883e92e Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Tue, 17 Oct 2023 06:30:37 +0530 Subject: [PATCH 5/9] Middleware override feature in asset location (#6368) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Middleware override feature in asset location * Fix tooltip clipping * Fix Flaky Cypress test in Facility Module (#6384) * fix random facility page failure * fix random facility page failure * Revert "Prescriptions: Shrink discontinued prescriptions + Flip MAR timeline + Freeze primary columns in horizontal scroll (#6282)" (#6386) This reverts commit 5009a86abfb96e9b21f8371635176e4c3f73c5a7. * Refactor Asset Model Import Formatting (#6388) * Refactor Asset Model Import Formatting * fixes to warranty_amc_end_of_validity * Fix asset import file for cypress * Refactor: replaced Dispatch to useQuery/Request of src/Components/Auth/log… (#6333) * Refactor: replaced useDispatch to useQuery of src/Components/Auth/login.tsx * fix:useQuery changed to Request * feat: replaced dispatch with request * fix: types and added pathparams * fix: request body error * Update Login.tsx * change:Tres to Tbody * fixes: response type change * Update package-lock.json * fix hover height (#6352) * Split routes of App Router (#6363) * Prescription: show prescribed on & by and discontinued date in detail card (#6365) * Fixed bug in location picker in update facility page (#6377) * add all cases for map rendering * implement location pointer on searched location * Added cam auto reset in asset config page (#6375) * 💊 Adds support for editing prescriptions + Adds `useSlug` hook (#6369) * Adds hook: `useSlug` * bug fix: NumericWithUnits field not showing intial value * Form: support for showing global errors * Adds support for editing prescriptions (fixes #6340) * Fix cypress * fix cypress * Improve design --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Co-authored-by: Rithvik Nishad Co-authored-by: Aaron Jevil Nazareth <93522968+jevil25@users.noreply.github.com> Co-authored-by: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Co-authored-by: Gampa Sri Harsh <114745442+sriharsh05@users.noreply.github.com> Co-authored-by: Tasnimul H. Tauhid --- cypress/pageobject/Asset/AssetCreation.ts | 2 +- src/CAREUI/misc/PaginatedList.tsx | 2 +- .../Assets/AssetType/HL7Monitor.tsx | 41 +++++++++++++++---- .../Assets/AssetType/ONVIFCamera.tsx | 30 ++++++++++++-- src/Components/Facility/AddLocationForm.tsx | 27 +++++++++++- src/Components/Facility/FacilityConfigure.tsx | 1 + .../Facility/LocationManagement.tsx | 20 +++++++-- src/Components/Facility/models.tsx | 1 + 8 files changed, 104 insertions(+), 20 deletions(-) diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts index 45356a1a4e7..2c727820292 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); } diff --git a/src/CAREUI/misc/PaginatedList.tsx b/src/CAREUI/misc/PaginatedList.tsx index 28ee17650ad..3b699177d9e 100644 --- a/src/CAREUI/misc/PaginatedList.tsx +++ b/src/CAREUI/misc/PaginatedList.tsx @@ -30,7 +30,7 @@ function useContextualized() { 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/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index 55f4d0c258e..6f896ac343f 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -74,15 +74,16 @@ const HL7Monitor = (props: HL7MonitorProps) => { }); } 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 ; return ( @@ -93,11 +94,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 +146,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..cb9cc1cc497 100644 --- a/src/Components/Assets/AssetType/ONVIFCamera.tsx +++ b/src/Components/Assets/AssetType/ONVIFCamera.tsx @@ -17,6 +17,7 @@ import TextFormField from "../../Form/FormFields/TextFormField"; import { Submit } from "../../Common/components/ButtonV2"; import { SyntheticEvent } from "react"; import useAuthUser from "../../../Common/hooks/useAuthUser"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; interface Props { assetId: string; @@ -90,11 +91,11 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { 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"); } }; @@ -139,6 +140,9 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { setLoadingAddPreset(false); }; + const fallbackMiddleware = + asset?.location_object?.middleware_address || facilityMiddlewareHostname; + if (isLoading) return ; return ( @@ -148,8 +152,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/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/FacilityConfigure.tsx b/src/Components/Facility/FacilityConfigure.tsx index 6f0a8a9869c..fd5754b441d 100644 --- a/src/Components/Facility/FacilityConfigure.tsx +++ b/src/Components/Facility/FacilityConfigure.tsx @@ -155,6 +155,7 @@ export const FacilityConfigure = (props: any) => { handleChange(e)} error={state.errors?.middleware_address} 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; }; From c7765246999d6ad593ace5da165f64b2a611fd4e Mon Sep 17 00:00:00 2001 From: konavivekramakrishna <101407963+konavivekramakrishna@users.noreply.github.com> Date: Tue, 17 Oct 2023 06:33:24 +0530 Subject: [PATCH 6/9] Replaced useDispatch with useQuery and request in ExternalResult (src/Components/ExternalResult/** ) (#6402) * replaced dispatch with useQuery in ResultList * replaced useDispatch with useQuery in listfilter * replaced useDispatch with reqeust in ExternalResultUpload * fixed filtering * fix ResultList * rm clg * make code readable * fix build * removed useEffect and useState from ResultList.tsx * Apply changes from code review --------- Co-authored-by: rithviknishad --- .../ExternalResult/ExternalResultUpload.tsx | 35 +++-- src/Components/ExternalResult/ListFilter.tsx | 115 +++++++++-------- src/Components/ExternalResult/ResultItem.tsx | 117 ++++++++--------- src/Components/ExternalResult/ResultList.tsx | 106 ++++++--------- .../ExternalResult/ResultUpdate.tsx | 121 ++++++++---------- src/Components/ExternalResult/models.ts | 72 +++++++++++ src/Redux/actions.tsx | 7 - src/Redux/api.tsx | 27 +++- src/Utils/request/request.ts | 20 ++- 9 files changed, 340 insertions(+), 280 deletions(-) create mode 100644 src/Components/ExternalResult/models.ts diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 5f39cb213a4..a9d5c29bfe8 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -2,21 +2,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 +32,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); } 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/Redux/actions.tsx b/src/Redux/actions.tsx index 26e006f4cea..06a12796737 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -668,13 +668,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 }); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 8a4ca5cf1df..02c38afcc5a 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -1,6 +1,14 @@ import { IConfig } from "../Common/hooks/useConfig"; import { AssetData } from "../Components/Assets/AssetTypes"; -import { LocationModel } from "../Components/Facility/models"; +import { + IDeleteExternalResult, + IExternalResult, + IExternalResultCsv, + ILocalBodies, + ILocalBodyByDistrict, + IPartialUpdateExternalResult, +} from "../Components/ExternalResult/models"; +import { LocationModel, WardModel } from "../Components/Facility/models"; import { Prescription } from "../Components/Medicine/models"; import { UserModel } from "../Components/Users/models"; import { PaginatedResponse } from "../Utils/request/types"; @@ -501,18 +509,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 +538,8 @@ const routes = { partialUpdateExternalResult: { path: "/api/v1/external_result/{id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, // States @@ -547,9 +564,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 +593,8 @@ const routes = { }, getWardByLocalBody: { path: "/api/v1/ward/?local_body={id}", + method: "GET", + TRes: Type>(), }, // Sample Test diff --git a/src/Utils/request/request.ts b/src/Utils/request/request.ts index 2d735734aae..d428107b64c 100644 --- a/src/Utils/request/request.ts +++ b/src/Utils/request/request.ts @@ -38,7 +38,7 @@ 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 +61,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; + } +} From 7e29f250190ad65ff7726842a133694fd6bf5b86 Mon Sep 17 00:00:00 2001 From: Gokulram A Date: Tue, 17 Oct 2023 06:33:58 +0530 Subject: [PATCH 7/9] Added cypress test to verify warranty expiry label on an asset (#6428) * added cypress test to verify warranty expiry label * updated spec to check if the warranty expiry label goes off once updated * verify the last change --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- cypress/e2e/assets_spec/assets_manage.cy.ts | 33 +++++++++++++++++++++ cypress/pageobject/Asset/AssetCreation.ts | 27 +++++++++++++++-- src/CAREUI/display/Chip.tsx | 2 ++ src/Components/Assets/AssetsList.tsx | 3 ++ 4 files changed, 63 insertions(+), 2 deletions(-) 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 2c727820292..93bbc87c9ab 100644 --- a/cypress/pageobject/Asset/AssetCreation.ts +++ b/cypress/pageobject/Asset/AssetCreation.ts @@ -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/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 ( Date: Tue, 17 Oct 2023 06:34:39 +0530 Subject: [PATCH 8/9] Replaced export button Icon with relevant Export Icon (#6451) --- src/Components/Common/Export.tsx | 2 +- src/Components/Patient/ManagePatients.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 ) : ( From 9b362c104390e5a60bf8059b8ef38a217987b007 Mon Sep 17 00:00:00 2001 From: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com> Date: Wed, 18 Oct 2023 06:46:31 +0530 Subject: [PATCH 9/9] Replaced dispatch with useQuery and request in Asset module (#6374) * changed to request in HL7monitor.tsx * replaced dispatch in assetTypes * changed dispatch in filteer, import files * changed all dispatch requests with useQuery and request in asset module * added TBody to various endpoints * fixed assetfilter page * fixed asset import modal page * made requested changes * made requested changes * reverted to assetBedModel * fixed delete asset function * fixed after delete redirection * removed extra setstates * updated failing cypress tests for asset module * removed useEffects * removed changes from cypress files * fixed merge issues * fixed transactions issue * remove console logs ---------- Co-authored-by: Rithvik Nishad --- cypress/e2e/assets_spec/asset_homepage.cy.ts | 2 +- src/Components/Assets/AssetFilter.tsx | 79 +++------ src/Components/Assets/AssetImportModal.tsx | 22 +-- src/Components/Assets/AssetManage.tsx | 129 ++++++-------- .../Assets/AssetServiceEditModal.tsx | 20 ++- .../Assets/AssetType/HL7Monitor.tsx | 36 ++-- .../Assets/AssetType/ONVIFCamera.tsx | 58 +++--- src/Components/Assets/AssetTypes.tsx | 12 ++ src/Components/Assets/AssetsList.tsx | 166 ++++++------------ .../Assets/configure/MonitorConfigure.tsx | 78 ++++---- src/Redux/actions.tsx | 32 ---- src/Redux/api.tsx | 33 +++- 12 files changed, 270 insertions(+), 397 deletions(-) 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/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 6f896ac343f..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,9 +58,10 @@ 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", @@ -85,7 +79,7 @@ const HL7Monitor = (props: HL7MonitorProps) => { const fallbackMiddleware = asset?.location_object?.middleware_address || facilityMiddlewareHostname; - if (isLoading) return ; + if (isLoading || loading || !facility) return ; return (
diff --git a/src/Components/Assets/AssetType/ONVIFCamera.tsx b/src/Components/Assets/AssetType/ONVIFCamera.tsx index cb9cc1cc497..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"; @@ -17,8 +11,14 @@ import TextFormField from "../../Form/FormFields/TextFormField"; 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; @@ -44,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) { @@ -84,9 +80,10 @@ 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?.(); @@ -111,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", @@ -139,12 +135,12 @@ const ONVIFCamera = ({ assetId, facilityId, asset, onUpdated }: Props) => { } setLoadingAddPreset(false); }; + + if (isLoading || loading || !facility) return ; const fallbackMiddleware = asset?.location_object?.middleware_address || facilityMiddlewareHostname; - if (isLoading) return ; - return (
{["DistrictAdmin", "StateAdmin"].includes(authUser.user_type) && ( 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 1c366a23e7e..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", diff --git a/src/Components/Assets/configure/MonitorConfigure.tsx b/src/Components/Assets/configure/MonitorConfigure.tsx index 1d469335ce2..d1b24b24959 100644 --- a/src/Components/Assets/configure/MonitorConfigure.tsx +++ b/src/Components/Assets/configure/MonitorConfigure.tsx @@ -1,62 +1,50 @@ -import { Dispatch, useEffect, useState } from "react"; +import { useState } from "react"; import { BedSelect } from "../../Common/BedSelect"; import { BedModel } from "../../Facility/models"; import { AssetData } from "../AssetTypes"; -import { - createAssetBed, - listAssetBeds, - partialUpdateAssetBed, -} from "../../../Redux/actions"; import * as Notification from "../../../Utils/Notifications.js"; -import { useDispatch } from "react-redux"; import { Submit } from "../../Common/components/ButtonV2"; import { FieldLabel } from "../../Form/FormFields/FormField"; +import request from "../../../Utils/request/request"; +import routes from "../../../Redux/api"; +import useQuery from "../../../Utils/request/useQuery"; -const saveLink = (assetId: string, bedId: string, dispatch: Dispatch) => { - 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/Redux/actions.tsx b/src/Redux/actions.tsx index 06a12796737..7a92b1a7658 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -834,47 +834,15 @@ 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 }); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 02c38afcc5a..a05d461c98d 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -1,5 +1,17 @@ import { IConfig } from "../Common/hooks/useConfig"; -import { AssetData } from "../Components/Assets/AssetTypes"; + +import { + AssetBedBody, + AssetBedModel, + AssetData, + AssetLocationObject, + AssetService, + AssetServiceUpdate, + AssetTransaction, + AssetUpdate, +} from "../Components/Assets/AssetTypes"; +import { FacilityModel, LocationModel, WardModel } from "../Components/Facility/models"; + import { IDeleteExternalResult, IExternalResult, @@ -8,7 +20,6 @@ import { ILocalBodyByDistrict, IPartialUpdateExternalResult, } from "../Components/ExternalResult/models"; -import { LocationModel, WardModel } from "../Components/Facility/models"; import { Prescription } from "../Components/Medicine/models"; import { UserModel } from "../Components/Users/models"; import { PaginatedResponse } from "../Utils/request/types"; @@ -197,10 +208,14 @@ const routes = { getPermittedFacility: { path: "/api/v1/facility/{id}/", + method: "GET", + TRes: Type(), }, getAnyFacility: { path: "/api/v1/getallfacilities/{id}/", + method: "GET", + TRes: Type(), }, updateFacility: { @@ -234,6 +249,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}/", @@ -248,10 +264,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}/", @@ -264,6 +283,8 @@ const routes = { partialUpdateAssetBed: { path: "/api/v1/assetbed/{external_id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, deleteAssetBed: { path: "/api/v1/assetbed/{external_id}/", @@ -830,6 +851,7 @@ const routes = { listAssets: { path: "/api/v1/asset", method: "GET", + TRes: Type>(), }, createAsset: { path: "/api/v1/asset/", @@ -851,6 +873,7 @@ const routes = { deleteAsset: { path: "/api/v1/asset/{external_id}/", method: "DELETE", + TRes: Type(), }, updateAsset: { path: "/api/v1/asset/{external_id}/", @@ -859,6 +882,8 @@ const routes = { partialUpdateAsset: { path: "/api/v1/asset/{external_id}/", method: "PATCH", + TRes: Type(), + TBody: Type(), }, // Asset transaction endpoints @@ -866,6 +891,7 @@ const routes = { listAssetTransaction: { path: "/api/v1/asset_transaction/", method: "GET", + TRes: Type>(), }, getAssetTransaction: { path: "/api/v1/asset_transaction/{id}", @@ -877,6 +903,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}", @@ -885,6 +912,8 @@ const routes = { updateAssetService: { path: "/api/v1/asset/{asset_external_id}/service_records/{external_id}", method: "PUT", + TRes: Type(), + TBody: Type(), }, // ABDM HealthID endpoints