feat(ui): "Foundry Deck" visual redesign + full README

Complete visual redesign via a single centralized design system in
src/index.css (no JSX/logic changes; all existing class selectors kept
1:1, so all ~20 pages restyle at once): blackened-steel dark palette,
molten-ember accent with heat glow, blueprint-grid + grain background,
Saira Condensed / Hanken Grotesk / JetBrains Mono web fonts, staggered
load animations, reduced-motion fallback. README written from stub.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
h z
2026-05-16 17:50:26 +01:00
parent 6432255203
commit 0fba859adf
2 changed files with 704 additions and 199 deletions

180
README.md
View File

@@ -1,3 +1,181 @@
# HarborForge.Frontend
HarborForge Frontend - React + TypeScript
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](../README.md) 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 `/login` on
HTTP `401`, except on the public paths `/monitor` and `/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()`:
1. It calls the backend `GET /config/status`. If that returns
`initialized: true`, the app is `ready` (and `backend_url`, if present,
is cached into `localStorage['HF_BACKEND_BASE_URL']`).
2. Otherwise, if a wizard port was previously saved
(`localStorage['HF_WIZARD_PORT']`), it tries
`http://127.0.0.1:<port>/api/v1/config/harborforge.json` directly.
3. 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-checks
`http://127.0.0.1:<port>/health` and 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 to
`docker compose restart` on the server before refreshing.
## Local Development
```bash
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
```bash
npm run build # tsc type-check, then vite build → dist/
npm run preview # serve the production build locally
```
### Test
```bash
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
```bash
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`, default `http://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/status` response — 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`, `--ember` gradient, 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 `@import` at the top
of `src/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.