From 9615ee3f4121bb9e210256064f678b58db1d1504 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sun, 12 Jul 2026 04:14:37 +0200 Subject: [PATCH] feat(ux): per-view subtitles, correspondence cross-links, mobile kanban, a11y Continuing the product/UX review's deferred items: - Every top-level view now gets a one-line subtitle under its title (Dashboard/Jobs/Kanban/Reminders/Correspondence/Gmail review) stating what that specific view is for, instead of navigation being the only signal of what each page does. - Correspondence inbox and Gmail review queue cross-link to each other instead of being two unexplained flat sidebar items -- kept both nav entries (renaming/nesting risked breaking muscle memory) but made the relationship between them explicit in the UI itself. - Kanban board switches to a horizontal scroll-snap row on phone-width viewports instead of stacking all 5 columns vertically, which meant a lot of scrolling to see anything past "Applied". - Match-score ring gets an aria-label with the actual percentage -- it was two nested decorative CircularProgress elements with no accessible text. (Keyboard-accessible status changes on kanban cards were already covered by the existing "..." menu -- no gap there.) --- job-tracker-ui/src/App.tsx | 12 ++++++++++++ .../src/components/JobDetailsDialog.tsx | 5 +++-- job-tracker-ui/src/components/KanbanBoard.tsx | 15 ++++++++++++++- job-tracker-ui/src/i18n/translations.ts | 12 ++++++++++++ job-tracker-ui/src/layout/AppShell.tsx | 7 +++++++ .../src/views/CorrespondenceInboxPage.tsx | 3 ++- job-tracker-ui/src/views/GmailReviewPage.tsx | 1 + 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/job-tracker-ui/src/App.tsx b/job-tracker-ui/src/App.tsx index 0210bd3..bb8e0cd 100644 --- a/job-tracker-ui/src/App.tsx +++ b/job-tracker-ui/src/App.tsx @@ -100,6 +100,16 @@ function titleFor(path: string, t: (k: any) => string): string { return t("appTitle"); } +function subtitleFor(path: string, t: (k: any) => string): string | undefined { + if (path === "/dashboard") return t("dashboardPageSubtitle"); + if (path.startsWith("/jobs")) return t("jobsPageSubtitle"); + if (path.startsWith("/kanban")) return t("kanbanPageSubtitle"); + if (path.startsWith("/reminders")) return t("remindersPageSubtitle"); + if (path.startsWith("/correspondence/review")) return t("gmailReviewPageSubtitle"); + if (path.startsWith("/correspondence")) return t("correspondencePageSubtitle"); + return undefined; +} + function PageLoader() { return Loading...; } @@ -209,6 +219,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo if (requireAuth && !me) return ; const pageTitle = titleFor(path, t); + const pageSubtitle = subtitleFor(path, t); const breadcrumbs = breadcrumbsFor(path, t); const setAndPersistPageSize = (n: 15 | 20 | 25) => { setJobPageSize(n); window.localStorage.setItem("jobPageSize", String(n)); }; const setAndPersistColumns = (next: JobTableColumns) => { setJobColumns(next); window.localStorage.setItem("jobColumns", JSON.stringify(next)); }; @@ -275,6 +286,7 @@ function Shell({ jobPageSize, setJobPageSize, jobColumns, setJobColumns, themeMo <> {score.hasEnoughSignal ? ( - - + + - + } label={`${items.length} items`} variant="outlined" /> 0 ? "success" : "default"} /> + diff --git a/job-tracker-ui/src/views/GmailReviewPage.tsx b/job-tracker-ui/src/views/GmailReviewPage.tsx index ea6fc33..4b058e9 100644 --- a/job-tracker-ui/src/views/GmailReviewPage.tsx +++ b/job-tracker-ui/src/views/GmailReviewPage.tsx @@ -138,6 +138,7 @@ export default function GmailReviewPage() { +