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
}
onClick={handleEditInfo}
>
Edit Profile
}
onClick={() => setChangePasswordOpened(true)}
>
Change Password
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
}
variant="subtle"
onClick={() => changePage(DashboardPageType.PatientBooking)}
>
View All
{loadingBookings ? (
Loading...
) : bookings.length > 0 ? (
ID
Category
Appointment Time
Description
Status
Team
Feedback
{bookingRows}
) : (
No appointments found.
)}
Recent Treatments
}
variant="subtle"
onClick={() => changePage(DashboardPageType.TreatmentRecord, `/dashboard/treatment_record/${patientData.id}`)}
>
View All
{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
}
onClick={handleEditInfo}
>
Edit Profile
}
onClick={() => setChangePasswordOpened(true)}
>
Change Password
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
}
variant="subtle"
onClick={() => changePage(DashboardPageType.DoctorTreatment)}
>
View All
{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.
)}
);
}