/** * MobileEditModal - Full-screen edit form for medications (mobile-optimized) * Handles new medication creation and editing existing medications */ import { Minus, Plus, Trash2 } from "lucide-react"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import type { DoseUnit, FieldErrors, FormBlister, FormIntake, FormState, Medication } from "../types"; import { DOSE_UNITS } from "../types"; import { deriveTotal } from "../utils"; import { DateInput } from "./DateInput"; // Field limits for validation const FIELD_LIMITS = { name: { max: 100 }, genericName: { max: 100 }, takenBy: { max: 50 }, notes: { max: 1000 }, }; export interface MobileEditModalProps { show: boolean; editingId: number | null; form: FormState; onFormChange: (form: FormState) => void; fieldErrors: FieldErrors; saving: boolean; formSaved: boolean; formChanged: boolean; hasValidationErrors: boolean; dateConsistencyError: string | null; readOnlyMode: boolean; // TakenBy tag input takenByInput: string; onTakenByInputChange: (value: string) => void; existingPeople: string[]; onAddTakenByPerson: (person: string) => void; onRemoveTakenByPerson: (person: string) => void; onTakenByKeyDown: (e: React.KeyboardEvent) => void; // Blister helpers (legacy) onSetBlisterValue: (idx: number, field: keyof FormBlister, value: string) => void; onAddBlister: () => void; onRemoveBlister: (idx: number) => void; // Intake helpers (new - with per-intake takenBy) onSetIntakeValue: (idx: number, field: keyof FormIntake, value: string | boolean) => void; onAddIntake: (takenBy?: string) => void; onRemoveIntake: (idx: number) => void; // Value change handler for numeric fields onHandleValueChange: (field: K, value: FormState[K]) => void; // Image handling meds: Medication[]; onUploadMedImage: (medId: number, file: File) => Promise; onDeleteMedImage: (medId: number) => Promise; // Actions onClose: () => void; onResetForm: () => void; onSaveMedication: (e: React.FormEvent) => void; } /** Calculate total pills from form state */ function deriveTotalFromForm(form: FormState) { if (form.packageType === "bottle") { // For bottle type, looseTablets is the current stock return Number(form.looseTablets) || 0; } const packCount = Number(form.packCount) || 0; const blistersPerPack = Number(form.blistersPerPack) || 0; const pillsPerBlister = Number(form.pillsPerBlister) || 1; return deriveTotal(packCount, blistersPerPack, pillsPerBlister, 0); } export function MobileEditModal({ show, editingId, form, onFormChange, fieldErrors, saving, formSaved, formChanged, hasValidationErrors, dateConsistencyError, readOnlyMode, takenByInput, onTakenByInputChange, existingPeople, onAddTakenByPerson, onRemoveTakenByPerson, onTakenByKeyDown, onSetBlisterValue, onAddBlister, onRemoveBlister, onSetIntakeValue, onAddIntake, onRemoveIntake, onHandleValueChange, meds, onUploadMedImage, onDeleteMedImage, onClose, _onResetForm, onSaveMedication, }: MobileEditModalProps) { const { t } = useTranslation(); const [activeTab, setActiveTab] = useState<"general" | "stock" | "prescription" | "schedule">("general"); // Reset tab when modal opens useEffect(() => { if (show) setActiveTab("general"); }, [show]); // Close on Escape key useEffect(() => { if (!show) return; function handleKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { onClose(); } } document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [show, onClose]); // Lock background scroll while modal is open. useEffect(() => { if (!show) return; const previousOverflow = document.body.style.overflow; document.body.style.overflow = "hidden"; return () => { document.body.style.overflow = previousOverflow; }; }, [show]); if (!show) return null; const currentMed = editingId ? meds.find((m) => m.id === editingId) : null; return (
{ if (e.key === "Escape") onClose(); }} >
e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} >

{(() => { const editLabel = readOnlyMode ? t("form.viewEntry") : t("form.editEntry"); return editingId ? editLabel : t("form.newEntry"); })()}

{ // Check native HTML5 validation first const formElement = e.currentTarget; if (!formElement.checkValidity()) { // Let browser show native validation messages formElement.reportValidity(); e.preventDefault(); return; } onSaveMedication(e); }} >

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

{editingId && (

{t("form.medicationImage")}

{currentMed?.imageUrl ? (
{currentMed.name}
) : ( e.target.files?.[0] && onUploadMedImage(editingId, e.target.files[0])} /> )}
)}

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

{form.packageType === "blister" ? ( <> ) : ( <> )} {form.packageType === "bottle" && (

{t("form.total")}: {deriveTotalFromForm(form)}{" "} {deriveTotalFromForm(form) === 1 ? t("common.pill") : t("common.pills")}

)}