feat(team): Datenschicht für Property On
Property On wird als Funktionsbereich "Teamübersicht" in Property Match eingebettet — sieben digitale Mitarbeiter, die ausführen, während der Mensch freigibt, anpasst oder zurückweist. Durchgehend Provider → Service → Hook, kein übersprungener Layer: - domain/: teamAgent, agentWorkItem, agentProtocol, agentConnection, agentFilters - mock-data/: 7 Personalblätter aus dem Agenten-Katalog (Aufgabenzahlen exakt: Ferdi 9, Sina 7, Lea 11, Bruno 9, Reto 10, Livia 11, Nora 8), 14 Vorgänge, 38 Protokolleinträge, 9 Verbindungen - provider/: vier Mockup-Provider mit versionierter localStorage-Persistenz - services/: Fachlogik inkl. Freigabe, Zurückweisung, Kennzahlen, Demo-Reset - hooks/: React Query, Mutationen entwerten Vorgänge, Kennzahlen und Protokoll gemeinsam — sonst zeigt die Oberfläche widersprüchliche Zahlen Namensgebung: Der Entitätstyp heisst TeamAgent, nicht Agent. Im Repo existiert bereits PowerOn als KI-Backend-Proxy, und Claude Code legt Arbeitskopien unter agent-* ab; ein nackter Typ Agent wäre in Suchen nicht auffindbar. Alle Satellitentypen sind Agent*-präfixiert, weil domain/index.ts per export * bündelt und generische Namen dort kollidieren. Eigener Protokolltyp statt ActivityEvent: den Namen gibt es im Repo dreifach und gegenseitig inkompatibel (domain/activityEvent.ts, governanceService.ts, PropertyActivityLogPanel.tsx). Ein Anschluss hätte den Konflikt zementiert. Demo-Uhr in lib/teamClock.ts: die Mockdaten sind auf den 20.05.2026 verankert. Gegen die Systemzeit gerechnet wären die Filter "heute / diese Woche / dieser Monat" an jedem anderen Kalendertag leer und die Auswertung sähe kaputt aus, obwohl sie korrekt rechnet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
36ce548169
commit
52dc7de25e
38 files changed
+9055
No files matched your search
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* Property On — «Teamübersicht»: digitale Mitarbeiter.
|
||||
*
|
||||
* Namensgebung: Der Entitätstyp heisst `TeamAgent`, nicht `Agent`. Im Repo existiert
|
||||
* bereits `PowerOn` (AI-Backend-Proxy) und Claude-Code legt Worktrees unter `agent-*`
|
||||
* ab — ein nackter Typ `Agent` wäre in Suchen praktisch nicht auffindbar. Alle
|
||||
* Satellitentypen tragen deshalb das Präfix `Agent*`; generische Namen wie `Status`,
|
||||
* `Priority` oder `Channel` sind tabu, weil `src/domain/index.ts` per `export *`
|
||||
* bündelt und dort bereits `ReviewStatus`, `SourceType`, `RiskLevel` u. a. liegen.
|
||||
*
|
||||
* Fachliche Wahrheit ist der Agenten-Katalog: Rollen, Personalnummern und
|
||||
* Zuständigkeiten der sieben Kernagenten werden nicht verändert.
|
||||
*/
|
||||
|
||||
// ── Status & Autonomie ────────────────────────────────────────────────────────
|
||||
|
||||
export const AgentStatus = {
|
||||
ACTIVE: 'ACTIVE',
|
||||
BUILDING: 'BUILDING',
|
||||
PAUSED: 'PAUSED',
|
||||
} as const
|
||||
export type AgentStatus = typeof AgentStatus[keyof typeof AgentStatus]
|
||||
|
||||
export const AgentAutonomyLevel = {
|
||||
AUTONOMOUS: 'AUTONOMOUS',
|
||||
APPROVAL_REQUIRED: 'APPROVAL_REQUIRED',
|
||||
PROPOSAL_ONLY: 'PROPOSAL_ONLY',
|
||||
} as const
|
||||
export type AgentAutonomyLevel = typeof AgentAutonomyLevel[keyof typeof AgentAutonomyLevel]
|
||||
|
||||
// ── Kanäle ────────────────────────────────────────────────────────────────────
|
||||
|
||||
export const AgentChannelType = {
|
||||
EMAIL: 'EMAIL',
|
||||
TEAMS: 'TEAMS',
|
||||
WHATSAPP: 'WHATSAPP',
|
||||
WORKSPACE_CHAT: 'WORKSPACE_CHAT',
|
||||
CALENDAR: 'CALENDAR',
|
||||
WEB_FORM: 'WEB_FORM',
|
||||
PORTAL_LEAD_MAIL: 'PORTAL_LEAD_MAIL',
|
||||
PHONE_VOICE: 'PHONE_VOICE',
|
||||
PHONE_NOTE: 'PHONE_NOTE',
|
||||
AUDIO_OUTPUT: 'AUDIO_OUTPUT',
|
||||
STRUCTURED_MAIL_FALLBACK: 'STRUCTURED_MAIL_FALLBACK',
|
||||
} as const
|
||||
export type AgentChannelType = typeof AgentChannelType[keyof typeof AgentChannelType]
|
||||
|
||||
export const AgentChannelDirection = {
|
||||
INBOUND: 'INBOUND',
|
||||
OUTBOUND: 'OUTBOUND',
|
||||
BOTH: 'BOTH',
|
||||
} as const
|
||||
export type AgentChannelDirection = typeof AgentChannelDirection[keyof typeof AgentChannelDirection]
|
||||
|
||||
export const AgentConnectionStatus = {
|
||||
CONNECTED: 'CONNECTED',
|
||||
DISCONNECTED: 'DISCONNECTED',
|
||||
ROADMAP: 'ROADMAP',
|
||||
} as const
|
||||
export type AgentConnectionStatus = typeof AgentConnectionStatus[keyof typeof AgentConnectionStatus]
|
||||
|
||||
/** Simulierte Kanalkonfiguration — es werden nie echte Zugangsdaten erfasst. */
|
||||
export interface AgentChannelConfig {
|
||||
displayName: string
|
||||
senderAddress?: string
|
||||
inboxAddress?: string
|
||||
defaultRecipients: string[]
|
||||
autoReplyEnabled: boolean
|
||||
}
|
||||
|
||||
export interface AgentChannel {
|
||||
id: string
|
||||
type: AgentChannelType
|
||||
direction: AgentChannelDirection
|
||||
description: string
|
||||
status: AgentConnectionStatus
|
||||
enabled: boolean
|
||||
/** Kennzeichnet im Katalog als «optional» geführte Kanäle. */
|
||||
optional?: boolean
|
||||
config: AgentChannelConfig
|
||||
}
|
||||
|
||||
// ── Systeme ───────────────────────────────────────────────────────────────────
|
||||
|
||||
export const AgentSystemType = {
|
||||
M365_EXCHANGE: 'M365_EXCHANGE',
|
||||
OUTLOOK: 'OUTLOOK',
|
||||
SHAREPOINT: 'SHAREPOINT',
|
||||
ONEDRIVE: 'ONEDRIVE',
|
||||
FILESHARE: 'FILESHARE',
|
||||
DMS: 'DMS',
|
||||
CALENDAR: 'CALENDAR',
|
||||
IMMOTOP2: 'IMMOTOP2',
|
||||
ABACUS_ABAIMMO: 'ABACUS_ABAIMMO',
|
||||
RIMO_R5: 'RIMO_R5',
|
||||
GARAIO_REM: 'GARAIO_REM',
|
||||
CRM: 'CRM',
|
||||
ZEFIX: 'ZEFIX',
|
||||
SHAB: 'SHAB',
|
||||
PUBLIC_WEB: 'PUBLIC_WEB',
|
||||
PORTAL_LEAD_INBOX: 'PORTAL_LEAD_INBOX',
|
||||
WORKSPACE: 'WORKSPACE',
|
||||
} as const
|
||||
export type AgentSystemType = typeof AgentSystemType[keyof typeof AgentSystemType]
|
||||
|
||||
export const AgentAccessLevel = {
|
||||
READ: 'READ',
|
||||
WRITE: 'WRITE',
|
||||
READ_WRITE: 'READ_WRITE',
|
||||
} as const
|
||||
export type AgentAccessLevel = typeof AgentAccessLevel[keyof typeof AgentAccessLevel]
|
||||
|
||||
export interface AgentSystem {
|
||||
id: string
|
||||
type: AgentSystemType
|
||||
access: AgentAccessLevel
|
||||
status: AgentConnectionStatus
|
||||
/** Wofür der Agent dieses System nutzt — in Alltagssprache, ohne KI-Jargon. */
|
||||
usage: string
|
||||
/** Klartext zur Berechtigung, z. B. «Schreibt erst nach Freigabe». */
|
||||
permissionNote: string
|
||||
}
|
||||
|
||||
// ── Aufgaben ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export interface AgentTask {
|
||||
id: string
|
||||
title: string
|
||||
description: string
|
||||
enabled: boolean
|
||||
/** Zeitplan oder auslösendes Ereignis, z. B. «täglich 06:00» oder «bei Eingang». */
|
||||
schedule: string
|
||||
requiresApproval: boolean
|
||||
dependsOnChannel?: AgentChannelType
|
||||
dependsOnSystem?: AgentSystemType
|
||||
}
|
||||
|
||||
// ── Einstellungen ─────────────────────────────────────────────────────────────
|
||||
|
||||
export const AgentSettingKind = {
|
||||
TEXT: 'TEXT',
|
||||
NUMBER: 'NUMBER',
|
||||
SELECT: 'SELECT',
|
||||
MULTI_SELECT: 'MULTI_SELECT',
|
||||
BOOLEAN: 'BOOLEAN',
|
||||
TIME: 'TIME',
|
||||
} as const
|
||||
export type AgentSettingKind = typeof AgentSettingKind[keyof typeof AgentSettingKind]
|
||||
|
||||
export const AgentSettingGroup = {
|
||||
GENERAL: 'GENERAL',
|
||||
APPROVAL: 'APPROVAL',
|
||||
DELIVERY: 'DELIVERY',
|
||||
SPECIFIC: 'SPECIFIC',
|
||||
} as const
|
||||
export type AgentSettingGroup = typeof AgentSettingGroup[keyof typeof AgentSettingGroup]
|
||||
|
||||
export interface AgentSettingOption {
|
||||
value: string
|
||||
label: string
|
||||
}
|
||||
|
||||
export interface AgentSetting {
|
||||
id: string
|
||||
label: string
|
||||
description?: string
|
||||
kind: AgentSettingKind
|
||||
group: AgentSettingGroup
|
||||
value: string | number | boolean | string[]
|
||||
options?: AgentSettingOption[]
|
||||
unit?: string
|
||||
min?: number
|
||||
max?: number
|
||||
/** Fachlich nicht abschaltbar — z. B. Sinas Quellenzwang. */
|
||||
locked?: boolean
|
||||
lockedReason?: string
|
||||
}
|
||||
|
||||
// ── Kennzahlen & Profil ───────────────────────────────────────────────────────
|
||||
|
||||
export interface AgentMetric {
|
||||
id: string
|
||||
label: string
|
||||
value: string
|
||||
hint?: string
|
||||
}
|
||||
|
||||
/** Informationsbox unter dem Kopfbereich des Personalblatts (§7.6). */
|
||||
export interface AgentProfile {
|
||||
purpose: string
|
||||
input: string[]
|
||||
coreFlow: string[]
|
||||
output: string[]
|
||||
}
|
||||
|
||||
// ── Entität ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Farbton des Avatars. Bewusst ein Token-Schlüssel statt eines Hex-Werts —
|
||||
* `npm run check:tokens` liegt über dem Schwellwert und darf nicht wachsen.
|
||||
*/
|
||||
export const AgentAvatarTone = {
|
||||
BRAND: 'BRAND',
|
||||
SUCCESS: 'SUCCESS',
|
||||
INFO: 'INFO',
|
||||
SIGNAL: 'SIGNAL',
|
||||
WARNING: 'WARNING',
|
||||
GOLD: 'GOLD',
|
||||
SLATE: 'SLATE',
|
||||
} as const
|
||||
export type AgentAvatarTone = typeof AgentAvatarTone[keyof typeof AgentAvatarTone]
|
||||
|
||||
export interface TeamAgent {
|
||||
id: string
|
||||
name: string
|
||||
role: string
|
||||
personnelNumber: string
|
||||
department: string
|
||||
email: string
|
||||
avatarTone: AgentAvatarTone
|
||||
status: AgentStatus
|
||||
autonomyLevel: AgentAutonomyLevel
|
||||
/** Ein Satz für Karte und Listeneintrag. */
|
||||
shortDescription: string
|
||||
/** Freitext zur Autonomie, wo der Katalog differenziert («autonom beim Erinnern, Freigabe bei Eskalationen»). */
|
||||
autonomyNote: string
|
||||
profile: AgentProfile
|
||||
responsibilities: string[]
|
||||
tasks: AgentTask[]
|
||||
channels: AgentChannel[]
|
||||
systems: AgentSystem[]
|
||||
settings: AgentSetting[]
|
||||
metrics: AgentMetric[]
|
||||
/** Kurzkennzahl für die Agentenkarte auf der Teamübersicht. */
|
||||
headlineMetricId: string
|
||||
lastRun?: string
|
||||
}
|
||||
|
||||
// ── Schreib-Inputs ────────────────────────────────────────────────────────────
|
||||
|
||||
export interface UpdateTeamAgentInput {
|
||||
status?: AgentStatus
|
||||
tasks?: AgentTask[]
|
||||
channels?: AgentChannel[]
|
||||
settings?: AgentSetting[]
|
||||
}
|
||||
Reference in new issue
Block a user