mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
fix(capture): guard the bottom-nav Log sheet against silent discard
CapturePicker mounted the same measurement/mood/medication-intake forms the dashboard quick-entry sheets do, but closed unconditionally on a backdrop tap, Escape, or mobile swipe-down — the dashboard's sheetBodyHasUnsavedInput() confirm guard never made it into this newer picker. Reuse it here too, so a half-filled form from the primary mobile capture path asks before dropping typed input instead of losing it.
This commit is contained in:
1 parent
20f0d13eba
commit
a66a346d0d
1 file changed
+67
-3
@@ -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<CaptureKind | null>(null);
|
||||
const [footerEl, setFooterEl] = useState<HTMLDivElement | null>(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. */}
|
||||
<ResponsiveSheet
|
||||
open={kind !== null}
|
||||
onOpenChange={(next) => {
|
||||
if (!next) closeForm();
|
||||
}}
|
||||
onOpenChange={handleFormOpenChange}
|
||||
title={formTitle}
|
||||
footer={<div ref={setFooterEl} className="flex w-full" />}
|
||||
>
|
||||
@@ -155,6 +186,39 @@ export function CapturePicker({ open, onOpenChange }: CapturePickerProps) {
|
||||
/>
|
||||
)}
|
||||
</ResponsiveSheet>
|
||||
|
||||
{/* 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. */}
|
||||
<AlertDialog
|
||||
open={confirmDiscardOpen}
|
||||
onOpenChange={setConfirmDiscardOpen}
|
||||
>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>
|
||||
{t("dashboard.quickEntryDiscard.title")}
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("dashboard.quickEntryDiscard.description")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>
|
||||
{t("dashboard.quickEntryDiscard.cancel")}
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => {
|
||||
setConfirmDiscardOpen(false);
|
||||
closeForm();
|
||||
}}
|
||||
>
|
||||
{t("dashboard.quickEntryDiscard.confirm")}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user