diff --git a/src/assets/meeting-room/HERKUNFT.md b/src/assets/staff/HERKUNFT.md similarity index 100% rename from src/assets/meeting-room/HERKUNFT.md rename to src/assets/staff/HERKUNFT.md diff --git a/src/assets/meeting-room/buero.jpg b/src/assets/staff/buero.jpg similarity index 100% rename from src/assets/meeting-room/buero.jpg rename to src/assets/staff/buero.jpg diff --git a/src/assets/team/giorgio.svg b/src/assets/team/giorgio.svg deleted file mode 100644 index 6f9c11e..0000000 --- a/src/assets/team/giorgio.svg +++ /dev/null @@ -1,60 +0,0 @@ - - Giorgio - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/components/layout/appShellConfig.ts b/src/components/layout/appShellConfig.ts index 5eba829..4750d0d 100644 --- a/src/components/layout/appShellConfig.ts +++ b/src/components/layout/appShellConfig.ts @@ -66,7 +66,7 @@ export const WORKSPACE_CONFIG: Record = { chipColor: '#152642', navItems: [ // Runde 5, §2.1/§2.2: «Übersicht» ist entfernt, «Meine Objekte» heisst im - // Menü «Startseite». Der Meetingraum und die Objektverwaltung liegen auf + // Menü «Startseite». Die Belegschaft und die Objektverwaltung liegen auf // derselben Seite — zwei Einträge für eine Seite wären ein toter Link. { path: ROUTES.SUPPLY.PROPERTIES, label: HOME_NAV_LABEL, icon: Building2 }, { diff --git a/src/components/onboarding/WelcomeDialog.tsx b/src/components/onboarding/WelcomeDialog.tsx index 200de47..75a6f56 100644 --- a/src/components/onboarding/WelcomeDialog.tsx +++ b/src/components/onboarding/WelcomeDialog.tsx @@ -31,7 +31,7 @@ import { DS_ACCENT, DS_BRAND, DS_TEXT, LEAD_CHANNEL_META } from '../../lib/ds' * Sie erklärt die Anwendung, wie sie seit dieser Runde aufgebaut ist: der * entfernte Bereich «Übersicht» kommt nicht mehr vor, der Menüeintrag heisst * «Startseite», und «Meine Objekte» bezeichnet ausschliesslich den - * Objektbereich unterhalb des Meetingraums. + * Objektbereich unterhalb der Belegschaft. * * Die Tour zeigt Bilder und Text in einem Dialog und markiert keine Elemente * auf der Seite. Das ist Absicht: eine Tour, die auf DOM-Knoten zeigt, läuft @@ -73,12 +73,12 @@ function SlideStartseite() { } title={`Ihre ${HOME_NAV_LABEL}`} - lead="Der Einstieg ist der Besprechungsraum Ihrer digitalen Mitarbeitenden — darunter folgt Ihr Objektbestand." + lead="Der Einstieg zeigt Ihre digitalen Mitarbeitenden im Dienst — darunter folgt Ihr Objektbestand." > } color={DS_BRAND.main} - text="Ein Klick auf einen Agenten am Tisch öffnet direkt dessen Arbeitsbereich." + text="Jede Karte zeigt, woran der Mitarbeitende gerade sitzt. Ein Klick öffnet seinen Arbeitsbereich." /> } @@ -119,7 +119,7 @@ function SlideAgenten() { } color={DS_ACCENT.violet.main} - text="Giorgio am Kopfende des Tisches führt in die Personalverwaltung: Aufgaben, Kanäle & Systeme, Bearbeitungsverlauf." + text="Über «Meine Agenten» verwalten Sie Ihr Personal: Aufgaben, Kanäle & Systeme und den Bearbeitungsverlauf." /> ) diff --git a/src/components/meeting-room/AgentStaffCard.tsx b/src/components/staff/AgentStaffCard.tsx similarity index 96% rename from src/components/meeting-room/AgentStaffCard.tsx rename to src/components/staff/AgentStaffCard.tsx index 7172535..32e7dba 100644 --- a/src/components/meeting-room/AgentStaffCard.tsx +++ b/src/components/staff/AgentStaffCard.tsx @@ -58,7 +58,10 @@ export const AgentStaffCard = memo(function AgentStaffCard({ presence, onOpen }: gap: 1.25, p: { xs: 1.75, xl: 2 }, borderRadius: 3, - height: '100%', + // Keine eigene Höhe: im Flexlayout gibt die Zeile die Höhe vor, und alle + // Karten einer Zeile werden von selbst gleich hoch. Mit `height: 100%` + // beanspruchte jede Karte die volle Fläche und die zweite Zeile fiel + // aus dem sichtbaren Bereich. // Glasfläche statt Vollton: das Büro dahinter bleibt spürbar, ohne die // Schrift zu stören. bgcolor: 'rgba(255,255,255,0.06)', diff --git a/src/components/meeting-room/AgentMeetingRoom.tsx b/src/components/staff/AgentStaffOverview.tsx similarity index 81% rename from src/components/meeting-room/AgentMeetingRoom.tsx rename to src/components/staff/AgentStaffOverview.tsx index 676117a..63bd454 100644 --- a/src/components/meeting-room/AgentMeetingRoom.tsx +++ b/src/components/staff/AgentStaffOverview.tsx @@ -1,7 +1,7 @@ import { useCallback, useMemo } from 'react' import { useNavigate } from 'react-router' import { Box, Typography } from '@mui/material' -import { MeetingRoomScene } from './MeetingRoomScene' +import { StaffBackdrop } from './StaffBackdrop' import { AgentStaffCard } from './AgentStaffCard' import { useAgentPresence } from '../../hooks/useAgentPresence' import { WorkloadTone } from '../../domain/agentWorkload' @@ -24,12 +24,13 @@ const RESERVED_PX = TOP_BAR_PX + KEEP_VISIBLE_BELOW_PX const MIN_SCENE_PX = 420 /** - * Die digitale Belegschaft auf der Startseite (Runde 5, §3/§6). + * Die digitale Belegschaft auf der Startseite (Runde 5, §3). * - * Sechs Mitarbeitende an ihrem Arbeitsplatz — mit Porträt, Funktion, + * Die fünf Mitarbeitenden an ihrem Arbeitsplatz — mit Porträt, Funktion, * Abteilung, Personalnummer, Dienstzustand, einem Satz in der ersten Person * und der zuletzt protokollierten Handlung. Ein Klick auf eine Karte öffnet - * den jeweiligen Arbeitsbereich, bei Giorgio die Personalverwaltung. + * den jeweiligen Arbeitsbereich. Die Personalverwaltung selbst liegt im + * Menüpunkt «Meine Agenten» und braucht keine eigene Person am Bildschirm. * * Bewusst keine nachgebaute Raumperspektive mehr: eine gezeichnete Kulisse * bleibt neben fotografierten Porträts erkennbar gezeichnet, und ein Raum @@ -37,7 +38,7 @@ const MIN_SCENE_PX = 420 * echten Büro — dass jemand da ist, etwas zu sagen hat und nachweisbar * gearbeitet hat. */ -export function AgentMeetingRoom() { +export function AgentStaffOverview() { const navigate = useNavigate() const { data: staff } = useAgentPresence() @@ -69,7 +70,7 @@ export function AgentMeetingRoom() { overflow: 'hidden', }} > - + *': { + flex: { + xs: '1 1 100%', + sm: '0 1 calc((100% - 12px) / 2)', + lg: '0 1 calc((100% - 32px) / 3)', + }, + }, // Auf niedrigen Fenstern darf die Belegschaft scrollen, statt die // Karten bis zur Unlesbarkeit zu stauchen. overflowY: 'auto', @@ -114,8 +122,8 @@ export function AgentMeetingRoom() { * Anriss über der Belegschaft. * * Beantwortet die Frage, mit der jemand diese Seite öffnet: muss ich heute - * irgendwo eingreifen? Ohne den Satz müsste man sechs Karten lesen und selbst - * zusammenzählen. + * irgendwo eingreifen? Ohne den Satz müsste man jede Karte einzeln lesen und + * selbst zusammenzählen. */ function StaffHeadline({ urgent, onDuty, total }: { urgent: number; onDuty: number; total: number }) { const summary = diff --git a/src/components/meeting-room/MeetingRoomScene.tsx b/src/components/staff/StaffBackdrop.tsx similarity index 89% rename from src/components/meeting-room/MeetingRoomScene.tsx rename to src/components/staff/StaffBackdrop.tsx index c33593a..14e32e1 100644 --- a/src/components/meeting-room/MeetingRoomScene.tsx +++ b/src/components/staff/StaffBackdrop.tsx @@ -4,7 +4,7 @@ import { ROOM_BACKDROP } from './roomBackdrop' import { DS_NEUTRAL } from '../../lib/ds' /** - * Bildhintergrund des Agenten-Meetingraums (Runde 5, §3). + * Bildhintergrund der Belegschaftsansicht (Runde 5, §3). * * Bewusst eine einzige Bildfläche mit Verlauf — und nicht länger ein aus * Flächen konstruierter Raum. Ein selbstgezeichneter Raum bleibt neben @@ -14,7 +14,7 @@ import { DS_NEUTRAL } from '../../lib/ds' * Die Komponente selbst weiss nichts über das Motiv. Sie zeigt, was * `ROOM_BACKDROP` liefert — die Aussicht heute, eine Raumaufnahme später. */ -export const MeetingRoomScene = memo(function MeetingRoomScene() { +export const StaffBackdrop = memo(function StaffBackdrop() { return ( = { bruno, @@ -90,5 +86,4 @@ export const AGENT_PHOTOS: Record = { ida, jana, zeno, - giorgio, } diff --git a/src/domain/agentWorkload.ts b/src/domain/agentWorkload.ts index a4d62de..182aa13 100644 --- a/src/domain/agentWorkload.ts +++ b/src/domain/agentWorkload.ts @@ -1,7 +1,7 @@ /** * Property On — aktuelle Auslastung eines digitalen Mitarbeitenden. * - * Der Meetingraum auf der Startseite zeigt nicht nur, *wer* für einen + * Die Belegschaftsansicht der Startseite zeigt nicht nur, *wer* für einen * arbeitet, sondern *woran gerade*. Ohne diese Zahl wäre die Szene Dekoration; * mit ihr ist sie eine Lageübersicht — genau der Anspruch aus CLAUDE.md §1: * jeder Bildschirm ist um eine Entscheidung herum gebaut. diff --git a/src/hooks/useAgentWorkload.ts b/src/hooks/useAgentWorkload.ts index 6d5678b..13ccf32 100644 --- a/src/hooks/useAgentWorkload.ts +++ b/src/hooks/useAgentWorkload.ts @@ -4,12 +4,11 @@ import { useVisitAssignments } from './useVisitAssignments' import { useExposeLeads } from './useExposeLeads' import { useUnifiedLeads } from './useUnifiedLeads' import { useProperties } from './useProperties' -import { useAgentWorkItems } from './useAgentWorkItems' import { computeAgentWorkload } from '../services/agentWorkloadService' import type { AgentWorkload } from '../domain/agentWorkload' /** - * Aktuelle Auslastung aller sechs digitalen Mitarbeitenden (Runde 5). + * Aktuelle Auslastung der fünf digitalen Mitarbeitenden (Runde 5). * * Der Hook sammelt nur die Bestände ein; was daraus wird, entscheidet * `agentWorkloadService` — Schwellen, Zeitfenster und Formulierungen sind @@ -22,17 +21,16 @@ export function useAgentWorkload(): { data: Record; isLoa const { data: exposeLeads = [], isLoading: exposeLoading } = useExposeLeads() const { data: leads, isLoading: leadsLoading } = useUnifiedLeads() const { data: properties = [], isLoading: propertiesLoading } = useProperties() - const { data: workItems = [], isLoading: workItemsLoading } = useAgentWorkItems() const data = useMemo( - () => computeAgentWorkload({ reminders, visits, exposeLeads, leads, properties, workItems }), - [reminders, visits, exposeLeads, leads, properties, workItems], + () => computeAgentWorkload({ reminders, visits, exposeLeads, leads, properties }), + [reminders, visits, exposeLeads, leads, properties], ) return { data, isLoading: remindersLoading || visitsLoading || exposeLoading || - leadsLoading || propertiesLoading || workItemsLoading, + leadsLoading || propertiesLoading, } } diff --git a/src/lib/agentWorkspaces.ts b/src/lib/agentWorkspaces.ts index f97abf6..886529c 100644 --- a/src/lib/agentWorkspaces.ts +++ b/src/lib/agentWorkspaces.ts @@ -14,10 +14,6 @@ import { ROUTES } from './constants' export interface AgentWorkspace { - /** Abteilung laut Personalblatt. */ - department?: string - /** Personalnummer laut Personalblatt. */ - personnelNumber?: string /** Identisch mit der Agenten-ID in `mock-data/agents/` und `AGENT_PHOTOS`. */ id: string name: string @@ -37,22 +33,3 @@ export const AGENT_WORKSPACES: AgentWorkspace[] = [ export function agentWorkspaceById(id: string): AgentWorkspace | undefined { return AGENT_WORKSPACES.find(a => a.id === id) } - -/** - * Giorgio — Personalverwalter und Vorgesetzter der fünf Agenten (Runde 5, §6). - * - * Bewusst nicht Teil von `AGENT_WORKSPACES`: Giorgio hat keine eigene - * Arbeitsseite, sondern steht für die Adminfläche selbst. Sein Ziel ist - * «Meine Agenten» mit den Bereichen Personalverwaltung, Kanäle & Systeme und - * Bearbeitungsverlauf. Stünde er in derselben Liste, erschiene er als sechster - * Eintrag im Seitenmenü und als sechster Mitarbeitender in der Belegschaft — - * beides wäre falsch. - */ -export const AGENT_SUPERVISOR: AgentWorkspace = { - id: 'giorgio', - name: 'Giorgio', - role: 'Personalverwalter', - department: 'Personalwesen', - personnelNumber: 'PO-ZD-01', - path: ROUTES.SUPPLY.TEAM, -} diff --git a/src/pages/supply/Properties.tsx b/src/pages/supply/Properties.tsx index 0b5ef2e..4d34d7c 100644 --- a/src/pages/supply/Properties.tsx +++ b/src/pages/supply/Properties.tsx @@ -4,7 +4,7 @@ import { Box, Collapse, Drawer, Typography, useMediaQuery, useTheme } from '@mui import { ChevronDown, ChevronUp } from 'lucide-react' import { PageHeader } from '../../components/layout' import { ViewToggle } from '../../components/shared' -import { AgentMeetingRoom } from '../../components/meeting-room' +import { AgentStaffOverview } from '../../components/staff' import { useProperties } from '../../hooks/useProperties' import { useActiveInquiries } from '../../hooks/useInquiries' import { PropertyFilterBar, PropertyTable, PropertyDetailView, PropertyIntelligenceCard } from '../../components/supply' @@ -95,13 +95,13 @@ export default function Properties() { const filtered = useMemo(() => applyFilters(properties, filters), [properties, filters]) return ( - // Runde 5, §2.3: Die Startseite scrollt als Ganzes. Der Meetingraum steht + // Runde 5, §2.3: Die Startseite scrollt als Ganzes. Die Belegschaft steht // oben und misst sich an der Viewport-Höhe; Titel und Filterbalken bleiben // beim Einstieg gerade noch sichtbar, die Objektliste beginnt darunter. // Ein eigener Scrollbereich für die Tabelle wäre ein zweiter Scrollbalken // im selben Bild und würde genau das verhindern. - + [id, workload(id)]), + ['ferdi', 'bruno', 'livia', 'nora', 'sina'].map(id => [id, workload(id)]), ) function input(partial: Partial = {}): AgentPresenceInput { @@ -72,9 +72,9 @@ describe('Zeitangabe in Alltagssprache', () => { }) describe('Belegschaft der Startseite', () => { - it('führt alle sechs in verbindlicher Reihenfolge, Giorgio zuletzt', () => { + it('führt die fünf Agenten in verbindlicher Reihenfolge', () => { const staff = buildAgentPresence(input(), NOW) - expect(staff.map(p => p.agentId)).toEqual(['ferdi', 'bruno', 'livia', 'nora', 'sina', 'giorgio']) + expect(staff.map(p => p.agentId)).toEqual(['ferdi', 'bruno', 'livia', 'nora', 'sina']) }) it('übernimmt Personalien aus dem Personalblatt', () => { @@ -88,13 +88,6 @@ describe('Belegschaft der Startseite', () => { expect(ferdi.autonomy).toBe('Autonom') }) - it('gibt Giorgio Personalien und Stufe aus der Rollendefinition — er hat kein Dossier', () => { - const giorgio = buildAgentPresence(input(), NOW).find(p => p.agentId === 'giorgio')! - expect(giorgio.department).toBe('Personalwesen') - expect(giorgio.personnelNumber).toBe('PO-ZD-01') - expect(giorgio.autonomy).toBe('Vorgesetzter') - }) - it('zeigt einen pausierten Mitarbeitenden als nicht im Dienst', () => { const staff = buildAgentPresence( input({ agents: [dossier('sina', { status: AgentStatus.PAUSED })] }), diff --git a/src/services/__tests__/agentWorkloadService.test.ts b/src/services/__tests__/agentWorkloadService.test.ts index cddc67e..5950c4e 100644 --- a/src/services/__tests__/agentWorkloadService.test.ts +++ b/src/services/__tests__/agentWorkloadService.test.ts @@ -14,7 +14,6 @@ import type { AgentWorkloadInput } from '../agentWorkloadService' import { WorkloadTone } from '../../domain/agentWorkload' import { ReminderStatus } from '../../domain/reminder' import { ExposeLeadStatus } from '../../domain/exposeLead' -import { AgentWorkItemStatus } from '../../domain/agentWorkItem' const NOW = Date.parse('2026-08-06T12:00:00.000Z') const DAY = 24 * 60 * 60 * 1000 @@ -24,7 +23,7 @@ function iso(offsetDays: number): string { } const EMPTY: AgentWorkloadInput = { - reminders: [], visits: [], exposeLeads: [], leads: [], properties: [], workItems: [], + reminders: [], visits: [], exposeLeads: [], leads: [], properties: [], } function input(partial: Partial): AgentWorkloadInput { @@ -92,22 +91,6 @@ describe('Auslastung je Agent', () => { expect(w.label).toBe('2 Objekte mit Datenlücken') }) - it('zählt bei Giorgio die offenen Freigaben', () => { - const w = computeAgentWorkload( - input({ - workItems: [ - { status: AgentWorkItemStatus.PENDING }, - { status: AgentWorkItemStatus.PENDING }, - { status: AgentWorkItemStatus.APPROVED }, - ] as AgentWorkloadInput['workItems'], - }), - NOW, - ).giorgio - - expect(w.count).toBe(2) - expect(w.label).toBe('2 Freigaben offen') - }) - it('hält Noras Zahl nie für dringend — sie schlägt vor, sie entscheidet nicht', () => { const leads = Array.from({ length: 40 }, () => ({ receivedAt: iso(-1) })) const w = computeAgentWorkload( @@ -131,7 +114,7 @@ describe('Auslastung je Agent', () => { it('meldet bei leerer Datenlage überall Ruhe statt Lücken', () => { const all = computeAgentWorkload(EMPTY, NOW) - expect(Object.keys(all)).toEqual(['ferdi', 'bruno', 'livia', 'nora', 'sina', 'giorgio']) + expect(Object.keys(all)).toEqual(['ferdi', 'bruno', 'livia', 'nora', 'sina']) for (const w of Object.values(all)) { expect(w.count).toBe(0) expect(w.tone).toBe(WorkloadTone.CALM) diff --git a/src/services/agentPresenceService.ts b/src/services/agentPresenceService.ts index 9510ca0..b0dcf5f 100644 --- a/src/services/agentPresenceService.ts +++ b/src/services/agentPresenceService.ts @@ -3,7 +3,7 @@ import type { AgentWorkload } from '../domain/agentWorkload' import type { AgentProtocolEntry } from '../domain/agentProtocol' import type { TeamAgent } from '../domain/teamAgent' import { AgentStatus } from '../domain/teamAgent' -import { AGENT_SUPERVISOR, AGENT_WORKSPACES } from '../lib/agentWorkspaces' +import { AGENT_WORKSPACES } from '../lib/agentWorkspaces' import { AGENT_AUTONOMY_LABELS } from '../lib/constants' /** @@ -13,10 +13,6 @@ import { AGENT_AUTONOMY_LABELS } from '../lib/constants' * als «letzte Handlung» gilt und wie eine Zeitangabe in Alltagssprache lautet, * sind fachliche Festlegungen. Der Stichzeitpunkt ist ein Parameter — dieselbe * Datenlage ergibt damit immer dasselbe Ergebnis und lässt sich prüfen. - * - * Giorgio steht bewusst am Ende: er ist Vorgesetzter, kein sechster - * Sachbearbeiter, und sein Klick führt nicht auf eine Arbeitsseite, sondern in - * die Personalverwaltung. */ const MINUTE = 60 * 1000 @@ -73,7 +69,7 @@ export function buildAgentPresence( } const byId = new Map(agents.map(a => [a.id, a])) - const roster = [...AGENT_WORKSPACES, AGENT_SUPERVISOR] + const roster = AGENT_WORKSPACES return roster.map(member => { const dossier = byId.get(member.id) @@ -83,17 +79,13 @@ export function buildAgentPresence( agentId: member.id, name: member.name, role: member.role, - // Personalblatt zuerst; für Giorgio, der keines hat, die Angaben aus - // der zentralen Rollendefinition. - department: dossier?.department ?? member.department ?? '', - personnelNumber: dossier?.personnelNumber ?? member.personnelNumber ?? '', + department: dossier?.department ?? '', + personnelNumber: dossier?.personnelNumber ?? '', path: member.path, // Ohne Personalblatt gilt jemand als im Dienst: die Belegschaftsansicht // soll niemanden fälschlich als pausiert zeigen, nur weil ein Dossier fehlt. onDuty: dossier ? dossier.status === AgentStatus.ACTIVE : true, - // Giorgio führt die Belegschaft und entscheidet nichts allein über - // Objekte — seine Stufe ergibt sich aus der Rolle, nicht aus einem Dossier. - autonomy: dossier ? (AGENT_AUTONOMY_LABELS[dossier.autonomyLevel] ?? '') : 'Vorgesetzter', + autonomy: dossier ? (AGENT_AUTONOMY_LABELS[dossier.autonomyLevel] ?? '') : '', workload: workload[member.id], lastAction: action ? { title: action.title, when: formatRelativeTime(action.timestamp, now) } diff --git a/src/services/agentWorkloadService.ts b/src/services/agentWorkloadService.ts index ace5bd7..21b88de 100644 --- a/src/services/agentWorkloadService.ts +++ b/src/services/agentWorkloadService.ts @@ -2,18 +2,15 @@ import { ReminderStatus } from '../domain/reminder' import type { Reminder } from '../domain/reminder' import { ExposeLeadStatus } from '../domain/exposeLead' import type { ExposeLead } from '../domain/exposeLead' -import { AgentWorkItemStatus } from '../domain/agentWorkItem' -import type { AgentWorkItem } from '../domain/agentWorkItem' import type { VisitAssignment } from '../domain/visitAssignment' import type { UnifiedLead } from '../domain/unifiedLead' import type { Property } from '../domain/property' import { WorkloadTone } from '../domain/agentWorkload' import type { AgentWorkload } from '../domain/agentWorkload' -import { AGENT_SUPERVISOR } from '../lib/agentWorkspaces' import { DQ_MEDIUM } from '../lib/constants' /** - * Auslastung der sechs digitalen Mitarbeitenden (Runde 5). + * Auslastung der fünf digitalen Mitarbeitenden (Runde 5). * * Reine Geschäftslogik, bewusst ausserhalb von React: was «diese Woche» * bedeutet, ab wann eine Zahl dringend ist und welcher Bestand zu welchem @@ -38,7 +35,6 @@ const URGENT_FROM = { visits: 4, expose: 5, dataGaps: 10, - approvals: 5, } as const /** Zeitfenster der Auswertung. */ @@ -55,7 +51,6 @@ export interface AgentWorkloadInput { exposeLeads: ExposeLead[] leads: UnifiedLead[] properties: Property[] - workItems: AgentWorkItem[] } function within(iso: string, from: number, to: number): boolean { @@ -156,19 +151,7 @@ export function computeAgentWorkload( : `Bei ${gaps} ${plural(gaps, 'Objekt fehlen', 'Objekten fehlen')} mir Angaben. Ich melde, was ich nicht selbst finde.`, } - // ── Giorgio: Freigaben in der Personalverwaltung ──────────────────────────── - const pending = input.workItems.filter(w => w.status === AgentWorkItemStatus.PENDING).length - const giorgio: AgentWorkload = { - agentId: AGENT_SUPERVISOR.id, - count: pending, - label: `${pending} ${plural(pending, 'Freigabe', 'Freigaben')} offen`, - tone: tone(pending, URGENT_FROM.approvals), - voice: pending === 0 - ? 'Nichts liegt zur Freigabe bereit. Die Belegschaft läuft.' - : `${pending} ${plural(pending, 'Vorgang wartet', 'Vorgänge warten')} auf deine Freigabe.`, - } - return Object.fromEntries( - [ferdi, bruno, livia, nora, sina, giorgio].map(w => [w.agentId, w]), + [ferdi, bruno, livia, nora, sina].map(w => [w.agentId, w]), ) }