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>
This commit is contained in:
2026-09-08 22:45:24 +02:00
co-authored by Claude Opus 5
parent 1b58579c22
commit 07bca5104e
15 changed files with 2058 additions and 226 deletions
+256 -15
View File
@@ -691,21 +691,6 @@ input[type='range']::-webkit-slider-runnable-track {
Beschriftungen nicht nebeneinander - dann scrollt die Leiste seitwaerts,
statt die Namen umzubrechen. Die durchgehende Linie unter den Reitern
darf dabei nicht mitscrollen, sie gehoert an den unteren Rand. */
.raum-tabs {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
flex-wrap: nowrap;
}
.raum-tabs::-webkit-scrollbar {
display: none;
}
.raum-tabs .nav-link {
padding: 0.3rem 0.7rem;
font-size: 0.8rem;
white-space: nowrap;
border-bottom-color: transparent;
}
/* Waehrend ein Kommando unterwegs ist. Ein "Zu" an einer Jalousie dauert
ueber eine Minute; ohne sichtbare Sperre tippt man in der Zeit nach. */
@@ -1022,6 +1007,71 @@ input[type='range']::-webkit-slider-runnable-track {
--skoda-blech: 128, 138, 148; /* alles Unbeteiligte am Fahrzeug */
}
/* --- Reiter --------------------------------------------------------------
Eine Bauart fuer die ganze Seite: Einstellungen, Raum-Modal, Automatiken.
Gebaut werden sie von restricted/reiter.php, umgeschaltet von Bootstrap.
Statt der Ordnerlaschen von Bootstrap eine Unterstreichung: die Laschen
brauchen einen Rahmen um die Flaeche darunter, damit sie nicht in der Luft
haengen - und der Rahmen ist genau das, was diese Oberflaeche sonst
nirgends hat. Der aktive Reiter traegt die Akzentfarbe.
Jede Regel nennt .nav-tabs UND .reiter, obwohl .reiter allein die Leiste
schon trifft. Das ist noetig, nicht schmueckend: adminlte.min.css wird nach
dieser Datei geladen und bringt eigene Regeln fuer .nav-tabs .nav-link mit.
Bei gleicher Spezifitaet gewaenne die spaetere - der aktive Reiter waere
weiss statt bernstein. */
.nav-tabs.reiter {
border-bottom: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
gap: .1rem;
}
.nav-tabs.reiter .nav-link {
border: 0;
border-bottom: 2px solid transparent;
border-radius: .35rem .35rem 0 0;
padding: .45rem .9rem;
color: var(--ton-schrift-2, #9aa6ad);
background: transparent;
transition: color .18s, background .18s, border-color .18s;
}
@media (hover: hover) {
.nav-tabs.reiter .nav-link:hover {
background: rgba(138, 148, 158, .08);
color: var(--ton-schrift, #e8ede9);
border-bottom-color: var(--ton-linie-satt, rgba(138, 148, 158, .38));
}
}
.nav-tabs.reiter .nav-link.active {
background: transparent;
color: var(--ton-sonne, #f0b429);
border-bottom-color: var(--ton-sonne, #f0b429);
}
.nav-tabs.reiter .nav-link:focus-visible {
outline: 2px solid var(--ton-sonne, #f0b429);
outline-offset: -2px;
}
.nav-tabs.reiter .nav-link i { margin-right: .5rem; }
/* Schmale Fassung fuer enge Stellen (Raum-Modal): kleinere Reiter, die
waagerecht scrollen statt umzubrechen - eine Zeile bleibt eine Zeile. */
.nav-tabs.reiter-schmal {
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
}
.nav-tabs.reiter-schmal::-webkit-scrollbar { display: none; }
.nav-tabs.reiter-schmal .nav-link {
padding: .3rem .7rem;
font-size: .8rem;
white-space: nowrap;
}
.nav-tabs.reiter-schmal .nav-link i { margin-right: .3rem; }
@media (prefers-reduced-motion: reduce) {
.nav-tabs.reiter .nav-link { transition: none; }
}
/* --- Aufbau der Fahrzeugkachel -------------------------------------------
Links das Bild, rechts die Zahlen dazu. Die Kachel bricht um, sobald die
Zahlenspalte unter 24rem faellt; das Bild behaelt dann seine Breite und
@@ -1816,6 +1866,197 @@ input[type='range']::-webkit-slider-runnable-track {
.skoda-alter { font-size: .85rem; }
/* === Einstellungen: Werte auf den Home-Kacheln ===========================
Eine Zeile je Raum, aufklappbar. Die Zeile zeigt im zugeklappten Zustand
schon, was auf der Kachel steht - sonst muesste man zwanzig Raeume
aufklappen, um einen zu finden. */
.kachel-etage { margin-bottom: 1.25rem; }
.kachel-etage:last-child { margin-bottom: 0; }
.kachel-etage-name {
font-size: .7rem;
letter-spacing: .13em;
text-transform: uppercase;
color: var(--ton-schrift-3, #6f7a81);
margin-bottom: .35rem;
}
.kachel-zeile {
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
border-radius: .5rem;
margin-bottom: .4rem;
overflow: hidden;
}
.kachel-zeile.offen { border-color: var(--ton-linie-satt, rgba(138, 148, 158, .38)); }
.kachel-kopf {
display: flex;
align-items: center;
gap: .6rem;
width: 100%;
padding: .5rem .75rem;
background: none;
border: 0;
color: inherit;
font: inherit;
text-align: left;
cursor: pointer;
}
@media (hover: hover) {
.kachel-kopf:hover { background: rgba(138, 148, 158, .08); }
}
.kachel-kopf .pfeil {
color: var(--ton-schrift-3, #6f7a81);
transition: transform .18s;
flex: 0 0 auto;
}
.kachel-zeile.offen .kachel-kopf .pfeil { transform: rotate(90deg); }
.kachel-kopf .name { min-width: 8rem; }
.kachel-kopf .werte {
display: flex;
flex-wrap: wrap;
gap: .3rem;
flex: 1 1 auto;
}
.kachel-chip {
font-size: .72rem;
padding: .1rem .45rem;
border: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
border-radius: 1rem;
color: var(--ton-schrift-2, #9aa6ad);
white-space: nowrap;
}
.kachel-chip.leer { font-style: italic; }
.kachel-editor {
padding: .25rem .75rem .75rem;
border-top: 1px solid var(--ton-linie, rgba(138, 148, 158, .2));
}
/* Eine Wertzeile. Auf breiten Schirmen nebeneinander, auf dem Handy
untereinander - die Felder sind zu kurz, um sie zu quetschen. */
.kachel-wert {
display: grid;
grid-template-columns: 7.5rem minmax(10rem, 1fr) 7rem 5.5rem 4rem auto;
gap: .4rem;
align-items: center;
padding: .5rem 0;
border-bottom: 1px dashed var(--ton-linie, rgba(138, 148, 158, .2));
}
.kachel-wert:last-of-type { border-bottom: 0; }
.kachel-wert .kw-schalter {
display: flex;
align-items: center;
gap: .5rem;
white-space: nowrap;
}
.kachel-wert .kw-schalter label {
display: inline-flex;
align-items: center;
gap: .25rem;
font-size: .78rem;
color: var(--ton-schrift-2, #9aa6ad);
cursor: pointer;
}
@media (max-width: 991.98px) {
.kachel-wert {
grid-template-columns: 1fr 1fr;
}
.kachel-wert .kw-topic { grid-column: 1 / -1; }
}
.kachel-fuss {
display: flex;
align-items: center;
gap: .75rem;
margin-top: .75rem;
}
/* === Geraetetafel (Einstellungen) ========================================
Sechzig Zeilen, in denen die meiste Arbeit an einer Handvoll haengt: den
Geraeten ohne Raum und denen, die seit Monaten schweigen. Beide sind
markiert, damit man sie nicht suchen muss - der Rest bleibt ruhig. */
.geraete-sieb {
display: flex;
align-items: center;
gap: .9rem;
margin-bottom: .75rem;
}
.geraete-sieb input[type="search"] { max-width: 22rem; }
.geraete-sieb .form-check { flex: none; }
.geraete-tafel {
max-height: calc(100vh - 22rem);
min-height: 14rem;
overflow-y: auto;
border: 1px solid var(--bs-border-color, #2c3034);
border-radius: .375rem;
}
/* Der Kopf bleibt beim Scrollen stehen - sonst raet man ab Zeile zwanzig,
welche Spalte der Raum war. */
.geraete-tafel thead th {
position: sticky;
top: 0;
z-index: 1;
background: var(--ton-flaeche-2, #2b3237);
font-size: .72rem;
letter-spacing: .08em;
text-transform: uppercase;
color: var(--ton-schrift-3, #6f7a81);
white-space: nowrap;
}
.geraete-tafel td { vertical-align: middle; }
.geraete-tafel .name { line-height: 1.25; }
.geraete-tafel .name .neben {
display: block;
font-size: .72rem;
color: var(--ton-schrift-3, #6f7a81);
}
.geraete-tafel .raum { min-width: 11rem; }
.geraete-tafel .raum select { min-width: 9rem; }
.geraete-tafel .alter { color: var(--ton-schrift-2, #9aa4ab); white-space: nowrap; }
.geraete-tafel .alter.lange { color: var(--ton-sonne, #f0b429); }
/* Ohne Raum: eine Kante links statt einer eingefaerbten Zeile - Letzteres
sieht bei zwanzig offenen Geraeten nach Fehler aus, ist aber nur Arbeit.
Als ::before und nicht als border, weil Bootstrap Tabellenzellen im
Hover mit einem inset-Schatten fuellt und den Rand damit ueberdeckt. */
.geraete-tafel tr.ohne-raum td:first-child { position: relative; }
.geraete-tafel tr.ohne-raum td:first-child::before {
content: "";
position: absolute;
left: 0; top: 0; bottom: 0;
width: 3px;
background: var(--ton-sonne, #f0b429);
}
/* Der Papierkorb haelt sich zurueck, bis man ihn braucht. */
.geraete-tafel .geraet-weg {
color: var(--ton-schrift-3, #6f7a81);
border-color: transparent;
line-height: 1;
}
.geraete-tafel .geraet-weg:hover:not(:disabled),
.geraete-tafel .geraet-weg:focus-visible:not(:disabled) {
color: var(--ton-schlecht, #e06060);
border-color: var(--ton-schlecht, #e06060);
}
.geraete-tafel .geraet-weg:disabled { opacity: .25; }
.geraete-fuss {
display: flex;
justify-content: space-between;
align-items: center;
gap: .75rem;
margin-top: .5rem;
}
@media (max-width: 575.98px) {
.geraete-sieb { flex-wrap: wrap; }
.geraete-sieb input[type="search"] { max-width: none; }
.geraete-tafel { max-height: calc(100vh - 18rem); }
}
/* === Protokollseite ======================================================
Ein Log liest sich nur in einer Schrift mit fester Breite: die Zeitstempel
stehen dann untereinander, und das Auge findet die Stelle, an der etwas