import { useMemo, useRef, useState } from 'react' import { Download, FileText, FolderOpen, Scale, ReceiptText, FileSignature, FileIcon, Plus, Pencil, Trash2, Upload, Eye } from 'lucide-react' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogFooter, } from '@/components/ui/dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { useStore } from '../store/store' import PdfLightbox from '../components/PdfLightbox' import { apiUpload, ApiFehler } from '../lib/api' import type { DocumentItem, Unit } from '../types' const KATEGORIEN: DocumentItem['category'][] = ['Mietvertrag', 'Nebenkostenabrechnung', 'Hausordnung', 'Protokoll', 'Sonstiges'] const catIcon = (cat: string) => { switch (cat) { case 'Mietvertrag': return case 'Nebenkostenabrechnung': return case 'Hausordnung': return case 'Protokoll': return default: return } } const heute = () => new Date().toLocaleDateString('de-DE') function DokumentDialog({ dokument, einheiten }: { dokument?: DocumentItem; einheiten: Unit[] }) { const { addDokument, updateDokument, syncDokument } = useStore() const bearbeiten = Boolean(dokument) const [open, setOpen] = useState(false) const [titel, setTitel] = useState(dokument?.title ?? '') const [kategorie, setKategorie] = useState(dokument?.category ?? 'Mietvertrag') const [einheit, setEinheit] = useState(dokument?.unitId ?? '__alle') // '__alle' = Alle Häuser const [datum, setDatum] = useState(dokument?.date ?? heute()) const [groesse, setGroesse] = useState(dokument?.size === '–' ? '' : (dokument?.size ?? '')) const [datei, setDatei] = useState(null) const dateiInput = useRef(null) const [sende, setSende] = useState(false) const [fehler, setFehler] = useState(null) const dateiWaehlen = (f: File | null) => { setFehler(null) if (!f) return setDatei(null) const istPdf = f.type === 'application/pdf' || /\.pdf$/i.test(f.name) if (!istPdf) { dateiInput.current && (dateiInput.current.value = '') return setFehler('Nur PDF-Dateien sind erlaubt.') } if (f.size > 25 * 1024 * 1024) { dateiInput.current && (dateiInput.current.value = '') return setFehler('Die Datei ist größer als 25 MB.') } setDatei(f) } const submit = async () => { if (sende) return setSende(true) setFehler(null) const payload = { title: titel.trim(), category: kategorie, unitId: einheit === '__alle' ? null : einheit, date: datum.trim() || heute(), ...(groesse.trim() ? { size: groesse.trim() } : {}), } let dokId: string if (bearbeiten) { const err = await updateDokument(dokument!.id, payload) if (err) { setSende(false); return setFehler(err) } dokId = dokument!.id } else { const r = await addDokument(payload) if (r.fehler) { setSende(false); return setFehler(r.fehler) } dokId = r.id! } if (datei) { try { const { dokument: hochgeladen } = await apiUpload<{ dokument: DocumentItem }>( `/api/documents/${dokId}/upload`, datei, ) syncDokument(hochgeladen) // Größe/Dateiname kommen aus der Upload-Antwort } catch (e) { setSende(false) return setFehler(e instanceof ApiFehler ? e.message : 'Der PDF-Upload ist fehlgeschlagen.') } } setSende(false) setOpen(false) } return ( {bearbeiten ? ( ) : ( )} {bearbeiten ? 'Dokument bearbeiten' : 'Neues Dokument anlegen'}
setTitel(e.target.value)} />
setDatum(e.target.value)} />
setGroesse(e.target.value)} />
dateiWaehlen(e.target.files?.[0] ?? null)} /> {datei && ( )}
{bearbeiten && dokument?.hasFile && !datei && (

Bereits hinterlegt: {dokument.fileName ?? 'PDF'} – ein neuer Upload ersetzt die Datei.

)} {datei && (

{datei.name} wird beim Speichern hochgeladen.

)}
{fehler &&

{fehler}

}
) } export default function Documents() { const { documents, role, units, meineEinheit, deleteDokument } = useStore() const [query, setQuery] = useState('') const rows = useMemo(() => { const base = role === 'tenant' ? documents.filter((d) => d.unitId === meineEinheit.id || d.unitId === null) : documents const q = query.trim().toLowerCase() return q ? base.filter( (d) => d.title.toLowerCase().includes(q) || d.category.toLowerCase().includes(q) || (units.find((u) => u.id === d.unitId)?.building ?? '').toLowerCase().includes(q), ) : base }, [documents, role, query, units, meineEinheit.id]) const cats = ['Alle', ...Array.from(new Set(rows.map((d) => d.category)))] const [cat, setCat] = useState('Alle') const filtered = cat === 'Alle' ? rows : rows.filter((d) => d.category === cat) const loeschen = async (d: DocumentItem) => { if (!window.confirm(`Dokument „${d.title}" wirklich löschen?`)) return await deleteDokument(d.id) } return (

Dokumente

{role === 'tenant' ? 'Verträge, Abrechnungen und Unterlagen für Ihre Wohnung' : 'Zentraler Dokumentenspeicher des gesamten Bestands – Verträge, Abrechnungen und Unterlagen'}

setQuery(e.target.value)} className="w-64" /> {role === 'admin' && }
{cats.map((c) => ( ))}
{filtered.length === 0 ? (
Keine Dokumente gefunden.
) : ( Dokument {role === 'admin' && Wohnung} Kategorie Datum Größe Aktion {filtered.map((d) => ( {catIcon(d.category)} {d.title} {role === 'admin' && ( {d.unitId ? (() => { const u = units.find((x) => x.id === d.unitId) return u ? `${u.building}, Whg. ${u.number}` : d.unitId })() : 'Alle Häuser'} )} {d.category} {d.date} {d.size}
{d.hasFile ? ( <> Vorschau } /> PDF ) : ( PDF )} {role === 'admin' && ( <> )}
))}
)}

Alle Dokumente werden verschlüsselt gespeichert und sind nur für berechtigte Konten einsehbar. Die Verwaltung lädt Vertrags-PDFs hoch; Mieter laden sie hier direkt herunter.

) }