website / src /frontend /README.md
Andrej Janchevski
docs: add technical documentation set
175b650
# 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 `<html>` 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.