Frontend for the Job Apply Tracker: manage job applications, follow-ups, interview stages, and metrics. Rebuilt in TypeScript + React + Tailwind CSS following a monochrome "Notion + Vercel" design system (clean, typography-forward, 1px borders, 4px radius, no decorative color).
Issues and discussions are centralized in the backend repo.
- React 19 + TypeScript (strict)
- Vite 7
- React Router 7
- Tailwind CSS 3 (custom
monopalette, Inter + JetBrains Mono) - Zustand (auth + gamification state, persisted)
- Axios (typed client with bearer auth + single-flight token refresh)
- React Hook Form (form state & validation)
- Playwright (E2E tests against a mocked API)
The UI implements the Applywell wireframe handoff (claude.ai/design):
- Palette:
#000 β #fffonly, exposed as Tailwindmono-{0,1,2,5,9,c,e5,f5,w} - Type: Inter (UI) + JetBrains Mono (numbers, hints, eyebrows)
- Components: square-ish controls, hover
bg-mono-f5, selectedbg-black text-white - Status badges: backend statuses map to six monochrome "families"
(draft β sent β replied β interview β offer β rejected) in
src/lib/statuses.ts
- Auth β Login, Register, Forgot Password
- Dashboard β metric cards, achievement badges, To Send Later / Overdue follow-up panels, and a Standard β Gamified variant toggle
- Applications β Active/Archived tabs, search + status + sort filters, Table / Board / Mobile views, server-side pagination, create/edit form with unsaved-changes banner, archive & delete confirmations
- Metrics β conversion funnel, applications-by-status bars, weekly-volume line chart, pipeline averages (pure CSS/SVG, monochrome)
- Developer Tools β API info, JSON/CSV export, environment panel
- Account Settings β profile, change password, passkeys, Google Drive status, danger zone
src/
api/ # Typed HTTP modules (auth, applications, dashboard, gamification, resumes)
components/
applications/ # Table / Board / Cards views
dashboard/ # MetricCard, AchievementCard, ListPanel
metrics/ # Monochrome chart primitives
layout/ # Sidebar, Topbar, AppLayout, ProtectedRoute, navigation
ui/ # Button, StatusBadge, Field, Segmented, Panel, Dialog, Pager, iconsβ¦
hooks/ # useAsync, useDebouncedValue
lib/ # api (axios), statuses, format, utils
pages/ # Screens grouped by domain
store/ # Zustand stores (auth, gamification)
types/ # Domain types mirroring the OpenAPI contract
App.tsx # Route tree + boot-time session restore
main.tsx # Entry point
tests/
support/ # Mock API (stateful) + fixtures + seed data
*.spec.ts # E2E specs
- Token + user persisted in
applywell-auth(localStorage) via Zustand persist. - Axios request interceptor attaches
Authorization: Bearer <token>. - On
401/403, a single-flight refresh retries the original request; a definitive failure clears the session andProtectedRouteredirects to/login.
- Node.js 20+
- npm 10+
- Backend at
http://localhost:8080(default)
VITE_API_BASE_URL(orVITE_API_URL): API base;/api/v1is appended if absentVITE_BASE_PATH: deploy base path (default/)API_TARGET: dev proxy target for/api(defaulthttp://localhost:8080)
VITE_API_BASE_URL=http://localhost:8080
VITE_BASE_PATH=/
API_TARGET=http://localhost:8080npm install
npm run dev # http://localhost:5173npm run dev # Dev server
npm run build # tsc + production build
npm run preview # Preview the build
npm run typecheck # tsc --noEmit
npm run lint # ESLint
npm run test:e2e # Playwright E2E (mocked API, no backend needed)
npm run test:e2e:ui # Playwright UI mode
npm run test:e2e:report # Open the HTML reportTests live in tests/ and run against a stateful in-memory mock of the
backend (tests/support/mockApi.ts) β no live API required. Mutations (create,
archive, delete) are reflected within a test run, and an authenticated session is
seeded into localStorage before navigation.
npm run test:e2eThis frontend consumes the Spring Boot API under /api/v1. The expected backend
lives in the sibling repo:
SpringBoot-JobApplyTracker.