// app/(app)/devices/DeviceEditModal.tsx 'use client'; import { useCallback, useEffect, useState, ChangeEvent, Dispatch, SetStateAction } from 'react'; import Modal from '@/components/ui/Modal'; import { PencilIcon } from '@heroicons/react/24/outline'; import DeviceHistorySidebar from './DeviceHistorySidebar'; import TagMultiCombobox, { TagOption } from '@/components/ui/TagMultiCombobox'; import type { DeviceDetail } from './page'; // Typ aus page.tsx (siehe unten) type DeviceEditModalProps = { open: boolean; inventoryNumber: string | null; onClose: () => void; onSaved: (device: DeviceDetail) => void; allTags: TagOption[]; setAllTags: Dispatch>; }; export default function DeviceEditModal({ open, inventoryNumber, onClose, onSaved, allTags, setAllTags, }: DeviceEditModalProps) { const [editDevice, setEditDevice] = useState(null); const [editLoading, setEditLoading] = useState(false); const [editError, setEditError] = useState(null); const [saveLoading, setSaveLoading] = useState(false); // Gerät laden, wenn Modal geöffnet wird useEffect(() => { if (!open || !inventoryNumber) { setEditDevice(null); setEditError(null); return; } let cancelled = false; async function loadDevice() { setEditLoading(true); setEditError(null); setEditDevice(null); try { const res = await fetch( `/api/devices/${encodeURIComponent(inventoryNumber)}`, { method: 'GET', headers: { 'Content-Type': 'application/json' }, cache: 'no-store', }, ); if (!res.ok) { if (res.status === 404) { throw new Error('Gerät wurde nicht gefunden.'); } throw new Error('Beim Laden der Gerätedaten ist ein Fehler aufgetreten.'); } const data = (await res.json()) as DeviceDetail; if (!cancelled) { setEditDevice(data); } } catch (err: any) { console.error('Error loading device', err); if (!cancelled) { setEditError( err instanceof Error ? err.message : 'Netzwerkfehler beim Laden der Gerätedaten.', ); } } finally { if (!cancelled) { setEditLoading(false); } } } loadDevice(); return () => { cancelled = true; }; }, [open, inventoryNumber]); const handleFieldChange = ( field: keyof DeviceDetail, e: ChangeEvent, ) => { const value = e.target.value; setEditDevice((prev) => prev ? ({ ...prev, [field]: value } as DeviceDetail) : prev, ); }; const handleSave = useCallback(async () => { if (!editDevice) return; setSaveLoading(true); setEditError(null); try { const res = await fetch( `/api/devices/${encodeURIComponent(editDevice.inventoryNumber)}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: editDevice.name, manufacturer: editDevice.manufacturer, model: editDevice.model, serialNumber: editDevice.serialNumber || null, productNumber: editDevice.productNumber || null, comment: editDevice.comment || null, group: editDevice.group || null, location: editDevice.location || null, ipv4Address: editDevice.ipv4Address || null, ipv6Address: editDevice.ipv6Address || null, macAddress: editDevice.macAddress || null, username: editDevice.username || null, passwordHash: editDevice.passwordHash || null, tags: editDevice.tags ?? [], }), }, ); if (!res.ok) { if (res.status === 404) { throw new Error('Gerät wurde nicht gefunden.'); } throw new Error('Speichern der Änderungen ist fehlgeschlagen.'); } const updated = (await res.json()) as DeviceDetail; setEditDevice(updated); onSaved(updated); // Tabelle im Parent aktualisieren } catch (err: any) { console.error('Error saving device', err); setEditError( err instanceof Error ? err.message : 'Netzwerkfehler beim Speichern der Gerätedaten.', ); } finally { setSaveLoading(false); } }, [editDevice, onSaved]); const handleClose = () => { if (saveLoading) return; onClose(); }; return ( } tone="info" variant="centered" size="lg" primaryAction={{ label: saveLoading ? 'Speichern …' : 'Speichern', onClick: handleSave, autoFocus: true, }} secondaryAction={{ label: 'Abbrechen', variant: 'secondary', onClick: handleClose, }} sidebar={ editDevice ? ( ) : undefined } > {editLoading && (

Gerätedaten werden geladen …

)} {editError && (

{editError}

)} {!editLoading && !editError && editDevice && (
{/* Inventarnummer */}

Inventar-Nr.

{/* Bezeichnung */}

Bezeichnung

handleFieldChange('name', e)} />
{/* Hersteller / Modell */}

Hersteller

handleFieldChange('manufacturer', e)} />

Modell

handleFieldChange('model', e)} />
{/* Seriennummer / Produktnummer */}

Seriennummer

handleFieldChange('serialNumber', e)} />

Produktnummer

handleFieldChange('productNumber', e)} />
{/* Standort / Gruppe */}

Standort / Raum

handleFieldChange('location', e)} />

Gruppe

handleFieldChange('group', e)} />
{/* Tags */}
({ name }))} onChange={(next) => { const names = next.map((t) => t.name); // in editDevice speichern setEditDevice((prev) => prev ? ({ ...prev, tags: names } as DeviceDetail) : prev, ); // allTags im Parent erweitern setAllTags((prev) => { const map = new Map(prev.map((t) => [t.name.toLowerCase(), t])); for (const t of next) { const key = t.name.toLowerCase(); if (!map.has(key)) { map.set(key, t); } } return Array.from(map.values()); }); }} placeholder="z.B. Drucker, Serverraum, kritisch" />
{/* Netzwerkdaten */}

IPv4-Adresse

handleFieldChange('ipv4Address', e)} />

IPv6-Adresse

handleFieldChange('ipv6Address', e)} />

MAC-Adresse

handleFieldChange('macAddress', e)} />

Benutzername

handleFieldChange('username', e)} />

Passwort

handleFieldChange('passwordHash', e)} />
{/* Kommentar */}

Kommentar