From 2c0337950453970b9c13d6d49d8b8486e76f2a64 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Mon, 23 Mar 2026 21:05:49 +0100 Subject: [PATCH] Localize kanban, auth, backup, and admin utilities --- job-tracker-ui/src/components/Attachments.tsx | 2 +- .../src/components/AuthStatusCard.tsx | 14 ++-- job-tracker-ui/src/components/BackupCard.tsx | 12 ++-- .../src/components/Correspondence.tsx | 38 +++++----- .../src/components/ErrorBoundary.tsx | 21 ++++-- job-tracker-ui/src/components/KanbanBoard.tsx | 27 +++++-- .../src/components/RulesSettingsCard.tsx | 2 +- .../src/components/UserManagementCard.tsx | 40 ++++++----- job-tracker-ui/src/i18n/translations.ts | 72 +++++++++++++++++++ job-tracker-ui/src/pages/AdminSystemPage.tsx | 24 +++---- job-tracker-ui/src/pages/AdminUsersPage.tsx | 4 +- 11 files changed, 181 insertions(+), 75 deletions(-) diff --git a/job-tracker-ui/src/components/Attachments.tsx b/job-tracker-ui/src/components/Attachments.tsx index beffe20..52e5537 100644 --- a/job-tracker-ui/src/components/Attachments.tsx +++ b/job-tracker-ui/src/components/Attachments.tsx @@ -313,7 +313,7 @@ export default function Attachments({ jobId }: { jobId: number }) { )} - {preview ? : null} + {preview ? : null} diff --git a/job-tracker-ui/src/components/AuthStatusCard.tsx b/job-tracker-ui/src/components/AuthStatusCard.tsx index e09170d..eac3021 100644 --- a/job-tracker-ui/src/components/AuthStatusCard.tsx +++ b/job-tracker-ui/src/components/AuthStatusCard.tsx @@ -5,6 +5,7 @@ import { Box, Button, Paper, Typography } from "@mui/material"; import { api } from "../api"; import { clearAuthToken, getAuthToken } from "../auth"; import { useToast } from "../toast"; +import { useI18n } from "../i18n/I18nProvider"; type MeResponse = { provider?: string; @@ -23,6 +24,7 @@ type MeResponse = { export default function AuthStatusCard() { const { toast } = useToast(); + const { t } = useI18n(); const token = getAuthToken(); const [me, setMe] = useState(null); @@ -42,11 +44,11 @@ export default function AuthStatusCard() { return ( - Authentication + {t("authStatusTitle")} {!token ? ( - Not signed in. + {t("authStatusNotSignedIn")} ) : ( @@ -54,12 +56,12 @@ export default function AuthStatusCard() { {me?.roles && me.roles.length > 0 ? ( - Roles: {me.roles.join(", ")} + {t("authStatusRoles", { roles: me.roles.join(", ") })} ) : null} {me?.googleLink?.linked ? ( - Google linked{me.googleLink.email ? `: ${me.googleLink.email}` : "."} + {t("authStatusGoogleLinked", { suffix: me.googleLink.email ? `: ${me.googleLink.email}` : "." })} ) : null} @@ -69,10 +71,10 @@ export default function AuthStatusCard() { onClick={() => { clearAuthToken(); setMe(null); - toast("Signed out.", "info"); + toast(t("signedOut"), "info"); }} > - Sign out + {t("signOut")} diff --git a/job-tracker-ui/src/components/BackupCard.tsx b/job-tracker-ui/src/components/BackupCard.tsx index 5321f09..59bcbf3 100644 --- a/job-tracker-ui/src/components/BackupCard.tsx +++ b/job-tracker-ui/src/components/BackupCard.tsx @@ -3,9 +3,11 @@ import React, { useState } from "react"; import { Box, Button, Paper, Typography } from "@mui/material"; import { api } from "../api"; import { useToast } from "../toast"; +import { useI18n } from "../i18n/I18nProvider"; export default function BackupCard() { const { toast } = useToast(); + const { t } = useI18n(); const [downloading, setDownloading] = useState(false); const downloadEncrypted = async () => { @@ -25,9 +27,9 @@ export default function BackupCard() { link.click(); link.remove(); window.setTimeout(() => URL.revokeObjectURL(url), 5000); - toast("Backup downloaded.", "success"); + toast(t("backupDownloaded"), "success"); } catch { - toast("Backup failed.", "error"); + toast(t("backupFailed"), "error"); } finally { setDownloading(false); } @@ -36,14 +38,14 @@ export default function BackupCard() { return ( - Data Safety + {t("backupTitle")} - One-click encrypted backup (Windows DPAPI). + {t("backupBody")} diff --git a/job-tracker-ui/src/components/Correspondence.tsx b/job-tracker-ui/src/components/Correspondence.tsx index 0746e0f..c756d34 100644 --- a/job-tracker-ui/src/components/Correspondence.tsx +++ b/job-tracker-ui/src/components/Correspondence.tsx @@ -233,7 +233,7 @@ export default function Correspondence({ jobId }: { jobId: number }) { await load(); toast(t("correspondenceLogEmail"), "success"); } catch (error) { - toast(getApiErrorMessage(error, "Failed to import email."), "error"); + toast(getApiErrorMessage(error, t("addJobModalImportFailed")), "error"); } }; @@ -241,9 +241,9 @@ export default function Correspondence({ jobId }: { jobId: number }) { try { const res = await api.get<{ url: string }>("/gmail/connect-url"); const popup = window.open(res.data.url, "jobtracker-gmail-connect", "width=620,height=760,resizable=yes,scrollbars=yes"); - if (!popup) toast("Your browser blocked the Gmail popup.", "error"); + if (!popup) toast(t("correspondenceBlockedPopup"), "error"); } catch (error) { - toast(getApiErrorMessage(error, "Failed to start Gmail connection."), "error"); + toast(getApiErrorMessage(error, t("correspondenceStartGmailFailed")), "error"); } }; @@ -254,18 +254,18 @@ export default function Correspondence({ jobId }: { jobId: number }) { setGmailMessages([]); toast(t("googleUnlinked"), "success"); } catch (error) { - toast(getApiErrorMessage(error, "Failed to disconnect Gmail."), "error"); + toast(getApiErrorMessage(error, t("correspondenceDisconnectFailed")), "error"); } }; const deleteMessage = async (messageId: number) => { - if (!(await confirmAction("Remove this correspondence message?", { title: "Delete message", confirmLabel: t("jobTableDeleteSelected"), destructive: true }))) return; + if (!(await confirmAction(t("correspondenceDeleteConfirm"), { title: t("correspondenceDeleteTitle"), confirmLabel: t("adminUsersDelete"), destructive: true }))) return; try { await api.delete(`/correspondence/${messageId}`); await load(); - toast("Message removed.", "success"); + toast(t("correspondenceDeleted"), "success"); } catch (error) { - toast(getApiErrorMessage(error, "Failed to remove message."), "error"); + toast(getApiErrorMessage(error, t("correspondenceDeleteFailed")), "error"); } }; @@ -276,7 +276,7 @@ export default function Correspondence({ jobId }: { jobId: number }) { await load(); toast(t("correspondenceImportEmail"), "success"); } catch (error: any) { - toast(getApiErrorMessage(error, "Failed to import Gmail message."), "error"); + toast(getApiErrorMessage(error, t("correspondenceImportGmailFailed")), "error"); } finally { setImportingMessageId(null); } @@ -287,9 +287,9 @@ export default function Correspondence({ jobId }: { jobId: number }) { setImportingThreadId(threadId); const res = await api.post<{ imported: number; skipped: number; threadId?: string }>("/gmail/import-thread", { jobApplicationId: jobId, threadId, messageIds }); await load(); - toast(`Imported ${res.data.imported} messages${res.data.skipped ? `, skipped ${res.data.skipped} duplicates` : ""}.`, "success"); + toast(t("correspondenceImportThreadResult", { imported: res.data.imported, skippedText: res.data.skipped ? t("correspondenceImportThreadSkipped", { count: res.data.skipped }) : "" }), "success"); } catch (error: any) { - toast(getApiErrorMessage(error, "Failed to import Gmail thread."), "error"); + toast(getApiErrorMessage(error, t("correspondenceImportThreadFailed")), "error"); } finally { setImportingThreadId(null); } @@ -299,7 +299,7 @@ export default function Correspondence({ jobId }: { jobId: number }) { {messages.length === 0 ? ( - No messages yet. + {t("correspondenceNoMessages")} ) : ( {messages.map((m) => { @@ -384,12 +384,12 @@ export default function Correspondence({ jobId }: { jobId: number }) { setGmailQuery(e.target.value)} placeholder={t("correspondenceSearchGmailPlaceholder")} size="small" fullWidth /> - {gmailStatus.lastSyncedAt ? : null} + {gmailStatus.lastSyncedAt ? : null} {gmailMessagesLoading ? ( ) : groupedByThread.length === 0 ? ( - No Gmail messages found. + {t("correspondenceNoGmailMessages")} ) : ( {groupedByThread.map(({ threadId, items }, threadIndex) => ( @@ -398,11 +398,11 @@ export default function Correspondence({ jobId }: { jobId: number }) { - {items[0]?.subject || "(No subject)"} - {items.length} message{items.length === 1 ? "" : "s"} in thread + {items[0]?.subject || t("correspondenceNoSubject")} + {t("correspondenceMessagesInThread", { count: items.length })} {items.map((message, index) => ( @@ -410,11 +410,11 @@ export default function Correspondence({ jobId }: { jobId: number }) { {index > 0 ? : null} {message.subject || "(No subject)"}{message.date ? new Date(message.date).toLocaleString() : ""}} - secondary={From: {message.from || t("correspondenceUnknown")}{message.snippet}} + primary={{message.subject || t("correspondenceNoSubject")}{message.date ? new Date(message.date).toLocaleString() : ""}} + secondary={{t("correspondenceFromLabel", { value: message.from || t("correspondenceUnknown") })}{message.snippet}} /> diff --git a/job-tracker-ui/src/components/ErrorBoundary.tsx b/job-tracker-ui/src/components/ErrorBoundary.tsx index 7ae4c68..2707ccc 100644 --- a/job-tracker-ui/src/components/ErrorBoundary.tsx +++ b/job-tracker-ui/src/components/ErrorBoundary.tsx @@ -1,16 +1,22 @@ import React from "react"; + import { api } from "../api"; +import { useI18n } from "../i18n/I18nProvider"; type Props = { children: React.ReactNode; }; +type ErrorBoundaryInnerProps = Props & { + t: (key: "errorBoundaryTitle" | "errorBoundaryBody" | "errorBoundaryUnknown" | "errorBoundaryRefresh") => string; +}; + type State = { hasError: boolean; errorId?: string; }; -export default class ErrorBoundary extends React.Component { +class ErrorBoundaryInner extends React.Component { state: State = { hasError: false }; static getDerivedStateFromError(_: any) { @@ -21,7 +27,6 @@ export default class ErrorBoundary extends React.Component { const errorId = `ui_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`; this.setState({ errorId }); - // Best-effort: report to API. If offline/misconfigured, we still show fallback UI. void api.post("/client-errors", { errorId, message: String(error?.message ?? error), @@ -38,16 +43,20 @@ export default class ErrorBoundary extends React.Component { return (
-

Something crashed.

+

{this.props.t("errorBoundaryTitle")}

- Try refreshing. If it keeps happening, capture this ID: + {this.props.t("errorBoundaryBody")}
- {this.state.errorId ?? "unknown"} + {this.state.errorId ?? this.props.t("errorBoundaryUnknown")}
- +
); } } +export default function ErrorBoundary(props: Props) { + const { t } = useI18n(); + return ; +} diff --git a/job-tracker-ui/src/components/KanbanBoard.tsx b/job-tracker-ui/src/components/KanbanBoard.tsx index 36aeb9b..dbd7ea5 100644 --- a/job-tracker-ui/src/components/KanbanBoard.tsx +++ b/job-tracker-ui/src/components/KanbanBoard.tsx @@ -35,6 +35,25 @@ function toneColor(theme: any, status: Status | "Other"): string { return theme.palette.primary.main; } +function statusLabel(t: (key: any, params?: any) => string, status: Status): string { + switch (status) { + case "Applied": + return t("statusApplied"); + case "Waiting": + return t("statusWaiting"); + case "Interview": + return t("statusInterview"); + case "Offer": + return t("statusOffer"); + case "Rejected": + return t("statusRejected"); + case "Ghosted": + return t("statusGhosted"); + default: + return status; + } +} + export default function KanbanBoard() { const theme = useTheme(); const { t } = useI18n(); @@ -79,7 +98,7 @@ export default function KanbanBoard() { return ( - Drag cards between columns to update status. + {t("kanbanHint")} @@ -101,7 +120,7 @@ export default function KanbanBoard() { > - {status} + {statusLabel(t, status)} - Drop here + {t("kanbanDropHere")} )} @@ -174,7 +193,7 @@ export default function KanbanBoard() { .filter((s) => s !== currentMenuStatus) .map((s) => ( { if (menuJobId) void setStatus(menuJobId, s); setMenuAnchor(null); setMenuJobId(null); }}> - {t("jobTableSetStatus", { status: s })} + {t("jobTableSetStatus", { status: statusLabel(t, s) })} ))} diff --git a/job-tracker-ui/src/components/RulesSettingsCard.tsx b/job-tracker-ui/src/components/RulesSettingsCard.tsx index a58eb40..b2a276b 100644 --- a/job-tracker-ui/src/components/RulesSettingsCard.tsx +++ b/job-tracker-ui/src/components/RulesSettingsCard.tsx @@ -33,7 +33,7 @@ export default function RulesSettingsCard() { await api.put("/rules", s); toast(t("rulesSave"), "success"); } catch { - toast("Failed to save rules.", "error"); + toast(t("rulesSaveFailed"), "error"); } finally { setSaving(false); } diff --git a/job-tracker-ui/src/components/UserManagementCard.tsx b/job-tracker-ui/src/components/UserManagementCard.tsx index aa8a4fb..4001e68 100644 --- a/job-tracker-ui/src/components/UserManagementCard.tsx +++ b/job-tracker-ui/src/components/UserManagementCard.tsx @@ -6,6 +6,7 @@ import { api } from "../api"; import { getAuthToken } from "../auth"; import { useToast } from "../toast"; import { useDialogActions } from "../dialogs"; +import { useI18n } from "../i18n/I18nProvider"; type UserDto = { id: string; @@ -18,6 +19,7 @@ type UserDto = { export default function UserManagementCard() { const { toast } = useToast(); const { confirmAction } = useDialogActions(); + const { t } = useI18n(); const token = getAuthToken(); const [supported, setSupported] = useState(null); @@ -41,7 +43,7 @@ export default function UserManagementCard() { if (status === 401 || status === 403) { setSupported(false); } else { - toast("Failed to load users.", "error"); + toast(t("adminUsersLoadFailed"), "error"); setSupported(false); } } finally { @@ -65,21 +67,21 @@ export default function UserManagementCard() { return ( - User management + {t("adminUsersTitle")} - Admin-only. + {t("adminUsersSubtitle")} setNewEmail(e.target.value)} fullWidth /> setNewPassword(e.target.value)} type="password" @@ -92,7 +94,7 @@ export default function UserManagementCard() { variant={newIsAdmin ? "contained" : "outlined"} onClick={() => setNewIsAdmin((v) => !v)} > - {newIsAdmin ? "Admin: Yes" : "Admin: No"} + {newIsAdmin ? t("adminUsersAdminYes") : t("adminUsersAdminNo")} {users.length === 0 ? ( - No users. + {t("adminUsersNoUsers")} ) : ( users.map((u) => ( - {u.email || u.userName || u.id} + {u.userName || u.email || u.id} - Roles: {u.roles?.length ? u.roles.join(", ") : "—"} + {t("adminUsersRolesLabel")}: {u.roles?.length ? u.roles.join(", ") : "—"} @@ -151,17 +153,17 @@ export default function UserManagementCard() { variant="outlined" color="error" onClick={async () => { - if (!(await confirmAction("Delete this user?", { title: "Delete user", confirmLabel: "Delete", destructive: true }))) return; + if (!(await confirmAction(t("adminUsersDeleteConfirmBody"), { title: t("adminUsersDeleteConfirmTitle"), confirmLabel: t("adminUsersDelete"), destructive: true }))) return; try { await api.delete(`/users/${u.id}`); - toast("User deleted.", "info"); + toast(t("adminUsersDeleted"), "info"); await load(); } catch { - toast("Failed to delete user.", "error"); + toast(t("adminUsersDeleteFailed"), "error"); } }} > - Delete + {t("adminUsersDelete")} )) diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index 447f94e..0871e51 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -54,6 +54,8 @@ export const translations = { save: "Save", create: "Create", createJob: "Create job", + yes: "Yes", + noWord: "No", createAndAddAnother: "Create & add another", loading: "Loading...", notFoundTitle: "Page not found", @@ -259,6 +261,7 @@ export const translations = { adminUsersAdmin: "Admin", adminUsersSendReset: "Send reset", adminUsersDelete: "Delete", + adminUsersRolesLabel: "Roles", adminUsersConfirmed: "Confirmed", adminUsersActions: "Actions", adminUsersNoUsers: "No users.", @@ -271,6 +274,13 @@ export const translations = { adminUsersDeleteFailed: "Failed to delete user.", adminUsersCreated: "User created.", adminUsersCreateFailed: "Failed to create user.", + adminUsersLoadFailed: "Failed to load users.", + adminUsersAdminYes: "Admin: Yes", + adminUsersAdminNo: "Admin: No", + adminUsersDeleteConfirmBody: "Delete this user?", + adminUsersPassword: "Password", + kanbanHint: "Drag cards between columns to update status.", + kanbanDropHere: "Drop here", adminSystemTitle: "System status", adminSystemSubtitle: "Production diagnostics for runtime, database, auth, email, AI service health, and OCR readiness.", adminSystemRunProbe: "Run probe now", @@ -352,6 +362,17 @@ export const translations = { correspondenceImportThread: "Import thread", correspondenceImporting: "Importing...", correspondenceFromLabel: "From: {value}", + correspondenceBlockedPopup: "Your browser blocked the Gmail popup.", + correspondenceStartGmailFailed: "Failed to start Gmail connection.", + correspondenceDisconnectFailed: "Failed to disconnect Gmail.", + correspondenceDeleteConfirm: "Remove this correspondence message?", + correspondenceDeleteTitle: "Delete message", + correspondenceDeleted: "Message removed.", + correspondenceDeleteFailed: "Failed to remove message.", + correspondenceImportGmailFailed: "Failed to import Gmail message.", + correspondenceImportThreadResult: "Imported {imported} messages{skippedText}.", + correspondenceImportThreadSkipped: ", skipped {count} duplicates", + correspondenceImportThreadFailed: "Failed to import Gmail thread.", attachmentsTitle: "Attachments ({count})", attachmentsSubtitle: "Upload resumes, cover letters, portfolios, and supporting files for this application.", attachmentsImages: "{count} images", @@ -446,6 +467,20 @@ export const translations = { signedOut: "Signed out.", signedInAs: "Signed in as {name}.", unlinkGoogle: "Unlink Google", + backupTitle: "Data safety", + backupBody: "One-click encrypted backup (Windows DPAPI).", + backupPreparing: "Preparing...", + backupDownload: "Download encrypted backup", + backupDownloaded: "Backup downloaded.", + backupFailed: "Backup failed.", + authStatusTitle: "Authentication", + authStatusNotSignedIn: "Not signed in.", + authStatusRoles: "Roles: {roles}", + authStatusGoogleLinked: "Google linked{suffix}", + errorBoundaryTitle: "Something crashed.", + errorBoundaryBody: "Try refreshing. If it keeps happening, capture this ID:", + errorBoundaryUnknown: "unknown", + errorBoundaryRefresh: "Refresh", importExportTitle: "Import / Export", importExportBody: "Import expects the JSON exported by this app (an array of job objects with embedded company).", exportJson: "Export JSON", @@ -676,6 +711,7 @@ export const translations = { rulesFeedbackGhostDays: "Feedback: ghost days", rulesSaving: "Saving...", rulesSave: "Save Rules", + rulesSaveFailed: "Failed to save rules.", }, no: { appTitle: "Jobbjakt", @@ -730,6 +766,8 @@ export const translations = { save: "Lagre", create: "Opprett", createJob: "Opprett jobb", + yes: "Ja", + noWord: "Nei", createAndAddAnother: "Opprett og legg til en til", loading: "Laster...", notFoundTitle: "Siden ble ikke funnet", @@ -935,6 +973,7 @@ export const translations = { adminUsersAdmin: "Admin", adminUsersSendReset: "Send tilbakestilling", adminUsersDelete: "Slett", + adminUsersRolesLabel: "Roller", adminUsersConfirmed: "Bekreftet", adminUsersActions: "Handlinger", adminUsersNoUsers: "Ingen brukere.", @@ -947,6 +986,13 @@ export const translations = { adminUsersDeleteFailed: "Kunne ikke slette bruker.", adminUsersCreated: "Bruker opprettet.", adminUsersCreateFailed: "Kunne ikke opprette bruker.", + adminUsersLoadFailed: "Kunne ikke laste brukere.", + adminUsersAdminYes: "Admin: Ja", + adminUsersAdminNo: "Admin: Nei", + adminUsersDeleteConfirmBody: "Slette denne brukeren?", + adminUsersPassword: "Passord", + kanbanHint: "Dra kort mellom kolonnene for å oppdatere status.", + kanbanDropHere: "Slipp her", adminSystemTitle: "Systemstatus", adminSystemSubtitle: "Produksjonsdiagnostikk for kjøretid, database, autentisering, e-post, AI-tjenestehelse og OCR-beredskap.", adminSystemRunProbe: "Kjør probe nå", @@ -1028,6 +1074,17 @@ export const translations = { correspondenceImportThread: "Importer tråd", correspondenceImporting: "Importerer...", correspondenceFromLabel: "Fra: {value}", + correspondenceBlockedPopup: "Nettleseren din blokkerte Gmail-popupen.", + correspondenceStartGmailFailed: "Kunne ikke starte Gmail-tilkobling.", + correspondenceDisconnectFailed: "Kunne ikke koble fra Gmail.", + correspondenceDeleteConfirm: "Fjerne denne meldingen fra korrespondansen?", + correspondenceDeleteTitle: "Slett melding", + correspondenceDeleted: "Melding fjernet.", + correspondenceDeleteFailed: "Kunne ikke fjerne melding.", + correspondenceImportGmailFailed: "Kunne ikke importere Gmail-melding.", + correspondenceImportThreadResult: "Importerte {imported} meldinger{skippedText}.", + correspondenceImportThreadSkipped: ", hoppet over {count} duplikater", + correspondenceImportThreadFailed: "Kunne ikke importere Gmail-tråd.", attachmentsTitle: "Vedlegg ({count})", attachmentsSubtitle: "Last opp CV-er, søknadsbrev, porteføljer og støttedokumenter for denne søknaden.", attachmentsImages: "{count} bilder", @@ -1122,6 +1179,20 @@ export const translations = { signedOut: "Logget ut.", signedInAs: "Logget inn som {name}.", unlinkGoogle: "Koble fra Google", + backupTitle: "Datasikkerhet", + backupBody: "Kryptert sikkerhetskopi med ett klikk (Windows DPAPI).", + backupPreparing: "Forbereder...", + backupDownload: "Last ned kryptert sikkerhetskopi", + backupDownloaded: "Sikkerhetskopi lastet ned.", + backupFailed: "Sikkerhetskopiering mislyktes.", + authStatusTitle: "Autentisering", + authStatusNotSignedIn: "Ikke logget inn.", + authStatusRoles: "Roller: {roles}", + authStatusGoogleLinked: "Google koblet{suffix}", + errorBoundaryTitle: "Noe krasjet.", + errorBoundaryBody: "Prøv å laste inn siden på nytt. Hvis det fortsetter, ta vare på denne ID-en:", + errorBoundaryUnknown: "ukjent", + errorBoundaryRefresh: "Oppdater", importExportTitle: "Import / eksport", importExportBody: "Import forventer JSON eksportert av denne appen (en matrise med jobbobjekter med innebygd selskap).", exportJson: "Eksporter JSON", @@ -1352,6 +1423,7 @@ export const translations = { rulesFeedbackGhostDays: "Tilbakemelding: ghostingdager", rulesSaving: "Lagrer...", rulesSave: "Lagre regler", + rulesSaveFailed: "Kunne ikke lagre regler.", }, } as const; diff --git a/job-tracker-ui/src/pages/AdminSystemPage.tsx b/job-tracker-ui/src/pages/AdminSystemPage.tsx index 66a0d54..f10fce0 100644 --- a/job-tracker-ui/src/pages/AdminSystemPage.tsx +++ b/job-tracker-ui/src/pages/AdminSystemPage.tsx @@ -249,14 +249,14 @@ export default function AdminSystemPage() { - - - + + + - + - + @@ -271,10 +271,10 @@ export default function AdminSystemPage() { - - - - + + + + @@ -283,12 +283,12 @@ export default function AdminSystemPage() { {t("adminSystemEmailConfig")} - + - + @@ -297,7 +297,7 @@ export default function AdminSystemPage() { - + diff --git a/job-tracker-ui/src/pages/AdminUsersPage.tsx b/job-tracker-ui/src/pages/AdminUsersPage.tsx index 732baf9..d83c18b 100644 --- a/job-tracker-ui/src/pages/AdminUsersPage.tsx +++ b/job-tracker-ui/src/pages/AdminUsersPage.tsx @@ -133,7 +133,7 @@ export default function AdminUsersPage() { {t("profileEmail")} {t("profileUsername")} - Roles + {t("adminUsersRolesLabel")} {t("adminUsersConfirmed")} {t("adminUsersActions")} @@ -146,7 +146,7 @@ export default function AdminUsersPage() { {u.email || ""} {u.userName || ""} {u.roles?.length ? u.roles.join(", ") : "-"} - {u.emailConfirmed ? "Yes" : "No"} + {u.emailConfirmed ? t("yes") : t("noWord")}