diff --git a/docs/work-programmes/master-progress.md b/docs/work-programmes/master-progress.md index 8863c9e..e0581a4 100644 --- a/docs/work-programmes/master-progress.md +++ b/docs/work-programmes/master-progress.md @@ -38,6 +38,7 @@ Updated: 2026-08-29 - Localized the Admin System runtime and CV benchmark diagnostics, including probe/email failures, model/Ollama state, parser findings, benchmark summaries, fallback guidance, and locale-aware timestamps while leaving runtime/provider/file values untouched. - Consolidated active list/dashboard/Kanban/reminder loading and failure presentation through the shared resource notice. Retry/progress accessibility follows EN/NB, duplicate fallback text is suppressed, and Kanban drag/keyboard announcements are localized. - Migrated Admin Audit to the shared retryable resource state so an unavailable API is no longer misreported as an empty log. Audit actions/dates, notification-setting feedback, profile-load fallbacks, and CV extraction run notifications now follow EN/NB while stored event/profile content remains unchanged. +- Localized the pre-authentication security-check label and the embedded Turnstile challenge language, so switching to Bokmål no longer leaves the verification widget in English. ### In progress @@ -99,6 +100,7 @@ Updated: 2026-08-29 - Post-fix complete regression: backend 716/716 and frontend 62 suites with 268/268 tests passed; the optimized Next production build and integrated TypeScript check passed. - Shared view-state focused verification: 3 suites and 15/15 tests passed, including Bokmål loading/retry and Kanban interaction coverage; TypeScript passed. - Admin Audit/settings/profile focused verification: 3 suites and 17/17 tests passed, including unavailable-versus-empty recovery and Bokmål audit actions that preserve stored event content; TypeScript passed. +- Turnstile/auth recovery focused verification: 4 suites and 18/18 tests passed; the provider widget receives the Bokmål language code and TypeScript passed. - Backend matcher/intelligence focused verification: 35/35 passed, including detection of a manually created Norwegian advert with no saved translation. - Full backend: 712/712 tests passed on .NET 9. - Next optimized production build and TypeScript: passed after the Job Workspace/checklist batch. diff --git a/job-tracker-ui/src/components/TurnstileWidget.tsx b/job-tracker-ui/src/components/TurnstileWidget.tsx index 2f74360..bc091de 100644 --- a/job-tracker-ui/src/components/TurnstileWidget.tsx +++ b/job-tracker-ui/src/components/TurnstileWidget.tsx @@ -1,9 +1,11 @@ import { useEffect, useRef } from "react"; import { Box, Typography } from "@mui/material"; +import { useI18n } from "../i18n/I18nProvider"; type Props = { siteKey: string; action: "login" | "register"; onToken: (token: string) => void; }; export default function TurnstileWidget({ siteKey, action, onToken }: Props) { + const { language, t } = useI18n(); const ref = useRef(null); useEffect(() => { let widgetId: string | undefined; @@ -11,7 +13,7 @@ export default function TurnstileWidget({ siteKey, action, onToken }: Props) { const render = () => { if (cancelled || !ref.current || !(window as any).turnstile) return; widgetId = (window as any).turnstile.render(ref.current, { - sitekey: siteKey, action, theme: "auto", size: "flexible", + sitekey: siteKey, action, theme: "auto", size: "flexible", language: language === "nb" ? "nb" : "en", callback: onToken, "expired-callback": () => onToken(""), "error-callback": () => onToken(""), @@ -28,7 +30,7 @@ export default function TurnstileWidget({ siteKey, action, onToken }: Props) { document.head.appendChild(script); } return () => { cancelled = true; if (widgetId && (window as any).turnstile) (window as any).turnstile.remove(widgetId); }; - }, [action, onToken, siteKey]); + }, [action, language, onToken, siteKey]); - return Security check; + return {t("securityCheck")}; } diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index 1bcec60..2b6f34e 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -903,6 +903,7 @@ export const translations = { proFeatureView: "View Pro", proFeatureDismiss: "Dismiss {title}", registrationUnavailable: "Registration is currently unavailable.", + securityCheck: "Security check", dashboardTotalCount: "{count} total", jobTableSelectJob: "Select {title}", characterCount: "{count} characters", @@ -3215,6 +3216,7 @@ export const translations = { proFeatureView: "Se Pro", proFeatureDismiss: "Lukk {title}", registrationUnavailable: "Registrering er ikke tilgjengelig for øyeblikket.", + securityCheck: "Sikkerhetskontroll", dashboardTotalCount: "{count} totalt", jobTableSelectJob: "Velg {title}", characterCount: "{count} tegn", diff --git a/job-tracker-ui/src/turnstile-widget.test.tsx b/job-tracker-ui/src/turnstile-widget.test.tsx new file mode 100644 index 0000000..bfb4f4d --- /dev/null +++ b/job-tracker-ui/src/turnstile-widget.test.tsx @@ -0,0 +1,28 @@ +import React from "react"; +import "@testing-library/jest-dom"; +import { render, screen } from "@testing-library/react"; + +import TurnstileWidget from "./components/TurnstileWidget"; +import { I18nProvider } from "./i18n/I18nProvider"; + +beforeEach(() => { + window.localStorage.clear(); + (window as any).turnstile = { render: jest.fn(() => "widget-1"), remove: jest.fn() }; +}); + +afterEach(() => { + delete (window as any).turnstile; +}); + +test("renders the security check and provider widget in Bokmål", () => { + window.localStorage.setItem("uiLanguage", "nb-NO"); + + render(); + + expect(screen.getByText("Sikkerhetskontroll")).toBeInTheDocument(); + expect((window as any).turnstile.render).toHaveBeenCalledWith(expect.any(HTMLElement), expect.objectContaining({ + sitekey: "site-key", + action: "login", + language: "nb", + })); +});