import type {Route} from "../../../.react-router/types/app/pages/dashboard/+types/PatientBooking"; import {useDisclosure} from "@mantine/hooks"; import {useEffect, useMemo, useState} from "react"; import {BookingCategory, DashboardPageType} from "~/utils/hms_enums.ts"; import {useNavigate, useOutletContext} from "react-router"; import { type OutletContextType, type PatientBookingInfo, type DoctorTeamInfo, SORT_SYMBOLS } from "~/utils/models.ts"; import {Accordion, ActionIcon, Button, Card, Group, Pagination, Stack, Table, Text, Badge} from "@mantine/core"; import {apiGetPatientBookingList, get_team_info} from "~/utils/hms_api.ts"; import {showErrorMessage, timestampToDate} from "~/utils/utils.ts"; import {iconMStyle, marginLeftRight, marginRightBottom, marginRound, marginTopBottom, textCenter} from "~/styles.ts"; import PencilIcon from "mdi-react/PencilIcon"; import DeleteIcon from "mdi-react/DeleteIcon"; import InfoIcon from "mdi-react/InfoIcon"; import { confirmDeleteBooking, confirmEditOrCreateBooking, confirmViewTeamMembers } from "~/components/subs/confirms.tsx"; import AddIcon from "mdi-react/AddIcon"; import {TruncatedText} from "~/components/subs/TruncatedText.tsx"; import { ResponsiveTableContainer } from "~/components/subs/ResponsiveTableContainer"; export function meta({}: Route.MetaArgs) { return [ { title: "Patient Booking" }, { name: "description", content: "Dashboard Patient Booking" }, ]; } export default function Component() { const navigate = useNavigate(); const [refreshingBookingList, setRefreshingBookingList] = useState(false); const [bookingInfo, setBookingInfo] = useState<{appointments: PatientBookingInfo[], total_pages: number}>({appointments: [], total_pages: 1}); const [teamInfoMap, setTeamInfoMap] = useState>(new Map()); const [loadingTeams, setLoadingTeams] = useState>(new Set()); const [sortKey, setSortKey] = useState(""); const [sortDesc, setSortDesc] = useState(true); const [currPage, setCurrPage] = useState(1); const { loginUserInfo, refreshMyInfo } = useOutletContext(); useEffect(() => { refreshBookingList(); }, []); useEffect(() => { refreshBookingList(); }, [currPage]); const refreshBookingList = () => { setRefreshingBookingList(true); apiGetPatientBookingList(currPage).then(res => { if (!res.success) { showErrorMessage(res.message, "Failed to get appointments list"); return; } setBookingInfo(res.data); // Fetch team info for all appointments with assigned teams const teamsToFetch = res.data.appointments .filter(app => app.assigned_team !== null) .map(app => app.assigned_team as number); // Remove duplicates const uniqueTeams = [...new Set(teamsToFetch)]; // Fetch team info for each unique team ID uniqueTeams.forEach(teamId => { fetchTeamInfo(teamId); }); }) .catch(err => {}) .finally(() => { setRefreshingBookingList(false); }); }; const fetchTeamInfo = (teamId: number) => { if (teamId === null || teamInfoMap.has(teamId) || loadingTeams.has(teamId)) { return; } setLoadingTeams(prev => new Set([...prev, teamId])); get_team_info(teamId).then(res => { if (res.success) { setTeamInfoMap(prev => { const newMap = new Map(prev); newMap.set(teamId, res.data.team); return newMap; }); } }) .catch(err => {}) .finally(() => { setLoadingTeams(prev => { const newSet = new Set(prev); newSet.delete(teamId); return newSet; }); }); }; const handleViewTeam = (teamId: number) => { const teamInfo = teamInfoMap.get(teamId); if (teamInfo) { confirmViewTeamMembers(teamInfo); } else { // If team info is not fetched yet, fetch it and then show get_team_info(teamId).then(res => { if (res.success) { setTeamInfoMap(prev => { const newMap = new Map(prev); newMap.set(teamId, res.data.team); return newMap; }); confirmViewTeamMembers({ ...res.data.team, members: res.data.members }); } else { showErrorMessage(res.message, "Failed to fetch team information"); } }).catch(err => { showErrorMessage("Failed to fetch team information", "Error"); }); } }; const handleSort = (key: string) => { if (sortKey === key) { setSortDesc(!sortDesc); } else { setSortKey(key); setSortDesc(false); // Default ascending } }; const sortedBookings = useMemo(() => { let data = [...bookingInfo.appointments]; if (!sortKey) return data; data.sort((a, b) => { const valA = a[sortKey]; const valB = b[sortKey]; if (typeof valA === 'string' && typeof valB === 'string') { const cmp = valA.localeCompare(valB); return sortDesc ? -cmp : cmp; } if (typeof valA === 'number' && typeof valB === 'number') { return sortDesc ? valB - valA : valA - valB; } if (typeof valA === 'boolean' && typeof valB === 'boolean') { return sortDesc ? (valB ? 1 : -1) : (valA ? 1 : -1); } return 0; }); return data; }, [bookingInfo.appointments, sortKey, sortDesc]); const handleCreateBooking = () => { confirmEditOrCreateBooking(null, refreshBookingList); }; const handleEditBooking = (booking: PatientBookingInfo) => { confirmEditOrCreateBooking(booking, refreshBookingList); }; const handleDeleteBooking = (bookingId: number, category: string) => { confirmDeleteBooking(bookingId, category, refreshBookingList); }; const getCategoryDisplay = (category: string) => { const key = Object.entries(BookingCategory).find(([_, value]) => value === category)?.[0]; return key ? key.replace(/_/g, ' ') : category; }; const getStatusBadge = (appointment: PatientBookingInfo) => { if (appointment.discharged) { return Discharged; } else if (appointment.admitted) { return Admitted; } else if (appointment.approved) { if (appointment.assigned_team === null) { return Rejected; } else { return Approved; } } else { return Pending; } }; const getTeamDisplay = (booking: PatientBookingInfo) => { if (!booking.approved) { return Pending approval; } if (booking.assigned_team === null) { return Not assigned; } const teamInfo = teamInfoMap.get(booking.assigned_team); if (!teamInfo) { return ( Team ID: {booking.assigned_team} handleViewTeam(booking.assigned_team as number)} title="View team details" > ); } return ( {teamInfo.department.replace(/_/g, ' ')} handleViewTeam(booking.assigned_team as number)} title="View team details" > ); }; const rows = sortedBookings.map((booking) => ( {booking.id} {getCategoryDisplay(booking.category)} {new Date(booking.appointment_time * 1000).toLocaleString('en-GB').replace(',', '')} {getStatusBadge(booking)} {booking.feedback ? : '-' } {getTeamDisplay(booking)} {!booking.approved && ( <> handleEditBooking(booking)}> handleDeleteBooking(booking.id, booking.category)}> )} {booking.approved && ( No actions available )} )); return ( My Appointments handleSort("id")}> ID{" "} {sortKey === "id" && SORT_SYMBOLS[sortDesc ? "desc" : "asc"]} handleSort("category")}> Category{" "} {sortKey === "category" && SORT_SYMBOLS[sortDesc ? "desc" : "asc"]} handleSort("appointment_time")}> Appointment Time{" "} {sortKey === "appointment_time" && SORT_SYMBOLS[sortDesc ? "desc" : "asc"]} Description handleSort("approved")}> Status{" "} {sortKey === "approved" && SORT_SYMBOLS[sortDesc ? "desc" : "asc"]} Feedback Assigned Team Actions {rows}
); }