Add web GUI: FastAPI API + Vue 3 frontend with dark theme
API (web/api.py):
GET /api/projects, /api/projects/{id}, /api/tasks/{id}
GET /api/decisions?project=X, /api/cost?days=7, /api/support/tickets
POST /api/projects, /api/tasks, /api/decisions, /api/bootstrap
CORS for localhost:5173, all queries via models.py
Frontend (web/frontend/):
Vue 3 + TypeScript + Vite + Tailwind CSS v3
Dashboard: project cards with task counters, cost, status badges
ProjectView: tabs for Tasks/Decisions/Modules with filters
Modals: Add Project, Add Task, Add Decision, Bootstrap
Dark theme, monospace font, minimal clean design
Startup:
API: cd web && uvicorn api:app --reload --port 8420
Web: cd web/frontend && npm install && npm run dev
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b95db7c7d6
commit
86e5b8febf
21 changed files with 3386 additions and 1 deletions
95
web/frontend/src/api.ts
Normal file
95
web/frontend/src/api.ts
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
const BASE = 'http://localhost:8420/api'
|
||||
|
||||
async function get<T>(path: string): Promise<T> {
|
||||
const res = await fetch(`${BASE}${path}`)
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
async function post<T>(path: string, body: unknown): Promise<T> {
|
||||
const res = await fetch(`${BASE}${path}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (!res.ok) throw new Error(`${res.status} ${res.statusText}`)
|
||||
return res.json()
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
id: string
|
||||
name: string
|
||||
path: string
|
||||
status: string
|
||||
priority: number
|
||||
tech_stack: string[] | null
|
||||
created_at: string
|
||||
total_tasks: number
|
||||
done_tasks: number
|
||||
active_tasks: number
|
||||
blocked_tasks: number
|
||||
}
|
||||
|
||||
export interface ProjectDetail extends Project {
|
||||
tasks: Task[]
|
||||
modules: Module[]
|
||||
decisions: Decision[]
|
||||
}
|
||||
|
||||
export interface Task {
|
||||
id: string
|
||||
project_id: string
|
||||
title: string
|
||||
status: string
|
||||
priority: number
|
||||
assigned_role: string | null
|
||||
parent_task_id: string | null
|
||||
brief: Record<string, unknown> | null
|
||||
spec: Record<string, unknown> | null
|
||||
created_at: string
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface Decision {
|
||||
id: number
|
||||
project_id: string
|
||||
task_id: string | null
|
||||
type: string
|
||||
category: string | null
|
||||
title: string
|
||||
description: string
|
||||
tags: string[] | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface Module {
|
||||
id: number
|
||||
project_id: string
|
||||
name: string
|
||||
type: string
|
||||
path: string
|
||||
description: string | null
|
||||
owner_role: string | null
|
||||
dependencies: string[] | null
|
||||
}
|
||||
|
||||
export interface CostEntry {
|
||||
project_id: string
|
||||
project_name: string
|
||||
runs: number
|
||||
total_tokens: number
|
||||
total_cost_usd: number
|
||||
total_duration_seconds: number
|
||||
}
|
||||
|
||||
export const api = {
|
||||
projects: () => get<Project[]>('/projects'),
|
||||
project: (id: string) => get<ProjectDetail>(`/projects/${id}`),
|
||||
cost: (days = 7) => get<CostEntry[]>(`/cost?days=${days}`),
|
||||
createProject: (data: { id: string; name: string; path: string; tech_stack?: string[]; priority?: number }) =>
|
||||
post<Project>('/projects', data),
|
||||
createTask: (data: { project_id: string; title: string; priority?: number; route_type?: string }) =>
|
||||
post<Task>('/tasks', data),
|
||||
bootstrap: (data: { path: string; id: string; name: string }) =>
|
||||
post<{ project: Project }>('/bootstrap', data),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue