diff --git a/src/components/dashboard/quick-entry-forms.lazy.tsx b/src/components/dashboard/quick-entry-forms.lazy.tsx new file mode 100644 index 000000000..1a4ed22e7 --- /dev/null +++ b/src/components/dashboard/quick-entry-forms.lazy.tsx @@ -0,0 +1,68 @@ +"use client"; + +/** + * The quick-entry forms, loaded when a sheet first opens rather than with the + * page. The dashboard and the bottom bar's capture picker are on nearly every + * authenticated paint, and both used to import all five forms eagerly (each + * with react-hook-form, zod, its pickers and its own queries) for sheets most + * visits never open. + * + * Client-only (`ssr: false`): every sheet is closed on the server render. The + * options are inline object literals because the bundler requires them so. + * The placeholder holds the sheet body's height while the chunk arrives so the + * sheet does not grow under the finger. Same `next/dynamic` arrangement as + * the Coach drawer mount. + */ +import dynamic from "next/dynamic"; + +import { Skeleton } from "@/components/ui/skeleton"; + +function SheetBodySkeleton() { + return ( +
+ + + +
+ ); +} + +export const MeasurementForm = dynamic( + () => + import("@/components/measurements/measurement-form").then((m) => ({ + default: m.MeasurementForm, + })), + { ssr: false, loading: SheetBodySkeleton }, +); + +export const MoodForm = dynamic( + () => + import("@/components/mood/mood-form").then((m) => ({ + default: m.MoodForm, + })), + { ssr: false, loading: SheetBodySkeleton }, +); + +export const MedicationIntakeQuickAdd = dynamic( + () => + import("@/components/dashboard/medication-intake-quick-add").then((m) => ({ + default: m.MedicationIntakeQuickAdd, + })), + { ssr: false, loading: SheetBodySkeleton }, +); + +export const ManualWorkoutForm = dynamic( + () => + import("@/components/workouts/manual-workout-form").then((m) => ({ + default: m.ManualWorkoutForm, + })), + { ssr: false, loading: SheetBodySkeleton }, +); + +export const SymptomEntryForm = dynamic( + () => + import("@/components/symptoms/symptom-entry-form").then((m) => ({ + default: m.SymptomEntryForm, + })), + { ssr: false, loading: SheetBodySkeleton }, +); diff --git a/src/components/dashboard/quick-entry-sheets.tsx b/src/components/dashboard/quick-entry-sheets.tsx index aca64f4ff..3eaf1e12d 100644 --- a/src/components/dashboard/quick-entry-sheets.tsx +++ b/src/components/dashboard/quick-entry-sheets.tsx @@ -11,6 +11,13 @@ */ import { useState } from "react"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; +import { + ManualWorkoutForm, + MeasurementForm, + MedicationIntakeQuickAdd, + MoodForm, + SymptomEntryForm, +} from "@/components/dashboard/quick-entry-forms.lazy"; import { AlertDialog, AlertDialogAction, @@ -21,11 +28,6 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { MeasurementForm } from "@/components/measurements/measurement-form"; -import { MoodForm } from "@/components/mood/mood-form"; -import { MedicationIntakeQuickAdd } from "@/components/dashboard/medication-intake-quick-add"; -import { ManualWorkoutForm } from "@/components/workouts/manual-workout-form"; -import { SymptomEntryForm } from "@/components/symptoms/symptom-entry-form"; import { useTranslations } from "@/lib/i18n/context"; import { useRecordCapabilities, diff --git a/src/components/layout/__tests__/capture-picker-discard.test.tsx b/src/components/layout/__tests__/capture-picker-discard.test.tsx index a62bfc7b4..f62052fe8 100644 --- a/src/components/layout/__tests__/capture-picker-discard.test.tsx +++ b/src/components/layout/__tests__/capture-picker-discard.test.tsx @@ -82,14 +82,12 @@ vi.mock("@/components/ui/alert-dialog", () => ({ AlertDialogTitle: markedComponent("AlertDialogTitle"), })); -vi.mock("@/components/measurements/measurement-form", () => ({ +vi.mock("@/components/dashboard/quick-entry-forms.lazy", () => ({ MeasurementForm: markedComponent("MeasurementForm"), -})); -vi.mock("@/components/mood/mood-form", () => ({ MoodForm: markedComponent("MoodForm"), -})); -vi.mock("@/components/dashboard/medication-intake-quick-add", () => ({ MedicationIntakeQuickAdd: markedComponent("MedicationIntakeQuickAdd"), + ManualWorkoutForm: markedComponent("ManualWorkoutForm"), + SymptomEntryForm: markedComponent("SymptomEntryForm"), })); import { CapturePicker } from "../capture-picker"; diff --git a/src/components/layout/__tests__/capture-picker.test.tsx b/src/components/layout/__tests__/capture-picker.test.tsx index 20d5e86af..694f57ec1 100644 --- a/src/components/layout/__tests__/capture-picker.test.tsx +++ b/src/components/layout/__tests__/capture-picker.test.tsx @@ -35,16 +35,12 @@ vi.mock("@/components/ui/responsive-sheet", () => ({ }) => (open ?
{children}
: null), })); -vi.mock("@/components/measurements/measurement-form", () => ({ +vi.mock("@/components/dashboard/quick-entry-forms.lazy", () => ({ MeasurementForm: () =>
, -})); - -vi.mock("@/components/mood/mood-form", () => ({ MoodForm: () =>
, -})); - -vi.mock("@/components/dashboard/medication-intake-quick-add", () => ({ MedicationIntakeQuickAdd: () =>
, + ManualWorkoutForm: () =>
, + SymptomEntryForm: () =>
, })); import { CapturePicker } from "../capture-picker"; diff --git a/src/components/layout/capture-picker.tsx b/src/components/layout/capture-picker.tsx index c95c06add..139ead635 100644 --- a/src/components/layout/capture-picker.tsx +++ b/src/components/layout/capture-picker.tsx @@ -3,12 +3,14 @@ import { useState } from "react"; import { Activity, Footprints, Pill, Stethoscope, Waves } from "lucide-react"; -import { MeasurementForm } from "@/components/measurements/measurement-form"; -import { MoodForm } from "@/components/mood/mood-form"; -import { MedicationIntakeQuickAdd } from "@/components/dashboard/medication-intake-quick-add"; -import { ManualWorkoutForm } from "@/components/workouts/manual-workout-form"; -import { SymptomEntryForm } from "@/components/symptoms/symptom-entry-form"; import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; +import { + ManualWorkoutForm, + MeasurementForm, + MedicationIntakeQuickAdd, + MoodForm, + SymptomEntryForm, +} from "@/components/dashboard/quick-entry-forms.lazy"; import { AlertDialog, AlertDialogAction,