fix(settings): use catalog-only modality labels

This commit is contained in:
diegosouzapw
2026-08-08 19:56:06 -03:00
parent 0298b82442
commit e66feceb76
3 changed files with 37 additions and 9 deletions

View File

@@ -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))

View File

@@ -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<TabId>(() => {
const requested = searchParams.get("tab") as TabId | null;
@@ -40,7 +38,7 @@ function ModalityBridgePageContent() {
<div
className="flex gap-1 overflow-x-auto border-b border-border"
role="tablist"
aria-label={translateOrFallback("modalityBridgeSubTabsAria", "Modality Bridge sections")}
aria-label={t("modalityBridgeSubTabsAria")}
>
{TABS.map((tab) => (
<button
@@ -56,7 +54,7 @@ function ModalityBridgePageContent() {
: "border-transparent text-text-muted hover:text-text"
}`}
>
{translateOrFallback(tab.labelKey, tab.fallback)}
{t(tab.labelKey)}
</button>
))}
</div>

View File

@@ -17,6 +17,11 @@ const navigation = vi.hoisted(() => ({
redirect: vi.fn(),
}));
const messages = vi.hoisted(() => ({
has: true,
values: {} as Record<string, string>,
}));
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();