feat(agenten): Runde 4 — «Meine Agenten» mit fünf digitalen Mitarbeitenden
Setzt das Umsetzungsbriefing Runde 4 im bestehenden Frontend um. Informationsarchitektur - «Teamübersicht» heisst «Meine Agenten»; ihr bisheriger Inhalt (Kreisgrafik, Auswertung, Verbindungszusammenfassung) ist entfallen. - Personalverwaltung, Bearbeitungsverlauf und Kanäle & Systeme sind jetzt Reiter derselben Seite (?section=), gestaltet wie die Auswahl im Bearbeitungsverlauf. Es wird nur der gewählte Bereich gerendert. - Die alten Unterseitenpfade leiten um, damit Lesezeichen nicht brechen. Agentenbestand - Reto und Lea vollständig entfernt — aus Navigation, Dossiers, Protokoll, Verbindungen, Vorgängen und Porträtbestand. - Retos Aufgaben liegen bei Bruno: Nachbereitung, WhatsApp-Anruf, Protokoll und Kundennotiz, Ablage im CRM. - Livia ist «Exposé Master»: Lageberichte, Inserate, Angebotsbroschüren. - Sidebar führt Ferdi, Bruno, Livia, Nora, Sina mit Porträt und Funktion. Agentenseiten - Ein gemeinsamer AgentWorkspaceHero auf allen fünf Seiten. - Ferdi: Auswertungskarten, Priorität und Typfarben entfallen; Fälligkeit nur bei fünf Tagen oder weniger rot; Objektlinks nach «Meine Objekte»; neu die Terminplanung im verbundenen Kalender mit typgerechtem PDF-Ausschnitt. - Sina: reduzierte, filter- und sortierbare Objektübersicht; Detailansicht direkt editierbar, leere Pflichtfelder rot umrandet. - Nora: Signale ohne Prozentsätze und Konfidenzstufen, nur belegbare Angaben; Mehrfachauswahl leitet Objekte an Livia weiter. - Livia: aktive und archivierte Leads, Arbeitsbereich gleitet an den oberen Rand; dreistufiger Exposé-Prozess Hochladen → Exposé → Export. - Bruno: neue Seite mit Auftragsliste und Vor-/Nachbereitungs-Drawer; Glocke warnt bei Besichtigung unter 24 Stunden ohne Bericht. Datenschicht - Neu: Kalender, Exposé-Leads, Exposé-Entwürfe, Besichtigungsaufträge — je Domain, Provider, Service und Hook. - IAIService um generateExposeText erweitert; der Entwurf nutzt ausschliesslich erfasste Objektdaten und meldet Lücken, statt sie zu füllen. - Alle Objektverweise zeigen auf reale Einträge aus «Meine Objekte»; neue Detailroute /supply/properties/:propertyId. Offen: Chat, Kalender, CRM und DMS sind Frontend-Simulation ohne Anbindung. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
cc5582b7fd
commit
d200d4e930
352 files changed
+8069
-8512
No files matched your search
@@ -1,69 +1,85 @@
|
||||
/**
|
||||
* Property On — Position und Struktur des Navigationseintrags (§3.3, §3.4).
|
||||
* Property On — Position und Struktur des Navigationseintrags «Meine Agenten».
|
||||
*
|
||||
* Die Spezifikation ist hier wörtlich: «Teamübersicht» steht direkt unter
|
||||
* «Meine Objekte» und oberhalb von «Reminder Manager». Eine spätere Umsortierung
|
||||
* der Navigation würde das still brechen — dieser Test hält es fest.
|
||||
* Seit Runde 4 ist «Meine Agenten» der einzige Eintrag mit Unterpunkten, und
|
||||
* diese Unterpunkte sind die fünf digitalen Mitarbeitenden in verbindlicher
|
||||
* Reihenfolge. Eine spätere Umsortierung würde das still brechen — dieser Test
|
||||
* hält es fest.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { WORKSPACE_CONFIG, getPageNameFromPath } from '../appShellConfig'
|
||||
import { WorkspaceType } from '../../../domain/enums'
|
||||
import { ROUTES } from '../../../lib/constants'
|
||||
import { MY_AGENTS_LABEL, ROUTES } from '../../../lib/constants'
|
||||
import { AGENT_WORKSPACES } from '../../../lib/agentWorkspaces'
|
||||
|
||||
const supplyNav = WORKSPACE_CONFIG[WorkspaceType.SUPPLY].navItems
|
||||
|
||||
describe('Navigationseintrag «Teamübersicht»', () => {
|
||||
it('steht direkt zwischen «Meine Objekte» und «Reminder Manager»', () => {
|
||||
describe('Navigationseintrag «Meine Agenten»', () => {
|
||||
it('steht direkt unter «Meine Objekte»', () => {
|
||||
const labels = supplyNav.map(item => item.label)
|
||||
const objekte = labels.indexOf('Meine Objekte')
|
||||
const team = labels.indexOf('Teamübersicht')
|
||||
const reminder = labels.indexOf('Reminder Manager')
|
||||
const agenten = labels.indexOf(MY_AGENTS_LABEL)
|
||||
|
||||
expect(objekte).toBeGreaterThanOrEqual(0)
|
||||
expect(team).toBe(objekte + 1)
|
||||
expect(reminder).toBe(team + 1)
|
||||
expect(agenten).toBe(objekte + 1)
|
||||
})
|
||||
|
||||
it('führt auf den Basispfad des Funktionsbereichs', () => {
|
||||
const team = supplyNav.find(item => item.label === 'Teamübersicht')
|
||||
expect(team?.path).toBe(ROUTES.SUPPLY.TEAM)
|
||||
const agenten = supplyNav.find(item => item.label === MY_AGENTS_LABEL)
|
||||
expect(agenten?.path).toBe(ROUTES.SUPPLY.TEAM)
|
||||
})
|
||||
|
||||
it('trägt die drei Subreiter in verbindlicher Reihenfolge', () => {
|
||||
const team = supplyNav.find(item => item.label === 'Teamübersicht')
|
||||
expect(team?.children?.map(c => c.label)).toEqual([
|
||||
'Personalverwaltung',
|
||||
'Bearbeitungsverlauf',
|
||||
'Kanäle & Systeme',
|
||||
])
|
||||
it('trägt die fünf Agenten in verbindlicher Reihenfolge', () => {
|
||||
const agenten = supplyNav.find(item => item.label === MY_AGENTS_LABEL)
|
||||
expect(agenten?.children?.map(c => c.label)).toEqual(['Ferdi', 'Bruno', 'Livia', 'Nora', 'Sina'])
|
||||
})
|
||||
|
||||
it('verweist mit jedem Subreiter auf einen Unterpfad der Teamübersicht', () => {
|
||||
const team = supplyNav.find(item => item.label === 'Teamübersicht')
|
||||
for (const child of team?.children ?? []) {
|
||||
expect(child.path.startsWith(`${ROUTES.SUPPLY.TEAM}/`)).toBe(true)
|
||||
it('gibt jedem Agenten Porträt-ID und Funktionsbezeichnung mit', () => {
|
||||
const agenten = supplyNav.find(item => item.label === MY_AGENTS_LABEL)
|
||||
for (const child of agenten?.children ?? []) {
|
||||
expect(child.agentId).toBeTruthy()
|
||||
expect(child.role).toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('verweist mit jedem Agenten auf eine eigene Seite', () => {
|
||||
const agenten = supplyNav.find(item => item.label === MY_AGENTS_LABEL)
|
||||
const paths = agenten?.children?.map(c => c.path) ?? []
|
||||
expect(paths).toEqual(AGENT_WORKSPACES.map(a => a.path))
|
||||
expect(new Set(paths).size).toBe(paths.length)
|
||||
})
|
||||
|
||||
it('ist der einzige Hauptreiter mit Subreitern — sonst wäre die Sidebar uneinheitlich', () => {
|
||||
const withChildren = supplyNav.filter(item => (item.children?.length ?? 0) > 0)
|
||||
expect(withChildren.map(item => item.label)).toEqual(['Teamübersicht'])
|
||||
expect(withChildren.map(item => item.label)).toEqual([MY_AGENTS_LABEL])
|
||||
})
|
||||
|
||||
it('führt Reto und Lea nicht mehr', () => {
|
||||
const labels = supplyNav.flatMap(item => [item.label, ...(item.children ?? []).map(c => c.label)])
|
||||
expect(labels).not.toContain('Reto')
|
||||
expect(labels).not.toContain('Lea')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Seitentitel aus dem Pfad', () => {
|
||||
it('löst Haupt- und Subreiter über die Navigation auf', () => {
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM)).toBe('Teamübersicht')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_PERSONNEL)).toBe('Personalverwaltung')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_HISTORY)).toBe('Bearbeitungsverlauf')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_CONNECTIONS)).toBe('Kanäle & Systeme')
|
||||
it('löst Haupt- und Agentenreiter über die Navigation auf', () => {
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM)).toBe(MY_AGENTS_LABEL)
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.AGENT_FERDI)).toBe('Ferdi')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.AGENT_BRUNO)).toBe('Bruno')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.AGENT_LIVIA)).toBe('Livia')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.AGENT_NORA)).toBe('Nora')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.AGENT_SINA)).toBe('Sina')
|
||||
})
|
||||
|
||||
it('fängt Deep-Links ab, statt in den Segment-Fallback zu fallen', () => {
|
||||
// Ohne eigene Regel ergäbe der Fallback «Erledigte auftraege» bzw. «Ferdi».
|
||||
expect(getPageNameFromPath(`${ROUTES.SUPPLY.TEAM_PERSONNEL}/ferdi/aufgaben`)).toBe('Personalblatt')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_HISTORY_DONE)).toBe('Bearbeitungsverlauf')
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_HISTORY_PENDING)).toBe('Bearbeitungsverlauf')
|
||||
it('trägt die alten Unterseitenpfade bis zur Umleitung unter «Meine Agenten»', () => {
|
||||
// Ohne eigene Regel ergäbe der Fallback «Personalverwaltung» bzw. «Erledigte auftraege».
|
||||
expect(getPageNameFromPath(`${ROUTES.SUPPLY.TEAM_PERSONNEL}/ferdi/aufgaben`)).toBe(MY_AGENTS_LABEL)
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_HISTORY_DONE)).toBe(MY_AGENTS_LABEL)
|
||||
expect(getPageNameFromPath(ROUTES.SUPPLY.TEAM_CONNECTIONS)).toBe(MY_AGENTS_LABEL)
|
||||
})
|
||||
|
||||
it('erkennt die Objekt-Detailroute', () => {
|
||||
expect(getPageNameFromPath(`${ROUTES.SUPPLY.PROPERTIES}/prop-001`)).toBe('Objekt Detail')
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user