Admin Web Dashboard - Telemetrie #1

Open
opened 2026-08-27 20:27:37 +02:00 by erdbruegger · 6 comments
Owner

Die Tabellenansicht im Admin Web Dashboard Telemetrie Tab ist aktuell sehr statisch. Damit kann man wenig anfangen. Die Einträge sollte Filterbar und Sortierbar sein

Die Tabellenansicht im Admin Web Dashboard Telemetrie Tab ist aktuell sehr statisch. Damit kann man wenig anfangen. Die Einträge sollte Filterbar und Sortierbar sein
Collaborator

Umgesetzt in 73d9e58 (release) — CI grün, deployt und gegen die Live-Instanz verifiziert.

Was die Telemetrie-Tabelle jetzt kann

  • Sortieren: Klick auf den Spaltenkopf, erneuter Klick dreht die Richtung um (▲/▼). Leere Werte stehen immer am Ende, egal in welche Richtung sortiert wird.
  • Filtern: eine Eingabezeile unter den Spaltenköpfen, alle Filter wirken per UND zusammen.
    • Zahlenspalten: 80, >80, >=80, <80, <=80, 10..50 (Bereich, inklusive), leer (nur Nullwerte). Komma als Dezimaltrenner geht auch.
    • Textspalten (Zeit, Gang): Teilstring, Groß-/Kleinschreibung egal — z. B. 2026-08-27 oder 10:03.
    • Ein unvollständiger Ausdruck (z. B. nur >) wird rot markiert und ignoriert, statt die Tabelle leerzufiltern.
  • Fußzeile „X von Y Zeilen" plus Button Filter zurücksetzen.
  • Der Chart folgt dem Filter — immer in Zeitreihenfolge, egal wonach die Tabelle gerade sortiert ist.
  • Neue Ladeparameter im Tab: Von/Bis (Zeitfenster) und Limit.

Backend

GET /vehicles/:id/timeseries kennt jetzt limit (jüngste N Punkte, Default 5000, max 50000; die Antwort bleibt aufsteigend nach ts). from/to werden validiert und fallen bei Unsinn auf den Default zurück, statt einen 500er zu werfen; every ist auf 1..100000 geklemmt.

Entscheidungen (proceed-with-default)

  • Filtern/Sortieren läuft im Browser auf dem geladenen Satz — nicht in SQL. Was geladen wird, steuern Zeitfenster/every/Limit. Das ist bei ein paar tausend Zeilen sofort responsiv und spart eine Rundreise pro Klick. Wenn du später über wirklich große Zeiträume filtern willst, wäre serverseitiges Filtern der nächste Schritt.
  • limit hat einen Default von 5000. Vorher lieferte der Endpunkt unbegrenzt viele Zeilen. Falls irgendwo ein Client mehr als 5000 Punkte am Stück erwartet, muss er limit jetzt explizit mitgeben.
  • Beim Tippen wird nur das tbody neu gerendert, damit die Filter-Eingaben Fokus und Cursorposition behalten.

Verifikation

  • 28 Filter-/Sortier-Testfälle als src/routes/selftest.ts — laufen ab jetzt in der CI mit. Dafür liegen die reinen Helfer als exportierter FILTER_JS-String vor; das Dashboard-JS ist für tsc sonst komplett unsichtbar.
  • 30 jsdom-Tests lokal end-to-end gegen das gerenderte Dashboard: Sortierung in beide Richtungen, Null-Handling, alle Filterformen, Kombination über mehrere Spalten, Chart-Kopplung, Fokuserhalt, Reset.
  • Zeitreihe gegen echtes Postgres: limit=3 liefert die 3 jüngsten Punkte in aufsteigender Reihenfolge, every=10 sampled korrekt, kaputtes from gibt 200 statt 500, limit=0 wird auf 1 geklemmt.
  • Live nach dem Deploy: /health ok, das ausgelieferte Dashboard enthält die neue Filter-/Sortier-UI, /vehicles/:id/timeseries antwortet unauthentifiziert korrekt mit 401.
**Umgesetzt** in `73d9e58` (release) — CI grün, deployt und gegen die Live-Instanz verifiziert. ### Was die Telemetrie-Tabelle jetzt kann - **Sortieren:** Klick auf den Spaltenkopf, erneuter Klick dreht die Richtung um (▲/▼). Leere Werte stehen immer am Ende, egal in welche Richtung sortiert wird. - **Filtern:** eine Eingabezeile unter den Spaltenköpfen, alle Filter wirken per UND zusammen. - Zahlenspalten: `80`, `>80`, `>=80`, `<80`, `<=80`, `10..50` (Bereich, inklusive), `leer` (nur Nullwerte). Komma als Dezimaltrenner geht auch. - Textspalten (Zeit, Gang): Teilstring, Groß-/Kleinschreibung egal — z. B. `2026-08-27` oder `10:03`. - Ein unvollständiger Ausdruck (z. B. nur `>`) wird rot markiert und ignoriert, statt die Tabelle leerzufiltern. - **Fußzeile** „X von Y Zeilen" plus Button *Filter zurücksetzen*. - **Der Chart folgt dem Filter** — immer in Zeitreihenfolge, egal wonach die Tabelle gerade sortiert ist. - **Neue Ladeparameter** im Tab: `Von`/`Bis` (Zeitfenster) und `Limit`. ### Backend `GET /vehicles/:id/timeseries` kennt jetzt `limit` (jüngste N Punkte, Default 5000, max 50000; die Antwort bleibt aufsteigend nach `ts`). `from`/`to` werden validiert und fallen bei Unsinn auf den Default zurück, statt einen 500er zu werfen; `every` ist auf 1..100000 geklemmt. ### Entscheidungen (proceed-with-default) - **Filtern/Sortieren läuft im Browser** auf dem geladenen Satz — nicht in SQL. Was geladen wird, steuern Zeitfenster/`every`/`Limit`. Das ist bei ein paar tausend Zeilen sofort responsiv und spart eine Rundreise pro Klick. Wenn du später über wirklich große Zeiträume filtern willst, wäre serverseitiges Filtern der nächste Schritt. - **`limit` hat einen Default von 5000.** Vorher lieferte der Endpunkt unbegrenzt viele Zeilen. Falls irgendwo ein Client mehr als 5000 Punkte am Stück erwartet, muss er `limit` jetzt explizit mitgeben. - Beim Tippen wird nur das `tbody` neu gerendert, damit die Filter-Eingaben Fokus und Cursorposition behalten. ### Verifikation - **28 Filter-/Sortier-Testfälle** als `src/routes/selftest.ts` — laufen ab jetzt in der CI mit. Dafür liegen die reinen Helfer als exportierter `FILTER_JS`-String vor; das Dashboard-JS ist für `tsc` sonst komplett unsichtbar. - **30 jsdom-Tests** lokal end-to-end gegen das gerenderte Dashboard: Sortierung in beide Richtungen, Null-Handling, alle Filterformen, Kombination über mehrere Spalten, Chart-Kopplung, Fokuserhalt, Reset. - **Zeitreihe gegen echtes Postgres:** `limit=3` liefert die 3 jüngsten Punkte in aufsteigender Reihenfolge, `every=10` sampled korrekt, kaputtes `from` gibt 200 statt 500, `limit=0` wird auf 1 geklemmt. - **Live nach dem Deploy:** `/health` ok, das ausgelieferte Dashboard enthält die neue Filter-/Sortier-UI, `/vehicles/:id/timeseries` antwortet unauthentifiziert korrekt mit 401.
Author
Owner

Sieht jetzt auf jeden Fall viel besser aus! Aber:

  • Aktuell scrollt der Header der Tabelle weg. Der sollte fixiert sein und nur der Tabellen-Inhalt scrollen.
  • Das Filtern / Sortieren muss auf jeden Fall im backend erfolgen per SQL. Nur im Frontend macht absolut keinen Sinn.... Der default beim limit kann auf 100 gesetzt sein, 5000 ist zu viel
  • Der Chart: Kann hier eine interaktive Komponente verwendet werden? Außerden: Aktuell gibt es einen Chart für verschiedene Attribute die per DropDown selektiert werden. Es wäre besser, wenn es mehrere charts geben würde, also eins pro Attribute und wenn man sich die Charts auch selbst definieren kann (für attribute X ein Line-Chart, attribute Y ein Bar-Chart, etc. pp)
Sieht jetzt auf jeden Fall viel besser aus! Aber: - Aktuell scrollt der Header der Tabelle weg. Der sollte fixiert sein und nur der Tabellen-Inhalt scrollen. - Das Filtern / Sortieren muss auf jeden Fall im backend erfolgen per SQL. Nur im Frontend macht absolut keinen Sinn.... Der default beim limit kann auf 100 gesetzt sein, 5000 ist zu viel - Der Chart: Kann hier eine interaktive Komponente verwendet werden? Außerden: Aktuell gibt es einen Chart für verschiedene Attribute die per DropDown selektiert werden. Es wäre besser, wenn es mehrere charts geben würde, also eins pro Attribute und wenn man sich die Charts auch selbst definieren kann (für attribute X ein Line-Chart, attribute Y ein Bar-Chart, etc. pp)
Collaborator

⚠️ Der vorige Bearbeitungslauf lief in den 30-Min-Timeout (Task zu groß). Zurück in die Queue gesetzt; ich nehme den Faden mit erhöhtem Zeitlimit wieder auf.

⚠️ Der vorige Bearbeitungslauf lief in den 30-Min-Timeout (Task zu groß). Zurück in die Queue gesetzt; ich nehme den Faden mit erhöhtem Zeitlimit wieder auf.
Collaborator

Umgesetzt in 5d9efb4 + d45b7d8 (release) — CI grün, deployt, gegen die Live-Instanz verifiziert.

1. Tabellenkopf bleibt stehen

Die Tabelle sitzt jetzt in einem eigenen Scroll-Container; beide Kopfzeilen kleben oben — die Sortierzeile bei top:0, die Filterzeile direkt darunter. Nur der Inhalt scrollt.

2. Filtern und Sortieren laufen im Backend per SQL

Komplett umgebaut — das Frontend filtert nichts mehr selbst. GET /vehicles/:id/timeseries kennt jetzt:

Parameter Bedeutung
f_<spalte> Filter, z. B. f_soc=>80, f_speed=10..50, f_gear=D, f_range_km=leer
sort + dir Sortierspalte + asc/desc
limit Seitengröße, Default 100 (vorher 5000), max 50000
offset Blättern
every jede N-te Zeile (Downsampling)

Die Trefferzahl über den gesamten Bestand steht im Header X-Total-Count. Weil limit jetzt eine Seitengröße statt eines Deckels ist, hat die Tabelle zurück/weiter-Buttons und eine Fußzeile „Zeile 101–200 von 4711" bekommen — sonst käme man an alles jenseits der ersten 100 Zeilen nicht mehr heran. Jede Änderung an Filter, Sortierung oder Zeitfenster springt zurück auf Seite 1; Tippen im Filter ist um 300 ms entprellt.

Zur Sicherheit: Spaltennamen kommen ausschließlich aus einer Whitelist, die Operatoren aus einer festen Alternativenliste, und jeder Wert geht als Bind-Parameter raus. Ein ?sort=soc; DROP TABLE telemetry fällt auf die Default-Sortierung zurück, ein f_gear=D'; DROP TABLE telemetry; -- landet als harmloser Suchstring. Beides ist live geprüft — die Tabelle stand danach unverändert da.

3. Charts: eins pro Messgröße, Typ frei wählbar

Das Dropdown ist weg. Stattdessen eine Chart-Leiste, in der du dir dein Set selbst zusammenstellst: + Chart, je Zeile eine Messgröße (SoC, Packspannung, Packstrom, Speed, Odometer, Reichweite, Höhe, Kurs) und ein Typ (Linie / Fläche / Balken / Punkte), × entfernt. Die Zusammenstellung wird im Browser gespeichert und ist beim nächsten Login wieder da; Standard setzt zurück.

Interaktiv sind die Charts über ein Fadenkreuz mit Tooltip: Maus über das Diagramm zeigt Wert + Zeitstempel des nächstgelegenen Punktes, Lücken (Nullwerte) werden übersprungen. Bewusst weiter ohne Chart-Bibliothek — das Dashboard wird als eine Datei ohne Frontend-Build ausgeliefert, und eine externe Lib würde diese Eigenschaft aufgeben. Falls du echtes Zoomen/Pannen willst, sag Bescheid, dann hole ich uPlot o. ä. dazu.

Unterwegs gefunden und mitgefixt

Ich habe die neue SQL-Schicht gegen eine echte Postgres-Instanz mit dem Produktivschema getestet, nicht nur gegen die Unit-Tests — und dabei einen Fehler gefunden, den die Tests nicht sehen konnten:

soc ist SMALLINT, speed/odometer sind REAL. Ohne expliziten Cast leitet Postgres den Typ des Bind-Parameters aus der Spalte ab, und ein Filter wie soc > 80.5 brach mit invalid input syntax for type smallint als 500er ab. Behoben in d45b7d8 ($n::numeric), Regressionstest ergänzt.

Verifikation (echte DB, Produktivschema, 500 Zeilen)

  • Alle Filtertypen greifen: >80 → 95 Treffer, 10..50 → 164, gear=D → 166, range_km=leer → 71 (genau die Nullwerte), Zeitstempel-Textsuche → 60.
  • Blättern lückenlos: 5 Seiten à 100 Zeilen ergaben 500 eindeutige Zeitstempel — keine Überlappung, keine Lücke, Seite 6 leer.
  • X-Total-Count stimmt auch mit Downsampling: every=5 → 100, every=5 + f_soc=>80 → 19 (= ⌊95/5⌋).
  • Unparsebarer Filter (f_soc=>) → sauberer 400 statt 500.
  • Live: /health ok, Dashboard liefert den neuen Stand aus, /vehicles/1/timeseries ohne Token weiterhin 401.
  • 33 Selbsttests grün (inkl. der neuen SQL-Übersetzung), Testdatenbank danach wieder entfernt.

Anmerkung

Ein Detail, das sich aus serverseitigem Sortieren zwangsläufig ergibt: sortierst du nach z. B. SoC absteigend, zeigen die Charts die geladene Seite — also die 100 höchsten SoC-Werte, chronologisch aufgetragen — und nicht mehr einen zusammenhängenden Zeitverlauf. Für einen sauberen Verlauf nach ts sortieren (Default). Sag Bescheid, wenn die Charts stattdessen unabhängig von der Tabellenseite immer das volle Zeitfenster zeichnen sollen — das wäre eine zweite, gröber gesampelte Abfrage.

**Umgesetzt** in `5d9efb4` + `d45b7d8` (release) — CI grün, deployt, gegen die Live-Instanz verifiziert. ### 1. Tabellenkopf bleibt stehen Die Tabelle sitzt jetzt in einem eigenen Scroll-Container; **beide** Kopfzeilen kleben oben — die Sortierzeile bei `top:0`, die Filterzeile direkt darunter. Nur der Inhalt scrollt. ### 2. Filtern und Sortieren laufen im Backend per SQL Komplett umgebaut — das Frontend filtert nichts mehr selbst. `GET /vehicles/:id/timeseries` kennt jetzt: | Parameter | Bedeutung | |---|---| | `f_<spalte>` | Filter, z. B. `f_soc=>80`, `f_speed=10..50`, `f_gear=D`, `f_range_km=leer` | | `sort` + `dir` | Sortierspalte + `asc`/`desc` | | `limit` | Seitengröße, **Default 100** (vorher 5000), max 50000 | | `offset` | Blättern | | `every` | jede N-te Zeile (Downsampling) | Die Trefferzahl über den **gesamten** Bestand steht im Header `X-Total-Count`. Weil `limit` jetzt eine Seitengröße statt eines Deckels ist, hat die Tabelle **zurück/weiter**-Buttons und eine Fußzeile „Zeile 101–200 von 4711" bekommen — sonst käme man an alles jenseits der ersten 100 Zeilen nicht mehr heran. Jede Änderung an Filter, Sortierung oder Zeitfenster springt zurück auf Seite 1; Tippen im Filter ist um 300 ms entprellt. **Zur Sicherheit:** Spaltennamen kommen ausschließlich aus einer Whitelist, die Operatoren aus einer festen Alternativenliste, und *jeder* Wert geht als Bind-Parameter raus. Ein `?sort=soc; DROP TABLE telemetry` fällt auf die Default-Sortierung zurück, ein `f_gear=D'; DROP TABLE telemetry; --` landet als harmloser Suchstring. Beides ist live geprüft — die Tabelle stand danach unverändert da. ### 3. Charts: eins pro Messgröße, Typ frei wählbar Das Dropdown ist weg. Stattdessen eine Chart-Leiste, in der du dir dein Set selbst zusammenstellst: **+ Chart**, je Zeile eine Messgröße (SoC, Packspannung, Packstrom, Speed, Odometer, Reichweite, Höhe, Kurs) und ein Typ (**Linie / Fläche / Balken / Punkte**), `×` entfernt. Die Zusammenstellung wird im Browser gespeichert und ist beim nächsten Login wieder da; **Standard** setzt zurück. Interaktiv sind die Charts über ein Fadenkreuz mit Tooltip: Maus über das Diagramm zeigt Wert + Zeitstempel des nächstgelegenen Punktes, Lücken (Nullwerte) werden übersprungen. Bewusst weiter ohne Chart-Bibliothek — das Dashboard wird als eine Datei ohne Frontend-Build ausgeliefert, und eine externe Lib würde diese Eigenschaft aufgeben. Falls du echtes Zoomen/Pannen willst, sag Bescheid, dann hole ich uPlot o. ä. dazu. ### Unterwegs gefunden und mitgefixt Ich habe die neue SQL-Schicht gegen eine **echte Postgres-Instanz mit dem Produktivschema** getestet, nicht nur gegen die Unit-Tests — und dabei einen Fehler gefunden, den die Tests nicht sehen konnten: `soc` ist `SMALLINT`, `speed`/`odometer` sind `REAL`. Ohne expliziten Cast leitet Postgres den Typ des Bind-Parameters aus der Spalte ab, und ein Filter wie `soc > 80.5` brach mit `invalid input syntax for type smallint` als **500er** ab. Behoben in `d45b7d8` (`$n::numeric`), Regressionstest ergänzt. ### Verifikation (echte DB, Produktivschema, 500 Zeilen) - Alle Filtertypen greifen: `>80` → 95 Treffer, `10..50` → 164, `gear=D` → 166, `range_km=leer` → 71 (genau die Nullwerte), Zeitstempel-Textsuche → 60. - **Blättern lückenlos:** 5 Seiten à 100 Zeilen ergaben 500 eindeutige Zeitstempel — keine Überlappung, keine Lücke, Seite 6 leer. - `X-Total-Count` stimmt auch mit Downsampling: `every=5` → 100, `every=5` + `f_soc=>80` → 19 (= ⌊95/5⌋). - Unparsebarer Filter (`f_soc=>`) → sauberer **400** statt 500. - Live: `/health` ok, Dashboard liefert den neuen Stand aus, `/vehicles/1/timeseries` ohne Token weiterhin **401**. - 33 Selbsttests grün (inkl. der neuen SQL-Übersetzung), Testdatenbank danach wieder entfernt. ### Anmerkung Ein Detail, das sich aus serverseitigem Sortieren zwangsläufig ergibt: sortierst du nach z. B. SoC absteigend, zeigen die Charts die geladene **Seite** — also die 100 höchsten SoC-Werte, chronologisch aufgetragen — und nicht mehr einen zusammenhängenden Zeitverlauf. Für einen sauberen Verlauf nach `ts` sortieren (Default). Sag Bescheid, wenn die Charts stattdessen unabhängig von der Tabellenseite immer das volle Zeitfenster zeichnen sollen — das wäre eine zweite, gröber gesampelte Abfrage.
Author
Owner

Ja, das ist jetzt schon richtig nützlich! Kannst du jetzt noch die Seite etwas responsive machen? Also dass sie sich an die Display-Größe anpasst und die Charts sollten auch drei verschiedene Größen unterstüzen (klein mittel groß -> wobei groß ist das was es jetzt ist)

Ja, das ist jetzt schon richtig nützlich! Kannst du jetzt noch die Seite etwas responsive machen? Also dass sie sich an die Display-Größe anpasst und die Charts sollten auch drei verschiedene Größen unterstüzen (klein mittel groß -> wobei groß ist das was es jetzt ist)
Collaborator

Umgesetzt in 99e9ec4 (release) — CI grün, deployt, live verifiziert.

Charts in drei Größen

Je Chart eine dritte Auswahl neben Messgröße und Typ: klein / mittel / groß. groß ist genau die bisherige Darstellung. Die Größe steuert beides — Breite im Raster und Höhe in Pixeln:

Größe Rasterbreite Höhe
klein 4 von 12 Spalten 130 px
mittel 6 von 12 180 px
groß volle Breite 220 px

Drei kleine Charts liegen also nebeneinander. Die Auswahl wird wie Messgröße und Typ im Browser gespeichert; deine bereits eingerichteten Charts laufen als groß weiter, da ändert sich nichts.

Dabei einen Darstellungsfehler behoben

Die Charts wurden bisher in einen festen 900×200-Raum gezeichnet und per CSS auf die Breite gestreckt. Sobald ich Höhen frei wählbar machte, wäre daraus eine nicht-uniforme Skalierung geworden — Achsenbeschriftung und Punkte hätten sich verzogen.

Jetzt wird stattdessen die tatsächliche Pixelbreite des Containers gemessen und das SVG in genau dieser Größe aufgebaut. Eine SVG-Einheit ist damit ein Pixel: nichts wird gestreckt, in keiner Größe und bei keiner Fensterbreite. Bei Größenänderung des Fensters wird neu vermessen und gezeichnet (entprellt, damit das Ziehen am Fensterrand nicht den ganzen Satz pro Pixel neu rendert). Bei den kleinen Charts sind zusätzlich Rand, Schriftgröße, Linienstärke und Punktradius etwas reduziert — sonst bliebe von der Zeichenfläche kaum etwas übrig.

Responsiv

  • Bis 1100 px: kleine Charts rücken auf halbe Breite, mittlere auf volle — sonst werden sie unlesbar schmal.
  • Bis 720 px (Telefon): alle Charts einspaltig, Eingabezeilen stapeln untereinander statt nebeneinander, Bedienelemente auf volle Breite, Tabellen kompakter, Navigation kleiner. Die Telemetrie-Tabelle nutzt jetzt 60 % der Fensterhöhe statt fester 440 px — auf einem hohen Schirm siehst du damit mehr, auf einem kleinen quetscht sie nicht.
  • main darf 1400 px breit werden statt 1000 und ist zentriert — auf einem breiten Monitor war vorher rechts viel Luft.

Verifikation

  • Alle 12 Kombinationen aus drei Größen × vier Typen (Linie/Fläche/Balken/Punkte) gerendert und geprüft: width/height/viewBox stimmen exakt überein, alle erzeugten Koordinaten liegen innerhalb der Zeichenfläche, kein NaN und kein undefined im Ergebnis.
  • Lücken (Nullwerte) erzeugen keine kaputten Pfade; eine Messgröße ohne Werte meldet sich sauber statt leer zu bleiben; Mindestmaße (160×90) greifen, falls ein Container beim Messen noch 0 breit ist.
  • Client-JS des Dashboards parst fehlerfrei — auch als von der Live-Instanz ausgeliefert gegengeprüft, nicht nur lokal.
  • Live: /health ok, alle neuen Marker (Größenklassen, Media-Queries, Größen-Auswahl) sind im ausgelieferten Dashboard vorhanden.

Ansehen kannst du dir das am besten, indem du das Fenster einmal schmal ziehst — und ein paar Charts auf klein stellst, dann liegen sie zu dritt nebeneinander.

**Umgesetzt** in `99e9ec4` (release) — CI grün, deployt, live verifiziert. ### Charts in drei Größen Je Chart eine dritte Auswahl neben Messgröße und Typ: **klein / mittel / groß**. `groß` ist genau die bisherige Darstellung. Die Größe steuert beides — Breite im Raster und Höhe in Pixeln: | Größe | Rasterbreite | Höhe | |---|---|---| | klein | 4 von 12 Spalten | 130 px | | mittel | 6 von 12 | 180 px | | groß | volle Breite | 220 px | Drei kleine Charts liegen also nebeneinander. Die Auswahl wird wie Messgröße und Typ im Browser gespeichert; deine bereits eingerichteten Charts laufen als `groß` weiter, da ändert sich nichts. ### Dabei einen Darstellungsfehler behoben Die Charts wurden bisher in einen festen 900×200-Raum gezeichnet und per CSS auf die Breite gestreckt. Sobald ich Höhen frei wählbar machte, wäre daraus eine **nicht-uniforme Skalierung** geworden — Achsenbeschriftung und Punkte hätten sich verzogen. Jetzt wird stattdessen die **tatsächliche Pixelbreite** des Containers gemessen und das SVG in genau dieser Größe aufgebaut. Eine SVG-Einheit ist damit ein Pixel: nichts wird gestreckt, in keiner Größe und bei keiner Fensterbreite. Bei Größenänderung des Fensters wird neu vermessen und gezeichnet (entprellt, damit das Ziehen am Fensterrand nicht den ganzen Satz pro Pixel neu rendert). Bei den kleinen Charts sind zusätzlich Rand, Schriftgröße, Linienstärke und Punktradius etwas reduziert — sonst bliebe von der Zeichenfläche kaum etwas übrig. ### Responsiv - **Bis 1100 px:** kleine Charts rücken auf halbe Breite, mittlere auf volle — sonst werden sie unlesbar schmal. - **Bis 720 px (Telefon):** alle Charts einspaltig, Eingabezeilen stapeln untereinander statt nebeneinander, Bedienelemente auf volle Breite, Tabellen kompakter, Navigation kleiner. Die Telemetrie-Tabelle nutzt jetzt 60 % der Fensterhöhe statt fester 440 px — auf einem hohen Schirm siehst du damit mehr, auf einem kleinen quetscht sie nicht. - `main` darf 1400 px breit werden statt 1000 und ist zentriert — auf einem breiten Monitor war vorher rechts viel Luft. ### Verifikation - **Alle 12 Kombinationen** aus drei Größen × vier Typen (Linie/Fläche/Balken/Punkte) gerendert und geprüft: `width`/`height`/`viewBox` stimmen exakt überein, **alle** erzeugten Koordinaten liegen innerhalb der Zeichenfläche, kein `NaN` und kein `undefined` im Ergebnis. - Lücken (Nullwerte) erzeugen keine kaputten Pfade; eine Messgröße ohne Werte meldet sich sauber statt leer zu bleiben; Mindestmaße (160×90) greifen, falls ein Container beim Messen noch 0 breit ist. - Client-JS des Dashboards parst fehlerfrei — auch als **von der Live-Instanz ausgeliefert** gegengeprüft, nicht nur lokal. - Live: `/health` ok, alle neuen Marker (Größenklassen, Media-Queries, Größen-Auswahl) sind im ausgelieferten Dashboard vorhanden. Ansehen kannst du dir das am besten, indem du das Fenster einmal schmal ziehst — und ein paar Charts auf *klein* stellst, dann liegen sie zu dritt nebeneinander.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
aiways/Backend#1
No description provided.