207 lines
7.7 KiB
TypeScript
207 lines
7.7 KiB
TypeScript
import {type PatientData, SORT_SYMBOLS, type WardInfo, WardTypes} from "~/utils/models.ts";
|
|
import {ActionIcon, Group, Table, Text} from "@mantine/core";
|
|
import {useEffect, useMemo, useState} from "react";
|
|
import {apiGetWardPatients, apiTransferWard, apiGetWardList} from "~/utils/hms_api.ts";
|
|
import {getUserDisplayFullName, showErrorMessage, showInfoMessage} from "~/utils/utils.ts";
|
|
import {confirmCheckWardPatients, confirmDeleteWard, confirmEditOrCreateWard} from "~/components/subs/confirms.tsx";
|
|
import EyeIcon from "mdi-react/EyeIcon";
|
|
import {iconMStyle} from "~/styles.ts";
|
|
import PencilIcon from "mdi-react/PencilIcon";
|
|
import DeleteIcon from "mdi-react/DeleteIcon";
|
|
import SwapHorizontalIcon from "mdi-react/SwapHorizontalIcon";
|
|
import {modals} from "@mantine/modals";
|
|
import {Button, Select, Stack} from "@mantine/core";
|
|
import {useForm} from "@mantine/form";
|
|
import { ResponsiveTableContainer } from "./ResponsiveTableContainer";
|
|
|
|
function ConfirmTransferWard({patientId, currentWardId, onSuccess}: {patientId: number, currentWardId: number, onSuccess: () => void}) {
|
|
const [loading, setLoading] = useState(false);
|
|
const [wards, setWards] = useState<WardInfo[]>([]);
|
|
const [loadingWards, setLoadingWards] = useState(true);
|
|
|
|
const form = useForm({
|
|
initialValues: {
|
|
patient_id: patientId,
|
|
target_ward_id: '',
|
|
},
|
|
validate: {
|
|
target_ward_id: (value) => (!value ? 'Please select a ward' : null),
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
// Load wards for selection
|
|
apiGetWardList(1, true).then(res => {
|
|
if (res.success) {
|
|
setWards(res.data.wards);
|
|
} else {
|
|
showErrorMessage(res.message, "Failed to load wards");
|
|
}
|
|
})
|
|
.catch(err => {
|
|
showErrorMessage("Failed to load wards", "Error");
|
|
})
|
|
.finally(() => {
|
|
setLoadingWards(false);
|
|
});
|
|
}, []);
|
|
|
|
const wardOptions = useMemo(() => {
|
|
return wards
|
|
.filter(ward => ward.id !== currentWardId && ward.current_occupancy < ward.total_capacity) // Exclude current ward and show only those with available capacity
|
|
.map(ward => ({
|
|
value: ward.id.toString(),
|
|
label: `${ward.name} (${ward.current_occupancy}/${ward.total_capacity}) - ${WardTypes[ward.type]}`
|
|
}));
|
|
}, [wards, currentWardId]);
|
|
|
|
const handleFormSubmit = (values: typeof form.values) => {
|
|
setLoading(true);
|
|
apiTransferWard(
|
|
values.patient_id,
|
|
parseInt(values.target_ward_id)
|
|
).then(res => {
|
|
if (res.success) {
|
|
showInfoMessage("Patient transferred successfully", "Success");
|
|
modals.closeAll();
|
|
onSuccess();
|
|
} else {
|
|
showErrorMessage(res.message, "Failed to transfer patient");
|
|
}
|
|
})
|
|
.catch(err => {
|
|
showErrorMessage("Failed to transfer patient", "Error");
|
|
})
|
|
.finally(() => {
|
|
setLoading(false);
|
|
});
|
|
};
|
|
|
|
return (
|
|
<form onSubmit={form.onSubmit(handleFormSubmit)}>
|
|
<Stack>
|
|
<Select
|
|
withAsterisk
|
|
label="Select Target Ward"
|
|
placeholder="Choose a ward to transfer patient to"
|
|
data={wardOptions}
|
|
searchable
|
|
nothingFoundMessage={wardOptions.length === 0 ? "No available wards with capacity" : "No matching wards found"}
|
|
disabled={loadingWards || wardOptions.length === 0}
|
|
{...form.getInputProps('target_ward_id')}
|
|
onChange={(value) => {
|
|
if (!value) return;
|
|
form.setFieldValue('target_ward_id', value);
|
|
}}
|
|
/>
|
|
|
|
{wardOptions.length === 0 && !loadingWards && (
|
|
<Text c="red" size="sm">
|
|
No available wards with capacity. Please free up space in a ward first.
|
|
</Text>
|
|
)}
|
|
|
|
<Group mt="md" justify="flex-end">
|
|
<Button variant="outline" onClick={() => modals.closeAll()}>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
loading={loading || loadingWards}
|
|
disabled={wardOptions.length === 0}
|
|
color="blue"
|
|
>
|
|
Transfer Patient
|
|
</Button>
|
|
</Group>
|
|
</Stack>
|
|
</form>
|
|
);
|
|
}
|
|
|
|
export default function WardPatients ({ward_id, onChanged}: {ward_id: number, onChanged: () => void}) {
|
|
const [wardPatients, setWardPatients] = useState<PatientData[]>([])
|
|
|
|
useEffect(() => {
|
|
refreshWardPatients()
|
|
}, []);
|
|
|
|
const refreshWardPatients = () => {
|
|
apiGetWardPatients(ward_id).then((result) => {
|
|
if (result.success) {
|
|
setWardPatients(result.data.patients)
|
|
}
|
|
else {
|
|
showErrorMessage(result.message, "Failed to get ward patients")
|
|
}
|
|
}).catch((err => { showErrorMessage(err.toString(), "Failed to get ward patients") }) )
|
|
}
|
|
|
|
const handleTransferWard = (patientId: number) => {
|
|
modals.open({
|
|
title: "Transfer Patient",
|
|
centered: true,
|
|
size: "md",
|
|
children: (
|
|
<ConfirmTransferWard
|
|
patientId={patientId}
|
|
currentWardId={ward_id}
|
|
onSuccess={() => {
|
|
refreshWardPatients()
|
|
onChanged()
|
|
}}
|
|
/>
|
|
)
|
|
});
|
|
};
|
|
|
|
const rows = wardPatients.map((patient) => (
|
|
<Table.Tr key={patient.id}>
|
|
<Table.Td>{`${patient.title} ${patient.name}`}</Table.Td>
|
|
<Table.Td>{patient.gender}</Table.Td>
|
|
<Table.Td>{patient.birth_date}</Table.Td>
|
|
<Table.Td>{patient.email}</Table.Td>
|
|
<Table.Td>{patient.phone}</Table.Td>
|
|
<Table.Td>
|
|
<Group>
|
|
<ActionIcon onClick={() => handleTransferWard(patient.id)} title="Transfer to another ward">
|
|
<SwapHorizontalIcon style={iconMStyle}/>
|
|
</ActionIcon>
|
|
</Group>
|
|
</Table.Td>
|
|
</Table.Tr>
|
|
))
|
|
|
|
return (
|
|
<>
|
|
<ResponsiveTableContainer minWidth={600}>
|
|
<Table striped highlightOnHover withColumnBorders withTableBorder>
|
|
<Table.Thead>
|
|
<Table.Tr>
|
|
<Table.Th>
|
|
Name
|
|
</Table.Th>
|
|
<Table.Th>
|
|
Gender
|
|
</Table.Th>
|
|
<Table.Th>
|
|
Date of Birth
|
|
</Table.Th>
|
|
<Table.Th>
|
|
Email
|
|
</Table.Th>
|
|
<Table.Th>
|
|
Phone
|
|
</Table.Th>
|
|
<Table.Th>
|
|
Operations
|
|
</Table.Th>
|
|
</Table.Tr>
|
|
</Table.Thead>
|
|
<Table.Tbody>{rows}</Table.Tbody>
|
|
</Table>
|
|
</ResponsiveTableContainer>
|
|
</>
|
|
)
|
|
}
|