Organigramm: Lage je Abteilung in der Teamuebersicht (#688)
- TeamMember: Laeufe und Kosten heute, offene und blockierte Aufgaben je Agent (eine Abfrage fuer alle, nicht eine je Agent) - TeamUnit: dieselben Werte samt Untereinheiten, offene Vorgaenge aus der Einheit, heutige Kostenmeldung (#699) - team.js: Lagezeile auf jeder Karte und in der Seitenleiste, Instanz summiert; Spruenge zu Kosten je Einheit und zu den Vorgaengen; i18n en/de - Tests: TeamOverviewTests (+2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
888369698d
commit
f649f2fd7a
@@ -2332,6 +2332,21 @@ td.klein {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* #688: a department's situation — runs, cost, tasks, cases */
|
||||
.team-lage {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 10px;
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
color: var(--gedaempft);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.team-lage .ton-warnung {
|
||||
color: var(--warnung);
|
||||
}
|
||||
|
||||
.team-chip {
|
||||
padding: 1px 6px;
|
||||
font-size: 11px;
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
"team.members": "Mitglieder",
|
||||
"team.subUnits": "Untereinheiten",
|
||||
"team.openOrganisation": "Organisation bearbeiten",
|
||||
"team.openCosts": "Kosten je Einheit",
|
||||
"team.openCases": "Offene Vorgänge",
|
||||
"team.situation.runsHint": "Läufe heute, in dieser Einheit und darunter",
|
||||
"team.situation.costHint": "Kosten heute, in dieser Einheit und darunter",
|
||||
"team.situation.tasksHint": "Offene Aufgaben",
|
||||
"team.situation.blockedHint": "Blockierte Aufgaben — warten auf jemanden",
|
||||
"team.situation.casesHint": "Offene Vorgänge aus dieser Einheit",
|
||||
"team.rootSubtitle": "Instanz · oberste Ebene",
|
||||
"team.structure": "Struktur",
|
||||
"team.searchPlaceholder": "Agenten, Einheiten, Modelle suchen…",
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
"team.members": "Members",
|
||||
"team.subUnits": "Sub-units",
|
||||
"team.openOrganisation": "Edit organisation",
|
||||
"team.openCosts": "Costs by unit",
|
||||
"team.openCases": "Open cases",
|
||||
"team.situation.runsHint": "Runs today, in this unit and below",
|
||||
"team.situation.costHint": "Cost today, in this unit and below",
|
||||
"team.situation.tasksHint": "Open tasks",
|
||||
"team.situation.blockedHint": "Blocked tasks — waiting for someone",
|
||||
"team.situation.casesHint": "Open cases raised in this unit",
|
||||
"team.rootSubtitle": "Instance · top level",
|
||||
"team.structure": "Structure",
|
||||
"team.searchPlaceholder": "Search agents, units, models…",
|
||||
|
||||
@@ -235,7 +235,8 @@ function indexSnapshot() {
|
||||
agents: snapshot.members.length,
|
||||
states,
|
||||
pendingApprovals: snapshot.members.reduce((sum, m) => sum + m.pendingApprovals, 0),
|
||||
worst
|
||||
worst,
|
||||
situation: sumSituation(snapshot.members)
|
||||
});
|
||||
|
||||
for (const unit of snapshot.units ?? []) {
|
||||
@@ -250,7 +251,15 @@ function indexSnapshot() {
|
||||
agents: unit.agents,
|
||||
states: unit.states ?? {},
|
||||
pendingApprovals: unit.pendingApprovals,
|
||||
worst: unit.worst
|
||||
worst: unit.worst,
|
||||
situation: {
|
||||
runsToday: unit.runsToday ?? 0,
|
||||
costToday: unit.costToday ?? 0,
|
||||
openTasks: unit.openTasks ?? 0,
|
||||
blockedTasks: unit.blockedTasks ?? 0,
|
||||
openCases: unit.openCases ?? 0,
|
||||
costAlert: unit.costAlert ?? null
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -727,6 +736,33 @@ function visibleMembers(card) {
|
||||
return { rows, hidden: all.length - rows.length };
|
||||
}
|
||||
|
||||
/** #688: The instance's own situation — the units' come from the instance, this total is added up here. */
|
||||
function sumSituation(members) {
|
||||
const sum = (key) => members.reduce((n, m) => n + (m[key] ?? 0), 0);
|
||||
return { runsToday: sum("runsToday"), costToday: sum("costToday"), openTasks: sum("openTasks"), blockedTasks: sum("blockedTasks"), openCases: 0, costAlert: null };
|
||||
}
|
||||
|
||||
/**
|
||||
* #688: A department's situation in one line — runs and cost today, tasks open and blocked,
|
||||
* cases raised in it. The way in from the top: first the unit that stands out, then the agent.
|
||||
*/
|
||||
function situationLine(card) {
|
||||
const s = card.situation;
|
||||
if (!s) return null;
|
||||
const parts = [
|
||||
erstelleElement("span", { title: t("team.situation.runsHint"), text: `▶ ${s.runsToday}` }),
|
||||
erstelleElement("span", {
|
||||
klasse: s.costAlert ? "ton-warnung" : "",
|
||||
title: s.costAlert ? t(`usage.units.kind.${s.costAlert}`) : t("team.situation.costHint"),
|
||||
text: `${s.costAlert ? "⚠ " : ""}${Number(s.costToday).toFixed(2)} $`
|
||||
}),
|
||||
erstelleElement("span", { title: t("team.situation.tasksHint"), text: `☐ ${s.openTasks}` })
|
||||
];
|
||||
if (s.blockedTasks) parts.push(erstelleElement("span", { klasse: "ton-warnung", title: t("team.situation.blockedHint"), text: `⛔ ${s.blockedTasks}` }));
|
||||
if (s.openCases) parts.push(erstelleElement("span", { klasse: "ton-warnung", title: t("team.situation.casesHint"), text: `⚖ ${s.openCases}` }));
|
||||
return erstelleElement("div", { klasse: "team-lage" }, ...parts);
|
||||
}
|
||||
|
||||
function buildCard(card) {
|
||||
const worst = STATES[card.worst] ?? STATES.Idle;
|
||||
const dimmed = hits && !hits.onPath.has(card.id);
|
||||
@@ -754,7 +790,8 @@ function buildCard(card) {
|
||||
erstelleElement("span", { klasse: "team-marker", text: worst.symbol }),
|
||||
erstelleElement("span", { klasse: "team-karte-name", text: card.title })),
|
||||
erstelleElement("div", { klasse: "team-karte-unter", text: card.subtitle }),
|
||||
erstelleElement("div", { klasse: "team-chips" }, ...chips(card))));
|
||||
erstelleElement("div", { klasse: "team-chips" }, ...chips(card)),
|
||||
situationLine(card)));
|
||||
|
||||
const { rows, hidden } = visibleMembers(card);
|
||||
if (rows.length || hidden) {
|
||||
@@ -1222,8 +1259,9 @@ function cardRows(cardId) {
|
||||
panelHead(card.title),
|
||||
erstelleElement("p", { klasse: "gedaempft text-klein", text: card.unit?.path ?? card.subtitle }),
|
||||
erstelleElement("div", { klasse: "team-chips" }, ...chips(card)),
|
||||
situationLine(card),
|
||||
erstelleElement("p", { klasse: "gedaempft text-klein", text: t("team.subtreeHint") })
|
||||
];
|
||||
].filter(Boolean);
|
||||
|
||||
if (card.unit?.goal) rows.push(erstelleElement("p", { klasse: "text-klein", text: card.unit.goal }));
|
||||
|
||||
@@ -1263,6 +1301,8 @@ function cardRows(cardId) {
|
||||
}));
|
||||
}
|
||||
if (card.unit) actions.append(link("#/organisation", t("team.openOrganisation")));
|
||||
if (card.situation?.costAlert || card.situation?.costToday > 0) actions.append(link("#/verbrauch", t("team.openCosts")));
|
||||
if (card.situation?.openCases) actions.append(link("#/cases", t("team.openCases")));
|
||||
if (actions.children.length) rows.push(actions);
|
||||
return rows;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user