feat(training+controls): interactive video pipeline, training blocks, control generator, CE libraries
Some checks failed
CI/CD / go-lint (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) Failing after 37s
CI/CD / test-python-backend-compliance (push) Successful in 39s
CI/CD / test-python-document-crawler (push) Successful in 26s
CI/CD / test-python-dsms-gateway (push) Successful in 23s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / Deploy (push) Has been skipped
Some checks failed
CI/CD / go-lint (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) Failing after 37s
CI/CD / test-python-backend-compliance (push) Successful in 39s
CI/CD / test-python-document-crawler (push) Successful in 26s
CI/CD / test-python-dsms-gateway (push) Successful in 23s
CI/CD / validate-canonical-controls (push) Successful in 12s
CI/CD / Deploy (push) Has been skipped
Interactive Training Videos (CP-TRAIN): - DB migration 022: training_checkpoints + checkpoint_progress tables - NarratorScript generation via Anthropic (AI Teacher persona, German) - TTS batch synthesis + interactive video pipeline (slides + checkpoint slides + FFmpeg) - 4 new API endpoints: generate-interactive, interactive-manifest, checkpoint submit, checkpoint progress - InteractiveVideoPlayer component (HTML5 Video, quiz overlay, seek protection, progress tracking) - Learner portal integration with automatic completion on all checkpoints passed - 30 new tests (handler validation + grading logic + manifest/progress + seek protection) Training Blocks: - Block generator, block store, block config CRUD + preview/generate endpoints - Migration 021: training_blocks schema Control Generator + Canonical Library: - Control generator routes + service enhancements - Canonical control library helpers, sidebar entry - Citation backfill service + tests - CE libraries data (hazard, protection, evidence, lifecycle, components) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ export interface CanonicalControl {
|
||||
customer_visible?: boolean
|
||||
verification_method: string | null
|
||||
category: string | null
|
||||
target_audience: string | null
|
||||
target_audience: string | string[] | null
|
||||
generation_metadata?: Record<string, unknown> | null
|
||||
generation_strategy?: string | null
|
||||
created_at: string
|
||||
@@ -142,10 +142,27 @@ export const CATEGORY_OPTIONS = [
|
||||
]
|
||||
|
||||
export const TARGET_AUDIENCE_OPTIONS: Record<string, { bg: string; label: string }> = {
|
||||
// Legacy English keys
|
||||
enterprise: { bg: 'bg-cyan-100 text-cyan-700', label: 'Unternehmen' },
|
||||
authority: { bg: 'bg-rose-100 text-rose-700', label: 'Behoerden' },
|
||||
provider: { bg: 'bg-violet-100 text-violet-700', label: 'Anbieter' },
|
||||
all: { bg: 'bg-gray-100 text-gray-700', label: 'Alle' },
|
||||
// German keys from LLM generation
|
||||
unternehmen: { bg: 'bg-cyan-100 text-cyan-700', label: 'Unternehmen' },
|
||||
behoerden: { bg: 'bg-rose-100 text-rose-700', label: 'Behoerden' },
|
||||
entwickler: { bg: 'bg-sky-100 text-sky-700', label: 'Entwickler' },
|
||||
datenschutzbeauftragte: { bg: 'bg-purple-100 text-purple-700', label: 'DSB' },
|
||||
geschaeftsfuehrung: { bg: 'bg-amber-100 text-amber-700', label: 'GF' },
|
||||
'it-abteilung': { bg: 'bg-blue-100 text-blue-700', label: 'IT' },
|
||||
rechtsabteilung: { bg: 'bg-fuchsia-100 text-fuchsia-700', label: 'Recht' },
|
||||
'compliance-officer': { bg: 'bg-indigo-100 text-indigo-700', label: 'Compliance' },
|
||||
personalwesen: { bg: 'bg-pink-100 text-pink-700', label: 'Personal' },
|
||||
einkauf: { bg: 'bg-lime-100 text-lime-700', label: 'Einkauf' },
|
||||
produktion: { bg: 'bg-orange-100 text-orange-700', label: 'Produktion' },
|
||||
vertrieb: { bg: 'bg-teal-100 text-teal-700', label: 'Vertrieb' },
|
||||
gesundheitswesen: { bg: 'bg-red-100 text-red-700', label: 'Gesundheit' },
|
||||
finanzwesen: { bg: 'bg-emerald-100 text-emerald-700', label: 'Finanzen' },
|
||||
oeffentlicher_dienst: { bg: 'bg-rose-100 text-rose-700', label: 'Oeffentl. Dienst' },
|
||||
}
|
||||
|
||||
export const COLLECTION_OPTIONS = [
|
||||
@@ -223,11 +240,32 @@ export function CategoryBadge({ category }: { category: string | null }) {
|
||||
)
|
||||
}
|
||||
|
||||
export function TargetAudienceBadge({ audience }: { audience: string | null }) {
|
||||
export function TargetAudienceBadge({ audience }: { audience: string | string[] | null }) {
|
||||
if (!audience) return null
|
||||
const config = TARGET_AUDIENCE_OPTIONS[audience]
|
||||
if (!config) return null
|
||||
return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.bg}`}>{config.label}</span>
|
||||
|
||||
// Parse JSON array string from DB (e.g. '["unternehmen", "einkauf"]')
|
||||
let items: string[] = []
|
||||
if (typeof audience === 'string') {
|
||||
if (audience.startsWith('[')) {
|
||||
try { items = JSON.parse(audience) } catch { items = [audience] }
|
||||
} else {
|
||||
items = [audience]
|
||||
}
|
||||
} else if (Array.isArray(audience)) {
|
||||
items = audience
|
||||
}
|
||||
|
||||
if (items.length === 0) return null
|
||||
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 flex-wrap">
|
||||
{items.map((item, i) => {
|
||||
const config = TARGET_AUDIENCE_OPTIONS[item]
|
||||
if (!config) return <span key={i} className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-600">{item}</span>
|
||||
return <span key={i} className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.bg}`}>{config.label}</span>
|
||||
})}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function GenerationStrategyBadge({ strategy }: { strategy: string | null | undefined }) {
|
||||
|
||||
Reference in New Issue
Block a user