From 19a751380eef2fc61afa6cafebaad26a0ccfaae2 Mon Sep 17 00:00:00 2001 From: Abhiuday Date: Sat, 28 Oct 2023 18:45:31 +0530 Subject: [PATCH] fix(location): added modal to add/remove duty staff --- src/Components/Assets/AssetTypes.tsx | 6 + src/Components/Facility/AddLocationForm.tsx | 39 ++- .../Facility/LocationManagement.tsx | 232 ++++++++++++++---- src/Components/Facility/models.tsx | 10 +- src/Locale/en/Asset.json | 7 +- src/Locale/en/Location.json | 6 + src/Locale/en/index.js | 18 +- src/Redux/actions.tsx | 29 +++ src/Redux/api.tsx | 45 +++- 9 files changed, 303 insertions(+), 89 deletions(-) create mode 100644 src/Locale/en/Location.json diff --git a/src/Components/Assets/AssetTypes.tsx b/src/Components/Assets/AssetTypes.tsx index a4005404da1..5773bf8765c 100644 --- a/src/Components/Assets/AssetTypes.tsx +++ b/src/Components/Assets/AssetTypes.tsx @@ -1,6 +1,7 @@ import { BedModel } from "../Facility/models"; import { PerformedByModel } from "../HCX/misc"; import { PatientModel } from "../Patient/models"; +import { UserAssignedModel } from "../Users/models"; export interface AssetLocationObject { id: string; @@ -15,6 +16,11 @@ export interface AssetLocationObject { }; } +export interface AssetLocationDutyStaffObject { + duty_staff_objects: UserAssignedModel[]; + duty_staff: number; +} + export enum AssetType { NONE = "NONE", INTERNAL = "INTERNAL", diff --git a/src/Components/Facility/AddLocationForm.tsx b/src/Components/Facility/AddLocationForm.tsx index e71b68cc95c..090ee259e9e 100644 --- a/src/Components/Facility/AddLocationForm.tsx +++ b/src/Components/Facility/AddLocationForm.tsx @@ -1,4 +1,5 @@ -import { useState, useEffect, lazy, SyntheticEvent } from "react"; +import { navigate } from "raviger"; +import { SyntheticEvent, lazy, useEffect, useState } from "react"; import { useDispatch } from "react-redux"; import { createFacilityAssetLocation, @@ -7,11 +8,10 @@ import { updateFacilityAssetLocation, } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; -import { navigate } from "raviger"; -import { Submit, Cancel } from "../Common/components/ButtonV2"; -import TextFormField from "../Form/FormFields/TextFormField"; -import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; +import { Cancel, Submit } from "../Common/components/ButtonV2"; import Page from "../Common/components/Page"; +import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; +import TextFormField from "../Form/FormFields/TextFormField"; const Loading = lazy(() => import("../Common/Loading")); @@ -29,10 +29,16 @@ export const AddLocationForm = (props: LocationFormProps) => { const [description, setDescription] = useState(""); const [facilityName, setFacilityName] = useState(""); const [locationName, setLocationName] = useState(""); - const [errors, setErrors] = useState({ + const [errors, setErrors] = useState<{ + name: string; + description: string; + middlewareAddress: string; + duty_staff: string; + }>({ name: "", description: "", middlewareAddress: "", + duty_staff: "", }); const headerText = !locationId ? "Add Location" : "Update Location"; const buttonText = !locationId ? "Add Location" : "Update Location"; @@ -41,9 +47,8 @@ export const AddLocationForm = (props: LocationFormProps) => { async function fetchFacilityName() { setIsLoading(true); if (facilityId) { - const res = await dispatchAction(getAnyFacility(facilityId)); - - setFacilityName(res?.data?.name || ""); + const facility = await dispatchAction(getAnyFacility(facilityId)); + setFacilityName(facility?.data?.name || ""); } if (locationId) { const res = await dispatchAction( @@ -66,6 +71,7 @@ export const AddLocationForm = (props: LocationFormProps) => { name: "", description: "", middlewareAddress: "", + duty_staff: "", }; if (name.trim().length === 0) { @@ -112,15 +118,16 @@ export const AddLocationForm = (props: LocationFormProps) => { ? "Location updated successfully" : "Location created successfully"; - navigate(`/facility/${facilityId}/location`, { - replace: true, - }); Notification.Success({ msg: notificationMessage, }); + + navigate(`/facility/${facilityId}/location`, { + replace: true, + }); } else if (res.status === 400) { Object.keys(res.data).forEach((key) => { - setErrors((prevState: any) => ({ + setErrors((prevState) => ({ ...prevState, [key]: res.data[key], })); @@ -151,6 +158,12 @@ export const AddLocationForm = (props: LocationFormProps) => {
+
+ +
+
import("../Common/Loading")); @@ -14,6 +21,7 @@ interface Props { } export default function LocationManagement({ facilityId }: Props) { + const { t } = useTranslation(); return ( {() => ( - Add New Location + {t("add_new_location")}
- No locations available + {t("no_locations_available")} @@ -53,7 +61,7 @@ export default function LocationManagement({ facilityId }: Props) { className="my-8 flex grow flex-col gap-3 lg:mx-8"> - {(item) => } + {(item) => }
@@ -64,48 +72,172 @@ export default function LocationManagement({ facilityId }: Props) { ); } +interface LocationProps extends LocationModel { + facilityId: string; +} + +const Location = (props: LocationProps) => { + const { t } = useTranslation(); + const { id, name, description, middleware_address, facilityId } = props; + const [toggle, setToggle] = useState(false); + const [disabled, setDisabled] = useState(false); + const { data, loading } = useQuery(routes.getFacilityUsers, { + pathParams: { facility_id: facilityId }, + }); + const [selected, setSelected] = useState(); + const userList = + data?.results.filter( + (u) => u.user_type === "Doctor" || u.user_type === "Staff" + ) || []; + + const { + data: dutyStaffList, + loading: dutyStaffLoading, + refetch, + } = useQuery(routes.getFacilityAssetLocationDutyStaff, { + pathParams: { facility_external_id: facilityId, external_id: id ?? "" }, + }); + + const handleAssign = async () => { + if (!selected) return; + setDisabled(true); -const Location = ({ - name, - description, - middleware_address, - id, -}: LocationModel) => ( -
-
-
-

- {name} -

- {description || "-"} -

-

-

- {middleware_address} -

+ const { res } = await request(routes.createFacilityAssetLocationDutyStaff, { + pathParams: { facility_external_id: facilityId, external_id: id ?? "" }, + body: { duty_staff: selected.id }, + }); + + if (res) { + if (res?.ok && res.status === 201) { + Notification.Success({ + msg: "Duty Staff Assigned Successfully", + }); + refetch(); + } + } else { + Notification.Error({ + msg: "Something went wrong", + }); + } + + setDisabled(false); + setSelected(undefined); + }; + + const handleDelete = async (userId: number) => { + if (!userId) return; + setDisabled(true); + + const { res } = await request(routes.removeFacilityAssetLocationDutyStaff, { + pathParams: { facility_external_id: facilityId, external_id: id ?? "" }, + body: { duty_staff: userId }, + }); + + if (res) { + if (res?.ok && res.status === 204) { + Notification.Success({ + msg: "Duty Staff removed Successfully", + }); + refetch(); + } + } else { + Notification.Error({ + msg: "Something went wrong", + }); + } + setDisabled(false); + }; + + if (loading || dutyStaffLoading) { + return ; + } + + return ( +
+
+
+

{name}

+

{description}

+

{middleware_address}

+
-
-
- - - Edit - - - - Manage Beds - +
+ {toggle && ( + setToggle((prev) => !prev)} + > +
+ {dutyStaffList?.map((user) => ( + + ))} +
+
+ setSelected(e.value)} + options={userList} + optionLabel={(option) => + `${option.first_name} ${option.last_name} (${option.user_type})` + } + optionValue={(option) => option} + isLoading={loading} + /> + handleAssign()} + disabled={!selected} + > + {t("assign")} + +
+
+ )} + setToggle((prev) => !prev)} + > + + {t("assign_duty_staff")} + + + + {t("edit")} + + + + {t("manage_beds")} + +
-
-); + ); +}; diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index cdc7074c145..4152089f806 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -1,10 +1,10 @@ -import { AssignedToObjectModel } from "../Patient/models"; -import { ProcedureType } from "../Common/prescription-builder/ProcedureBuilder"; -import { NormalPrescription, PRNPrescription } from "../Medicine/models"; import { AssetData } from "../Assets/AssetTypes"; -import { UserBareMinimum } from "../Users/models"; import { RouteToFacility } from "../Common/RouteToFacilitySelect"; +import { ProcedureType } from "../Common/prescription-builder/ProcedureBuilder"; import { ConsultationDiagnosis, CreateDiagnosis } from "../Diagnosis/types"; +import { NormalPrescription, PRNPrescription } from "../Medicine/models"; +import { AssignedToObjectModel } from "../Patient/models"; +import { UserAssignedModel, UserBareMinimum } from "../Users/models"; export interface LocalBodyModel { name: string; @@ -206,6 +206,8 @@ export interface LocationModel { facility?: { name: string; }; + + users?: UserAssignedModel[]; } export interface BedModel { diff --git a/src/Locale/en/Asset.json b/src/Locale/en/Asset.json index f24549ee0b6..7b9979b7d8b 100644 --- a/src/Locale/en/Asset.json +++ b/src/Locale/en/Asset.json @@ -11,5 +11,8 @@ "update_asset_service_record": "Update Asset Service Record", "eg_details_on_functionality_service_etc": "Eg. Details on functionality, service, etc.", "updating": "Updating", - "update": "Update" -} + "update": "Update", + "search_user_placeholder": "Search User", + "assign_duty_staff": "Assign Duty Staff", + "assign": "Assign" +} \ No newline at end of file diff --git a/src/Locale/en/Location.json b/src/Locale/en/Location.json new file mode 100644 index 00000000000..626cbada8da --- /dev/null +++ b/src/Locale/en/Location.json @@ -0,0 +1,6 @@ +{ + "location_management": "Location Management", + "manage_beds": "Manage Beds", + "add_new_location": "Add New Location", + "no_locations_available": "No Locations Available" +} \ No newline at end of file diff --git a/src/Locale/en/index.js b/src/Locale/en/index.js index 781ce97b009..d214226e0f7 100644 --- a/src/Locale/en/index.js +++ b/src/Locale/en/index.js @@ -1,20 +1,21 @@ -import Auth from "./Auth.json"; import Asset from "./Asset.json"; +import Auth from "./Auth.json"; +import Bed from "./Bed.json"; import Common from "./Common.json"; import Consultation from "./Consultation.json"; +import CoverImageEdit from "./CoverImageEdit.json"; +import Diagnosis from "./Diagnosis.json"; import Entities from "./Entities.json"; +import ErrorPages from "./ErrorPages.json"; +import ExternalResult from "./ExternalResult.json"; import Facility from "./Facility.json"; import Hub from "./Hub.json"; -import ErrorPages from "./ErrorPages.json"; -import Shifting from "./Shifting.json"; +import Location from "./Location.json"; +import Medicine from "./Medicine.json"; import Notifications from "./Notifications.json"; -import ExternalResult from "./ExternalResult.json"; -import CoverImageEdit from "./CoverImageEdit.json"; import Resource from "./Resource.json"; +import Shifting from "./Shifting.json"; import SortOptions from "./SortOptions.json"; -import Bed from "./Bed.json"; -import Medicine from "./Medicine.json"; -import Diagnosis from "./Diagnosis.json"; export default { ...Auth, @@ -33,5 +34,6 @@ export default { ...Resource, ...Shifting, ...Bed, + ...Location, SortOptions, }; diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index e380949887b..fb999d3e191 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -169,6 +169,35 @@ export const partialUpdateFacilityAssetLocation = ( external_id, }); +export const createFacilityAssetLocationDutyStaff = ( + params: object, + facility_external_id: string, + external_id: string +) => + fireRequest("createFacilityAssetLocationDutyStaff", [], params, { + facility_external_id, + external_id, + }); + +export const removeFacilityAssetLocationDutyStaff = ( + duty_staff: number[], + facility_external_id: string, + external_id: string +) => + fireRequest( + "removeFacilityAssetLocationDutyStaff", + [], + { + data: { + duty_staff: duty_staff, + }, + }, + { + facility_external_id, + external_id, + } + ); + // asset bed export const listAssetBeds = (params: object, altKey?: string) => fireRequest("listAssetBeds", [], params, {}, altKey); diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 4172effe432..c1c7803fa41 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -28,6 +28,14 @@ import { AssetTransaction, AssetUpdate, } from "../Components/Assets/AssetTypes"; +import { + IDeleteExternalResult, + IExternalResult, + IExternalResultCsv, + ILocalBodies, + ILocalBodyByDistrict, + IPartialUpdateExternalResult, +} from "../Components/ExternalResult/models"; import { CapacityModal, ConsultationModel, @@ -44,27 +52,19 @@ import { PatientStatsModel, WardModel, } from "../Components/Facility/models"; -import { - IDeleteExternalResult, - IExternalResult, - IExternalResultCsv, - ILocalBodies, - ILocalBodyByDistrict, - IPartialUpdateExternalResult, -} from "../Components/ExternalResult/models"; import { Prescription } from "../Components/Medicine/models"; -import { UserModel } from "../Components/Users/models"; -import { DailyRoundsModel, PatientModel } from "../Components/Patient/models"; -import { PaginatedResponse } from "../Utils/request/types"; import { NotificationData, PNconfigData, } from "../Components/Notifications/models"; +import { DailyRoundsModel, PatientModel } from "../Components/Patient/models"; +import { HCXPolicyModel } from "../Components/HCX/models"; import { IComment, IResource } from "../Components/Resource/models"; import { IShift } from "../Components/Shifting/models"; -import { HCXPolicyModel } from "../Components/HCX/models"; +import { UserBareMinimum, UserModel } from "../Components/Users/models"; +import { PaginatedResponse } from "../Utils/request/types"; /** * A fake function that returns an empty object casted to type T @@ -325,6 +325,27 @@ const routes = { path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/", method: "PATCH", }, + getFacilityAssetLocationDutyStaff: { + path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/duty_staff/", + method: "GET", + TRes: Type(), + }, + createFacilityAssetLocationDutyStaff: { + path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/duty_staff/", + method: "POST", + TRes: Type>(), + TBody: Type<{ + duty_staff: number; + }>(), + }, + removeFacilityAssetLocationDutyStaff: { + path: "/api/v1/facility/{facility_external_id}/asset_location/{external_id}/duty_staff/", + method: "DELETE", + TRes: Type>(), + TBody: Type<{ + duty_staff: number; + }>(), + }, // Asset bed listAssetBeds: {