fix: add missing training page components to fix admin-compliance Docker build
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

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>
This commit is contained in:
Sharang Parnerkar
2026-04-17 10:32:35 +02:00
parent 54add75eb0
commit b5d20a4c1d
9 changed files with 902 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
'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>
)
}