52dc7de25e
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>
362 lines
11 KiB
TypeScript
362 lines
11 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',
|
||
}
|
||
|
||
/** 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',
|
||
}
|