From 5cde45749e73ef99bd0554a2ebd925c3e4484ea1 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 28 Nov 2023 15:34:20 +0530 Subject: [PATCH 01/12] Allow route to facility to be edited if null (#6742) --- src/Components/Facility/ConsultationForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index ed3934441a8..486adfa5170 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -946,7 +946,7 @@ export const ConsultationForm = (props: any) => { required label="Route to Facility" {...field("route_to_facility")} - disabled={isUpdate} + disabled={isUpdate && !!state.form.route_to_facility} // For backwards compatibility; Allow in edit form only if route_to_facility is not set previously /> From 4da37ba8437bf5483c3fae365e6f356f299568de Mon Sep 17 00:00:00 2001 From: Gampa Sri Harsh <114745442+sriharsh05@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:21:45 +0530 Subject: [PATCH 02/12] replace useDispatch with useQuery and request in FacilityCard, FacililyHome and FacilityUsers (#6575) * replace useDispatch with useQuery and request in facilityCard, facilityHome and facilityUsers * remove delete model and let useQuery handel delete response * remove unused useStates variables in FacilityUsers * replace useState variables with useQuery variables and also replaced useEffect with useQuery * handle loading states * Implemented a dedicated component for the triage table in FacilityHome * Implemented a dedicated component for the doctor list in FacilityHome * implemeted a dedicated component for bed capacity in facility home * remove trailing slash * Removed unused fire requests * Fix deleting bed and doctor capacity bug and remove redundant code * fix doctor capacity total count bug --------- Co-authored-by: Rithvik Nishad --- .../Facility/FacilityBedCapacity.tsx | 119 +++++ src/Components/Facility/FacilityCard.tsx | 21 +- .../Facility/FacilityDoctorList.tsx | 121 +++++ src/Components/Facility/FacilityHome.tsx | 460 +++--------------- .../Facility/FacilityHomeTriage.tsx | 88 ++++ src/Components/Facility/FacilityUsers.tsx | 205 ++++---- src/Components/Facility/models.tsx | 13 + src/Redux/actions.tsx | 7 - src/Redux/api.tsx | 23 +- 9 files changed, 529 insertions(+), 528 deletions(-) create mode 100644 src/Components/Facility/FacilityBedCapacity.tsx create mode 100644 src/Components/Facility/FacilityDoctorList.tsx create mode 100644 src/Components/Facility/FacilityHomeTriage.tsx diff --git a/src/Components/Facility/FacilityBedCapacity.tsx b/src/Components/Facility/FacilityBedCapacity.tsx new file mode 100644 index 00000000000..33583840643 --- /dev/null +++ b/src/Components/Facility/FacilityBedCapacity.tsx @@ -0,0 +1,119 @@ +import { useState } from "react"; +import { getBedTypes } from "../../Common/constants"; +import routes from "../../Redux/api"; +import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; +import useQuery from "../../Utils/request/useQuery"; +import DialogModal from "../Common/Dialog"; +import ButtonV2 from "../Common/components/ButtonV2"; +import { BedCapacity } from "./BedCapacity"; +import BedTypeCard from "./BedTypeCard"; +import useConfig from "../../Common/hooks/useConfig"; + +export const FacilityBedCapacity = (props: any) => { + const [bedCapacityModalOpen, setBedCapacityModalOpen] = useState(false); + const config = useConfig(); + + const capacityQuery = useQuery(routes.getCapacity, { + pathParams: { facilityId: props.facilityId }, + }); + + let capacityList: any = null; + if (!capacityQuery.data || !capacityQuery.data.results.length) { + capacityList = ( +
+ No Bed Types Found +
+ ); + } else { + const totalBedCount = capacityQuery.data.results.reduce( + (acc, x) => acc + (x.total_capacity ? x.total_capacity : 0), + 0 + ); + const totalOccupiedBedCount = capacityQuery.data.results.reduce( + (acc, x) => acc + (x.current_capacity ? x.current_capacity : 0), + 0 + ); + + capacityList = ( +
+ { + return; + }} + /> + {getBedTypes(config).map((x) => { + const res = capacityQuery.data?.results.find((data) => { + return data.room_type === x.id; + }); + if ( + res && + res.current_capacity !== undefined && + res.total_capacity !== undefined + ) { + const removeCurrentBedType = (bedTypeId: number | undefined) => { + if (capacityQuery.data !== undefined) { + capacityQuery.data.results.filter((i) => i.id !== bedTypeId); + capacityQuery.refetch(); + } + }; + return ( + { + capacityQuery.refetch(); + }} + /> + ); + } + })} +
+ ); + } + + return ( +
+
+
+
Bed Capacity
+ setBedCapacityModalOpen(true)} + authorizeFor={NonReadOnlyUsers} + > + + Add More Bed Types + +
+
{capacityList}
+
+ + {bedCapacityModalOpen && ( + setBedCapacityModalOpen(false)} + title="Add Bed Capacity" + className="max-w-md md:min-w-[600px]" + > + setBedCapacityModalOpen(false)} + handleUpdate={async () => { + capacityQuery.refetch(); + }} + /> + + )} +
+ ); +}; diff --git a/src/Components/Facility/FacilityCard.tsx b/src/Components/Facility/FacilityCard.tsx index e8e0dbef70f..900b6b00f47 100644 --- a/src/Components/Facility/FacilityCard.tsx +++ b/src/Components/Facility/FacilityCard.tsx @@ -1,9 +1,6 @@ import { useState } from "react"; -import { useDispatch } from "react-redux"; import { Link } from "raviger"; import { useTranslation } from "react-i18next"; - -import { sendNotificationMessages } from "../../Redux/actions"; import { FACILITY_FEATURE_TYPES } from "../../Common/constants"; import ButtonV2, { Cancel, Submit } from "../Common/components/ButtonV2"; import * as Notification from "../../Utils/Notifications.js"; @@ -14,26 +11,28 @@ import DialogModal from "../Common/Dialog"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import useConfig from "../../Common/hooks/useConfig"; import { classNames } from "../../Utils/utils"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; export const FacilityCard = (props: { facility: any; userType: any }) => { const { facility, userType } = props; const { kasp_string } = useConfig(); const { t } = useTranslation(); - const dispatchAction: any = useDispatch(); const [notifyModalFor, setNotifyModalFor] = useState(undefined); const [notifyMessage, setNotifyMessage] = useState(""); const [notifyError, setNotifyError] = useState(""); const handleNotifySubmit = async (id: any) => { - const data = { - facility: id, - message: notifyMessage, - }; - if (data.message.trim().length >= 1) { + if (notifyMessage.trim().length >= 1) { setNotifyError(""); - const res = await dispatchAction(sendNotificationMessages(data)); - if (res && res.status == 204) { + const { res } = await request(routes.sendNotificationMessages, { + body: { + facility: id, + message: notifyMessage, + }, + }); + if (res?.ok) { Notification.Success({ msg: "Facility Notified", }); diff --git a/src/Components/Facility/FacilityDoctorList.tsx b/src/Components/Facility/FacilityDoctorList.tsx new file mode 100644 index 00000000000..e6bbc7f7f3b --- /dev/null +++ b/src/Components/Facility/FacilityDoctorList.tsx @@ -0,0 +1,121 @@ +import { useState } from "react"; +import { DOCTOR_SPECIALIZATION } from "../../Common/constants"; +import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; +import ButtonV2 from "../Common/components/ButtonV2"; +import DialogModal from "../Common/Dialog"; +import { DoctorCapacity } from "./DoctorCapacity"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import { DoctorModal } from "./models"; +import DoctorsCountCard from "./DoctorsCountCard"; +import { DoctorIcon } from "../TeleIcu/Icons/DoctorIcon"; + +export const FacilityDoctorList = (props: any) => { + const [doctorCapacityModalOpen, setDoctorCapacityModalOpen] = useState(false); + const [totalDoctors, setTotalDoctors] = useState(0); + + const doctorQuery = useQuery(routes.listDoctor, { + pathParams: { facilityId: props.facilityId }, + onResponse: ({ res, data }) => { + if (res?.ok && data) { + let totalCount = 0; + data.results.map((doctor: DoctorModal) => { + if (doctor.count) { + totalCount += doctor.count; + } + }); + setTotalDoctors(totalCount); + } + }, + }); + + let doctorList: any = null; + if (!doctorQuery.data || !doctorQuery.data.results.length) { + doctorList = ( +
+ No Doctors Found +
+ ); + } else { + doctorList = ( +
+ {/* Total Doctors Count Card */} +
+
+
+
+ +
+
+
+ Total Doctors +
+

{totalDoctors}

+
+
+
+
+ + {doctorQuery.data.results.map((data: DoctorModal) => { + const removeCurrentDoctorData = (doctorId: number | undefined) => { + if (doctorQuery.data !== undefined) { + doctorQuery.data?.results.filter( + (i: DoctorModal) => i.id !== doctorId + ); + doctorQuery.refetch(); + } + }; + + return ( + { + doctorQuery.refetch(); + }} + {...data} + removeDoctor={removeCurrentDoctorData} + /> + ); + })} +
+ ); + } + + return ( +
+
+
+
Doctors List
+ setDoctorCapacityModalOpen(true)} + disabled={doctorList.length === DOCTOR_SPECIALIZATION.length} + authorizeFor={NonReadOnlyUsers} + > + + Add Doctor Types + +
+
{doctorList}
+
+ + {doctorCapacityModalOpen && ( + setDoctorCapacityModalOpen(false)} + title="Add Doctor Capacity" + className="max-w-md md:min-w-[600px]" + > + setDoctorCapacityModalOpen(false)} + handleUpdate={async () => { + doctorQuery.refetch(); + }} + /> + + )} +
+ ); +}; diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx index f14057d4a91..e971f0f3b9d 100644 --- a/src/Components/Facility/FacilityHome.tsx +++ b/src/Components/Facility/FacilityHome.tsx @@ -1,50 +1,32 @@ import * as Notification from "../../Utils/Notifications.js"; import AuthorizeFor, { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; -import { - CapacityModal, - DoctorModal, - FacilityModel, - PatientStatsModel, -} from "./models"; -import { - DOCTOR_SPECIALIZATION, - FACILITY_FEATURE_TYPES, - USER_TYPES, - getBedTypes, -} from "../../Common/constants"; +import { FacilityModel } from "./models"; +import { FACILITY_FEATURE_TYPES, USER_TYPES } from "../../Common/constants"; import DropdownMenu, { DropdownItem } from "../Common/components/Menu"; -import { - deleteFacility, - getPermittedFacility, - getTriageInfo, - listCapacity, - listDoctor, -} from "../../Redux/actions"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { lazy, useCallback, useState } from "react"; -import { useDispatch } from "react-redux"; -import { BedCapacity } from "./BedCapacity"; -import BedTypeCard from "./BedTypeCard"; +import { lazy, useState } from "react"; + import ButtonV2 from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; import Chip from "../../CAREUI/display/Chip"; import ConfirmDialog from "../Common/ConfirmDialog"; import ContactLink from "../Common/components/ContactLink"; import CoverImageEditModal from "./CoverImageEditModal"; -import DialogModal from "../Common/Dialog"; -import { DoctorCapacity } from "./DoctorCapacity"; -import { DoctorIcon } from "../TeleIcu/Icons/DoctorIcon"; -import DoctorsCountCard from "./DoctorsCountCard"; + import Page from "../Common/components/Page"; import RecordMeta from "../../CAREUI/display/RecordMeta"; import Table from "../Common/components/Table"; import { navigate } from "raviger"; -import useConfig from "../../Common/hooks/useConfig"; import { useMessageListener } from "../../Common/hooks/useMessageListener"; import { useTranslation } from "react-i18next"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; +import request from "../../Utils/request/request.js"; +import routes from "../../Redux/api.js"; +import useQuery from "../../Utils/request/useQuery.js"; +import { FacilityHomeTriage } from "./FacilityHomeTriage.js"; +import { FacilityDoctorList } from "./FacilityDoctorList.js"; +import { FacilityBedCapacity } from "./FacilityBedCapacity.js"; const Loading = lazy(() => import("../Common/Loading")); @@ -61,80 +43,25 @@ export const getFacilityFeatureIcon = (featureId: number) => { export const FacilityHome = (props: any) => { const { t } = useTranslation(); const { facilityId } = props; - const dispatch: any = useDispatch(); - const [facilityData, setFacilityData] = useState({}); - const [capacityData, setCapacityData] = useState>([]); - const [doctorData, setDoctorData] = useState>([]); - const [isLoading, setIsLoading] = useState(false); const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const [editCoverImage, setEditCoverImage] = useState(false); const [imageKey, setImageKey] = useState(Date.now()); - const [totalDoctors, setTotalDoctors] = useState(0); - const [patientStatsData, setPatientStatsData] = useState< - Array - >([]); - const [bedCapacityModalOpen, setBedCapacityModalOpen] = useState(false); - const [doctorCapacityModalOpen, setDoctorCapacityModalOpen] = useState(false); const authUser = useAuthUser(); - const config = useConfig(); useMessageListener((data) => console.log(data)); - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const facilityRes = await dispatch(getPermittedFacility(facilityId)); - if (facilityRes) { - const [capacityRes, doctorRes, triageRes] = await Promise.all([ - dispatch(listCapacity({}, { facilityId })), - dispatch(listDoctor({}, { facilityId })), - dispatch(getTriageInfo({ facilityId })), - ]); - if (!status.aborted) { - setIsLoading(false); - if (!facilityRes.data) { - Notification.Error({ - msg: "Something went wrong..!", - }); - } else { - setFacilityData(facilityRes.data); - if (capacityRes && capacityRes.data) { - setCapacityData(capacityRes.data.results); - } - if (doctorRes && doctorRes.data) { - setDoctorData(doctorRes.data.results); - // calculating total doctors count - let totalCount = 0; - doctorRes.data.results.map((doctor: DoctorModal) => { - if (doctor.count) { - totalCount += doctor.count; - } - }); - setTotalDoctors(totalCount); - } - if ( - triageRes && - triageRes.data && - triageRes.data.results && - triageRes.data.results.length - ) { - setPatientStatsData(triageRes.data.results); - } - } + const { data: facilityData, loading: isLoading } = useQuery( + routes.getPermittedFacility, + { + pathParams: { + id: facilityId, + }, + onResponse: ({ res }) => { + if (!res?.ok) { + navigate("/not-found"); } - } else { - navigate("/not-found"); - setIsLoading(false); - } - }, - [dispatch, facilityId] - ); - - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [dispatch, fetchData] + }, + } ); const handleDeleteClose = () => { @@ -142,179 +69,24 @@ export const FacilityHome = (props: any) => { }; const handleDeleteSubmit = async () => { - const res = await dispatch(deleteFacility(facilityId)); - if (res?.status === 204) { - Notification.Success({ - msg: "Facility deleted successfully", - }); - } else { - Notification.Error({ - msg: "Error while deleting Facility: " + (res?.data?.detail || ""), - }); - } - navigate("/facility"); + await request(routes.deleteFacility, { + pathParams: { id: facilityId }, + onResponse: ({ res }) => { + if (res?.ok) { + Notification.Success({ + msg: "Facility deleted successfully", + }); + } + navigate("/facility"); + }, + }); }; if (isLoading) { return ; } - let capacityList: any = null; - let totalBedCount = 0; - let totalOccupiedBedCount = 0; - if (!capacityData || !capacityData.length) { - capacityList = ( -
- No Bed Types Found -
- ); - } else { - capacityData.forEach((x) => { - totalBedCount += x.total_capacity ? x.total_capacity : 0; - totalOccupiedBedCount += x.current_capacity ? x.current_capacity : 0; - }); - capacityList = ( -
- { - return; - }} - /> - {getBedTypes(config).map((x) => { - const res = capacityData.find((data) => { - return data.room_type === x.id; - }); - if ( - res && - res.current_capacity !== undefined && - res.total_capacity !== undefined - ) { - const removeCurrentBedType = (bedTypeId: number | undefined) => { - setCapacityData((state) => - state.filter((i) => i.id !== bedTypeId) - ); - }; - return ( - { - const capacityRes = await dispatch( - listCapacity({}, { facilityId }) - ); - if (capacityRes && capacityRes.data) { - setCapacityData(capacityRes.data.results); - } - }} - /> - ); - } - })} -
- ); - } - - let doctorList: any = null; - if (!doctorData || !doctorData.length) { - doctorList = ( -
- No Doctors Found -
- ); - } else { - doctorList = ( -
- {/* Total Doctors Count Card */} -
-
-
-
- -
-
-
- Total Doctors -
-

{totalDoctors}

-
-
-
-
- - {doctorData.map((data: DoctorModal) => { - const removeCurrentDoctorData = (doctorId: number | undefined) => { - setDoctorData((state) => - state.filter((i: DoctorModal) => i.id !== doctorId) - ); - }; - - return ( - { - const doctorRes = await dispatch( - listDoctor({}, { facilityId }) - ); - if (doctorRes && doctorRes.data) { - setDoctorData(doctorRes.data.results); - // update total doctors count - let totalCount = 0; - doctorRes.data.results.map((doctor: DoctorModal) => { - if (doctor.count) { - totalCount += doctor.count; - } - }); - setTotalDoctors(totalCount); - } - }} - {...data} - removeDoctor={removeCurrentDoctorData} - /> - ); - })} -
- ); - } - - const stats: (string | JSX.Element)[][] = []; - for (let i = 0; i < patientStatsData.length; i++) { - const temp: (string | JSX.Element)[] = []; - temp.push(String(patientStatsData[i].entry_date) || "0"); - temp.push(String(patientStatsData[i].num_patients_visited) || "0"); - temp.push(String(patientStatsData[i].num_patients_home_quarantine) || "0"); - temp.push(String(patientStatsData[i].num_patients_isolation) || "0"); - temp.push(String(patientStatsData[i].num_patient_referred) || "0"); - temp.push( - String(patientStatsData[i].num_patient_confirmed_positive) || "0" - ); - temp.push( - - navigate(`/facility/${facilityId}/triage/${patientStatsData[i].id}`) - } - authorizeFor={NonReadOnlyUsers} - > - Edit - - ); - stats.push(temp); - } - - const hasCoverImage = !!facilityData.read_cover_image_url; + const hasCoverImage = !!facilityData?.read_cover_image_url; const StaffUserTypeIndex = USER_TYPES.findIndex((type) => type === "Staff"); const hasPermissionToEditCoverImage = @@ -331,24 +103,25 @@ export const FacilityHome = (props: any) => { const CoverImage = () => ( {facilityData.name} ); return ( - Are you sure you want to delete {facilityData.name} + Are you sure you want to delete{" "} + {facilityData?.name} } action="Delete" @@ -360,13 +133,13 @@ export const FacilityHome = (props: any) => { - facilityData.read_cover_image_url + facilityData?.read_cover_image_url ? setImageKey(Date.now()) : window.location.reload() } onClose={() => setEditCoverImage(false)} onDelete={() => window.location.reload()} - facility={facilityData} + facility={facilityData ?? ({} as FacilityModel)} /> {hasCoverImage ? (
{ {editCoverImageTooltip}
-

{facilityData.name}

+

{facilityData?.name}

{facilityData?.modified_date && ( { Address

- {facilityData.address} + {facilityData?.address}

@@ -453,7 +226,7 @@ export const FacilityHome = (props: any) => {

Phone Number

- + @@ -492,13 +265,13 @@ export const FacilityHome = (props: any) => {
- {facilityData.features?.some((feature: any) => + {facilityData?.features?.some((feature: any) => FACILITY_FEATURE_TYPES.some((f) => f.id === feature) ) && (

Available features

)}
- {facilityData.features?.map( + {facilityData?.features?.map( (feature: number, i: number) => FACILITY_FEATURE_TYPES.some((f) => f.id === feature) && ( { rows={[ [ "Capacity", - String(facilityData.oxygen_capacity), - String(facilityData.type_b_cylinders), - String(facilityData.type_c_cylinders), - String(facilityData.type_d_cylinders), + String(facilityData?.oxygen_capacity), + String(facilityData?.type_b_cylinders), + String(facilityData?.type_c_cylinders), + String(facilityData?.type_d_cylinders), ], [ "Daily Expected Consumption", - String(facilityData.expected_oxygen_requirement), - String(facilityData.expected_type_b_cylinders), - String(facilityData.expected_type_c_cylinders), - String(facilityData.expected_type_d_cylinders), + String(facilityData?.expected_oxygen_requirement), + String(facilityData?.expected_type_b_cylinders), + String(facilityData?.expected_type_c_cylinders), + String(facilityData?.expected_type_d_cylinders), ], ]} />
-
-
-
Bed Capacity
- setBedCapacityModalOpen(true)} - authorizeFor={NonReadOnlyUsers} - > - - Add More Bed Types - -
-
{capacityList}
-
-
-
-
Doctors List
- setDoctorCapacityModalOpen(true)} - disabled={doctorList.length === DOCTOR_SPECIALIZATION.length} - authorizeFor={NonReadOnlyUsers} - > - - Add Doctor Types - -
-
{doctorList}
-
-
-
-
-
Corona Triage
- navigate(`/facility/${facilityId}/triage`)} - authorizeFor={NonReadOnlyUsers} - > - - Add Triage - -
-
- - {stats.length === 0 && ( - <> -
-
- No Data Found -
- - )} - - - - {bedCapacityModalOpen && ( - setBedCapacityModalOpen(false)} - title="Add Bed Capacity" - className="max-w-md md:min-w-[600px]" - > - setBedCapacityModalOpen(false)} - handleUpdate={async () => { - const capacityRes = await dispatch( - listCapacity({}, { facilityId }) - ); - if (capacityRes && capacityRes.data) { - setCapacityData(capacityRes.data.results); - } - }} - /> - - )} - {doctorCapacityModalOpen && ( - setDoctorCapacityModalOpen(false)} - title="Add Doctor Capacity" - className="max-w-md md:min-w-[600px]" - > - setDoctorCapacityModalOpen(false)} - handleUpdate={async () => { - const doctorRes = await dispatch(listDoctor({}, { facilityId })); - if (doctorRes && doctorRes.data) { - setDoctorData(doctorRes.data.results); - // update total doctors count - setTotalDoctors( - doctorRes.data.results.reduce( - (acc: number, doctor: DoctorModal) => - acc + (doctor.count || 0), - 0 - ) - ); - } - }} - /> - - )} + + + + ); }; diff --git a/src/Components/Facility/FacilityHomeTriage.tsx b/src/Components/Facility/FacilityHomeTriage.tsx new file mode 100644 index 00000000000..f96a5181cfc --- /dev/null +++ b/src/Components/Facility/FacilityHomeTriage.tsx @@ -0,0 +1,88 @@ +import { navigate } from "raviger"; +import ButtonV2 from "../Common/components/ButtonV2"; +import Table from "../Common/components/Table"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; + +export const FacilityHomeTriage = (props: any) => { + const triageQuery = useQuery(routes.getTriage, { + pathParams: { facilityId: props.facilityId }, + }); + + const stats: (string | JSX.Element)[][] = []; + for ( + let i = 0; + triageQuery.data?.results && i < triageQuery.data.results.length; + i++ + ) { + const temp: (string | JSX.Element)[] = []; + temp.push(String(triageQuery.data.results[i].entry_date) || "0"); + temp.push(String(triageQuery.data.results[i].num_patients_visited) || "0"); + temp.push( + String(triageQuery.data.results[i].num_patients_home_quarantine) || "0" + ); + temp.push( + String(triageQuery.data.results[i].num_patients_isolation) || "0" + ); + temp.push(String(triageQuery.data.results[i].num_patient_referred) || "0"); + temp.push( + String(triageQuery.data.results[i].num_patient_confirmed_positive) || "0" + ); + temp.push( + + navigate( + `/facility/${props.facilityId}/triage/${triageQuery.data?.results[i].id}` + ) + } + authorizeFor={props.NonReadOnlyUsers} + > + Edit + + ); + stats.push(temp); + } + + return ( +
+
+
+
Corona Triage
+ navigate(`/facility/${props.facilityId}/triage`)} + authorizeFor={props.NonReadOnlyUsers} + > + + Add Triage + +
+
+
+ {stats.length === 0 && ( + <> +
+
+ No Data Found +
+ + )} + + + + ); +}; diff --git a/src/Components/Facility/FacilityUsers.tsx b/src/Components/Facility/FacilityUsers.tsx index 6963c4006db..aba12cc3833 100644 --- a/src/Components/Facility/FacilityUsers.tsx +++ b/src/Components/Facility/FacilityUsers.tsx @@ -1,18 +1,7 @@ -import { lazy, useCallback, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { lazy, useState } from "react"; import CountBlock from "../../CAREUI/display/Count"; import CareIcon from "../../CAREUI/icons/CareIcon"; import { RESULTS_PER_PAGE_LIMIT } from "../../Common/constants"; -import useAuthUser from "../../Common/hooks/useAuthUser"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { - addUserFacility, - deleteUser, - deleteUserFacility, - getAnyFacility, - getFacilityUsers, - getUserListFacility, -} from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; import { classNames, isUserOnline, relativeTime } from "../../Utils/utils"; import Pagination from "../Common/Pagination"; @@ -23,25 +12,22 @@ import { FacilityModel } from "../Facility/models"; import LinkFacilityDialog from "../Users/LinkFacilityDialog"; import UnlinkFacilityDialog from "../Users/UnlinkFacilityDialog"; import UserDeleteDialog from "../Users/UserDeleteDialog"; +import useAuthUser from "../../Common/hooks/useAuthUser"; +import request from "../../Utils/request/request"; +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; const Loading = lazy(() => import("../Common/Loading")); export default function FacilityUsers(props: any) { const { facilityId } = props; - const dispatch: any = useDispatch(); - const initialData: any[] = []; let manageUsers: any = null; - const [users, setUsers] = useState(initialData); - const [isLoading, setIsLoading] = useState(false); - const [isFacilityLoading, setIsFacilityLoading] = useState(false); - const [totalCount, setTotalCount] = useState(0); + const [isUnlinkFacilityLoading, setIsUnlinkFacilityLoading] = useState(false); + const [isAddFacilityLoading, setIsAddFacilityLoading] = useState(false); + const [isLoadFacilityLoading, setIsLoadFacilityLoading] = useState(false); const [currentPage, setCurrentPage] = useState(1); // eslint-disable-next-line @typescript-eslint/no-unused-vars const [offset, setOffset] = useState(0); - const [facilityData, setFacilityData] = useState({ - name: "", - district_object_id: 0, - }); const authUser = useAuthUser(); const [linkFacility, setLinkFacility] = useState<{ @@ -63,48 +49,22 @@ export default function FacilityUsers(props: any) { const limit = RESULTS_PER_PAGE_LIMIT; - useEffect(() => { - async function fetchFacilityName() { - if (facilityId) { - const res = await dispatch(getAnyFacility(facilityId)); - setFacilityData({ - name: res?.data?.name || "", - district_object_id: res?.data?.district_object?.id || 0, - }); - } else { - setFacilityData({ - name: "", - district_object_id: 0, - }); - } - } - fetchFacilityName(); - }, [dispatch, facilityId]); - - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatch( - getFacilityUsers(facilityId, { offset, limit }) - ); - - if (!status.aborted) { - if (res && res.data) { - setUsers(res.data.results); - setTotalCount(res.data.count); - } - setIsLoading(false); - } + const { data: facilityData } = useQuery(routes.getAnyFacility, { + pathParams: { + id: facilityId, }, - [dispatch, facilityId, offset, limit] - ); + prefetch: facilityId !== undefined, + }); - useAbortableEffect( - (status: statusType) => { - fetchData(status); - }, - [fetchData] - ); + const { + data: facilityUserData, + refetch: facilityUserFetch, + loading: isLoading, + } = useQuery(routes.getFacilityUsers, { + query: { offset: offset, limit: limit }, + pathParams: { facility_id: facilityId }, + prefetch: facilityId !== undefined, + }); const handlePagination = (page: number, limit: number) => { const offset = (page - 1) * limit; @@ -113,23 +73,24 @@ export default function FacilityUsers(props: any) { }; const loadFacilities = async (username: string) => { - if (isFacilityLoading) { + if (isUnlinkFacilityLoading || isAddFacilityLoading) { return; } - setIsFacilityLoading(true); - const res = await dispatch(getUserListFacility({ username })); - if (res && res.data) { - const updated = users.map((user) => { + setIsLoadFacilityLoading(true); + const { res, data } = await request(routes.userListFacility, { + pathParams: { username: username }, + }); + if (res?.ok && data && facilityUserData) { + facilityUserData.results = facilityUserData.results.map((user) => { return user.username === username ? { ...user, - facilities: res.data, + facilities: data, } : user; }); - setUsers(updated); } - setIsFacilityLoading(false); + setIsLoadFacilityLoading(false); }; const showLinkFacilityModal = (username: string) => { @@ -155,14 +116,22 @@ export default function FacilityUsers(props: any) { }; const handleUnlinkFacilitySubmit = async () => { - setIsFacilityLoading(true); - await dispatch( - deleteUserFacility( - unlinkFacilityData.userName, - String(unlinkFacilityData?.facility?.id) - ) - ); - setIsFacilityLoading(false); + setIsUnlinkFacilityLoading(true); + await request(routes.deleteUserFacility, { + // body given in the dispatch call but there is no body in API documentation + body: { facility: String(unlinkFacilityData?.facility?.id) }, + pathParams: { + username: unlinkFacilityData.userName, + }, + onResponse: ({ res }) => { + if (res?.status === 204) { + Notification.Success({ + msg: "User Facility deleted successfully", + }); + } + }, + }); + setIsUnlinkFacilityLoading(false); loadFacilities(unlinkFacilityData.userName); hideUnlinkFacilityModal(); }; @@ -173,19 +142,18 @@ export default function FacilityUsers(props: any) { const handleSubmit = async () => { const username = userData.username; - const res = await dispatch(deleteUser(username)); - if (res?.status === 204) { - Notification.Success({ - msg: "User deleted successfully", - }); - } else { - Notification.Error({ - msg: "Error while deleting User: " + (res?.data?.detail || ""), - }); - } - + await request(routes.deleteUser, { + pathParams: { username: username }, + onResponse: ({ res }) => { + if (res?.status === 204) { + Notification.Success({ + msg: "User deleted successfully", + }); + } + }, + }); setUserData({ show: false, username: "", name: "" }); - fetchData({ aborted: false }); + facilityUserFetch(); }; const handleDelete = (user: any) => { @@ -198,7 +166,9 @@ export default function FacilityUsers(props: any) { const facilityClassname = classNames( "align-baseline text-sm font-bold", - isFacilityLoading ? "text-gray-500" : "text-blue-500 hover:text-blue-800" + isAddFacilityLoading || isUnlinkFacilityLoading || isLoadFacilityLoading + ? "text-gray-500" + : "text-blue-500 hover:text-blue-800" ); const showLinkFacility = (username: string) => { @@ -236,7 +206,7 @@ export default function FacilityUsers(props: any) { size="small" circle variant="secondary" - disabled={isFacilityLoading} + disabled={isUnlinkFacilityLoading} onClick={() => setUnlinkFacilityData({ show: true, @@ -267,17 +237,26 @@ export default function FacilityUsers(props: any) { const addFacility = async (username: string, facility: any) => { hideLinkFacilityModal(); - setIsFacilityLoading(true); - await dispatch(addUserFacility(username, String(facility.id))); - setIsFacilityLoading(false); + setIsAddFacilityLoading(true); + // Remaining props of request are not specified in dispatch request + await request(routes.addUserFacility, { + body: { + facility: String(facility.id), + }, + pathParams: { + username: username, + }, + }); + setIsAddFacilityLoading(false); loadFacilities(username); }; let userList: any[] = []; - users && - users.length && - (userList = users.map((user: any) => { + facilityUserData && + facilityUserData.results && + facilityUserData.results.length && + (userList = facilityUserData.results.map((user: any) => { return (
; - } else if (users && users.length) { + } else if (facilityUserData.results && facilityUserData.results.length) { manageUsers = (
{userList}
- {totalCount > limit && ( + {facilityUserData && facilityUserData.count > limit && (
)}
); - } else if (users && users.length === 0) { + } else if ( + facilityUserData.results && + facilityUserData.results.length === 0 + ) { manageUsers = (
@@ -443,15 +425,16 @@ export default function FacilityUsers(props: any) { )}
- + {facilityUserData && ( + + )}
-
{manageUsers}
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 8dcc5f71e0d..cdc7074c145 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -488,3 +488,16 @@ export interface PatientNotesModel { user_type?: string; created_date: string; } + +export type IFacilityNotificationRequest = { + facility: string; + message: string; +}; + +export type IFacilityNotificationResponse = { + [key: string]: string; +}; + +export type IUserFacilityRequest = { + facility: string; +}; diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index a120c384a3d..e380949887b 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -56,9 +56,6 @@ export const updateFacility = (id: string, params: object) => { export const partialUpdateFacility = (id: string, params: object) => { return fireRequest("partialUpdateFacility", [id], params); }; -export const deleteFacility = (id: string) => { - return fireRequest("deleteFacility", [id], {}); -}; export const deleteFacilityCoverImage = (id: string) => { return fireRequest("deleteFacilityCoverImage", [], {}, { id }); }; @@ -681,10 +678,6 @@ export const getPublicKey = () => { return fireRequest("getPublicKey", [], {}, {}); }; -export const sendNotificationMessages = (params: object) => { - return fireRequest("sendNotificationMessages", [], params, {}); -}; - // FileUpload export const createUpload = (params: object) => { diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index a69914bddb7..4172effe432 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -29,13 +29,19 @@ import { AssetUpdate, } from "../Components/Assets/AssetTypes"; import { + CapacityModal, ConsultationModel, CreateBedBody, CurrentBed, DailyRoundsBody, DailyRoundsRes, + DoctorModal, FacilityModel, + IFacilityNotificationRequest, + IFacilityNotificationResponse, + IUserFacilityRequest, LocationModel, + PatientStatsModel, WardModel, } from "../Components/Facility/models"; import { @@ -162,11 +168,14 @@ const routes = { userListFacility: { path: "/api/v1/users/{username}/get_facilities/", + TRes: Type(), }, addUserFacility: { path: "/api/v1/users/{username}/add_facility/", method: "PUT", + TBody: Type(), + TRes: Type(), }, addUserSkill: { @@ -177,6 +186,8 @@ const routes = { deleteUserFacility: { path: "/api/v1/users/{username}/delete_facility/", method: "DELETE", + TBody: Type(), + TRes: Type>(), }, clearHomeFacility: { @@ -206,8 +217,9 @@ const routes = { }, deleteUser: { - path: "/api/v1/users", + path: "/api/v1/users/{username}/", method: "DELETE", + TRes: Type>(), }, addUser: { @@ -288,6 +300,7 @@ const routes = { getFacilityUsers: { path: "/api/v1/facility/{facility_id}/get_users/", + TRes: Type>(), }, listFacilityAssetLocation: { @@ -403,8 +416,9 @@ const routes = { // Download Api deleteFacility: { - path: "/api/v1/facility", + path: "/api/v1/facility/{id}/", method: "DELETE", + TRes: Type>(), }, downloadFacility: { @@ -496,6 +510,7 @@ const routes = { getCapacity: { path: "/api/v1/facility/{facilityId}/capacity/", + TRes: Type>(), }, getCapacityBed: { @@ -509,6 +524,7 @@ const routes = { listDoctor: { path: "/api/v1/facility/{facilityId}/hospital_doctor/", + TRes: Type>(), }, getDoctor: { path: "/api/v1/facility/{facilityId}/hospital_doctor/{id}/", @@ -536,6 +552,7 @@ const routes = { }, getTriage: { path: "/api/v1/facility/{facilityId}/patient_stats/", + TRes: Type>(), }, getTriageDetails: { @@ -841,6 +858,8 @@ const routes = { sendNotificationMessages: { path: "/api/v1/notification/notify/", method: "POST", + TRes: Type(), + Tbody: Type(), }, // FileUpload Create From 8d9479b22495ba9e20cd6dc548d177e9a31dc58e Mon Sep 17 00:00:00 2001 From: Gampa Sri Harsh <114745442+sriharsh05@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:22:42 +0530 Subject: [PATCH 03/12] add validation for admission_date in consultation form (#6746) --- src/Components/Facility/ConsultationForm.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 486adfa5170..1d4abbedc39 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -491,6 +491,10 @@ export const ConsultationForm = (props: any) => { errors[field] = "Field is required"; invalidForm = true; } + if (dayjs(state.form.admission_date).isBefore(dayjs("2000-01-01"))) { + errors[field] = "Admission date cannot be before 01/01/2000"; + invalidForm = true; + } return; case "cause_of_death": if (state.form.suggestion === "DD" && !state.form[field]) { From fc1f1a93c9f4f01666e92111f21571cf7591ad8c Mon Sep 17 00:00:00 2001 From: konavivekramakrishna <101407963+konavivekramakrishna@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:22:53 +0530 Subject: [PATCH 04/12] Remove required attribute from SelectFormField (#6737) --- src/Components/Patient/DailyRounds.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 72075939bd7..ce619370d03 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -501,7 +501,6 @@ export const DailyRounds = (props: any) => { Date: Wed, 29 Nov 2023 10:23:04 +0530 Subject: [PATCH 05/12] changed bed type text in constants file (#6721) * changed bed type text in constants file * made corresponding change in cypress test --------- Co-authored-by: Rithvik Nishad --- cypress/e2e/facility_spec/facility.cy.ts | 2 +- src/Common/constants.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts index f2ff847c9d9..5ae5d7cb9db 100644 --- a/cypress/e2e/facility_spec/facility.cy.ts +++ b/cypress/e2e/facility_spec/facility.cy.ts @@ -31,7 +31,7 @@ describe("Facility Creation", () => { facilityPage.fillPhoneNumber("9898469865"); facilityPage.submitForm(); - facilityPage.selectBedType("Non-Covid Oxygen beds"); + facilityPage.selectBedType("Oxygen beds"); facilityPage.fillTotalCapacity("10"); facilityPage.fillCurrentlyOccupied("5"); facilityPage.saveAndExitBedCapacityForm(); diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 065e7ccd759..069a39e1539 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -213,10 +213,10 @@ export const getBedTypes = ({ : []; return [ - { id: 1, text: "Non-Covid Ordinary Beds" }, - { id: 150, text: "Non-Covid Oxygen beds" }, - { id: 10, text: "Non-Covid ICU (ICU without ventilator)" }, - { id: 20, text: "Non-Covid Ventilator (ICU with ventilator)" }, + { id: 1, text: "Ordinary Beds" }, + { id: 150, text: "Oxygen beds" }, + { id: 10, text: "ICU (ICU without ventilator)" }, + { id: 20, text: "Ventilator (ICU with ventilator)" }, { id: 30, text: "Covid Ordinary Beds" }, { id: 120, text: "Covid Oxygen beds" }, { id: 110, text: "Covid ICU (ICU without ventilator)" }, From 88fa9a238c2fa57935ef039ecb04fa73717917d4 Mon Sep 17 00:00:00 2001 From: Gokulram A Date: Wed, 29 Nov 2023 10:23:45 +0530 Subject: [PATCH 06/12] Rearranging buttons in the Patient consultation page (#6674) * Rearranging buttons in the Patient consultation * fix discharge patient cypress * Remove unused imports in ConsultationDetails component --- .../pageobject/Patient/PatientConsultation.ts | 3 +- .../Facility/ConsultationDetails/index.tsx | 78 +---------- src/Components/Patient/PatientInfoCard.tsx | 130 +++++++++++++++++- 3 files changed, 131 insertions(+), 80 deletions(-) diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts index 9c588f50c4e..26d75feba2e 100644 --- a/cypress/pageobject/Patient/PatientConsultation.ts +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -213,7 +213,8 @@ export class PatientConsultationPage { } clickDischargePatient() { - cy.get("#discharge_patient_from_care").click(); + cy.get("#show-more").click(); + cy.contains("p", "Discharge from CARE").click(); } selectDischargeReason(reason: string) { diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index 7e7664707eb..534f253a416 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -13,10 +13,6 @@ import { } from "../../../Redux/actions"; import { statusType, useAbortableEffect } from "../../../Common/utils"; import { lazy, useCallback, useState } from "react"; -import ButtonV2 from "../../Common/components/ButtonV2"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; -import DischargeModal from "../DischargeModal"; -import DischargeSummaryModal from "../DischargeSummaryModal"; import DoctorVideoSlideover from "../DoctorVideoSlideover"; import { make as Link } from "../../Common/components/Link.bs"; import PatientInfoCard from "../../Patient/PatientInfoCard"; @@ -25,7 +21,6 @@ import { formatDateTime, relativeTime } from "../../../Utils/utils"; import { navigate, useQueryParams } from "raviger"; import { useDispatch } from "react-redux"; -import { useTranslation } from "react-i18next"; import { triggerGoal } from "../../../Integrations/Plausible"; import useAuthUser from "../../../Common/hooks/useAuthUser"; import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab"; @@ -73,7 +68,6 @@ const TABS = { }; export const ConsultationDetails = (props: any) => { - const { t } = useTranslation(); const { facilityId, patientId, consultationId } = props; const tab = props.tab.toUpperCase() as keyof typeof TABS; const dispatch: any = useDispatch(); @@ -86,9 +80,6 @@ export const ConsultationDetails = (props: any) => { ); const [patientData, setPatientData] = useState({}); const [activeShiftingData, setActiveShiftingData] = useState>([]); - const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = - useState(false); - const [openDischargeDialog, setOpenDischargeDialog] = useState(false); const [isCameraAttached, setIsCameraAttached] = useState(false); const getPatientGender = (patientData: any) => @@ -197,19 +188,6 @@ export const ConsultationDetails = (props: any) => { const SelectedTab = TABS[tab]; - const hasActiveShiftingRequest = () => { - if (activeShiftingData.length > 0) { - return [ - "PENDING", - "APPROVED", - "DESTINATION APPROVED", - "PATIENT TO BE PICKED UP", - ].includes(activeShiftingData[activeShiftingData.length - 1].status); - } - - return false; - }; - if (isLoading) { return ; } @@ -272,18 +250,6 @@ export const ConsultationDetails = (props: any) => { return (
- setOpenDischargeSummaryDialog(false)} - /> - - setOpenDischargeDialog(false)} - consultationData={consultationData} - /> -
-
- setOpenDischargeSummaryDialog(true)}> - - {t("discharge_summary")} - - - setOpenDischargeDialog(true)} - disabled={!!consultationData.discharge_date} - > - - {t("discharge_from_care")} - -
diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index f3436fb8acc..bfcc4c23d96 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -10,7 +10,7 @@ import { import { ConsultationModel, PatientCategory } from "../Facility/models"; import { Switch, Menu } from "@headlessui/react"; -import { Link } from "raviger"; +import { Link, navigate } from "raviger"; import { useState } from "react"; import CareIcon from "../../CAREUI/icons/CareIcon"; import useConfig from "../../Common/hooks/useConfig"; @@ -29,27 +29,35 @@ import routes from "../../Redux/api.js"; import DropdownMenu from "../Common/components/Menu.js"; import { triggerGoal } from "../../Integrations/Plausible.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; +import DischargeSummaryModal from "../Facility/DischargeSummaryModal.js"; +import DischargeModal from "../Facility/DischargeModal.js"; +import { useTranslation } from "react-i18next"; export default function PatientInfoCard(props: { patient: PatientModel; consultation?: ConsultationModel; fetchPatientData?: (state: { aborted: boolean }) => void; + activeShiftingData: any; consultationId: string; showAbhaProfile?: boolean; }) { const authUser = useAuthUser(); - + const { t } = useTranslation(); const [open, setOpen] = useState(false); const [showLinkABHANumber, setShowLinkABHANumber] = useState(false); const [showABHAProfile, setShowABHAProfile] = useState( !!props.showAbhaProfile ); + const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = + useState(false); + const [openDischargeDialog, setOpenDischargeDialog] = useState(false); const { enable_hcx, enable_abdm } = useConfig(); const [showLinkCareContext, setShowLinkCareContext] = useState(false); const patient = props.patient; const consultation = props.consultation; + const activeShiftingData = props.activeShiftingData; const [medicoLegalCase, setMedicoLegalCase] = useState( consultation?.medico_legal_case ?? false @@ -86,6 +94,19 @@ export default function PatientInfoCard(props: { } }; + const hasActiveShiftingRequest = () => { + if (activeShiftingData.length > 0) { + return [ + "PENDING", + "APPROVED", + "DESTINATION APPROVED", + "PATIENT TO BE PICKED UP", + ].includes(activeShiftingData[activeShiftingData.length - 1].status); + } + + return false; + }; + return ( <> Invalid Patient Data
)} + + {consultation && ( + <> + setOpenDischargeSummaryDialog(false)} + /> + setOpenDischargeDialog(false)} + consultationData={consultation} + /> + + )} +
{/* Can support for patient picture in the future */} @@ -406,6 +443,7 @@ export default function PatientInfoCard(props: { ) )} } @@ -545,6 +583,94 @@ export default function PatientInfoCard(props: { ))}
+
+ {!consultation?.discharge_date && ( + + {({ close }) => ( + <> + {hasActiveShiftingRequest() ? ( +
{ + close(); + navigate( + `/shifting/${ + activeShiftingData[ + activeShiftingData.length - 1 + ].id + }` + ); + }} + > + + +

Track Shifting

+
+
+ ) : ( +
{ + close(); + navigate( + `/facility/${patient.facility}/patient/${patient.id}/shift/new` + ); + }} + > + + +

Shift Patient

+
+
+ )} + + )} +
+ )} + + {({ close }) => ( +
{ + close(); + setOpenDischargeSummaryDialog(true); + }} + > + + +

{t("discharge_summary")}

+
+
+ )} +
+ + {({ close }) => ( +
{ + if (!consultation?.discharge_date) { + close(); + setOpenDischargeDialog(true); + } + }} + > + + +

{t("discharge_from_care")}

+
+
+ )} +
+
Date: Wed, 29 Nov 2023 10:24:06 +0530 Subject: [PATCH 07/12] Hide KASP Advance Filter (#6687) * Hide KASP Advance Filter * removed KASP Advance Filter * removed unused filter --- .../Facility/FacilityFilter/index.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Components/Facility/FacilityFilter/index.tsx b/src/Components/Facility/FacilityFilter/index.tsx index ff4d3f89508..fd6edd664d1 100644 --- a/src/Components/Facility/FacilityFilter/index.tsx +++ b/src/Components/Facility/FacilityFilter/index.tsx @@ -1,7 +1,6 @@ import { navigate } from "raviger"; import { FACILITY_TYPES } from "../../../Common/constants"; import useMergeState from "../../../Common/hooks/useMergeState"; -import useConfig from "../../../Common/hooks/useConfig"; import FiltersSlideover from "../../../CAREUI/interactive/FiltersSlideover"; import { useTranslation } from "react-i18next"; import StateAutocompleteFormField from "../../Common/StateAutocompleteFormField"; @@ -15,19 +14,17 @@ const clearFilterState = { district: "", local_body: "", facility_type: "", - kasp_empanelled: "", }; function FacilityFilter(props: any) { const { t } = useTranslation(); const { filter, onChange, closeFilter } = props; - const { kasp_string } = useConfig(); + const [filterState, setFilterState] = useMergeState({ state: filter.state || "", district: filter.district || "", local_body: filter.local_body || "", facility_type: filter.facility_type || "", - kasp_empanelled: filter.kasp_empanelled || "", }); const applyFilter = () => { @@ -36,7 +33,6 @@ function FacilityFilter(props: any) { district: Number(filterState.district) || "", local_body: Number(filterState.local_body) || "", facility_type: filterState.facility_type || "", - kasp_empanelled: filterState.kasp_empanelled || "", }; onChange(data); }; @@ -92,17 +88,6 @@ function FacilityFilter(props: any) { optionValue={(option) => option.id} placeholder={t("show_all")} /> - option.text} - optionValue={(option) => option.id} - placeholder={t("show_all")} - />
); From f5bc74173b463ffd0c89400576b6e5996e9b1303 Mon Sep 17 00:00:00 2001 From: Ayush Seth Date: Wed, 29 Nov 2023 10:24:19 +0530 Subject: [PATCH 08/12] hide location filter if facility is not selected (#6688) * make error optional and render only when required * fix markup and remove unnecessary tw classes * hide location filter if facility is not selected * revert changes to error component --- src/Components/Common/LocationSelect.tsx | 2 +- src/Components/Form/FormFields/FormField.tsx | 2 +- src/Components/Patient/PatientFilter.tsx | 89 ++++++++++---------- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/src/Components/Common/LocationSelect.tsx b/src/Components/Common/LocationSelect.tsx index ef7280f1907..d20c843fad8 100644 --- a/src/Components/Common/LocationSelect.tsx +++ b/src/Components/Common/LocationSelect.tsx @@ -7,7 +7,7 @@ interface LocationSelectProps { name: string; disabled?: boolean; margin?: string; - errors: string; + errors?: string; className?: string; searchAll?: boolean; multiple?: boolean; diff --git a/src/Components/Form/FormFields/FormField.tsx b/src/Components/Form/FormFields/FormField.tsx index af5a3e91804..ffd42c0d50e 100644 --- a/src/Components/Form/FormFields/FormField.tsx +++ b/src/Components/Form/FormFields/FormField.tsx @@ -1,6 +1,6 @@ +import { classNames } from "../../../Utils/utils"; import { FieldError } from "../FieldValidators"; import { FormFieldBaseProps } from "./Utils"; -import { classNames } from "../../../Utils/utils"; type LabelProps = { id?: string | undefined; diff --git a/src/Components/Patient/PatientFilter.tsx b/src/Components/Patient/PatientFilter.tsx index 4ab7f14c895..10b170f4547 100644 --- a/src/Components/Patient/PatientFilter.tsx +++ b/src/Components/Patient/PatientFilter.tsx @@ -1,40 +1,40 @@ -import { useEffect, useCallback } from "react"; -import { FacilitySelect } from "../Common/FacilitySelect"; -import AutoCompleteAsync from "../Form/AutoCompleteAsync"; +import dayjs from "dayjs"; +import { navigate } from "raviger"; +import { useCallback, useEffect } from "react"; +import { useDispatch } from "react-redux"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; import { - GENDER_TYPES, - FACILITY_TYPES, - DISEASE_STATUS, - PATIENT_FILTER_CATEGORIES, ADMITTED_TO, DISCHARGE_REASONS, + DISEASE_STATUS, + FACILITY_TYPES, + GENDER_TYPES, + PATIENT_FILTER_CATEGORIES, } from "../../Common/constants"; +import useConfig from "../../Common/hooks/useConfig"; +import useMergeState from "../../Common/hooks/useMergeState"; import { getAllLocalBody, getAnyFacility, getDistrict, } from "../../Redux/actions"; -import { useDispatch } from "react-redux"; -import { navigate } from "raviger"; +import { dateQueryString } from "../../Utils/utils"; +import { DateRange } from "../Common/DateRangeInputV2"; +import { FacilitySelect } from "../Common/FacilitySelect"; +import { LocationSelect } from "../Common/LocationSelect"; +import AccordionV2 from "../Common/components/AccordionV2"; import DistrictSelect from "../Facility/FacilityFilter/DistrictSelect"; -import SelectMenuV2 from "../Form/SelectMenuV2"; +import AutoCompleteAsync from "../Form/AutoCompleteAsync"; +import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; +import { FieldLabel } from "../Form/FormFields/FormField"; import TextFormField from "../Form/FormFields/TextFormField"; import { FieldChangeEvent, FieldChangeEventHandler, } from "../Form/FormFields/Utils"; -import { FieldLabel } from "../Form/FormFields/FormField"; import MultiSelectMenuV2 from "../Form/MultiSelectMenuV2"; -import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; -import { DateRange } from "../Common/DateRangeInputV2"; -import CareIcon from "../../CAREUI/icons/CareIcon"; -import useMergeState from "../../Common/hooks/useMergeState"; -import useConfig from "../../Common/hooks/useConfig"; -import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; -import AccordionV2 from "../Common/components/AccordionV2"; -import { dateQueryString } from "../../Utils/utils"; -import dayjs from "dayjs"; -import { LocationSelect } from "../Common/LocationSelect"; +import SelectMenuV2 from "../Form/SelectMenuV2"; const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); @@ -587,10 +587,10 @@ export default function PatientFilter(props: any) { } expanded={true} - className="w-full rounded-md" + className="rounded-md" > -
-
+
+
Facility setFacility(obj, "facility")} />
-
- Location - - setFilterState({ - ...filterState, - last_consultation_current_bed__location: selected, - }) - } - /> -
-
+ {filterState.facility && ( +
+ Location + + setFilterState({ + ...filterState, + last_consultation_current_bed__location: selected, + }) + } + /> +
+ )} +
Facility type
-
+
LSG Body
-
+
District Date: Wed, 29 Nov 2023 10:24:33 +0530 Subject: [PATCH 09/12] Refactor onClick event in ConsultationDetails (#6704) component --- src/Components/Facility/ConsultationDetails/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index 534f253a416..2f6ca03ef06 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -309,7 +309,13 @@ export const ConsultationDetails = (props: any) => { setShowPatientNotesPopup(true)} + onClick={() => + showPatientNotesPopup + ? navigate( + `/facility/${facilityId}/patient/${patientId}/notes` + ) + : setShowPatientNotesPopup(true) + } className="btn btn-primary m-1 w-full hover:text-white" > Doctor's Notes From bf4920cef704efee5764b7bba66768524a14f15c Mon Sep 17 00:00:00 2001 From: Gampa Sri Harsh <114745442+sriharsh05@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:24:46 +0530 Subject: [PATCH 10/12] Remove blood group, weight and height badges on the top of patient info card (#6712) --- src/Common/utils.tsx | 10 ---------- src/Components/Patient/PatientInfoCard.tsx | 12 ------------ 2 files changed, 22 deletions(-) diff --git a/src/Common/utils.tsx b/src/Common/utils.tsx index 270e2f5efe6..3596469a6d5 100644 --- a/src/Common/utils.tsx +++ b/src/Common/utils.tsx @@ -44,16 +44,6 @@ export const parseOptionId: ( return textArray.join(", "); }; -export const getDimensionOrDash = ( - value: number | string | null | undefined, - unit: string -) => { - if (value === undefined || value === null || value === 0 || value === "0") { - return "-"; - } - return value + unit; -}; - export const deepEqual = (x: any, y: any): boolean => { if (x === y) return true; diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index bfcc4c23d96..e49607629fa 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -14,7 +14,6 @@ import { Link, navigate } 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 { classNames, formatDate, formatDateTime } from "../../Utils/utils.js"; import ABHAProfileModal from "../ABDM/ABHAProfileModal"; @@ -284,17 +283,6 @@ export default function PatientInfoCard(props: {
{[ - ["Blood Group", patient.blood_group, patient.blood_group], - [ - "Weight", - getDimensionOrDash(consultation?.weight, " kg"), - true, - ], - [ - "Height", - getDimensionOrDash(consultation?.height, "cm"), - true, - ], [ "Respiratory Support", RESPIRATORY_SUPPORT.find( From 9b2d9473712912950cb7f24c2373a0ef74e7f14a Mon Sep 17 00:00:00 2001 From: Khavin Shankar Date: Wed, 29 Nov 2023 19:53:55 +0530 Subject: [PATCH 11/12] Make facilityMiddlewareHostname as a state (#6753) --- src/Components/Facility/Consultations/Feed.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx index 0389fe298dc..43d5b89cb38 100644 --- a/src/Components/Facility/Consultations/Feed.tsx +++ b/src/Components/Facility/Consultations/Feed.tsx @@ -55,15 +55,15 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { const [isFullscreen, setFullscreen] = useFullscreen(); const [videoStartTime, setVideoStartTime] = useState(null); const [statusReported, setStatusReported] = useState(false); + const [facilityMiddlewareHostname, setFacilityMiddlewareHostname] = + useState(""); const authUser = useAuthUser(); - let facilityMiddlewareHostname = ""; - useQuery(routes.getPermittedFacility, { pathParams: { id: facilityId || "" }, onResponse: ({ res, data }) => { if (res && res.status === 200 && data && data.middleware_address) { - facilityMiddlewareHostname = data.middleware_address; + setFacilityMiddlewareHostname(data.middleware_address); } }, }); From a18016b6b3d063d07ede0dd096c98ffb5b09803c Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:56:44 +0530 Subject: [PATCH 12/12] facility advance filter (#6736) --- cypress/e2e/facility_spec/facility.cy.ts | 2 +- .../e2e/facility_spec/facility_homepage.cy.ts | 24 ++++++++++++++++++- .../pageobject/Facility/FacilityCreation.ts | 16 +++++++++++-- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/facility_spec/facility.cy.ts b/cypress/e2e/facility_spec/facility.cy.ts index 5ae5d7cb9db..da06c65ed53 100644 --- a/cypress/e2e/facility_spec/facility.cy.ts +++ b/cypress/e2e/facility_spec/facility.cy.ts @@ -49,7 +49,7 @@ describe("Facility Creation", () => { facilityPage.visitUpdateFacilityPage(facilityUrl); facilityPage.clickManageFacilityDropdown(); facilityPage.clickUpdateFacilityOption(); - facilityPage.clickUpdateFacilityType(); + facilityPage.clickUpdateFacilityType("Request Approving Center"); facilityPage.fillFacilityName("cypress facility updated"); facilityPage.fillAddress("Cypress Facility Updated Address"); facilityPage.fillOxygenCapacity("100"); diff --git a/cypress/e2e/facility_spec/facility_homepage.cy.ts b/cypress/e2e/facility_spec/facility_homepage.cy.ts index bf6ce12536e..3d916b4ba47 100644 --- a/cypress/e2e/facility_spec/facility_homepage.cy.ts +++ b/cypress/e2e/facility_spec/facility_homepage.cy.ts @@ -4,17 +4,22 @@ import LoginPage from "../../pageobject/Login/LoginPage"; import FacilityHome from "../../pageobject/Facility/FacilityHome"; import ManageUserPage from "../../pageobject/Users/ManageUserPage"; import FacilityPage from "../../pageobject/Facility/FacilityCreation"; +import { UserPage } from "../../pageobject/Users/UserSearch"; -describe("Facility Creation", () => { +describe("Facility Homepage Function", () => { const loginPage = new LoginPage(); const facilityHome = new FacilityHome(); const facilityPage = new FacilityPage(); const manageUserPage = new ManageUserPage(); + const userPage = new UserPage(); const facilitiesAlias = "downloadFacilitiesCSV"; const capacitiesAlias = "downloadCapacitiesCSV"; const doctorsAlias = "downloadDoctorsCSV"; const triagesAlias = "downloadTriagesCSV"; const facilityname = "Dummy Facility 1"; + const statename = "Kerala"; + const district = "Ernakulam"; + const facilitytype = "Private Hospital"; before(() => { loginPage.loginAsDisctrictAdmin(); @@ -26,6 +31,23 @@ describe("Facility Creation", () => { cy.awaitUrl("/facility"); }); + it("Verify the functionality of advance filter", () => { + userPage.clickAdvancedFilters(); + facilityPage.selectState(statename); + facilityPage.selectDistrict(district); + // facilityPage.selectLocalBody("Anthikad Grama"); current dummy data have issue in local body + facilityPage.clickUpdateFacilityType(facilitytype); + userPage.applyFilter(); + facilityPage.verifyStateBadgeContent(statename); + facilityPage.verifyDistrictBadgeContent(district); + facilityPage.verifyFacilityTypeBadgeContent(facilitytype); + manageUserPage.assertFacilityInCard(facilityname); + userPage.clearFilters(); + userPage.verifyDataTestIdNotVisible("State"); + userPage.verifyDataTestIdNotVisible("District"); + userPage.verifyDataTestIdNotVisible("Facility type"); + }); + it("Search a facility in homepage", () => { manageUserPage.typeFacilitySearch(facilityname); facilityPage.verifyFacilityBadgeContent(facilityname); diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index d27433f04e7..8c20b808c98 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -17,11 +17,11 @@ class FacilityPage { cy.get("#manage-facility-dropdown button").should("be.visible"); } - clickUpdateFacilityType() { + clickUpdateFacilityType(facilityType) { cy.get("#facility_type") .click() .then(() => { - cy.get("[role='option']").contains("Request Approving Center").click(); + cy.get("[role='option']").contains(facilityType).click(); }); } @@ -195,6 +195,18 @@ class FacilityPage { ); } + verifyStateBadgeContent(expectedText: string) { + cy.get("[data-testid='State']").should("contain", expectedText); + } + + verifyDistrictBadgeContent(expectedText: string) { + cy.get("[data-testid='District']").should("contain", expectedText); + } + + verifyFacilityTypeBadgeContent(expectedText: string) { + cy.get("[data-testid='Facility type']").should("contain", expectedText); + } + verifyfacilitycreateassetredirection() { cy.url().should("include", "/assets/new"); }