v1.21: Live-Stammdaten aus Master-Data-CSV importiert, Sync verworfen, Demo-Reste entfernt

This commit is contained in:
2026-10-04 13:50:13 +02:00
parent 26372d563c
commit 497abfcd4b
13 changed files with 246 additions and 27 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ function NeuerBenutzerDialog() {
const [email, setEmail] = useState('')
const [rolle, setRolle] = useState<BenutzerRolle>('mieter')
const [pw, setPw] = useState('0815')
const [einheitId, setEinheitId] = useState('hs-7')
const [einheitId, setEinheitId] = useState(units[0]?.id ?? '')
const [fehler, setFehler] = useState<string | null>(null)
const submit = async () => {
+7 -7
View File
@@ -215,7 +215,7 @@ function TenantDashboard() {
}
function AdminDashboard() {
const { units, transactions, tickets, announcements } = useStore()
const { units, transactions, tickets, announcements, buildings: gebaeude } = 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)
@@ -229,10 +229,10 @@ function AdminDashboard() {
.reduce((s, t) => s + t.amount, 0),
}))
const buildings = ['Hauptstraße 12', 'Ringweg 3', 'Am Park 5'].map((b) => {
const us = units.filter((u) => u.building === b)
const buildings = gebaeude.map((g) => {
const us = units.filter((u) => u.building === g.name)
return {
name: b,
name: g.name,
Vermietet: us.filter((u) => u.status === 'vermietet').length,
Frei: us.filter((u) => u.status === 'frei').length,
}
@@ -243,13 +243,13 @@ function AdminDashboard() {
<div>
<h1 className="text-2xl font-bold tracking-tight">Verwaltungs-Dashboard</h1>
<p className="text-sm text-slate-500">
Bestand: 3 Häuser · 50 Wohnungen · {rented.length} vermietet
Bestand: {buildings.length} {buildings.length === 1 ? 'Haus' : 'Häuser'} · {units.length} Wohneinheiten · {rented.length} vermietet
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
<StatCard icon={Building2} label="Wohnungen gesamt" value="50" sub={`${units.filter((u) => u.status === 'frei').length} aktuell frei`} />
<StatCard icon={Users} label="Vermietungsquote" value={`${occupancy} %`} sub={`${rented.length} von 50 Wohnungen`} />
<StatCard icon={Building2} label="Wohneinheiten gesamt" value={String(units.length)} sub={`${units.filter((u) => u.status === 'frei').length} aktuell frei`} />
<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
icon={CircleAlert}
+1 -1
View File
@@ -137,7 +137,7 @@ function DokumentDialog({ dokument, einheiten }: { dokument?: DocumentItem; einh
<Label htmlFor="d-titel">Titel</Label>
<Input
id="d-titel"
placeholder="z. B. Mietvertrag – Hauptstraße 12, Whg. 7"
placeholder="z. B. Mietvertrag – Blücherstr. 59, Whg. 1"
value={titel}
onChange={(e) => setTitel(e.target.value)}
/>
+1 -2
View File
@@ -17,7 +17,6 @@ const features = [
const demoZugaenge = [
{ benutzername: 'j.hinterstein', label: 'Joseph Hinterstein (Verwaltung)' },
{ benutzername: 'a.berger', label: 'Anna Berger (Mieterin)' },
]
export default function Login() {
@@ -84,7 +83,7 @@ export default function Login() {
<div className="absolute inset-0 bg-gradient-to-t from-[#22333b]/85 via-transparent to-transparent" />
<div className="absolute bottom-0 p-6 text-white">
<div className="text-lg font-semibold">Ihr Zuhause. Ihr Portal.</div>
<div className="text-sm text-slate-200">3 Wohnanlagen · 50 Wohnungen · persönliche Verwaltung</div>
<div className="text-sm text-slate-200">Ihr Bestand. Ihre Mieter. Ein Portal.</div>
</div>
</div>
+1 -1
View File
@@ -112,7 +112,7 @@ export default function Messages() {
<p className="text-sm text-slate-500">
{role === 'tenant'
? 'Direktdraht zur Verwaltung – Antwort meist innerhalb eines Werktags'
: 'Mieter-Kommunikation über alle 50 Wohnungen'}
: 'Mieter-Kommunikation über den gesamten Bestand'}
</p>
</div>
<NewMessageDialog onCreated={setActiveId} />
+5 -5
View File
@@ -138,7 +138,7 @@ function TenantPayments() {
}
function AdminPayments() {
const { units, transactions } = useStore()
const { units, transactions, buildings } = useStore()
const [building, setBuilding] = useState('alle')
const list = units
@@ -161,7 +161,7 @@ function AdminPayments() {
<div className="flex flex-wrap items-end justify-between gap-3">
<div>
<h1 className="text-2xl font-bold tracking-tight">Mietkonto & Zahlungen</h1>
<p className="text-sm text-slate-500">Forderungsmanagement für 50 Wohnungen</p>
<p className="text-sm text-slate-500">Forderungsmanagement für den gesamten Bestand</p>
</div>
<Select value={building} onValueChange={setBuilding}>
<SelectTrigger className="w-56">
@@ -169,9 +169,9 @@ function AdminPayments() {
</SelectTrigger>
<SelectContent>
<SelectItem value="alle">Alle Häuser</SelectItem>
<SelectItem value="Hauptstraße 12">Hauptstraße 12</SelectItem>
<SelectItem value="Ringweg 3">Ringweg 3</SelectItem>
<SelectItem value="Am Park 5">Am Park 5</SelectItem>
{buildings.map((b) => (
<SelectItem key={b.name} value={b.name}>{b.name}</SelectItem>
))}
</SelectContent>
</Select>
</div>
+2 -2
View File
@@ -46,14 +46,14 @@ const fmtStand = (n: number) =>
n.toLocaleString('de-DE', { maximumFractionDigits: 3 })
function ZaehlerDialog({ meter }: { meter?: Meter }) {
const { addZaehler, updateZaehler, units } = useStore()
const { addZaehler, updateZaehler, units, buildings } = useStore()
const bearbeiten = Boolean(meter)
const [open, setOpen] = useState(false)
const [nummer, setNummer] = useState(meter?.nummer ?? '')
const [art, setArt] = useState<ZaehlerArt>(meter?.art ?? 'Strom')
const [stand, setStand] = useState(String(meter?.stand ?? '').replace('.', ','))
const [unitId, setUnitId] = useState<string>(meter?.unitId ?? 'haus')
const [building, setBuilding] = useState(meter?.building ?? 'Hauptstraße 12')
const [building, setBuilding] = useState(meter?.building ?? buildings[0]?.name ?? '')
const [sende, setSende] = useState(false)
const [fehler, setFehler] = useState<string | null>(null)