v1.36: Mieterverwaltung (Mieter 1:1 pro Wohnung, Kaution/Vertrag/Dokumente) + v1.37: KI-Assistent (N8N + Kimi, 6 Portal-Tools)
This commit is contained in:
+13
-12
@@ -59,11 +59,12 @@ function StatCard({
|
||||
}
|
||||
|
||||
function TenantDashboard() {
|
||||
const { meineEinheit: demoUnit, me, transactions, tickets, threads, announcements } = useStore()
|
||||
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,
|
||||
@@ -89,8 +90,8 @@ function TenantDashboard() {
|
||||
<StatCard
|
||||
icon={Wallet}
|
||||
label="Monatliche Warmmiete"
|
||||
value={fmtEUR.format(demoUnit.warmRent)}
|
||||
sub={`Kaltmiete ${fmtEUR.format(demoUnit.coldRent)} + NK`}
|
||||
value={meinMieter ? fmtEUR.format(meinMieter.warmmiete) : '–'}
|
||||
sub={meinMieter ? `Kaltmiete ${fmtEUR.format(meinMieter.kaltmiete)} + NK` : 'Mieterdaten folgen'}
|
||||
/>
|
||||
<StatCard
|
||||
icon={Euro}
|
||||
@@ -109,8 +110,8 @@ function TenantDashboard() {
|
||||
<StatCard
|
||||
icon={TicketCheck}
|
||||
label="Mietverhältnis seit"
|
||||
value="März 2024"
|
||||
sub="Kaution: 1.500,00 € hinterlegt"
|
||||
value={meinMieter?.mietbeginnAm ? fmtDatum(meinMieter.mietbeginnAm) : '–'}
|
||||
sub={meinMieter ? `Kaution: ${fmtEUR.format(meinMieter.kautionHoehe)} hinterlegt` : 'Mieterdaten folgen'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -216,10 +217,10 @@ function TenantDashboard() {
|
||||
}
|
||||
|
||||
function AdminDashboard() {
|
||||
const { units, transactions, tickets, announcements, buildings: gebaeude } = useStore()
|
||||
const { units, transactions, tickets, announcements, buildings: gebaeude, mieter } = useStore()
|
||||
const rented = units.filter((u) => u.status === 'vermietet')
|
||||
const occupancy = Math.round((rented.length / units.length) * 100)
|
||||
const monthlyIncome = rented.reduce((s, u) => s + u.warmRent, 0)
|
||||
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')
|
||||
|
||||
@@ -235,7 +236,7 @@ function AdminDashboard() {
|
||||
return {
|
||||
name: g.name,
|
||||
Vermietet: us.filter((u) => u.status === 'vermietet').length,
|
||||
Frei: us.filter((u) => u.status === 'frei').length,
|
||||
Leerstand: us.filter((u) => u.status === 'leerstand').length,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -249,7 +250,7 @@ function AdminDashboard() {
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<StatCard icon={Building2} label="Wohneinheiten gesamt" value={String(units.length)} sub={`${units.filter((u) => u.status === 'frei').length} aktuell frei`} />
|
||||
<StatCard icon={Building2} label="Wohneinheiten gesamt" value={String(units.length)} sub={`${units.filter((u) => u.status === 'leerstand').length} aktuell im Leerstand`} />
|
||||
<StatCard icon={Users} label="Vermietungsquote" value={`${occupancy} %`} sub={`${rented.length} von ${units.length} Wohneinheiten`} />
|
||||
<StatCard icon={Euro} label="Monatliche Warmmiete (Soll)" value={fmtEUR.format(monthlyIncome)} sub="Summe aller vermieteten Einheiten" />
|
||||
<StatCard
|
||||
@@ -293,13 +294,13 @@ function AdminDashboard() {
|
||||
<YAxis type="category" dataKey="name" tick={{ fontSize: 11 }} stroke="#94a3b8" width={95} />
|
||||
<Tooltip />
|
||||
<Bar dataKey="Vermietet" stackId="a" fill="#0d9488" radius={[0, 0, 0, 0]} isAnimationActive={false} />
|
||||
<Bar dataKey="Frei" stackId="a" fill="#f59e0b" radius={[0, 6, 6, 0]} isAnimationActive={false} />
|
||||
<Bar dataKey="Leerstand" stackId="a" fill="#f59e0b" radius={[0, 6, 6, 0]} isAnimationActive={false} />
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
<div className="mt-2 flex gap-4 text-xs text-slate-500">
|
||||
<span className="flex items-center gap-1.5"><span className="h-2.5 w-2.5 rounded-sm bg-teal-600" /> vermietet</span>
|
||||
<span className="flex items-center gap-1.5"><span className="h-2.5 w-2.5 rounded-sm bg-amber-500" /> frei</span>
|
||||
<span className="flex items-center gap-1.5"><span className="h-2.5 w-2.5 rounded-sm bg-amber-500" /> Leerstand</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user