/** * MedDetailModal - Medication detail view with nested modals * Displays medication information, stock, schedules, and provides refill/edit functionality * * Can work in two modes: * 1. Context mode: Uses useAppContext() for all state (when no props provided) * 2. Props mode: Accepts all required data as props (for gradual adoption) */ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Lightbox, MedicationAvatar } from "../components"; import type { Coverage, Medication, RefillEntry, StockThresholds } from "../types"; import { getMedTotal, getPackageSize } from "../types"; import { formatNumber, generateICS, getExpiryClass, getSystemLocale } from "../utils"; import { getStockStatus } from "../utils/schedule"; // ============================================================================= // Local Helper Functions // ============================================================================= /** * Calculate blister stock - divides current pills into full blisters and partial */ function getBlisterStock( currentPills: number, pillsPerBlister: number, _originalLooseTablets: number, _originalTotalPills: number ): { fullBlisters: number; openBlisterPills: number; loosePills: number } { if (pillsPerBlister <= 0 || pillsPerBlister === 1) { return { fullBlisters: 0, openBlisterPills: 0, loosePills: currentPills }; } const fullBlisters = Math.floor(currentPills / pillsPerBlister); const openBlisterPills = currentPills % pillsPerBlister; return { fullBlisters, openBlisterPills, loosePills: 0 }; } /** * Format full blisters column */ function formatFullBlisters(fullBlisters: number, t: (key: string) => string): string { if (fullBlisters === 0) return "—"; return `${fullBlisters} ${fullBlisters === 1 ? t("common.blister") : t("common.blisters")}`; } /** * Format open blister column */ function formatOpenBlisterAndLoose( openBlisterPills: number, _loosePills: number, pillsPerBlister: number, t: (key: string) => string ): string { if (openBlisterPills > 0) { return `${openBlisterPills} ${t("common.of")} ${pillsPerBlister} ${t("common.pills")}`; } return "—"; } // ============================================================================= // Props Interface // ============================================================================= export interface MedDetailModalProps { // Required selectedMed: Medication | null; coverage: { all: Coverage[] }; settings: StockThresholds; // Modal state showImageLightbox: boolean; showRefillModal: boolean; showEditStockModal: boolean; // Modal actions onClose: () => void; onOpenImageLightbox: () => void; onCloseImageLightbox: () => void; onOpenRefillModal: () => void; onCloseRefillModal: () => void; onOpenEditStockModal: () => void; onCloseEditStockModal: () => void; // Refill state refillPacks: number; onRefillPacksChange: (value: number) => void; refillLoose: number; onRefillLooseChange: (value: number) => void; usePrescriptionRefill: boolean; onUsePrescriptionRefillChange: (value: boolean) => void; refillSaving: boolean; refillHistory: RefillEntry[]; refillHistoryExpanded: boolean; onRefillHistoryExpandedChange: (value: boolean) => void; onSubmitRefill: (medId: number, usePrescription?: boolean) => Promise; // Edit stock state editStockFullBlisters: number; onEditStockFullBlistersChange: (value: number) => void; editStockPartialBlisterPills: number; onEditStockPartialBlisterPillsChange: (value: number) => void; editStockSaving: boolean; onSubmitStockCorrection: (medId: number) => Promise; } export function MedDetailModal({ selectedMed, coverage, settings, showImageLightbox, showRefillModal, showEditStockModal, onClose, onOpenImageLightbox, onCloseImageLightbox, onOpenRefillModal, onCloseRefillModal, onOpenEditStockModal, onCloseEditStockModal, refillPacks, onRefillPacksChange, refillLoose, onRefillLooseChange, usePrescriptionRefill, onUsePrescriptionRefillChange, refillSaving, refillHistory, refillHistoryExpanded, onRefillHistoryExpandedChange, onSubmitRefill, editStockFullBlisters, onEditStockFullBlistersChange, editStockPartialBlisterPills, onEditStockPartialBlisterPillsChange, editStockSaving, onSubmitStockCorrection, }: MedDetailModalProps) { const { t, i18n } = useTranslation(); const [editStockFullInput, setEditStockFullInput] = useState("0"); const [editStockPartialInput, setEditStockPartialInput] = useState("0"); const parseStockInput = (value: string): number => { const parsed = Number.parseInt(value, 10); return Number.isNaN(parsed) ? 0 : parsed; }; useEffect(() => { if (showEditStockModal) { setEditStockFullInput(String(editStockFullBlisters)); setEditStockPartialInput(String(editStockPartialBlisterPills)); } }, [showEditStockModal, editStockFullBlisters, editStockPartialBlisterPills]); if (!selectedMed) return null; const medCoverage = coverage.all.find((c) => c.name === selectedMed.name); const packageSize = getPackageSize(selectedMed); const currentStock = medCoverage ? Math.round(medCoverage.medsLeft) : getMedTotal(selectedMed); const status = medCoverage ? getStockStatus(medCoverage.daysLeft, medCoverage.medsLeft, settings) : null; const textClass = status?.className === "danger" ? "danger-text" : status?.className === "warning" ? "warning-text" : "success-text"; const stock = getBlisterStock(currentStock, selectedMed.pillsPerBlister, selectedMed.looseTablets, packageSize); const fullForBounds = Math.max(0, parseStockInput(editStockFullInput)); return (
e.stopPropagation()}>
{/* Header */}
selectedMed.imageUrl && onOpenImageLightbox()} > {selectedMed.imageUrl && 🔍}

{selectedMed.name}

{selectedMed.genericName && {selectedMed.genericName}} {selectedMed.takenBy && (selectedMed.takenBy || []).length > 0 && ( {t("modal.for")} {selectedMed.takenBy.join(", ")} )}
{/* Stock Info Section */}

{t("modal.stockInfo")}

{selectedMed.packageType === "blister" && ( <>
{t("table.fullBlisters")} {formatFullBlisters(stock.fullBlisters, t)}
{t("table.openBlister")} {formatOpenBlisterAndLoose( stock.openBlisterPills, stock.loosePills, selectedMed.pillsPerBlister ?? 1, t )}
)}
{t("modal.currentStock")} {currentStock} /{" "} {selectedMed.packageType === "bottle" ? (selectedMed.totalPills ?? packageSize) : packageSize} {currentStock > (selectedMed.packageType === "bottle" ? (selectedMed.totalPills ?? packageSize) : packageSize) && ( {" "} ⚠️ )}
{/* Package Details Section */}

{t("modal.packageDetails")} ( {selectedMed.packageType === "bottle" ? t("form.packageTypeBottle") : t("form.packageTypeBlister")})

{selectedMed.packageType === "blister" ? ( <>
{t("modal.packs")} {selectedMed.packCount}
{t("modal.blistersPerPack")} {selectedMed.blistersPerPack}
{t("modal.pillsPerBlister")} {selectedMed.pillsPerBlister}
) : (
{t("form.totalCapacity")} {(selectedMed.totalPills ?? packageSize) || "—"}
)} {selectedMed.pillWeightMg && (
{t("modal.pillWeight")} {selectedMed.pillWeightMg} {selectedMed.doseUnit ?? "mg"}
)} {selectedMed.expiryDate && (
{t("modal.expiryDate")} {new Date(selectedMed.expiryDate).toLocaleDateString(getSystemLocale(i18n.language), { day: "2-digit", month: "short", year: "numeric", })}
)}
{/* Prescription Details Section */} {selectedMed.prescriptionEnabled && (

{t("form.sections.prescription")}

{t("prescription.authorizedRefills")} {selectedMed.prescriptionAuthorizedRefills ?? "—"}
{t("prescription.remainingRefills")} {selectedMed.prescriptionRemainingRefills ?? "—"}
{t("prescription.lowThreshold")} {selectedMed.prescriptionLowRefillThreshold ?? "—"}
{t("prescription.expiryDate")} {selectedMed.prescriptionExpiryDate ? new Date(selectedMed.prescriptionExpiryDate).toLocaleDateString( getSystemLocale(i18n.language), { day: "2-digit", month: "short", year: "numeric", } ) : "—"}
)} {/* Intake Schedule Section */} {selectedMed.blisters.length > 0 && (

{t("modal.intakeSchedule")}{" "} {selectedMed.intakeRemindersEnabled && ( 🔔 )}

{selectedMed.blisters.map((blister, idx) => { // When using new intakes format with per-intake takenBy, // each intake already represents one person's dose — don't multiply. // For legacy intakes (no per-intake takenBy), multiply by personCount. const intake = selectedMed.intakes?.[idx]; const hasPerIntakeTakenBy = !!intake?.takenBy; const personCount = hasPerIntakeTakenBy ? 1 : Math.max(1, selectedMed.takenBy?.length || 1); const totalUsage = blister.usage * personCount; return (
{totalUsage} {totalUsage !== 1 ? t("common.pills") : t("common.pill")} {selectedMed.pillWeightMg && ` (${totalUsage * selectedMed.pillWeightMg} ${selectedMed.doseUnit ?? "mg"})`} {t("form.blisters.every")} {blister.every}{" "} {blister.every !== 1 ? t("common.days") : t("common.day")} {t("modal.at")}{" "} {new Date(blister.start).toLocaleTimeString(getSystemLocale(i18n.language), { hour: "2-digit", minute: "2-digit", })}
); })}
)} {/* Coverage Status Section */} {medCoverage && status && (

{t("modal.coverageStatus")} {t(status.label)}

{t("modal.daysLeft")} {medCoverage.daysLeft !== null ? formatNumber(medCoverage.daysLeft) : "—"}
{t("modal.runsOut")} {medCoverage.depletionDate ?? "—"}
)} {/* Notes Section */} {selectedMed.notes && (

📝 {t("modal.notes")}

{selectedMed.notes}
)} {/* Refill History Section */} {refillHistory.length > 0 && (

onRefillHistoryExpandedChange(!refillHistoryExpanded)} > {t("refill.history")} ({refillHistory.length}) {refillHistoryExpanded ? "▼" : "▶"}

{refillHistoryExpanded && (
{refillHistory.map((entry) => (
{new Date(entry.refillDate).toLocaleDateString(getSystemLocale(i18n.language), { day: "2-digit", month: "short", year: "numeric", })} ,{" "} {new Date(entry.refillDate).toLocaleTimeString(getSystemLocale(i18n.language), { hour: "2-digit", minute: "2-digit", })} {(() => { const total = selectedMed.packageType === "bottle" ? entry.loosePillsAdded : entry.packsAdded * selectedMed.blistersPerPack * selectedMed.pillsPerBlister + entry.loosePillsAdded; return `+${total} ${total === 1 ? t("common.pill") : t("common.pills")}`; })()} {entry.usedPrescription && ( {" "} 📋 )}
))}
)}
)}
{/* Footer */}
{selectedMed.blisters.length > 0 && ( )}
{/* Image Lightbox */} {showImageLightbox && selectedMed.imageUrl && ( )} {/* Refill Modal */} {showRefillModal && (
{ e.stopPropagation(); onCloseRefillModal(); }} >
e.stopPropagation()}>

{t("refill.title")}

{selectedMed.name}

{selectedMed.packageType === "blister" ? ( <> ) : ( )} {selectedMed.prescriptionEnabled && (
{t("prescription.remainingRefills")}: {Number(selectedMed.prescriptionRemainingRefills) || 0}
)}
{(() => { const totalRefill = selectedMed.packageType === "blister" ? refillPacks * selectedMed.blistersPerPack * selectedMed.pillsPerBlister + refillLoose : refillLoose; return totalRefill > 0 ? ( +{totalRefill} {totalRefill === 1 ? t("common.pill") : t("common.pills")} ) : null; })()}
)} {/* Edit Stock Modal */} {showEditStockModal && (
{ e.stopPropagation(); onCloseEditStockModal(); }} >
e.stopPropagation()}>

{t("editStock.title")}

{selectedMed.name}

{t("editStock.hint")}

{(() => { const dbTotal = getMedTotal(selectedMed); const currentTotal = medCoverage ? Math.round(medCoverage.medsLeft) : dbTotal; const isBottle = selectedMed.packageType === "bottle"; const newTotal = isBottle ? editStockPartialBlisterPills : editStockFullBlisters * selectedMed.pillsPerBlister + editStockPartialBlisterPills; const difference = newTotal - currentTotal; return ( <>
{isBottle ? ( ) : ( <> )}
{t("editStock.currentTotal")}: {currentTotal} {currentTotal === 1 ? t("common.pill") : t("common.pills")}
{t("editStock.newTotal")}: {newTotal} {newTotal === 1 ? t("common.pill") : t("common.pills")}
0 ? "positive" : difference < 0 ? "negative" : ""}`} > {t("editStock.difference")}: {difference > 0 ? "+" : ""} {difference} {Math.abs(difference) === 1 ? t("common.pill") : t("common.pills")}
); })()}
)}
); }