From e66feceb76d45078da91227e01750349bee2c59d Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Sat, 8 Aug 2026 19:56:06 -0300 Subject: [PATCH] fix(settings): use catalog-only modality labels --- .../features/9782-modality-bridge-settings.md | 1 + .../settings/modality-bridge/page.tsx | 14 ++++----- tests/unit/ui/modality-bridge-page.test.tsx | 31 ++++++++++++++++++- 3 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 changelog.d/features/9782-modality-bridge-settings.md diff --git a/changelog.d/features/9782-modality-bridge-settings.md b/changelog.d/features/9782-modality-bridge-settings.md new file mode 100644 index 0000000000..47ab7c56e6 --- /dev/null +++ b/changelog.d/features/9782-modality-bridge-settings.md @@ -0,0 +1 @@ +- **feat(settings):** add a dedicated Modality Bridge settings page with Vision controls, runtime stats, and URL-addressable Audio and Video tabs ([#9782](https://github.com/diegosouzapw/OmniRoute/pull/9782)) diff --git a/src/app/(dashboard)/dashboard/settings/modality-bridge/page.tsx b/src/app/(dashboard)/dashboard/settings/modality-bridge/page.tsx index 6d29768751..fcb3b6df5a 100644 --- a/src/app/(dashboard)/dashboard/settings/modality-bridge/page.tsx +++ b/src/app/(dashboard)/dashboard/settings/modality-bridge/page.tsx @@ -9,10 +9,10 @@ import ModalityBridgeVisionTab from "@/app/(dashboard)/dashboard/settings/compon type TabId = "vision" | "audio" | "video"; -const TABS: ReadonlyArray<{ id: TabId; labelKey: string; fallback: string }> = [ - { id: "vision", labelKey: "modalityBridgeVisionTab", fallback: "Vision" }, - { id: "audio", labelKey: "modalityBridgeAudioTab", fallback: "Audio" }, - { id: "video", labelKey: "modalityBridgeVideoTab", fallback: "Video" }, +const TABS: ReadonlyArray<{ id: TabId; labelKey: string }> = [ + { id: "vision", labelKey: "modalityBridgeVisionTab" }, + { id: "audio", labelKey: "modalityBridgeAudioTab" }, + { id: "video", labelKey: "modalityBridgeVideoTab" }, ]; function ModalityBridgePageContent() { @@ -20,8 +20,6 @@ function ModalityBridgePageContent() { const searchParams = useSearchParams(); const router = useRouter(); const pathname = usePathname(); - const translateOrFallback = (key: string, fallback: string) => - typeof t.has === "function" && !t.has(key) ? fallback : t(key); const activeTab = useMemo(() => { const requested = searchParams.get("tab") as TabId | null; @@ -40,7 +38,7 @@ function ModalityBridgePageContent() {
{TABS.map((tab) => ( ))}
diff --git a/tests/unit/ui/modality-bridge-page.test.tsx b/tests/unit/ui/modality-bridge-page.test.tsx index 2a4f04f255..74d4a4f4f4 100644 --- a/tests/unit/ui/modality-bridge-page.test.tsx +++ b/tests/unit/ui/modality-bridge-page.test.tsx @@ -17,6 +17,11 @@ const navigation = vi.hoisted(() => ({ redirect: vi.fn(), })); +const messages = vi.hoisted(() => ({ + has: true, + values: {} as Record, +})); + vi.mock("next/navigation", () => ({ useRouter: () => ({ replace: navigation.replace }), useSearchParams: () => new URLSearchParams(navigation.search), @@ -25,7 +30,8 @@ vi.mock("next/navigation", () => ({ })); vi.mock("next-intl", () => ({ - useTranslations: () => Object.assign((key: string) => key, { has: () => true }), + useTranslations: () => + Object.assign((key: string) => messages.values[key] ?? key, { has: () => messages.has }), })); vi.mock( @@ -50,6 +56,8 @@ describe("Modality Bridge settings page", () => { navigation.search = ""; navigation.replace.mockReset(); navigation.redirect.mockReset(); + messages.has = true; + messages.values = {}; }); afterEach(() => { @@ -75,6 +83,27 @@ describe("Modality Bridge settings page", () => { expect(el.querySelector('[data-testid="vision-tab"]')).toBeTruthy(); }); + it("uses catalog translations without hardcoded English fallbacks", () => { + messages.has = false; + messages.values = { + modalityBridgeIntro: "Introducción localizada", + modalityBridgeVisionTab: "Visión localizada", + modalityBridgeAudioTab: "Audio localizado", + modalityBridgeVideoTab: "Vídeo localizado", + modalityBridgeSubTabsAria: "Secciones localizadas", + }; + + const el = renderPage(); + expect(Array.from(el.querySelectorAll('[role="tab"]')).map((tab) => tab.textContent)).toEqual([ + "Visión localizada", + "Audio localizado", + "Vídeo localizado", + ]); + expect(el.querySelector('[role="tablist"]')?.getAttribute("aria-label")).toBe( + "Secciones localizadas" + ); + }); + it("updates the URL without scrolling when an operator changes tabs", () => { navigation.search = "tab=vision&source=sidebar"; const el = renderPage();