Files
adminandClaude Opus 5 07bca5104e Einstellungsseite: Reiter, Kachelwerte und Geraeteliste
Die Einstellungen bestanden aus einer einzigen langen Spalte mit den
Preistabellen. Sie bekommt Reiter, und darin zwei neue Flaechen: was die
Kacheln der Startseite anzeigen, und welche Geraete es ueberhaupt gibt.

Reiter gab es vorher dreimal in drei Ausfuehrungen - die Etagen ueber den
Automatismen mit eigenem switchTab(), die Geraetegruppen im Raum-Modal mit
eigener CSS-Klasse, und die Einstellungsseite noch einmal anders. Jetzt baut
restricted/reiter.php alle drei; umgeschaltet wird von Bootstrap, den offenen
Reiter merkt reiterMerken() in der Adresse.

Kachelwerte: was auf einer Kachel steht, stand fest in rooms.php. Die neue
Tabelle kachelWerte (solarLog_kacheln.sql) ueberschreibt das je Raum, die
Maske waehlt die Messwerte aus demselben Katalog wie der Automatik-Editor -
nach Etage sortiert, die Geraete des eigenen Raums oben. Fehlt die Tabelle,
zeigt jede Kachel weiter ihre Vorgabe.

Geraete: die Zuordnung Geraet -> Raum war ein Modal, erreichbar ueber ein
Zahnrad neben den Automatismen. Sie ist eine Einstellung und steht jetzt dort,
zusammen mit dem, was fehlte: seit wann ein Geraet nichts mehr meldet, in
welchen Automatiken es steckt, und ein Loeschknopf. Letzterer ist noetig, weil
der Suchlauf nichts loescht - er legt an und frischt auf, damit die Automatiken
ihre Zustands-Ids behalten; ein abgebautes Geraet bliebe sonst fuer immer
stehen. Geloescht wird nur, was in keiner Automatik vorkommt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 22:45:24 +02:00

129 lines
5.2 KiB
PHP

<?php
/**
* Reiter - eine Bauart für die ganze Seite.
*
* Vorher gab es drei: die Etagenreiter über den Automatiken (Home) mit einem
* eigenen switchTab() in JavaScript, die Gerätegruppen im Raum-Modal als
* Bootstrap-Reiter mit eigener CSS-Klasse, und die Einstellungsseite noch
* einmal anders. Drei Markups, zwei Verhaltensweisen, ein Sonderaussehen -
* und jede Änderung am Erscheinungsbild wäre dreimal zu machen gewesen.
*
* Hier steht nur noch, wie ein Reiter aussieht und heißt. Umgeschaltet wird
* von Bootstrap (data-bs-toggle="tab"); eigenes JavaScript dafür gibt es
* nicht mehr. Wer sich den offenen Reiter merken will, setzt "merken" - das
* übernimmt reiterMerken() in js/solar/common.js für alle Leisten der Seite.
*
* Ein Reiter ist:
*
* id Kürzel, taucht in den Element-Ids und in der Adresse auf
* titel Beschriftung
* symbol optional ein Bootstrap-Icon ("bi-currency-euro")
* titel2 optional der lange Name als Sprechblase (Etagenkürzel!)
* inhalt nur für reiterBlock(): der fertige Inhalt der Fläche
*
* Aufrufarten - je nachdem, ob der Inhalt schon als Text vorliegt:
*
* reiterBlock($reiter, $opt) Leiste und Flächen am Stück
* reiterLeiste($reiter, $opt) nur die Leiste ...
* reiterFlaecheAuf($id, $opt) ... und die Flächen einzeln, wenn der
* reiterFlaecheZu() Inhalt im Template steht
*/
/**
* Die Kennung eines Reiters. Der Name der Leiste steht davor, damit zwei
* Leisten auf derselben Seite sich nicht dieselben Ids teilen.
*/
function reiterId($opt, $id, $anhang = "")
{
return ($opt["name"] ?? "reiter") . "-" . $id . $anhang;
}
/**
* Welcher Reiter offen ist: der ausdrücklich genannte, sonst der erste.
*
* Als eigene Funktion, weil Leiste und Flächen dieselbe Antwort brauchen -
* und weil "der erste" bei den Flächen sonst noch einmal ausgerechnet würde.
*/
function reiterAktiv($reiter, $opt)
{
$ids = array_column($reiter, "id");
$aktiv = $opt["aktiv"] ?? null;
return ($aktiv !== null && in_array($aktiv, $ids, true)) ? $aktiv : ($ids[0] ?? null);
}
/**
* Die Leiste.
*
* $opt:
* name Namensraum der Ids; sollte gesetzt sein, sonst "reiter"
* aktiv id des offenen Reiters (Vorgabe: der erste)
* merken true: der offene Reiter steht in der Adresse und übersteht ein
* Neuladen. Nur eine Leiste je Seite sollte das tun.
* schmal true: kleinere Reiter, die waagerecht scrollen statt umzubrechen
* - für enge Stellen wie das Raum-Modal
* klasse zusätzliche Klassen für die Leiste
*/
function reiterLeiste($reiter, $opt = [])
{
if (count($reiter) < 2 && empty($opt["immer"])) {
// Ein einzelner Reiter beschriftet nur sich selbst. Das Raum-Modal
// hat oft nur eine Gerätegruppe - dort ist die Leiste dann weg.
return "";
}
$aktiv = reiterAktiv($reiter, $opt);
$klassen = "nav nav-tabs reiter"
. (!empty($opt["schmal"]) ? " reiter-schmal" : "")
. (isset($opt["klasse"]) ? " " . $opt["klasse"] : "");
$html = "<ul class='" . $klassen . "' role='tablist'"
. (!empty($opt["merken"]) ? " data-reiter-merken='" . htmlspecialchars($opt["name"] ?? "reiter") . "'" : "")
. ">";
foreach ($reiter as $r) {
$offen = $r["id"] === $aktiv;
$html .= "<li class='nav-item' role='presentation'>"
. "<button class='nav-link" . ($offen ? " active" : "") . "' type='button' role='tab'"
. " id='" . reiterId($opt, $r["id"], "-tab") . "'"
. " data-bs-toggle='tab' data-bs-target='#" . reiterId($opt, $r["id"]) . "'"
. " data-reiter='" . htmlspecialchars($r["id"]) . "'"
. " aria-controls='" . reiterId($opt, $r["id"]) . "'"
. " aria-selected='" . ($offen ? "true" : "false") . "'"
. (isset($r["titel2"]) ? " title='" . htmlspecialchars($r["titel2"]) . "'" : "")
. ">"
// Der Abstand zum Text kommt aus dem Stylesheet, nicht aus
// einer Hilfsklasse: in der schmalen Fassung ist er kleiner.
. (isset($r["symbol"]) ? "<i class='bi " . htmlspecialchars($r["symbol"]) . "'></i>" : "")
. htmlspecialchars($r["titel"])
. "</button></li>";
}
return $html . "</ul>";
}
/** Anfang einer Fläche - der Inhalt steht danach im Template. */
function reiterFlaecheAuf($id, $opt = [])
{
$offen = ($opt["aktiv"] ?? null) === $id;
return "<div class='tab-pane fade" . ($offen ? " show active" : "") . "'"
. " id='" . reiterId($opt, $id) . "' role='tabpanel'"
. " aria-labelledby='" . reiterId($opt, $id, "-tab") . "'>";
}
/** Ende einer Fläche. Eigene Funktion, damit im Template kein nacktes </div> steht. */
function reiterFlaecheZu()
{
return "</div>";
}
/**
* Leiste und Flächen am Stück - für Aufrufer, die den Inhalt schon als Text
* haben (siehe zeichneGruppen() im Raum-Modal).
*/
function reiterBlock($reiter, $opt = [])
{
$opt["aktiv"] = reiterAktiv($reiter, $opt);
$html = reiterLeiste($reiter, $opt) . "<div class='tab-content'>";
foreach ($reiter as $r) {
$html .= reiterFlaecheAuf($r["id"], $opt) . ($r["inhalt"] ?? "") . reiterFlaecheZu();
}
return $html . "</div>";
}