diff --git a/html/js/office-render.js b/html/js/office-render.js
index deda218..fe98c27 100644
--- a/html/js/office-render.js
+++ b/html/js/office-render.js
@@ -34,6 +34,11 @@ import {
resolveDoorVisitVisual,
advanceDoorVisitStage,
DOOR_VISIT_STAGES,
+ detectUnassignedTicketChanges,
+ advancePoVisitStage,
+ PO_VISIT_STAGES,
+ PO_AGENT_ID,
+ PO_DISPLAY_NAME,
resolveReloadIntervalSeconds,
nextCountdownSeconds,
} from "./office-scene.js";
@@ -62,6 +67,11 @@ const DESK_ROW_Y = [540, 840];
const DESK_COL_X = [232, 644, 1056, 1468];
const COFFEE_CORNER = { x: 960, y: 780 };
+// Fester Platz des Product Owner (Issue #19): bewusst ausserhalb des dynamischen 8er-Rasters
+// (DESK_COL_X/DESK_ROW_Y), in der freien Nische zwischen Board/Monitor-Wand und der ersten
+// Schreibtischreihe - so kollidiert er nie mit einem echten, ueber die API registrierten Agenten.
+const PO_DESK = { x: 870, y: 380, w: 180 };
+
const NOTE_W = 96;
const NOTE_H = 50;
const NOTE_STEP_Y = 56;
@@ -74,6 +84,9 @@ const COFFEE_FEET = { x: COFFEE_CORNER.x, y: COFFEE_CORNER.y + 26 };
// Ruheplatz der Tuerbesucher (Phase 6, F2): direkt vor der linken Tuer, wie schon der
// Ueberlaufplatz fuer Agenten ohne Schreibtisch (siehe agentPlacements()).
const DOOR_FEET = { x: 20, y: DOOR.y + DOOR.h / 2 };
+// Ruheplatz des PO an seinem festen Platz (Issue #19), analog zu den Fuss-Positionen der
+// regulaeren Schreibtische (deskSeat().feet).
+const PO_DESK_FEET = { x: PO_DESK.x + PO_DESK.w / 2, y: PO_DESK.y + 40 + DESK.h + 30 };
const IDLE_MIN_DELAY_MS = 15000;
const IDLE_MAX_DELAY_MS = 40000;
@@ -429,16 +442,17 @@ function deskScreenPoseClass(pose) {
// Zeichnet nur die Moebel/Deko + den Namen des Schreibtisch-Besitzers - die eigentliche
// Person ist ein eigenstaendiger, beweglicher Avatar (siehe renderAgentAvatar/
// updateAgentsLayer), damit sie zum Board/Monitor "laufen" kann, ohne den Schreibtisch neu
-// zeichnen zu muessen.
-function renderDesk({ deskIndex, agent }) {
- const { origin, feet } = deskSeat(deskIndex);
+// zeichnen zu muessen. Parametrisiert ueber origin/width, damit sowohl die regulaeren
+// Schreibtische (deskSeat()) als auch der feste PO-Platz (PO_DESK, Issue #19) dieselbe
+// Zeichenlogik/Deko (inkl. rollenabhaengiger desk.deco) nutzen koennen.
+function renderDeskAt(origin, agent, width = DESK.w) {
const role = agent ? ROLES[agent.role] : null;
const g = el("g", {
class: `desk ${agent ? "desk-occupied" : "desk-empty"}`,
transform: `translate(${origin.x}, ${origin.y})`,
});
- g.appendChild(el("rect", { x: 0, y: 40, width: DESK.w, height: DESK.h, rx: 6, class: "desk-table" }));
+ g.appendChild(el("rect", { x: 0, y: 40, width, height: DESK.h, rx: 6, class: "desk-table" }));
g.appendChild(
el("rect", {
x: 20,
@@ -449,18 +463,18 @@ function renderDesk({ deskIndex, agent }) {
class: `desk-screen ${agent ? deskScreenPoseClass(agent.pose) : ""}`,
})
);
- g.appendChild(el("circle", { cx: DESK.w / 2, cy: feet.y - origin.y, r: 22, class: "desk-chair" }));
+ g.appendChild(el("circle", { cx: width / 2, cy: 40 + DESK.h + 30, r: 22, class: "desk-chair" }));
if (role) {
if (role.deco === "star") {
g.appendChild(
el("path", {
- d: "M170,20 l4,10 11,0 -9,7 3,11 -9,-7 -9,7 3,-11 -9,-7 11,0 z",
+ d: `M${width - 50},20 l4,10 11,0 -9,7 3,11 -9,-7 -9,7 3,-11 -9,-7 11,0 z`,
class: "desk-deco desk-deco-star",
})
);
} else if (role.deco === "flower") {
- const cx = 175;
+ const cx = width - 45;
const cy = 22;
g.appendChild(
el("g", { class: "desk-deco desk-deco-flower" }, [
@@ -473,13 +487,13 @@ function renderDesk({ deskIndex, agent }) {
);
} else if (role.deco === "check") {
g.appendChild(
- el("path", { d: "M162,24 l8,8 16,-18", class: "desk-deco desk-deco-check" })
+ el("path", { d: `M${width - 58},24 l8,8 16,-18`, class: "desk-deco desk-deco-check" })
);
}
g.appendChild(
text(agent.name, {
- x: DESK.w / 2,
+ x: width / 2,
y: 40 + DESK.h + 74,
class: "desk-name",
"text-anchor": "middle",
@@ -488,7 +502,7 @@ function renderDesk({ deskIndex, agent }) {
} else {
g.appendChild(
text("frei", {
- x: DESK.w / 2,
+ x: width / 2,
y: 40 + DESK.h + 74,
class: "desk-name desk-name-empty",
"text-anchor": "middle",
@@ -499,9 +513,21 @@ function renderDesk({ deskIndex, agent }) {
return g;
}
-function renderDesks(desks) {
+function renderDesk({ deskIndex, agent }) {
+ return renderDeskAt(deskSeat(deskIndex).origin, agent);
+}
+
+/**
+ * @param {Array