'use client' /** * DataPointCatalog Component * * Zeigt den vollstaendigen Datenpunktkatalog an. * Ermoeglicht Filterung, Suche und Auswahl von Datenpunkten. * Unterstützt 18 Kategorien (A-R) inkl. Art. 9 DSGVO Warnungen. */ import { useState, useMemo } from 'react' import { Search, Filter, CheckCircle, Circle, Shield, AlertTriangle, ChevronDown, ChevronRight, Key, Megaphone, MessageSquare, CreditCard, Users, Bot, Lock, User, Mail, Activity, MapPin, Smartphone, BarChart3, Share2, Heart, Briefcase, FileText, FileCode, Info, X, } from 'lucide-react' import { DataPoint, DataPointCategory, RiskLevel, LegalBasis, SupportedLanguage, CATEGORY_METADATA, RISK_LEVEL_STYLING, LEGAL_BASIS_INFO, RETENTION_PERIOD_INFO, ARTICLE_9_WARNING, EMPLOYEE_DATA_WARNING, AI_DATA_WARNING, } from '@/lib/sdk/einwilligungen/types' import { searchDataPoints } from '@/lib/sdk/einwilligungen/catalog/loader' // ============================================================================= // TYPES // ============================================================================= interface DataPointCatalogProps { dataPoints: DataPoint[] selectedIds: string[] onToggle: (id: string) => void onSelectAll?: () => void onDeselectAll?: () => void language?: SupportedLanguage showFilters?: boolean readOnly?: boolean } // ============================================================================= // HELPER COMPONENTS // ============================================================================= const CategoryIcon: React.FC<{ category: DataPointCategory; className?: string }> = ({ category, className = 'w-5 h-5', }) => { const icons: Record = { // 18 Kategorien (A-R) MASTER_DATA: , CONTACT_DATA: , AUTHENTICATION: , CONSENT: , COMMUNICATION: , PAYMENT: , USAGE_DATA: , LOCATION: , DEVICE_DATA: , MARKETING: , ANALYTICS: , SOCIAL_MEDIA: , HEALTH_DATA: , EMPLOYEE_DATA: , CONTRACT_DATA: , LOG_DATA: , AI_DATA: , SECURITY: , } return <>{icons[category] || } } const RiskBadge: React.FC<{ level: RiskLevel; language: SupportedLanguage }> = ({ level, language, }) => { const styling = RISK_LEVEL_STYLING[level] return ( {styling.label[language]} ) } const LegalBasisBadge: React.FC<{ basis: LegalBasis; language: SupportedLanguage }> = ({ basis, language, }) => { const info = LEGAL_BASIS_INFO[basis] const colors: Record = { CONTRACT: 'bg-blue-100 text-blue-700', CONSENT: 'bg-purple-100 text-purple-700', EXPLICIT_CONSENT: 'bg-rose-100 text-rose-700', LEGITIMATE_INTEREST: 'bg-amber-100 text-amber-700', LEGAL_OBLIGATION: 'bg-slate-100 text-slate-700', VITAL_INTERESTS: 'bg-emerald-100 text-emerald-700', PUBLIC_INTEREST: 'bg-cyan-100 text-cyan-700', } return ( {info?.name[language] || basis} ) } /** * Warnung fuer besondere Kategorien (Art. 9 DSGVO, BDSG § 26, AI Act) */ const SpecialCategoryWarning: React.FC<{ category: DataPointCategory language: SupportedLanguage onClose?: () => void }> = ({ category, language, onClose }) => { // Bestimme welche Warnung angezeigt werden soll let warning = null let bgColor = '' let borderColor = '' let iconColor = '' if (category === 'HEALTH_DATA') { warning = ARTICLE_9_WARNING bgColor = 'bg-rose-50' borderColor = 'border-rose-200' iconColor = 'text-rose-600' } else if (category === 'EMPLOYEE_DATA') { warning = EMPLOYEE_DATA_WARNING bgColor = 'bg-orange-50' borderColor = 'border-orange-200' iconColor = 'text-orange-600' } else if (category === 'AI_DATA') { warning = AI_DATA_WARNING bgColor = 'bg-fuchsia-50' borderColor = 'border-fuchsia-200' iconColor = 'text-fuchsia-600' } if (!warning) return null return (

{warning.title[language]}

{onClose && ( )}

{warning.description[language]}

    {warning.requirements.map((req, idx) => (
  • {req[language]}
  • ))}
) } /** * Inline-Hinweis fuer Art. 9 Datenpunkte */ const Article9Badge: React.FC<{ language: SupportedLanguage }> = ({ language }) => ( {language === 'de' ? 'Art. 9 DSGVO' : 'Art. 9 GDPR'} ) // ============================================================================= // MAIN COMPONENT // ============================================================================= export function DataPointCatalog({ dataPoints, selectedIds, onToggle, onSelectAll, onDeselectAll, language = 'de', showFilters = true, readOnly = false, }: DataPointCatalogProps) { // Alle 18 Kategorien in der richtigen Reihenfolge (A-R) const ALL_CATEGORIES: DataPointCategory[] = [ 'MASTER_DATA', // A 'CONTACT_DATA', // B 'AUTHENTICATION', // C 'CONSENT', // D 'COMMUNICATION', // E 'PAYMENT', // F 'USAGE_DATA', // G 'LOCATION', // H 'DEVICE_DATA', // I 'MARKETING', // J 'ANALYTICS', // K 'SOCIAL_MEDIA', // L 'HEALTH_DATA', // M - Art. 9 DSGVO 'EMPLOYEE_DATA', // N - BDSG § 26 'CONTRACT_DATA', // O 'LOG_DATA', // P 'AI_DATA', // Q - AI Act 'SECURITY', // R ] // State const [searchQuery, setSearchQuery] = useState('') const [expandedCategories, setExpandedCategories] = useState>( new Set(ALL_CATEGORIES) ) const [filterCategory, setFilterCategory] = useState('ALL') const [filterRisk, setFilterRisk] = useState('ALL') const [filterBasis, setFilterBasis] = useState('ALL') const [dismissedWarnings, setDismissedWarnings] = useState>(new Set()) // Filtered and searched data points const filteredDataPoints = useMemo(() => { let result = dataPoints // Search if (searchQuery.trim()) { result = searchDataPoints(result, searchQuery, language) } // Filter by category if (filterCategory !== 'ALL') { result = result.filter((dp) => dp.category === filterCategory) } // Filter by risk if (filterRisk !== 'ALL') { result = result.filter((dp) => dp.riskLevel === filterRisk) } // Filter by legal basis if (filterBasis !== 'ALL') { result = result.filter((dp) => dp.legalBasis === filterBasis) } return result }, [dataPoints, searchQuery, filterCategory, filterRisk, filterBasis, language]) // Group by category (18 Kategorien) const groupedDataPoints = useMemo(() => { const grouped = new Map() for (const cat of ALL_CATEGORIES) { grouped.set(cat, []) } for (const dp of filteredDataPoints) { const existing = grouped.get(dp.category) || [] grouped.set(dp.category, [...existing, dp]) } return grouped }, [filteredDataPoints]) // Zaehle ausgewaehlte spezielle Kategorien fuer Warnungen const selectedSpecialCategories = useMemo(() => { const special = new Set() for (const id of selectedIds) { const dp = dataPoints.find(d => d.id === id) if (dp) { if (dp.category === 'HEALTH_DATA' || dp.isSpecialCategory) { special.add('HEALTH_DATA') } if (dp.category === 'EMPLOYEE_DATA') { special.add('EMPLOYEE_DATA') } if (dp.category === 'AI_DATA') { special.add('AI_DATA') } } } return special }, [selectedIds, dataPoints]) // Toggle category expansion const toggleCategory = (category: DataPointCategory) => { setExpandedCategories((prev) => { const next = new Set(prev) if (next.has(category)) { next.delete(category) } else { next.add(category) } return next }) } // Stats const totalSelected = selectedIds.length const totalDataPoints = dataPoints.length return (
{/* Header with Stats */}
{totalSelected} von{' '} {totalDataPoints} Datenpunkte ausgewaehlt
{!readOnly && (
|
)}
{/* Art. 9 DSGVO / BDSG § 26 / AI Act Warnungen */} {selectedSpecialCategories.size > 0 && (
{selectedSpecialCategories.has('HEALTH_DATA') && !dismissedWarnings.has('HEALTH_DATA') && ( setDismissedWarnings(prev => new Set([...prev, 'HEALTH_DATA']))} /> )} {selectedSpecialCategories.has('EMPLOYEE_DATA') && !dismissedWarnings.has('EMPLOYEE_DATA') && ( setDismissedWarnings(prev => new Set([...prev, 'EMPLOYEE_DATA']))} /> )} {selectedSpecialCategories.has('AI_DATA') && !dismissedWarnings.has('AI_DATA') && ( setDismissedWarnings(prev => new Set([...prev, 'AI_DATA']))} /> )}
)} {/* Search and Filters */} {showFilters && (
{/* Search */}
setSearchQuery(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent" />
{/* Category Filter */} {/* Risk Filter */} {/* Legal Basis Filter */}
)} {/* Data Points by Category */}
{Array.from(groupedDataPoints.entries()).map(([category, categoryDataPoints]) => { if (categoryDataPoints.length === 0) return null const meta = CATEGORY_METADATA[category] const isExpanded = expandedCategories.has(category) const selectedInCategory = categoryDataPoints.filter((dp) => selectedIds.includes(dp.id) ).length return (
{/* Category Header */} {/* Data Points List */} {isExpanded && (
{categoryDataPoints.map((dp) => { const isSelected = selectedIds.includes(dp.id) return (
!readOnly && onToggle(dp.id)} > {/* Checkbox */} {!readOnly && (
{isSelected ? ( ) : ( )}
)} {/* Content */}
{dp.code} {dp.name[language]} {dp.isSpecialCategory && ( )} {dp.isCustom && ( {language === 'de' ? 'Benutzerdefiniert' : 'Custom'} )}

{dp.description[language]}

{/* Details */}
{language === 'de' ? 'Zweck' : 'Purpose'}: {dp.purpose[language]} {language === 'de' ? 'Loeschfrist' : 'Retention'}:{' '} {RETENTION_PERIOD_INFO[dp.retentionPeriod]?.label[language] || dp.retentionPeriod} {dp.cookieCategory && ( Cookie: {dp.cookieCategory} )}
{/* Spezielle Warnungen fuer Art. 9 / BDSG / AI Act */} {(dp.requiresExplicitConsent || dp.isSpecialCategory) && (
{language === 'de' ? 'Ausdrueckliche Einwilligung erforderlich' : 'Explicit consent required'} {dp.legalBasis === 'EXPLICIT_CONSENT' && ( {language === 'de' ? 'Art. 9 Abs. 2 lit. a DSGVO - Separate Einwilligungserklaerung notwendig' : 'Art. 9(2)(a) GDPR - Separate consent declaration required'} )}
)} {/* Third Party Recipients */} {dp.thirdPartyRecipients.length > 0 && (
Drittanbieter:{' '} {dp.thirdPartyRecipients.join(', ')}
)}
) })}
)}
) })}
{/* Empty State */} {filteredDataPoints.length === 0 && (

Keine Datenpunkte gefunden

Versuchen Sie andere Suchbegriffe oder Filter

)}
) } export default DataPointCatalog