Vue 3 + Vite 8 + TS + PrimeVue 4 (Nora/Industrial) + Pinia + vue-i18n SPA, served by the SPRO gateway as a static page-module under /module/temper_overview and embedded into Grafana via iframe. - Ports the LOCKED design #4 "Shop-floor Schematic": one connected line Pressen -> (cyan) -> Ofen -> (green) -> Fertig; carts under their origin press; oven trays as dwell-progress bars; finished band; range picker scoping only the Fertig figures. Flow/order colours match PressV (fleet recognisability). - Python page-module wrapper: register(app) mounts committed dist/ + a no-auth read API (GET /api/overview). Kernel DB seam in _db.py. - Backend returns SAMPLE data; frontend falls back to bundled sample so `npm run dev` renders with zero backend (TODO markers point at the real temper aggregates). - Rich README bakes in the design, contract, wiring + Grafana embedding for standalone development. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
154 lines
7.0 KiB
Markdown
154 lines
7.0 KiB
Markdown
# SPA_TemperProductionOverview — Temper-Linie · Live-Übersicht
|
||
|
||
A **login-free, read-only** whole-line temper overview, built as a Vue 3 SPA that
|
||
the **SPRO Universal Gateway** serves as a static page-module under
|
||
`/module/temper_overview/` and that is **embedded into Grafana via an `<iframe>`**.
|
||
|
||
> **Standalone orientation.** This repo is developed on its own (like `FastPress`,
|
||
> `PressV`, `sp-ui-kit` — sibling repos), but it only comes fully alive when mounted
|
||
> into the gateway as a git submodule at `modules/temper_overview/`. This README
|
||
> bakes in everything a fresh session needs — you shouldn't need the gateway repo
|
||
> open to work on the frontend.
|
||
|
||
Board it renders (dark, one connected line): **Pressen → (cyan ↓) → Ofen →
|
||
(green ↓) → Fertig**, carts hanging under their origin press, oven trays whose
|
||
background fills with dwell progress, a finished band, and a time-range picker that
|
||
scopes only the "Fertig" figures. Run `npm run dev` to see it.
|
||
|
||
---
|
||
|
||
## Quick start (standalone dev)
|
||
|
||
```bash
|
||
# 1) auth for the private @sp-ui-kit design-token package (one-time)
|
||
cp .npmrc.example .npmrc # then paste a Gitea PAT with "package: Read" scope
|
||
|
||
# 2) install + run
|
||
npm install
|
||
npm run dev # http://localhost:5173/module/temper_overview/
|
||
```
|
||
|
||
With **no gateway backend running**, the store falls back to **bundled sample data**
|
||
(`src/data/sample.ts`), so `npm run dev` shows the full design immediately (a
|
||
`DEMO-DATEN` tag appears in the header). `npm run build` → `dist/` (committed).
|
||
|
||
---
|
||
|
||
## How it's wired into the gateway (page-module contract)
|
||
|
||
This is a gateway module per `agent_docs/vue-page-modules.md` in the gateway repo.
|
||
The **repo root is a Python package** (`__init__.py`) so the kernel's module loader
|
||
can `import temper_overview` and call `register(app)`:
|
||
|
||
| File | Role |
|
||
|---|---|
|
||
| `__init__.py` | `register(app)` — mounts `dist/` at `MODULE_PREFIX` + includes the API router |
|
||
| `_db.py` | kernel DB seam (`run_select_query`) — used once real SQL is wired |
|
||
| `api/routes.py` | the no-auth read API (`GET /module/temper_overview/api/overview`) |
|
||
| `dist/` | **committed** build output — the gateway serves this (deploy host has no Node) |
|
||
| `src/` etc. | the Vue app source |
|
||
|
||
**URL prefix lives in two places that MUST match** — change both together:
|
||
`vite.config.ts` → `base: '/module/temper_overview/'` and `__init__.py` →
|
||
`MODULE_PREFIX = "/module/temper_overview"`. The frontend derives its API base from
|
||
`import.meta.env.BASE_URL`, so it follows the prefix automatically.
|
||
|
||
Add it to a gateway checkout:
|
||
|
||
```bash
|
||
# in the gateway repo (fastAPI)
|
||
git submodule add https://git.sprodat.eu/Erik/SPA_TemperProductionOverview.git modules/temper_overview
|
||
# MODULES_ENABLED in .env must include temper_overview (or be empty = load all)
|
||
```
|
||
|
||
Restart the gateway; the log shows `loaded module: temper_overview`. Smoke-test
|
||
(mind the trailing slash): `http://localhost:8800/module/temper_overview/` and
|
||
`…/api/overview`.
|
||
|
||
> **Deploy rule:** always `npm run build` and **commit `dist/`** when the app
|
||
> changes, then bump the submodule pin in the gateway repo. Never build on the
|
||
> gateway host.
|
||
|
||
---
|
||
|
||
## Data API contract
|
||
|
||
`GET /module/temper_overview/api/overview?range=12h|1d|7d|14d|30d` → one JSON
|
||
payload for the whole line. The TypeScript shape is authoritative:
|
||
[`src/types/overview.ts`](src/types/overview.ts); the Python side builds the same
|
||
shape in [`api/routes.py`](api/routes.py). `range` scopes **only** the `finished`
|
||
figures — presses / on-the-way / oven are always real-time.
|
||
|
||
```jsonc
|
||
{
|
||
"plant": "Werk 1", "generatedAt": "…", "range": "1d",
|
||
"ovenTempC": 185, "ovenAvgMin": 120, "sample": true,
|
||
"presses": [{ "name":"P1","status":"run","orders":[{"orderNo":"O-4471","colorIndex":0}],
|
||
"good":1240,"cycleTime":"4.2","notVerladen":34,"note":null,"placeholder":false }],
|
||
"onWay": { "P3":[{ "cartId":"C-014a","orders":[{"orderNo":"O-4471","qty":54,"colorIndex":0}],"etaMin":3 }] },
|
||
"oven": [{ "cartId":"C-011","orders":[],"totalMin":120,"remainingMin":42 }],
|
||
"finished": { "carts":47,"parts":5120,"scrap":63,"avgOvenMin":118,
|
||
"byOrder":[{"orderNo":"O-4471","colorIndex":0,"parts":1680}] },
|
||
"finishedList": [{ "cartId":"C-010","orders":[],"ago":"vor 8 min" }]
|
||
}
|
||
```
|
||
|
||
### Wiring the REAL data (currently SAMPLE)
|
||
|
||
`api/routes.py` returns sample data today. To make it live (see the big `TODO` there
|
||
and `.claude/memory/temper-line-overview.md` in the gateway):
|
||
|
||
- Import the kernel proxy: `from .._db import run_select_query` (read-only).
|
||
- Reuse the temper status-derivation in `modules/fastpress/press/temper.py`
|
||
(`_temper_aggregates_all` derives on-way / in-oven / finished from planned
|
||
timestamps vs `NOW()`), un-scoped across **all** presses. Read
|
||
`production.temper_tracking` (carts grouped by `ttCartId`, keyed by origin press
|
||
`ttPress`) + `production.active_orders`/`finished_orders`.
|
||
- `notVerladen` = produced good parts (cycles) − parts logged to temper. **Open
|
||
question:** per-press or plant-wide?
|
||
- Scope `finished` by `range` on `ttOutOfOvenAt`. Never echo customer/price fields.
|
||
|
||
Alternatively, add a public `GET /press/temper/overview` in fastpress and have the
|
||
frontend call that — decide before wiring; keep the payload shape above either way.
|
||
|
||
---
|
||
|
||
## Tech stack (matches PressV / the fleet)
|
||
|
||
Vue 3.5 · Vite 8 · TypeScript 5.9 · Pinia 3 · vue-router 4 (hash) · vue-i18n 10
|
||
(de default, `?lang=en`) · PrimeVue 4.5 (Nora + custom **Industrial** preset, sky
|
||
primary) · Tailwind v4 (CSS-first) · `@sp-ui-kit/tokens` (shared design tokens,
|
||
private registry) · Inter Variable. **Dark-first**, flat 2px corners, 0s
|
||
transitions, `prefers-reduced-motion` disables the conveyor animations.
|
||
|
||
**Store pattern** (mirrors PressV `productionOverview`): `src/stores/temperOverview.ts`
|
||
does `loadCache()` (sessionStorage) → `hydrate()` (spinner only on cold start) →
|
||
`refresh()` (background) → `startPolling(12000)`. Robust to a dev server's SPA
|
||
fallback: a response without a `presses[]` array is treated as "no backend" → sample.
|
||
|
||
### Design tokens (recognisable across SPRO apps)
|
||
|
||
Flow accents match PressV `--c-*`: press/machine `#a78bfa`, on-the-way/cart
|
||
`#22d3ee`, oven `#f97316` (hot `#ef4444`), finished `#34d399`. Order chips (from
|
||
PressV `orderColors.ts`): `#5794F2 #E8825A #B57EDC #4FB7A8`. Defined in
|
||
`src/style.css` (`:root` dark + `html:not(.dark)` light) and `src/data/orderColors.ts`.
|
||
|
||
> Not yet used but part of the fleet stack — add when needed: `@sp-ui-kit/ui-core`
|
||
> components and `echarts`/`vue-echarts`. The board's by-order bars are CSS today.
|
||
|
||
---
|
||
|
||
## Grafana embedding
|
||
|
||
The gateway sets no `X-Frame-Options`/CSP, so a Grafana **Text** panel (HTML mode,
|
||
`disable_sanitize_html = true`) can iframe it:
|
||
|
||
```html
|
||
<iframe src="http://<gateway-host>:8800/module/temper_overview/#/"
|
||
width="100%" height="900" style="border:0"></iframe>
|
||
```
|
||
|
||
Append `?lang=en` before the `#` for English. Design is locked as design #4
|
||
"Shop-floor Schematic" — see `.claude/memory/temper-line-overview.md` and the
|
||
prototype at `modules/fastpress/reference/temper-overview/` in the gateway repo.
|