refactor(admin): split AIUseCaseModuleEditor, DataPointCatalog, ProjectSelector components
AIUseCaseModuleEditor (698 LOC) → thin orchestrator (187) + constants (29) + barrel tabs (4) + tabs implementation split into SystemData (261), PurposeAct (149), RisksReview (219). DataPointCatalog (658 LOC) → main (291) + helpers (190) + CategoryGroup (124) + Row (108). ProjectSelector (656 LOC) → main (211) + CreateProjectDialog (169) + ProjectActionDialog (140) + ProjectCard (128). All files now under 300 LOC soft target and 500 LOC hard cap. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
'use client'
|
||||
|
||||
import React from 'react'
|
||||
import {
|
||||
CheckCircle,
|
||||
Circle,
|
||||
Shield,
|
||||
AlertTriangle,
|
||||
Key,
|
||||
Megaphone,
|
||||
MessageSquare,
|
||||
CreditCard,
|
||||
Bot,
|
||||
User,
|
||||
Mail,
|
||||
Activity,
|
||||
MapPin,
|
||||
Smartphone,
|
||||
BarChart3,
|
||||
Share2,
|
||||
Heart,
|
||||
Briefcase,
|
||||
FileText,
|
||||
FileCode,
|
||||
X,
|
||||
} from 'lucide-react'
|
||||
import {
|
||||
DataPointCategory,
|
||||
RiskLevel,
|
||||
LegalBasis,
|
||||
SupportedLanguage,
|
||||
RISK_LEVEL_STYLING,
|
||||
LEGAL_BASIS_INFO,
|
||||
ARTICLE_9_WARNING,
|
||||
EMPLOYEE_DATA_WARNING,
|
||||
AI_DATA_WARNING,
|
||||
} from '@/lib/sdk/einwilligungen/types'
|
||||
|
||||
// =============================================================================
|
||||
// CategoryIcon
|
||||
// =============================================================================
|
||||
|
||||
export const CategoryIcon: React.FC<{ category: DataPointCategory; className?: string }> = ({
|
||||
category,
|
||||
className = 'w-5 h-5',
|
||||
}) => {
|
||||
const icons: Record<DataPointCategory, React.ReactNode> = {
|
||||
MASTER_DATA: <User className={className} />,
|
||||
CONTACT_DATA: <Mail className={className} />,
|
||||
AUTHENTICATION: <Key className={className} />,
|
||||
CONSENT: <CheckCircle className={className} />,
|
||||
COMMUNICATION: <MessageSquare className={className} />,
|
||||
PAYMENT: <CreditCard className={className} />,
|
||||
USAGE_DATA: <Activity className={className} />,
|
||||
LOCATION: <MapPin className={className} />,
|
||||
DEVICE_DATA: <Smartphone className={className} />,
|
||||
MARKETING: <Megaphone className={className} />,
|
||||
ANALYTICS: <BarChart3 className={className} />,
|
||||
SOCIAL_MEDIA: <Share2 className={className} />,
|
||||
HEALTH_DATA: <Heart className={className} />,
|
||||
EMPLOYEE_DATA: <Briefcase className={className} />,
|
||||
CONTRACT_DATA: <FileText className={className} />,
|
||||
LOG_DATA: <FileCode className={className} />,
|
||||
AI_DATA: <Bot className={className} />,
|
||||
SECURITY: <Shield className={className} />,
|
||||
}
|
||||
return <>{icons[category] || <Circle className={className} />}</>
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// RiskBadge
|
||||
// =============================================================================
|
||||
|
||||
export const RiskBadge: React.FC<{ level: RiskLevel; language: SupportedLanguage }> = ({
|
||||
level,
|
||||
language,
|
||||
}) => {
|
||||
const styling = RISK_LEVEL_STYLING[level]
|
||||
return (
|
||||
<span
|
||||
className={`px-2 py-0.5 text-xs font-medium rounded-full ${styling.bgColor} ${styling.color}`}
|
||||
>
|
||||
{styling.label[language]}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// LegalBasisBadge
|
||||
// =============================================================================
|
||||
|
||||
export const LegalBasisBadge: React.FC<{ basis: LegalBasis; language: SupportedLanguage }> = ({
|
||||
basis,
|
||||
language,
|
||||
}) => {
|
||||
const info = LEGAL_BASIS_INFO[basis]
|
||||
const colors: Record<LegalBasis, string> = {
|
||||
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 (
|
||||
<span className={`px-2 py-0.5 text-xs font-medium rounded-full ${colors[basis] || 'bg-gray-100 text-gray-700'}`}>
|
||||
{info?.name[language] || basis}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// SpecialCategoryWarning
|
||||
// =============================================================================
|
||||
|
||||
export const SpecialCategoryWarning: React.FC<{
|
||||
category: DataPointCategory
|
||||
language: SupportedLanguage
|
||||
onClose?: () => void
|
||||
}> = ({ category, language, onClose }) => {
|
||||
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 (
|
||||
<div className={`p-4 rounded-lg border ${bgColor} ${borderColor} mb-4`}>
|
||||
<div className="flex items-start gap-3">
|
||||
<AlertTriangle className={`w-5 h-5 ${iconColor} flex-shrink-0 mt-0.5`} />
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className={`font-semibold ${iconColor}`}>
|
||||
{warning.title[language]}
|
||||
</h4>
|
||||
{onClose && (
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-slate-400 hover:text-slate-600 transition-colors"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-slate-600 mt-1">
|
||||
{warning.description[language]}
|
||||
</p>
|
||||
<ul className="mt-3 space-y-1">
|
||||
{warning.requirements.map((req, idx) => (
|
||||
<li key={idx} className="text-sm text-slate-700 flex items-start gap-2">
|
||||
<span className={`${iconColor} font-bold`}>•</span>
|
||||
<span>{req[language]}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Article9Badge
|
||||
// =============================================================================
|
||||
|
||||
export const Article9Badge: React.FC<{ language: SupportedLanguage }> = ({ language }) => (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full bg-rose-100 text-rose-700 border border-rose-200">
|
||||
<Heart className="w-3 h-3" />
|
||||
{language === 'de' ? 'Art. 9 DSGVO' : 'Art. 9 GDPR'}
|
||||
</span>
|
||||
)
|
||||
Reference in New Issue
Block a user