diff --git a/src/components/layout/capture-picker.tsx b/src/components/layout/capture-picker.tsx index 71b68fa82..7060b20be 100644 --- a/src/components/layout/capture-picker.tsx +++ b/src/components/layout/capture-picker.tsx @@ -7,6 +7,17 @@ import { MeasurementForm } from "@/components/measurements/measurement-form"; import { MoodForm } from "@/components/mood/mood-form"; import { MedicationIntakeQuickAdd } from "@/components/dashboard/medication-intake-quick-add"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { sheetBodyHasUnsavedInput } from "@/components/dashboard/quick-entry-sheets"; import { useTranslations } from "@/lib/i18n/context"; /** @@ -26,6 +37,12 @@ import { useTranslations } from "@/lib/i18n/context"; * Nothing here orphans a route: the same surfaces remain reachable * from their own pages (`/measurements`, `/mood`, dashboard) and from * the More hub. The picker is an additional fast path, not a removal. + * + * v1.30.1 — the form sheet shares the dashboard quick-entry sheets' + * confirm-before-discard guard (`sheetBodyHasUnsavedInput()`), so a + * swipe-down / backdrop tap / Escape on a half-filled form from the + * primary mobile capture path asks before dropping typed input, + * instead of closing unconditionally. */ type CaptureKind = "measurement" | "medication" | "mood"; @@ -41,6 +58,10 @@ export function CapturePicker({ open, onOpenChange }: CapturePickerProps) { const { t } = useTranslations(); const [kind, setKind] = useState(null); const [footerEl, setFooterEl] = useState(null); + // v1.30.1 — mirrors `QuickEntrySheets`' `confirmDiscardOpen`: hold a + // dismiss attempt here instead of closing outright when the form + // body carries unsaved input. + const [confirmDiscardOpen, setConfirmDiscardOpen] = useState(false); function chooseKind(next: CaptureKind) { // Close the chooser first, then open the form sheet so only one @@ -53,6 +74,18 @@ export function CapturePicker({ open, onOpenChange }: CapturePickerProps) { setKind(null); } + // Intercept a form-sheet dismiss: close immediately when the form is + // clean, otherwise keep the sheet open and ask before discarding — + // same contract as `QuickEntrySheets.handleQuickEntryOpenChange`. + function handleFormOpenChange(next: boolean) { + if (next) return; + if (sheetBodyHasUnsavedInput()) { + setConfirmDiscardOpen(true); + return; + } + closeForm(); + } + const options: ReadonlyArray<{ kind: CaptureKind; label: string; @@ -127,9 +160,7 @@ export function CapturePicker({ open, onOpenChange }: CapturePickerProps) { {/* The chosen capture surface, reusing the existing form. */} { - if (!next) closeForm(); - }} + onOpenChange={handleFormOpenChange} title={formTitle} footer={
} > @@ -155,6 +186,39 @@ export function CapturePicker({ open, onOpenChange }: CapturePickerProps) { /> )} + + {/* v1.30.1 — confirm before discarding a partly-filled capture + form when the sheet is dismissed by an overlay tap, Escape, + or a mobile swipe-down. Copy shared with the dashboard + quick-entry sheets' identical guard. */} + + + + + {t("dashboard.quickEntryDiscard.title")} + + + {t("dashboard.quickEntryDiscard.description")} + + + + + {t("dashboard.quickEntryDiscard.cancel")} + + { + setConfirmDiscardOpen(false); + closeForm(); + }} + > + {t("dashboard.quickEntryDiscard.confirm")} + + + + ); }