mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
feat(modules): unify Vorsorge, Illness and Labs with the medication pattern
- add a wrench beside each module's add button linking to the module's own settings page; rename every add button to a plain "hinzufügen" - give each module a settings page: view (cards/list) + manual reorder; Labs also gets a sort-order choice (by last reading) and biomarker CRUD/reorder - collapse the Illness "abgeschlossen" list by default - rebuild the Vorsorge card on the medication card's building blocks: a green "Jetzt messen" action, the interval as a chip beside the name, and an in-interval progress signal; drop the kebab "anpassen" and stray actions - persist per-module view/order/sort client-side; add reverse-coverage-clean strings across all six locales
This commit is contained in:
1 parent
ae3091df9e
commit
267dcad13d
22 files changed
+1834
-273
No files matched your search
+22
-41
@@ -1,20 +1,14 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { FlaskConical, Loader2, MoreVertical, Plus } from "lucide-react";
|
||||
import { Loader2, Plus, Wrench } from "lucide-react";
|
||||
|
||||
import { BiomarkerManager } from "@/components/labs/biomarker-manager";
|
||||
import { LabForm } from "@/components/labs/lab-form";
|
||||
import { LabList } from "@/components/labs/lab-list";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { PullToRefreshIndicator } from "@/components/ui/pull-to-refresh-indicator";
|
||||
import { ResponsiveSheet } from "@/components/ui/responsive-sheet";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
@@ -31,7 +25,6 @@ export default function LabsPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const { t } = useTranslations();
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [manageOpen, setManageOpen] = useState(false);
|
||||
// Sticky-footer slot element for the add-result sheet (filled by the
|
||||
// ResponsiveSheet `footer` ref so the form portals its action row there).
|
||||
const [addFooterEl, setAddFooterEl] = useState<HTMLDivElement | null>(null);
|
||||
@@ -42,7 +35,7 @@ export default function LabsPage() {
|
||||
);
|
||||
const pull = usePullToRefresh({
|
||||
onRefresh: refreshVisible,
|
||||
disabled: dialogOpen || manageOpen,
|
||||
disabled: dialogOpen,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@@ -76,34 +69,31 @@ export default function LabsPage() {
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<ModuleTourTrigger stopId="labs" />
|
||||
{/* v1.18.6 (MOD-01) — the wrench is the module's customize entry
|
||||
point, left of the primary Add and linking to the Labs settings
|
||||
page (view, sort order, biomarker CRUD + reorder). Mirrors the
|
||||
medication page header's wrench glyph + slot + 44px tap floor. */}
|
||||
<Button
|
||||
asChild
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
>
|
||||
<Link
|
||||
href="/settings/labs"
|
||||
aria-label={t("labs.customize")}
|
||||
title={t("labs.customize")}
|
||||
>
|
||||
<Wrench className="h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => setDialogOpen(true)}
|
||||
className="min-h-11 sm:min-h-9"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
{t("labs.addResult")}
|
||||
{t("common.add")}
|
||||
</Button>
|
||||
{/* Secondary actions fold into one overflow kebab beside the primary
|
||||
Add, mirroring the medication-card header (one cluster, not a
|
||||
two-button row). */}
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
aria-label={t("common.moreOptions")}
|
||||
>
|
||||
<MoreVertical className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
<DropdownMenuItem onClick={() => setManageOpen(true)}>
|
||||
<FlaskConical className="mr-2 h-4 w-4" />
|
||||
{t("labs.biomarker.manage")}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -126,15 +116,6 @@ export default function LabsPage() {
|
||||
/>
|
||||
</ResponsiveSheet>
|
||||
|
||||
<ResponsiveSheet
|
||||
open={manageOpen}
|
||||
onOpenChange={setManageOpen}
|
||||
title={t("labs.biomarker.manageTitle")}
|
||||
description={t("labs.biomarker.manageSheetDescription")}
|
||||
>
|
||||
<BiomarkerManager />
|
||||
</ResponsiveSheet>
|
||||
|
||||
<LabList onAddFirst={() => setDialogOpen(true)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useMounted } from "@/hooks/use-mounted";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { ModuleSettingsFrame } from "@/components/module-list/module-settings-frame";
|
||||
import { IllnessSettings } from "@/components/illness/illness-settings";
|
||||
|
||||
/**
|
||||
* v1.18.6 (W8 / MOD-03) — the Illness module's own settings page. Dedicated
|
||||
* static route (wins over `/settings/[section]`); reached from the wrench
|
||||
* beside the Illness page's "hinzufügen" button.
|
||||
*/
|
||||
export default function IllnessSettingsPage() {
|
||||
const { isLoading } = useAuth();
|
||||
const mounted = useMounted();
|
||||
const { t } = useTranslations();
|
||||
|
||||
if (!mounted || isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<Loader2 className="text-muted-foreground h-6 w-6 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModuleSettingsFrame
|
||||
title={t("illness.settings.title")}
|
||||
description={t("illness.settings.description")}
|
||||
backHref="/illness"
|
||||
backLabelKey="illness.settings.back"
|
||||
>
|
||||
<IllnessSettings />
|
||||
</ModuleSettingsFrame>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useMounted } from "@/hooks/use-mounted";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { ModuleSettingsFrame } from "@/components/module-list/module-settings-frame";
|
||||
import { LabsSettings } from "@/components/labs/labs-settings";
|
||||
|
||||
/**
|
||||
* v1.18.6 (W8 / MOD-03 + MOD-04) — the Labs module's own settings page.
|
||||
* Dedicated static route (wins over `/settings/[section]`); reached from the
|
||||
* wrench beside the Labs page's "hinzufügen" button.
|
||||
*/
|
||||
export default function LabsSettingsPage() {
|
||||
const { isLoading } = useAuth();
|
||||
const mounted = useMounted();
|
||||
const { t } = useTranslations();
|
||||
|
||||
if (!mounted || isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<Loader2 className="text-muted-foreground h-6 w-6 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModuleSettingsFrame
|
||||
title={t("labs.settings.title")}
|
||||
description={t("labs.settings.description")}
|
||||
backHref="/labs"
|
||||
backLabelKey="labs.settings.back"
|
||||
>
|
||||
<LabsSettings />
|
||||
</ModuleSettingsFrame>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
"use client";
|
||||
|
||||
import { Loader2 } from "lucide-react";
|
||||
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useMounted } from "@/hooks/use-mounted";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
import { ModuleSettingsFrame } from "@/components/module-list/module-settings-frame";
|
||||
import { VorsorgeSettings } from "@/components/measurement-reminders/vorsorge-settings";
|
||||
|
||||
/**
|
||||
* v1.18.6 (W8 / MOD-03) — the Vorsorge module's own settings page.
|
||||
*
|
||||
* A dedicated static route that wins over the dynamic `/settings/[section]`
|
||||
* router (static segments take precedence in Next.js), so it adds a
|
||||
* per-module settings surface without touching the parallel-phase-owned
|
||||
* `section-slugs` registry or `settings-shell`. Reached from the wrench
|
||||
* beside the Vorsorge page's "hinzufügen" button.
|
||||
*/
|
||||
export default function VorsorgeSettingsPage() {
|
||||
const { isLoading } = useAuth();
|
||||
const mounted = useMounted();
|
||||
const { t } = useTranslations();
|
||||
|
||||
if (!mounted || isLoading) {
|
||||
return (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
<Loader2 className="text-muted-foreground h-6 w-6 animate-spin motion-reduce:animate-none" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ModuleSettingsFrame
|
||||
title={t("measurementReminders.settings.title")}
|
||||
description={t("measurementReminders.settings.description")}
|
||||
backHref="/vorsorge"
|
||||
backLabelKey="measurementReminders.settings.back"
|
||||
>
|
||||
<VorsorgeSettings />
|
||||
</ModuleSettingsFrame>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user