Setzt Visualization.md Abschnitt 9, Phasen 3-5 vollstaendig um: Phase 3 (Kanban-Board live): Board zeigt echte Ticket-Daten aus GET /api/v1/tickets (office-api.js), Klick auf eine Spalte oeffnet ein Modal mit Titel/Beschreibung/Typ und einem Link auf die Codeberg-Quelle bei per Sync angelegten Tickets (F4). Phase 4 (Monitor-Wand live): Event-, System- und KPI-Monitor zeigen echte Daten, Klick-Modals fuer alle drei. Neuer SSE-Endpoint GET /api/v1/stream (F13) meldet per ChangeFeed-Fingerabdruck, welche Ressourcen sich geaendert haben; office-realtime.js nutzt EventSource mit Polling-Fallback. KPI-Monitor zeichnet eine eigene SVG-Sparkline (F10, kein Chart-Lib) sobald KPIs gemeldet wurden, sonst weiterhin der Phase-2-Platzhalter (F7 bleibt inhaltlich offen). API-Antworten tragen jetzt CORS-Header, da Frontend (:8090) und API (:8091) im Docker-Compose-Setup unterschiedliche Origins sind. Phase 5 (Agenten & Animation): resolveAgentVisualState bildet gemeldete Agenten-Zustaende auf Ort/Pose ab; Agenten-Avatare sind eine per CSS transition animierte, von Board/ Schreibtisch-Moebeln entkoppelte Schicht, die zwischen Schreibtisch, Board, Monitor-Wand und einer neuen Kaffee-Ecke laufen. Idle-Varianten (Zeitung/Kaffee/Schwaetzchen) werden rein kosmetisch client-seitig zufaellig ausgeloest (F8), eine AnimationQueue serialisiert Update-Animationen bei vielen gleichzeitigen Ereignissen (F9). 29 Node-Tests (100% Line-Coverage office-scene.js) und neue PHPUnit-Tests fuer Response::stream(), ChangeFeed, StreamController und die neuen Repository::fingerprint()- Methoden. PHP/Docker-Ausfuehrung konnte in dieser Umgebung nicht verifiziert werden (kein PHP verfuegbar) - vor dem Merge bitte `docker compose run --rm api composer test` pruefen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
59 lines
1.8 KiB
JavaScript
59 lines
1.8 KiB
JavaScript
// Generisches Modal fuer Board-/Monitor-Detailansichten (Visualization.md 4.5: "Modals sind
|
|
// reines HTML/CSS/JS ohne fremde UI-Bibliothek, per <dialog>-Element ... umgesetzt"). Dieses
|
|
// Modul kuemmert sich nur um Erzeugen/Oeffnen/Schliessen des <dialog>-Elements; welchen Inhalt
|
|
// es zeigt, entscheidet der Aufrufer (office-render.js) per renderBody-Callback.
|
|
|
|
let dialogEl = null;
|
|
|
|
function ensureDialog() {
|
|
if (dialogEl) {
|
|
return dialogEl;
|
|
}
|
|
|
|
dialogEl = document.createElement("dialog");
|
|
dialogEl.className = "office-modal";
|
|
dialogEl.innerHTML = `
|
|
<form method="dialog" class="office-modal-form">
|
|
<header class="office-modal-header">
|
|
<h2 class="office-modal-title"></h2>
|
|
<button type="submit" class="office-modal-close" aria-label="Schliessen">×</button>
|
|
</header>
|
|
<div class="office-modal-body"></div>
|
|
</form>
|
|
`;
|
|
document.body.appendChild(dialogEl);
|
|
return dialogEl;
|
|
}
|
|
|
|
/**
|
|
* Oeffnet das Modal mit einem Titel und laesst den Aufrufer den Body fuellen.
|
|
*
|
|
* @param {{title: string}} content
|
|
* @param {(body: HTMLElement, content: object) => void} renderBody
|
|
*/
|
|
export function openModal(content, renderBody) {
|
|
const dialog = ensureDialog();
|
|
dialog.querySelector(".office-modal-title").textContent = content.title;
|
|
|
|
const body = dialog.querySelector(".office-modal-body");
|
|
body.replaceChildren();
|
|
renderBody(body, content);
|
|
|
|
if (typeof dialog.showModal === "function") {
|
|
dialog.showModal();
|
|
} else {
|
|
// Fallback fuer Umgebungen ohne <dialog>-Unterstuetzung (z.B. sehr alte Browser).
|
|
dialog.setAttribute("open", "");
|
|
}
|
|
}
|
|
|
|
export function closeModal() {
|
|
if (dialogEl === null) {
|
|
return;
|
|
}
|
|
if (typeof dialogEl.close === "function") {
|
|
dialogEl.close();
|
|
} else {
|
|
dialogEl.removeAttribute("open");
|
|
}
|
|
}
|