'use client' import React from 'react' // ============================================================================= // RE-EXPORTS FROM SEPARATE FILES // ============================================================================= export { ThresholdAnalysisSection } from './ThresholdAnalysisSection' export { DSFASidebar } from './DSFASidebar' export { StakeholderConsultationSection } from './StakeholderConsultationSection' export { Art36Warning } from './Art36Warning' export { ReviewScheduleSection } from './ReviewScheduleSection' export { SourceAttribution, InlineSourceRef, AttributionFooter } from './SourceAttribution' // ============================================================================= // DSFA Card Component // ============================================================================= interface DSFACardProps { dsfa: { id: string title: string status: string risk_level?: string created_at?: string updated_at?: string processing_description?: string } onDelete?: (id: string) => void onExport?: (id: string) => void } export function DSFACard({ dsfa, onDelete, onExport }: DSFACardProps) { const statusColors: Record = { draft: 'bg-slate-100 text-slate-700', in_progress: 'bg-blue-100 text-blue-700', review: 'bg-amber-100 text-amber-700', approved: 'bg-green-100 text-green-700', rejected: 'bg-red-100 text-red-700', } return React.createElement('div', { className: 'bg-white rounded-xl border border-slate-200 p-5 hover:shadow-md transition-shadow' }, React.createElement('div', { className: 'flex items-start justify-between mb-3' }, React.createElement('h3', { className: 'font-semibold text-slate-900 text-lg' }, dsfa.title), React.createElement('span', { className: `px-2.5 py-1 rounded-full text-xs font-medium ${statusColors[dsfa.status] || statusColors.draft}` }, dsfa.status) ), dsfa.processing_description && React.createElement('p', { className: 'text-sm text-slate-500 mb-4 line-clamp-2' }, dsfa.processing_description), React.createElement('div', { className: 'flex items-center gap-2' }, React.createElement('a', { href: `/sdk/dsfa/${dsfa.id}`, className: 'px-3 py-1.5 bg-primary-600 text-white rounded-lg text-sm hover:bg-primary-700 transition-colors' }, 'Bearbeiten'), onExport && React.createElement('button', { onClick: () => onExport(dsfa.id), className: 'px-3 py-1.5 bg-slate-100 text-slate-700 rounded-lg text-sm hover:bg-slate-200 transition-colors' }, 'Export'), onDelete && React.createElement('button', { onClick: () => onDelete(dsfa.id), className: 'px-3 py-1.5 text-red-600 rounded-lg text-sm hover:bg-red-50 transition-colors' }, 'Loeschen') ) ) } // ============================================================================= // Risk Matrix Component // ============================================================================= // DSFARisk type matching lib/sdk/dsfa/types.ts interface DSFARiskInput { id: string category?: string description: string likelihood: 'low' | 'medium' | 'high' impact: 'low' | 'medium' | 'high' risk_level?: string affected_data?: string[] } interface RiskMatrixProps { risks: DSFARiskInput[] onRiskSelect?: (risk: DSFARiskInput) => void onRiskClick?: (riskId: string) => void onAddRisk?: (likelihood: 'low' | 'medium' | 'high', impact: 'low' | 'medium' | 'high') => void selectedRiskId?: string readOnly?: boolean } export function RiskMatrix({ risks, onRiskSelect, onRiskClick, onAddRisk, selectedRiskId, readOnly }: RiskMatrixProps) { const likelihoodLevels: Array<'low' | 'medium' | 'high'> = ['high', 'medium', 'low'] const impactLevels: Array<'low' | 'medium' | 'high'> = ['low', 'medium', 'high'] const levelLabels = { low: 'Niedrig', medium: 'Mittel', high: 'Hoch' } const cellColors: Record = { low: 'bg-green-100 hover:bg-green-200', medium: 'bg-yellow-100 hover:bg-yellow-200', high: 'bg-orange-100 hover:bg-orange-200', very_high: 'bg-red-100 hover:bg-red-200', } const getRiskColor = (likelihood: 'low' | 'medium' | 'high', impact: 'low' | 'medium' | 'high') => { const matrix: Record> = { low: { low: 'low', medium: 'low', high: 'medium' }, medium: { low: 'low', medium: 'medium', high: 'high' }, high: { low: 'medium', medium: 'high', high: 'very_high' }, } return cellColors[matrix[likelihood]?.[impact] || 'medium'] } const handleCellClick = (likelihood: 'low' | 'medium' | 'high', impact: 'low' | 'medium' | 'high') => { const cellRisks = risks.filter(r => r.likelihood === likelihood && r.impact === impact) if (cellRisks.length > 0 && onRiskSelect) { onRiskSelect(cellRisks[0]) } else if (cellRisks.length > 0 && onRiskClick) { onRiskClick(cellRisks[0].id) } else if (!readOnly && onAddRisk) { onAddRisk(likelihood, impact) } } return React.createElement('div', { className: 'bg-white rounded-xl border border-slate-200 p-5' }, React.createElement('h3', { className: 'font-semibold text-slate-900 mb-4' }, 'Risikomatrix'), React.createElement('div', { className: 'text-xs text-slate-500 mb-2' }, 'Eintrittswahrscheinlichkeit ↑ | Schwere →'), React.createElement('div', { className: 'grid grid-cols-4 gap-1' }, // Header row React.createElement('div'), ...impactLevels.map(i => React.createElement('div', { key: `h-${i}`, className: 'text-center text-xs text-slate-500 py-1' }, levelLabels[i])), // Grid rows ...likelihoodLevels.map(likelihood => [ React.createElement('div', { key: `l-${likelihood}`, className: 'text-right text-xs text-slate-500 pr-2 flex items-center justify-end' }, levelLabels[likelihood]), ...impactLevels.map(impact => { const cellRisks = risks.filter(r => r.likelihood === likelihood && r.impact === impact) const isSelected = cellRisks.some(r => r.id === selectedRiskId) return React.createElement('div', { key: `${likelihood}-${impact}`, className: `aspect-square rounded ${getRiskColor(likelihood, impact)} flex items-center justify-center text-xs font-medium cursor-pointer ${isSelected ? 'ring-2 ring-purple-500' : ''}`, onClick: () => handleCellClick(likelihood, impact) }, cellRisks.length > 0 ? String(cellRisks.length) : (readOnly ? '' : '+')) }) ] ).flat() ) ) } // ============================================================================= // Approval Panel Component // ============================================================================= interface ApprovalPanelProps { dsfa: { id: string status: string approved_by?: string approved_at?: string rejection_reason?: string } onApprove?: () => void onReject?: (reason: string) => void } export function ApprovalPanel({ dsfa, onApprove, onReject }: ApprovalPanelProps) { const [rejectionReason, setRejectionReason] = React.useState('') const [showRejectForm, setShowRejectForm] = React.useState(false) if (dsfa.status === 'approved') { return React.createElement('div', { className: 'bg-green-50 border border-green-200 rounded-xl p-5' }, React.createElement('div', { className: 'flex items-center gap-2 mb-2' }, React.createElement('span', { className: 'text-green-600 text-lg' }, '\u2713'), React.createElement('h3', { className: 'font-semibold text-green-800' }, 'DSFA genehmigt') ), dsfa.approved_by && React.createElement('p', { className: 'text-sm text-green-700' }, `Genehmigt von: ${dsfa.approved_by}` ) ) } return React.createElement('div', { className: 'bg-white rounded-xl border border-slate-200 p-5' }, React.createElement('h3', { className: 'font-semibold text-slate-900 mb-4' }, 'Freigabe'), React.createElement('div', { className: 'flex gap-3' }, onApprove && React.createElement('button', { onClick: onApprove, className: 'px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 transition-colors' }, 'Genehmigen'), onReject && React.createElement('button', { onClick: () => setShowRejectForm(true), className: 'px-4 py-2 bg-red-600 text-white rounded-lg text-sm hover:bg-red-700 transition-colors' }, 'Ablehnen') ), showRejectForm && React.createElement('div', { className: 'mt-4' }, React.createElement('textarea', { value: rejectionReason, onChange: (e: React.ChangeEvent) => setRejectionReason(e.target.value), placeholder: 'Ablehnungsgrund...', className: 'w-full p-3 border border-slate-200 rounded-lg text-sm resize-none', rows: 3 }), React.createElement('button', { onClick: () => { onReject?.(rejectionReason); setShowRejectForm(false) }, className: 'mt-2 px-4 py-2 bg-red-600 text-white rounded-lg text-sm hover:bg-red-700' }, 'Ablehnung senden') ) ) }