Skip to content

Commit

Permalink
Refactor Facility Settings; Location Organizations
Browse files Browse the repository at this point in the history
  • Loading branch information
gigincg committed Jan 30, 2025
1 parent e35cc72 commit 81a6257
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 156 deletions.
4 changes: 4 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@
"add_files": "Add Files",
"add_insurance_details": "Add Insurance Details",
"add_location": "Add Location",
"add_location_description": "Create a Location such as Rooms/Beds",
"add_new_beds": "Add New Bed(s)",
"add_new_facility": "Add New Facility",
"add_new_patient": "Add New Patient",
Expand Down Expand Up @@ -764,6 +765,8 @@
"edit_cover_photo": "Edit Cover Photo",
"edit_facility": "Edit Facility",
"edit_history": "Edit History",
"edit_location": "Edit Location",
"edit_location_description": "Edit the Location to make any changes",
"edit_policy": "Edit Insurance Policy",
"edit_policy_description": "Add or edit patient's insurance details",
"edit_prescriptions": "Edit Prescriptions",
Expand Down Expand Up @@ -1792,6 +1795,7 @@
"search_by": "Search by",
"search_by_emergency_contact_phone_number": "Search by Emergency Contact Phone Number",
"search_by_emergency_phone_number": "Search by Emergency Phone Number",
"search_by_name": "Search by Name",
"search_by_patient_name": "Search by Patient Name",
"search_by_patient_no": "Search by Patient Number",
"search_by_phone_number": "Search by Phone Number",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,77 @@ import {
import routes from "@/Utils/request/api";
import mutate from "@/Utils/request/mutate";
import FacilityOrganizationSelector from "@/pages/Facility/settings/organizations/components/FacilityOrganizationSelector";
import { Encounter } from "@/types/emr/encounter";
import { FacilityOrganization } from "@/types/facilityOrganization/facilityOrganization";
import locationApi from "@/types/location/locationApi";

interface Props {
encounter: Encounter;
entityType: "encounter" | "location";
entityId: string;
currentOrganizations: FacilityOrganization[];
facilityId: string;
trigger?: React.ReactNode;
onUpdate?: () => void;
}

export default function ManageEncounterOrganizations({
encounter,
type MutationRoute =
| typeof routes.encounter.addOrganization
| typeof routes.encounter.removeOrganization
| typeof locationApi.addOrganization
| typeof locationApi.removeOrganization;

interface EncounterPathParams {
encounterId: string;
}

interface LocationPathParams {
facility_id: string;
id: string;
}

type PathParams = EncounterPathParams | LocationPathParams;

interface MutationParams {
route: MutationRoute;
pathParams: PathParams;
queryKey: string[];
}

function getMutationParams(
entityType: "encounter" | "location",
entityId: string,
facilityId: string,
isAdd: boolean,
): MutationParams {
if (entityType === "encounter") {
return {
route: isAdd
? routes.encounter.addOrganization
: routes.encounter.removeOrganization,
pathParams: { encounterId: entityId } as EncounterPathParams,
queryKey: ["encounter", entityId],
};
}
return {
route: isAdd ? locationApi.addOrganization : locationApi.removeOrganization,
pathParams: { facility_id: facilityId, id: entityId } as LocationPathParams,
queryKey: ["location", entityId],
};
}

function getInvalidateQueries(
entityType: "encounter" | "location",
entityId: string,
) {
if (entityType === "encounter") {
return ["encounter", entityId];
}
return ["location", entityId, "organizations"];
}

export default function LinkDepartmentsSheet({
entityType,
entityId,
currentOrganizations,
facilityId,
trigger,
onUpdate,
Expand All @@ -37,13 +97,21 @@ export default function ManageEncounterOrganizations({
const queryClient = useQueryClient();

const { mutate: addOrganization, isPending: isAdding } = useMutation({
mutationFn: (organizationId: string) =>
mutate(routes.encounter.addOrganization, {
pathParams: { encounterId: encounter.id },
mutationFn: (organizationId: string) => {
const { route, pathParams } = getMutationParams(
entityType,
entityId,
facilityId,
true,
);
return mutate(route, {
pathParams,
body: { organization: organizationId },
})({ organization: organizationId }),
})({ organization: organizationId });
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["encounter", encounter.id] });
const invalidateQueries = getInvalidateQueries(entityType, entityId);
queryClient.invalidateQueries({ queryKey: invalidateQueries });
toast.success("Organization added successfully");
setSelectedOrg("");
setOpen(false);
Expand All @@ -58,13 +126,26 @@ export default function ManageEncounterOrganizations({
});

const { mutate: removeOrganization, isPending: isRemoving } = useMutation({
mutationFn: (organizationId: string) =>
mutate(routes.encounter.removeOrganization, {
pathParams: { encounterId: encounter.id },
mutationFn: (organizationId: string) => {
const { route, pathParams } = getMutationParams(
entityType,
entityId,
facilityId,
false,
);
return mutate(route, {
pathParams,
body: { organization: organizationId },
})({ organization: organizationId }),
})({ organization: organizationId });
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["encounter", encounter.id] });
const { queryKey } = getMutationParams(
entityType,
entityId,
facilityId,
false,
);
queryClient.invalidateQueries({ queryKey });
toast.success("Organization removed successfully");
onUpdate?.();
},
Expand Down Expand Up @@ -118,7 +199,7 @@ export default function ManageEncounterOrganizations({
{t("current_organizations")}
</h3>
<div className="space-y-2">
{encounter.organizations.map((org) => (
{currentOrganizations.map((org) => (
<div
key={org.id}
className="flex items-center justify-between rounded-md border p-2"
Expand Down Expand Up @@ -148,7 +229,7 @@ export default function ManageEncounterOrganizations({
</Button>
</div>
))}
{encounter.organizations.length === 0 && (
{currentOrganizations.length === 0 && (
<p className="text-sm text-muted-foreground">
{t("no_organizations_added_yet")}
</p>
Expand Down
8 changes: 5 additions & 3 deletions src/components/Patient/PatientInfoCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { formatDateTime, formatPatientAge } from "@/Utils/utils";
import { Encounter, completedEncounterStatus } from "@/types/emr/encounter";
import { Patient } from "@/types/emr/newPatient";

import ManageEncounterOrganizations from "./ManageEncounterOrganizations";
import LinkDepartmentsSheet from "./LinkDepartmentsSheet";

export interface PatientInfoCardProps {
patient: Patient;
Expand Down Expand Up @@ -269,8 +269,10 @@ export default function PatientInfoCard(props: PatientInfoCardProps) {
</Badge>
)}

<ManageEncounterOrganizations
encounter={encounter}
<LinkDepartmentsSheet
entityType="encounter"
entityId={encounter.id}
currentOrganizations={encounter.organizations}
facilityId={encounter.facility.id}
trigger={
<div className="flex flex-wrap gap-2">
Expand Down
2 changes: 1 addition & 1 deletion src/components/ui/sidebar/facility-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ function generateFacilityLinks(
{ name: t("users"), url: `${baseUrl}/users`, icon: "d-people" },
{
name: t("settings"),
url: `${baseUrl}/settings`,
url: `${baseUrl}/settings/general`,
icon: "l-setting",
},
];
Expand Down
3 changes: 1 addition & 2 deletions src/pages/Facility/settings/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ interface SettingsLayoutProps {
}

const getRoutes = (facilityId: string) => ({
"/": () => <GeneralSettings facilityId={facilityId} />,
"/general": () => <GeneralSettings facilityId={facilityId} />,
"/departments": () => <FacilityOrganizationIndex facilityId={facilityId} />,
"/departments/:id": ({ id }: { id: string }) => (
Expand Down Expand Up @@ -72,7 +71,7 @@ export function SettingsLayout({ facilityId }: SettingsLayoutProps) {
<Link key={tab.value} href={tab.href}>
<TabsTrigger
value={tab.value}
className="border-b-2 border-transparent px-4 py-2 data-[state=active]:border-primary-500 data-[state=active]:bg-transparent data-[state=active]:shadow-none rounded-none"
className="border-b-2 border-transparent px-4 py-2 text-gray-600 hover:text-gray-900 data-[state=active]:border-primary-500 data-[state=active]:bg-transparent data-[state=active]:shadow-none rounded-none"
>
{tab.label}
</TabsTrigger>
Expand Down
95 changes: 51 additions & 44 deletions src/pages/Facility/settings/locations/LocationForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useEffect } from "react";
import { useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
Expand All @@ -25,11 +26,12 @@ import {
import { Textarea } from "@/components/ui/textarea";

import mutate from "@/Utils/request/mutate";
import query from "@/Utils/request/query";
import {
LocationList,
LocationWrite,
OperationalStatus,
Status,
locationFormOptions,
} from "@/types/location/location";
import locationApi from "@/types/location/locationApi";

Expand Down Expand Up @@ -58,52 +60,71 @@ const formSchema = z.object({
mode: z.enum(["instance", "kind"] as const),
parent: z.string().optional().nullable(),
organizations: z.array(z.string()).default([]),
availability_status: z.enum(["available", "unavailable"] as const),
});

type FormValues = z.infer<typeof formSchema>;

interface Props {
facilityId: string;
onSuccess?: () => void;
location?: LocationList;
locationId?: string;
parentId?: string;
}

const defaultValues: FormValues = {
name: "",
description: "",
status: "active",
operational_status: "O",
form: "ro",
mode: "instance",
parent: null,
organizations: [],
availability_status: "available",
};

export default function LocationForm({
facilityId,
onSuccess,
location,
locationId,
parentId,
}: Props) {
const { t } = useTranslation();
const queryClient = useQueryClient();

// Initialize form with either edit values or create defaults
const { data: location, isLoading } = useQuery({
queryKey: ["location", locationId],
queryFn: query(locationApi.get, {
pathParams: { facility_id: facilityId, id: locationId },
}),
enabled: !!locationId,
});

const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: location
? {
name: location.name,
description: location.description,
status: location.status,
operational_status: location.operational_status,
form: location.form,
mode: location.mode,
parent: parentId || null,
organizations: [],
}
: {
name: "",
description: "",
status: "active",
operational_status: "O",
form: "ro",
mode: "instance",
parent: parentId || null,
organizations: [],
},
defaultValues: {
...defaultValues,
parent: parentId || null,
},
});

useEffect(() => {
if (location) {
form.reset({
name: location.name,
description: location.description,
status: location.status,
operational_status: location.operational_status,
form: location.form,
mode: location.mode,
parent: parentId || null,
organizations: [],
availability_status: location.availability_status || "available",
});
}
}, [location, form, parentId]);

const { mutate: submitForm, isPending } = useMutation({
mutationFn: location?.id
? mutate(locationApi.update, {
Expand Down Expand Up @@ -137,24 +158,6 @@ export default function LocationForm({
submitForm(locationData);
}

const locationFormOptions = [
{ value: "si", label: "Site" },
{ value: "bu", label: "Building" },
{ value: "wi", label: "Wing" },
{ value: "wa", label: "Ward" },
{ value: "lvl", label: "Level" },
{ value: "co", label: "Corridor" },
{ value: "ro", label: "Room" },
{ value: "bd", label: "Bed" },
{ value: "ve", label: "Vehicle" },
{ value: "ho", label: "House" },
{ value: "ca", label: "Cabinet" },
{ value: "rd", label: "Road" },
{ value: "area", label: "Area" },
{ value: "jdn", label: "Jurisdiction" },
{ value: "vi", label: "Virtual" },
];

const statusOptions: { value: Status; label: string }[] = [
{ value: "active", label: "Active" },
{ value: "inactive", label: "Inactive" },
Expand All @@ -178,6 +181,10 @@ export default function LocationForm({
{ value: "kind", label: "Kind" },
];

if (locationId && isLoading) {
return <div className="p-4">Loading...</div>;
}

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
Expand Down
Loading

0 comments on commit 81a6257

Please sign in to comment.