import { useState, useMemo } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { Link, useNavigate } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { customerApi, contractApi, contractTaskApi, appSettingsApi } from '../services/api'; import Card from '../components/ui/Card'; import Button from '../components/ui/Button'; import Input from '../components/ui/Input'; import Modal from '../components/ui/Modal'; import { Users, FileText, AlertCircle, AlertTriangle, CheckCircle, User, ClipboardList, MessageSquare, Plus, Clock, XCircle, } from 'lucide-react'; import type { Contract } from '../types'; export default function Dashboard() { const { user, isCustomer, isCustomerPortal } = useAuth(); const [showCreateTicketModal, setShowCreateTicketModal] = useState(false); // Lade öffentliche Einstellungen (für Kundenportal - Support-Tickets aktiviert?) const { data: publicSettings, isLoading: isLoadingSettings } = useQuery({ queryKey: ['app-settings-public'], queryFn: () => appSettingsApi.getPublic(), enabled: isCustomerPortal, staleTime: 0, // Immer neu laden, damit Einstellungsänderungen sofort wirken }); // Wichtig: Nur true wenn explizit aktiviert UND geladen const supportTicketsEnabled = !isLoadingSettings && publicSettings?.data?.customerSupportTicketsEnabled === 'true'; const { data: customersData } = useQuery({ queryKey: ['customers-count'], queryFn: () => customerApi.getAll({ limit: 1 }), enabled: !isCustomer, }); const { data: contractsData } = useQuery({ queryKey: ['contracts', isCustomer ? user?.customerId : undefined], queryFn: () => contractApi.getAll(isCustomer ? { customerId: user?.customerId } : { limit: 1 }), }); const { data: activeContractsData } = useQuery({ queryKey: ['contracts-active', isCustomer ? user?.customerId : undefined], queryFn: () => contractApi.getAll({ status: 'ACTIVE', ...(isCustomer ? { customerId: user?.customerId } : { limit: 1 }), }), }); const { data: pendingContractsData } = useQuery({ queryKey: ['contracts-pending', isCustomer ? user?.customerId : undefined], queryFn: () => contractApi.getAll({ status: 'PENDING', ...(isCustomer ? { customerId: user?.customerId } : { limit: 1 }), }), }); // Task-Statistik const { data: taskStatsData } = useQuery({ queryKey: ['task-stats'], queryFn: () => contractTaskApi.getStats(), }); // Vertrags-Cockpit für Mitarbeiter/Admins const { data: cockpitData } = useQuery({ queryKey: ['contract-cockpit'], queryFn: () => contractApi.getCockpit(), enabled: !isCustomer, staleTime: 0, }); // Für Kundenportal: Verträge nach eigene/fremd gruppieren const { ownContracts, representedContracts } = useMemo(() => { if (!isCustomerPortal || !contractsData?.data) { return { ownContracts: [], representedContracts: [] }; } const own: Contract[] = []; const represented: Record = {}; for (const contract of contractsData.data) { if (contract.customerId === user?.customerId) { own.push(contract); } else { const customerId = contract.customerId; if (!represented[customerId]) { const customerName = contract.customer ? (contract.customer.companyName || `${contract.customer.firstName} ${contract.customer.lastName}`) : `Kunde ${customerId}`; represented[customerId] = { customerName, contracts: [] }; } represented[customerId].contracts.push(contract); } } return { ownContracts: own, representedContracts: Object.values(represented).sort((a, b) => a.customerName.localeCompare(b.customerName) ), }; }, [contractsData?.data, isCustomerPortal, user?.customerId]); // Zähle Verträge für eigene vs. fremd const ownActiveCount = useMemo(() => ownContracts.filter(c => c.status === 'ACTIVE').length, [ownContracts] ); const ownPendingCount = useMemo(() => ownContracts.filter(c => c.status === 'PENDING').length, [ownContracts] ); const ownExpiredCount = useMemo(() => ownContracts.filter(c => c.status === 'EXPIRED').length, [ownContracts] ); const representedTotalCount = useMemo(() => representedContracts.reduce((sum, g) => sum + g.contracts.length, 0), [representedContracts] ); const representedActiveCount = useMemo(() => representedContracts.reduce((sum, g) => sum + g.contracts.filter(c => c.status === 'ACTIVE').length, 0), [representedContracts] ); const representedExpiredCount = useMemo(() => representedContracts.reduce((sum, g) => sum + g.contracts.filter(c => c.status === 'EXPIRED').length, 0), [representedContracts] ); const openTasksCount = taskStatsData?.data?.openCount || 0; // Helper zum Rendern einer klickbaren Stat-Karte const renderStatCard = (stat: { label: string; value: number; icon: typeof FileText; color: string; link?: string; }) => ( {stat.link ? (

{stat.label}

{stat.value}

) : (

{stat.label}

{stat.value}

)}
); return (

Willkommen, {user?.firstName}!

{/* Support-Ticket erstellen Button für Kundenportal */} {isCustomerPortal && supportTicketsEnabled && ( )}
{/* Kundenportal: Getrennte Statistiken */} {isCustomerPortal ? ( <> {/* Eigene Verträge Stats */}

Meine Verträge

{renderStatCard({ label: 'Eigene Verträge', value: ownContracts.length, icon: FileText, color: 'bg-blue-500', link: '/contracts', })} {renderStatCard({ label: 'Davon aktiv', value: ownActiveCount, icon: CheckCircle, color: 'bg-green-500', })} {renderStatCard({ label: 'Davon ausstehend', value: ownPendingCount, icon: Clock, color: 'bg-yellow-500', })} {renderStatCard({ label: 'Davon abgelaufen', value: ownExpiredCount, icon: XCircle, color: 'bg-red-500', })}
{/* Fremdverträge Stats - nur anzeigen wenn vorhanden */} {representedTotalCount > 0 && (

Fremdverträge

{renderStatCard({ label: 'Fremdverträge', value: representedTotalCount, icon: Users, color: 'bg-purple-500', link: '/contracts', })} {renderStatCard({ label: 'Davon aktiv', value: representedActiveCount, icon: CheckCircle, color: 'bg-green-500', })} {/* Leere Karte für Symmetrie */}
{renderStatCard({ label: 'Davon abgelaufen', value: representedExpiredCount, icon: XCircle, color: 'bg-red-500', })}
)} {/* Support-Anfragen Stats */}

Support-Anfragen

{renderStatCard({ label: 'Offene Anfragen', value: openTasksCount, icon: MessageSquare, color: 'bg-orange-500', link: '/tasks', })}
) : ( /* Mitarbeiter/Admin: Standard Stats */ <>
{renderStatCard({ label: 'Kunden', value: customersData?.pagination?.total || 0, icon: Users, color: 'bg-blue-500', link: '/customers', })} {renderStatCard({ label: 'Verträge gesamt', value: contractsData?.pagination?.total || 0, icon: FileText, color: 'bg-purple-500', link: '/contracts', })} {renderStatCard({ label: 'Aktive Verträge', value: activeContractsData?.pagination?.total || 0, icon: CheckCircle, color: 'bg-green-500', })} {renderStatCard({ label: 'Ausstehende Verträge', value: pendingContractsData?.pagination?.total || 0, icon: AlertCircle, color: 'bg-yellow-500', })}
{/* Vertrags-Cockpit Übersicht */} {cockpitData?.data && (

Vertrags-Cockpit

Alle anzeigen

Kritisch (<{cockpitData.data.thresholds.criticalDays} Tage)

{cockpitData.data.summary.criticalCount}

Warnung (<{cockpitData.data.thresholds.warningDays} Tage)

{cockpitData.data.summary.warningCount}

OK (<{cockpitData.data.thresholds.okDays} Tage)

{cockpitData.data.summary.okCount}

Handlungsbedarf

{cockpitData.data.summary.totalContracts}

)} {/* Aufgaben Stats für Mitarbeiter */}

Aufgaben

{renderStatCard({ label: 'Offene Aufgaben', value: openTasksCount, icon: ClipboardList, color: 'bg-orange-500', link: '/tasks', })}
)} {/* Support-Ticket erstellen Modal (für Kundenportal) */} {isCustomerPortal && ( setShowCreateTicketModal(false)} /> )}
); } // Modal für neue Support-Anfrage (Kundenportal) function CreateSupportTicketModal({ isOpen, onClose, }: { isOpen: boolean; onClose: () => void; }) { const { user } = useAuth(); const navigate = useNavigate(); const queryClient = useQueryClient(); const [customerFilter, setCustomerFilter] = useState<'own' | number>('own'); const [selectedContractId, setSelectedContractId] = useState(null); const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [contractSearch, setContractSearch] = useState(''); // Lade alle Verträge des Benutzers (eigene + freigegebene) const { data: contractsData } = useQuery({ queryKey: ['contracts', user?.customerId], queryFn: () => contractApi.getAll({ customerId: user?.customerId }), enabled: isOpen, }); // Gruppiere Verträge nach Kunde const groupedContracts = useMemo(() => { if (!contractsData?.data) return { own: [], represented: {} as Record }; const own: Contract[] = []; const represented: Record = {}; for (const contract of contractsData.data) { if (contract.customerId === user?.customerId) { own.push(contract); } else { if (!represented[contract.customerId]) { const name = contract.customer ? (contract.customer.companyName || `${contract.customer.firstName} ${contract.customer.lastName}`) : `Kunde ${contract.customerId}`; represented[contract.customerId] = { name, contracts: [] }; } represented[contract.customerId].contracts.push(contract); } } return { own, represented }; }, [contractsData?.data, user?.customerId]); // Hat der Benutzer freigegebene Kunden? const hasRepresentedCustomers = Object.keys(groupedContracts.represented).length > 0; // Aktuelle Verträge basierend auf Kundenfilter const currentContracts = useMemo(() => { if (customerFilter === 'own') { return groupedContracts.own; } return groupedContracts.represented[customerFilter]?.contracts || []; }, [customerFilter, groupedContracts]); // Gefilterte Verträge basierend auf Suche const filteredContracts = useMemo(() => { if (!contractSearch) return currentContracts; const search = contractSearch.toLowerCase(); return currentContracts.filter(c => c.contractNumber.toLowerCase().includes(search) || (c.providerName || '').toLowerCase().includes(search) || (c.tariffName || '').toLowerCase().includes(search) ); }, [currentContracts, contractSearch]); const handleSubmit = async () => { if (!selectedContractId || !title.trim()) return; setIsSubmitting(true); try { await contractTaskApi.createSupportTicket(selectedContractId, { title: title.trim(), description: description.trim() || undefined, }); // Invalidate task stats queryClient.invalidateQueries({ queryKey: ['task-stats'] }); queryClient.invalidateQueries({ queryKey: ['all-tasks'] }); onClose(); // Reset form setTitle(''); setDescription(''); setSelectedContractId(null); setCustomerFilter('own'); // Navigate to the contract navigate(`/contracts/${selectedContractId}`); } catch (error) { console.error('Fehler beim Erstellen der Support-Anfrage:', error); alert('Fehler beim Erstellen der Support-Anfrage. Bitte versuchen Sie es erneut.'); } finally { setIsSubmitting(false); } }; const handleClose = () => { setTitle(''); setDescription(''); setSelectedContractId(null); setCustomerFilter('own'); setContractSearch(''); onClose(); }; return (
{/* Kundenauswahl (nur wenn freigegebene Kunden vorhanden) */} {hasRepresentedCustomers && (
)} {/* Vertragsauswahl */}
setContractSearch(e.target.value)} className="mb-2" />
{filteredContracts.length > 0 ? ( filteredContracts.map((contract) => (
setSelectedContractId(contract.id)} className={`p-3 cursor-pointer border-b last:border-b-0 hover:bg-gray-50 ${ selectedContractId === contract.id ? 'bg-blue-50 border-blue-200' : '' }`} >
{contract.contractNumber}
{contract.providerName || 'Kein Anbieter'} {contract.tariffName && ` - ${contract.tariffName}`}
)) ) : (
Keine Verträge gefunden.
)}
{/* Titel */}
setTitle(e.target.value)} placeholder="Kurze Beschreibung Ihres Anliegens" />
{/* Beschreibung */}