import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts' import { ArrowRight, Building2, CalendarClock, CircleAlert, Euro, Megaphone, TicketCheck, Users, Wallet, Wrench, } from 'lucide-react' import { Link } from 'react-router' import { useStore, fmtEUR } from '../store/store' import { fmtDatum } from '../lib/format' const monthShort = ['Jan', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'] function StatCard({ icon: Icon, label, value, sub, tone = 'teal', }: { icon: React.ElementType label: string value: string sub?: string tone?: 'teal' | 'amber' | 'red' | 'blue' }) { const tones = { teal: 'bg-teal-50 text-teal-700', amber: 'bg-amber-50 text-amber-700', red: 'bg-red-50 text-red-600', blue: 'bg-blue-50 text-blue-700', } return (
{value}
{label}
{sub &&
{sub}
}
) } function TenantDashboard() { const { meineEinheit: demoUnit, me, transactions, tickets, threads, announcements, mieter } = useStore() const myTx = transactions.filter((t) => t.unitId === demoUnit.id) const openTx = myTx.filter((t) => t.status !== 'bezahlt') const myTickets = tickets.filter((t) => t.unitId === demoUnit.id) const unread = threads.filter((t) => t.unreadByTenant) const meinMieter = mieter.find((m) => m.unitId === demoUnit.id) const chartData = monthShort.slice(0, 10).map((m, i) => ({ m, Betrag: myTx.filter((t) => t.year === 2026 && myTx.indexOf(t) % 12 === i).reduce((s, t) => s + (t.status === 'bezahlt' ? t.amount : 0), 0), })) return (

Hallo, {me?.name}

{demoUnit.building} · Wohnung {demoUnit.number} · {demoUnit.size} m²

Nächste Zahlung: Oktober-Miete bis 03.10.2026
0 ? 'Offene Forderungen' : 'Keine offenen Forderungen'} value={openTx.length > 0 ? fmtEUR.format(openTx.reduce((s, t) => s + t.amount, 0)) : '0,00 €'} tone={openTx.length > 0 ? 'amber' : 'teal'} sub={openTx.length > 0 ? `${openTx.length} Posten zu zahlen` : 'Alles bezahlt – danke!'} /> t.status !== 'erledigt').length} in Bearbeitung`} />

Gezahlte Miete 2026

Zum Mietkonto
fmtEUR.format(Number(v))} />

Ankündigungen

    {announcements.slice(0, 3).map((a) => (
  • {a.tag} {fmtDatum(a.date)}
    {a.title}
    {a.text}
  • ))}

Offene Schadensmeldungen

Alle anzeigen
{myTickets.filter((t) => t.status !== 'erledigt').length === 0 ? (

Aktuell keine offenen Meldungen.

) : (
    {myTickets .filter((t) => t.status !== 'erledigt') .map((t) => (
  • {t.title}
    {t.category} · {fmtDatum(t.createdAt)}
    {t.status}
  • ))}
)}

Nachrichten

Posteingang
{unread.length === 0 ? (

Keine ungelesenen Nachrichten.

) : (
    {unread.map((t) => (
  • {t.subject}
    {t.messages[t.messages.length - 1]?.text}
  • ))}
)}
) } function AdminDashboard() { const { units, transactions, tickets, announcements, buildings: gebaeude, mieter } = useStore() const rented = units.filter((u) => u.status === 'vermietet') const occupancy = units.length ? Math.round((rented.length / units.length) * 100) : 0 const monthlyIncome = mieter.reduce((s, m) => s + m.warmmiete, 0) const openTickets = tickets.filter((t) => t.status === 'offen') const overdue = transactions.filter((t) => t.status === 'überfällig') const incomeData = monthShort.slice(0, 10).map((m, i) => ({ m, Einnahmen: transactions .filter((t) => t.month === ['Januar','Februar','März','April','Mai','Juni','Juli','August','September','Oktober'][i] && t.status === 'bezahlt') .reduce((s, t) => s + t.amount, 0), })) const buildings = gebaeude.map((g) => { const us = units.filter((u) => u.building === g.name) return { name: g.name, Vermietet: us.filter((u) => u.status === 'vermietet').length, Leerstand: us.filter((u) => u.status === 'leerstand').length, } }) return (

Verwaltungs-Dashboard

Bestand: {buildings.length} {buildings.length === 1 ? 'Haus' : 'Häuser'} · {units.length} Wohneinheiten · {rented.length} vermietet

u.status === 'leerstand').length} aktuell im Leerstand`} /> 0 ? 'red' : 'teal'} sub="Offene Schäden und rückständige Zahlungen" />

Eingänge 2026 (bezahlte Mieten)

fmtEUR.format(Number(v))} />

Belegung pro Haus

vermietet Leerstand

Neueste Schadensmeldungen

Alle anzeigen
    {tickets.slice(0, 4).map((t) => (
  • {t.title}
    {units.find((u) => u.id === t.unitId)?.building}, Whg. {units.find((u) => u.id === t.unitId)?.number} · {t.tenant}
    {t.status}
  • ))}

Letzte Ankündigung

{announcements[0].tag} {announcements[0].date}
{announcements[0].title}

{announcements[0].text}

Ankündigungen sind im Mieter-Dashboard sowie als Nachricht sichtbar.

) } export default function Dashboard() { const { role } = useStore() return role === 'tenant' ? : }