8377ce03b5
Setzt die Referenzgrafik «Agenten_Bewirtschaftung_v2» um: die 36 digitalen Mitarbeitenden in konzentrischen Ringen, Kernteam innen und gold gerahmt. DATENBASIS Alle 36 Mitarbeitenden samt Porträts sind aus dem Konzeptdokument extrahiert. Die Stufen stecken bereits in den Quelldaten und decken sich exakt mit den Bereichen: lvl 1 = 7 Kernteam, lvl 2 = 18 Zentrale Dienste, lvl 3 = 7 GB Kommerziell, lvl 4 = 4 GB Wohnen. Auch die Reiterbeschriftungen sind wörtlich übernommen — keine neuen Stufennamen erfunden. Neu getrennt: `AgentDirectoryEntry` trägt die Stammdaten aller 36 für die Kreisdarstellung, `TeamAgent` bleibt das vollständige Dossier der sieben Kernteammitglieder. Für die übrigen 29 werden bewusst keine Aufgaben, Kanäle oder Tätigkeiten erfunden; ein Klick zeigt nur eine Vorschau aus dem Verzeichnis. ABWEICHUNG VOM AUFTRAG Der Auftrag nennt als Kernteam Ferdi, Sami, Leo, Bruno, Reto, Milo, Nora. Referenzgrafik und Konzeptdaten sagen übereinstimmend Ferdi, Sina, Lea, Bruno, Reto, Livia, Nora — dort tragen genau diese sieben `kern: true` und `lvl: 1`. Da der Auftrag die Grafik selbst als verbindlich erklärt und Namen laut §10 kein Primärschlüssel sind, folge ich Grafik und Daten. Sami, Leo und Milo kommen in keiner Quelle vor. OBERFLÄCHE - Teamübersicht: Beschreibungstext und «Demo zurücksetzen» entfernt, Kreis mit kumulativen Stufenreitern direkt unter dem Titel - Personalverwaltung: Umschaltung Dossieransicht / Organigramm über ?view=, Organigramm als oben abgeschnittener Halbkreis mit drehbarem Agentenrad (Ziehen, Touch, Pfeiltasten, Klick), aktiv ist wer unten mittig einrastet - Dossier: Untermenü direkt unter den Stammdaten, «Agentenbeschrieb» als erster Reiter, Zuständigkeiten auf drei gekürzt - Kanäle & Systeme: vier Karten pro Zeile, Kategorie-Sinnbild links vor dem Titel, Porträtgruppe statt Namensliste, ein Regler statt Verbinden/Trennen - Bearbeitungsverlauf: neutrale Karten, Farbe nur noch bei Ausnahmepriorität und abweichendem Status, mehr Weissraum Markenlogos gibt es im Repo nicht und wurden nicht nachgezeichnet — je Kategorie steht ein neutrales Sinnbild. Fehlendes Asset, bewusst so gelöst. Positionsdaten stehen ausschliesslich in `agentRingConfig.ts`; Vollkreis und Halbkreis teilen sich dieselbe Geometrie. AUFGERÄUMT `AgentCard` ist durch die Kreisdarstellung ersetzt und entfernt, seine Zusagen sind in `TeamOverviewRing.test.tsx` übergegangen — inklusive der wichtigsten: ausserhalb des Kernteams führt kein Klick in ein Dossier. Zwei echte Fehler unterwegs behoben: jsdom kennt keinen `ResizeObserver` (jetzt mit Rückfallebene statt Absturz), und das Rad las eine Ref im Render. 413 Tests grün, Build grün, ESLint über den Property-On-Code sauber, check:tokens unverändert bei 2249. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
396 lines
12 KiB
TypeScript
396 lines
12 KiB
TypeScript
// Centralized app-wide constants — no magic strings anywhere else
|
||
|
||
export const APP_NAME = 'Property-Match'
|
||
export const APP_VERSION = '0.1.0'
|
||
|
||
// Organisation
|
||
export const DEFAULT_ORG_ID = 'org-wincasa'
|
||
|
||
// Pagination
|
||
export const DEFAULT_PAGE_SIZE = 25
|
||
export const MAX_COMPARE_ITEMS = 3
|
||
|
||
// Data quality thresholds
|
||
export const DQ_HIGH = 0.8
|
||
export const DQ_MEDIUM = 0.6
|
||
|
||
// Confidence thresholds
|
||
export const CONF_HIGH = 0.85
|
||
export const CONF_MEDIUM = 0.65
|
||
|
||
// Match score thresholds (0–100)
|
||
export const SCORE_STRONG = 80
|
||
export const SCORE_MODERATE = 60
|
||
|
||
// Probability thresholds (future signals)
|
||
export const PROB_HIGH = 0.7
|
||
export const PROB_MEDIUM = 0.5
|
||
|
||
// Query stale times (ms)
|
||
export const STALE_PROPERTIES = 5 * 60 * 1000
|
||
export const STALE_MATCHES = 2 * 60 * 1000
|
||
export const STALE_SIGNALS = 5 * 60 * 1000
|
||
export const STALE_MARKET_SIGNALS = 30 * 1000 // aggressive — market leads refresh often
|
||
export const STALE_REVIEW_QUEUE = 30 * 1000 // aggressive — ops team works in real-time
|
||
export const STALE_TEAM_AGENTS = 5 * 60 * 1000
|
||
export const STALE_AGENT_WORK_ITEMS = 15 * 1000 // aggressive — Freigaben ändern die Liste sofort
|
||
export const STALE_AGENT_PROTOCOL = 15 * 1000 // aggressive — jede Aktion schreibt einen Eintrag
|
||
export const STALE_AGENT_CONNECTIONS = 60 * 1000
|
||
|
||
// Route paths — single source of truth
|
||
export const ROUTES = {
|
||
HOME: '/',
|
||
SUPPLY: {
|
||
DASHBOARD: '/supply/dashboard',
|
||
PROPERTIES: '/supply/properties',
|
||
MATCH_CENTER: '/supply/match-center',
|
||
FUTURE_AVAILABILITY: '/supply/future-availability',
|
||
DATA_QUALITY: '/supply/data-quality',
|
||
// Property On — «Teamübersicht». Basispfad bewusst `/supply/team` statt
|
||
// `/supply/teamuebersicht`: kürzer, kollidiert nicht mit `PowerOn` in Suchen
|
||
// und passt zum Ordner `components/team/`. Hierarchie und Deep-Links bleiben.
|
||
TEAM: '/supply/team',
|
||
TEAM_PERSONNEL: '/supply/team/personalverwaltung',
|
||
TEAM_HISTORY: '/supply/team/bearbeitungsverlauf',
|
||
TEAM_HISTORY_DONE: '/supply/team/bearbeitungsverlauf/erledigte-auftraege',
|
||
TEAM_HISTORY_PENDING: '/supply/team/bearbeitungsverlauf/pendente-anfragen',
|
||
TEAM_CONNECTIONS: '/supply/team/kanaele-systeme',
|
||
},
|
||
DEMAND: {
|
||
AI_SEARCH: '/demand/ai-search',
|
||
RESULTS: '/demand/results',
|
||
COMPARE: '/demand/compare',
|
||
SHORTLISTS: '/demand/shortlists',
|
||
},
|
||
OPS: {
|
||
REVIEW_QUEUE: '/ops/review-queue',
|
||
AI_MONITORING: '/ops/ai-monitoring',
|
||
GOVERNANCE: '/ops/governance',
|
||
},
|
||
} as const
|
||
|
||
// Asset type display labels
|
||
export const ASSET_TYPE_LABELS: Record<string, string> = {
|
||
OFFICE: 'Büro',
|
||
RETAIL: 'Retail',
|
||
GASTRO: 'Gastronomie',
|
||
LOGISTICS: 'Logistik',
|
||
LIGHT_INDUSTRIAL: 'Gewerbe',
|
||
PRODUCTION: 'Produktion',
|
||
MIXED: 'Gemischt',
|
||
}
|
||
|
||
// Result type display labels
|
||
export const RESULT_TYPE_LABELS: Record<string, string> = {
|
||
VERIFIED_PORTFOLIO: 'Plattform',
|
||
MAISON_WORK: 'Maison Work',
|
||
FUTURE_AVAILABILITY: 'Zukunftssignal',
|
||
}
|
||
|
||
// Match strength display labels
|
||
export const MATCH_STRENGTH_LABELS: Record<string, string> = {
|
||
STRONG: 'Stark',
|
||
MODERATE: 'Mittel',
|
||
WEAK: 'Schwach',
|
||
}
|
||
|
||
// Availability status display labels
|
||
export const AVAILABILITY_LABELS: Record<string, string> = {
|
||
AVAILABLE_NOW: 'Verfügbar',
|
||
AVAILABLE_SOON: 'Bald verfügbar',
|
||
FUTURE_SIGNAL: 'Zukunftssignal',
|
||
OCCUPIED: 'Belegt',
|
||
UNKNOWN: 'Unbekannt',
|
||
}
|
||
|
||
// Risk level display labels
|
||
export const RISK_LABELS: Record<string, string> = {
|
||
LOW: 'Niedrig',
|
||
MEDIUM: 'Mittel',
|
||
HIGH: 'Hoch',
|
||
CRITICAL: 'Kritisch',
|
||
}
|
||
|
||
// Signal type display labels
|
||
export const SIGNAL_TYPE_LABELS: Record<string, string> = {
|
||
EXPANSION: 'Expansion',
|
||
POSSIBLE_MOVE_OUT: 'Möglicher Auszug',
|
||
CONSTRUCTION_PROJECT: 'Bauprojekt',
|
||
RESTRUCTURING: 'Umstrukturierung',
|
||
PROJECT_DEVELOPMENT: 'Projektentwicklung',
|
||
SPACE_CONSOLIDATION: 'Flächenkonsolidierung',
|
||
}
|
||
|
||
// Data freshness display labels
|
||
export const FRESHNESS_LABELS: Record<string, string> = {
|
||
FRESH: 'Aktuell',
|
||
STALE: 'Veraltet',
|
||
OUTDATED: 'Abgelaufen',
|
||
}
|
||
|
||
// Confidence level display labels
|
||
export const CONFIDENCE_LABELS: Record<string, string> = {
|
||
VERY_HIGH: 'Sehr hoch',
|
||
HIGH: 'Hoch',
|
||
MEDIUM: 'Mittel',
|
||
LOW: 'Niedrig',
|
||
VERY_LOW: 'Sehr niedrig',
|
||
}
|
||
|
||
// Data quality level display labels
|
||
export const DATA_QUALITY_LABELS: Record<string, string> = {
|
||
HIGH: 'Hoch',
|
||
MEDIUM: 'Mittel',
|
||
LOW: 'Niedrig',
|
||
INCOMPLETE: 'Unvollständig',
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
// Property On — «Teamübersicht»
|
||
//
|
||
// Schweizer Schreibweise, kein Eszett. Sprache der Zielgruppe: Immobilien-
|
||
// bewirtschafter, nicht KI-Entwickler — deshalb «Aufgabe», «Freigabe»,
|
||
// «Systemzugang» statt Prompt, Token, Tool Call.
|
||
// ─────────────────────────────────────────────────────────────────────────────
|
||
|
||
export const AGENT_STATUS_LABELS: Record<string, string> = {
|
||
ACTIVE: 'Aktiv',
|
||
BUILDING: 'Im Aufbau',
|
||
PAUSED: 'Pausiert',
|
||
}
|
||
|
||
export const AGENT_AUTONOMY_LABELS: Record<string, string> = {
|
||
AUTONOMOUS: 'Autonom',
|
||
APPROVAL_REQUIRED: 'Freigabe erforderlich',
|
||
PROPOSAL_ONLY: 'Nur Vorschlag',
|
||
}
|
||
|
||
export const AGENT_CHANNEL_LABELS: Record<string, string> = {
|
||
EMAIL: 'E-Mail',
|
||
TEAMS: 'Microsoft Teams',
|
||
WHATSAPP: 'WhatsApp Business',
|
||
WORKSPACE_CHAT: 'Workspace-Chat',
|
||
CALENDAR: 'Kalender',
|
||
WEB_FORM: 'Website-Formular',
|
||
PORTAL_LEAD_MAIL: 'Portal-Lead-Mail',
|
||
PHONE_VOICE: 'Telefon / Voice',
|
||
PHONE_NOTE: 'Telefonnotiz',
|
||
AUDIO_OUTPUT: 'Audioausgabe',
|
||
STRUCTURED_MAIL_FALLBACK: 'Strukturierter Mail-Fallback',
|
||
}
|
||
|
||
export const AGENT_CHANNEL_DIRECTION_LABELS: Record<string, string> = {
|
||
INBOUND: 'Eingang',
|
||
OUTBOUND: 'Ausgang',
|
||
BOTH: 'Eingang und Ausgang',
|
||
}
|
||
|
||
export const AGENT_CONNECTION_STATUS_LABELS: Record<string, string> = {
|
||
CONNECTED: 'Verbunden',
|
||
DISCONNECTED: 'Nicht verbunden',
|
||
ROADMAP: 'Geplant',
|
||
}
|
||
|
||
export const AGENT_SYSTEM_LABELS: Record<string, string> = {
|
||
M365_EXCHANGE: 'Microsoft 365 / Exchange',
|
||
OUTLOOK: 'Outlook',
|
||
SHAREPOINT: 'SharePoint',
|
||
ONEDRIVE: 'OneDrive',
|
||
FILESHARE: 'Windows-Fileshare',
|
||
DMS: 'Dokumentenablage (DMS)',
|
||
CALENDAR: 'Kalender',
|
||
IMMOTOP2: 'ImmoTop2',
|
||
ABACUS_ABAIMMO: 'Abacus AbaImmo',
|
||
RIMO_R5: 'Rimo R5',
|
||
GARAIO_REM: 'Garaio REM',
|
||
CRM: 'CRM',
|
||
ZEFIX: 'Zefix',
|
||
SHAB: 'SHAB',
|
||
PUBLIC_WEB: 'Öffentliche Webquellen',
|
||
PORTAL_LEAD_INBOX: 'Portal-Lead-Postfach',
|
||
WORKSPACE: 'Property-On-Workspace',
|
||
}
|
||
|
||
export const AGENT_ACCESS_LABELS: Record<string, string> = {
|
||
READ: 'Lesen',
|
||
WRITE: 'Schreiben',
|
||
READ_WRITE: 'Lesen und schreiben',
|
||
}
|
||
|
||
export const AGENT_WORK_ITEM_STATUS_LABELS: Record<string, string> = {
|
||
PENDING: 'Offen',
|
||
APPROVED: 'Freigegeben',
|
||
REJECTED: 'Zurückgewiesen',
|
||
EDITING: 'In Bearbeitung',
|
||
COMPLETED: 'Abgeschlossen',
|
||
}
|
||
|
||
export const AGENT_PRIORITY_LABELS: Record<string, string> = {
|
||
LOW: 'Niedrig',
|
||
MEDIUM: 'Mittel',
|
||
HIGH: 'Hoch',
|
||
CRITICAL: 'Kritisch',
|
||
}
|
||
|
||
export const AGENT_WORK_ITEM_KIND_LABELS: Record<string, string> = {
|
||
MESSAGE_TRIAGE: 'Nachricht kategorisiert',
|
||
REMINDER_CREATED: 'Reminder erstellt',
|
||
CONTRACT_ANSWER: 'Vertragsauskunft',
|
||
LOCATION_MODULE: 'Lagebaustein',
|
||
INQUIRY_QUALIFIED: 'Anfrage qualifiziert',
|
||
AUTO_REJECTION: 'Automatische Absage',
|
||
APPOINTMENT: 'Termin',
|
||
BRIEFING: 'Besichtigungs-Briefing',
|
||
RECAP: 'Gesprächsprotokoll',
|
||
MARKET_SIGNAL: 'Marktsignal',
|
||
ESCALATION: 'Eskalation',
|
||
DOC_GAP: 'Dokumentationslücke',
|
||
SYSTEM_ISSUE: 'Systemproblem',
|
||
}
|
||
|
||
export const AGENT_DOMAIN_AREA_LABELS: Record<string, string> = {
|
||
CONTRACT_DEADLINES: 'Fristen und Verträge',
|
||
DOCUMENTATION: 'Dokumentation',
|
||
INQUIRIES: 'Anfragen',
|
||
VIEWINGS: 'Besichtigungen',
|
||
LOCATION: 'Lage',
|
||
MARKET: 'Markt',
|
||
}
|
||
|
||
export const AGENT_WORK_ITEM_ACTION_LABELS: Record<string, string> = {
|
||
APPROVE: 'Freigeben',
|
||
ONE_CLICK_CONFIRM: 'Mit einem Klick bestätigen',
|
||
DECIDE: 'Entscheidung treffen',
|
||
EDIT: 'Anpassen',
|
||
REJECT: 'Zurückweisen',
|
||
ANSWER_QUERY: 'Rückfrage beantworten',
|
||
OPEN_SOURCE: 'Quelle öffnen',
|
||
MARK_DONE: 'Als erledigt markieren',
|
||
}
|
||
|
||
export const AGENT_PROTOCOL_STATUS_LABELS: Record<string, string> = {
|
||
SUCCESS: 'Erfolgreich',
|
||
WARNING: 'Warnung',
|
||
ERROR: 'Fehler',
|
||
INFO: 'Hinweis',
|
||
}
|
||
|
||
export const AGENT_PROTOCOL_EVENT_LABELS: Record<string, string> = {
|
||
TASK_RUN: 'Aufgabe ausgeführt',
|
||
MESSAGE_PROCESSED: 'Nachricht verarbeitet',
|
||
REMINDER_SENT: 'Erinnerung versendet',
|
||
ESCALATION: 'Eskalation',
|
||
APPROVAL: 'Freigabe',
|
||
REJECTION: 'Zurückweisung',
|
||
EDIT: 'Anpassung',
|
||
SETTINGS_SAVED: 'Einstellungen gespeichert',
|
||
TASKS_SAVED: 'Aufgaben gespeichert',
|
||
AGENT_PAUSED: 'Mitarbeiter pausiert',
|
||
AGENT_RESUMED: 'Mitarbeiter reaktiviert',
|
||
CHANNEL_TESTED: 'Verbindung getestet',
|
||
CHANNEL_TOGGLED: 'Kanal umgeschaltet',
|
||
CONNECTION_CREATED: 'Verbindung angelegt',
|
||
CONNECTION_REMOVED: 'Verbindung getrennt',
|
||
DEMO_RESET: 'Demo zurückgesetzt',
|
||
}
|
||
|
||
export const AGENT_CONNECTION_CATEGORY_LABELS: Record<string, string> = {
|
||
EMAIL_M365: 'E-Mail / Microsoft 365',
|
||
WHATSAPP: 'WhatsApp Business',
|
||
TEAMS: 'Microsoft Teams',
|
||
CALENDAR: 'Kalender',
|
||
DOCUMENT_STORE: 'Dokumentenablage',
|
||
PHONE_VOICE: 'Telefon / Voice',
|
||
ERP: 'ERP',
|
||
CRM: 'CRM',
|
||
PUBLIC_SOURCES: 'Öffentliche Datenquellen',
|
||
}
|
||
|
||
export const AGENT_ERP_VENDOR_LABELS: Record<string, string> = {
|
||
IMMOTOP2: 'ImmoTop2',
|
||
ABACUS_ABAIMMO: 'Abacus AbaImmo',
|
||
RIMO_R5: 'Rimo R5',
|
||
GARAIO_REM: 'Garaio REM',
|
||
OTHER: 'Anderes ERP',
|
||
}
|
||
|
||
export const AGENT_SETTING_GROUP_LABELS: Record<string, string> = {
|
||
GENERAL: 'Allgemein',
|
||
APPROVAL: 'Freigabe und Eskalation',
|
||
DELIVERY: 'Zustellung',
|
||
SPECIFIC: 'Fachliche Einstellungen',
|
||
}
|
||
|
||
/** Zeitraumfilter für Auswertung und Bearbeitungsverlauf (§4.2, §5.3). */
|
||
export const AGENT_PERIOD_LABELS: Record<string, string> = {
|
||
TODAY: 'Heute',
|
||
WEEK: 'Diese Woche',
|
||
MONTH: 'Dieser Monat',
|
||
YEAR: 'Dieses Jahr',
|
||
ALL: 'Gesamter Zeitraum',
|
||
}
|
||
|
||
/**
|
||
* Personalstufen der digitalen Belegschaft.
|
||
*
|
||
* Wortlaut unverändert aus dem Konzeptdokument übernommen — die Reiter sind
|
||
* kumulativ zu lesen: «+ Zentrale Dienste» meint Kernteam plus Zentrale Dienste.
|
||
*/
|
||
export const AGENT_LEVEL_LABELS: Record<string, string> = {
|
||
CORE: 'Kernteam',
|
||
CENTRAL_SERVICES: 'Zentrale Dienste',
|
||
COMMERCIAL: 'GB Kommerzielle Immobilien',
|
||
RESIDENTIAL: 'GB Wohnen',
|
||
}
|
||
|
||
/** Beschriftung der Filterreiter — ebenfalls Wortlaut des Konzepts. */
|
||
export const AGENT_LEVEL_TAB_LABELS: Record<string, string> = {
|
||
CORE: 'Kernteam',
|
||
CENTRAL_SERVICES: '+ Zentrale Dienste',
|
||
COMMERCIAL: '+ GB Kommerziell',
|
||
RESIDENTIAL: 'Alle 36',
|
||
}
|
||
|
||
export const AGENT_DEPARTMENT_LABELS: Record<string, string> = {
|
||
CENTRAL_SERVICES: 'Zentrale Dienste',
|
||
COMMERCIAL: 'GB Kommerzielle Immobilien',
|
||
RESIDENTIAL: 'GB Wohnen',
|
||
}
|
||
|
||
/** Aufbaustand aus dem Konzept — nicht der Betriebszustand aktiv/pausiert. */
|
||
export const AGENT_ROLLOUT_LABELS: Record<string, string> = {
|
||
PROTOTYPE: 'Prototyp läuft',
|
||
IN_PROGRESS: 'Im Aufbau',
|
||
PLANNED: 'Geplant',
|
||
}
|
||
|
||
/** Simulierte Ladezeit für Demo-Aktionen (§19.1: 300–800 ms). */
|
||
export const AGENT_SIM_DELAY_MS = 420
|
||
export const AGENT_SIM_DELAY_SLOW_MS = 760
|
||
|
||
/** Hinweis, der auf jeder Property-On-Seite dezent mitläuft (§19.3). */
|
||
export const AGENT_DEMO_NOTICE = 'Demo-Modus – keine echten Daten oder Systemverbindungen.'
|
||
|
||
// Swiss CRB/BKP fit-out investment benchmarks (CHF/m², tenant's cost)
|
||
export const FIT_OUT_COST_CHF_PER_SQM: Record<string, { min: number; max: number }> = {
|
||
SHELL: { min: 800, max: 1500 },
|
||
BASIC: { min: 400, max: 800 },
|
||
FULL: { min: 50, max: 200 },
|
||
PREMIUM: { min: 0, max: 50 },
|
||
}
|
||
|
||
// Assumed amortization period for fit-out investment — change here to affect all cost calculations
|
||
export const FITOUT_AMORTIZATION_YEARS = 5
|
||
|
||
// Kalkulationszins p.a. für die annuitätische Amortisation der Ausbauinvestition (Schweizer Richtwert)
|
||
export const FITOUT_ANNUITY_RATE = 0.05
|
||
|
||
// Zentrale Ausbaustandard-Labels — branchenüblich + internationale Synonyme (CAT A/B, Core & Shell).
|
||
// Single Source of Truth für Dropdown, Panel, Score, Vergleich.
|
||
export const FIT_OUT_LABELS: Record<string, string> = {
|
||
SHELL: 'Rohbau · Core & Shell',
|
||
BASIC: 'Edelrohbau · CAT A',
|
||
FULL: 'Ausgebaut · CAT B',
|
||
PREMIUM: 'Vollausgebaut · Plug & Play',
|
||
}
|