Extract components and hooks to _components/ and _hooks/ subdirectories to bring all three page.tsx files under the 500-LOC hard cap. modules/page.tsx: 595 → 239 LOC security-backlog/page.tsx: 586 → 174 LOC consent/page.tsx: 569 → 305 LOC Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
120 lines
4.0 KiB
TypeScript
120 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import type { DisplayModule } from '../_hooks/useModules'
|
|
|
|
export function ModuleCard({
|
|
module,
|
|
isActive,
|
|
onActivate,
|
|
onDeactivate,
|
|
onConfigure,
|
|
}: {
|
|
module: DisplayModule
|
|
isActive: boolean
|
|
onActivate: () => void
|
|
onDeactivate: () => void
|
|
onConfigure: () => void
|
|
}) {
|
|
const categoryColors = {
|
|
gdpr: 'bg-blue-100 text-blue-700',
|
|
'ai-act': 'bg-purple-100 text-purple-700',
|
|
iso27001: 'bg-green-100 text-green-700',
|
|
nis2: 'bg-orange-100 text-orange-700',
|
|
custom: 'bg-gray-100 text-gray-700',
|
|
}
|
|
|
|
const statusColors = {
|
|
active: 'bg-green-100 text-green-700',
|
|
inactive: 'bg-gray-100 text-gray-500',
|
|
pending: 'bg-yellow-100 text-yellow-700',
|
|
}
|
|
|
|
return (
|
|
<div className={`bg-white rounded-xl border-2 p-6 transition-all ${
|
|
isActive ? 'border-purple-400 shadow-lg' : 'border-gray-200 hover:border-purple-300'
|
|
}`}>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1">
|
|
<div className="flex items-center gap-2 mb-2">
|
|
<span className={`px-2 py-1 text-xs rounded-full ${categoryColors[module.category]}`}>
|
|
{module.category.toUpperCase()}
|
|
</span>
|
|
<span className={`px-2 py-1 text-xs rounded-full ${statusColors[module.status]}`}>
|
|
{module.status === 'active' ? 'Aktiv' : module.status === 'pending' ? 'Ausstehend' : 'Inaktiv'}
|
|
</span>
|
|
{module.hasAIComponents && (
|
|
<span className="px-2 py-1 text-xs rounded-full bg-indigo-100 text-indigo-700">
|
|
KI
|
|
</span>
|
|
)}
|
|
</div>
|
|
<h3 className="text-lg font-semibold text-gray-900">{module.name}</h3>
|
|
<p className="text-sm text-gray-500 mt-1">{module.description}</p>
|
|
{module.regulations.length > 0 && (
|
|
<div className="mt-2 flex flex-wrap gap-1">
|
|
{module.regulations.map(reg => (
|
|
<span key={reg} className="px-2 py-0.5 text-xs bg-gray-100 text-gray-600 rounded">
|
|
{reg}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 grid grid-cols-2 gap-4 text-sm">
|
|
<div>
|
|
<span className="text-gray-500">Anforderungen:</span>
|
|
<span className="ml-2 font-medium">{module.requirementsCount}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-500">Kontrollen:</span>
|
|
<span className="ml-2 font-medium">{module.controlsCount}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4">
|
|
<div className="flex items-center justify-between text-sm mb-1">
|
|
<span className="text-gray-500">Fortschritt</span>
|
|
<span className="font-medium text-purple-600">{module.completionPercent}%</span>
|
|
</div>
|
|
<div className="h-2 bg-gray-100 rounded-full overflow-hidden">
|
|
<div
|
|
className={`h-full rounded-full transition-all ${
|
|
module.completionPercent === 100 ? 'bg-green-500' : 'bg-purple-600'
|
|
}`}
|
|
style={{ width: `${module.completionPercent}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-4 flex items-center gap-2">
|
|
{isActive ? (
|
|
<>
|
|
<button
|
|
onClick={onConfigure}
|
|
className="flex-1 px-4 py-2 text-sm bg-purple-50 text-purple-700 rounded-lg hover:bg-purple-100 transition-colors"
|
|
>
|
|
Konfigurieren
|
|
</button>
|
|
<button
|
|
onClick={onDeactivate}
|
|
className="px-4 py-2 text-sm text-red-600 hover:bg-red-50 rounded-lg transition-colors"
|
|
>
|
|
Deaktivieren
|
|
</button>
|
|
</>
|
|
) : (
|
|
<button
|
|
onClick={onActivate}
|
|
className="flex-1 px-4 py-2 text-sm bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
|
|
>
|
|
Modul aktivieren
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|