Nummer 4 im Redaktionsplan, erscheint Fr 21.08. Carousel, fuenf Slides, Theme modern, Variante hell, Punktgrund. Stichwort des Plans: "Der Ueberblick, den jeder Betrieb vermisst." DER BEITRAG Das Demo-Projekt haelt fuer die Pipeline vier eigene Bildschirme bereit -- Schablone, Board, Liste, Detail. Jeder bekommt eine Slide, dazu die Karte aus der Naehe: 1 Von der Anfrage bis zur Abnahme. 4A das Board, drei Phasen 2 Alles Wichtige auf einer Karte. 4A eine Karte gross 3 Vom Verkauf in die Abwicklung. 4B das Projekt in der Abwicklung 4 Board oder Liste. 4C dieselben Projekte als Tabelle 5 Du bestimmst, was draufsteht. 3 der Feldeditor ES SIND ZWEI PIPELINES, NICHT EINE Das ist die Pointe und der Grund fuer Slide 3. Das Board gehoert zu "Vertrieb: Anlagen-Verkauf", das Projekt in der Detailansicht liegt in "Auftragsabwicklung SHK", und ein Knopf in der Kopfzeile schiebt es von der einen in die andere. Beide Namen stehen woertlich in PipelineBoardInteractive.tsx und S02PipelinesDetailansicht.tsx. Warum das eine Software sein kann, beantwortet Slide 5: die Felder haengen an der Pipeline, nicht am Programm. Siebzehn Feldtypen und drei Stufen unter "Pflicht ab Board-Spalte", im Screen offen auf "Aufmass erledigt". Ein SHK-Betrieb legt sich das an, ein anderer etwas anderes. EINE RICHTIGSTELLUNG Ein Zwischenstand behauptete, die Detailansicht sei nur ein Entwurfs-PNG. Geprueft worden waren die Beschriftungen aus 07_Screen-4b_Karten-Detail.png -- KUNDENDATEN, OBJEKTDATEN, Wahrscheinlichkeit, Heizlast, Lead-Quelle -- und die kommen im Code wirklich nirgends vor. Daraus wurde der falsche Schluss gezogen, es gebe den Bildschirm nicht. Es gibt ihn: app/(screens)/4b/ rendert S02PipelinesDetailansicht.tsx, 3150 Zeilen, mit den Rubriken GRUNDDATEN, PLANUNG & VERANTWORTLICHE, PROJEKTKONTAKTDATEN, KONTAKTE, FIRMENDATEN, ANSPRECHPARTNER, PROJEKTADRESSE und ZUSATZADRESSEN. Das PNG ist ein aelterer Entwurf desselben Bildschirms, nicht sein Beleg. Die Abwesenheit einer Zeichenkette beweist die Abwesenheit eines Bildschirms nicht. DIE KARTE KOMMT AUS DEM CODE, NICHT AUS DEM SCREENSHOT Der erste Anlauf war von assets/screens/pipelines.png abgemalt und lag an drei Stellen daneben, von denen im Bild keine zu erkennen war: Prioritaet, Balken und Kuerzel stehen als Gruppe rechts und haengen ueber die Kante des Fusses (absolute -bottom-4 right-2), im Fuss steht ueber dem Termin die Zeitspanne des Projekts, und die Balken sitzen in einem eigenen umrandeten Kasten. Erst ProjectCard.tsx hat das gezeigt. Die Karte ist jetzt Klasse fuer Klasse daraus nachgebaut, 320 breit wie w-80, Titel auf hoechstens zwei Zeilen wie line-clamp-2. Anders als bei Post 4 wird nichts umgefaerbt. Eine Pipeline lebt davon, dass man das Gewerk an der Farbe erkennt, ohne zu lesen, und dass der Fuss sich mit der Prioritaet faerbt. Zuordnung aus CATEGORY_TONES, Toene aus ui/theme.css, Kuerzelfarbe aus avatarColor je Projekt. ALLE ZAHLEN SIND GERECHNET Anzahl und Summe je Spalte kommen aus denselben 41 Datensaetzen wie die Karten: Angebot 189.800, Auftrag 224.800, In Ausfuehrung 352.300 Euro. Wer eine aendert, muss die andere mitziehen. WO BEWUSST ABGEWICHEN WIRD - Slide 1 zeigt zwei bis drei Karten je Spalte, der Kopf zaehlt weiter vier. So ist es im Werkzeug auch, eine Spalte zeigt, was aufs Bild passt. Gleich hohe Saeulen sahen aus wie eine Tabelle. - Slide 4 zeigt vier von neun Tabellenspalten und sieben von elf Zeilen. Die Spalte Sachbearbeiter ist entfallen, damit "Auftragswert gesperrt" ungekuerzt hineinpasst -- der Wortlaut ist der Grund, warum die Spalte ueberhaupt auf der Slide steht. - Slide 3 stellt die Rubriken untereinander statt nebeneinander, und zwei fehlen: PDS-INTEGRATION, weil der Beitrag sich an Betriebe allgemein richtet und nicht an die mit diesem einen Branchenprogramm, und "ADRESSDATEN . TEST A (ZUSATZADRESSEN AN)", weil das ein Arbeitstitel aus der Entwicklung ist und kein Bildschirmtext. - Ein "Kalender" im Umschalter stand kurz drin und ist raus: die ToggleGroup des Boards kennt board und list, mehr nicht. DIE MASSSTAEBE SIND ABGESTIMMT Slide 1, 3, 4 und 5 zeigen ihre Schrift zwischen 16 und 23 px. Slide 2 ist als Nahaufnahme gedacht und liegt bei 47. Vorher stand sie auf 59 bis 73, und eine einzelne Karte wirkte damit groesser als das ganze Board davor. Der Kopfbereich ueber dem Board -- Seitentitel, Umschalter, Filterleiste -- ist wieder entfallen. Im Profilraster ist die Slide eine Kachel von rund 150 px, dort liest niemand eine Bedienoberflaeche. Wie die Pipeline heisst, sagt Slide 3; dass es eine Liste gibt, sagt Slide 4. EIN PRUEFSKRIPT HAT DEN PROJEKTSTAND VERSTELLT export.mjs schaltet die Slides mit der Pfeiltaste weiter. Die Seite startet aber im Bearbeiten-Modus, und dort verschieben Pfeiltasten das gewaehlte Element statt die Slide zu wechseln. Jeder Prueflauf hat die Zeichnungen geschoben und das in state/post5.json geschrieben: zuletzt vis dx 3 dy -69 auf Slide 1 und auf Slide 2 ein size 762, das die im Code eingestellten 720 ueberschrieb. Die Karte, ueber deren Groesse zwei Runden lang geredet wurde, war beim Messen also gar nicht die aus dem Code. Gemerkt an Messwerten, die im Code nirgends standen -- 89 px Rand rechts bei eingestellten 92, 177 px unten bei eingestellten 120. Zahlen, die man sich nicht erklaeren kann, sind ein Befund und keine Messungenauigkeit. Der Stand ist bereinigt, das Skript verlaesst vor dem Blaettern den Bearbeiten-Modus und schreibt die Datei am Ende zurueck. NEBENBEI - Plaettchenbreiten werden gemessen statt geschaetzt. Zeichenzahl mal 0,52 em stimmt fuer "Heizung" und geht bei "Waermepumpe" daneben, das Wort stand ueber dem Rand. Ein Canvas misst dieselbe Schrift, die das SVG spaeter zeichnet; aus derselben Messung kommt der Titelumbruch. - Die Ueberschrift von Slide 1 steht auf denselben Werten wie die von post4.html, nachgemessen: 76 px, Gewicht 800, -2,432 px Sperrung, zentriert, Oberkante 95, Blockhoehe 158, Versatz dy 3. - Die Projektliste steht siebenmal im Repo, einmal je Post-Seite plus uebersicht.html, dort zusaetzlich in PROJEKTE. Post 5 ist ueberall eingetragen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
235 lines
9.7 KiB
HTML
235 lines
9.7 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>PostGrid · Alle Projekte</title>
|
|
<link rel="stylesheet" href="./assets/fonts/manrope.css">
|
|
<style>
|
|
/* ─── Projektübersicht ────────────────────────────────────────────────────
|
|
Alle Streifen untereinander auf einer Fläche. Die Seite baut die Slides
|
|
nicht selbst: jedes Projekt kommt als iframe im Modus ?nur=streifen, in
|
|
dem die Post-Seite ihre Bedienoberfläche ausblendet und nur den Streifen
|
|
in natürlicher Größe zeigt. Skaliert wird hier außen.
|
|
|
|
Warum nicht nachbauen: die Streifen müssen genau das zeigen, was der
|
|
Generator zeigt — bis auf den letzten gespeicherten Versatz. Jede zweite
|
|
Implementierung würde irgendwann abweichen, und dann wäre die Übersicht
|
|
schlimmer als keine. */
|
|
* { box-sizing: border-box; }
|
|
:root { color-scheme: dark; }
|
|
html, body { height: 100%; }
|
|
body {
|
|
margin: 0; font-family: Manrope, system-ui, sans-serif;
|
|
background: #101116; color: #C7CCD6; overflow: hidden;
|
|
}
|
|
|
|
.menubar {
|
|
position: relative; z-index: 10; display: flex; align-items: center; gap: 2px;
|
|
height: 34px; padding: 0 10px; background: #16181F;
|
|
border-bottom: 1px solid #23262F;
|
|
font-size: 13px; user-select: none;
|
|
}
|
|
.menubar .marke { font-weight: 800; color: #FFFFFF; margin-right: 14px; letter-spacing: -.01em; }
|
|
.menu { position: relative; }
|
|
.menu > button {
|
|
font: inherit; color: #C7CCD6; background: none; border: 0;
|
|
padding: 5px 10px; border-radius: 6px; cursor: pointer;
|
|
}
|
|
.menu > button:hover, .menu[data-auf] > button { background: #23262F; color: #FFFFFF; }
|
|
.menu .liste {
|
|
display: none; position: absolute; left: 0; top: 100%; min-width: 250px;
|
|
padding: 5px; background: #1B1E26; border: 1px solid #2E313B;
|
|
border-radius: 9px; flex-direction: column;
|
|
}
|
|
.menu[data-auf] .liste { display: flex; }
|
|
.menu .liste button {
|
|
font: inherit; text-align: left; color: #C7CCD6; background: none;
|
|
border: 0; padding: 7px 10px; border-radius: 6px; cursor: pointer;
|
|
}
|
|
.menu .liste button:hover { background: #262A34; color: #FFFFFF; }
|
|
.menu .liste hr { border: 0; border-top: 1px solid #2E313B; margin: 5px 2px; }
|
|
|
|
/* Derselbe gepunktete Grund wie im Generator, aber ohne dessen Canvas:
|
|
hier wird nur gescrollt. */
|
|
.stage { position: absolute; inset: 34px 0 0; overflow: hidden;
|
|
background-color: #101116;
|
|
background-image: radial-gradient(circle, #282B35 1.6px, transparent 1.6px);
|
|
background-size: 26px 26px;
|
|
}
|
|
/* Nur hoch und runter. Die Streifen sind auf die Breite eingepasst,
|
|
also gibt es waagerecht nichts zu holen. */
|
|
.scroller { position: absolute; inset: 0; overflow-y: auto; overflow-x: hidden;
|
|
overscroll-behavior: contain; }
|
|
.flaeche {
|
|
width: max-content; min-width: 100%; height: max-content; min-height: 100%;
|
|
padding: 54px; display: flex; flex-direction: column; gap: 46px;
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.reihe { display: flex; flex-direction: column; gap: 10px; }
|
|
.reihe h2 {
|
|
margin: 0; font-size: 12px; font-weight: 800; letter-spacing: .14em;
|
|
text-transform: uppercase; color: #6E7480;
|
|
}
|
|
.reihe h2 b { color: #C7CCD6; font-weight: 800; }
|
|
/* Der Rahmen bekommt die Größe des skalierten Streifens, das iframe darin
|
|
bleibt in Originalgröße und wird nur transformiert. Ohne den Rahmen
|
|
würde das ungeskalierte iframe die Fläche aufblähen. */
|
|
.rahmen { overflow: hidden; background: #FFFFFF; border: 1px solid #2E313B; }
|
|
/* pointer-events: none -- sonst zoomt das Mausrad im Streifen die
|
|
Post-Seite darin, die ja ihren eigenen Canvas mitbringt. Die Uebersicht
|
|
ist zum Ansehen da, bedient wird im Projekt selbst. */
|
|
.rahmen iframe {
|
|
display: block; border: 0; transform-origin: 0 0; background: #FFFFFF;
|
|
pointer-events: none;
|
|
}
|
|
.reihe.fehlt .rahmen { display: grid; place-items: center; width: 420px; height: 90px;
|
|
background: #16181F; color: #6E7480; font-size: 13px; }
|
|
|
|
.hud {
|
|
position: absolute; left: 0; right: 0; bottom: 0; z-index: 5;
|
|
display: flex; align-items: center; gap: 12px;
|
|
padding: 8px 12px; background: rgba(16,17,22,.92);
|
|
border-top: 1px solid #23262F;
|
|
}
|
|
.hud p { margin: 0; font-size: 12px; color: #6E7480; letter-spacing: .04em; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="menubar">
|
|
<span class="marke">PostGrid</span>
|
|
<div class="menu" data-menu="projekt">
|
|
<button type="button">Projekt</button>
|
|
<div class="liste">
|
|
<button type="button" data-gehe="./post1.html">Post 1 · Lösung</button>
|
|
<button type="button" data-gehe="./post2.html">Post 2 · Die Geschichte</button>
|
|
<button type="button" data-gehe="./post2-alt.html">Post 2 · Alternative</button>
|
|
<button type="button" data-gehe="./post3.html">Post 3 · Verbinden statt ersetzen</button>
|
|
<button type="button" data-gehe="./post4.html">Post 4 · Workflow-Engine</button>
|
|
<button type="button" data-gehe="./post5.html">Post 5 · Projektmanagement</button>
|
|
<hr>
|
|
<button type="button" data-klick="neu">Streifen neu laden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<main class="stage">
|
|
<div class="scroller" id="scroller">
|
|
<div class="flaeche" id="flaeche"></div>
|
|
</div>
|
|
<div class="hud">
|
|
<p id="meta">Lade Streifen ...</p>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
(() => {
|
|
"use strict";
|
|
|
|
const PROJEKTE = [
|
|
{ datei: "./post1.html", name: "Post 1 · Lösung" },
|
|
{ datei: "./post2.html", name: "Post 2 · Die Geschichte", theme: "notebook" },
|
|
{ datei: "./post2-alt.html", name: "Post 2 · Alternative", theme: "modern" },
|
|
{ datei: "./post3.html", name: "Post 3 · Verbinden statt ersetzen", theme: "modern" },
|
|
{ datei: "./post4.html", name: "Post 4 · Workflow-Engine", theme: "modern" },
|
|
{ datei: "./post5.html", name: "Post 5 · Projektmanagement", theme: "modern" },
|
|
];
|
|
|
|
const flaeche = document.getElementById("flaeche");
|
|
const scroller = document.getElementById("scroller");
|
|
const meta = document.getElementById("meta");
|
|
|
|
/* Kein Zoom. Ein Streifen ist bis zu 6480 px breit und wird immer auf die
|
|
Fensterbreite eingepasst; gescrollt wird nur hoch und runter. Ein
|
|
Zwischenstand hatte Rad-Zoom und Zoomknöpfe -- das war doppelt gemoppelt,
|
|
weil die Post-Seite im iframe ihren eigenen Canvas mitbringt, und die
|
|
Beschriftungen skalierten nicht mit, standen beim Herauszoomen also
|
|
riesig über winzigen Streifen. */
|
|
const reihen = [];
|
|
|
|
function bauen() {
|
|
flaeche.innerHTML = "";
|
|
reihen.length = 0;
|
|
PROJEKTE.forEach(pr => {
|
|
const reihe = document.createElement("div");
|
|
reihe.className = "reihe";
|
|
const h2 = document.createElement("h2");
|
|
h2.innerHTML = "<b>" + pr.name + "</b>" + (pr.theme ? " · Theme " + pr.theme : "");
|
|
const rahmen = document.createElement("div");
|
|
rahmen.className = "rahmen";
|
|
const ram = document.createElement("iframe");
|
|
ram.src = pr.datei + "?nur=streifen";
|
|
ram.loading = "eager";
|
|
rahmen.appendChild(ram);
|
|
reihe.appendChild(h2); reihe.appendChild(rahmen);
|
|
flaeche.appendChild(reihe);
|
|
reihen.push({ pr, reihe, rahmen, ram, w: 6480, h: 1350, da: false });
|
|
});
|
|
}
|
|
|
|
/* Jede Post-Seite meldet ihre Streifenbreite, sobald sie steht. Die Zahl
|
|
der Slides ist je Projekt anders und kann sich ändern, deshalb wird sie
|
|
nicht hier gepflegt. */
|
|
window.addEventListener("message", e => {
|
|
const d = e.data;
|
|
if (!d || d.hg !== "streifen") return;
|
|
const r = reihen.find(x => x.ram.contentWindow === e.source);
|
|
if (!r) return;
|
|
r.w = d.w; r.h = d.h; r.da = true;
|
|
legen();
|
|
});
|
|
|
|
// Eingepasst wird nur in die Breite, die Höhe ergibt sich und wird gescrollt.
|
|
const POLSTER = 54 * 2;
|
|
function passend() {
|
|
const breit = Math.max(...reihen.map(r => r.w), 1080);
|
|
return Math.min((scroller.clientWidth - POLSTER) / breit, 0.5);
|
|
}
|
|
|
|
function legen() {
|
|
const k = passend();
|
|
reihen.forEach(r => {
|
|
r.ram.style.width = r.w + "px";
|
|
r.ram.style.height = r.h + "px";
|
|
r.ram.style.transform = "scale(" + k + ")";
|
|
r.rahmen.style.width = Math.round(r.w * k) + "px";
|
|
r.rahmen.style.height = Math.round(r.h * k) + "px";
|
|
});
|
|
const offen = reihen.filter(r => r.da).length;
|
|
meta.textContent = offen < reihen.length
|
|
? "Lade Streifen ... " + offen + " von " + reihen.length
|
|
// Seit Post 3 gibt es Einzelbilder: "1 Slides" stand vorher da.
|
|
: reihen.length + " Projekte · " + reihen.map(r => {
|
|
const n = Math.round(r.w / 1080);
|
|
return n + (n === 1 ? " Slide" : " Slides");
|
|
}).join(" · ");
|
|
}
|
|
|
|
// Kein Ziehen, kein Rad-Zoom: die Seite scrollt wie eine gewoehnliche Seite.
|
|
window.addEventListener("resize", legen);
|
|
|
|
// ─── Menü ────────────────────────────────────────────────────────────────
|
|
document.querySelectorAll(".menu").forEach(m => {
|
|
m.querySelector("button").addEventListener("click", e => {
|
|
e.stopPropagation();
|
|
const auf = m.hasAttribute("data-auf");
|
|
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
|
|
if (!auf) m.setAttribute("data-auf", "");
|
|
});
|
|
});
|
|
document.addEventListener("click", e => {
|
|
const b = e.target.closest("button[data-gehe], button[data-klick]");
|
|
document.querySelectorAll(".menu").forEach(x => x.removeAttribute("data-auf"));
|
|
if (!b) return;
|
|
if (b.dataset.gehe) { location.href = b.dataset.gehe; return; }
|
|
if (b.dataset.klick === "neu") { bauen(); legen(); }
|
|
});
|
|
bauen();
|
|
legen();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|