// Reine Layout-/Datenlogik fuer die Buero-Visualisierung (Phase 2, siehe Visualization.md // Abschnitt 9). Enthaelt keine DOM-Zugriffe, damit die Funktionen ohne Browser (z.B. mit dem // Node-Testrunner) getestet werden koennen. Das eigentliche Zeichnen uebernimmt office-render.js. // Spaltenreihenfolge exakt wie API.md ("Tickets" -> Spalten, F3). export const BOARD_COLUMNS = [ { key: "backlog", label: "Backlog" }, { key: "bereit", label: "Bereit" }, { key: "in_arbeit", label: "In Arbeit" }, { key: "qa", label: "QA" }, { key: "testing", label: "Testing" }, { key: "fertig", label: "Fertig" }, { key: "done", label: "Done" }, ]; // Rollen exakt wie API.md ("Agenten", F1/F2). "user" hat nie einen Schreibtisch. export const ROLES = { entwickler: { label: "Entwickler:in", accentColor: "#3b82f6", deco: "star" }, product_owner: { label: "Product Owner:in", accentColor: "#ec4899", deco: "flower" }, qa: { label: "QA / Tester:in", accentColor: "#22c55e", deco: "check" }, user: { label: "User", accentColor: "#f59e0b", deco: "none" }, }; export const MAX_VISIBLE_NOTES_PER_COLUMN = 4; export const MAX_SYSTEM_TILES = 9; export const MAX_EVENT_LINES = 5; export const DEFAULT_DESK_COUNT = 8; /** * Baut aus rohen Ticket-Zaehlern pro Spalte die Darstellung fuer das Board. * Regel (Detailantworten F3): mehr als 5 Tickets in einer Spalte -> die aeltesten 4 werden * einzeln dargestellt, der Rest als ein "Stapel" mit der Anzahl der zusaetzlichen Tickets. * Regel (Detailantworten F2): die Spalte "done" wird immer als ein Stapel (Zeit-Historie) * dargestellt, unabhaengig von der Anzahl. * * @param {Record} ticketCounts - Anzahl Tickets je Spalten-Key. * @returns {Array<{key: string, label: string, count: number, visibleNotes: number, stackCount: number}>} */ export function buildBoardColumnLayout(ticketCounts = {}) { return BOARD_COLUMNS.map(({ key, label }) => { const count = Math.max(0, ticketCounts[key] ?? 0); if (key === "done") { return { key, label, count, visibleNotes: 0, stackCount: count }; } if (count <= MAX_VISIBLE_NOTES_PER_COLUMN + 1) { return { key, label, count, visibleNotes: count, stackCount: 0 }; } return { key, label, count, visibleNotes: MAX_VISIBLE_NOTES_PER_COLUMN, stackCount: count - MAX_VISIBLE_NOTES_PER_COLUMN, }; }); } /** * Ordnet Agenten-Demo-Daten den Schreibtischen zu. Unbesetzte Schreibtische bleiben leer. * Ueberzaehlige Agenten (mehr als deskCount) werden ignoriert - Phase 2 zeigt nur den * statischen Grundzustand, die "weitere Agenten kommen durch die Tuer"-Logik ist Teil von * Phase 5 (Animation). * * @param {Array<{name: string, role: string, pose?: string}>} agents * @param {number} deskCount * @returns {Array<{deskIndex: number, agent: object|null}>} */ export function buildDeskLayout(agents = [], deskCount = DEFAULT_DESK_COUNT) { const desks = []; for (let deskIndex = 0; deskIndex < deskCount; deskIndex++) { const agent = agents[deskIndex] ?? null; desks.push({ deskIndex, agent: agent ? { pose: "idle", ...agent } : null }); } return desks; } /** * Bereitet Systemkacheln fuer den System-Monitor auf: Fehler zuerst (wie GET /api/v1/systems), * maximal MAX_SYSTEM_TILES Kacheln (F6). * * @param {Array<{id: string, name: string, status: "ok"|"error"}>} systems * @returns {Array} */ export function buildSystemTiles(systems = []) { const sorted = [...systems].sort((a, b) => { if (a.status === b.status) return 0; return a.status === "error" ? -1 : 1; }); return sorted.slice(0, MAX_SYSTEM_TILES); } /** * Bereitet die Zeilen des Event-Monitors auf: neuestes Event zuerst, maximal * MAX_EVENT_LINES Zeilen (F5). Erwartet bereits absteigend nach Zeit sortierte Events * (wie GET /api/v1/events sie liefert). * * @param {Array<{severity: "warning"|"error"}>} events * @returns {Array} */ export function buildEventLines(events = []) { return events.slice(0, MAX_EVENT_LINES); }