From 7328057e7dbfeebef6c7ddfcdc71b19eacd95725 Mon Sep 17 00:00:00 2001 From: openclaw Date: Mon, 23 Mar 2026 17:15:30 +0000 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20add=20shared=20layer=20?= =?UTF-8?q?=E2=80=94=20types,=20API=20client,=20router,=20app=20layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- frontend/src/App.vue | 15 +++ frontend/src/env.d.ts | 5 + frontend/src/main.ts | 10 ++ frontend/src/router/index.ts | 12 ++ frontend/src/shared/api.ts | 3 + frontend/src/shared/types/api.ts | 198 +++++++++++++++++++++++++++++++ frontend/src/style.css | 83 +++++++++++++ frontend/src/vite-env.d.ts | 1 + 8 files changed, 327 insertions(+) create mode 100644 frontend/src/env.d.ts create mode 100644 frontend/src/style.css create mode 100644 frontend/src/vite-env.d.ts diff --git a/frontend/src/App.vue b/frontend/src/App.vue index e69de29..d687428 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -0,0 +1,15 @@ + + + diff --git a/frontend/src/env.d.ts b/frontend/src/env.d.ts new file mode 100644 index 0000000..2b97bd9 --- /dev/null +++ b/frontend/src/env.d.ts @@ -0,0 +1,5 @@ +declare module '*.vue' { + import type { DefineComponent } from 'vue' + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/frontend/src/main.ts b/frontend/src/main.ts index e69de29..501dee0 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -0,0 +1,10 @@ +import { createApp } from 'vue' +import { createPinia } from 'pinia' +import App from './App.vue' +import router from './router' +import './style.css' + +const app = createApp(App) +app.use(createPinia()) +app.use(router) +app.mount('#app') diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index e69de29..7c742de 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -0,0 +1,12 @@ +import { createRouter, createWebHistory } from 'vue-router' + +const router = createRouter({ + history: createWebHistory(), + routes: [ + { path: '/', component: () => import('@/modules/project/components/ProjectList.vue') }, + { path: '/projects/:id', component: () => import('@/modules/graph/components/GraphPanorama.vue') }, + { path: '/projects/:id/editor', component: () => import('@/modules/editor/components/EditorPage.vue') }, + ], +}) + +export default router diff --git a/frontend/src/shared/api.ts b/frontend/src/shared/api.ts index e69de29..a60e00f 100644 --- a/frontend/src/shared/api.ts +++ b/frontend/src/shared/api.ts @@ -0,0 +1,3 @@ +import axios from 'axios' +const api = axios.create({ baseURL: '/api' }) +export default api diff --git a/frontend/src/shared/types/api.ts b/frontend/src/shared/types/api.ts index e69de29..43fa19d 100644 --- a/frontend/src/shared/types/api.ts +++ b/frontend/src/shared/types/api.ts @@ -0,0 +1,198 @@ +export interface Project { + id: string + name: string + design_dir: string + code_dir: string | null + created_at: string +} + +export interface FileStatusEntry { + path: string + status: string + content_lines: number +} + +export interface ScanSummary { + total_files: number + ok: number + sparse: number + missing: number + placeholder_heavy: number + template_residue: number +} + +export interface ScanResult { + project_id: string + scanned_at: string + file_statuses: FileStatusEntry[] + summary: ScanSummary +} + +export interface GraphNode { + id: string + type: string + label: string + status: string + group_id: string +} + +export interface GraphEdge { + source: string + target: string + relation: string +} + +export interface GraphGroup { + id: string + label: string + layer: string +} + +export interface GraphView { + nodes: GraphNode[] + edges: GraphEdge[] + groups: GraphGroup[] +} + +export interface Capability { + capability_id: string + name: string + description: string + priority: string + phase: string + related_value_flows: string[] +} + +export interface Module { + module_id: string + name: string + layer: string + description: string + phase: string + depends_on: string[] + capabilities: string[] +} + +export interface Entity { + entity_id: string + name: string + domain: string + owner_module: string + description: string + phase: string + source_file: string +} + +export interface Integration { + integration_id: string + source: string + target: string + target_type: string + direction: string + protocol: string + trigger: string + phase: string + description: string +} + +export interface ValueFlow { + value_flow_id: string + name: string + trigger: string + actor: string + steps: string + outcome: string + phase: string + related_capabilities: string[] +} + +export interface UserJourney { + journey_id: string + name: string + actor: string + precondition: string + steps: string + postcondition: string + phase: string + related_value_flows: string[] +} + +export interface DataFlow { + data_flow_id: string + source: string + target: string + data_content: string + trigger: string + protocol: string + phase: string + description: string +} + +export interface ExternalSystem { + system_id: string + name: string + type: string + protocol: string + direction: string + phase: string + description: string +} + +export interface TraceabilityLink { + trace_id: string + capability_id: string + module_id: string + entity_ids: string[] + value_flow_ids: string[] + notes: string +} + +export interface RuntimeComponent { + component_id: string + name: string + type: string + technology: string + port: string +} + +export interface EditableFile { + path: string + format: string + content: string + last_modified: string +} + +export interface AffectedFile { + path: string + reason: string +} + +export interface ImpactResult { + source_file: string + affected_files: AffectedFile[] +} + +export interface ImplProgress { + module_id: string + percentage: number + source: string + evaluated_at: string +} + +export interface CapabilityDetail { + capability: Capability + modules: Module[] + value_flows: ValueFlow[] +} + +export interface ModuleDetail { + module: Module + entities: Entity[] + integrations: Integration[] + codebase_alignment: Record | null +} + +export interface EntityDetail { + entity: Entity + data_flows: DataFlow[] +} diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..ef40b86 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,83 @@ +:root { + --sidebar-width: 260px; + --color-primary: #1976D2; + --color-bg: #f5f5f5; + --color-sidebar: #fff; + --color-border: #e0e0e0; + --color-ok: #4CAF50; + --color-sparse: #FFC107; + --color-missing: #F44336; + --color-template-residue: #FF9800; + --color-placeholder-heavy: #9C27B0; + --color-unknown: #9E9E9E; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background: var(--color-bg); + color: #333; +} + +.app-layout { + display: grid; + grid-template-columns: var(--sidebar-width) 1fr; + min-height: 100vh; +} + +.sidebar { + background: var(--color-sidebar); + border-right: 1px solid var(--color-border); + padding: 16px; + overflow-y: auto; +} + +.sidebar h2 { + font-size: 16px; + margin-bottom: 16px; + color: var(--color-primary); +} + +.content { + padding: 24px; + overflow-y: auto; +} + +button { + cursor: pointer; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 14px; +} + +button.primary { + background: var(--color-primary); + color: white; +} + +button.danger { + background: var(--color-missing); + color: white; +} + +input, textarea { + border: 1px solid var(--color-border); + border-radius: 4px; + padding: 8px; + font-size: 14px; + width: 100%; +} + +.card { + background: white; + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 16px; + margin-bottom: 12px; +} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1 @@ +///