Agent-completed splits committed after agents hit rate limits before committing their work. All 4 pages now under 500 LOC: - consent-management: 1303 -> 193 LOC (+ 7 _components, _hooks, _data, _types) - control-library: 1210 -> 298 LOC (+ _components, _types) - incidents: 1150 -> 373 LOC (+ _components) - training: 1127 -> 366 LOC (+ _components) Verification: next build clean (142 pages generated). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
75 lines
3.9 KiB
TypeScript
75 lines
3.9 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { setMatrixEntry } from '@/lib/sdk/training/api'
|
|
import type { TrainingModule } from '@/lib/sdk/training/types'
|
|
|
|
export function MatrixAddModal({ roleCode, modules, onClose, onSaved }: {
|
|
roleCode: string
|
|
modules: TrainingModule[]
|
|
onClose: () => void
|
|
onSaved: () => void
|
|
}) {
|
|
const activeModules = modules.filter(m => m.is_active).sort((a, b) => a.module_code.localeCompare(b.module_code))
|
|
const [moduleId, setModuleId] = useState(activeModules[0]?.id || '')
|
|
const [isMandatory, setIsMandatory] = useState(true)
|
|
const [priority, setPriority] = useState(1)
|
|
const [saving, setSaving] = useState(false)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
const handleSave = async () => {
|
|
if (!moduleId) return
|
|
setSaving(true)
|
|
setError(null)
|
|
try {
|
|
await setMatrixEntry({ role_code: roleCode, module_id: moduleId, is_mandatory: isMandatory, priority })
|
|
onSaved()
|
|
} catch (e) {
|
|
setError(e instanceof Error ? e.message : 'Fehler beim Hinzufuegen')
|
|
} finally {
|
|
setSaving(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
|
|
<div className="flex items-center justify-between p-6 border-b border-gray-200">
|
|
<h2 className="text-lg font-semibold text-gray-900">Modul zu Rolle hinzufuegen</h2>
|
|
<button onClick={onClose} className="text-gray-400 hover:text-gray-600">
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div className="p-6 space-y-4">
|
|
<p className="text-sm text-gray-500">Rolle: <span className="font-medium text-gray-900">{roleCode}</span></p>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Modul</label>
|
|
<select value={moduleId} onChange={e => setModuleId(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
|
|
{activeModules.map(m => <option key={m.id} value={m.id}>{m.module_code} — {m.title}</option>)}
|
|
</select>
|
|
</div>
|
|
<div className="flex items-center justify-between">
|
|
<label className="text-sm font-medium text-gray-700">Pflichtmodul</label>
|
|
<button onClick={() => setIsMandatory(!isMandatory)} className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${isMandatory ? 'bg-blue-600' : 'bg-gray-200'}`}>
|
|
<span className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${isMandatory ? 'translate-x-6' : 'translate-x-1'}`} />
|
|
</button>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-1">Prioritaet</label>
|
|
<input type="number" min={1} value={priority} onChange={e => setPriority(Number(e.target.value))} className="w-24 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" />
|
|
</div>
|
|
{error && <p className="text-sm text-red-600">{error}</p>}
|
|
</div>
|
|
<div className="flex items-center justify-end gap-3 p-6 border-t border-gray-200">
|
|
<button onClick={onClose} className="px-4 py-2 text-sm text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">Abbrechen</button>
|
|
<button onClick={handleSave} disabled={saving || !moduleId} className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 transition-colors">
|
|
{saving ? 'Hinzufuegen...' : 'Hinzufuegen'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|