mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
feat(settings): turn Appearance into a hub of per-module subpages
Appearance ("Darstellung") stacked every module's view and ordering
settings on one long page. Split it into a hub that lists the modules as
navigable rows, each opening its own /settings/layout/<module> subpage
with a back link — the per-module settings move verbatim, no restyling.
The module routes now redirect to those subpages instead of an in-page
anchor, and the in-app cogs link straight to them.
This commit is contained in:
1 parent
fa2e2c6ba2
commit
a01c9d0435
20 files changed
+451
-205
No files matched your search
@@ -570,7 +570,9 @@ export default function InsightsPage() {
|
||||
description={t("insights.editMode.emptyDescription")}
|
||||
action={
|
||||
<Button size="sm" asChild>
|
||||
<Link href="/settings/insights">{t("insights.customize")}</Link>
|
||||
<Link href="/settings/layout/insights">
|
||||
{t("insights.customize")}
|
||||
</Link>
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function LabsPage() {
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
>
|
||||
<Link
|
||||
href="/settings/labs"
|
||||
href="/settings/layout/labs"
|
||||
aria-label={t("labs.customize")}
|
||||
title={t("labs.customize")}
|
||||
>
|
||||
|
||||
@@ -123,12 +123,14 @@ describe("/medications — view renders from the persisted preference", () => {
|
||||
expect(html).not.toContain('data-slot="medications-view-table"');
|
||||
});
|
||||
|
||||
it("links the header customize glyph to /settings/medications", () => {
|
||||
it("links the header customize glyph to /settings/layout/medications", () => {
|
||||
// v1.16.11 — same entry-point logic as the dashboard and insights
|
||||
// headers: one wrench ghost icon linking to the page's settings
|
||||
// section (view preference + manual order live there).
|
||||
// v1.25.11 (#148) — the per-module settings now live on the Appearance
|
||||
// subpage `/settings/layout/medications`.
|
||||
const html = renderPage({ version: 1, view: "cards", order: [] });
|
||||
expect(html).toContain('href="/settings/medications"');
|
||||
expect(html).toContain('href="/settings/layout/medications"');
|
||||
expect(html).toContain('aria-label="Customize medications"');
|
||||
// The in-page order dialog is gone — no dialog trigger remains.
|
||||
expect(html).not.toContain('aria-label="Adjust order"');
|
||||
|
||||
@@ -370,7 +370,7 @@ export default function MedicationsPage() {
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
>
|
||||
<Link
|
||||
href="/settings/medications"
|
||||
href="/settings/layout/medications"
|
||||
aria-label={t("medications.customize")}
|
||||
title={t("medications.customize")}
|
||||
>
|
||||
|
||||
@@ -90,7 +90,7 @@ export default function MoodPage() {
|
||||
className="min-h-11 min-w-11 sm:min-h-9 sm:min-w-9"
|
||||
>
|
||||
<Link
|
||||
href="/settings/mood"
|
||||
href="/settings/layout/mood"
|
||||
aria-label={t("mood.customize")}
|
||||
title={t("mood.customize")}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
import { LayoutModuleGate } from "@/components/settings/layout-module-gate";
|
||||
import {
|
||||
LAYOUT_GROUPS,
|
||||
LAYOUT_GROUP_IDS,
|
||||
isLayoutGroupId,
|
||||
} from "@/components/settings/layout-groups";
|
||||
import { SettingsHubBackLink } from "@/components/settings/settings-hub-back-link";
|
||||
import { SettingsShell } from "@/components/settings/settings-shell";
|
||||
import { resolveServerLocale } from "@/lib/i18n/server-locale";
|
||||
import { getServerTranslator } from "@/lib/i18n/server-translator";
|
||||
|
||||
/**
|
||||
* v1.25.11 (#148) — per-module Appearance subpage.
|
||||
*
|
||||
* `/settings/layout/<module>` renders ONLY that module's section (the existing
|
||||
* `*Section` component, verbatim) under the Appearance hub, with a
|
||||
* "← Appearance" back-link. The hub at `/settings/layout` lists the modules and
|
||||
* links here; the legacy `/settings/<module>` routes 301-redirect here
|
||||
* (next.config.ts).
|
||||
*
|
||||
* Each subpage is pre-rendered for the known module ids via
|
||||
* `generateStaticParams()`; `dynamicParams = false` 404s any other segment at
|
||||
* routing time (re-checked with `notFound()` for defence-in-depth). The slug
|
||||
* list is imported from the SERVER-safe `layout-groups` module so this server
|
||||
* component never pulls runtime values across a client boundary.
|
||||
*/
|
||||
|
||||
export const dynamicParams = false;
|
||||
|
||||
export function generateStaticParams() {
|
||||
return LAYOUT_GROUP_IDS.map((module) => ({ module }));
|
||||
}
|
||||
|
||||
interface PageProps {
|
||||
// Next.js 16 made route `params` an async Promise. We `await` it before use.
|
||||
params: Promise<{ module: string }>;
|
||||
}
|
||||
|
||||
export default async function SettingsLayoutModulePage({ params }: PageProps) {
|
||||
const { module } = await params;
|
||||
|
||||
if (!isLayoutGroupId(module)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const group = LAYOUT_GROUPS.find((entry) => entry.id === module);
|
||||
if (!group) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
// Heading lives in the shell (its own grid row spanning only the content
|
||||
// column). The title is the MODULE's title, resolved server-side from the
|
||||
// cookie locale (mirroring the onboarding pages) so the shell receives a
|
||||
// ready string — no client re-translation, so server HTML and the hydration
|
||||
// render carry the identical heading text.
|
||||
const locale = await resolveServerLocale();
|
||||
const { t } = getServerTranslator(locale);
|
||||
const Body = group.Body;
|
||||
const headingId = `settings-section-${module}-title`;
|
||||
|
||||
return (
|
||||
<SettingsShell
|
||||
active="layout"
|
||||
heading={{
|
||||
title: t(group.titleKey),
|
||||
subtitle: t(group.descriptionKey),
|
||||
headingId,
|
||||
topSlot: (
|
||||
<SettingsHubBackLink
|
||||
href="/settings/layout"
|
||||
labelKey="settings.sections.layout.backToHub"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
>
|
||||
<section aria-labelledby={headingId} className="space-y-6">
|
||||
<LayoutModuleGate moduleGate={group.moduleGate}>
|
||||
<Body />
|
||||
</LayoutModuleGate>
|
||||
</section>
|
||||
</SettingsShell>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user