From b91b302fe88a6b8e594255abc68d4d595051692f Mon Sep 17 00:00:00 2001 From: Linrador <68631622+Linrador@users.noreply.github.com> Date: Tue, 12 May 2026 15:44:31 +0200 Subject: [PATCH] updated --- frontend/src/components/DeviceModal.tsx | 388 ++++++++++++++++++++++ frontend/src/components/Modal.tsx | 35 ++ frontend/src/components/types.ts | 21 ++ frontend/src/pages/DevicesPage.tsx | 407 ++---------------------- 4 files changed, 476 insertions(+), 375 deletions(-) create mode 100644 frontend/src/components/DeviceModal.tsx diff --git a/frontend/src/components/DeviceModal.tsx b/frontend/src/components/DeviceModal.tsx new file mode 100644 index 0000000..04565ab --- /dev/null +++ b/frontend/src/components/DeviceModal.tsx @@ -0,0 +1,388 @@ +import type { FormEvent } from 'react' +import { DialogTitle } from '@headlessui/react' +import { XMarkIcon } from '@heroicons/react/24/outline' +import { PencilSquareIcon, PlusCircleIcon } from '@heroicons/react/20/solid' +import Modal from './Modal' +import Feed, { type FeedTimelineItem } from './Feed' +import Button from './Button' +import type { Device, DeviceHistoryChange, DeviceHistoryEntry } from './types' + +type DeviceModalProps = { + open: boolean + setOpen: (open: boolean) => void + isSaving: boolean + error: string | null + devices: Device[] + editingDevice: Device | null + relatedDeviceIds: string[] + onToggleRelatedDevice: (deviceId: string) => void + onSubmit: (event: FormEvent) => void + deviceHistory: DeviceHistoryEntry[] + isHistoryLoading: boolean + historyError: string | null +} + +const inputClassName = + 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500' + +const selectClassName = + 'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:*:bg-gray-800 dark:focus:outline-indigo-500' + +function classNames(...classes: Array) { + return classes.filter(Boolean).join(' ') +} + +function formatHistoryDate(value: string) { + try { + return new Intl.DateTimeFormat('de-DE', { + dateStyle: 'short', + timeStyle: 'short', + }).format(new Date(value)) + } catch { + return value + } +} + +function formatHistoryChange(change: DeviceHistoryChange) { + const oldValue = change.oldValue || '—' + const newValue = change.newValue || '—' + + return `${change.label}: ${oldValue} → ${newValue}` +} + +function getHistoryContent(entry: DeviceHistoryEntry) { + if (entry.action === 'created') { + return 'Gerät erstellt durch' + } + + if (!entry.changes.length) { + return 'Gerät bearbeitet durch' + } + + return `${entry.changes.map(formatHistoryChange).join(', ')} durch` +} + +export default function DeviceModal({ + open, + setOpen, + isSaving, + error, + devices, + editingDevice, + relatedDeviceIds, + onToggleRelatedDevice, + onSubmit, + deviceHistory, + isHistoryLoading, + historyError, +}: DeviceModalProps) { + const isEditing = editingDevice !== null + const assignableDevices = devices.filter((device) => device.id !== editingDevice?.id) + + const historyTimeline: FeedTimelineItem[] = deviceHistory.map((entry) => ({ + id: entry.id, + content: getHistoryContent(entry), + target: entry.userDisplayName || 'Unbekannt', + href: '#', + date: formatHistoryDate(entry.createdAt), + datetime: entry.createdAt, + icon: entry.action === 'created' ? PlusCircleIcon : PencilSquareIcon, + iconBackground: entry.action === 'created' ? 'bg-green-500' : 'bg-blue-500', + })) + + function handleOpenChange(nextOpen: boolean) { + if (!nextOpen && isSaving) { + return + } + + setOpen(nextOpen) + } + + return ( + +
+
+
+ + {isEditing ? 'Gerät bearbeiten' : 'Gerät hinzufügen'} + + +

+ {isEditing + ? 'Bearbeite Gerätedaten, Standort, Verleih-Status und Zubehör.' + : 'Erfasse ein neues Gerät inklusive Inventur-Nr., Standort und Zubehör.'} +

+
+ + +
+ + {error && ( +
+ {error} +
+ )} + +
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+