HarborForge.Frontend
Single-page web client for HarborForge — the agent/human collaborative task-management platform. It provides the operator UI for projects, milestones, tasks, proposals, calendar/meetings, users, roles and the live monitor, and walks operators through first-time provisioning via the SSH-tunnel Setup Wizard.
Part of the HarborForge platform.
Tech Stack
- React 18 (
react,react-dom) - TypeScript 5
- Vite 5 dev server / bundler (
@vitejs/plugin-react) - react-router-dom 6 for client-side routing
- axios for HTTP, with a shared request/response interceptor layer
- dayjs for date handling
- Vitest 4 + Testing Library (
@testing-library/react,jsdom) for unit tests
Path alias @ → src/ (configured in vite.config.ts and tsconfig.json).
Pages
Routes are declared in src/App.tsx. The app gates rendering on three
states — checking (probing configuration), setup (Setup Wizard), and
ready (authenticated app). /monitor, /login, /roles and /users
are reachable without an authenticated session; everything else requires login.
| Route | Page | Purpose |
|---|---|---|
/ |
DashboardPage |
Landing dashboard |
/tasks |
TasksPage |
Task list / filtering |
/tasks/:taskCode |
TaskDetailPage |
Task detail, transitions, comments |
/projects |
ProjectsPage |
Project list |
/projects/:id |
ProjectDetailPage |
Project detail / editor |
/milestones |
MilestonesPage |
Milestone list |
/milestones/:milestoneCode |
MilestoneDetailPage |
Milestone detail / actions |
/proposals (/proposes) |
ProposalsPage |
Proposal list |
/proposals/:proposalCode (/proposes/:proposalCode) |
ProposalDetailPage |
Proposal detail, accept/reject/reopen |
/calendar |
CalendarPage |
Calendar view |
/meetings/:meetingCode |
MeetingDetailPage |
Meeting detail |
/supports/:supportCode |
SupportDetailPage |
Support request detail |
/notifications |
NotificationsPage |
Notification inbox |
/roles |
RoleEditorPage |
Role / permission (RBAC) editor |
/users |
UsersPage |
User management |
/monitor |
MonitorPage |
Live system monitor (public) |
/login |
LoginPage |
Authentication |
| (state-gated) | SetupWizardPage |
First-run provisioning wizard |
Shared building blocks live in src/components/ (Sidebar,
CreateTaskModal, MilestoneFormModal, ProjectFormModal,
CopyableCode); the auth hook is src/hooks/useAuth.ts; shared types are
in src/types/index.ts.
How It Talks to the Backend
All API access goes through the shared axios instance in
src/services/api.ts:
- The base URL is resolved at runtime from
localStorage['HF_BACKEND_BASE_URL'](re-read on every request via an axios request interceptor), not baked in at build time. - A bearer token from
localStorage['token']is attached automatically when present. - A response interceptor clears the token and redirects to
/loginon HTTP401, except on the public paths/monitorand/login.
Authentication (src/hooks/useAuth.ts) posts form-encoded credentials to
/auth/token, stores the returned access_token, and loads the current
user from /auth/me.
In local dev, vite.config.ts also proxies /api/* to
http://backend:8000 (stripping the /api prefix) for same-origin
development against a Compose backend.
Setup Wizard / SSH-Tunnel Flow
On startup App.tsx runs checkInitialized():
- It calls the backend
GET /config/status. If that returnsinitialized: true, the app isready(andbackend_url, if present, is cached intolocalStorage['HF_BACKEND_BASE_URL']). - Otherwise, if a wizard port was previously saved
(
localStorage['HF_WIZARD_PORT']), it trieshttp://127.0.0.1:<port>/api/v1/config/harborforge.jsondirectly. - If neither indicates an initialized install, it renders
SetupWizardPage.
The Setup Wizard (src/pages/SetupWizardPage.tsx) is a 4-step flow —
Wizard → Admin → Backend → Finish:
- Wizard: the operator enters the local port that an SSH tunnel
forwards to AbstractWizard
(
ssh -L <port>:127.0.0.1:<port> user@server). The UI health-checkshttp://127.0.0.1:<port>/healthand stores the port. - Admin: collects the first admin account (username, password, email, full name).
- Backend: optional backend base URL override.
- Finish: writes the assembled config to AbstractWizard via
PUT http://127.0.0.1:<port>/api/v1/config/harborforge.json, caches the backend URL locally, and instructs the operator todocker compose restarton the server before refreshing.
Local Development
npm install
npm run dev # Vite dev server on http://0.0.0.0:3000
The dev server proxies /api to http://backend:8000 and allows the
hosts frontend, 127.0.0.1, localhost.
Build
npm run build # tsc type-check, then vite build → dist/
npm run preview # serve the production build locally
Test
npm test # vitest run (jsdom)
npm run test:watch # watch mode
npm run test:ui # Vitest UI
Tests live in src/test/**/*.test.{ts,tsx} (e.g. calendar.test.tsx,
proposal-essential.test.tsx) with global setup in src/test/setup.ts.
Docker
docker build -t harborforge-frontend .
docker run -p 3000:3000 harborforge-frontend
The multi-stage Dockerfile builds with Node 20 and serves dist/ via
serve on port 3000. Set FRONTEND_DEV_MODE=1 to run the Vite dev server
inside the container instead of serving the static build.
Environment / Configuration
VITE_API_BASE(.env, defaulthttp://backend:8000) — a build hint for the default backend location.- The effective backend base URL is determined at runtime from
localStorage['HF_BACKEND_BASE_URL'], which is populated by the Setup Wizard or by the backend's/config/statusresponse — so changing the backend target does not require a rebuild. localStorage['HF_WIZARD_PORT']— last AbstractWizard tunnel port.localStorage['token']— current JWT bearer token.
Theming
The entire visual design is a single self-contained design system in
src/index.css — there is no CSS-in-JS, no Tailwind, and no
per-component stylesheets. Components only reference global CSS classes;
all colors, typography, spacing and effects are driven by CSS custom
properties on :root. To re-theme the app, edit this one file.
The theme is "Foundry Deck" — a shipwright's drafting table meets a foundry control panel:
- Blackened-steel dark palette — cool-tinted near-black surfaces
(
--bg,--bg-card,--bg-hover,--bg-sink) with machined, tight-radius borders. - Molten-ember accent — a single hot orange accent
(
--accent,--accent-hover,--embergradient, ember glow) used sparingly as the signature element. - Oxidized-steel secondary plus a forge-mapped status palette (patina success, heat-amber warning, rust danger, arc violet).
- Blueprint-grid background — layered radial ember/steel vignettes, a fine 40px draughting grid, and a subtle SVG film-grain overlay.
- Technical web fonts loaded via a Google Fonts
@importat the top ofsrc/index.css: Saira Condensed (headings), Hanken Grotesk (body), and JetBrains Mono (code/monospace).
Selectors are kept 1:1 with the existing markup, so theming changes are
made entirely in src/index.css via the CSS variables and global classes.