SPA_TemperProductionOverview/README.md
Erik adecca9e9a feat(overview): press-number badges, header rework, fused top panel
- Stamp the origin press (machine) number on every cart across the line:
  on-the-way, in-oven, finished tiles, and the finished by-order list.
  New shared PressTag.vue badge (icon-free, ink/white text on a violet box).
- Thread `press` through the payload (CartOnWay/OvenBatch/FinishedCart/
  FinishedByOrder) — backend derives distinct press(es) per cart/order.
- Header: pill now "Temperprozess" (was PressV); "Live-Uebersicht" moved into
  the blue accent slot; dropped the duplicate subtitle.
- Fuse the header bar and flow legend into one denser two-row panel (flat
  prop on both + a shared .toppanel wrapper).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 16:25:53 +02:00

155 lines
7.2 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)
Package manager is **yarn 1.22.22** (pinned via `packageManager`; matches PressV /
the fleet). `corepack` provides it — no global yarn install needed.
```bash
# 0) let corepack honour the pinned yarn (one-time per machine)
corepack enable
# 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
# (yarn v1 reads .npmrc for the scoped registry)
# 2) install + run
yarn install
yarn 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 `yarn dev` shows the full design immediately (a
`DEMO-DATEN` tag appears in the header). `yarn 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 `yarn 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","press":"P3","orders":[{"orderNo":"O-4471","qty":54,"colorIndex":0}],"etaMin":3 }] },
"oven": [{ "cartId":"C-011","press":"P1","orders":[],"totalMin":120,"remainingMin":42 }],
"finished": { "carts":47,"parts":5120,"scrap":63,"avgOvenMin":118,
"byOrder":[{"orderNo":"O-4471","colorIndex":0,"parts":1680,"press":"P1"}] },
"finishedList": [{ "cartId":"C-010","press":"P6","orders":[],"ago":"vor 8 min" }]
}
```
### Live data wiring
`api/routes.py` reads live MariaDB (`sample:true` only appears in the standalone
dev fallback with no backend). Details + the exact column sources are in the gateway
memory `.claude/memory/temper-line-overview.md`. In short: it uses the kernel proxy
`from .._db import run_select_query` (read-only), un-scoped across **all** presses for
plant `PLANT_NAME` — press list from `press_settings.presses`; good/chips from
`active_orders`+`rejects`; run/stop/idle from `cycles_new` recency; carts +
`notVerladen` from `production.temper_tracking` with position derived in SQL from the
planned timestamps vs `NOW()` (reprints excluded). `finished` is scoped by `range` on
`ttOutOfOvenAt`. Oven temperature is omitted (no sensor). Never echo customer/price
fields.
---
## 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.