From a5126501584253e93c1f7cf429ba95b10370fec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 9 May 2026 23:51:33 +0200 Subject: [PATCH] feat(settings): /settings/export dynamic route wired into sidebar The new `export` slug is now part of `SETTINGS_SECTION_SLUGS`, so the dynamic `/settings/[section]/page.tsx` route picks it up via `generateStaticParams()` and the existing 404-on-unknown guard. The shell sidebar gains a Download-iconed entry between API and Advanced; the placeholder fallback also learned the new icon so a future slug addition can't crash type-checking. The shell-test suite now expects ten sections (was nine) and asserts that `/settings/export` is a real link in the rendered nav. Old "advanced" copy already trimmed in the previous commit, so the export entry is the new home for every "give me my data out" path. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/settings/[section]/page.tsx | 2 ++ .../settings/__tests__/settings-shell.test.tsx | 9 ++++++++- src/components/settings/section-placeholder.tsx | 2 ++ src/components/settings/section-slugs.ts | 1 + src/components/settings/settings-shell.tsx | 6 ++++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/app/settings/[section]/page.tsx b/src/app/settings/[section]/page.tsx index 2e4ab29bd..8f8b6c399 100644 --- a/src/app/settings/[section]/page.tsx +++ b/src/app/settings/[section]/page.tsx @@ -7,6 +7,7 @@ import { AdvancedSection } from "@/components/settings/advanced-section"; import { AiSection } from "@/components/settings/ai-section"; import { ApiSection } from "@/components/settings/api-section"; import { DashboardSection } from "@/components/settings/dashboard-section"; +import { ExportSection } from "@/components/settings/export-section"; import { IntegrationsSection } from "@/components/settings/integrations-section"; import { NotificationsSection } from "@/components/settings/notifications-section"; import { SectionPlaceholder } from "@/components/settings/section-placeholder"; @@ -45,6 +46,7 @@ const SECTION_COMPONENTS: Record< dashboard: DashboardSection, thresholds: ThresholdsSettingsSection, api: ApiSection, + export: ExportSection, advanced: AdvancedSection, }; diff --git a/src/components/settings/__tests__/settings-shell.test.tsx b/src/components/settings/__tests__/settings-shell.test.tsx index eb5d257f3..5cb4c6042 100644 --- a/src/components/settings/__tests__/settings-shell.test.tsx +++ b/src/components/settings/__tests__/settings-shell.test.tsx @@ -35,12 +35,15 @@ function renderShell(props: { } describe("SETTINGS_SECTION_SLUGS", () => { - it("declares exactly the nine sections agreed for v1.4.3", () => { + it("declares the ten sections agreed for v1.4.16", () => { // Order matters — `generateStaticParams()` and the sidebar derive their // ordering from this constant, so a reorder is a behaviour change. // v1.4.3 split the dashboard panel: layout stays under `dashboard`, // per-metric overrides moved to their own `thresholds` slug so it's // a top-level entry in the settings nav. + // v1.4.16 phase B7 added the consolidated `export` section between + // `api` and `advanced` so every "give me my data out" path lives in + // one place. expect([...SETTINGS_SECTION_SLUGS]).toEqual([ "account", "integrations", @@ -49,6 +52,7 @@ describe("SETTINGS_SECTION_SLUGS", () => { "thresholds", "ai", "api", + "export", "advanced", "about", ]); @@ -133,6 +137,9 @@ describe("", () => { // The ampersand is HTML-escaped by React SSR — assert on the encoded // form so we don't accidentally match a parser that double-escapes. expect(html).toContain("API & Tokens"); + // v1.4.16 phase B7: the consolidated Export section is a top-level + // entry in the sidebar; the link must be present in both locales. + expect(html).toContain('href="/settings/export"'); expect(html).toContain("Advanced"); expect(html).toContain("About"); }); diff --git a/src/components/settings/section-placeholder.tsx b/src/components/settings/section-placeholder.tsx index 7e63de08c..fabe9c11e 100644 --- a/src/components/settings/section-placeholder.tsx +++ b/src/components/settings/section-placeholder.tsx @@ -13,6 +13,7 @@ import { Bell, + Download, Info, KeyRound, LayoutDashboard, @@ -36,6 +37,7 @@ const SLUG_ICON: Record = { thresholds: SlidersHorizontal, ai: Sparkles, api: KeyRound, + export: Download, advanced: Settings2, about: Info, }; diff --git a/src/components/settings/section-slugs.ts b/src/components/settings/section-slugs.ts index 5a69cbf0f..0c85e1508 100644 --- a/src/components/settings/section-slugs.ts +++ b/src/components/settings/section-slugs.ts @@ -14,6 +14,7 @@ export const SETTINGS_SECTION_SLUGS = [ "thresholds", "ai", "api", + "export", "advanced", "about", ] as const; diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 6434d543b..59743d3cf 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -19,6 +19,7 @@ import Link from "next/link"; import { usePathname } from "next/navigation"; import { Bell, + Download, Info, KeyRound, LayoutDashboard, @@ -87,6 +88,11 @@ export const SETTINGS_SECTIONS: readonly SettingsSection[] = [ }, { slug: "ai", titleKey: "settings.sections.ai.title", icon: Sparkles }, { slug: "api", titleKey: "settings.sections.api.title", icon: KeyRound }, + { + slug: "export", + titleKey: "settings.sections.export.title", + icon: Download, + }, { slug: "advanced", titleKey: "settings.sections.advanced.title",