Files
property-match/src/lib/constants.ts
T
Benjamin Sutter 52dc7de25e 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>
2026-08-03 16:55:40 +02:00

362 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 (0100)
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: 300800 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',
}