e1d570992e
Drei zusammengehörige Teile in einem Repository: - homeassistant/ Das fertige, im Einsatz befindliche Home-Assistant-Panel (panel_custom Custom Element + pyscript-Backend). Echte Fahrzeug- und Personendaten (fahrzeugprofil.json, fahrten.jsonl, tankvorgaenge.jsonl, Tankbelege) bleiben per .gitignore außen vor; die anonymisierte Vorlage fahrzeugprofil.example.json ist mit dabei. - design-system/ Eigenständige React-Komponentenbibliothek (@audi-dash/ui), die die visuelle Sprache des Panels nachbildet - ohne Audi-Markenzeichen und ohne die lizenzierte Hausschrift. Dient als Grundlage für Claude Design. War bis hierher ein eigenes Repository und ist in dieses eingeschmolzen worden. - companion-app/ Datenschicht der neuen App DataMetric360 (iOS/Android via Capacitor, zusätzlich als Iframe im HA-Dashboard). Noch ohne Oberfläche: REST- und WebSocket-Zugriff auf Home Assistant plus Warteschlange für Änderungen ohne Netz. Ersetzt das eingespritzte hass-Objekt, das nur innerhalb des HA-Frontends existiert. Dazu die Projektdokumentation: SPECIFICATION.md (Ist-Stand des Panels), COMPANION_APP_ARCHITECTURE.md (Architekturentscheidungen der neuen App), AUDIT_2026-08-10.md, DESIGN_BRIEF_DATAMETRIC360.md und der ursprüngliche Bauauftrag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
72 lines
2.5 KiB
JavaScript
72 lines
2.5 KiB
JavaScript
import * as React from "react";
|
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
import * as UI from "../dist/index.js";
|
|
|
|
const h = React.createElement;
|
|
const noop = () => {};
|
|
|
|
const cases = [
|
|
{ name: "Pill", el: h(UI.Pill, {}, "Text") },
|
|
{ name: "IconButton", el: h(UI.IconButton, { "aria-label": "x" }, "*") },
|
|
{ name: "Fig", el: h(UI.Fig, { value: 42, unit: "km" }) },
|
|
{ name: "StatusRow", el: h(UI.StatusRow, { status: "ok", title: "Titel", subtitle: "Sub" }) },
|
|
{ name: "ActionButton", el: h(UI.ActionButton, {}, "Aktion") },
|
|
{ name: "Switch", el: h(UI.Switch, { checked: true, onChange: noop }) },
|
|
{
|
|
name: "Seg",
|
|
el: h(UI.Seg, {
|
|
options: [{ value: "a", label: "A" }, { value: "b", label: "B" }],
|
|
value: "a",
|
|
onChange: noop,
|
|
}),
|
|
},
|
|
{
|
|
name: "RowList",
|
|
el: h(UI.RowList, { items: [{ key: 1, label: "L", value: "V", subValue: "SV" }] }),
|
|
},
|
|
{ name: "LeafRow", el: h(UI.LeafRow, { title: "T", value: "V" }) },
|
|
{ name: "Tile", el: h(UI.Tile, {}, "Inhalt") },
|
|
{ name: "Tile (button)", el: h(UI.Tile, { variant: "button", chevron: true, onClick: noop }, "Klick") },
|
|
{ name: "Feld", el: h(UI.Feld, { label: "L", value: "V" }) },
|
|
{ name: "ProgressRing", el: h(UI.ProgressRing, { percent: 42, unit: "%" }) },
|
|
{ name: "ProgressBar (single)", el: h(UI.ProgressBar, { percent: 50 }) },
|
|
{
|
|
name: "ProgressBar (segments)",
|
|
el: h(UI.ProgressBar, { segments: [{ value: 3, label: "A" }, { value: 1, label: "B" }] }),
|
|
},
|
|
{
|
|
name: "StatGrid",
|
|
el: h(UI.StatGrid, { items: [{ value: 1, caption: "C" }, { value: 2, caption: "C2" }] }),
|
|
},
|
|
{ name: "Accordion", el: h(UI.Accordion, { title: "T", defaultOpen: true }, "Body") },
|
|
{ name: "SwipeRow", el: h(UI.SwipeRow, { onDelete: noop }, "Zeile") },
|
|
{
|
|
name: "TabBar",
|
|
el: h(UI.TabBar, {
|
|
items: [{ key: "a", label: "A", icon: h("svg") }],
|
|
activeKey: "a",
|
|
onChange: noop,
|
|
}),
|
|
},
|
|
{
|
|
name: "Popup",
|
|
el: h(UI.Popup, { open: true, onClose: noop }, h(UI.PopupMenuItem, {}, "Item")),
|
|
},
|
|
{ name: "ImagePlaceholder", el: h(UI.ImagePlaceholder, { alt: "a", label: "Label" }) },
|
|
];
|
|
|
|
let failed = 0;
|
|
for (const c of cases) {
|
|
try {
|
|
const html = renderToStaticMarkup(c.el);
|
|
if (!html || html.length === 0) throw new Error("empty output");
|
|
console.log(`PASS ${c.name}`);
|
|
} catch (err) {
|
|
failed++;
|
|
console.log(`FAIL ${c.name}: ${err.message}`);
|
|
}
|
|
}
|
|
|
|
console.log(`\n${cases.length - failed}/${cases.length} passed`);
|
|
if (failed > 0) process.exit(1);
|