Ladestand ins Fahrzeugbild, Jahresstatistik als Vergleichstabelle
Drei Teile, die zusammengehoeren: die Fahrzeugkachel zeigt den Ladestand jetzt dort, wo er im Auto auch sitzt, die Jahresstatistik wird zur Tabelle, und beide holen ihr Aussehen aus einer gemeinsamen Farbwelt. Fahrzeugkachel. Die Kachel "Batterie und Ladung" ist aufgeloest; ihr Inhalt steckt in der Draufsicht. Die Akkuplatte im Fahrzeugboden fuellt sich von hinten nach vorn, traegt die Zielmarke und nennt Prozent und Reichweite in ihrer Mitte - auf dem Goldenen Schnitt der Plattenhoehe, damit sie nicht am Klimaknopf klebt. Beim Laden wandern Streifen im gefuellten Teil nach vorn, und vom Anschluss fliesst eine Linie in die Platte. Was sich nicht zeichnen laesst - Ladeleistung, Restzeit, Kilometerstand, Klima, Herkunft des Ladestroms - steht als Kennzahl daneben, dazu die gefahrenen Kilometer aus Woche, Monat und Jahr (?was=strecke, gerechnet aus dem Zaehlerstand statt aus erkannten Fahrten - faellt eine Zeile aus, fehlt die Fahrt in der Liste, im Zaehlerstand aber nicht). Am Fahrzeug selbst: kein Panoramadach, das hat unseres nicht. Reling und Spoiler neu gezeichnet, weil sie mit der Dachplatte bisher ein geschlossenes Rechteck bildeten und das wie ein Fensterrahmen aussah. Die Blende am Bug ist schmaler und heller - von oben sieht man von einer senkrechten Flaeche nur einen Streifen, und fast schwarz las sie sich wie ein Loch. Die Ladeklappe ist gebaut wie der Klimaknopf; den Ladezustand traegt jetzt die Farbe statt einer zweiten Form. Jahresvergleich. getStats.php liefert kein fertiges HTML mehr, sondern JSON, das sich selbst beschreibt: Einheit, Nachkommastellen, Richtung und Zugehoerigkeit stehen als Metadaten je Kennzahl, nicht mehr als zwei Arrays, die ueber den Spaltenindex mit der Abfrage synchron gehalten werden mussten. Zwei Abfragen holen alles - Monatswerte, daraus die ganzen Jahre und die Summe seit Aufzeichnungsbeginn, dazu der gleiche Zeitraum jedes Jahres bis zum heutigen Kalendertag. Verhaeltniszahlen sind auf jeder Ebene neu aus den Summen gerechnet; ein Jahresmittel ist nicht das Mittel der Monatsmittel. Gezeichnet wird das von einem Baustein, den sich Historien- und Solarseite teilen: dort drei Jahresspalten mit verschiebbarem Fenster bis zurueck zum ersten Jahr, hier eine Spalte ohne Werkzeuge. Zeilen, die sich aufteilen, stehen als "davon" darunter; was bereits in einer anderen Zeile steckt, als "darin" samt Namen der Zeile - der Beitrag der Batterie und die Solarladung des Autos duerfen nicht zum Ertrag addiert werden. Zusammengefasste Zeilen sind zunaechst zu, damit die Tabelle mit zehn Zeilen aufmacht statt mit siebzehn. Farbwelt. Statt Regel fuer Regel zu ueberschreiben, ist die Palette ausgetauscht: ein paar Variablen am Wurzelelement, an die Bootstrap und AdminLTE ohnehin gehen. Anthrazit als Grund, eine Spur hellere Flaeche fuer Karten, Haarlinien statt Rahmen und Schatten, Bernstein als einzige Akzentfarbe - auf dieser Seite die Farbe der eigenen Anlage. Gruen, Rot und Gelb bleiben den Zustaenden vorbehalten. Erklaerungen, die bisher nur im title standen, sind antippbar: am Finger gibt es kein Ueberfahren, und ein title erscheint dort nie. Die beiden neuen Abfragen sind gegen solarLog gelaufen, die Monatswerte decken sich mit einer unabhaengigen Zerlegung. grundpreisImJahr() ruft jetzt grundpreisImZeitraum() auf - dieselbe Rechnung, nur auch fuer einen Teil des Jahres. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+26
-3
@@ -325,9 +325,32 @@ function preiseSpeichern($key, $zeilen)
|
||||
function grundpreisImJahr($jahr)
|
||||
{
|
||||
$jahr = intval($jahr);
|
||||
$von = $jahr . "-01-01";
|
||||
$bis = $jahr . "-12-31";
|
||||
$tage = intval((new DateTimeImmutable($bis))->format("z")) + 1; // 365 oder 366
|
||||
return grundpreisImZeitraum($jahr . "-01-01", $jahr . "-12-31");
|
||||
}
|
||||
|
||||
/**
|
||||
* Grundpreis Strom fuer einen Zeitraum innerhalb eines Kalenderjahres.
|
||||
*
|
||||
* Der Grundpreis steht als Jahresbetrag in der Tarifzeile; auf einen Zeitraum
|
||||
* kommt er anteilig nach Tagen. Geteilt wird deshalb durch die Tage des
|
||||
* JAHRES, nicht durch die des Zeitraums - sonst zahlte ein halbes Jahr den
|
||||
* vollen Betrag.
|
||||
*
|
||||
* Von und bis muessen im selben Jahr liegen: die Statistik fragt entweder ein
|
||||
* ganzes Jahr oder dessen Anfang bis zu einem Stichtag, und ein Zeitraum ueber
|
||||
* den Jahreswechsel haette zwei verschiedene Nenner.
|
||||
*/
|
||||
function grundpreisImZeitraum($von, $bis)
|
||||
{
|
||||
$vonTag = new DateTimeImmutable($von);
|
||||
$bisTag = new DateTimeImmutable($bis);
|
||||
if ($vonTag->format("Y") !== $bisTag->format("Y")) {
|
||||
throw new InvalidArgumentException("Zeitraum muss innerhalb eines Jahres liegen.");
|
||||
}
|
||||
$von = $vonTag->format("Y-m-d");
|
||||
$bis = $bisTag->format("Y-m-d");
|
||||
// Tage des Jahres, 365 oder 366 - der Nenner des Jahresbetrags.
|
||||
$tage = intval((new DateTimeImmutable($vonTag->format("Y") . "-12-31"))->format("z")) + 1;
|
||||
|
||||
$stmt = solarDb()->prepare(
|
||||
"WITH p AS (SELECT active_date, base,
|
||||
|
||||
+34
-19
@@ -1,16 +1,24 @@
|
||||
<?php
|
||||
/*
|
||||
* Seite "Vergangenheit": sechs Diagramme und darunter der Jahresvergleich.
|
||||
*
|
||||
* Der Vergleich war einmal drei Karten mit je achtzehn Kacheln - dieselben
|
||||
* Beschriftungen dreimal untereinander, und wer zwei Jahre vergleichen wollte,
|
||||
* musste scrollen. Jetzt ist es eine Tabelle: Kennzahlen in Zeilen, Jahre in
|
||||
* Spalten, der Vergleich also eine Zeile weit.
|
||||
*
|
||||
* Gebaut wird der Vergleich von baueJahresstatistik() in
|
||||
* js/solar/jahresstatistik.js, aus einer einzigen Antwort von
|
||||
* ajax/getStats.php - auch die Zeilen selbst, denn welche Kennzahlen es gibt,
|
||||
* weiss der Server. Denselben Baustein zeigt die Solarseite einspaltig.
|
||||
*/
|
||||
|
||||
// Die sechs Diagramme unterscheiden sich nur in Zeitraum und Canvas-ID.
|
||||
$periods = [
|
||||
"month" => "Letzter Monat",
|
||||
"year" => "Letzte 13 Monate",
|
||||
"decade" => "Letzte 10 Jahre",
|
||||
];
|
||||
// Die Jahresstatistiken darunter folgen demselben Muster.
|
||||
$statistics = [
|
||||
"Stats-Year" => "Statistik dieses Jahr",
|
||||
"Stats-Lastyear" => "Statistik letztes Jahr",
|
||||
"Stats-Prelastyear" => "Statistik vorletztes Jahr",
|
||||
];
|
||||
?>
|
||||
<!--begin::App Main-->
|
||||
<main class="app-main">
|
||||
@@ -63,23 +71,30 @@ $statistics = [
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach ($statistics as $id => $title): ?>
|
||||
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
||||
<div class="card p-0 mb-4" style="width:100%">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title"><?= $title ?></h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
<!-- ============================================ Jahresvergleich ==== -->
|
||||
<!-- Der Inhalt kommt aus baueJahresstatistik() in
|
||||
js/solar/jahresstatistik.js: Leitzahlen, Werkzeugleiste, Tabelle und
|
||||
Fussnoten baut der Baustein selbst, damit die Solarseite dieselbe
|
||||
Statistik in schmaler Form zeigen kann, ohne dass es sie zweimal
|
||||
gaebe. -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card mb-4 p-0 w-100">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Jahresvergleich</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body statistik">
|
||||
<div id="jahresvergleich"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-cols-1 row-cols-md-1 g-4 p-2" id="<?= $id ?>">
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- /.row (main row) -->
|
||||
</div>
|
||||
<!--end::Container-->
|
||||
|
||||
+375
-180
@@ -19,10 +19,16 @@
|
||||
* 4648 x 1877 mm, Radstand 2765 mm, daraus das Seitenverhältnis 2,48:1 und
|
||||
* die Lage der Achsen. Kein Foto, sondern ein Schema - die Teile sollen
|
||||
* ihren Zustand zeigen, nicht das Auto porträtieren.
|
||||
*
|
||||
* Der Ladestand ist Teil dieser Grafik und nicht mehr eine Anzeige daneben:
|
||||
* die Akkuplatte im Fahrzeugboden füllt sich von hinten nach vorn, trägt die
|
||||
* Zielmarke und nennt Prozent und Reichweite in ihrer Mitte. Alles, was sich
|
||||
* nicht zeichnen lässt - Ladeleistung, Restzeit, Kilometerstand, Klima und
|
||||
* die Herkunft des Ladestroms - steht als Kennzahl daneben.
|
||||
*/
|
||||
?>
|
||||
<!--begin::App Main-->
|
||||
<main class="app-main">
|
||||
<main class="app-main skoda-seite">
|
||||
<div class="app-content-header">
|
||||
<div class="container-fluid">
|
||||
<div class="row align-items-center">
|
||||
@@ -39,208 +45,397 @@
|
||||
<div class="app-content">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- ================================================ Zustand ===== -->
|
||||
<!-- ================================================ Übersicht ==== -->
|
||||
<!-- Eine Kachel, zwei Hälften: links das Fahrzeug, rechts die Zahlen
|
||||
dazu. Der Ladestand steht nicht mehr als eigener Ring daneben,
|
||||
sondern dort, wo er im Auto auch sitzt - in der Akkuplatte im
|
||||
Fahrzeugboden. Ladestand, Ladeziel und Reichweite gehören zusammen
|
||||
und stehen deshalb beieinander im Bild; alles, was sich nicht
|
||||
zeichnen lässt, steht als Kennzahl daneben. -->
|
||||
<div class="row">
|
||||
|
||||
<!-- Das Fahrzeug als Anzeigeelement. Jedes Teil trägt seinen Zustand
|
||||
selbst: offene Türen und Klappen leuchten bernstein, Licht gelb,
|
||||
der Ladeanschluss pulst beim Laden. Die Zeile darunter benennt
|
||||
dasselbe in Worten und hebt beim Überfahren das zugehörige Teil
|
||||
hervor - Farbe allein soll die Aussage nicht tragen. -->
|
||||
<div class="col-xxl-4 col-lg-5 d-flex align-items-stretch">
|
||||
<div class="col-12">
|
||||
<div class="card mb-4 p-0 w-100">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Fahrzeug</h3>
|
||||
<div class="card-tools">
|
||||
<h3 class="card-title">Übersicht</h3>
|
||||
<div class="card-tools d-flex align-items-center gap-2">
|
||||
<span class="badge text-bg-secondary" id="skodaSchloss">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column align-items-center pt-2">
|
||||
|
||||
<svg id="skodaAuto" class="skoda-auto" viewBox="0 0 224 500"
|
||||
role="img" aria-label="Draufsicht des Fahrzeugs mit dem Zustand von Türen, Fenstern, Klappen und Licht">
|
||||
|
||||
<!-- Räder. Vorderachse bei 860 mm, Hinterachse bei 3625 mm
|
||||
ab Bugspitze - der Radstand von 2765 mm maßstäblich. -->
|
||||
<g class="rad">
|
||||
<rect x="5" y="68" width="21" height="58" rx="9"/>
|
||||
<rect x="198" y="68" width="21" height="58" rx="9"/>
|
||||
<rect x="5" y="362" width="21" height="58" rx="9"/>
|
||||
<rect x="198" y="362" width="21" height="58" rx="9"/>
|
||||
</g>
|
||||
|
||||
<!-- Karosserie -->
|
||||
<path class="karosserie" d="M112 6
|
||||
C150 6, 198 16, 204 50 C209 90, 212 160, 212 250
|
||||
C212 340, 209 410, 204 450 C198 484, 150 494, 112 494
|
||||
C74 494, 26 484, 20 450 C15 410, 12 340, 12 250
|
||||
C12 160, 15 90, 20 50 C26 16, 74 6, 112 6 Z"/>
|
||||
|
||||
<!-- Tech-Deck Face: die glänzend schwarze Blende über die
|
||||
volle Bugbreite, die den Enyaq seit der Überarbeitung
|
||||
kennzeichnet. Von oben sieht man ihre Oberkante. -->
|
||||
<path class="techdeck" d="M112 7
|
||||
C150 7, 197 17, 203 49 L194 53 C187 27, 148 18, 112 18
|
||||
C76 18, 37 27, 30 53 L21 49 C27 17, 74 7, 112 7 Z"/>
|
||||
|
||||
<!-- Scheinwerfer, vier Elemente je Seite wie beim Original,
|
||||
dazu die Rückleuchten als durchgehendes Band. -->
|
||||
<g id="teil-lights" class="teil teil-licht" data-teil="lights">
|
||||
<rect x="36" y="30" width="9" height="13" rx="4"/>
|
||||
<rect x="48" y="27" width="9" height="13" rx="4"/>
|
||||
<rect x="60" y="25" width="9" height="13" rx="4"/>
|
||||
<rect x="72" y="24" width="9" height="13" rx="4"/>
|
||||
<rect x="143" y="24" width="9" height="13" rx="4"/>
|
||||
<rect x="155" y="25" width="9" height="13" rx="4"/>
|
||||
<rect x="167" y="27" width="9" height="13" rx="4"/>
|
||||
<rect x="179" y="30" width="9" height="13" rx="4"/>
|
||||
<rect class="rueck" x="40" y="462" width="52" height="13" rx="6"/>
|
||||
<rect class="rueck" x="132" y="462" width="52" height="13" rx="6"/>
|
||||
<title>Beleuchtung</title>
|
||||
</g>
|
||||
|
||||
<!-- Motorhaube mit angedeuteter Mittelsicke -->
|
||||
<g id="teil-bonnet" class="teil" tabindex="0" data-teil="bonnet">
|
||||
<path d="M31 152 L193 152 L189 62 C169 40, 55 40, 35 62 Z"/>
|
||||
<title>Motorhaube</title>
|
||||
</g>
|
||||
<path class="sicke" d="M112 60 L112 150"/>
|
||||
|
||||
<!-- Windschutzscheibe, Dach, Heckscheibe -->
|
||||
<path class="scheibe" d="M34 156 L190 156 L178 200 L46 200 Z"/>
|
||||
<rect class="dach" x="46" y="200" width="132" height="136" rx="10"/>
|
||||
|
||||
<!-- Panoramaglasdach: beim Enyaq nahezu die ganze Dachfläche -->
|
||||
<g id="teil-sunroof" class="teil" tabindex="0" data-teil="sunroof">
|
||||
<rect x="62" y="206" width="100" height="120" rx="8"/>
|
||||
<title>Panoramadach</title>
|
||||
</g>
|
||||
|
||||
<!-- Dachreling -->
|
||||
<g class="reling">
|
||||
<rect x="48" y="204" width="9" height="134" rx="4"/>
|
||||
<rect x="167" y="204" width="9" height="134" rx="4"/>
|
||||
</g>
|
||||
|
||||
<path class="scheibe" d="M46 336 L178 336 L190 382 L34 382 Z"/>
|
||||
<rect class="spoiler" x="52" y="330" width="120" height="9" rx="4"/>
|
||||
|
||||
<!-- Heckklappe -->
|
||||
<g id="teil-trunk" class="teil" tabindex="0" data-teil="trunk">
|
||||
<path d="M34 384 L190 384 L186 458 C168 478, 56 478, 38 458 Z"/>
|
||||
<title>Heckklappe</title>
|
||||
</g>
|
||||
|
||||
<!-- Türen, jeweils mit Seitenfenster darin -->
|
||||
<g id="teil-doors" class="teil" data-teil="doors">
|
||||
<g tabindex="0"><rect x="12" y="202" width="23" height="74" rx="6"/><title>Tür vorn links</title></g>
|
||||
<g tabindex="0"><rect x="12" y="278" width="23" height="66" rx="6"/><title>Tür hinten links</title></g>
|
||||
<g tabindex="0"><rect x="189" y="202" width="23" height="74" rx="6"/><title>Tür vorn rechts</title></g>
|
||||
<g tabindex="0"><rect x="189" y="278" width="23" height="66" rx="6"/><title>Tür hinten rechts</title></g>
|
||||
</g>
|
||||
<g id="teil-windows" class="teil teil-fenster" data-teil="windows">
|
||||
<rect x="17" y="210" width="13" height="58" rx="4"/>
|
||||
<rect x="17" y="286" width="13" height="50" rx="4"/>
|
||||
<rect x="194" y="210" width="13" height="58" rx="4"/>
|
||||
<rect x="194" y="286" width="13" height="50" rx="4"/>
|
||||
<title>Seitenfenster</title>
|
||||
</g>
|
||||
|
||||
<!-- Außenspiegel -->
|
||||
<g class="spiegel">
|
||||
<rect x="1" y="194" width="16" height="10" rx="5"/>
|
||||
<rect x="207" y="194" width="16" height="10" rx="5"/>
|
||||
</g>
|
||||
|
||||
<!-- ============================================================
|
||||
Steuerung: die beiden anklickbaren Stellen.
|
||||
|
||||
Bedient wird das, worum es geht - der Ladeanschluss für
|
||||
das Laden, der Innenraum für Klima und Lüftung. Beide
|
||||
tragen einen Ring als Hinweis darauf, dass sie mehr sind
|
||||
als Anzeige, und öffnen ein Modal mit den Befehlen.
|
||||
|
||||
Sie stehen bewusst NICHT in der Zustandszeile darunter:
|
||||
die zeigt, was ist. Diese beiden ändern etwas.
|
||||
============================================================ -->
|
||||
|
||||
<!-- Ladeanschluss. Beim Enyaq sitzt die Klappe am hinteren
|
||||
rechten Kotflügel; sie pulst, solange geladen wird. -->
|
||||
<g id="teil-plug" class="teil teil-stecker steuer" tabindex="0"
|
||||
role="button" data-steuer="laden" aria-label="Laden steuern">
|
||||
<circle class="ring" cx="203" cy="408" r="17"/>
|
||||
<circle cx="203" cy="408" r="11"/>
|
||||
<path d="M198 402 l6 5 -4 0 4 6"/>
|
||||
<title>Laden steuern</title>
|
||||
</g>
|
||||
|
||||
<!-- Innenraum, auf der Windschutzscheibe: Klima und Lüftung. -->
|
||||
<g id="steuer-klima" class="steuer" tabindex="0"
|
||||
role="button" data-steuer="klima" aria-label="Klima und Lüftung steuern">
|
||||
<circle class="ring" cx="112" cy="178" r="19"/>
|
||||
<circle class="flaeche" cx="112" cy="178" r="13"/>
|
||||
<path class="glyphe" d="M112 170 v10 M108 174 h8 M105 185 q7 -4 14 0"/>
|
||||
<title>Klima und Lüftung steuern</title>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div class="skoda-teile" id="skodaTeile"></div>
|
||||
<p class="skoda-hinweis">
|
||||
Ladeanschluss und Innenraum antippen, um zu steuern
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Batterie, Reichweite, Ladung -->
|
||||
<div class="col-xxl-8 col-lg-7 d-flex align-items-stretch">
|
||||
<div class="card mb-4 p-0 w-100">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Batterie und Ladung</h3>
|
||||
<div class="card-tools">
|
||||
<span class="badge text-bg-secondary" id="skodaLadeZustand">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center g-3">
|
||||
<div class="skoda-fahrzeug">
|
||||
|
||||
<!-- Ladestandsring. Der Strich auf dem Ring ist das
|
||||
eingestellte Ladeziel - eine Ladung, die dort endet, ist
|
||||
kein Fehler, sondern Absicht. -->
|
||||
<div class="col-md-5 text-center">
|
||||
<svg class="skoda-ring" viewBox="0 0 200 200" role="img" aria-labelledby="skodaSocText">
|
||||
<circle class="ring-bahn" cx="100" cy="100" r="82"/>
|
||||
<circle class="ring-wert" id="skodaRing" cx="100" cy="100" r="82"/>
|
||||
<line class="ring-ziel" id="skodaZiel" x1="100" y1="8" x2="100" y2="26"/>
|
||||
<text id="skodaSocText" class="ring-zahl" x="100" y="98">–</text>
|
||||
<text class="ring-einheit" x="100" y="126">Prozent</text>
|
||||
<!-- Das Fahrzeug als Anzeigeelement. Jedes Teil trägt seinen
|
||||
Zustand selbst: offene Türen und Klappen leuchten
|
||||
bernstein, Licht gelb, der Ladeanschluss pulst beim Laden.
|
||||
Die Zustandszeile rechts benennt dasselbe in Worten und
|
||||
hebt beim Überfahren das zugehörige Teil hervor - Farbe
|
||||
allein soll die Aussage nicht tragen. -->
|
||||
<div class="skoda-bild">
|
||||
<svg id="skodaAuto" class="skoda-auto" viewBox="-16 -8 256 516"
|
||||
role="img" aria-label="Draufsicht des Fahrzeugs mit Ladestand der Batterie und dem Zustand von Türen, Fenstern, Klappen und Licht">
|
||||
|
||||
<defs>
|
||||
<!-- Der Verlauf der Füllung ist am Fahrzeug verankert,
|
||||
nicht am Füllrechteck: sonst wanderte er mit dem
|
||||
Ladestand mit und die Farbe unten änderte sich bei
|
||||
jedem Prozentpunkt. Die Farbe selbst kommt aus
|
||||
--akku-farbe, gesetzt von der Zustandsklasse am
|
||||
SVG-Element; das Attribut hier ist der Rückfall. -->
|
||||
<linearGradient id="akkuVerlauf" gradientUnits="userSpaceOnUse"
|
||||
x1="0" y1="378" x2="0" y2="108">
|
||||
<stop offset="0" stop-color="#7aa7c7" stop-opacity=".75"/>
|
||||
<stop offset="1" stop-color="#7aa7c7" stop-opacity=".3"/>
|
||||
</linearGradient>
|
||||
|
||||
<!-- Innenkontur der Platte: schneidet Füllung, Strömung
|
||||
und Pegelkante auf die abgerundete Form zu. -->
|
||||
<clipPath id="akkuInnen">
|
||||
<rect x="50" y="108" width="124" height="270" rx="7"/>
|
||||
</clipPath>
|
||||
|
||||
<!-- Auf Höhe des Ladestands beschnitten, damit die
|
||||
Strömung nur im gefüllten Teil läuft. Das Rechteck
|
||||
darin setzt zeichneAkku() mit. -->
|
||||
<clipPath id="akkuPegel">
|
||||
<rect id="akkuPegelFeld" x="50" y="378" width="124" height="0"/>
|
||||
</clipPath>
|
||||
|
||||
<!-- Ein Flügel des Lüfterrads, um die eigene Nabe im
|
||||
Ursprung gezeichnet. Das Rad setzt ihn dreimal um je
|
||||
120 Grad versetzt ein. -->
|
||||
<path id="klimaFluegel"
|
||||
d="M0 0 C 1.5 -4, 5 -7, 7.5 -5 C 9.6 -3.2, 5.5 -0.8, 0 0 Z"/>
|
||||
</defs>
|
||||
|
||||
<!-- Räder. Vorderachse bei 860 mm, Hinterachse bei 3625 mm
|
||||
ab Bugspitze - der Radstand von 2765 mm maßstäblich. -->
|
||||
<g class="rad">
|
||||
<rect x="5" y="68" width="21" height="58" rx="9"/>
|
||||
<rect x="198" y="68" width="21" height="58" rx="9"/>
|
||||
<rect x="5" y="362" width="21" height="58" rx="9"/>
|
||||
<rect x="198" y="362" width="21" height="58" rx="9"/>
|
||||
</g>
|
||||
|
||||
<!-- Karosserie -->
|
||||
<path class="karosserie" d="M112 6
|
||||
C150 6, 198 16, 204 50 C209 90, 212 160, 212 250
|
||||
C212 340, 209 410, 204 450 C198 484, 150 494, 112 494
|
||||
C74 494, 26 484, 20 450 C15 410, 12 340, 12 250
|
||||
C12 160, 15 90, 20 50 C26 16, 74 6, 112 6 Z"/>
|
||||
|
||||
<!-- Tech-Deck Face: die glänzend schwarze Blende über die
|
||||
volle Bugbreite, die den Enyaq seit der Überarbeitung
|
||||
kennzeichnet. Von oben sieht man ihre Oberkante, also
|
||||
ein schmales Band, das zu den Kotflügeln hin ausläuft.
|
||||
|
||||
Schmal und nur halb deckend: die Blende steht am Auto
|
||||
fast senkrecht, von oben ist sie deshalb ein Streifen
|
||||
und keine Fläche. Als breites, fast schwarzes Band war
|
||||
sie das dunkelste im Bild und las sich wie ein Loch.
|
||||
Die Enden laufen spitz zusammen statt gerade abzu-
|
||||
schneiden - ein gerader Schnitt ergab dort zwei Stufen,
|
||||
die es am Auto nicht gibt. -->
|
||||
<path class="techdeck" d="M22 46
|
||||
C 30 17, 74 7, 112 7 C 150 7, 194 17, 202 46
|
||||
C 196 24, 156 17, 112 17 C 68 17, 28 24, 22 46 Z"/>
|
||||
|
||||
<!-- Scheinwerfer, vier Elemente je Seite wie beim Original,
|
||||
dazu die Rückleuchten als durchgehendes Band. -->
|
||||
<g id="teil-lights" class="teil teil-licht" data-teil="lights">
|
||||
<rect x="36" y="30" width="9" height="13" rx="4"/>
|
||||
<rect x="48" y="27" width="9" height="13" rx="4"/>
|
||||
<rect x="60" y="25" width="9" height="13" rx="4"/>
|
||||
<rect x="72" y="24" width="9" height="13" rx="4"/>
|
||||
<rect x="143" y="24" width="9" height="13" rx="4"/>
|
||||
<rect x="155" y="25" width="9" height="13" rx="4"/>
|
||||
<rect x="167" y="27" width="9" height="13" rx="4"/>
|
||||
<rect x="179" y="30" width="9" height="13" rx="4"/>
|
||||
<rect class="rueck" x="40" y="462" width="52" height="13" rx="6"/>
|
||||
<rect class="rueck" x="132" y="462" width="52" height="13" rx="6"/>
|
||||
<title>Beleuchtung</title>
|
||||
</g>
|
||||
|
||||
<!-- Motorhaube mit angedeuteter Mittelsicke -->
|
||||
<g id="teil-bonnet" class="teil" tabindex="0" data-teil="bonnet">
|
||||
<path d="M31 152 L193 152 L189 62 C169 40, 55 40, 35 62 Z"/>
|
||||
<title>Motorhaube</title>
|
||||
</g>
|
||||
<path class="sicke" d="M112 60 L112 150"/>
|
||||
|
||||
<!-- Windschutzscheibe, Dach, Heckscheibe -->
|
||||
<path class="scheibe" d="M34 156 L190 156 L178 200 L46 200 Z"/>
|
||||
<rect class="dach" x="46" y="200" width="132" height="136" rx="10"/>
|
||||
|
||||
<!-- Kein Panoramadach: dieses Fahrzeug hat ein geschlossenes
|
||||
Blechdach. Die Zustandszeile kennt es deshalb auch nicht
|
||||
(TEILE in js/solar/skodaMQTT.js). -->
|
||||
|
||||
<!-- Dachreling. Sie liegt AUF dem Dach: nach innen gerückt,
|
||||
nicht auf dessen Seitenkanten, und sie bleibt zwischen
|
||||
den Scheiben - vorn hinter der Windschutzscheibe, hinten
|
||||
am Spoiler. Läge sie bündig auf den Kanten, ergäbe sie
|
||||
mit Platte und Spoiler ein geschlossenes Rechteck, und
|
||||
das sieht aus wie ein Fensterrahmen. Die runden Enden
|
||||
kommen von der Strichstärke, nicht aus der Geometrie. -->
|
||||
<g class="reling">
|
||||
<line x1="54" y1="206" x2="54" y2="330"/>
|
||||
<line x1="170" y1="206" x2="170" y2="330"/>
|
||||
</g>
|
||||
|
||||
<path class="scheibe" d="M46 336 L178 336 L190 382 L34 382 Z"/>
|
||||
<!-- Heckspoiler: ein Blatt über der Dachkante, in der Mitte
|
||||
breit, zu den Enden auslaufend - so sitzt er auf der
|
||||
Kante statt sie nachzuzeichnen. -->
|
||||
<path class="spoiler" d="M44 336 C 74 327, 150 327, 180 336
|
||||
C 150 344, 74 344, 44 336 Z"/>
|
||||
|
||||
<!-- ========================================================
|
||||
Antriebsbatterie - der Ladestand selbst.
|
||||
|
||||
Beim MEB liegt der Akku als flache Platte zwischen den
|
||||
Achsen; genau dort sitzt sie hier auch, von 108 bis 378
|
||||
über den Radstand. Gefüllt wird von hinten nach vorn,
|
||||
also im Bild von unten nach oben - dieselbe Richtung,
|
||||
in der jeder einen Füllstand liest.
|
||||
|
||||
Die Reihenfolge ist Absicht und gilt ohne Ausnahme:
|
||||
die Platte liegt ÜBER allem, was nur Blech und Glas ist
|
||||
- Karosserie, Haube, beide Scheiben, Dach, Reling,
|
||||
Spoiler -, und UNTER allem, was Zustand zeigt oder
|
||||
bedient wird: Türen, Fenster, Klappen, Licht und die
|
||||
beiden Knöpfe. Eine offene Klappe muss sichtbar
|
||||
bleiben, auch wenn die Batterie darunter liegt - der
|
||||
Zustand hat Vorrang vor der Anzeige.
|
||||
|
||||
Deshalb steht dieser Block hier und nicht weiter oben:
|
||||
stand er vor der Heckscheibe, lag die Platte über der
|
||||
Windschutzscheibe, aber unter der Heckscheibe, und die
|
||||
Relingstäbe schwammen auf ihr.
|
||||
|
||||
Alles Veränderliche - Füllhöhe, Zielmarke, Zahlen und
|
||||
Farbe - setzt zeichneAkku().
|
||||
======================================================== -->
|
||||
<g id="akku" class="akku">
|
||||
<rect class="huelle" x="46" y="104" width="132" height="278" rx="10"/>
|
||||
|
||||
<g clip-path="url(#akkuInnen)">
|
||||
<rect class="fuellung" id="akkuFuellung" x="50" y="378" width="124" height="0"/>
|
||||
|
||||
<!-- Ladestrom: Streifen, die im gefüllten Teil nach
|
||||
vorn wandern. Nur beim Laden sichtbar. -->
|
||||
<g class="stroemung" clip-path="url(#akkuPegel)">
|
||||
<g class="bahn">
|
||||
<rect x="50" y="430" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="376" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="322" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="268" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="214" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="160" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="106" width="124" height="3" rx="1.5"/>
|
||||
<rect x="50" y="52" width="124" height="3" rx="1.5"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Pegelkante: die helle Linie oben auf der Füllung.
|
||||
Sie macht den Stand auch dann ablesbar, wenn die
|
||||
Fläche darunter nur schwach getönt ist. -->
|
||||
<rect class="kante" id="akkuKante" x="50" y="377" width="124" height="2.5"/>
|
||||
</g>
|
||||
|
||||
<!-- Modulfugen. Sie liegen auf 20, 40, 60 und 80 Prozent
|
||||
und dienen damit zugleich als Skala. -->
|
||||
<g class="fugen">
|
||||
<line x1="50" y1="324" x2="174" y2="324"/>
|
||||
<line x1="50" y1="270" x2="174" y2="270"/>
|
||||
<line x1="50" y1="216" x2="174" y2="216"/>
|
||||
<line x1="50" y1="162" x2="174" y2="162"/>
|
||||
</g>
|
||||
|
||||
<!-- Ladeziel. Eine Ladung, die dort endet, ist kein
|
||||
Fehler, sondern Absicht - deshalb steht die Marke im
|
||||
Bild und nicht nur als Zahl daneben. -->
|
||||
<g class="zielmarke" id="akkuZielMarke">
|
||||
<line id="akkuZiel" x1="44" y1="108" x2="180" y2="108"/>
|
||||
<text id="akkuZielText" x="174" y="102">Ziel –</text>
|
||||
</g>
|
||||
|
||||
<!-- Ladestand und Reichweite in der Platte. Der dunkle
|
||||
Umriss der Zahlen (paint-order) hält sie lesbar,
|
||||
gleich ob Füllung oder leere Zelle dahinter liegt.
|
||||
|
||||
Die Höhe ist gerechnet, nicht geschätzt: die Platte
|
||||
reicht von 108 bis 378, ist also 270 hoch; der
|
||||
Goldene Schnitt von oben liegt bei 108 + 270 × 0,618
|
||||
= 274,9. Dort sitzt die optische Mitte der beiden
|
||||
Zeilen (Oberkante der Ziffern 246, Grundlinie der
|
||||
Reichweite 303). Weiter oben klebten sie am
|
||||
Klimaknopf, dessen Ring bei 205 endet - jetzt liegen
|
||||
41 Einheiten Luft dazwischen. -->
|
||||
<text class="wert" x="112" y="275">
|
||||
<tspan id="akkuText">–</tspan><tspan class="einheit" dx="3">%</tspan>
|
||||
</text>
|
||||
<text class="neben" id="akkuNeben" x="112" y="303">–</text>
|
||||
|
||||
<title id="akkuTitel">Antriebsbatterie</title>
|
||||
</g>
|
||||
|
||||
<!-- Heckklappe -->
|
||||
<g id="teil-trunk" class="teil" tabindex="0" data-teil="trunk">
|
||||
<path d="M34 384 L190 384 L186 458 C168 478, 56 478, 38 458 Z"/>
|
||||
<title>Heckklappe</title>
|
||||
</g>
|
||||
|
||||
<!-- Türen, jeweils mit Seitenfenster darin -->
|
||||
<g id="teil-doors" class="teil" data-teil="doors">
|
||||
<g tabindex="0"><rect x="12" y="202" width="23" height="74" rx="6"/><title>Tür vorn links</title></g>
|
||||
<g tabindex="0"><rect x="12" y="278" width="23" height="66" rx="6"/><title>Tür hinten links</title></g>
|
||||
<g tabindex="0"><rect x="189" y="202" width="23" height="74" rx="6"/><title>Tür vorn rechts</title></g>
|
||||
<g tabindex="0"><rect x="189" y="278" width="23" height="66" rx="6"/><title>Tür hinten rechts</title></g>
|
||||
</g>
|
||||
<g id="teil-windows" class="teil teil-fenster" data-teil="windows">
|
||||
<rect x="17" y="210" width="13" height="58" rx="4"/>
|
||||
<rect x="17" y="286" width="13" height="50" rx="4"/>
|
||||
<rect x="194" y="210" width="13" height="58" rx="4"/>
|
||||
<rect x="194" y="286" width="13" height="50" rx="4"/>
|
||||
<title>Seitenfenster</title>
|
||||
</g>
|
||||
|
||||
<!-- Außenspiegel -->
|
||||
<g class="spiegel">
|
||||
<rect x="1" y="194" width="16" height="10" rx="5"/>
|
||||
<rect x="207" y="194" width="16" height="10" rx="5"/>
|
||||
</g>
|
||||
|
||||
<!-- ========================================================
|
||||
Steuerung: die beiden anklickbaren Stellen.
|
||||
|
||||
Bedient wird das, worum es geht - der Ladeanschluss für
|
||||
das Laden, der Innenraum für Klima und Lüftung. Beide
|
||||
tragen einen Ring als Hinweis darauf, dass sie mehr
|
||||
sind als Anzeige, und öffnen ein Modal mit den
|
||||
Befehlen.
|
||||
|
||||
Sie stehen bewusst NICHT in der Zustandszeile: die
|
||||
zeigt, was ist. Diese beiden ändern etwas.
|
||||
======================================================== -->
|
||||
|
||||
<!-- Der Weg des Ladestroms vom Anschluss in die Platte.
|
||||
Läuft nur, solange geladen wird. -->
|
||||
<path class="ladefluss" d="M199 399 C 192 393, 183 386, 176 376"/>
|
||||
|
||||
<!-- Ladeanschluss. Beim Enyaq sitzt die Klappe am hinteren
|
||||
rechten Kotflügel.
|
||||
|
||||
Gleiche Bauweise wie der Klimaknopf: gestrichelter Ring,
|
||||
deckende Scheibe, Zeichen darauf. Beides sind Knöpfe, also
|
||||
sehen beide gleich aus - was sie unterscheidet, ist das
|
||||
Zeichen und beim Laden die Farbe. Die deckende Scheibe ist
|
||||
auch hier nötig, weil darunter die Akkuplatte durchläuft. -->
|
||||
<g id="teil-plug" class="teil-stecker steuer" tabindex="0"
|
||||
role="button" data-steuer="laden" aria-label="Laden steuern">
|
||||
<circle class="ring" cx="203" cy="408" r="19"/>
|
||||
<circle class="flaeche" cx="203" cy="408" r="13"/>
|
||||
<path class="glyphe" d="M199 401 L207 407 L202 407 L207 415"/>
|
||||
<title>Laden steuern</title>
|
||||
</g>
|
||||
|
||||
<!-- Innenraum, auf der Windschutzscheibe: Klima und Lüftung. -->
|
||||
<g id="steuer-klima" class="steuer" tabindex="0"
|
||||
role="button" data-steuer="klima" aria-label="Klima und Lüftung steuern">
|
||||
<circle class="ring" cx="112" cy="186" r="19"/>
|
||||
<circle class="flaeche" cx="112" cy="186" r="13"/>
|
||||
<!-- Lüfterrad. Es dreht sich, solange die Klimatisierung
|
||||
läuft; die Drehung sitzt in der inneren Gruppe, damit
|
||||
sie die Verschiebung nach aussen nicht überschreibt. -->
|
||||
<g class="luefter" transform="translate(112 186)">
|
||||
<g class="blaetter">
|
||||
<use href="#klimaFluegel"/>
|
||||
<use href="#klimaFluegel" transform="rotate(120)"/>
|
||||
<use href="#klimaFluegel" transform="rotate(240)"/>
|
||||
</g>
|
||||
<circle class="nabe" r="1.9"/>
|
||||
</g>
|
||||
<title>Klima und Lüftung steuern</title>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<p class="skoda-hinweis">
|
||||
Ladeanschluss und Innenraum antippen, um zu steuern
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="skoda-werte">
|
||||
<div class="skoda-wert"><span class="k">Reichweite</span><span class="v" id="skodaReichweite">–</span></div>
|
||||
<div class="skoda-wert"><span class="k">Ladeleistung</span><span class="v" id="skodaLeistung">–</span></div>
|
||||
<div class="skoda-wert"><span class="k">Restzeit</span><span class="v" id="skodaRestzeit">–</span></div>
|
||||
<div class="skoda-wert"><span class="k">Ladeziel</span><span class="v" id="skodaZielText">–</span></div>
|
||||
<div class="skoda-wert"><span class="k">Kilometerstand</span><span class="v" id="skodaKm">–</span></div>
|
||||
<div class="skoda-wert"><span class="k">Klima</span><span class="v" id="skodaKlima">–</span></div>
|
||||
<!-- Alles, was sich nicht ins Bild zeichnen lässt. -->
|
||||
<div class="skoda-daten">
|
||||
|
||||
<div class="skoda-kennzahlen">
|
||||
<div class="kennzahl" id="skodaKachelLeistung">
|
||||
<span class="zeichen"><i class="bi bi-lightning-charge-fill" aria-hidden="true"></i></span>
|
||||
<span class="text">
|
||||
<span class="k">Ladeleistung</span>
|
||||
<span class="v" id="skodaLeistung">–</span>
|
||||
<span class="neben" id="skodaLadeart"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kennzahl" id="skodaKachelRestzeit">
|
||||
<span class="zeichen"><i class="bi bi-hourglass-split" aria-hidden="true"></i></span>
|
||||
<span class="text">
|
||||
<span class="k">Restzeit</span>
|
||||
<span class="v" id="skodaRestzeit">–</span>
|
||||
<span class="neben" id="skodaFertig"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kennzahl">
|
||||
<span class="zeichen"><i class="bi bi-speedometer2" aria-hidden="true"></i></span>
|
||||
<span class="text">
|
||||
<span class="k">Kilometer­stand</span>
|
||||
<span class="v" id="skodaKm">–</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="kennzahl" id="skodaKachelKlima">
|
||||
<span class="zeichen"><i class="bi bi-fan" id="skodaKlimaZeichen" aria-hidden="true"></i></span>
|
||||
<span class="text">
|
||||
<span class="k">Klima</span>
|
||||
<span class="v" id="skodaKlima">–</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Woher der Strom gerade kommt: der Balken teilt die
|
||||
Ladeleistung in den Anteil, den die PV deckt, und den
|
||||
Rest aus dem Netz. -->
|
||||
<div class="skoda-quelle mt-3" id="skodaQuelleBox">
|
||||
<div class="d-flex justify-content-between small text-secondary mb-1">
|
||||
<span>Ladestrom</span><span id="skodaQuelleText">–</span>
|
||||
Rest aus dem Netz. Steht nur da, solange geladen wird -
|
||||
ohne Ladung hat der Balken nichts zu teilen. -->
|
||||
<div class="skoda-quelle" id="skodaQuelleBox" hidden>
|
||||
<div class="kopf">
|
||||
<span class="skoda-marke">Ladestrom</span>
|
||||
<span class="summe" id="skodaQuelleText">–</span>
|
||||
</div>
|
||||
<div class="skoda-balken">
|
||||
<div class="teil-pv" id="skodaAnteilPV"></div>
|
||||
<div class="teil-netz" id="skodaAnteilNetz"></div>
|
||||
</div>
|
||||
<div class="legende">
|
||||
<span><i class="pkt pv"></i>Sonne <b id="skodaQuellePV">–</b></span>
|
||||
<span><i class="pkt netz"></i>Netz <b id="skodaQuelleNetz">–</b></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Was das Fahrzeug in Woche, Monat und Jahr gefahren ist.
|
||||
Gerechnet aus dem Kilometerstand, nicht aus den erkannten
|
||||
Fahrten - siehe ajax/skoda.php, ?was=strecke. -->
|
||||
<div class="skoda-fahrleistung">
|
||||
<span class="skoda-marke">Fahrleistung</span>
|
||||
<div class="reihe" id="skodaFahrleistung"></div>
|
||||
</div>
|
||||
|
||||
<!-- Zustandszeile: benennt in Worten, was die Farben im Bild
|
||||
zeigen, und hebt beim Überfahren das Teil hervor. -->
|
||||
<div class="skoda-zustand">
|
||||
<span class="skoda-marke">Zustand</span>
|
||||
<div class="skoda-teile" id="skodaTeile"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+16
-17
@@ -64,23 +64,6 @@
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
||||
<div class="card p-0 mb-4" style="width:100%">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Statistik dieses Jahr</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-cols-1 row-cols-md-1 g-4 p-2" id="Stats-Year">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
||||
<div class="card p-0 mb-4" style="width:100%">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
@@ -118,6 +101,22 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xxl-12 col-xl-12 col-lg-12 col-md-12 d-flex align-items-stretch">
|
||||
<div class="card p-0 mb-4" style="width:100%">
|
||||
<div class="card-header pb-0 pt-1">
|
||||
<h3 class="card-title">Statistik dieses Jahr</h3>
|
||||
<div class="card-tools">
|
||||
<button type="button" class="btn btn-tool" data-lte-toggle="card-remove">
|
||||
<i class="bi bi-x-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Gefuellt von baueJahresstatistik() aus js/solar/jahresstatistik.js -
|
||||
dieselbe Tabelle wie auf der Historienseite, nur einspaltig. -->
|
||||
<div class="card-body statistik" id="Stats-Year"></div>
|
||||
</div>
|
||||
<!-- /.card -->
|
||||
</div>
|
||||
|
||||
<!-- Consumption Graph-->
|
||||
<div class="col-lg-12">
|
||||
|
||||
Reference in New Issue
Block a user