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:
85
src/types.ts
Normal file
85
src/types.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
// Mirrors the Dialectic.Backend models — kept in sync by hand. If a
|
||||
// field is added on the backend (models/topic.go / store responses),
|
||||
// also add it here so the UI can use it.
|
||||
|
||||
export type TopicStatus =
|
||||
| 'created'
|
||||
| 'signup_open'
|
||||
| 'signup_closed'
|
||||
| 'debating'
|
||||
| 'completed'
|
||||
| 'cancelled';
|
||||
|
||||
export type Visibility = 'public' | 'private';
|
||||
|
||||
export type Camp = 'pro' | 'con' | 'judge';
|
||||
|
||||
export interface Topic {
|
||||
id: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
visibility: Visibility;
|
||||
verdict_schema_id: string;
|
||||
status: TopicStatus;
|
||||
signup_open_at: string;
|
||||
signup_close_at: string;
|
||||
debate_start_at: string;
|
||||
debate_end_at: string;
|
||||
creator_user_id: string;
|
||||
visibility_changed_by?: string | null;
|
||||
visibility_changed_at?: string | null;
|
||||
cancelled_reason?: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface CampAllocation {
|
||||
id: string;
|
||||
topic_id: string;
|
||||
camp: Camp;
|
||||
agent_id: string;
|
||||
allocated_at: string;
|
||||
}
|
||||
|
||||
// GET /api/topics/{id} returns the full Topic spread + `camps` sibling.
|
||||
export interface TopicDetail extends Topic {
|
||||
camps: CampAllocation[] | null;
|
||||
}
|
||||
|
||||
export interface Argument {
|
||||
id: string;
|
||||
topic_id: string;
|
||||
round_id: string;
|
||||
camp: Camp;
|
||||
agent_id: string;
|
||||
content: string;
|
||||
posted_at: string;
|
||||
}
|
||||
|
||||
export interface Verdict {
|
||||
id: string;
|
||||
topic_id: string;
|
||||
judge_agent_id: string;
|
||||
// verdict shape depends on the topic's verdict_schema_id; UI shows raw JSON.
|
||||
verdict: Record<string, unknown>;
|
||||
rationale: string;
|
||||
tokens_input: number;
|
||||
tokens_output: number;
|
||||
produced_at: string;
|
||||
}
|
||||
|
||||
// Admin endpoint (next commit) shape — declare now so frontend compiles.
|
||||
export interface AgentSummary {
|
||||
agent_id: string;
|
||||
key_provisioned: boolean;
|
||||
signups_count: number;
|
||||
arguments_count: number;
|
||||
verdicts_count: number;
|
||||
recent_topics: Array<{
|
||||
topic_id: string;
|
||||
title: string;
|
||||
status: TopicStatus;
|
||||
role: Camp | 'volunteer';
|
||||
last_action_at: string;
|
||||
}>;
|
||||
}
|
||||
Reference in New Issue
Block a user