Replace hardcoded light-theme inline colors (#fff/#f8f9fa/#e8f5e9/#888…)
with semantic CSS classes that consume the existing design tokens
(--bg-card/--ember/--ember-soft/--accent/--text-dim/--steel/etc.). Fixes
the unreadable role names, faded checkmark cards, and washed-out success
banner — everything was rendering "white-on-cream" against the dark
Foundry Deck background.
Visual structure now:
- two-column grid (sidebar 280px / detail flex)
- role-card: dark surface, ember-soft + 3px ember edge + glow on selected,
Saira Condensed display name + mono permission count
- perm-group card with dashed steel header
- perm-item: bg-sink default; ember-soft + accent border + ember-tint check
glyph on checked; native checkbox restyled with the ember palette
- banner pill: success-green or danger-red token, mono text
- create-role card: ember left-edge, mono uppercase labels
- delete uses .btn-danger (already in the token set)
No state/logic changes — same fetch + toggle + save flow.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
api.ts + useAuthConfig kept the old wizard-era localStorage path for the
backend URL, which is why fresh browsers saw blank pages on MonitorPage
and any other api-using page after the v0.4.0 wizard removal — without
the wizard pre-seeding localStorage, getApiBase() returned undefined and
axios called same-origin (frontend nginx, 404).
App.tsx getApiBase() partially worked because I had only refactored that
one path; api.ts (used by everything else) still had its own duplicate
getApiBase that I missed.
This commit removes the localStorage path entirely from all 3 read sites
(api.ts / App.tsx / useAuthConfig.ts) — single source of truth is now
the build-time env baked in by the Dockerfile ARG.
- src/services/api.ts: const API_BASE = import.meta.env.VITE_HF_BACKEND_BASE_URL
- src/App.tsx: const API_BASE = ... (replaces getApiBase())
- src/hooks/useAuthConfig.ts: const BACKEND_BASE = ...
- src/test/setup.ts: stub import.meta.env instead of localStorage key
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Frontend no longer has any wizard flow. Backend URL is baked into the bundle
at build time via VITE_HF_BACKEND_BASE_URL (forwarded as a Dockerfile ARG
from compose).
- src/App.tsx: drop SetupWizardPage import + appState='setup' fallback +
HF_WIZARD_PORT-via-localStorage probe. getApiBase() now reads
import.meta.env.VITE_HF_BACKEND_BASE_URL with localStorage as an escape
hatch for dev. When /config/status reports no admin yet, show a card
prompting the operator to run `docker exec hf_backend hf-cli admin
create-user ...`.
- src/pages/SetupWizardPage.tsx: deleted (~250 lines)
- src/index.css: drop .setup-wizard + .setup-* styles (~36 lines)
- src/vite-env.d.ts: add VITE_HF_BACKEND_BASE_URL to ImportMetaEnv
- Dockerfile: ARG VITE_HF_BACKEND_BASE_URL → ENV → npm run build
Build the prod image with:
docker build --build-arg VITE_HF_BACKEND_BASE_URL=https://hf-api.hangman-lab.top \
-t git.hangman-lab.top/zhi/harborforge-frontend:latest .
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the ⚓ emoji with a real logo image used as the in-app brand
mark and the favicon. Default bundled public/logo.svg is the
HangmanLab mark recolored to the Foundry-Deck ember (#ff6a1a).
Override at deploy time via HARBORFORGE_LOGO_URL (injected into
runtime-config.js; getLogoUrl() + favicon swap), no rebuild needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OIDC settings page + setup wizard now configure the bootstrap admin
role instead of a hand-typed OIDC subject. The OIDC-only admin link is
handled automatically by the backend admin-role auto-connect on first
sign-in (explained inline in both the wizard and settings page).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Solves the OIDC-only bootstrap lockout (admin can't reach the in-app
OIDC settings page when password login is disabled and OIDC is unset).
- Frontend image entrypoint injects /runtime-config.js from the
deploy-time HARBORFORGE_OIDC_ONLY env so the SPA knows the mode
before the backend exists.
- Setup wizard gains an "OIDC" step (between Admin and Backend):
required when OIDC-only (incl. admin's OIDC subject so the bootstrap
admin can sign in), optional otherwise; written into harborforge.json.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Non-admins fall through to the catch-all redirect instead of seeing
the OIDC settings page shell. Sidebar link, in-page guard and the
admin-only backend API remain as defense in depth.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New admin page /settings/oidc to configure the OIDC provider (issuer,
client id/secret, redirect/callback URL, scopes, post-login redirect).
Prominently shows the callback URL to register at the IdP, current
status/source, and the read-only deploy-level OIDC-only flag. Secret
is write-only (blank = keep). Sidebar entry for admins.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- useAuthConfig fetches public /auth/config; LoginPage hides the
password form when oidc_only and shows an SSO button when enabled.
- /oidc/callback route applies the returned JWT (sign-in) or shows the
link result; oidc_error surfaced on LoginPage.
- UsersPage: hides password fields in OIDC-only mode; admin OIDC
bind/unbind UI per user. Sidebar self-service "Link OIDC account"
(non-OIDC_ONLY).
- Dockerfile ARG/ENV HARBORFORGE_OIDC_ONLY.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
- New CalendarPage with daily view (date navigation, slot list) and plans tab
- Route /calendar added in App.tsx
- Sidebar entry added after Proposals
- Daily view: shows time slots with type, status, priority, duration, event data
- Distinguishes real vs virtual (plan) slots visually
- Plans tab: shows schedule plan rules with schedule parameters
- Remove 'story' type from TASK_TYPES in CreateTaskPage.tsx and CreateTaskModal.tsx
- All story/* task types now only creatable via Proposal Accept workflow
- Added comments explaining the restriction
- Add Essential and EssentialType types to frontend types
- Add essentials state and CRUD handlers in ProposalDetailPage
- Display Essential list with type, code, title
- Handle empty state (show message when no essentials)
- Add create/edit modal for Essentials
- Only show edit/delete buttons for open proposals
- Milestone navigation now uses milestone_code instead of numeric id
- MilestoneDetailPage uses milestone.id (numeric) for project-scoped API calls
- App.tsx checks /config/status on backend first, falls back to wizard
- Added milestone_code to Milestone type
- Fixed MilestoneDetailPage to use fetched milestone.id for sub-queries
- Fix: ProjectDetailPage now uses /projects/{id}/milestones instead of /milestones?project_id={code} (fixes 422)
- Add: Reset API Key button on UsersPage with permission-based visibility
- Add: One-time key display with copy-to-clipboard
- Protect admin and acc-mgr accounts from deletion in UI
- New CopyableCode component with monospace styling and click-to-copy
- TaskDetailPage: copyable task code in header
- ProjectDetailPage: copyable project code in header
- MilestoneDetailPage: copyable milestone code in header
- MeetingDetailPage: copyable meeting code in header
- SupportDetailPage: copyable support code in header
- ProposeDetailPage: copyable propose code in header and info section
Helps users easily copy resource codes for CLI usage (hf task get <code>, etc)
- Add routes for /meetings/:meetingId and /supports/:supportId in App.tsx
- Fix MilestoneDetailPage to navigate to code-first detail URLs
- Update table headers from '#' to 'Code' for supports/meetings lists
- Fix TypeScript types for supports/meetings (use any[] instead of Task[])
- MeetingDetailPage: full detail view with attend, transition, edit, delete
- SupportDetailPage: full detail view with take, transition, edit, delete
- DashboardPage: show task_code instead of #id, link via code
- TasksPage: navigate to tasks via task_code
- MilestoneDetailPage: navigate to tasks/supports/meetings via codes
- MilestoneDetailPage: display codes in support/meeting tables
- Fix support/meeting state types to any[] for code property access
- TaskDetailPage: show task_code instead of raw #id in header
- TaskDetailPage: show author_username in comment metadata
- ProjectDetailPage: show member username instead of User #id
- ProposeDetailPage: show created_by_username instead of User #id
- TasksPage: fix status filter options to match actual statuses
(pending/open/undergoing/completed/closed)
- TasksPage: fix status color map for correct status values
- Types: add username/full_name to ProjectMember, author_username
to Comment, created_by_username to Propose
- Supports TODO §3.1 (code-first UI migration)