Make /jobs/:id the canonical application workspace while preserving list state and compatibility links. Replace popup and expandable-row navigation with accessible whole-row routing and richer job details.
Rotate persisted delivery identity only after the matching owner attempt is definitively failed. Keep stale, foreign, pending, sent, and uncertain drafts non-retryable.
Keep provider, recipient, subject, thread, and body editable until an app-owned confirmation is accepted. Reuse one request UUID per attempt and block automatic retry when delivery is uncertain.
Gmail and Graph request explicit send consent and classify provider rejection separately from uncertain transport failure. IMAP remains read-only; no send API is exposed.
Last two views/*.tsx files not yet covered this session. Same
floating-shadow page-wrapper treatment as every other screen; the
outlined item-row Paper inside each list stays as-is, matching the
established "list rows stay flat/outlined, the page wrapper floats"
convention (same pattern already used for trusted-devices/sessions
list rows).
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.)
Wave 6. Swaps react-scripts' build/dev tooling for Next.js while
keeping the app's actual routing/rendering model unchanged -- the app
is almost entirely behind auth with no proven SSR/SEO need, so a real
App Router rewrite would touch ~90 files for zero user-visible benefit.
- next.config.js: output:'export' (static HTML+JS, same "single
index.html served by nginx with try_files fallback" deploy as CRA).
- app/layout.tsx + app/page.tsx: root shell ports public/index.html's
<head>, mounts the whole existing App tree client-only (ssr:false)
since it reads window/localStorage during initial render and Next's
static prerender would otherwise execute that on the server.
- Renamed src/pages/ -> src/views/ (Next's Pages Router auto-detects
any `pages/` dir under the app root and tried to build our React
Router page components as its own routes).
- REACT_APP_* -> NEXT_PUBLIC_* across code, .env.development,
Dockerfile, docker-compose.yml build args.
- Replaced the CRA SVGR import (`ReactComponent` from .svg, unsupported
under Turbopack) with a small inline JobbjaktMark component.
- TypeScript 4.9 -> 5.9 (MUI v8's type-checked build needs syntax
4.9's parser rejects; CRA never hit this because babel doesn't
type-check).
- Dropped CRA-only files (index.tsx, reportWebVitals, react-app-env.d.ts,
public/index.html); kept react-scripts as the Jest test runner only
(next/jest migration not needed -- the existing config already works).
Verified: `next build` static export succeeds, `next dev` serves the
landing page and client-side routes (login etc.) correctly, all 57
frontend tests + 172 backend tests still green.
Known caveat: deep-linking straight to a sub-route (e.g. /login) 404s
in `next dev` since there's no server route for it -- the app only
ever mounts at "/". Production is unaffected: nginx's existing
try_files fallback still serves index.html for any path.