feat(frontend): v2 rewrite — Vite + React + TS readonly SPA
Replaces the v1 CRA app (which targeted the obsolete Python Dialectic
backend) with a fresh Vite + React 18 + TypeScript scaffold that talks
to Dialectic.Backend Go v2.
Pages (all readonly — propose/signup/post are agent-only by design):
- / TopicList — filter by status, paginated
- /topics/:id TopicDetail — meta + camps + transcript
(polling every 8s)
- /topics/:id/verdict Verdict permalink (shareable)
- /agents/:id AgentActivity — admin diagnostics card
Stack:
- Vite 5 + React 18 + react-router-dom 6
- Pure ESM, NodeNext-style imports, .tsx
- Style: ~/STYLE.md tokens (IBM Plex Mono + Major Mono Display +
--acid #d8ff3e on --ink #080a0d, with subtle blueprint grid wash)
Auth:
- v1 dev-bypass only — VITE_OIDC_DEV_BYPASS auto-attaches
x-dev-bypass header. Real Keycloak OIDC redirect ships as v2.
- Admin endpoints (x-dialectic-admin-key) prompt on first visit
and store key in localStorage. Never baked into bundle. Never
sent to non-admin endpoints.
Backend pairing:
- Dialectic.Backend@0b16b52 adds GET /api/admin/agents/{id} for the
AgentActivity page. AgentActivity calls it via the admin-key
branch in api.ts.
Deploy:
- Multi-stage Dockerfile (node:22-alpine build → nginx:1.27-alpine
serve). nginx.conf reverse-proxies /api/ → dialectic-backend:8090
so the browser sees one origin (no CORS).
Reuses the existing hzhang/Dialectic.Frontend repo — old CRA contents
nuked in this commit. History preserved on master.
This commit is contained in:
54
src/App.tsx
Normal file
54
src/App.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { NavLink, Route, Routes } from 'react-router-dom';
|
||||
import { useAuth } from './auth';
|
||||
import { TopicListPage } from './pages/TopicList';
|
||||
import { TopicDetailPage } from './pages/TopicDetail';
|
||||
import { VerdictPage } from './pages/Verdict';
|
||||
import { AgentActivityPage } from './pages/AgentActivity';
|
||||
import { NotFoundPage } from './pages/NotFound';
|
||||
import './styles/app.css';
|
||||
|
||||
export function App() {
|
||||
const { user, devBypass } = useAuth();
|
||||
return (
|
||||
<div className="app">
|
||||
<header className="app-header">
|
||||
<div className="app-header-inner">
|
||||
<a href="/" className="app-brand brand-d">
|
||||
dialectic
|
||||
</a>
|
||||
<nav className="app-nav">
|
||||
<NavLink to="/" end>
|
||||
topics
|
||||
</NavLink>
|
||||
<NavLink to="/agents/recruiter">agents</NavLink>
|
||||
</nav>
|
||||
<div className="app-user">
|
||||
{user ? (
|
||||
<span className="eyebrow">{user.label}</span>
|
||||
) : (
|
||||
<span className="eyebrow">readonly</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{devBypass && (
|
||||
<div className="app-dev-banner">
|
||||
DEV BYPASS ACTIVE — auto-attaching <code>x-dev-bypass</code> on
|
||||
every request; do not run this build in production
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
<main className="app-main">
|
||||
<Routes>
|
||||
<Route path="/" element={<TopicListPage />} />
|
||||
<Route path="/topics/:id" element={<TopicDetailPage />} />
|
||||
<Route path="/topics/:id/verdict" element={<VerdictPage />} />
|
||||
<Route path="/agents/:id" element={<AgentActivityPage />} />
|
||||
<Route path="*" element={<NotFoundPage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
<footer className="app-footer">
|
||||
<span className="eyebrow">hangman lab · dialectic v2</span>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user