Files
breakpilot-compliance/developer-portal/components/DevPortalLayout.tsx
Benjamin Admin 4f6bc8f6f6
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
feat(training+controls): interactive video pipeline, training blocks, control generator, CE libraries
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>
2026-03-16 21:41:48 +01:00

324 lines
10 KiB
TypeScript

'use client'
import React from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { Book, Code, FileText, HelpCircle, Zap, Terminal, Database, Shield, ChevronRight, Clock, BookOpen } from 'lucide-react'
interface NavItem {
title: string
href: string
icon?: React.ReactNode
items?: NavItem[]
}
const navigation: NavItem[] = [
{
title: 'Getting Started',
href: '/getting-started',
icon: <Zap className="w-4 h-4" />,
items: [
{ title: 'Quick Start', href: '/getting-started' },
],
},
{
title: 'SDK Documentation',
href: '/sdk',
icon: <Code className="w-4 h-4" />,
items: [
{ title: 'Overview', href: '/sdk' },
{ title: 'Installation', href: '/sdk/installation' },
{ title: 'Configuration', href: '/sdk/configuration' },
],
},
{
title: 'Consent SDK',
href: '/sdk/consent',
icon: <Shield className="w-4 h-4" />,
items: [
{ title: 'Uebersicht', href: '/sdk/consent' },
{ title: 'Installation', href: '/sdk/consent/installation' },
{ title: 'API Referenz', href: '/sdk/consent/api-reference' },
{ title: 'Frameworks', href: '/sdk/consent/frameworks' },
{ title: 'Mobile SDKs', href: '/sdk/consent/mobile' },
{ title: 'Sicherheit', href: '/sdk/consent/security' },
],
},
{
title: 'API Reference',
href: '/api',
icon: <Terminal className="w-4 h-4" />,
items: [
{ title: 'Overview', href: '/api' },
{ title: 'State API', href: '/api/state' },
{ title: 'RAG Search API', href: '/api/rag' },
{ title: 'Generation API', href: '/api/generate' },
{ title: 'Export API', href: '/api/export' },
{ title: 'Training API', href: '/api/training' },
],
},
{
title: 'Guides',
href: '/guides',
icon: <Book className="w-4 h-4" />,
items: [
{ title: 'Overview', href: '/guides' },
{ title: 'Phase 1: Assessment', href: '/guides/phase1' },
{ title: 'Phase 2: Dokumentation', href: '/guides/phase2' },
],
},
{
title: 'Systemdokumentation',
href: '/development/docs',
icon: <BookOpen className="w-4 h-4" />,
items: [
{ title: 'Compliance Service', href: '/development/docs' },
{ title: 'Klausur-Namespace (BYOEH)', href: '/development/byoeh' },
],
},
{
title: 'Changelog',
href: '/changelog',
icon: <Clock className="w-4 h-4" />,
items: [
{ title: 'Versionshistorie', href: '/changelog' },
],
},
]
interface DevPortalLayoutProps {
children: React.ReactNode
title?: string
description?: string
}
export function DevPortalLayout({ children, title, description }: DevPortalLayoutProps) {
const pathname = usePathname()
return (
<div className="min-h-screen bg-white">
{/* Header */}
<header className="sticky top-0 z-50 border-b border-gray-200 bg-white/95 backdrop-blur">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between h-16">
<div className="flex items-center gap-4">
<Link href="/" className="flex items-center gap-2">
<div className="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-600 to-indigo-600 flex items-center justify-center">
<FileText className="w-5 h-5 text-white" />
</div>
<span className="font-semibold text-gray-900">Developer Portal</span>
</Link>
<span className="text-gray-300">|</span>
<span className="text-sm text-gray-500">AI Compliance SDK</span>
</div>
<div className="flex items-center gap-4">
<Link
href="https://macmini:3002/sdk"
className="text-sm text-gray-600 hover:text-gray-900 transition-colors"
>
SDK Dashboard
</Link>
<a
href="https://github.com/breakpilot/compliance-sdk"
target="_blank"
rel="noopener noreferrer"
className="text-sm text-gray-600 hover:text-gray-900 transition-colors"
>
GitHub
</a>
</div>
</div>
</div>
</header>
<div className="max-w-7xl mx-auto flex">
{/* Sidebar */}
<aside className="w-64 shrink-0 border-r border-gray-200 h-[calc(100vh-64px)] sticky top-16 overflow-y-auto">
<nav className="p-4 space-y-6">
{navigation.map((section) => (
<div key={section.href}>
<div className="flex items-center gap-2 text-sm font-medium text-gray-900 mb-2">
{section.icon}
<span>{section.title}</span>
</div>
{section.items && (
<ul className="ml-6 space-y-1">
{section.items.map((item) => (
<li key={item.href}>
<Link
href={item.href}
className={`block py-1.5 text-sm transition-colors ${
pathname === item.href
? 'text-blue-600 font-medium'
: 'text-gray-600 hover:text-gray-900'
}`}
>
{item.title}
</Link>
</li>
))}
</ul>
)}
</div>
))}
</nav>
</aside>
{/* Main Content */}
<main className="flex-1 min-w-0">
<div className="max-w-3xl mx-auto px-8 py-12">
{(title || description) && (
<div className="mb-8">
{title && (
<h1 className="text-3xl font-bold text-gray-900 mb-2">{title}</h1>
)}
{description && (
<p className="text-lg text-gray-600">{description}</p>
)}
</div>
)}
<div className="prose prose-gray prose-blue max-w-none">
{children}
</div>
</div>
</main>
</div>
</div>
)
}
// Re-usable components for documentation
export function ApiEndpoint({
method,
path,
description,
}: {
method: 'GET' | 'POST' | 'PUT' | 'DELETE'
path: string
description: string
}) {
const methodColors = {
GET: 'bg-green-100 text-green-800',
POST: 'bg-blue-100 text-blue-800',
PUT: 'bg-yellow-100 text-yellow-800',
DELETE: 'bg-red-100 text-red-800',
}
return (
<div className="border border-gray-200 rounded-lg p-4 my-4 not-prose">
<div className="flex items-center gap-3 mb-2">
<span className={`px-2 py-1 text-xs font-bold rounded ${methodColors[method]}`}>
{method}
</span>
<code className="text-sm font-mono text-gray-800">{path}</code>
</div>
<p className="text-sm text-gray-600">{description}</p>
</div>
)
}
export function CodeBlock({
language,
children,
filename,
}: {
language: string
children: string
filename?: string
}) {
return (
<div className="my-4 not-prose">
{filename && (
<div className="bg-gray-800 text-gray-300 text-xs px-4 py-2 rounded-t-lg border-b border-gray-700">
{filename}
</div>
)}
<pre className={`bg-gray-900 text-gray-100 p-4 overflow-x-auto text-sm ${filename ? 'rounded-b-lg' : 'rounded-lg'}`}>
<code className={`language-${language}`}>{children}</code>
</pre>
</div>
)
}
export function ParameterTable({
parameters,
}: {
parameters: Array<{
name: string
type: string
required?: boolean
description: string
}>
}) {
return (
<div className="my-4 overflow-x-auto not-prose">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Parameter</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Type</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Required</th>
<th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase">Description</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{parameters.map((param) => (
<tr key={param.name}>
<td className="px-4 py-3">
<code className="text-sm text-blue-600">{param.name}</code>
</td>
<td className="px-4 py-3">
<code className="text-sm text-gray-600">{param.type}</code>
</td>
<td className="px-4 py-3">
{param.required ? (
<span className="text-red-600 text-sm">Yes</span>
) : (
<span className="text-gray-400 text-sm">No</span>
)}
</td>
<td className="px-4 py-3 text-sm text-gray-600">{param.description}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export function InfoBox({
type = 'info',
title,
children,
}: {
type?: 'info' | 'warning' | 'success' | 'error'
title?: string
children: React.ReactNode
}) {
const styles = {
info: 'bg-blue-50 border-blue-200 text-blue-800',
warning: 'bg-yellow-50 border-yellow-200 text-yellow-800',
success: 'bg-green-50 border-green-200 text-green-800',
error: 'bg-red-50 border-red-200 text-red-800',
}
const icons = {
info: <HelpCircle className="w-5 h-5" />,
warning: <Shield className="w-5 h-5" />,
success: <Zap className="w-5 h-5" />,
error: <Shield className="w-5 h-5" />,
}
return (
<div className={`my-4 p-4 border rounded-lg ${styles[type]} not-prose`}>
<div className="flex items-start gap-3">
<div className="shrink-0 mt-0.5">{icons[type]}</div>
<div>
{title && <p className="font-medium mb-1">{title}</p>}
<div className="text-sm">{children}</div>
</div>
</div>
</div>
)
}