Feature: Live-Dashboard in Landing Page (PocketBase, 30s Auto-Refresh) + stündlicher Demo-Reset

This commit is contained in:
rene 2026-05-17 16:18:34 +02:00
parent a7176a589a
commit b06a3fac80
2 changed files with 163 additions and 27 deletions

View file

@ -1,6 +1,52 @@
<script lang="ts">
import logo from '$lib/assets/checkflo-logo.png';
import hafnerLogo from '$lib/assets/hafner-logo.png';
import { pb } from '$lib/pb';
import { onMount } from 'svelte';
import { browser } from '$app/environment';
const DEMO_TENANT = 'mengbzc3ajxpccz';
const STATUS_LABEL: Record<string, string> = { ok: 'OK', abweichung: 'Abweichung', kritisch: 'Kritisch' };
const STATUS_CLASS: Record<string, string> = { ok: 'ok', abweichung: 'warn', kritisch: 'crit' };
let liveLogs = $state<any[]>([]);
let liveStats = $state({ total: 0, ok: 0, warn: 0, crit: 0 });
let lastUpdate = $state('');
async function fetchLiveData() {
try {
const today = new Date();
today.setHours(0, 0, 0, 0);
const dateStr = today.toISOString().slice(0, 19).replace('T', ' ');
const result = await pb.collection('check_logs').getList(1, 3, {
filter: `tenant = '${DEMO_TENANT}' && created >= '${dateStr}'`,
expand: 'station',
sort: '-created'
});
liveLogs = result.items;
liveStats.total = result.totalItems;
liveStats.ok = result.items.filter((l: any) => l.status === 'ok').length;
liveStats.warn = result.items.filter((l: any) => l.status === 'abweichung').length;
liveStats.crit = result.items.filter((l: any) => l.status === 'kritisch').length;
lastUpdate = new Date().toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
} catch { /* public endpoint, sollte immer klappen */ }
}
onMount(() => {
if (!browser) return;
fetchLiveData();
const iv = setInterval(fetchLiveData, 30_000);
return () => clearInterval(iv);
});
function formatTime(iso: string) {
if (!iso) return '';
const d = new Date(iso);
return isNaN(d.getTime()) ? '' : d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
}
const features = [
{
@ -71,44 +117,39 @@
</div>
<div class="dash-main">
<div class="dash-title">Dashboard</div>
<div class="dash-date">Sonntag, 17. Mai</div>
<div class="dash-date">
{new Date().toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long' })}
{#if lastUpdate}<span class="dash-refresh">· {lastUpdate}</span>{/if}
</div>
<div class="dash-stats">
<div class="dash-stat">
<div class="dash-num">184</div>
<div class="dash-label">Checks gesamt</div>
<div class="dash-num">{liveStats.total}</div>
<div class="dash-label">Heute</div>
</div>
<div class="dash-stat ok">
<div class="dash-num">171</div>
<div class="dash-label">In Ordnung</div>
<div class="dash-num">{liveStats.ok}</div>
<div class="dash-label">OK</div>
</div>
<div class="dash-stat warn">
<div class="dash-num">11</div>
<div class="dash-label">Abweichungen</div>
<div class="dash-num">{liveStats.warn}</div>
<div class="dash-label">Abw.</div>
</div>
<div class="dash-stat crit">
<div class="dash-num">2</div>
<div class="dash-num">{liveStats.crit}</div>
<div class="dash-label">Kritisch</div>
</div>
</div>
<div class="dash-entries-title">Heutige Einträge</div>
<div class="dash-entry">
<span class="entry-badge ok">OK</span>
<span class="entry-station">Fleischkühltheke</span>
<span class="entry-temp">2 °C</span>
<span class="entry-who">Thomas K. · 06:55</span>
</div>
<div class="dash-entry">
<span class="entry-badge ok">OK</span>
<span class="entry-station">Wurstkühlschrank</span>
<span class="entry-temp">3 °C</span>
<span class="entry-who">Maria S. · 07:12</span>
</div>
<div class="dash-entry">
<span class="entry-badge warn">Abweichung</span>
<span class="entry-station">Tiefkühltruhe</span>
<span class="entry-temp">16 °C</span>
<span class="entry-who">Thomas K. · 08:03</span>
</div>
<div class="dash-entries-title">Letzte Einträge</div>
{#each liveLogs as log}
<div class="dash-entry">
<span class="entry-badge {STATUS_CLASS[log.status] ?? 'ok'}">{STATUS_LABEL[log.status] ?? log.status}</span>
<span class="entry-station">{log.expand?.station?.name ?? '—'}</span>
<span class="entry-temp">{log.temperature ? log.temperature + ' °C' : ''}</span>
<span class="entry-who">{log.checked_by} · {formatTime(log.created)}</span>
</div>
{:else}
<div class="dash-empty">Noch keine Einträge heute</div>
{/each}
</div>
</div>
</div>
@ -394,7 +435,9 @@
.dash-num { font-size: 1rem; font-weight: 800; color: #0B1023; line-height: 1; }
.dash-label { font-size: 0.5rem; color: #888; margin-top: 0.15rem; }
.dash-refresh { font-size: 0.55rem; color: #aaa; }
.dash-entries-title { font-size: 0.7rem; font-weight: 700; color: #0B1023; margin-bottom: 0.4rem; }
.dash-empty { font-size: 0.65rem; color: #aaa; padding: 0.5rem 0; }
.dash-entry {
background: #fff;
border-radius: 6px;