import { useTranslation } from "react-i18next"; import { MedicationAvatar } from "../components"; import { useAuth } from "../components/Auth"; import { useAppContext } from "../context"; import type { Coverage } from "../types"; import { expandDoseIds } from "../utils/schedule"; // Helper for user-specific localStorage keys function userStorageKey(userId: number | undefined, key: string): string { return userId ? `user_${userId}_${key}` : key; } // Helper function to get stock status based on thresholds function getStockStatus( daysLeft: number | null, medsLeft: number, settings: { lowStockDays: number; normalStockDays: number; highStockDays: number; reminderDaysBefore: number } ) { // Out of stock or completely depleted = danger (red) if (medsLeft <= 0 || daysLeft === 0) return { className: "danger", label: "status.outOfStock" }; // No schedule, but has stock = normal if (daysLeft === null) return { className: "success", label: "status.noSchedule" }; // Critical: at or below reminder threshold = danger (red) if (daysLeft <= settings.reminderDaysBefore) return { className: "danger", label: "status.criticalStock" }; // Low: below low stock threshold = warning (yellow) if (daysLeft < settings.lowStockDays) return { className: "warning", label: "status.lowStock" }; // High stock if (daysLeft >= settings.highStockDays) return { className: "high", label: "status.highStock" }; // Normal stock return { className: "success", label: "status.normal" }; } // Helper function to get worst stock status for a day function getDayStockStatus( dayMeds: Array<{ medName: string }>, coverageByMed: Record, settings: { lowStockDays: number; normalStockDays: number; highStockDays: number; reminderDaysBefore: number } ): string { let worstLevel = 3; // 3=success, 2=warning, 1=danger for (const item of dayMeds) { const cov = coverageByMed[item.medName]; if (!cov) continue; const status = getStockStatus(cov.daysLeft, cov.medsLeft, settings); if (status.className === "danger") worstLevel = Math.min(worstLevel, 1); else if (status.className === "warning") worstLevel = Math.min(worstLevel, 2); } return worstLevel === 1 ? "danger" : worstLevel === 2 ? "warning" : "success"; } // Helper to get dose ID (with or without person) function getDoseId(baseId: string, person: string | null): string { return person ? `${baseId}-${person}` : baseId; } export function SchedulePage() { const { t } = useTranslation(); const { user } = useAuth(); const { meds, settings, scheduleDays, setScheduleDays, showPastDays, setShowPastDays, pastDays, futureDays, takenDoses, markDoseTaken, undoDoseTaken, coverageByMed, depletionByMed, manuallyExpandedDays, toggleDayCollapse, openUserFilter, missedPastDoseIds, } = useAppContext(); return (

{t("dashboard.schedules.title")}

{/* Past days toggle */} {pastDays.length > 0 && (() => { // Use context's missedPastDoseIds which handles dismissed doses and previous schedule detection const missedCount = missedPastDoseIds.length; return (
0 ? "has-missed" : ""}`} onClick={() => setShowPastDays(!showPastDays)} > {showPastDays ? "▼" : "▶"} {showPastDays ? t("dashboard.schedules.hidePastDays") : t("dashboard.schedules.showPastDays")} ({t("dashboard.schedules.pastDaysCount", { count: pastDays.length })}) {missedCount > 0 && ( ⚠️ {missedCount} )}
); })()} {/* Past days (when expanded) */} {showPastDays && pastDays.map((day) => { const allDoseIds = day.meds.flatMap((item) => expandDoseIds(item.doses)); const allDayTaken = allDoseIds.length > 0 && allDoseIds.every((id) => takenDoses.has(id)); const takenCount = allDoseIds.filter((id) => takenDoses.has(id)).length; const isManuallyExpanded = manuallyExpandedDays.has(day.dateStr); const isCollapsed = !isManuallyExpanded; const worstStatus = getDayStockStatus(day.meds, coverageByMed, settings); return (
toggleDayCollapse(day.dateStr, true)} title={isCollapsed ? t("common.expand") : t("common.collapse")} > {isCollapsed ? "▶" : "▼"} {day.dateStr} {allDayTaken ? ( ✓ {t("dashboard.schedules.allTaken")} ) : ( <> ⚠️ {takenCount}/{allDoseIds.length} )}
{!isCollapsed && day.meds.map((item) => { const med = meds.find((m) => m.name === item.medName); const medCov = coverageByMed[item.medName]; const isEmpty = medCov ? medCov.medsLeft <= 0 : false; const itemDoseIds = expandDoseIds(item.doses); const allTaken = itemDoseIds.every((id) => takenDoses.has(id)); return (
{item.medName} {med?.intakeRemindersEnabled && ( 🔔 )}
{item.total} {t("common.pills")} {t("common.total")}
{item.doses.map((dose) => { // If no takenBy, show single checkbox; otherwise show one per person const people = (dose.takenBy || []).length > 0 ? dose.takenBy : [null]; return (
{dose.timeStr} {dose.usage} {dose.usage !== 1 ? t("common.pills") : t("common.pill")} {med?.pillWeightMg && ` (${dose.usage * med.pillWeightMg} ${med.doseUnit ?? "mg"})`}
{people.map((person) => { const doseId = getDoseId(dose.id, person); const isTaken = takenDoses.has(doseId); return (
{person && ( openUserFilter(person)} > {person} )} {isTaken ? ( ) : ( )}
); })}
); })}
); })}
); })} {/* Current and future days */} {futureDays.map((day) => { const today = new Date(); today.setHours(0, 0, 0, 0); const dayDate = new Date(day.date); dayDate.setHours(0, 0, 0, 0); const isToday = dayDate.getTime() === today.getTime(); return (
{day.dateStr}
{day.meds.map((item) => { const medCoverage = coverageByMed[item.medName]; const isEmpty = medCoverage ? medCoverage.medsLeft <= 0 : false; const med = meds.find((m) => m.name === item.medName); const depletionTime = depletionByMed[item.medName]; // Check if this dose is scheduled after medication runs out const willBeOutOfStock = typeof depletionTime === "number" && item.lastWhen > depletionTime; const status = willBeOutOfStock ? { className: "danger", label: "status.outOfStock" } : medCoverage ? getStockStatus(medCoverage.daysLeft, medCoverage.medsLeft, settings) : null; const itemDoseIds = expandDoseIds(item.doses); const allTaken = itemDoseIds.every((id) => takenDoses.has(id)); return (
{item.medName} {med?.intakeRemindersEnabled && ( 🔔 )}
{item.total} {t("common.pills")} {t("common.total")} {status && {t(status.label)}}
{item.doses.map((dose) => { const people = (dose.takenBy || []).length > 0 ? dose.takenBy : [null]; const now = Date.now(); const dayStart = new Date(day.date).setHours(0, 0, 0, 0); const isPastDay = dayStart < new Date().setHours(0, 0, 0, 0); return (
{dose.timeStr} {dose.usage} {dose.usage !== 1 ? t("common.pills") : t("common.pill")} {med?.pillWeightMg && ` (${dose.usage * med.pillWeightMg} ${med.doseUnit ?? "mg"})`}
{people.map((person) => { const doseId = getDoseId(dose.id, person); const isTaken = takenDoses.has(doseId); const isOverdue = !isTaken && dose.when < now && !isPastDay; return (
{person && ( openUserFilter(person)}> {person} )} {isTaken ? ( ) : ( )}
); })}
); })}
); })}
); })}
); }