Plan Checker, Wochenbericht-Fokus-Modus und Wandtafel-Dashboard-Feinschliff
Plan Checker (neues Studenten-Werkzeug, Seitenleiste + Admin → Plan-Checker- Merkmale): lädt einen Bauplan als PDF hoch und lässt ChatGPT oder Claude ihn gegen eine gepflegte Merkmalsliste prüfen. PDF-Rendering (pdf.js + @napi-rs/canvas) berücksichtigt jetzt Ebenen (Optional Content Groups) und WASM-Decoder korrekt, sendet eine Übersicht plus mehrere hochaufgelöste Kachel-Ausschnitte statt eines einzelnen, zu groben Bilds, und beide Anbieter bekommen dasselbe Bildmaterial. Jedes Prüfergebnis kann auf eines der gesendeten Bilder als Beleg verweisen (bildIndex) — als Screenshot im Ergebnis anklickbar, öffnet vergrößert in einem Modal statt eines von Chrome blockierten target=_blank-Links auf eine data:-URL. KI-Modellname ist jetzt pro Konto überschreibbar (Einstellungen → Mein Konto) für Drittanbieter mit abweichendem Modell-Katalog. Wochenbericht: dichte Kartenansicht bekommt einen Fokus-Modus — nur der Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in aufklappbaren Abschnitten statt dauerhaft nebeneinander zu stehen. Projektbereich: Terminplan-Auslastung rechnet jetzt exakt wie der Auslastungs-Tab/die Wandtafel (statt einer unabhängigen, tageweisen Schätzung); die LPH-Fortschrittsliste im Dashboard zeigt nur noch die beauftragten Leistungsphasen. Wandtafel: Dashboard-Kopf mit Kennzahlen-Reihe und Panel-Reihe neu strukturiert, in den Blautönen des Firmenlogos; dieselbe Palette wurde anschließend auf den gesamten App-weiten Dark Mode übernommen. Dateibereinigung: nicht mehr benötigte Test-Uploads/Backups entfernt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
db63a29365
commit
6ba93b2a92
308 files changed
+64441
-743
No files matched your search
+505
-353
@@ -309,35 +309,91 @@ button.menu-item:hover { background: var(--surface-sunk); }
|
||||
|
||||
#sidebarCollapseIcon { transition: transform .2s var(--ease); }
|
||||
|
||||
/* === Wandtafel (js/wand/wand.js) ===
|
||||
Zweispaltiges Touch-Dashboard für ein dauerhaft laufendes Display,
|
||||
gewerke- und nutzerübergreifend — ersetzt den früheren Show Mode, der nur
|
||||
die gerade angemeldete Sitzung großflächig klonte (keine Aggregation über
|
||||
Gewerke oder Nutzer hinweg, siehe CLAUDE.md). Seitenleiste und Kopfzeile
|
||||
blendet showTab() über body.wandtafel-aktiv aus: Das Kiosk-Konto ist ein
|
||||
/* === Wandtafel / Dashboard (js/wand/wand.js) ===
|
||||
Touch-Dashboard für ein dauerhaft laufendes Display, gewerke- und
|
||||
nutzerübergreifend — ersetzt den früheren Show Mode, der nur die gerade
|
||||
angemeldete Sitzung großflächig klonte (keine Aggregation über Gewerke
|
||||
oder Nutzer hinweg, siehe CLAUDE.md). Seitenleiste und Kopfzeile blendet
|
||||
showTab() über body.wandtafel-aktiv aus: Das Kiosk-Konto ist ein
|
||||
gewöhnliches Mitgliedskonto mit echten Schreibrechten — die Kachelfläche
|
||||
soll die einzige Berührfläche sein, damit eine zufällige Berührung nicht
|
||||
in einen bearbeitbaren Bereich wie "Projekte" führt.
|
||||
Verhält sich zusätzlich wie ein fester Bildschirmausschnitt statt einer
|
||||
normal scrollenden Seite (height:100vh + overflow:hidden auf dem Weg
|
||||
.app-main → .container → #wand) — ein unbeaufsichtigtes Display soll von
|
||||
selbst nie scrollen müssen; die zwei Spalten strecken sich über die volle
|
||||
restliche Höhe.
|
||||
Aufbau (Eriks Wahl aus dem Vergleich mehrerer Gesamtentwürfe, "Leitstand"-
|
||||
Struktur, später in Eriks eigener Logo-Farbwelt umgesetzt): Kopf
|
||||
(Marke "Dashboard" + Sitzung/Uhrzeit + Abwesend-Kürzel) → eine
|
||||
Kennzahlen-Reihe aus vier Kacheln (Auslastung gesamt, Besprochen gesamt,
|
||||
offene Aufgaben, überfällig) → eine Reihe aus drei Panels (Auslastung je
|
||||
Gewerk, Besprochen je Gewerk, Aktuell). Verhält sich zusätzlich wie ein
|
||||
fester Bildschirmausschnitt statt einer normal scrollenden Seite
|
||||
(height:100vh + overflow:hidden auf dem Weg .app-main → .container →
|
||||
#wand) — ein unbeaufsichtigtes Display soll von selbst nie scrollen
|
||||
müssen; Kopf, Kennzahlen-Reihe und Panel-Reihe strecken sich über die
|
||||
volle restliche Höhe.
|
||||
AUSNAHME, auf Eriks Wunsch (echter Touch-Monitor, "ich kann mich vor die
|
||||
Tafel stellen und scrollen"): unterhalb der zwei Spalten folgt die
|
||||
vollständige Projektliste (wandProjektlisteHtml(), js/wand/wand.js), und
|
||||
NUR dieser Bereich (#wandContent, Klasse .wand-scrollable, von
|
||||
renderWandboard() gesetzt) scrollt — eine offene Übersicht bleibt weiter
|
||||
exakt einen Bildschirm hoch, siehe .wand-uebersicht unten. Ohne Berührung
|
||||
kehrt die Ansicht nach WAND_IDLE_MS von selbst an den Anfang zurück
|
||||
Tafel stellen und scrollen"): unterhalb davon folgt die vollständige
|
||||
Projektliste (wandProjektlisteHtml(), js/wand/wand.js), und NUR dieser
|
||||
Bereich (#wandContent, Klasse .wand-scrollable, von renderWandboard()
|
||||
gesetzt) scrollt — eine offene Übersicht bleibt weiter exakt einen
|
||||
Bildschirm hoch, siehe .wand-uebersicht unten. Ohne Berührung kehrt die
|
||||
Ansicht nach WAND_IDLE_MS von selbst an den Anfang zurück
|
||||
(wandAktivitaetErkannt()/wandBeiUntaetigkeitZuruecksetzen()). */
|
||||
body.wandtafel-aktiv .app-topbar,
|
||||
body.wandtafel-aktiv .side-menu,
|
||||
body.wandtafel-aktiv .side-menu-backdrop { display: none !important; }
|
||||
body.wandtafel-aktiv .app-main { height: 100vh; overflow: hidden; }
|
||||
body.wandtafel-aktiv .container { height: 100%; overflow: hidden; display: flex; flex-direction: column; padding: 14px 20px; }
|
||||
body.wandtafel-aktiv .container {
|
||||
height: 100%; overflow: hidden; display: flex; flex-direction: column; padding: 14px 20px;
|
||||
background: var(--dash-bg);
|
||||
}
|
||||
body.wandtafel-aktiv #wand { display: flex !important; flex-direction: column; flex: 1; min-height: 0; }
|
||||
|
||||
/* Eigene Farbwelt der Wandtafel — bewusst GETRENNT von --accent/--surface der
|
||||
übrigen App: Eriks Wahl ("Pastell Freundlich", aus dem Design-Vergleich der
|
||||
Gesamtentwürfe) nutzt die zwei Farben des Firmenlogos (aus public/logo.ico
|
||||
ausgelesen: #94b0cc helles, #116896 dunkles Blau) statt des generischen
|
||||
App-Akzents. Jede Fläche (Kennzahlen, Panels) trägt eine eigene, davon
|
||||
abgeleitete Tönung. Rot/Gelb bleiben als Warn-/Kritisch-Farben eigenständig
|
||||
(müssen unabhängig von der Markenfarbe sofort erkennbar bleiben), nur
|
||||
gedeckter gewählt, damit sie zum Blau passen statt dagegen zu stehen.
|
||||
--dash-hover ist eine reine Hervorhebung beim Antippen (kein Ersatz einer
|
||||
bestehenden Flächenfarbe), deshalb für beide Modi eigens abgestimmt statt
|
||||
eines einzigen hartkodierten Werts. */
|
||||
body.wandtafel-aktiv {
|
||||
--dash-bg: #f7fafc;
|
||||
--dash-surface: #ffffff;
|
||||
--dash-ink: #116896;
|
||||
--dash-ink-soft: #94b0cc;
|
||||
--dash-text: #20282e;
|
||||
--dash-text-muted: #5d7488;
|
||||
--dash-text-subtle: #8a9aa6;
|
||||
--dash-panel-1: #dde9f1;
|
||||
--dash-panel-2: #cfe0ea;
|
||||
--dash-panel-3: #e7eff5;
|
||||
--dash-kpi-4: #f6e3e0;
|
||||
--dash-warn: #9a5a1f;
|
||||
--dash-crit: #b8463f;
|
||||
--dash-line: #e7eef3;
|
||||
--dash-hover: rgba(17, 104, 150, .07);
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html:not([data-theme="light"]) body.wandtafel-aktiv {
|
||||
--dash-bg: #10181f; --dash-surface: #141c23;
|
||||
--dash-ink: #7fc3dd; --dash-ink-soft: #4a7691;
|
||||
--dash-text: #e7eef3; --dash-text-muted: #9fb3c0; --dash-text-subtle: #70838f;
|
||||
--dash-panel-1: #16222c; --dash-panel-2: #141e27; --dash-panel-3: #131b22; --dash-kpi-4: #2a1f1c;
|
||||
--dash-warn: #e8a15c; --dash-crit: #ef6f63;
|
||||
--dash-line: #223039; --dash-hover: rgba(255, 255, 255, .06);
|
||||
}
|
||||
}
|
||||
html[data-theme="dark"] body.wandtafel-aktiv {
|
||||
--dash-bg: #10181f; --dash-surface: #141c23;
|
||||
--dash-ink: #7fc3dd; --dash-ink-soft: #4a7691;
|
||||
--dash-text: #e7eef3; --dash-text-muted: #9fb3c0; --dash-text-subtle: #70838f;
|
||||
--dash-panel-1: #16222c; --dash-panel-2: #141e27; --dash-panel-3: #131b22; --dash-kpi-4: #2a1f1c;
|
||||
--dash-warn: #e8a15c; --dash-crit: #ef6f63;
|
||||
--dash-line: #223039; --dash-hover: rgba(255, 255, 255, .06);
|
||||
}
|
||||
|
||||
.wand-kopfzeile {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
@@ -371,168 +427,124 @@ body.wandtafel-aktiv #wand { display: flex !important; flex-direction: column; f
|
||||
Block-Scrollcontainer um, dessen erstes Kind (.wand-ambient) weiterhin
|
||||
genau einen Bildschirm hoch ist — die Projektliste danach verlängert den
|
||||
Inhalt darüber hinaus und erzeugt dadurch den Scrollbalken. */
|
||||
.wand-grid-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
||||
.wand-grid-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; background: var(--dash-bg); }
|
||||
.wand-grid-wrap.wand-scrollable { display: block; overflow-y: auto; }
|
||||
.wand-ambient { min-height: 100%; display: flex; flex-direction: column; }
|
||||
|
||||
.wand-c-layout { flex: 1; min-height: 0; display: grid; grid-template-columns: 320px 1fr; gap: 16px; }
|
||||
.wand-c-links { display: flex; flex-direction: column; gap: 16px; min-height: 0; }
|
||||
.wand-c-links .wand-kachel { flex: 1; min-height: 0; }
|
||||
|
||||
.wand-c-rechts {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line-soft);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--sh-2);
|
||||
padding: 18px 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
/* ===== Kopf: Marke "Dashboard" + Sitzung/Uhrzeit + Abwesend-Mini ===== */
|
||||
.dash-kopf { flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; }
|
||||
.dash-marke { font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 19px; color: var(--dash-ink); }
|
||||
.dash-kopf-rechts { display: flex; align-items: center; gap: 14px; }
|
||||
.dash-datum { font-size: 12.5px; color: var(--dash-text-muted); background: var(--dash-panel-3); padding: 4px 12px; border-radius: var(--r-pill); }
|
||||
.dash-abwesend-mini { display: flex; }
|
||||
.dash-abwesend-mini.ist-klickbar { cursor: pointer; border-radius: var(--r-pill); }
|
||||
.dash-abwesend-mini.ist-klickbar:hover { filter: brightness(.96); }
|
||||
.dash-avatar-mini {
|
||||
width: 25px; height: 25px; border-radius: 50%; background: var(--dash-panel-2); color: var(--dash-ink);
|
||||
font-size: 9.5px; font-weight: 800; display: flex; align-items: center; justify-content: center;
|
||||
margin-left: -7px; border: 2px solid var(--dash-bg);
|
||||
}
|
||||
.dash-avatar-mini:first-child { margin-left: 0; }
|
||||
|
||||
.wand-kachel {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line-soft);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--sh-2);
|
||||
padding: 18px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
}
|
||||
.wand-kachel-titel {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .05em;
|
||||
color: var(--text-muted);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
/* Der Inhalt verteilt sich GLEICHMÄSSIG über die volle, gestreckte
|
||||
Kachelhöhe (space-evenly) statt oben zu kleben und unten Leerraum zu
|
||||
lassen — genau das ließ vorher dünn befüllte Kacheln neben vollen leer
|
||||
wirken. */
|
||||
.wand-kachel-body { flex: 1; display: flex; flex-direction: column; justify-content: space-evenly; min-height: 0; overflow: hidden; }
|
||||
/* ===== Kennzahlen-Reihe (4 Kacheln) ===== */
|
||||
.dash-kpi-reihe { flex-shrink: 0; display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 18px; }
|
||||
.dash-kpi { border-radius: var(--r-xl); padding: 16px 18px; }
|
||||
.dash-kpi:nth-child(1) { background: var(--dash-panel-1); }
|
||||
.dash-kpi:nth-child(2) { background: var(--dash-panel-2); }
|
||||
.dash-kpi:nth-child(3) { background: var(--dash-panel-3); }
|
||||
.dash-kpi:nth-child(4) { background: var(--dash-kpi-4); }
|
||||
.dash-kpi.ist-klickbar { cursor: pointer; }
|
||||
.dash-kpi.ist-klickbar:hover { filter: brightness(.96); }
|
||||
.dash-kpi-label { font-size: 11px; font-weight: 700; color: var(--dash-text-muted); margin-bottom: 8px; }
|
||||
.dash-kpi-wert { font-family: 'Fredoka', sans-serif; font-weight: 600; font-size: 32px; color: var(--dash-ink); font-variant-numeric: tabular-nums; }
|
||||
.dash-kpi-wert.ok { color: var(--dash-ink); }
|
||||
.dash-kpi-wert.info { color: var(--dash-ink-soft); }
|
||||
.dash-kpi-wert.warn { color: var(--dash-warn); }
|
||||
.dash-kpi-wert.crit { color: var(--dash-crit); }
|
||||
|
||||
/* Meter-Zeilen: Auslastung je Gewerk, Wochenbericht-Fortschritt je Gewerk.
|
||||
Nicht mehr die Kachel als Ganzes ist antippbar, sondern jede einzelne
|
||||
Gewerk-Zeile für sich (wandZeigeUebersicht('auslastung'|'besprochen', g)) —
|
||||
deshalb trägt .ist-klickbar hier die Zeile, nicht mehr .wand-kachel. */
|
||||
/* ===== Drei-Panel-Reihe: Auslastung / Besprochen / Aktuell =====
|
||||
Dieselbe Kacheltechnik wie zuvor (space-evenly auf .dash-panel-body) —
|
||||
der Inhalt verteilt sich gleichmäßig über die volle Höhe statt oben zu
|
||||
kleben, damit ein dünn befülltes Panel neben einem vollen nicht leer
|
||||
wirkt. */
|
||||
.dash-grid { flex: 1; min-height: 0; display: grid; grid-template-columns: 1fr 1fr 1.3fr; gap: 14px; }
|
||||
.dash-panel { border-radius: var(--r-xl); padding: 18px 20px; display: flex; flex-direction: column; min-height: 0; }
|
||||
.dash-grid > .dash-panel:nth-child(1) { background: var(--dash-panel-1); }
|
||||
.dash-grid > .dash-panel:nth-child(2) { background: var(--dash-panel-2); }
|
||||
.dash-grid > .dash-panel:nth-child(3) { background: var(--dash-panel-3); }
|
||||
.dash-panel-titel { flex-shrink: 0; font-size: 12px; font-weight: 700; color: var(--dash-ink); margin-bottom: 12px; }
|
||||
.dash-panel-body { flex: 1; min-height: 0; display: flex; flex-direction: column; justify-content: space-evenly; overflow: hidden; }
|
||||
|
||||
/* Meter-Zeilen: Auslastung je Gewerk, Wochenbericht-Fortschritt je Gewerk —
|
||||
jede Zeile einzeln antippbar (wandZeigeUebersicht('auslastung'|'besprochen', g)). */
|
||||
.wand-meter-zeile { display: flex; align-items: center; gap: 12px; }
|
||||
.wand-meter-zeile.ist-klickbar { cursor: pointer; border-radius: var(--r-md); margin: 0 -6px; padding: 3px 6px; transition: background .15s var(--ease); }
|
||||
.wand-meter-zeile.ist-klickbar:hover { background: var(--surface-sunk); }
|
||||
.wand-meter-label { flex: 0 0 90px; font-size: 13px; font-weight: 600; color: var(--text); }
|
||||
.wand-meter-zeile.ist-klickbar:hover { background: var(--dash-hover); }
|
||||
.wand-meter-label { flex: 0 0 90px; font-size: 13px; font-weight: 600; color: var(--dash-text); }
|
||||
.wand-meter {
|
||||
flex: 1;
|
||||
height: 18px;
|
||||
background: var(--surface-sunk);
|
||||
border-radius: var(--r-pill);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1; height: 14px; background: rgba(127, 127, 127, .18); border-radius: var(--r-pill);
|
||||
overflow: hidden; display: flex; align-items: center;
|
||||
}
|
||||
.wand-meter-fill { height: 100%; border-radius: var(--r-pill); transition: width .3s var(--ease); }
|
||||
.wand-meter-leer { font-size: 12px; color: var(--text-subtle); padding-left: 4px; }
|
||||
.wand-meter-leer { font-size: 12px; color: var(--dash-text-subtle); padding-left: 4px; }
|
||||
.wand-meter-wert {
|
||||
flex: 0 0 76px;
|
||||
text-align: right;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
font-variant-numeric: tabular-nums;
|
||||
flex: 0 0 76px; text-align: right; font-size: 15px; font-weight: 700; font-variant-numeric: tabular-nums;
|
||||
font-family: 'Fredoka', sans-serif; color: var(--dash-ink);
|
||||
}
|
||||
.wand-einheit { font-size: 11px; font-weight: 400; color: var(--text-subtle); margin-left: 2px; }
|
||||
.wand-leer-klein { font-size: 11.5px; color: var(--text-subtle); }
|
||||
.wand-einheit { font-size: 11px; font-weight: 400; color: var(--dash-text-subtle); margin-left: 2px; }
|
||||
.wand-leer-klein { font-size: 11.5px; color: var(--dash-text-subtle); }
|
||||
|
||||
/* Kennzahlen: Kachel "Heute" (offene/überfällige Aufgaben, Abwesend) — jede
|
||||
Kennzahl und jedes Kürzel ist für sich antippbar (.ist-klickbar), öffnet
|
||||
die passende Vollbild-Übersicht. */
|
||||
.wand-stat-reihe { display: flex; gap: 28px; }
|
||||
.wand-stat { display: flex; flex-direction: column; gap: 2px; }
|
||||
.wand-stat b { font-size: 30px; font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
|
||||
.wand-stat span { font-size: 12px; color: var(--text-subtle); }
|
||||
.wand-stat.ist-rot b { color: var(--red); }
|
||||
.wand-stat.ist-klickbar { cursor: pointer; }
|
||||
.wand-stat.ist-klickbar:hover b { text-decoration: underline; }
|
||||
.wand-namen { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.wand-avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
}
|
||||
.wand-avatar.ist-klickbar { cursor: pointer; }
|
||||
.wand-avatar.ist-klickbar:hover { box-shadow: 0 0 0 2px var(--accent); }
|
||||
.wand-leer { font-size: 13px; color: var(--text-subtle); }
|
||||
|
||||
/* Ereignis-Feed (rechte Spalte) — auf Eriks Wahl "Variante C" aus dem
|
||||
Design-Vergleich: drei Spalten nach Art (Neu/Überfällig/Termine) statt
|
||||
einer gemischten, chronologisch sortierten Liste. Zeigt auf einen Blick,
|
||||
WIE VIELE Ereignisse welcher Art anstehen — kostet dafür die vorherige
|
||||
"zeitlich am nächsten zuerst"-Durchmischung über die Arten hinweg;
|
||||
innerhalb einer Spalte sortiert wandAktuelleEreignisse() unverändert nach
|
||||
zeitlicher Nähe. Jeder Eintrag bleibt für sich antippbar und springt
|
||||
direkt ins betroffene Projekt. */
|
||||
.wand-feed-wrap { flex: 1; min-height: 0; overflow: hidden; }
|
||||
.wand-feed-gruppen { display: flex; gap: 16px; height: 100%; }
|
||||
.wand-feed-gruppe { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; overflow: hidden; }
|
||||
.wand-feed-gruppe-kopf { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; flex-shrink: 0; }
|
||||
.wand-feed-gruppe-punkt { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
|
||||
.wand-feed-gruppe-punkt.neu { background: var(--accent); }
|
||||
.wand-feed-gruppe-punkt.ueberfaellig { background: var(--red); }
|
||||
.wand-feed-gruppe-punkt.termin { background: var(--amber); }
|
||||
.wand-feed-gruppe-titel {
|
||||
font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted);
|
||||
}
|
||||
.wand-feed-gruppe-anzahl { font-size: 10.5px; color: var(--text-subtle); margin-left: auto; }
|
||||
.wand-feed-eintrag {
|
||||
font-size: 12px; line-height: 1.4; padding: 8px 0; border-top: 1px solid var(--line-soft);
|
||||
/* "Aktuell": flache, chronologisch sortierte Ereignisliste (wandAktuellInhalt(),
|
||||
js/wand/wand.js) — ein farbiger Punkt statt einer Pille zeigt die Art
|
||||
(Neu/Überfällig/Termin). Jeder Eintrag einzeln antippbar, springt direkt
|
||||
ins betroffene Projekt. */
|
||||
.dash-feed-eintrag {
|
||||
display: flex; align-items: flex-start; gap: 10px; padding: 9px 2px; border-top: 1px solid var(--dash-line);
|
||||
cursor: pointer; border-radius: var(--r-md); transition: background .15s var(--ease);
|
||||
}
|
||||
.wand-feed-eintrag:first-of-type { border-top: none; padding-top: 0; }
|
||||
.wand-feed-eintrag:hover { background: var(--surface-sunk); }
|
||||
.wand-feed-eintrag b { display: block; font-weight: 600; color: var(--text); margin-bottom: 1px; }
|
||||
.wand-feed-eintrag span { color: var(--text-muted); }
|
||||
.wand-feed-zeit { display: block; font-size: 10.5px; color: var(--text-subtle); margin-top: 2px; }
|
||||
|
||||
.wand-stand { text-align: center; font-size: 11px; color: var(--text-subtle); margin-top: 10px; flex-shrink: 0; }
|
||||
.dash-feed-eintrag:first-of-type { border-top: none; padding-top: 0; }
|
||||
.dash-feed-eintrag:hover { background: var(--dash-hover); }
|
||||
.dash-feed-punkt { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 6px; }
|
||||
.dash-feed-punkt.neu { background: var(--dash-ink); }
|
||||
.dash-feed-punkt.ueberfaellig { background: var(--dash-crit); }
|
||||
.dash-feed-punkt.termin { background: var(--dash-warn); }
|
||||
.dash-feed-text { flex: 1; font-size: 12.5px; line-height: 1.5; color: var(--dash-text); }
|
||||
.dash-feed-zeit { display: block; font-size: 10.5px; color: var(--dash-text-subtle); margin-top: 2px; }
|
||||
|
||||
/* Vollbild-Übersichten (überfällige/offene Aufgaben, Auslastung/Besprochen
|
||||
je Gewerk, Abwesenheiten) — ersetzen das zweispaltige Layout vorübergehend
|
||||
je Gewerk, Abwesenheiten) — ersetzen die Normalansicht vorübergehend
|
||||
vollständig (wandUebersicht). Die Liste ist die EINE bewusste Ausnahme vom
|
||||
"kein Scrollen"-Grundsatz der Wandtafel: ein gezielter Einstieg mit
|
||||
potenziell vielen Zeilen, kein passiver Dauerzustand. */
|
||||
potenziell vielen Zeilen, kein passiver Dauerzustand. In derselben
|
||||
Logo-Farbwelt wie die Normalansicht, damit der Wechsel nicht wie ein
|
||||
Bruch in eine andere App wirkt. */
|
||||
.wand-uebersicht {
|
||||
flex: 1; min-height: 0; display: flex; flex-direction: column;
|
||||
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-xl);
|
||||
box-shadow: var(--sh-2); padding: 20px 24px;
|
||||
background: var(--dash-surface); border: 1px solid var(--dash-line); border-radius: var(--r-xl);
|
||||
box-shadow: var(--sh-2); padding: 20px 24px; color: var(--dash-text);
|
||||
}
|
||||
.wand-uebersicht-kopf { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; flex-shrink: 0; }
|
||||
.wand-uebersicht-kopf h2 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.01em; }
|
||||
.wand-uebersicht-kopf h2 { margin: 0; font-size: 19px; font-weight: 700; letter-spacing: -.01em; font-family: 'Fredoka', sans-serif; }
|
||||
.wand-uebersicht-liste { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }
|
||||
.wand-uebersicht-zeile {
|
||||
display: flex; align-items: center; gap: 18px; padding: 13px 6px; border-top: 1px solid var(--line-soft);
|
||||
display: flex; align-items: center; gap: 18px; padding: 13px 6px; border-top: 1px solid var(--dash-line);
|
||||
transition: background .15s var(--ease);
|
||||
}
|
||||
.wand-uebersicht-zeile:first-child { border-top: none; }
|
||||
.wand-uebersicht-zeile.ist-klickbar { cursor: pointer; }
|
||||
.wand-uebersicht-zeile.ist-klickbar:hover { background: var(--surface-sunk); }
|
||||
.wand-uebersicht-projekt { flex: 0 0 280px; font-weight: 600; font-size: 14.5px; color: var(--text); }
|
||||
.wand-uebersicht-titel { flex: 1; font-size: 14.5px; color: var(--text); }
|
||||
/* Neutral, nicht mehr fest rot: eine Übersichts-Zeile kann auch eine
|
||||
unauffällige Angabe zeigen (Stunden, "noch offen", Abwesenheitsende) —
|
||||
.ist-rot markiert gezielt die wirklich überfälligen Zeilen. */
|
||||
.wand-uebersicht-seit { flex-shrink: 0; font-size: 13px; font-weight: 600; color: var(--text-muted); }
|
||||
.wand-uebersicht-seit.ist-rot { color: var(--red); }
|
||||
.wand-uebersicht-zeile.ist-klickbar:hover { background: var(--dash-hover); }
|
||||
.wand-uebersicht-projekt { flex: 0 0 280px; font-weight: 600; font-size: 14.5px; color: var(--dash-text); }
|
||||
.wand-uebersicht-titel { flex: 1; font-size: 14.5px; color: var(--dash-text); }
|
||||
/* Neutral, nicht fest rot: eine Übersichts-Zeile kann auch eine unauffällige
|
||||
Angabe zeigen (Stunden, "noch offen", Abwesenheitsende) — .ist-rot
|
||||
markiert gezielt die wirklich überfälligen Zeilen. */
|
||||
.wand-uebersicht-seit { flex-shrink: 0; font-size: 13px; font-weight: 600; color: var(--dash-text-muted); }
|
||||
.wand-uebersicht-seit.ist-rot { color: var(--dash-crit); }
|
||||
.wand-leer { font-size: 13px; color: var(--dash-text-subtle); }
|
||||
|
||||
/* Vollständige Projektliste unterhalb der zwei Spalten (wandProjektlisteHtml,
|
||||
js/wand/wand.js) — nur sichtbar/erreichbar durch Herunterscrollen auf
|
||||
/* Vollständige Projektliste unterhalb der Kennzahlen-/Panel-Reihe
|
||||
(wandProjektlisteHtml(), js/wand/wand.js) — nur sichtbar/erreichbar durch
|
||||
Herunterscrollen auf
|
||||
einem echten Touch-Monitor. Auf Eriks Wunsch optisch identisch mit der
|
||||
echten Projektliste: Gruppenüberschrift und Zeilen sind exakt
|
||||
.section-title/.board-list/.board-row aus der Projektliste selbst (keine
|
||||
@@ -1437,36 +1449,89 @@ tr:nth-child(even) td { background: var(--surface-alt); }
|
||||
}
|
||||
|
||||
/* === Dichte Fassung der Karte (.board-drawer-dicht, Wochenbericht) ===
|
||||
Gleiche Angaben, gleiche Bedienelemente, enger gesetzt. Den Ausschlag gibt
|
||||
die Leistungsphasen-Leiste: als Zeilen untereinander bestimmten bis zu zehn
|
||||
Phasen die Höhe der ganzen Karte — bei 29 Projekten wurde der Bericht damit
|
||||
rund 17 Bildschirmseiten lang. Nebeneinander braucht dieselbe Information
|
||||
eine Zeile. */
|
||||
.board-drawer-dicht .board-drawer-grid {
|
||||
/* Die Beteiligten brauchen so viel Breite wie der Kommentar: Sobald ihre
|
||||
Kürzel umbrechen, bestimmt ihre Spalte die Höhe der ganzen Karte. Mit
|
||||
1.45/1.15/1 waren es 14,2 Bildschirmseiten, mit gleich breiter
|
||||
Beteiligten-Spalte 11,6 — mehr Breite bringt darüber hinaus nichts,
|
||||
weil dann ohnehin nichts mehr umbricht. */
|
||||
/* Die rechte Spalte bekommt eine MINDESTBREITE: Zehn Phasenfelder in einer
|
||||
reinen fr-Spalte wurden 27 px schmal, und "100" passte nicht mehr ins
|
||||
Eingabefeld — 79 Werte waren abgeschnitten. Unter 620 px Containerbreite
|
||||
gibt die Mindestbreite nach und die Spalten rutschen untereinander. */
|
||||
grid-template-columns: minmax(0, 1fr) minmax(340px, 1fr);
|
||||
grid-template-areas: "kommentar kompakt";
|
||||
gap: 18px;
|
||||
Fokus-Modus (Eriks Wahl "C" aus dem Darstellungs-Vergleich): nur der
|
||||
Kommentar liegt offen, Leistungsphasen und Beteiligte stecken in
|
||||
aufklappbaren Abschnitten (.bericht-disclosure, siehe renderBoardDrawer()s
|
||||
dichte Fassung in js/board/board.js) statt dauerhaft nebeneinander zu
|
||||
stehen — bei 29 Projekten wurde der Bericht mit allem gleichzeitig
|
||||
sichtbar als "zu überladen" zurückgemeldet. Kein eigenes Raster mehr (die
|
||||
drei Blöcke stehen schlicht untereinander), deshalb sitzt die dichte
|
||||
Fassung flächig in ihrem Container statt als eigene Schublade mit
|
||||
zusätzlichem Rahmen/Hintergrund darin. */
|
||||
.board-drawer-dicht { padding: 0; background: transparent; border-top: none; }
|
||||
|
||||
.bericht-disclosure {
|
||||
border: 1px solid var(--line-soft);
|
||||
border-radius: var(--r-md);
|
||||
margin-top: 10px;
|
||||
overflow: hidden;
|
||||
background: var(--surface);
|
||||
}
|
||||
@media (max-width: 860px) {
|
||||
.board-drawer-dicht .board-drawer-grid {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "kommentar" "kompakt";
|
||||
gap: 14px;
|
||||
}
|
||||
.bericht-disclosure summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.drawer-col-kompakt { grid-area: kompakt; min-width: 0; }
|
||||
/* Die zweite Überschrift in derselben Spalte braucht Abstand nach oben, den
|
||||
die erste (am Spaltenanfang) nicht haben darf. */
|
||||
.drawer-h4-zweit { margin-top: 12px; }
|
||||
.bericht-disclosure summary::-webkit-details-marker { display: none; }
|
||||
.bericht-disclosure summary:hover { background: var(--surface-alt); }
|
||||
.bericht-disclosure summary .count {
|
||||
font-size: 11px;
|
||||
color: var(--text-subtle);
|
||||
font-weight: 500;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.bericht-disclosure summary .chev {
|
||||
margin-left: auto;
|
||||
color: var(--text-subtle);
|
||||
display: inline-flex;
|
||||
transition: transform .15s var(--ease);
|
||||
}
|
||||
.bericht-disclosure[open] summary .chev { transform: rotate(90deg); }
|
||||
.bericht-disclosure[open] summary { border-bottom: 1px solid var(--line-soft); }
|
||||
.bericht-disclosure-body { padding: 10px 14px 12px; }
|
||||
|
||||
/* Leistungsphasen als Balkenzeilen statt der Kästchen-Leiste des
|
||||
Projektboards (.board-lph-leiste/-cell bleiben dort unverändert) — in der
|
||||
schmalen Fokus-Spalte brauchen zehn Kästchen nebeneinander zu wenig Platz
|
||||
je Phase, eine Zeile je Phase dagegen hat Raum für Label, Balken UND
|
||||
Eingabefeld. */
|
||||
.bericht-lph-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; font-size: 12.5px; }
|
||||
.bericht-lph-label { width: 48px; flex-shrink: 0; color: var(--text-muted); }
|
||||
.bericht-lph-track { flex: 1; height: 4px; background: var(--line-soft); border-radius: 2px; overflow: hidden; }
|
||||
.bericht-lph-track i { display: block; height: 100%; background: var(--text-subtle); }
|
||||
.bericht-lph-row.is-aktiv .bericht-lph-track i { background: var(--accent); }
|
||||
.bericht-lph-row.is-aktiv .bericht-lph-label { color: var(--accent); font-weight: 650; }
|
||||
.bericht-lph-pct {
|
||||
display: flex; align-items: center; gap: 1px; flex-shrink: 0;
|
||||
color: var(--text-subtle); font-variant-numeric: tabular-nums; font-size: 11.5px;
|
||||
}
|
||||
/* .bericht-lph-row (zwei Klassen) statt der bloßen Element-Klasse: nötig, um
|
||||
den globalen input[type=text]-Grundstil (Polsterung/Rahmen/Hintergrund,
|
||||
weiter oben) zu schlagen — sonst gewinnt dessen höhere Spezifität und die
|
||||
Polsterung von 7px 10px ließ "100" im 42px schmalen Feld auf "10"
|
||||
abschneiden. */
|
||||
.bericht-lph-row .bericht-lph-input {
|
||||
width: 28px; flex-shrink: 0; border: none; border-radius: 0; background: transparent;
|
||||
text-align: right; font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--text);
|
||||
font-family: inherit; padding: 1px 0;
|
||||
}
|
||||
.bericht-lph-row .bericht-lph-input:focus { outline: 1.5px solid var(--accent); outline-offset: -1px; border-radius: 3px; box-shadow: none; }
|
||||
.bericht-lph-row .bericht-lph-input.locked-cell { cursor: default; }
|
||||
|
||||
/* Der Kommentar bleibt das größte Element der dichten Fassung — etwas
|
||||
größere Schrift als im Projektboard, wo er eine Spalte unter mehreren
|
||||
teilt. Radius/Schatten wie eine freistehende Karte (Entwurf "Fokus-Modus"). */
|
||||
.board-drawer-dicht .comment-editor .comment-field,
|
||||
.board-drawer-dicht .comment-readonly { font-size: 14px; line-height: 1.6; min-height: 76px; }
|
||||
/* .comment-editor hat bereits overflow:hidden (Basis-Regel) — ein größerer
|
||||
Radius hier genügt, Kopf/Fuß werden automatisch mitgerundet. */
|
||||
.board-drawer-dicht .comment-editor { border-radius: var(--r-lg); box-shadow: var(--sh-1); }
|
||||
|
||||
.board-lph-leiste {
|
||||
display: flex;
|
||||
@@ -2302,36 +2367,14 @@ a.secondary:hover { background: var(--surface-sunk); border-color: var(--text-su
|
||||
}
|
||||
|
||||
|
||||
/* === Anwesenheitsblock der Sitzung ======================================
|
||||
Eigener Block oben im Projektboard (renderAttendance(), js/board/board.js).
|
||||
Zustände sind bewusst binär (anwesend / nicht anwesend, Tabelle attendance);
|
||||
der Zähler in der Kopfzeile macht sichtbar, wenn noch niemand geklickt hat. */
|
||||
|
||||
.attendance-block {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line-soft);
|
||||
border-radius: var(--r-lg);
|
||||
box-shadow: var(--sh-1);
|
||||
margin-top: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.attendance-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
padding: 9px 14px;
|
||||
background: var(--surface-alt);
|
||||
border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
.attendance-title { font-weight: 600; font-size: 13px; }
|
||||
.attendance-count {
|
||||
font-size: 11.5px;
|
||||
color: var(--text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.attendance-spacer { margin-left: auto; }
|
||||
/* === Anwesenheit der Sitzung ============================================
|
||||
Fokus-Modus (Eriks Wahl "C"): ein aufklappbarer Abschnitt wie Leistungs-
|
||||
phasen/Beteiligte im Wochenbericht (.bericht-disclosure, siehe weiter
|
||||
oben), kein dauerhaft sichtbarer Block mehr. Zustände bleiben binär
|
||||
(anwesend / nicht anwesend, Tabelle attendance); der Zähler in der
|
||||
Kopfzeile macht sichtbar, wenn noch niemand geklickt hat. */
|
||||
#weekAttendance .bericht-disclosure { margin-top: 16px; }
|
||||
.attendance-actions { display: flex; gap: 8px; margin-bottom: 10px; }
|
||||
|
||||
.attendance-grid {
|
||||
display: flex;
|
||||
@@ -2485,34 +2528,46 @@ input[type=text].edit-name:focus {
|
||||
html:not([data-theme="light"]) { color-scheme: dark; }
|
||||
|
||||
html:not([data-theme="light"]) {
|
||||
--accent: #0a84ff;
|
||||
--accent-hover: #2f95ff;
|
||||
--accent-active: #0060df;
|
||||
--accent-soft: #10263f;
|
||||
/* Auf Eriks Wunsch ("das gesamte dark mode Farbschema an das der
|
||||
Wandtafel anpassen") übernimmt der GESAMTE App-Dark-Mode die
|
||||
Logo-Blau-Familie des Dashboards (--dash-* in body.wandtafel-aktiv
|
||||
weiter oben) statt des vorherigen, generischen Apple-Blaus — alle
|
||||
Komponenten der App lesen ausschließlich über diese Tokens, eine
|
||||
Änderung hier genügt also für die ganze Anwendung. Rot/Gelb/Grün
|
||||
bleiben als eigenständige Statusfarben bestehen (müssen unabhängig
|
||||
von der Markenfarbe sofort erkennbar sein, siehe Ampel-Logik in
|
||||
js/load/load.js), nur in denselben gedeckteren Tönen wie am Dashboard. */
|
||||
--accent: #7fc3dd;
|
||||
--accent-hover: #96cfe3;
|
||||
--accent-active: #5fa3c0;
|
||||
--accent-soft: #16222c;
|
||||
|
||||
--text: #f5f5f7;
|
||||
--text-muted: #a1a1a6;
|
||||
--text-subtle: #86868b;
|
||||
--text: #e7eef3;
|
||||
--text-muted: #9fb3c0;
|
||||
--text-subtle: #70838f;
|
||||
|
||||
--bg: #000000;
|
||||
--surface: #1c1c1e;
|
||||
--surface-alt: #232326;
|
||||
--surface-sunk: #2c2c2e;
|
||||
--bg: #10181f;
|
||||
--surface: #141c23;
|
||||
--surface-alt: #16222c;
|
||||
--surface-sunk: #1c2730;
|
||||
|
||||
--line: #3a3a3c;
|
||||
--line-soft: #2c2c2e;
|
||||
--line: #2c3e49;
|
||||
--line-soft: #1f2b33;
|
||||
|
||||
--green: #30d158;
|
||||
--amber: #ff9f0a;
|
||||
--red: #ff453a;
|
||||
--red-hover: #ff453a;
|
||||
--red-active: #a3000f;
|
||||
--green: #4cd080;
|
||||
--amber: #e8a15c;
|
||||
--red: #ef6f63;
|
||||
--red-hover: #f58a7f;
|
||||
--red-active: #c65349;
|
||||
|
||||
--attend-bg: rgba(48,209,88,.16);
|
||||
--attend-bg-hover: rgba(48,209,88,.24);
|
||||
--attend-bg: rgba(76,208,128,.14);
|
||||
--attend-bg-hover: rgba(76,208,128,.22);
|
||||
|
||||
--band-neu: #0a6fd0;
|
||||
--band-fortschritt: #1e7a38;
|
||||
/* Auf Weiß gesetzt — deshalb dunkler als --band-neu/--green gebraucht
|
||||
(Kontrastgrund wie im Hell-Modus): --band-neu ist das dunkle
|
||||
Logo-Blau selbst (dasselbe #116896 wie am Dashboard). */
|
||||
--band-neu: #116896;
|
||||
--band-fortschritt: #1f7a4f;
|
||||
|
||||
--protokoll-neu-bg: rgba(255,214,10,.14);
|
||||
--protokoll-neu-border: #ffd60a;
|
||||
@@ -2526,34 +2581,34 @@ input[type=text].edit-name:focus {
|
||||
html[data-theme="dark"] {
|
||||
color-scheme: dark;
|
||||
|
||||
--accent: #0a84ff;
|
||||
--accent-hover: #2f95ff;
|
||||
--accent-active: #0060df;
|
||||
--accent-soft: #10263f;
|
||||
--accent: #7fc3dd;
|
||||
--accent-hover: #96cfe3;
|
||||
--accent-active: #5fa3c0;
|
||||
--accent-soft: #16222c;
|
||||
|
||||
--text: #f5f5f7;
|
||||
--text-muted: #a1a1a6;
|
||||
--text-subtle: #86868b;
|
||||
--text: #e7eef3;
|
||||
--text-muted: #9fb3c0;
|
||||
--text-subtle: #70838f;
|
||||
|
||||
--bg: #000000;
|
||||
--surface: #1c1c1e;
|
||||
--surface-alt: #232326;
|
||||
--surface-sunk: #2c2c2e;
|
||||
--bg: #10181f;
|
||||
--surface: #141c23;
|
||||
--surface-alt: #16222c;
|
||||
--surface-sunk: #1c2730;
|
||||
|
||||
--line: #3a3a3c;
|
||||
--line-soft: #2c2c2e;
|
||||
--line: #2c3e49;
|
||||
--line-soft: #1f2b33;
|
||||
|
||||
--green: #30d158;
|
||||
--amber: #ff9f0a;
|
||||
--red: #ff453a;
|
||||
--red-hover: #ff453a;
|
||||
--red-active: #a3000f;
|
||||
--green: #4cd080;
|
||||
--amber: #e8a15c;
|
||||
--red: #ef6f63;
|
||||
--red-hover: #f58a7f;
|
||||
--red-active: #c65349;
|
||||
|
||||
--attend-bg: rgba(48,209,88,.16);
|
||||
--attend-bg-hover: rgba(48,209,88,.24);
|
||||
--attend-bg: rgba(76,208,128,.14);
|
||||
--attend-bg-hover: rgba(76,208,128,.22);
|
||||
|
||||
--band-neu: #0a6fd0;
|
||||
--band-fortschritt: #1e7a38;
|
||||
--band-neu: #116896;
|
||||
--band-fortschritt: #1f7a4f;
|
||||
|
||||
--protokoll-neu-bg: rgba(255,214,10,.14);
|
||||
--protokoll-neu-border: #ffd60a;
|
||||
@@ -3862,6 +3917,10 @@ html[data-theme="dark"] .absence-type-fortbildung { background: rgba(255,159,10,
|
||||
.ai-url-panel.is-open { display: block; }
|
||||
.ai-url-panel label { display: block; font-size: 11px; color: var(--text-subtle); margin-bottom: 5px; }
|
||||
.ai-url-panel .ai-provider-row { margin-bottom: 0; }
|
||||
/* Zweites Label-/Zeilenpaar im selben Panel (Modellname, unter der
|
||||
Server-Adresse): braucht Abstand nach oben, den das erste (am
|
||||
Panel-Anfang) nicht haben darf — gleiches Muster wie .drawer-h4-zweit. */
|
||||
.ai-url-panel label:nth-of-type(2) { margin-top: 12px; }
|
||||
|
||||
/* "Neu verbinden": prüft die gespeicherte Konfiguration wirklich (POST
|
||||
/my-ai-settings/test), statt nur anzuzeigen, dass irgendein Schlüssel
|
||||
@@ -4115,19 +4174,62 @@ td.actions-cell button { margin: 2px; }
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* === Wochenbericht als Tagesordnung (js/wochenbericht/wochenbericht.js) ===
|
||||
Links die vollständige Projektliste, rechts das gewählte Projekt. Die
|
||||
frühere Abfolge gestapelter Karten war bei 29 Projekten 18 Bildschirmseiten
|
||||
lang; hier bleibt die Liste stehen und man sieht jederzeit, wo man steht
|
||||
und was noch kommt. */
|
||||
/* === Wochenbericht als Tagesordnung, Fokus-Modus (Eriks Wahl "C" aus dem
|
||||
Darstellungs-Vergleich, js/wochenbericht/wochenbericht.js) ===
|
||||
Links eine schmale Nummern-Leiste (jedes Projekt nur eine Position + ggf.
|
||||
ein Änderungs-Punkt, Name im Tooltip), rechts das gewählte Projekt — nur
|
||||
Kommentar und Kernstatus liegen dort offen, Leistungsphasen/Beteiligte/
|
||||
Anwesenheit stecken in aufklappbaren Abschnitten (.bericht-disclosure).
|
||||
Ersetzt die vorherige ausgeschriebene Liste|Detail-Darstellung, die mit
|
||||
Gewerke-Dropdown, Fortschrittsbalken, Suchfeld, mehreren Badges je Zeile
|
||||
und allem gleichzeitig sichtbar als "zu überladen" zurückgemeldet wurde.
|
||||
Eine ganze Fläche (.bericht-panel), nicht zwei lose nebeneinanderstehende
|
||||
Karten — genau wie im freigegebenen Entwurf: eine Kopfleiste mit eigenem
|
||||
Hintergrund/Trennlinie, darunter die Leiste als Spalte MIT dem Ergebnis
|
||||
verbunden (Trennlinie statt eigenem Rahmen). */
|
||||
.bericht-titelzeile { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
|
||||
.bericht-titelzeile button { margin-left: auto; }
|
||||
|
||||
.bericht-kopf {
|
||||
display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
.bericht-panel {
|
||||
background: var(--surface); border: 1px solid var(--line-soft);
|
||||
border-radius: var(--r-lg); box-shadow: var(--sh-1); overflow: hidden;
|
||||
}
|
||||
.bericht-gewerk-select { font-size: 12.5px; padding: 5px 10px; min-width: 170px; }
|
||||
|
||||
.bericht-topbar {
|
||||
display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
|
||||
padding: 10px 20px; border-bottom: 1px solid var(--line-soft);
|
||||
}
|
||||
.bericht-gewerk-select {
|
||||
font-size: 12px; font-weight: 600; padding: 5px 10px;
|
||||
border-radius: var(--r-sm); background: var(--surface-alt);
|
||||
border: 1px solid var(--line); color: var(--text); cursor: pointer;
|
||||
}
|
||||
.bericht-ring {
|
||||
width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.bericht-ring span { width: 18px; height: 18px; border-radius: 50%; background: var(--surface); display: block; }
|
||||
.bericht-fortschritt-text { font-size: 11.5px; color: var(--text-muted); font-weight: 600; white-space: nowrap; }
|
||||
/* Reines Textlink, kein Knopf — im Entwurf steht an dieser Stelle nur der
|
||||
Fortschritt, nicht noch eine zusätzliche Schaltfläche daneben. */
|
||||
.bericht-reset-link {
|
||||
font-size: 11.5px; font-weight: 600; border: none; background: none;
|
||||
color: var(--text-subtle); cursor: pointer; padding: 0; text-decoration: underline;
|
||||
text-decoration-color: var(--line);
|
||||
}
|
||||
.bericht-reset-link:hover { color: var(--text); }
|
||||
.bericht-search-btn {
|
||||
margin-left: auto; border: 1px solid var(--line); background: var(--surface-alt);
|
||||
color: var(--text-subtle); font-size: 12px; padding: 5px 10px;
|
||||
border-radius: var(--r-pill); cursor: pointer;
|
||||
}
|
||||
.bericht-search-btn:hover { color: var(--text); border-color: var(--text-subtle); }
|
||||
.bericht-suche-input {
|
||||
margin-left: auto; border: 1px solid var(--line); border-radius: var(--r-pill);
|
||||
background: var(--surface-sunk); color: var(--text); font-size: 12px;
|
||||
padding: 6px 12px; width: 170px;
|
||||
}
|
||||
.bericht-suche-input:focus { outline: none; border-color: var(--accent); background: var(--surface); }
|
||||
|
||||
/* ---------- Planung (Terminplan-Sandbox, js/planung/planung.js) ---------- */
|
||||
.planung-row {
|
||||
@@ -4161,118 +4263,168 @@ td.actions-cell button { margin: 2px; }
|
||||
.choice-card-desc { font-size: 11.5px; color: var(--text-muted); line-height: 1.4; }
|
||||
.empty-hint { text-align: center; padding: 60px 20px; color: var(--text-subtle); font-size: 13px; }
|
||||
.empty-hint .big { font-size: 15px; color: var(--text-muted); margin-bottom: 6px; display: block; }
|
||||
.bericht-fortschritt { display: flex; align-items: center; gap: 9px; margin-left: auto; }
|
||||
.bericht-fortschritt-text { font-size: 12.5px; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
.bericht-fortschritt-bar { width: 120px; height: 6px; border-radius: 3px; background: var(--surface-sunk); overflow: hidden; }
|
||||
.bericht-fortschritt-bar i { display: block; height: 100%; background: var(--green); transition: width .25s var(--ease); }
|
||||
|
||||
.bericht-split {
|
||||
display: grid;
|
||||
grid-template-columns: 272px minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
align-items: start;
|
||||
/* ---------- Plan Checker (Studenten, js/planchecker/planchecker.js) ----------
|
||||
Darstellung "Arbeitsfläche" (Eriks Wahl "B" aus dem Design-Vergleich): zwei
|
||||
Spalten dauerhaft nebeneinander — links Upload/Konfiguration, rechts das
|
||||
Ergebnis, damit ein erneutes Prüfen nach einer Korrektur das vorherige
|
||||
Ergebnis nicht aus dem Blick verliert. Eigene .pc-*-Klassen statt
|
||||
bestehender Komponenten (.board-drawer, .settings-card …): keine davon
|
||||
passt auf eine Zwei-Spalten-Arbeitsfläche mit Ablagefläche + Ergebnisliste,
|
||||
ein Zurechtbiegen hätte mehr Sonderfälle erzeugt als eine schlanke eigene
|
||||
Regelgruppe. */
|
||||
.pc-layout { display: grid; grid-template-columns: 340px 1fr; gap: 20px; align-items: start; }
|
||||
@media (max-width: 860px) { .pc-layout { grid-template-columns: 1fr; } }
|
||||
|
||||
.pc-panel { background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: 20px; }
|
||||
|
||||
.pc-drop {
|
||||
display: block; border: 1.5px dashed var(--line); border-radius: var(--r-md);
|
||||
padding: 20px 14px; text-align: center; color: var(--text-muted);
|
||||
background: var(--surface-alt); cursor: pointer; font-size: 12.5px;
|
||||
transition: border-color .15s var(--ease), color .15s var(--ease), background .15s var(--ease);
|
||||
}
|
||||
@media (max-width: 900px) { .bericht-split { grid-template-columns: 1fr; } }
|
||||
.pc-drop:hover, .pc-drop.is-dragover { border-color: var(--accent); color: var(--text); background: var(--accent-soft); }
|
||||
.pc-drop-name { font-weight: 650; color: var(--text); margin-top: 4px; font-size: 13px; word-break: break-word; }
|
||||
|
||||
.bericht-liste-spalte {
|
||||
background: var(--surface); border-radius: var(--r-md); box-shadow: var(--sh-1);
|
||||
overflow: hidden;
|
||||
/* Die Liste bleibt beim Scrollen im Blick — sie IST die Tagesordnung. */
|
||||
.pc-section { margin-top: 18px; }
|
||||
.pc-label { display: block; font-size: 11.5px; font-weight: 650; color: var(--text-muted); margin-bottom: 5px; }
|
||||
|
||||
.pc-merkmal-chip { font-size: 12.5px; padding: 5px 2px; color: var(--text-muted); }
|
||||
.pc-zusatz { margin-top: 8px; }
|
||||
.pc-zusatz .pc-input { width: 100%; }
|
||||
|
||||
/* background-color statt der background-Kurzschreibweise: Die Kurzschreibweise
|
||||
setzt background-repeat/-position implizit auf ihre Vorgabewerte zurück
|
||||
(repeat/0% 0%) und schlägt dabei wegen höherer Klassen-Spezifität die
|
||||
globalen select-Regeln (background-repeat:no-repeat, siehe weiter oben),
|
||||
die den eigenen SVG-Pfeil sonst nur einmal rechts zeigen — das gekachelte
|
||||
Pfeil-Muster, das als "komisch" auffiel, war genau das. */
|
||||
.pc-select, .pc-input {
|
||||
border: 1px solid var(--line); border-radius: var(--r-md); background-color: var(--surface);
|
||||
color: var(--text); font-size: 13px; font-family: inherit; padding: 8px 10px;
|
||||
}
|
||||
.pc-select:focus, .pc-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 18%, transparent); }
|
||||
|
||||
.pc-btn {
|
||||
border: 1px solid var(--line); background: var(--surface); color: var(--text);
|
||||
font-size: 13px; font-weight: 600; padding: 9px 16px; border-radius: var(--r-md); cursor: pointer;
|
||||
}
|
||||
.pc-btn:hover { background: var(--surface-alt); }
|
||||
.pc-btn:disabled { opacity: .6; cursor: default; }
|
||||
.pc-btn-primary { border: none; background: var(--accent); color: #fff; }
|
||||
.pc-btn-primary:hover { background: var(--accent-hover); }
|
||||
.pc-error { color: var(--red); margin: 8px 0 0; }
|
||||
|
||||
.pc-verdict {
|
||||
display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
|
||||
width: 20px; height: 20px; border-radius: 50%; font-size: 12px; font-weight: 700; color: #fff;
|
||||
}
|
||||
.pc-verdict.ok { background: var(--green); }
|
||||
.pc-verdict.fail { background: var(--red); }
|
||||
|
||||
.pc-results { min-height: 420px; display: flex; flex-direction: column; }
|
||||
.pc-results-empty { margin: auto; text-align: center; color: var(--text-subtle); padding: 40px 20px; }
|
||||
.pc-results-empty .big { font-size: 14px; color: var(--text-muted); margin-bottom: 4px; display: block; }
|
||||
.pc-result-head { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--line-soft); flex-wrap: wrap; }
|
||||
.pc-result-head .filename { font-weight: 650; }
|
||||
.pc-result-head .meta { color: var(--text-subtle); font-size: 12px; margin-left: auto; }
|
||||
.pc-result-row { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line-soft); }
|
||||
.pc-result-row:last-child { border-bottom: none; }
|
||||
.pc-result-row .titel { font-weight: 650; margin-bottom: 2px; font-size: 13px; }
|
||||
.pc-result-row .begruendung { color: var(--text-muted); font-size: 12.5px; }
|
||||
.pc-result-text { flex: 1 1 auto; min-width: 0; }
|
||||
/* pc-result-shot: Screenshot des Planausschnitts, den die KI für das Merkmal
|
||||
als Beleg gewählt hat (server.js, pruefePlanMitKI(), bildIndex) — nur
|
||||
vorhanden, wenn die KI einen gültigen Bild-Index geliefert hat. Ein
|
||||
<button>, kein <a> (öffnet #pcShotModal statt eines Links auf die
|
||||
data:-URL, s. planCheckerZeigeScreenshot() — ein Link blieb wirkungslos,
|
||||
da Chrome das Öffnen einer data:-URL in einem neuen Tab blockiert),
|
||||
deshalb die Button-Vorgaben zurückgesetzt. */
|
||||
.pc-result-shot { flex: 0 0 auto; display: block; background: none; border: none; padding: 0; cursor: pointer; }
|
||||
.pc-result-shot img {
|
||||
width: 110px; height: 80px; object-fit: cover; display: block;
|
||||
border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--surface-alt);
|
||||
}
|
||||
.pc-result-shot:hover img { border-color: var(--accent); }
|
||||
.bericht-layout { display: grid; grid-template-columns: 64px minmax(0, 1fr); }
|
||||
@media (max-width: 760px) { .bericht-layout { grid-template-columns: 48px minmax(0, 1fr); } }
|
||||
|
||||
.bericht-rail {
|
||||
background: var(--surface-alt);
|
||||
border-right: 1px solid var(--line-soft);
|
||||
padding: 10px 0; display: flex; flex-direction: column; align-items: center; gap: 3px;
|
||||
/* Die Leiste bleibt beim Scrollen im Blick — sie IST die Tagesordnung. Kein
|
||||
eigener Rahmen/Radius/Schatten mehr und keine eigene Höhenbegrenzung:
|
||||
die Fläche gehört zu .bericht-panel, und eine gedeckelte Höhe zwang den
|
||||
eigenen (inzwischen entfernten) Tooltip zum Abschneiden — ein Element mit
|
||||
overflow-y ungleich visible schneidet nach der CSS-Spezifikation auch in
|
||||
X-Richtung ab. */
|
||||
position: sticky; top: 64px;
|
||||
}
|
||||
.bericht-suche { padding: 8px 9px; border-bottom: 1px solid var(--line-soft); }
|
||||
.bericht-suche input { width: 100%; padding: 5px 9px; font-size: 12.5px; }
|
||||
.bericht-liste { max-height: min(62vh, 620px); overflow-y: auto; }
|
||||
.bericht-rail-item {
|
||||
position: relative; width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--text-subtle); cursor: pointer;
|
||||
}
|
||||
.bericht-rail-item:hover { background: var(--surface-sunk); color: var(--text); }
|
||||
.bericht-rail-item:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
||||
.bericht-rail-item.is-aktiv { background: var(--accent); color: #fff; }
|
||||
/* Abgehakt: zurückgenommen, aber erkennbar — man muss im Meeting noch sehen
|
||||
können, was man schon besprochen hat. */
|
||||
.bericht-rail-item.is-ab { opacity: .55; }
|
||||
.bericht-rail-item.is-aktiv.is-ab { opacity: 1; }
|
||||
.bericht-rail-num { font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
|
||||
.bericht-rail-dot { position: absolute; top: 3px; right: 3px; width: 6px; height: 6px; border-radius: 50%; }
|
||||
.bericht-rail-dot.is-new, .bericht-rail-dot.is-status { background: var(--band-neu); }
|
||||
.bericht-rail-dot.is-progress { background: var(--band-fortschritt); }
|
||||
.bericht-rail-item.is-aktiv .bericht-rail-dot { outline: 1.5px solid var(--accent-soft); outline-offset: -1px; }
|
||||
.bericht-rail-tooltip {
|
||||
position: absolute; left: 48px; top: 50%; transform: translateY(-50%);
|
||||
background: var(--text); color: var(--surface); font-size: 11px; padding: 4px 8px;
|
||||
border-radius: var(--r-sm); white-space: nowrap; opacity: 0; pointer-events: none;
|
||||
transition: opacity .12s var(--ease); z-index: 5;
|
||||
}
|
||||
.bericht-rail-item:hover .bericht-rail-tooltip,
|
||||
.bericht-rail-item:focus-visible .bericht-rail-tooltip { opacity: 1; }
|
||||
|
||||
.bericht-gruppe {
|
||||
font-size: 10px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
|
||||
color: var(--text-subtle); padding: 9px 11px 3px;
|
||||
}
|
||||
.bericht-zeile {
|
||||
display: flex; align-items: center; gap: 7px;
|
||||
padding: 5px 10px; cursor: pointer;
|
||||
border-left: 2px solid transparent;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.bericht-zeile:hover { background: var(--surface-alt); }
|
||||
.bericht-zeile:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
||||
.bericht-zeile.is-aktiv { background: var(--accent-soft); border-left-color: var(--accent); }
|
||||
/* Abgehakt: zurückgenommen, aber lesbar — man muss im Meeting noch erkennen
|
||||
können, was man besprochen hat. */
|
||||
.bericht-zeile.is-ab .bericht-zeile-name,
|
||||
.bericht-zeile.is-ab .bericht-zeile-nr { color: var(--text-subtle); }
|
||||
.bericht-zeile-nr {
|
||||
font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px;
|
||||
color: var(--text-muted); flex-shrink: 0;
|
||||
}
|
||||
.bericht-zeile-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
/* max-width: die Hauptspalte bleibt eine schmale Lesespalte statt bis zum
|
||||
rechten Rand zu laufen — Teil der Fokus-Idee, nicht nur die Nebeninfos
|
||||
wegzuklappen, sondern auch dem Kommentar selbst keine unnötige Breite zu
|
||||
geben. */
|
||||
.bericht-main { padding: 26px 36px 30px; max-width: 640px; min-width: 0; }
|
||||
|
||||
.bericht-haken {
|
||||
width: 17px; height: 17px; flex-shrink: 0; padding: 0;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
border: 1.5px solid var(--line); border-radius: 50%;
|
||||
background: transparent; color: transparent;
|
||||
font-size: 10px; line-height: 1; cursor: pointer;
|
||||
.bericht-head-line { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
|
||||
.bericht-head-nr {
|
||||
font-family: ui-monospace, 'SF Mono', monospace; font-size: 11.5px;
|
||||
color: var(--text-subtle);
|
||||
}
|
||||
.bericht-haken:hover:not(:disabled) { border-color: var(--green); color: var(--green); }
|
||||
.bericht-haken.ab { background: var(--green); border-color: var(--green); color: #fff; }
|
||||
.bericht-haken:disabled { cursor: default; opacity: .5; }
|
||||
|
||||
/* Änderungs-Kennzeichen: nur noch ein Punkt in der Liste — die Gruppierung
|
||||
nach Änderungsart ist entfallen, weil im Meeting ohnehin jedes Projekt
|
||||
besprochen wird. Die ausformulierte Begründung steht im Detail. */
|
||||
/* Die Zustandsklassen tragen ein "is-"-Präfix: Ohne es hieß eine davon
|
||||
schlicht "status" und traf die Regel .status { min-width: 240px } weiter
|
||||
oben — der 7-px-Punkt wurde 240 px breit und quetschte den Projektnamen
|
||||
auf null. */
|
||||
.bericht-punkt { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; }
|
||||
.bericht-punkt.is-new, .bericht-punkt.is-status { background: var(--band-neu); }
|
||||
.bericht-punkt.is-progress { background: var(--band-fortschritt); }
|
||||
.bericht-abwesend {
|
||||
font-size: 10px; font-weight: 600; color: var(--amber);
|
||||
background: color-mix(in srgb, var(--amber) 15%, transparent);
|
||||
border-radius: var(--r-pill); padding: 0 5px; flex-shrink: 0;
|
||||
.bericht-change-pill {
|
||||
font-size: 10.5px; font-weight: 700; color: #fff; padding: 2px 9px; border-radius: var(--r-pill);
|
||||
}
|
||||
|
||||
.bericht-detail {
|
||||
background: var(--surface); border-radius: var(--r-md); box-shadow: var(--sh-1);
|
||||
padding: 15px 17px 16px; min-width: 0;
|
||||
}
|
||||
.bericht-detail-kopf { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
|
||||
.bericht-detail-kopf h3 { margin: 0; font-size: 16px; font-weight: 600; }
|
||||
.bericht-detail-zaehler {
|
||||
font-family: ui-monospace, 'SF Mono', monospace; font-size: 11px;
|
||||
color: var(--text-subtle); font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.bericht-detail-meta { color: var(--text-muted); font-size: 12px; margin: 2px 0 12px; }
|
||||
.bericht-kennz {
|
||||
font-size: 11px; border-radius: var(--r-pill); padding: 1px 9px; color: #fff;
|
||||
}
|
||||
.bericht-kennz.is-new, .bericht-kennz.is-status { background: var(--band-neu); }
|
||||
.bericht-kennz.is-progress { background: var(--band-fortschritt); }
|
||||
.bericht-change-pill.is-new, .bericht-change-pill.is-status { background: var(--band-neu); }
|
||||
.bericht-change-pill.is-progress { background: var(--band-fortschritt); }
|
||||
.bericht-h1 { font-size: 21px; font-weight: 650; letter-spacing: -0.012em; margin: 0 0 3px; }
|
||||
.bericht-sub { color: var(--text-subtle); font-size: 12px; margin-bottom: 16px; }
|
||||
|
||||
.bericht-abwesend-hinweis {
|
||||
font-size: 12.5px; color: var(--amber);
|
||||
background: color-mix(in srgb, var(--amber) 10%, transparent);
|
||||
border-radius: var(--r-sm); padding: 6px 10px; margin-bottom: 10px;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
color: var(--amber); font-size: 12px; margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Der Kommentar der Vorwoche steht ÜBER dem Eingabefeld: "Was hatten wir
|
||||
letzte Woche vereinbart?" ist die häufigste Frage im Meeting. */
|
||||
.bericht-vorwoche {
|
||||
display: flex; gap: 9px; align-items: baseline;
|
||||
padding: 7px 10px; margin-bottom: 11px;
|
||||
background: var(--surface-alt); border-left: 2px solid var(--line);
|
||||
border-radius: 0 var(--r-sm) var(--r-sm) 0;
|
||||
font-size: 12.5px;
|
||||
background: var(--surface-sunk); border-radius: var(--r-md);
|
||||
padding: 9px 12px; margin-bottom: 14px; font-size: 12px; color: var(--text-muted);
|
||||
}
|
||||
.bericht-vorwoche-label {
|
||||
display: block; font-weight: 600; color: var(--text-subtle);
|
||||
text-transform: uppercase; font-size: 10.5px; letter-spacing: .05em; margin-bottom: 3px;
|
||||
}
|
||||
.bericht-vorwoche-label { font-weight: 600; color: var(--text-muted); white-space: nowrap; flex-shrink: 0; }
|
||||
.bericht-vorwoche-text { color: var(--text-muted); }
|
||||
|
||||
.bericht-detail-fuss {
|
||||
display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
|
||||
margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line-soft);
|
||||
}
|
||||
.bericht-detail-fuss { display: flex; align-items: center; gap: 14px; margin-top: 20px; }
|
||||
.bericht-fuss-spacer { flex: 1; }
|
||||
|
||||
.person-chip-label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
|
||||
|
||||
Reference in new issue
Block a user