Aufgaben, Systeme und Kanäle werden alle drei als Liste geführt und ab jetzt auch gleich bearbeitet: anlegen, ändern, entfernen, zu- und abschalten — dort, wo man sie sieht. Die Maske und die drei Listengriffe stehen einmal zentral statt dreimal je Reiter; sonst unterscheiden sie sich nach der ersten Korrektur. - AgentEntityDialog: eine Maske für alle drei Entitäten, über den React- Schlüssel je Eintrag neu aufgebaut statt über einen Effekt zurückgesetzt - agentEntityEditing: upsertById, removeById, newEntityId als reine Funktionen - Systeme sind über Provider, Service und Hook schreibbar (saveSystems) und tragen neu einen Aktiv-Schalter - Systemreiter: eine einzige Liste statt «Lesende»/«Schreibende Systemzugänge», kein Einleitungsbalken mehr; Aktiv, Zugriff und Verbindung stehen je Eintrag - Thomas steht als «Agentenverwalter» in der Navigation, zählt aber nicht als sechster Agent in Sitzungszimmer, Auslastung und Begrüssung - Chatverlauf scrollt in sich, ohne die Seite springen zu lassen Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
158 lines
5.3 KiB
TypeScript
158 lines
5.3 KiB
TypeScript
import { WorkspaceType } from '../../domain/enums'
|
|
import { HOME_NAV_LABEL, MY_AGENTS_LABEL, ROUTES } from '../../lib/constants'
|
|
import { AGENT_NAV_ENTRIES } from '../../lib/agentWorkspaces'
|
|
import type { LucideIcon } from 'lucide-react'
|
|
import {
|
|
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: ROUTES.SUPPLY.PROPERTIES,
|
|
chipColor: '#152642',
|
|
navItems: [
|
|
// Runde 5, §2.1/§2.2: «Übersicht» ist entfernt, «Meine Objekte» heisst im
|
|
// 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 },
|
|
{
|
|
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_NAV_ENTRIES.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: ROUTES.SUPPLY.PROPERTIES,
|
|
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)
|
|
}
|