The admin-v2 application was incomplete in the repository. This commit restores all missing components: - Admin pages (76 pages): dashboard, ai, compliance, dsgvo, education, infrastructure, communication, development, onboarding, rbac - SDK pages (45 pages): tom, dsfa, vvt, loeschfristen, einwilligungen, vendor-compliance, tom-generator, dsr, and more - Developer portal (25 pages): API docs, SDK guides, frameworks - All components, lib files, hooks, and types - Updated package.json with all dependencies The issue was caused by incomplete initial repository state - the full admin-v2 codebase existed in backend/admin-v2 and docs-src/admin-v2 but was never fully synced to the main admin-v2 directory. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
216 lines
7.7 KiB
TypeScript
216 lines
7.7 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import { useSDK } from '@/lib/sdk'
|
|
import { SDKSidebar } from '../Sidebar'
|
|
import { CommandBar } from '../CommandBar'
|
|
import { SDKPipelineSidebar } from '../SDKPipelineSidebar'
|
|
|
|
// =============================================================================
|
|
// SDK HEADER
|
|
// =============================================================================
|
|
|
|
function SDKHeader() {
|
|
const { currentStep, setCommandBarOpen, completionPercentage } = useSDK()
|
|
|
|
return (
|
|
<header className="sticky top-0 z-30 bg-white border-b border-gray-200">
|
|
<div className="flex items-center justify-between px-6 py-3">
|
|
{/* Breadcrumb / Current Step */}
|
|
<div className="flex items-center gap-3">
|
|
<nav className="flex items-center text-sm text-gray-500">
|
|
<span>SDK</span>
|
|
<svg className="w-4 h-4 mx-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
<span className="text-gray-900 font-medium">
|
|
{currentStep?.name || 'Dashboard'}
|
|
</span>
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Actions */}
|
|
<div className="flex items-center gap-4">
|
|
{/* Command Bar Trigger */}
|
|
<button
|
|
onClick={() => setCommandBarOpen(true)}
|
|
className="flex items-center gap-2 px-3 py-1.5 text-sm text-gray-500 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
|
>
|
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
/>
|
|
</svg>
|
|
<span>Suchen...</span>
|
|
<kbd className="ml-2 px-1.5 py-0.5 text-xs bg-gray-200 rounded">⌘K</kbd>
|
|
</button>
|
|
|
|
{/* Progress Indicator */}
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-24 h-2 bg-gray-200 rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-gradient-to-r from-purple-500 to-indigo-500 rounded-full transition-all duration-500"
|
|
style={{ width: `${completionPercentage}%` }}
|
|
/>
|
|
</div>
|
|
<span className="text-sm font-medium text-gray-600">{completionPercentage}%</span>
|
|
</div>
|
|
|
|
{/* Help Button */}
|
|
<button className="p-2 text-gray-400 hover:text-gray-600 hover:bg-gray-100 rounded-lg transition-colors">
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={2}
|
|
d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
)
|
|
}
|
|
|
|
// =============================================================================
|
|
// NAVIGATION FOOTER
|
|
// =============================================================================
|
|
|
|
function NavigationFooter() {
|
|
const { goToNextStep, goToPreviousStep, canGoNext, canGoPrevious, currentStep, validateCheckpoint } = useSDK()
|
|
const [isValidating, setIsValidating] = React.useState(false)
|
|
|
|
const handleNext = async () => {
|
|
if (!currentStep) return
|
|
|
|
setIsValidating(true)
|
|
try {
|
|
const status = await validateCheckpoint(currentStep.checkpointId)
|
|
if (status.passed) {
|
|
goToNextStep()
|
|
} else {
|
|
// Show error notification (in production, use toast)
|
|
console.error('Checkpoint validation failed:', status.errors)
|
|
}
|
|
} finally {
|
|
setIsValidating(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<footer className="sticky bottom-0 bg-white border-t border-gray-200 px-6 py-4">
|
|
<div className="flex items-center justify-between">
|
|
{/* Previous Button */}
|
|
<button
|
|
onClick={goToPreviousStep}
|
|
disabled={!canGoPrevious}
|
|
className={`flex items-center gap-2 px-4 py-2 rounded-lg transition-colors ${
|
|
canGoPrevious
|
|
? 'text-gray-700 hover:bg-gray-100'
|
|
: 'text-gray-300 cursor-not-allowed'
|
|
}`}
|
|
>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
|
</svg>
|
|
<span>Zurück</span>
|
|
</button>
|
|
|
|
{/* Current Step Info */}
|
|
<div className="text-sm text-gray-500">
|
|
Schritt {currentStep?.order || 1} von {currentStep?.phase === 1 ? 8 : 11}
|
|
</div>
|
|
|
|
{/* Next / Complete Button */}
|
|
<div className="flex items-center gap-3">
|
|
<button
|
|
onClick={goToNextStep}
|
|
className="px-4 py-2 text-gray-600 hover:bg-gray-100 rounded-lg transition-colors"
|
|
>
|
|
Überspringen
|
|
</button>
|
|
<button
|
|
onClick={handleNext}
|
|
disabled={!canGoNext || isValidating}
|
|
className={`flex items-center gap-2 px-6 py-2 rounded-lg font-medium transition-colors ${
|
|
canGoNext && !isValidating
|
|
? 'bg-purple-600 text-white hover:bg-purple-700'
|
|
: 'bg-gray-200 text-gray-400 cursor-not-allowed'
|
|
}`}
|
|
>
|
|
{isValidating ? (
|
|
<>
|
|
<svg className="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
|
<circle
|
|
className="opacity-25"
|
|
cx="12"
|
|
cy="12"
|
|
r="10"
|
|
stroke="currentColor"
|
|
strokeWidth="4"
|
|
/>
|
|
<path
|
|
className="opacity-75"
|
|
fill="currentColor"
|
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
/>
|
|
</svg>
|
|
<span>Validiere...</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<span>Weiter</span>
|
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
</svg>
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
)
|
|
}
|
|
|
|
// =============================================================================
|
|
// MAIN LAYOUT
|
|
// =============================================================================
|
|
|
|
interface SDKLayoutProps {
|
|
children: React.ReactNode
|
|
showNavigation?: boolean
|
|
}
|
|
|
|
export function SDKLayout({ children, showNavigation = true }: SDKLayoutProps) {
|
|
const { isCommandBarOpen, setCommandBarOpen } = useSDK()
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50">
|
|
{/* Sidebar */}
|
|
<SDKSidebar />
|
|
|
|
{/* Main Content */}
|
|
<div className="ml-64 flex flex-col min-h-screen">
|
|
{/* Header */}
|
|
<SDKHeader />
|
|
|
|
{/* Page Content */}
|
|
<main className="flex-1 p-6">{children}</main>
|
|
|
|
{/* Navigation Footer */}
|
|
{showNavigation && <NavigationFooter />}
|
|
</div>
|
|
|
|
{/* Command Bar Modal */}
|
|
{isCommandBarOpen && <CommandBar onClose={() => setCommandBarOpen(false)} />}
|
|
|
|
{/* Pipeline Sidebar (FAB on mobile, fixed on desktop) */}
|
|
<SDKPipelineSidebar />
|
|
</div>
|
|
)
|
|
}
|