import { useState, useEffect, useCallback } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { databases, DATABASE_ID } from '@/lib/appwrite'; import { useAuth } from '@/context/AuthContext'; import { useAuditLog } from '@/hooks/useAuditLog'; import { useLagerstandorte } from '@/hooks/useLagerstandorte'; import { useColleagues } from '@/hooks/useColleagues'; import { getDaysOld, isOverdue } from '@/hooks/useAssets'; import { Card, CardHeader, CardTitle, CardContent, CardFooter } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { ArrowLeft, Pencil, Save, X } from 'lucide-react'; import { parseKommentarForDisplay } from '@/lib/kommentarAnhaenge'; import KommentarAnhaengeList from '@/components/KommentarAnhaengeList'; const STATUS_LABEL = { offen: 'Offen', in_bearbeitung: 'In Bearbeitung', entsorgt: 'Entsorgt' }; const PRIO_LABELS = { kritisch: 'Kritisch', hoch: 'Hoch', mittel: 'Mittel', niedrig: 'Niedrig' }; const PRIO_OPTIONS = ['kritisch', 'hoch', 'mittel', 'niedrig']; const STATUS_OPTIONS = ['offen', 'in_bearbeitung', 'entsorgt']; const BEARB_STATUS_LABELS = { '': 'Nicht gesetzt', portalpruefung: '\u{1F50D} Portalprüfung durchführen', gutschreiben_entsorgen: '\u267B\uFE0F Direkt gutschreiben & entsorgen', zurueck_hersteller: '\u{1F4E6} Zurück an Hersteller senden', defekt_ankunft: '\u26A0\uFE0F Defekt bei Ankunft melden', }; const BEARB_STATUS_OPTIONS = ['', 'portalpruefung', 'gutschreiben_entsorgen', 'zurueck_hersteller', 'defekt_ankunft']; function formatTimestamp(ts) { if (!ts) return '–'; const d = new Date(ts); return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) + ' ' + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } function StatusBadge({ status }) { if (status === 'offen') return {STATUS_LABEL[status]}; if (status === 'in_bearbeitung') return {STATUS_LABEL[status]}; return {STATUS_LABEL[status]}; } function PrioBadge({ prio }) { if (prio === 'kritisch') return {PRIO_LABELS[prio]}; if (prio === 'hoch') return {PRIO_LABELS[prio]}; if (prio === 'mittel') return {PRIO_LABELS[prio]}; return {PRIO_LABELS[prio]}; } function KommentarReadonly({ value }) { const { subject, body, attachments } = parseKommentarForDisplay(value); const empty = !subject && !(body && body.trim()) && attachments.length === 0; if (empty) return

; return (
{subject && (
{subject}
)} {body && body.trim() ? (

{body}

) : null}
); } export default function AssetDetail() { const { id } = useParams(); const navigate = useNavigate(); const { user, userMeta } = useAuth(); const { logs, loadingLogs, loadLogs, addLog } = useAuditLog(); const locationId = userMeta?.locationId || ''; const { activeLagerstandorte } = useLagerstandorte(locationId); const { colleagues } = useColleagues(locationId); const [asset, setAsset] = useState(null); const [loading, setLoading] = useState(true); const [editing, setEditing] = useState(false); const [saving, setSaving] = useState(false); const [form, setForm] = useState({}); const loadAsset = useCallback(async () => { try { const doc = await databases.getDocument(DATABASE_ID, 'assets', id); setAsset(doc); setForm({ erlNummer: doc.erlNummer || '', seriennummer: doc.seriennummer || '', artikelNr: doc.artikelNr || '', bezeichnung: doc.bezeichnung || '', defekt: doc.defekt || '', lagerstandortId: doc.lagerstandortId || '', zustaendig: doc.zustaendig || '', status: doc.status || 'offen', prio: doc.prio || 'mittel', bearbeitungsStatus: doc.bearbeitungsStatus || '', kommentar: doc.kommentar || '', }); } catch (err) { console.error('Asset laden fehlgeschlagen:', err); setAsset(null); } finally { setLoading(false); } }, [id]); useEffect(() => { loadAsset(); loadLogs(id); }, [loadAsset, loadLogs, id]); const userName = user?.name || user?.email || 'Unbekannt'; function buildChangeDetails(oldAsset, newForm) { const fields = [ { key: 'erlNummer', label: 'ERL-Nr.' }, { key: 'seriennummer', label: 'Seriennummer' }, { key: 'artikelNr', label: 'Artikelnr.' }, { key: 'bezeichnung', label: 'Bezeichnung' }, { key: 'defekt', label: 'Defekt' }, { key: 'lagerstandortId', label: 'Lagerstandort' }, { key: 'zustaendig', label: 'Zuständig' }, { key: 'status', label: 'Status' }, { key: 'prio', label: 'Priorität' }, { key: 'bearbeitungsStatus', label: 'Bearbeitungsstatus' }, { key: 'kommentar', label: 'Kommentar' }, ]; const changes = []; for (const f of fields) { const oldVal = oldAsset[f.key] || ''; const newVal = newForm[f.key] || ''; if (oldVal !== newVal) { if (f.key === 'status') { changes.push(`${f.label}: ${STATUS_LABEL[oldVal] || oldVal} → ${STATUS_LABEL[newVal] || newVal}`); } else if (f.key === 'bearbeitungsStatus') { changes.push(`${f.label}: ${BEARB_STATUS_LABELS[oldVal] || oldVal || 'Nicht gesetzt'} → ${BEARB_STATUS_LABELS[newVal] || newVal || 'Nicht gesetzt'}`); } else if (f.key === 'prio') { changes.push(`${f.label}: ${PRIO_LABELS[oldVal] || oldVal} → ${PRIO_LABELS[newVal] || newVal}`); } else { changes.push(`${f.label}: "${oldVal}" → "${newVal}"`); } } } return changes.join('; '); } async function handleSave() { if (!asset) return; setSaving(true); try { const changeDetails = buildChangeDetails(asset, form); if (!changeDetails) { setEditing(false); setSaving(false); return; } const updated = await databases.updateDocument(DATABASE_ID, 'assets', id, { ...form, lastEditedBy: userName, }); setAsset(updated); let logDetails = changeDetails; if (asset.zustaendig !== form.zustaendig && form.zustaendig) { const isSelf = form.zustaendig === userName; const reassignInfo = isSelf ? `${userName} hat sich das Asset selbst zugewiesen` : `${userName} hat das Asset ${form.zustaendig} zugewiesen`; logDetails = reassignInfo + (changeDetails.replace(/Zuständig:[^;]*;?\s?/, '').trim() ? '; ' + changeDetails.replace(/Zuständig:[^;]*;?\s?/, '').trim() : ''); } await addLog({ assetId: id, action: 'bearbeitet', details: logDetails, userId: user.$id, userName, }); setEditing(false); } catch (err) { console.error('Speichern fehlgeschlagen:', err); alert('Speichern fehlgeschlagen: ' + (err.message || err)); } finally { setSaving(false); } } function resetForm() { setEditing(false); setForm({ erlNummer: asset.erlNummer || '', seriennummer: asset.seriennummer || '', artikelNr: asset.artikelNr || '', bezeichnung: asset.bezeichnung || '', defekt: asset.defekt || '', lagerstandortId: asset.lagerstandortId || '', zustaendig: asset.zustaendig || '', status: asset.status || 'offen', prio: asset.prio || 'mittel', bearbeitungsStatus: asset.bearbeitungsStatus || '', kommentar: asset.kommentar || '', }); } if (loading) { return (
); } if (!asset) { return (

Asset nicht gefunden

Das Asset mit der ID {id} existiert nicht.

); } const days = getDaysOld(asset.$createdAt); const overdue = isOverdue(asset); return (
{/* Back button */} {/* Header area */}

Asset: {asset.erlNummer || '–'}

{overdue && ( Überfällig ({days} Tage) )}
{/* Properties card */} Eigenschaften
{!editing ? ( ) : ( <> )}
setForm(f => ({ ...f, erlNummer: v }))} /> setForm(f => ({ ...f, artikelNr: v }))} /> setForm(f => ({ ...f, bezeichnung: v }))} /> setForm(f => ({ ...f, seriennummer: v }))} mono /> setForm(f => ({ ...f, defekt: v }))} textarea className="sm:col-span-2" /> {/* Lagerstandort */}
{editing ? ( ) : (

{activeLagerstandorte.find(l => l.$id === asset.lagerstandortId)?.name || '–'}

)}
{/* Zuständig */}
{editing ? ( ) : (

{asset.zustaendig || '–'}

)}
{/* Status */}
{editing ? ( ) : ( )}
{/* Priorität */}
{editing ? ( ) : ( )}
{(form.status === 'in_bearbeitung' || asset.bearbeitungsStatus) && (
{editing ? ( ) : (

{BEARB_STATUS_LABELS[asset.bearbeitungsStatus || ''] || '–'}

)}
)}
{editing ? (