Skip to content

Commit d1fc0f5

Browse files
committed
Fetching rooms under different clinics
1 parent 23de37a commit d1fc0f5

File tree

7 files changed

+400
-175
lines changed

7 files changed

+400
-175
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"*"
5656
],
5757
"peerDependencies": {
58-
"@carbon/react": "1.x",
58+
"@carbon/react": "^1.83.0",
5959
"@openmrs/esm-framework": "*",
6060
"react": "18.x",
6161
"react-dom": "18.x",

src/change-location/change-location-modal.scss

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
@use '@carbon/styles/scss/type';
33
@use '@openmrs/esm-styleguide/src/vars' as *;
44

5+
.switch {
6+
display: flex;
7+
align-items: center;
8+
gap: 4px;
9+
}
10+
511
.container {
612
display: flex;
713
flex-direction: column;

src/change-location/change-location-modal.tsx

Lines changed: 151 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,33 @@
1-
import React, { useState } from "react";
1+
import React, { useCallback, useState } from "react";
22
import {
33
Button,
4+
ContentSwitcher,
45
Form,
6+
Hospital,
57
Layer,
68
ModalHeader,
79
ModalBody,
810
ModalFooter,
911
Select,
1012
SelectItem,
1113
Stack,
14+
Switch,
15+
TaskLocation,
1216
InlineNotification,
1317
InlineLoading,
1418
} from "@carbon/react";
1519
import {
1620
DefaultWorkspaceProps,
21+
showSnackbar,
1722
useLayoutType,
1823
useSession,
1924
} from "@openmrs/esm-framework";
20-
import { Controller, useForm } from "react-hook-form";
25+
import { Controller, SubmitHandler, useForm } from "react-hook-form";
2126
import styles from "./change-location-link.scss";
2227
import { useTranslation } from "react-i18next";
2328
import { zodResolver } from "@hookform/resolvers/zod";
2429
import z from "zod";
25-
import { useRoomLocations } from "./change-location.resource";
30+
import { Provider, saveProvider, useClinicLocations, useRoomLocations } from "./change-location.resource";
2631

2732
type ChangeLocationProps = DefaultWorkspaceProps;
2833

@@ -31,14 +36,18 @@ const ChangeLocationModal: React.FC<ChangeLocationProps> = ({
3136
}) => {
3237
const { t } = useTranslation();
3338
const isTablet = useLayoutType() === "tablet";
39+
const [tabType, setTabType] = useState("Change room");
3440
const session = useSession();
35-
const currentLocation = session?.sessionLocation?.display;
41+
const currentLocation = session?.sessionLocation?.uuid;
3642
const [isChangingRoom, setIsChangingRoom] = useState(false);
43+
const [selectedClinicRoom, setselectedClinicRoom] = useState<string | undefined>();
44+
const [selectedClinic, setSelectedClinic] = useState<string | undefined>();
45+
const { roomLocations, error: errorFetchingRooms } = useRoomLocations(selectedClinic);
46+
const {clinicsList, error: errorFetchingClinics} = useClinicLocations();
3747

38-
const { roomLocations, error: errorFetchingRooms } = useRoomLocations(
39-
session?.sessionLocation?.uuid
40-
);
41-
const [selectedClinciRoom, setselectedClinciRoom] = useState();
48+
const handleTabTypeChange = ({ name }) => {
49+
setTabType(name);
50+
};
4251

4352
const changeLocationSchema = z.object({
4453
clinicRoom: z.string().optional(),
@@ -51,16 +60,105 @@ const ChangeLocationModal: React.FC<ChangeLocationProps> = ({
5160
} = useForm({
5261
resolver: zodResolver(changeLocationSchema),
5362
});
63+
64+
const onSubmit: SubmitHandler<any> = useCallback(
65+
(data) => {
66+
setIsChangingRoom(true);
67+
68+
const newLocationUuid = data.clinicRoom ?? data.clinic;
69+
const providerUuid = session?.currentProvider?.uuid;
70+
const personUuid = session?.user?.person?.uuid;
71+
72+
const payload: Provider = {
73+
uuid: providerUuid,
74+
person: { uuid: personUuid },
75+
attributes: [
76+
{
77+
attributeType: {
78+
uuid: "13a721e4-68e5-4f7a-8aee-3cbcec127179",
79+
display: "Default Location",
80+
},
81+
value: {
82+
uuid: newLocationUuid,
83+
display: roomLocations.find(loc => loc.uuid === newLocationUuid)?.display || "Selected Location"
84+
}
85+
}
86+
],
87+
};
88+
89+
saveProvider(payload)
90+
.then(() => {
91+
closeWorkspace();
92+
showSnackbar({
93+
title: t("locationChanged", "Default location updated"),
94+
kind: "success",
95+
});
96+
})
97+
.catch((error) => {
98+
const errorMessage =
99+
error?.responseBody?.message || error?.message || "An unexpected error occurred";
100+
console.error("Provider update failed:", errorMessage);
101+
})
102+
.finally(() => {
103+
setIsChangingRoom(false);
104+
});
105+
},
106+
[session, closeWorkspace, t, roomLocations]
107+
);
108+
109+
54110
return (
55-
<Form>
111+
<Form onSubmit={handleSubmit(onSubmit)}>
56112
<ModalHeader
57113
closeModal={close}
58114
title={t("changeLocation", "Change location")}
59115
/>
60116
<ModalBody>
117+
118+
<ContentSwitcher onChange={handleTabTypeChange}>
119+
<Switch name="Change room" text={t("changeRoom", "Switch room")} />
120+
<Switch name="Change clinic" text={t("changeClinic", "Switch only clinic")} />
121+
</ContentSwitcher>
122+
61123
<Stack gap={5} className={styles.languageOptionsContainer}>
62124
<ResponsiveWrapper isTablet={isTablet}>
63-
<Controller
125+
{tabType === "Change room" && (
126+
<><Controller
127+
name="clinicLocation"
128+
control={control}
129+
defaultValue=""
130+
render={({ field }) => (
131+
<Select
132+
{...field}
133+
id="clinicLocation"
134+
name="clinicLocation"
135+
labelText="Select clinic"
136+
disabled={errorFetchingClinics}
137+
invalidText={errors.root?.message}
138+
value={field.value}
139+
onChange={(e) => {
140+
const selectedValue = e.target.value;
141+
field.onChange(selectedValue);
142+
setSelectedClinic(selectedValue);
143+
}}
144+
>
145+
{!field.value && (
146+
<SelectItem
147+
value=""
148+
text={t(
149+
"selectClinic",
150+
"Choose clinic"
151+
)}
152+
/>
153+
)}
154+
155+
{clinicsList.map(({ uuid, display }) => (
156+
<SelectItem key={uuid} value={uuid} text={display} />
157+
))}
158+
</Select>
159+
)}
160+
/>
161+
<Controller
64162
name="clinicRoom"
65163
control={control}
66164
defaultValue={currentLocation}
@@ -70,22 +168,21 @@ const ChangeLocationModal: React.FC<ChangeLocationProps> = ({
70168
id="clinicRoom"
71169
name="clinicRoom"
72170
labelText="Select room to change to"
73-
disabled={errorFetchingRooms}
74-
invalid={!!errors.locationTo}
171+
disabled={!selectedClinic || errorFetchingRooms}
75172
invalidText={errors.locationTo?.message}
76173
value={field.value}
77174
onChange={(e) => {
78175
const selectedValue = e.target.value;
79176
field.onChange(selectedValue);
80-
setselectedClinciRoom(selectedValue);
177+
setselectedClinicRoom(selectedValue);
81178
}}
82179
>
83180
{!field.value && (
84181
<SelectItem
85182
value=""
86183
text={t(
87-
"selectNextServicePoint",
88-
"Choose next service point"
184+
"selectRoom",
185+
"Choose room"
89186
)}
90187
/>
91188
)}
@@ -95,7 +192,44 @@ const ChangeLocationModal: React.FC<ChangeLocationProps> = ({
95192
))}
96193
</Select>
97194
)}
98-
/>
195+
/></>
196+
)}
197+
{tabType === "Change clinic" && (
198+
<Controller
199+
name="clinic"
200+
control={control}
201+
defaultValue={currentLocation}
202+
render={({ field }) => (
203+
<Select
204+
{...field}
205+
id="clinic"
206+
name="clinic"
207+
labelText="Select clinic to change to"
208+
disabled={errorFetchingClinics}
209+
invalidText={errors.root?.message}
210+
value={field.value}
211+
onChange={(e) => {
212+
const selectedValue = e.target.value;
213+
field.onChange(selectedValue);
214+
setSelectedClinic(selectedValue);
215+
}}
216+
>
217+
{!field.value && (
218+
<SelectItem
219+
value=""
220+
text={t(
221+
"selectClinic",
222+
"Choose clinic"
223+
)}
224+
/>
225+
)}
226+
227+
{clinicsList.map(({ uuid, display }) => (
228+
<SelectItem key={uuid} value={uuid} text={display} />
229+
))}
230+
</Select>
231+
)}
232+
/>)}
99233

100234
{errorFetchingRooms && (
101235
<InlineNotification
@@ -123,7 +257,7 @@ const ChangeLocationModal: React.FC<ChangeLocationProps> = ({
123257
>
124258
{isChangingRoom ? (
125259
<InlineLoading
126-
description={t("changingPassword", "Changing password") + "..."}
260+
description={t("changingLocation", "Changing location") + "..."}
127261
/>
128262
) : (
129263
<span>{t("change", "Change")}</span>

src/change-location/change-location.resource.ts

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,36 @@ import { restBaseUrl, openmrsFetch } from "@openmrs/esm-framework";
22
import { useMemo } from "react";
33
import useSWR from "swr";
44
import { RoomsResponse } from "../types";
5+
import { FACILITY_LOCATION_UUID } from "../constants";
56

6-
export function useRoomLocations(currentLocation: string) {
7-
const apiUrl = `${restBaseUrl}/location/${currentLocation}?v=full`;
7+
export interface Provider {
8+
uuid?: string;
9+
person: {
10+
uuid: string;
11+
};
12+
identifier?: string;
13+
attributes?: Attributes[];
14+
}
15+
16+
export interface Attributes {
17+
display?: string;
18+
uuid?: string;
19+
attributeType: AttributeType;
20+
value: Value;
21+
}
22+
23+
export interface AttributeType {
24+
uuid: string;
25+
display: string;
26+
}
27+
28+
export interface Value {
29+
uuid: string;
30+
display: string;
31+
}
32+
33+
export function useRoomLocations(clinicUuid?: string) {
34+
const apiUrl = clinicUuid ? `${restBaseUrl}/location/${clinicUuid}?v=full` : null;
835
const { data, error, isLoading, mutate } = useSWR<{ data: RoomsResponse }>(
936
apiUrl,
1037
openmrsFetch
@@ -25,3 +52,50 @@ export function useRoomLocations(currentLocation: string) {
2552
mutate,
2653
};
2754
}
55+
56+
export function useClinicLocations() {
57+
const apiUrl = `${restBaseUrl}/location/${FACILITY_LOCATION_UUID}?v=full`;
58+
const { data, error, isLoading, mutate } = useSWR<{ data: RoomsResponse }>(
59+
apiUrl,
60+
openmrsFetch
61+
);
62+
63+
const clinicLocations = useMemo(
64+
() => data?.data?.childLocations?.map((response) => response) ?? [],
65+
[data?.data?.childLocations]
66+
);
67+
return {
68+
clinicsList: clinicLocations.filter(
69+
(location) => location?.uuid != null
70+
)
71+
? clinicLocations
72+
: [],
73+
isLoading,
74+
error,
75+
mutate,
76+
};
77+
}
78+
79+
export async function saveProvider(payload: Provider) {
80+
const abortController = new AbortController();
81+
const isUpdating = !!payload.uuid;
82+
83+
const url = isUpdating
84+
? `${restBaseUrl}/provider/${payload.uuid}`
85+
: `${restBaseUrl}/provider`;
86+
87+
return await openmrsFetch(url, {
88+
method: "POST",
89+
signal: abortController.signal,
90+
headers: {
91+
"Content-Type": "application/json",
92+
},
93+
body: {
94+
person: payload.person.uuid,
95+
attributes: payload.attributes?.map(attr => ({
96+
attributeType: typeof attr.attributeType === 'string' ? attr.attributeType : attr.attributeType.uuid,
97+
value: typeof attr.value === 'string' ? attr.value : attr.value.uuid
98+
}))
99+
},
100+
});
101+
}

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const FACILITY_LOCATION_UUID = '629d78e9-93e5-43b0-ad8a-48313fd99117';

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,4 +223,4 @@ export interface Person {
223223

224224
export interface UserProperties {
225225
loginAttempts: string;
226-
}
226+
}

0 commit comments

Comments
 (0)