SPA_TemperProductionOverview/src/views/TemperOverviewPage.vue
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

90 lines
2.7 KiB
Vue

<!--
TemperOverviewPage.vue the whole-line temper board (design #4 "Shop-floor
Schematic", locked). One connected line: Pressen (per-press amber arrows,
only under presses with a cart on the way) Ofen (green ) Fertig. Carts
hang under their origin press; the oven & finished bands scroll horizontally.
The time-range picker scopes ONLY the Fertig figures.
Data comes from the store (GET /api/overview, polled). On connection loss the
board keeps the last real data (or shows a reconnect spinner on a cold start) and
retries it never shows demo data in production. Sample data renders only under
`npm run dev` (no gateway).
-->
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useI18n } from 'vue-i18n'
import OverviewHeader from '@/components/OverviewHeader.vue'
import FlowLegend from '@/components/FlowLegend.vue'
import KpiStrip from '@/components/KpiStrip.vue'
import ProductionLine from '@/components/ProductionLine.vue'
import { useTemperOverviewStore } from '@/stores/temperOverview'
const { t } = useI18n()
const store = useTemperOverviewStore()
const { payload, range, embedded, hasData, usingSample, disconnected } = storeToRefs(store)
onMounted(async () => {
await store.hydrate()
store.startPolling(12000)
})
onUnmounted(() => store.stopPolling())
</script>
<template>
<div class="wrap">
<template v-if="hasData && payload">
<!-- Header bar + flow legend fused into one denser panel (two rows). -->
<div class="toppanel">
<OverviewHeader
flat
:plant="payload.plant"
:range="range"
:sample="usingSample"
:disconnected="disconnected"
:embedded="embedded"
@update:range="store.setRange"
/>
<FlowLegend flat />
</div>
<KpiStrip :payload="payload" />
<ProductionLine :payload="payload" />
</template>
<!-- Cold start / no data yet: never blank, never fake — spinner that keeps retrying. -->
<div v-else class="state">
<span class="spinner" aria-hidden="true"></span>
<span class="msg">{{ disconnected ? t('conn.lost') : t('conn.connecting') }}</span>
</div>
</div>
</template>
<style scoped>
.wrap {
max-width: 1300px;
margin: 0 auto;
padding: 20px 22px 80px;
}
.toppanel {
border: 1px solid var(--line);
background: var(--panel);
border-radius: var(--r);
margin-bottom: 14px;
overflow: hidden;
}
.state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
min-height: 60vh;
color: var(--ink-dim);
font-family: var(--mono);
}
.state .msg {
font-size: 0.85rem;
letter-spacing: 0.04em;
}
</style>