mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 09:42:15 +03:00
fix(settings): use catalog-only modality labels
This commit is contained in:
1
changelog.d/features/9782-modality-bridge-settings.md
Normal file
1
changelog.d/features/9782-modality-bridge-settings.md
Normal 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))
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user