Files
breakpilot-compliance/admin-compliance/app/sdk/training/_components/ModuleEditDrawer.tsx
Sharang Parnerkar b5d20a4c1d
Some checks failed
Build + Deploy / build-admin-compliance (push) Failing after 34s
Build + Deploy / build-developer-portal (push) Successful in 56s
Build + Deploy / build-tts (push) Successful in 1m8s
CI/CD / go-lint (push) Has been skipped
Build + Deploy / trigger-orca (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 38s
CI/CD / test-python-backend-compliance (push) Successful in 32s
Build + Deploy / build-backend-compliance (push) Successful in 7s
Build + Deploy / build-ai-sdk (push) Successful in 7s
Build + Deploy / build-document-crawler (push) Successful in 33s
Build + Deploy / build-dsms-gateway (push) Successful in 20s
CI/CD / test-python-dsms-gateway (push) Has been cancelled
CI/CD / validate-canonical-controls (push) Has been cancelled
CI/CD / test-python-document-crawler (push) Has been cancelled
fix: add missing training page components to fix admin-compliance Docker build
All 8 components imported by app/sdk/training/page.tsx were missing.
Docker build was failing with Module not found errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-17 10:32:35 +02:00

150 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import { updateModule, deleteModule } from '@/lib/sdk/training/api'
import type { TrainingModule } from '@/lib/sdk/training/types'
import { REGULATION_LABELS, FREQUENCY_LABELS } from '@/lib/sdk/training/types'
export default function ModuleEditDrawer({
module,
onClose,
onSaved,
}: {
module: TrainingModule
onClose: () => void
onSaved: () => void
}) {
const [saving, setSaving] = useState(false)
const [error, setError] = useState<string | null>(null)
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault()
setSaving(true)
setError(null)
const fd = new FormData(e.currentTarget)
try {
await updateModule(module.id, {
title: fd.get('title') as string,
description: (fd.get('description') as string) || undefined,
regulation_area: fd.get('regulation_area') as string,
frequency_type: fd.get('frequency_type') as string,
validity_days: parseInt(fd.get('validity_days') as string),
duration_minutes: parseInt(fd.get('duration_minutes') as string),
pass_threshold: parseInt(fd.get('pass_threshold') as string),
risk_weight: parseFloat(fd.get('risk_weight') as string),
nis2_relevant: fd.get('nis2_relevant') === 'on',
is_active: fd.get('is_active') === 'on',
})
onSaved()
} catch (err) {
setError(err instanceof Error ? err.message : 'Fehler beim Speichern')
} finally {
setSaving(false)
}
}
async function handleDelete() {
if (!window.confirm('Modul wirklich loeschen?')) return
try {
await deleteModule(module.id)
onSaved()
} catch (err) {
setError(err instanceof Error ? err.message : 'Fehler beim Loeschen')
}
}
return (
<div className="fixed inset-0 z-50 flex justify-end">
<div className="absolute inset-0 bg-black/30" onClick={onClose} />
<div className="relative bg-white w-full max-w-md shadow-xl flex flex-col overflow-y-auto">
<div className="flex items-center justify-between px-6 py-4 border-b">
<h3 className="text-base font-semibold">Modul bearbeiten</h3>
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 text-xl">×</button>
</div>
<div className="px-6 py-4 flex-1">
{error && (
<div className="mb-4 text-sm text-red-600 bg-red-50 border border-red-200 rounded p-3">{error}</div>
)}
<div className="mb-4 text-xs text-gray-400">
<code className="bg-gray-100 px-1.5 py-0.5 rounded">{module.module_code}</code>
<span className="ml-2">ID: {module.id.slice(0, 8)}</span>
</div>
<form onSubmit={handleSubmit} className="space-y-3">
<div>
<label className="text-xs text-gray-600 block mb-1">Titel *</label>
<input name="title" required defaultValue={module.title} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
<div>
<label className="text-xs text-gray-600 block mb-1">Beschreibung</label>
<textarea name="description" rows={2} defaultValue={module.description} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-gray-600 block mb-1">Regulierungsbereich</label>
<select name="regulation_area" defaultValue={module.regulation_area} className="w-full px-3 py-2 text-sm border rounded-lg bg-white">
{Object.entries(REGULATION_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
</select>
</div>
<div>
<label className="text-xs text-gray-600 block mb-1">Frequenz</label>
<select name="frequency_type" defaultValue={module.frequency_type} className="w-full px-3 py-2 text-sm border rounded-lg bg-white">
{Object.entries(FREQUENCY_LABELS).map(([k, v]) => <option key={k} value={k}>{v}</option>)}
</select>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-gray-600 block mb-1">Gueltigkeitsdauer (Tage)</label>
<input name="validity_days" type="number" defaultValue={module.validity_days} min={1} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
<div>
<label className="text-xs text-gray-600 block mb-1">Dauer (Minuten)</label>
<input name="duration_minutes" type="number" defaultValue={module.duration_minutes} min={1} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-gray-600 block mb-1">Bestehensgrenze (%)</label>
<input name="pass_threshold" type="number" defaultValue={module.pass_threshold} min={0} max={100} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
<div>
<label className="text-xs text-gray-600 block mb-1">Risikogewicht</label>
<input name="risk_weight" type="number" defaultValue={module.risk_weight} min={0} step={0.1} className="w-full px-3 py-2 text-sm border rounded-lg" />
</div>
</div>
<div className="flex gap-4">
<div className="flex items-center gap-2">
<input name="nis2_relevant" type="checkbox" id="edit-nis2" defaultChecked={module.nis2_relevant} className="rounded" />
<label htmlFor="edit-nis2" className="text-xs text-gray-600">NIS-2 relevant</label>
</div>
<div className="flex items-center gap-2">
<input name="is_active" type="checkbox" id="edit-active" defaultChecked={module.is_active} className="rounded" />
<label htmlFor="edit-active" className="text-xs text-gray-600">Aktiv</label>
</div>
</div>
<div className="flex gap-3 pt-2">
<button type="submit" disabled={saving} className="flex-1 px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50">
{saving ? 'Speichere...' : 'Speichern'}
</button>
<button type="button" onClick={onClose} className="px-4 py-2 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">
Abbrechen
</button>
</div>
</form>
</div>
<div className="px-6 py-4 border-t">
<button
onClick={handleDelete}
className="w-full px-4 py-2 text-sm bg-red-50 text-red-700 border border-red-200 rounded-lg hover:bg-red-100"
>
Modul loeschen
</button>
</div>
</div>
</div>
)
}