// app/(app)/devices/DeviceDetailModal.tsx 'use client'; import { useEffect, useState } from 'react'; import Modal from '@/components/ui/Modal'; import { BookOpenIcon } from '@heroicons/react/24/outline'; import DeviceHistorySidebar from './DeviceHistorySidebar'; import Button from '@/components/ui/Button'; import type { DeviceDetail } from './page'; import { DeviceQrCode } from '@/components/DeviceQrCode'; import Tabs from '@/components/ui/Tabs'; import LoanDeviceModal from './LoanDeviceModal'; type DeviceDetailModalProps = { open: boolean; inventoryNumber: string | null; onClose: () => void; /** Darf der aktuelle Benutzer Geräte bearbeiten? */ canEdit?: boolean; /** Wird aufgerufen, wenn im Detail-Modal "Bearbeiten" geklickt wird */ onEdit?: (inventoryNumber: string) => void; }; const dtf = new Intl.DateTimeFormat('de-DE', { dateStyle: 'short', timeStyle: 'short', }); type DeviceDetailsGridProps = { device: DeviceDetail; onStartLoan?: () => void; /** Darf der aktuelle Benutzer Geräte bearbeiten? */ canEdit?: boolean; /** Wird ausgelöst, wenn auf "Bearbeiten" geklickt wird */ onEdit?: () => void; }; function DeviceDetailsGrid({ device, onStartLoan, canEdit, onEdit, }: DeviceDetailsGridProps) { const [activeSection, setActiveSection] = useState<'info' | 'zubehoer'>('info'); const hasParent = !!device.parentInventoryNumber; // 👉 accessories defensiv normalisieren const accessories = Array.isArray(device.accessories) ? device.accessories : []; const hasAccessories = accessories.length > 0; const showAccessoryTab = hasParent || hasAccessories; const isLoaned = Boolean(device.loanedTo); const now = new Date(); const isOverdue = isLoaned && device.loanedUntil != null && new Date(device.loanedUntil) < now; const statusLabel = !isLoaned ? 'Verfügbar' : isOverdue ? 'Verliehen (überfällig)' : 'Verliehen'; const statusClasses = !isLoaned ? 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-100' : isOverdue ? 'bg-rose-100 text-rose-800 dark:bg-rose-900/40 dark:text-rose-100' : 'bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-100'; const dotClasses = !isLoaned ? 'bg-emerald-500' : isOverdue ? 'bg-rose-500' : 'bg-amber-500'; // 🔹 Nur Zubehör-Zeilen, die wir wirklich anzeigen const accessoryRows: { inventoryNumber: string; name: string | null }[] = [ // Wenn dieses Gerät selbst Zubehör ist → sich selbst anzeigen ...(hasParent ? [ { inventoryNumber: device.inventoryNumber, name: device.name ?? null, }, ] : []), // Wenn dieses Gerät Hauptgerät ist → alle Kinder anzeigen ...accessories.map((acc) => ({ inventoryNumber: acc.inventoryNumber, name: acc.name ?? null, })), ]; return (
Inventar-Nr.
{device.inventoryNumber}
Status
Bezeichnung
{device.name || '–'}
Hersteller
{device.manufacturer || '–'}
Modell
{device.model || '–'}
Seriennummer
{device.serialNumber || '–'}
Produktnummer
{device.productNumber || '–'}
Standort / Raum
{device.location || '–'}
Gruppe
{device.group || '–'}
IPv4-Adresse
{device.ipv4Address || '–'}
IPv6-Adresse
{device.ipv6Address || '–'}
MAC-Adresse
{device.macAddress || '–'}
Benutzername
{device.username || '–'}
Passwort (Hash)
{device.passwordHash || '–'}
Tags
{device.tags && device.tags.length > 0 ? (–
)}Kommentar
Angelegt am
{device.createdAt ? dtf.format(new Date(device.createdAt)) : '–'}
Zuletzt geändert am
{device.updatedAt ? dtf.format(new Date(device.updatedAt)) : '–'}
Zubehör
| Inventar-Nr. | Bezeichnung |
|---|---|
| {row.inventoryNumber} | {row.name || '–'} |
Dieses Gerät ist Zubehör zu einem Hauptgerät, hat aber selbst kein weiteres Zubehör.
)} {hasAccessories && !hasParent && (Dieses Gerät ist ein Hauptgerät und besitzt die oben aufgeführten Zubehör-Geräte.
)}{device.inventoryNumber}
Gerätedaten werden geladen …
)} {error && ({error}
)} {!loading && !error && device && ( <> {/* Mobile-Inhalt (Tabs steuern Ansicht) */}