'use client' import React, { useState, useMemo, useCallback } from 'react' import { type InformationAsset, type AssetCategory, type AssetClassification, type ProtectionLevel, ASSET_CATEGORY_LABELS, CLASSIFICATION_LABELS, PROTECTION_LABELS, } from '../_types' // ============================================================================ // Local storage key (persisted in SDK state via JSONB) // ============================================================================ const STORAGE_KEY = 'isms_assets' function loadAssets(): InformationAsset[] { try { const raw = localStorage.getItem(STORAGE_KEY) return raw ? JSON.parse(raw) : [] } catch { return [] } } function saveAssets(assets: InformationAsset[]) { localStorage.setItem(STORAGE_KEY, JSON.stringify(assets)) } // ============================================================================ // Protection level colors // ============================================================================ const protectionColors: Record = { normal: 'bg-green-100 text-green-800', high: 'bg-amber-100 text-amber-800', very_high: 'bg-red-100 text-red-800', } const classificationColors: Record = { PUBLIC: 'bg-gray-100 text-gray-600', INTERNAL: 'bg-blue-100 text-blue-700', CONFIDENTIAL: 'bg-amber-100 text-amber-800', STRICTLY_CONFIDENTIAL: 'bg-red-100 text-red-800', } // ============================================================================ // Component // ============================================================================ export function AssetsTab() { const [assets, setAssets] = useState(() => loadAssets()) const [showForm, setShowForm] = useState(false) const [filterCategory, setFilterCategory] = useState('ALL') const [editingId, setEditingId] = useState(null) // Form state const [form, setForm] = useState>({ category: 'SOFTWARE', classification: 'INTERNAL', protectionNeed: { confidentiality: 'normal', integrity: 'normal', availability: 'normal' }, }) const filtered = useMemo(() => { if (filterCategory === 'ALL') return assets return assets.filter((a) => a.category === filterCategory) }, [assets, filterCategory]) const stats = useMemo(() => ({ total: assets.length, byCategory: Object.entries(ASSET_CATEGORY_LABELS).map(([cat, label]) => ({ category: cat, label, count: assets.filter((a) => a.category === cat).length, })), highProtection: assets.filter( (a) => a.protectionNeed.confidentiality === 'very_high' || a.protectionNeed.integrity === 'very_high' || a.protectionNeed.availability === 'very_high' ).length, }), [assets]) const handleSave = useCallback(() => { if (!form.name || !form.category || !form.owner) return const now = new Date().toISOString() const asset: InformationAsset = { id: editingId || `asset_${Date.now()}`, name: form.name || '', category: form.category as AssetCategory, description: form.description || '', owner: form.owner || '', location: form.location || '', classification: form.classification as AssetClassification || 'INTERNAL', protectionNeed: form.protectionNeed || { confidentiality: 'normal', integrity: 'normal', availability: 'normal' }, vendor: form.vendor, notes: form.notes, createdAt: editingId ? (assets.find((a) => a.id === editingId)?.createdAt || now) : now, updatedAt: now, } const updated = editingId ? assets.map((a) => (a.id === editingId ? asset : a)) : [...assets, asset] setAssets(updated) saveAssets(updated) setShowForm(false) setEditingId(null) setForm({ category: 'SOFTWARE', classification: 'INTERNAL', protectionNeed: { confidentiality: 'normal', integrity: 'normal', availability: 'normal' }, }) }, [form, editingId, assets]) const handleDelete = useCallback((id: string) => { const updated = assets.filter((a) => a.id !== id) setAssets(updated) saveAssets(updated) }, [assets]) const handleEdit = useCallback((asset: InformationAsset) => { setForm(asset) setEditingId(asset.id) setShowForm(true) }, []) const handleExport = useCallback(() => { const csv = [ ['Name', 'Kategorie', 'Eigentuemer', 'Standort', 'Klassifizierung', 'C', 'I', 'A', 'Beschreibung'].join(';'), ...assets.map((a) => [a.name, ASSET_CATEGORY_LABELS[a.category], a.owner, a.location, CLASSIFICATION_LABELS[a.classification], PROTECTION_LABELS[a.protectionNeed.confidentiality], PROTECTION_LABELS[a.protectionNeed.integrity], PROTECTION_LABELS[a.protectionNeed.availability], a.description].join(';') ), ].join('\n') const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `asset-register-${new Date().toISOString().slice(0, 10)}.csv` a.click() URL.revokeObjectURL(url) }, [assets]) return (
{/* Stats */}
Gesamt
{stats.total}
{stats.byCategory.filter((s) => s.count > 0).slice(0, 2).map((s) => (
{s.label}
{s.count}
))}
Sehr hoher Schutzbedarf
{stats.highProtection}
{/* Actions */}
{(['ALL', ...Object.keys(ASSET_CATEGORY_LABELS)] as const).map((cat) => ( ))}
{/* Form */} {showForm && (

{editingId ? 'Asset bearbeiten' : 'Neues Asset'}

setForm({ ...form, name: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm" placeholder="z.B. PostgreSQL Produktions-DB" />
setForm({ ...form, owner: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm" placeholder="Person oder Abteilung" />
setForm({ ...form, location: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm" placeholder="z.B. Hetzner Cloud EU" />
setForm({ ...form, vendor: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm" placeholder="Optional" />
{/* Protection need */}
{(['confidentiality', 'integrity', 'availability'] as const).map((dim) => (
))}