docs(admin): phase 3a session log + roadmap status
This commit is contained in:
98
apps/admin/README.md
Normal file
98
apps/admin/README.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# @solelog/admin
|
||||||
|
|
||||||
|
The SoleLog **Admin panel**: a **Vite + React + TypeScript** single-page app for the
|
||||||
|
shop-floor administrator. An admin logs in (email + password → bearer token in
|
||||||
|
`localStorage`), watches who is working **live**, and manages the **handelingen**
|
||||||
|
(activities). It is a **client only** — it talks to the `@solelog/api` backend over HTTP
|
||||||
|
with `Authorization: Bearer <token>`; it never touches the database.
|
||||||
|
|
||||||
|
> **Admin-only.** Sign-in confirms `role === 'admin'` via `GET /api/me`. A worker who signs
|
||||||
|
> in with valid credentials is rejected with **"Geen toegang — alleen beheerders."** and the
|
||||||
|
> token is cleared. The UI is in **Dutch**.
|
||||||
|
|
||||||
|
It mirrors `apps/worker`'s toolchain and conventions (same Vite/React/react-query/vitest
|
||||||
|
versions, the same `lib/api.ts` + `lib/auth-storage.ts`), so it shares proven patterns.
|
||||||
|
|
||||||
|
## What Phase 3a covers
|
||||||
|
|
||||||
|
- **Login** — Dutch email + password form (no self-signup), with the admin-only gate.
|
||||||
|
- **Sidebar shell** — left sidebar with the signed-in email + an Uitloggen button, nav to
|
||||||
|
**Live** and **Handelingen**, and a muted "Binnenkort" group (Rapporten / Gebruikers /
|
||||||
|
Handmatig) hinting at Phase 3b.
|
||||||
|
- **Live** (`/`) — who is working right now, from `GET /api/admin/sessions/active`,
|
||||||
|
auto-refreshing every **5 s** (read-only in 3a). One card per session: worker name,
|
||||||
|
activity, insole-type pill, pair count, and a client-side ticking elapsed timer. Empty
|
||||||
|
state: "Niemand is nu aan het werk.".
|
||||||
|
- **Handelingen** (`/handelingen`) — add / inline-edit / delete activities and their insole
|
||||||
|
types (`Kurk` / `Berk` / `3D`), against `/api/activities` (writes are admin-gated
|
||||||
|
server-side; the admin bearer satisfies them).
|
||||||
|
|
||||||
|
**Deferred to Phase 3b:** reports + all-users CSV export, user management
|
||||||
|
(`/api/auth/admin/*`), and manual session entry/edit + admin stop/fix of a running session
|
||||||
|
(needs new backend endpoints).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- Node + Corepack (Yarn 4 is pinned in the repo).
|
||||||
|
- From the **repo root**, install everything once:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run it
|
||||||
|
|
||||||
|
Two processes: the API on `:3000` and the admin SPA on `:5174`.
|
||||||
|
|
||||||
|
### 1. Backend API (`:3000`)
|
||||||
|
|
||||||
|
From the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn workspace @solelog/api db:migrate # apply migrations (creates ./data/app.db on first run)
|
||||||
|
yarn workspace @solelog/api db:seed # idempotent: seeds reference activities + dev logins
|
||||||
|
yarn workspace @solelog/api start # Hono server on http://localhost:3000
|
||||||
|
```
|
||||||
|
|
||||||
|
`db:seed` creates two **dev logins** (dev-only — skipped when `NODE_ENV=production`):
|
||||||
|
**admin** `admin@solelog.local` / `werkplaats-admin` and **worker**
|
||||||
|
`worker@solelog.local` / `werkplaats123`. Only the admin can use this app.
|
||||||
|
|
||||||
|
The API's default `WEB_ORIGINS` already allows `http://localhost:5174` (this app's dev
|
||||||
|
origin), so cross-origin sign-in and requests work out of the box. Override with
|
||||||
|
`CORS_ORIGINS` for other origins (e.g. a LAN IP).
|
||||||
|
|
||||||
|
### 2. Admin SPA (`:5174`)
|
||||||
|
|
||||||
|
From the repo root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn workspace @solelog/admin dev # Vite dev server on http://localhost:5174
|
||||||
|
```
|
||||||
|
|
||||||
|
Open **http://localhost:5174** and sign in as the admin. The API base URL comes from
|
||||||
|
`VITE_API_URL` (default `http://localhost:3000`).
|
||||||
|
|
||||||
|
## Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn workspace @solelog/admin dev # dev server (:5174, LAN-exposed)
|
||||||
|
yarn workspace @solelog/admin build # tsc -b && vite build → dist/
|
||||||
|
yarn workspace @solelog/admin preview # preview the production build
|
||||||
|
yarn workspace @solelog/admin typecheck # tsc -b
|
||||||
|
yarn workspace @solelog/admin test # vitest run
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
- **Auth.** `lib/api.ts` + `lib/auth-storage.ts` are copied from the worker: sign-in reads
|
||||||
|
the bearer token from the `set-auth-token` response header into `localStorage`, and
|
||||||
|
`apiFetch<T>` attaches `Authorization: Bearer <token>` to every request.
|
||||||
|
`auth/AuthContext.tsx` adds the admin gate (re-fetch `/api/me`, reject non-admins).
|
||||||
|
- **Live data.** `api/admin-sessions.ts` `useActiveSessions()` uses react-query with
|
||||||
|
`refetchInterval: 5000`; each card computes elapsed time from the server `start_time`
|
||||||
|
with a 1 s client tick (`lib/elapsed.ts` `formatTime`).
|
||||||
|
- **Activities.** `api/activities.ts` hooks (`useActivities` + create/update/delete)
|
||||||
|
invalidate the `['activities']` query on every mutation.
|
||||||
|
- **Shared contracts.** Request/response shapes are zod schemas in `@solelog/shared`,
|
||||||
|
imported here for a typed client.
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Insole Production Time Tracker — Rebuild Roadmap & Project Overview
|
# Insole Production Time Tracker — Rebuild Roadmap & Project Overview
|
||||||
|
|
||||||
- **Created:** 2026-06-17
|
- **Created:** 2026-06-17
|
||||||
- **Status:** Approved — living project doc; Phases 0–2 implemented (`docs/plans/phase-2-accounts-roles.md`)
|
- **Status:** Approved — living project doc; Phases 0–2 implemented + Phase **3a** implemented (`docs/superpowers/plans/2026-06-17-phase-3a-admin-panel.md`)
|
||||||
- **Type:** Greenfield rebuild of an inherited app
|
- **Type:** Greenfield rebuild of an inherited app
|
||||||
- **Tracked in git** under `docs/` (the project's documentation source of truth).
|
- **Tracked in git** under `docs/` (the project's documentation source of truth).
|
||||||
|
|
||||||
@@ -174,8 +174,16 @@ Each phase keeps the system working and is its own spec → plan → build cycle
|
|||||||
an admin account exists; admin manages users via `/api/auth/admin/*` and sees all sessions via
|
an admin account exists; admin manages users via `/api/auth/admin/*` and sees all sessions via
|
||||||
`/api/admin/sessions`. Public sign-up is closed; activity writes are admin-only.
|
`/api/admin/sessions`. Public sign-up is closed; activity writes are admin-only.
|
||||||
_Done when:_ workers see only their own sessions; an admin account exists.
|
_Done when:_ workers see only their own sessions; an admin account exists.
|
||||||
- **Phase 3 — Admin panel.** The React admin app: live active-work view, reports/export,
|
- **Phase 3 — Admin panel.** The React admin app (`apps/admin`, Vite + React, dev port
|
||||||
user management, **activity management**, **manual entry/edit (the fallback)**.
|
5174): live active-work view, reports/export, user management, **activity management**,
|
||||||
|
**manual entry/edit (the fallback)**.
|
||||||
|
**3a implemented** (`apps/admin`; plan `docs/superpowers/plans/2026-06-17-phase-3a-admin-panel.md`):
|
||||||
|
admin-only login (rejects non-admins via `role` on `/api/me`), the sidebar shell, the live
|
||||||
|
active-work view (`/api/admin/sessions/active`, 5 s auto-refresh, read-only), and activity
|
||||||
|
management (handelingen CRUD on `/api/activities`). **3b remaining:** reports/export
|
||||||
|
(all-users filtered CSV — current `/api/export` is self-scoped), user management
|
||||||
|
(better-auth `/api/auth/admin/*`), and manual entry/edit + admin stop/fix of a running
|
||||||
|
session (needs new backend endpoints).
|
||||||
Activity management (add/edit/delete handelingen + their `insole_types`) was removed from the
|
Activity management (add/edit/delete handelingen + their `insole_types`) was removed from the
|
||||||
worker client in the Phase 2 follow-up because it is admin-only; it must be **ported here**. The
|
worker client in the Phase 2 follow-up because it is admin-only; it must be **ported here**. The
|
||||||
backend already exists (`/api/activities` writes are admin-gated; `useActivities`/the legacy
|
backend already exists (`/api/activities` writes are admin-gated; `useActivities`/the legacy
|
||||||
|
|||||||
72
docs/sessions/2026-06-17-phase-3a-admin-panel.md
Normal file
72
docs/sessions/2026-06-17-phase-3a-admin-panel.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Session: 2026-06-17 — Phase 3a (Admin panel MVP)
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Build `apps/admin`: a Vite + React desktop SPA where an admin logs in, watches who is
|
||||||
|
working **live** (auto-refreshing, read-only), and manages **handelingen** (activities).
|
||||||
|
Everything rides on existing backend endpoints plus one tiny backend touch (`role` on
|
||||||
|
`/api/me`). Reports/export, user management, and manual session entry/edit are deferred to
|
||||||
|
Phase 3b. Spec: `docs/superpowers/specs/2026-06-17-phase-3a-admin-panel-design.md`;
|
||||||
|
plan: `docs/superpowers/plans/2026-06-17-phase-3a-admin-panel.md`.
|
||||||
|
|
||||||
|
## Work done
|
||||||
|
|
||||||
|
Implemented Phase 3a task-by-task per the plan (TDD throughout):
|
||||||
|
|
||||||
|
- **Task 1 — `role` on `/api/me` + admin-origin CORS** (`02b7522`). Added `role: Role` to
|
||||||
|
`PublicUser` in `@solelog/shared`; `apps/api/src/routes/me.ts` now returns the session
|
||||||
|
user's role (default `'worker'`). Added `http://localhost:5174` (admin dev origin) to the
|
||||||
|
default `WEB_ORIGINS` in `env.ts` + `.env.example` (it drives both `hono/cors` and
|
||||||
|
better-auth `trustedOrigins`). Extended `me.test.ts` and `cors.test.ts`. Worker client
|
||||||
|
ignores the extra field — no worker change needed.
|
||||||
|
- **Task 2 — Scaffold `apps/admin`** (`682a9dc`). New `@solelog/admin` workspace mirroring
|
||||||
|
`apps/worker`'s toolchain (Vite 7, React 18.3, react-router 6, react-query 5, vitest 3,
|
||||||
|
TS 5.7). Copied `lib/api.ts` + `lib/auth-storage.ts` verbatim (shared `solelog.token`
|
||||||
|
key; separate localStorage because the admin runs on a different port). Dev port **5174**.
|
||||||
|
- **Task 3 — Auth context + admin gate + Login** (`77659ed`). `auth/AuthContext.tsx`:
|
||||||
|
`signIn` calls the worker `signIn`, fetches `/api/me`, and rejects non-admins
|
||||||
|
(`clearToken()` + throw) so `Login` shows "Geen toegang — alleen beheerders."; other
|
||||||
|
failures show "Inloggen mislukt".
|
||||||
|
- **Task 4 — Sidebar shell + routing** (`286e2d2`). Left-sidebar shell: brand "SoleLog
|
||||||
|
Admin", `NavLink`s **Live** / **Handelingen**, a muted "Binnenkort" group (Rapporten /
|
||||||
|
Gebruikers / Handmatig) hinting 3b, and a header strip with the signed-in email + an
|
||||||
|
Uitloggen button (`aria-label="Uitloggen"`).
|
||||||
|
- **Task 5 — Live active-work view** (`67dd0d3`). `useActiveSessions()` polls
|
||||||
|
`/api/admin/sessions/active` with `refetchInterval: 5000`; `lib/elapsed.ts` `formatTime`
|
||||||
|
ported verbatim from the worker stopwatch. One card per session (worker name, activity,
|
||||||
|
insole-type pill, pair count, ticking elapsed timer). Header "Actief nu (N)"; empty state
|
||||||
|
"Niemand is nu aan het werk.".
|
||||||
|
- **Task 6 — Activity management** (`c0d9d21`). Ported the legacy worker `Settings.tsx`
|
||||||
|
(git `decb158`) near-verbatim: `api/activities.ts` hooks (`useActivities` +
|
||||||
|
create/update/delete, all invalidating `['activities']`) and `screens/Activities.tsx`
|
||||||
|
(add form with insole-type toggles, list with inline edit, delete-with-confirm). Title
|
||||||
|
"Handelingen".
|
||||||
|
- **Task 7 — Docs, lint, verification** (this task). Lint/format clean, full green matrix,
|
||||||
|
live smoke, docs.
|
||||||
|
|
||||||
|
## Verification (Task 7)
|
||||||
|
|
||||||
|
- `npx oxlint` — clean (exit 0).
|
||||||
|
- `npx oxfmt --list-different` over the phase-3a files — nothing to change (all formatted).
|
||||||
|
- `yarn workspace @solelog/api typecheck` — pass; `test` — **46 passed** (11 files).
|
||||||
|
- `yarn workspace @solelog/admin typecheck` — pass; `test` — **14 passed** (5 files);
|
||||||
|
`build` — pass (`vite build`, 89 modules).
|
||||||
|
- `yarn workspace @solelog/worker test` (regression) — **22 passed** (7 files).
|
||||||
|
- **Live smoke** (API started, seeded, then server tree killed + port 3000 freed):
|
||||||
|
- admin sign-in → `GET /api/me` → `{ ..., "role": "admin" }`.
|
||||||
|
- `GET /api/admin/sessions/active` with the admin bearer → `[]`, HTTP 200.
|
||||||
|
- worker sign-in → `GET /api/me` → `{ ..., "role": "worker" }`.
|
||||||
|
|
||||||
|
## Outcome
|
||||||
|
|
||||||
|
Phase 3a is implemented and green. An admin can sign in to the admin app, see who is
|
||||||
|
working right now (5s auto-refresh, read-only), and add/edit/delete handelingen; a worker
|
||||||
|
who signs in with valid credentials is rejected with "Geen toegang". Roadmap Phase 3 status
|
||||||
|
updated to "3a implemented; 3b remaining"; `apps/admin/README.md` filled.
|
||||||
|
|
||||||
|
## Next (Phase 3b)
|
||||||
|
|
||||||
|
- Reports + all-users filtered CSV export (current `/api/export` is self-scoped).
|
||||||
|
- User management via better-auth `/api/auth/admin/*`.
|
||||||
|
- Manual session entry/edit + admin stop/fix of another worker's session (needs new
|
||||||
|
backend endpoints).
|
||||||
Reference in New Issue
Block a user