import type {Route} from "../../../.react-router/types/app/pages/dashboard/+types/Home"; import {useEffect, useState} from "react"; import {useNavigate, useOutletContext} from "react-router"; import { type DoctorDataWithPermission, type DoctorTeamInfo, type OutletContextType, type PatientBookingInfo, type PatientData, type TreatmentInfoWithDoctorInfo, type WardInfo } from "~/utils/models.ts"; import {Badge, Button, Card, Grid, Group, Modal, PasswordInput, Stack, Table, Text} from "@mantine/core"; import { apiChangeMyPassword, apiGetPatientBookingList, apiGetTreatmentRecords, apiPatientGetCurrentWard } from "~/utils/hms_api.ts"; import {showErrorMessage, showInfoMessage} from "~/utils/utils.ts"; import {iconMStyle, marginLeftRight, marginRound, marginTop} from "~/styles.ts"; import {useForm} from "@mantine/form"; import {BookingCategory, DashboardPageType, DoctorGrade, UserType} from "~/utils/hms_enums.ts"; import {confirmEditDoctor, confirmEditPatient} from "~/components/subs/confirms.tsx"; import EditIcon from "mdi-react/EditIcon"; import LockResetIcon from "mdi-react/LockResetIcon"; import ArrowRightIcon from "mdi-react/ArrowRightIcon"; import {TruncatedText} from "~/components/subs/TruncatedText.tsx"; import {PatientInfoDisplay} from "~/components/subs/PatientInfoDisplay.tsx"; import {TeamInfoDisplay} from "~/components/subs/TeamInfoDisplay.tsx"; import {DoctorInfoDisplay} from "~/components/subs/DoctorInfoDisplay.tsx"; import DoctorTeamsSimple from "~/components/subs/DoctorTeamsSimple.tsx"; import { ResponsiveTableContainer } from "~/components/subs/ResponsiveTableContainer"; export function meta({}: Route.MetaArgs) { return [ { title: "Home" }, { name: "description", content: "Dashboard Home" }, ]; } interface ChangePasswordModalProps { opened: boolean; onClose: () => void; onSuccess: () => void; } function ChangePasswordModal({ opened, onClose, onSuccess }: ChangePasswordModalProps) { const form = useForm({ initialValues: { old_password: '', new_password: '', confirm_password: '', }, validate: { old_password: (value) => (!value ? 'Current password is required' : null), new_password: (value) => (!value ? 'New password is required' : value.length < 6 ? 'Password must be at least 6 characters' : null), confirm_password: (value, values) => (value !== values.new_password ? 'Passwords do not match' : null), }, }); const handleSubmit = (values: typeof form.values) => { apiChangeMyPassword(values.old_password, values.new_password) .then(res => { if (res.success) { showInfoMessage('', 'Password changed successfully', 3000); form.reset(); onClose(); onSuccess(); } else { showErrorMessage(res.message, 'Failed to change password'); } }) .catch(err => { showErrorMessage(err.toString(), 'Failed to change password'); }); }; return (
); } interface PatientHomeProps { patientData: PatientData; refreshUserInfo: () => void; } function PatientHome({ patientData, refreshUserInfo }: PatientHomeProps) { const navigate = useNavigate(); const [changePasswordOpened, setChangePasswordOpened] = useState(false); const [currentWard, setCurrentWard] = useState(null); const [loadingWard, setLoadingWard] = useState(false); const [bookings, setBookings] = useState([]); const [loadingBookings, setLoadingBookings] = useState(false); const [treatments, setTreatments] = useState([]); const [loadingTreatments, setLoadingTreatments] = useState(false); const { changePage } = useOutletContext(); useEffect(() => { fetchCurrentWard(); fetchRecentBookings(); fetchRecentTreatments(); }, []); const fetchCurrentWard = () => { setLoadingWard(true); apiPatientGetCurrentWard() .then(res => { if (res.success && res.data.ward) { setCurrentWard(res.data.ward); } else { setCurrentWard(null); } }) .catch(err => { showErrorMessage(err.toString(), 'Error'); }) .finally(() => { setLoadingWard(false); }); }; const fetchRecentBookings = () => { setLoadingBookings(true); apiGetPatientBookingList(1) .then(res => { if (res.success) { const recentBookings = [...res.data.appointments] .sort((a, b) => b.id - a.id) .slice(0, 5); setBookings(recentBookings); } else { showErrorMessage(res.message, 'Failed to load appointments'); } }) .catch(err => { showErrorMessage(err.toString(), 'Error'); }) .finally(() => { setLoadingBookings(false); }); }; const fetchRecentTreatments = () => { setLoadingTreatments(true); apiGetTreatmentRecords(1, patientData.id, null, null) .then(res => { if (res.success) { // Get only the 10 most recent treatments const recentTreatments = [...res.data.treatments] .sort((a, b) => b.treated_at - a.treated_at) .slice(0, 10); setTreatments(recentTreatments); } else { showErrorMessage(res.message, 'Failed to load treatments'); } }) .catch(err => { showErrorMessage(err.toString(), 'Error'); }) .finally(() => { setLoadingTreatments(false); }); }; const handleEditInfo = () => { confirmEditPatient(patientData, () => { refreshUserInfo(); }); }; const getStatusBadge = (booking: PatientBookingInfo) => { if (booking.discharged) { return Discharged; } else if (booking.admitted) { return Admitted; } else if (booking.approved) { if (booking.assigned_team === null) { return Rejected; } else { return Approved; } } else { return Pending; } }; const getCategoryDisplay = (category: string) => { const key = Object.entries(BookingCategory).find(([_, value]) => value === category)?.[0]; return key ? key.replace(/_/g, ' ') : category; }; const bookingRows = bookings.map((booking) => ( {booking.id} {getCategoryDisplay(booking.category)} {new Date(booking.appointment_time * 1000).toLocaleString('en-GB').replace(',', '')} {getStatusBadge(booking)} {booking.assigned_team ? ( ) : ( Not Assigned )} {booking.feedback ? ( ) : ( - )} )); const treatmentRows = treatments.map((treatment) => ( {treatment.id} {treatment.team ? ( ) : ( Not Assigned )} {new Date(treatment.treated_at * 1000).toLocaleString('en-GB').replace(',', '')} )); return ( Hi, {patientData.title} {patientData.name} Welcome to the Hospital Management System Full Name: {patientData.title} {patientData.name} Gender: {patientData.gender} Birth Date: {patientData.birth_date} Email: {patientData.email} Phone: {patientData.phone} Address: {patientData.address}, {patientData.postcode} Current Ward {loadingWard ? ( Loading... ) : currentWard ? ( Ward ID: {currentWard.id} Ward Name: {currentWard.name} Ward Type: {currentWard.type.replace(/_/g, ' ')} Occupancy: {currentWard.current_occupancy} / {currentWard.total_capacity} ) : ( You are not currently admitted to any ward. )} Recent Appointments {loadingBookings ? ( Loading... ) : bookings.length > 0 ? ( ID Category Appointment Time Description Status Team Feedback {bookingRows}
) : ( No appointments found. )}
Recent Treatments {loadingTreatments ? ( Loading... ) : treatments.length > 0 ? ( ID Doctor Medical Team Treatment Info Treatment Date {treatmentRows}
) : ( No treatment records found. )}
setChangePasswordOpened(false)} onSuccess={() => {navigate("/dashboard/account/login")}} />
); } interface DoctorHomeProps { doctorData: DoctorDataWithPermission; doctorTeams: DoctorTeamInfo[]; refreshUserInfo: () => void; } function DoctorHome({ doctorData, doctorTeams, refreshUserInfo }: DoctorHomeProps) { const [changePasswordOpened, setChangePasswordOpened] = useState(false); const [assignedPatients, setAssignedPatients] = useState([]); const [loadingPatients, setLoadingPatients] = useState(false); const { changePage } = useOutletContext(); const navigate = useNavigate(); useEffect(() => { fetchAssignedPatients(); }, []); const fetchAssignedPatients = () => { setLoadingPatients(true); apiGetTreatmentRecords(1, null, doctorData.id, null) .then(res => { if (res.success) { const uniquePatientIds = new Set(); const patientTreatments = res.data.treatments .filter(treatment => { if (!uniquePatientIds.has(treatment.patient_id)) { uniquePatientIds.add(treatment.patient_id); return true; } return false; }) .slice(0, 15); // Get only the first 15 unique patients setAssignedPatients(patientTreatments); } else { showErrorMessage(res.message, 'Failed to load patient assignments'); } }) .catch(err => { showErrorMessage(err.toString(), 'Error'); }) .finally(() => { setLoadingPatients(false); }); }; const handleEditInfo = () => { confirmEditDoctor(doctorData, () => { refreshUserInfo(); }); }; const patientRows = assignedPatients.map((treatment) => ( {new Date(treatment.treated_at * 1000).toLocaleString('en-GB').replace(',', '')} )); return ( Hi, {doctorData.title} {doctorData.name} Welcome to the Hospital Management System Full Name: {doctorData.title} {doctorData.name} Grade: {DoctorGrade[doctorData.grade]} Gender: {doctorData.gender} Birth Date: {doctorData.birth_date} Email: {doctorData.email} Phone: {doctorData.phone} Recent Patients {loadingPatients ? ( Loading... ) : assignedPatients.length > 0 ? ( Patient Last Treatment Treatment Date {patientRows}
) : ( No patients assigned yet. )}
setChangePasswordOpened(false)} onSuccess={() => {navigate("/dashboard/account/login")}} />
); } export default function Component() { const { loginUserInfo, refreshMyInfo } = useOutletContext(); if (!loginUserInfo) { return ( Dashboard Loading user information... ); } const isDoctor = loginUserInfo.user_type === UserType.DOCTOR; return ( {isDoctor && loginUserInfo.doctor_data ? ( ) : loginUserInfo.patient_data ? ( ) : ( Unable to load user profile information. )} ); }