diff --git a/src/components/admin/admin-shell.tsx b/src/components/admin/admin-shell.tsx index f0bd0209c..39f94bb37 100644 --- a/src/components/admin/admin-shell.tsx +++ b/src/components/admin/admin-shell.tsx @@ -316,7 +316,7 @@ export function AdminShell({ active, children }: AdminShellProps) { aria-label={t("admin.shell.sectionsNav")} className="hidden md:col-start-1 md:row-start-2 md:block" > -
+
{deleteMsg && ( diff --git a/src/components/settings/injection-sites-card.tsx b/src/components/settings/injection-sites-card.tsx index 3c1faf124..78cfb3a24 100644 --- a/src/components/settings/injection-sites-card.tsx +++ b/src/components/settings/injection-sites-card.tsx @@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Syringe } from "lucide-react"; +import { SettingsCard } from "@/components/settings/settings-card"; import { SettingsCardHeader } from "@/components/settings/_card-header"; import { Checkbox } from "@/components/ui/checkbox"; import { useTranslations } from "@/lib/i18n/context"; @@ -66,7 +67,7 @@ export function InjectionSitesCard({ } return ( -
+ ))}
-
+ ); } diff --git a/src/components/settings/layout-section.tsx b/src/components/settings/layout-section.tsx index e5ad4528c..7605fcdc4 100644 --- a/src/components/settings/layout-section.tsx +++ b/src/components/settings/layout-section.tsx @@ -8,8 +8,8 @@ import { type LucideIcon, } from "lucide-react"; -import { cn } from "@/lib/utils"; import { useTranslations } from "@/lib/i18n/context"; +import { SettingsCard } from "./settings-card"; /** * v1.17.1 (F-2) — the one "Layout & Personalization" home. @@ -53,19 +53,18 @@ const LAYOUT_LINKS: ReadonlyArray = [ export function LayoutSection() { const { t } = useTranslations(); - // v1.18.6 (W9) — the visible heading + subtitle now come from the shared - // `` in the route; this body is the hub link list. + // The visible heading + subtitle are painted by `SettingsShell` from the + // section slug; this body is the hub link list. return (
    {LAYOUT_LINKS.map((link) => { const Icon = link.icon; return (
  • -
  • ); })} diff --git a/src/components/settings/settings-section-frame.tsx b/src/components/settings/settings-section-frame.tsx deleted file mode 100644 index 98b9eee63..000000000 --- a/src/components/settings/settings-section-frame.tsx +++ /dev/null @@ -1,113 +0,0 @@ -"use client"; - -/** - * `` — the canonical page-frame for every Settings - * section. The settings twin of the admin renderer's `SectionFrame` - * (`src/app/admin/[section]/renderer.tsx`): it emits the one visible - * heading + subheading every page must show, in the reference style used by - * the Admin Konsole → Übersicht landing. - * - * Before this primitive every settings section hand-rolled its own - * `sr-only` `

    `, so no settings page showed a visible heading and the - * left-menu-to-first-card alignment drifted page to page. Routing all 19 - * sections through one frame fixes both: a constant-height heading block - * above the first card, and a real visible `h1 + subtitle`. - * - * - * {cards} - * - * - * Title comes from `settings.sections..title`; subtitle from - * `settings.sections..subtitle`. The `

    ` keeps the historic - * `settings-section--title` id so the section's own `aria-labelledby` - * linkage — and the spotlight-tour anchors that reference it — stay intact. - * - * `topSlot` renders ABOVE the heading (used by the Layout-hub child - * editors for their "← back to hub" link, which must sit above the title). - * `headingAccessory` renders inline at the top-right of the heading row - * (used by Gesundheitsakte for its guided-tour re-entry trigger). - */ - -import * as React from "react"; - -import { useTranslations } from "@/lib/i18n/context"; -import type { SettingsSectionSlug } from "./section-slugs"; - -interface SettingsSectionFrameBaseProps { - /** Optional content rendered above the heading (e.g. a back-to-hub link). */ - topSlot?: React.ReactNode; - /** Optional content rendered inline at the top-right of the heading row - * (e.g. a guided-tour re-entry trigger). */ - headingAccessory?: React.ReactNode; - children: React.ReactNode; -} - -/** - * Slug mode (the 19 shell sections): title + subtitle come from - * `settings.sections..{title,subtitle}` and the `

    ` keeps the - * historic `settings-section--title` id for `aria-labelledby` and - * spotlight-tour anchors. - */ -interface SettingsSectionFrameSlugProps extends SettingsSectionFrameBaseProps { - slug: SettingsSectionSlug; -} - -/** - * Explicit mode (the per-module settings pages — Vorsorge / Illness / Labs — - * which deliberately stay out of the slug registry because their static - * routes win over `/settings/[section]`): caller passes resolved strings and - * an optional heading id. This lets `ModuleSettingsFrame` delegate here so - * every settings page shares one heading frame. - */ -interface SettingsSectionFrameExplicitProps extends SettingsSectionFrameBaseProps { - title: string; - subtitle: string; - /** Heading element id (defaults to a stable slug-less id). */ - titleId?: string; -} - -export type SettingsSectionFrameProps = - | SettingsSectionFrameSlugProps - | SettingsSectionFrameExplicitProps; - -function isSlugProps( - props: SettingsSectionFrameProps, -): props is SettingsSectionFrameSlugProps { - return "slug" in props; -} - -export function SettingsSectionFrame(props: SettingsSectionFrameProps) { - const { t } = useTranslations(); - const { topSlot, headingAccessory, children } = props; - - let title: string; - let subtitle: string; - let titleId: string; - if (isSlugProps(props)) { - titleId = `settings-section-${props.slug}-title`; - title = t(`settings.sections.${props.slug}.title`); - subtitle = t(`settings.sections.${props.slug}.subtitle`); - } else { - titleId = props.titleId ?? "settings-section-title"; - title = props.title; - subtitle = props.subtitle; - } - - return ( -
    - {topSlot ?
    {topSlot}
    : null} -
    -
    -

    - {title} -

    -

    {subtitle}

    -
    - {headingAccessory ? ( -
    {headingAccessory}
    - ) : null} -
    - {children} -
    - ); -} diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index d9397a3ac..e091cfc9c 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -502,7 +502,7 @@ export function SettingsShell({ aria-label={t("settings.shell.sectionsNav")} className="hidden md:col-start-1 md:row-start-2 md:block" > -
    +
      {visibleSections.map((section) => { const isActive = section.slug === activeSlug;