# Frontend — Vue 3 SPA Single-page application for the PhD-research demo site. Vue 3 + Vite + Pinia + Fomantic UI; talks to the Django backend at `/api/v1/*`. Built once into `dist/` at image-build time and served by WhiteNoise on the same origin as the API in production. ## Tech stack - **Vue 3** (`^3.5.32`) — composition-API components. - **Vite 8** — dev server and production bundler. - **Vue Router 4** — `createWebHistory` mode, lazy-loaded demo views, wildcard 404. - **Pinia 3** — store-per-feature. - **axios** — JSON HTTP client for discovery / non-streaming endpoints. - **Fomantic UI CSS** — visual primitives (buttons, segments, dropdowns, etc.). ## Quick start From this directory: ```bash npm install npm run dev ``` Vite serves on `http://localhost:5173`. The Django backend must also be running on `http://localhost:8000` (see [the local-development guide](../../docs/guides/local-development.md)). Production build: ```bash npm run build # emits dist/, copied into the Docker image at /app/backend/dist ``` ## Project layout ``` src/frontend/src/ ├── main.js # bootstrap (Pinia, router, theme) ├── App.vue # shell: NavBar, RouterView, GraphBackground ├── api/ # axios client + per-method API wrappers │ ├── client.js │ ├── coins.js │ ├── graphGeneration.js │ ├── kgAnomaly.js │ └── health.js ├── composables/ │ └── useSseStream.js # SSE client for streaming inference ├── stores/ # Pinia stores │ ├── coinsDemo.js │ ├── multiproxanDemo.js │ ├── kgAnomalyDemo.js │ └── theme.js ├── router/index.js # routes + lazy demo loading + wildcard 404 ├── views/ # page-level components │ ├── HomeView.vue │ ├── CVView.vue │ ├── NotFoundView.vue │ └── demos/{Coins,MultiProxAn,KgAnomaly}View.vue ├── components/ # feature-grouped components (coins/, multiproxan/, kganomaly/, cv/, home/, layout/, common/, background/) ├── data/cv.js # static CV data └── styles/ # tokens, theme, responsive helpers ``` For a full walk-through of every store, composable and component group, see [`docs/reference/frontend-modules.md`](../../docs/reference/frontend-modules.md). ## Environment variables | File | Purpose | Value | |---|---|---| | `.env.development` | Dev — Vite reads this when running `npm run dev`. | `VITE_API_BASE_URL=http://localhost:8000` | | `.env.production` | Prod — Vite reads this when running `npm run build`. | `VITE_API_BASE_URL=` (empty → same-origin) | Both `src/api/client.js` and `src/composables/useSseStream.js` compute their base URL as: ```js `${import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:8000'}/api/v1` ``` The `??` (not `||`) is intentional — an empty string from `.env.production` must survive so the deployed SPA hits a same-origin `/api/v1`. ## API conventions - Discovery endpoints return JSON; the wrappers in `src/api/*.js` parse the response and return `data` directly. - Streaming endpoints (`/graph-generation/{generate,continue}`, `/kg-anomaly/{correct,continue}`) are consumed via `useSseStream`. Stores wire its `onProgress` / `onPreview` / `onResult` callbacks to mutate state. - Errors come back as `{ error: { code, message, details } }`. `apiError(error)` (in `client.js`) extracts that envelope from an axios error. The exact event protocol is in [`docs/reference/sse-protocol.md`](../../docs/reference/sse-protocol.md). ## Routing | Path | Name | View | |---|---|---| | `/` | `home` | `HomeView.vue` | | `/cv` | `cv` | `CVView.vue` | | `/demos/coins` | `demo-coins` | `CoinsView.vue` (lazy) | | `/demos/multiproxan` | `demo-multiproxan` | `MultiProxAnView.vue` (lazy) | | `/demos/kganomaly` | `demo-kganomaly` | `KgAnomalyView.vue` (lazy) | | `/:pathMatch(.*)*` | `not-found` | `NotFoundView.vue` (Lara Croft 404) | In production, the Django backend serves `dist/index.html` for any non-API path so the SPA's history mode keeps working on hard refreshes. ## Theming `stores/theme.js` toggles a `data-theme` attribute on `` between `light` and `dark`, persists the choice to `localStorage`, and falls back to `prefers-color-scheme`. The actual colors live in `styles/tokens.css` and `styles/theme.css` as CSS custom properties. ## Background animation `components/background/GraphBackground.vue` renders the floating-graph motif behind every page (the project's visual signature). It's mounted once in `App.vue` and respects `prefers-reduced-motion`. ## See also - [`docs/reference/frontend-modules.md`](../../docs/reference/frontend-modules.md) — full module reference. - [`docs/reference/sse-protocol.md`](../../docs/reference/sse-protocol.md) — the streaming protocol the demo stores parse. - [`docs/guides/local-development.md`](../../docs/guides/local-development.md) — running both servers together. - [`docs/explanation/architecture.md`](../../docs/explanation/architecture.md) — how the SPA is served by the deployed container.