import { useState } from 'react' import { Box, Button, Card, Chip, Typography, Stack, CircularProgress, } from '@mui/material' import { Building2, Edit, CheckCircle, XCircle, TrendingUp, Search, ClipboardList, } from 'lucide-react' import { useQuery } from '@tanstack/react-query' import { governanceService, type ActivityEventType, type ActivityEvent } from '../../services/governanceService' import { EmptyState } from '../../components/ui' function getEventLabel(type: ActivityEventType): string { switch (type) { case 'PROPERTY_CREATED': return 'Objekt erstellt' case 'PROPERTY_UPDATED': return 'Objekt aktualisiert' case 'MATCH_APPROVED': return 'Match genehmigt' case 'MATCH_REJECTED': return 'Match abgelehnt' case 'SIGNAL_VERIFIED': return 'Signal verifiziert' case 'NEED_CREATED': return 'Bedarf erstellt' case 'REVIEW_REQUESTED': return 'Überprüfung angefordert' } } function getEventDescription(event: ActivityEvent): string { const actor = event.performedBy const action = getEventLabel(event.type) const entity = `${event.entityType} ${event.entityId}` return `${actor} hat ${entity} — ${action}` } function getEventColor(type: ActivityEventType): string { switch (type) { case 'PROPERTY_CREATED': return '#1e3a5f' case 'PROPERTY_UPDATED': return '#1e3a5f' case 'MATCH_APPROVED': return '#1a7a4a' case 'MATCH_REJECTED': return '#c0392b' case 'SIGNAL_VERIFIED': return '#7c3aed' case 'NEED_CREATED': return '#0891b2' case 'REVIEW_REQUESTED': return '#d97706' } } function getEventIcon(type: ActivityEventType) { const size = 14 switch (type) { case 'PROPERTY_CREATED': return case 'PROPERTY_UPDATED': return case 'MATCH_APPROVED': return case 'MATCH_REJECTED': return case 'SIGNAL_VERIFIED': return case 'NEED_CREATED': return case 'REVIEW_REQUESTED': return } } const ALL_EVENT_TYPES: ActivityEventType[] = [ 'PROPERTY_CREATED', 'PROPERTY_UPDATED', 'MATCH_APPROVED', 'MATCH_REJECTED', 'SIGNAL_VERIFIED', 'NEED_CREATED', 'REVIEW_REQUESTED', ] function formatDateTime(dateStr: string): string { const d = new Date(dateStr) return d.toLocaleDateString('de-CH', { day: '2-digit', month: '2-digit', year: 'numeric', }) + ', ' + d.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' }) } function isToday(dateStr: string): boolean { const d = new Date(dateStr) const now = new Date() return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth() && d.getDate() === now.getDate() } export default function Governance() { const [filterType, setFilterType] = useState('ALL') const { data: activityResp, isLoading, error } = useQuery({ queryKey: ['activity', 'org-wincasa'], queryFn: () => governanceService.getActivityLog('org-wincasa'), }) const events = activityResp?.data ?? [] const presentTypes = [...new Set(events.map(e => e.type))] const todayCount = events.filter(e => isToday(e.createdAt)).length const uniqueUsers = new Set(events.map(e => e.performedBy)).size const filtered = filterType === 'ALL' ? events : events.filter(e => e.type === filterType) if (isLoading) { return ( ) } if (error) { return ( Fehler beim Laden des Aktivitätslogs. ) } return ( {/* Page Header */} Governance & Aktivitätslog Vollständiger Audit-Trail aller Plattformaktionen {/* Stats row */} Ereignisse gesamt {events.length} Alle Aktivitäten Ereignisse heute {todayCount} Heutige Aktivitäten Aktive Benutzer {uniqueUsers} Unterschiedliche Nutzer {/* Filter chips */} setFilterType('ALL')} sx={{ bgcolor: filterType === 'ALL' ? '#1e3a5f' : 'transparent', color: filterType === 'ALL' ? 'white' : 'text.secondary', border: `1px solid ${filterType === 'ALL' ? '#1e3a5f' : '#e2e8f0'}`, fontWeight: filterType === 'ALL' ? 600 : 400, }} /> {ALL_EVENT_TYPES.filter(t => presentTypes.includes(t)).map(t => ( setFilterType(t)} sx={{ bgcolor: filterType === t ? getEventColor(t) : 'transparent', color: filterType === t ? 'white' : 'text.secondary', border: `1px solid ${filterType === t ? getEventColor(t) : '#e2e8f0'}`, fontWeight: filterType === t ? 600 : 400, }} /> ))} {/* Activity Timeline */} Aktivitätslog {filtered.length === 0 ? ( ) : ( {/* Vertical line */} {filtered.map((event, idx) => ( {/* Icon dot */} {getEventIcon(event.type)} {/* Content */} {getEventDescription(event)} {event.notes && ( {event.notes} )} {/* Timestamp */} {formatDateTime(event.createdAt)} ))} )} ) }