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:
180
README.md
180
README.md
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user