Files
property-match/src/components/layout/appShellConfig.ts
T
Benjamin SutterandClaude Opus 5 d200d4e930 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>
2026-08-05 14:43:16 +02:00

157 lines
5.2 KiB
TypeScript

import { WorkspaceType } from '../../domain/enums'
import { MY_AGENTS_LABEL, ROUTES } from '../../lib/constants'
import { AGENT_WORKSPACES } from '../../lib/agentWorkspaces'
import type { LucideIcon } from 'lucide-react'
import {
LayoutDashboard,
Building2,
CheckSquare,
Search,
ClipboardList,
Settings,
Users,
} from 'lucide-react'
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
/**
* Eingerückter Subreiter unterhalb eines Hauptreiters.
*
* Ohne `agentId` bleibt der Eintrag eine reine Textzeile — Einrückung und
* schmalerer Schriftgrad machen die Hierarchie deutlich. Mit `agentId` zeigt
* die Sidebar stattdessen Porträt, Name und Funktion: die fünf digitalen
* Mitarbeitenden sind Personen, keine Menüpunkte (Runde 4, §2.2). Bewusst kein
* Autonomiegrad und kein technisches Kurzlabel.
*/
export interface NavSubItem {
path: string
label: string
/** Agenten-ID für Porträt und Funktionszeile. */
agentId?: string
/** Funktionsbezeichnung unter dem Namen. Nur zusammen mit `agentId`. */
role?: string
}
export interface NavItem {
path: string
label: string
icon: LucideIcon
/**
* Subreiter, die sichtbar bleiben, solange sich der Nutzer innerhalb des
* Funktionsbereichs befindet (Property On, §3.3).
*/
children?: NavSubItem[]
}
export interface WorkspaceConfig {
label: string
abbreviation: string
icon: LucideIcon
firstPath: string
navItems: NavItem[]
chipColor: string
}
// ---------------------------------------------------------------------------
// Workspace configuration
// ---------------------------------------------------------------------------
export const WORKSPACE_CONFIG: Record<WorkspaceType, WorkspaceConfig> = {
[WorkspaceType.SUPPLY]: {
label: 'Verwaltung',
abbreviation: 'VW',
icon: Building2,
firstPath: '/supply/dashboard',
chipColor: '#152642',
navItems: [
{ path: '/supply/dashboard', label: 'Übersicht', icon: LayoutDashboard },
{ path: '/supply/properties', label: 'Meine Objekte', icon: Building2 },
{
path: ROUTES.SUPPLY.TEAM,
label: MY_AGENTS_LABEL,
icon: Users,
// Die drei Verwaltungsbereiche sind Reiter der Hauptseite geworden;
// hier stehen ab Runde 4 die fünf digitalen Mitarbeitenden selbst.
children: AGENT_WORKSPACES.map(a => ({
path: a.path,
label: a.name,
agentId: a.id,
role: a.role,
})),
},
],
},
/**
* Die Flächensuche ist in diesem Branch entfernt. Der Eintrag bleibt nur
* bestehen, weil `WorkspaceType` als Domänen-Enum unangetastet bleibt und
* dieser Record vollständig sein muss — sichtbar ist er nirgends, denn
* `WORKSPACE_ORDER` führt ihn nicht mehr und es existiert keine Route.
*/
[WorkspaceType.DEMAND]: {
label: 'Suche',
abbreviation: 'SU',
icon: Search,
firstPath: '/supply/dashboard',
chipColor: '#1a7a4a',
navItems: [],
},
[WorkspaceType.OPERATIONS]: {
label: 'Operations',
abbreviation: 'OP',
icon: Settings,
firstPath: '/ops/review-queue',
chipColor: '#6b21a8',
navItems: [
{ path: '/ops/review-queue', label: 'Review Queue', icon: CheckSquare },
{ path: '/ops/audit', label: 'Audit Log', icon: ClipboardList },
],
},
}
// Ordered list for rendering workspace tabs
export const WORKSPACE_ORDER: WorkspaceType[] = [
WorkspaceType.SUPPLY,
]
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
export function getWorkspaceFromPath(pathname: string): WorkspaceType | null {
if (pathname.startsWith('/supply')) return WorkspaceType.SUPPLY
return null
}
export function getPageNameFromPath(pathname: string): string {
for (const ws of Object.values(WORKSPACE_CONFIG)) {
for (const item of ws.navItems) {
if (item.path === pathname) return item.label
for (const child of item.children ?? []) {
if (child.path === pathname) return child.label
}
}
}
// Property On: Deep-Links auf Personalblatt und Verlaufsreiter. Ohne diese Fälle
// fiele der Titel in den Segment-Fallback und ergäbe «Personalverwaltung» statt
// des Mitarbeiternamens bzw. «Erledigte auftraege». Die Basispfade der drei
// Verwaltungsbereiche leiten auf `/supply/team?section=…` um, führen aber bis
// zum Abschluss der Umleitung noch kurz durch den Seitentitel.
if (/^\/supply\/team\/personalverwaltung(\/.+)?$/.test(pathname)) return MY_AGENTS_LABEL
if (/^\/supply\/team\/bearbeitungsverlauf(\/.+)?$/.test(pathname)) return MY_AGENTS_LABEL
if (/^\/supply\/team\/kanaele-systeme$/.test(pathname)) return MY_AGENTS_LABEL
if (/^\/supply\/properties\/.+/.test(pathname)) return 'Objekt Detail'
const segment = pathname.split('/').filter(Boolean).pop() ?? ''
return segment.charAt(0).toUpperCase() + segment.slice(1).replace(/-/g, ' ')
}
export function getUserInitials(name: string): string {
return name
.split(' ')
.map((n) => n[0])
.join('')
.toUpperCase()
.slice(0, 2)
}