'use client' /** * SDK Pipeline Sidebar * * Floating Action Button mit Drawer zur Visualisierung der SDK-Pipeline. * Zeigt die zwei Phasen (Compliance Assessment & Dokumentengenerierung) * mit Fortschritt und ermΓΆglicht schnelle Navigation. * * Features: * - Desktop (xl+): Fixierte Sidebar rechts * - Mobile/Tablet: Floating Action Button mit Slide-In Drawer */ import Link from 'next/link' import { useState, useEffect } from 'react' import { usePathname } from 'next/navigation' import { useSDK, SDK_STEPS, getStepsForPhase, type SDKStep } from '@/lib/sdk' // ============================================================================= // ICONS // ============================================================================= const CheckIcon = () => ( ) const ArrowIcon = () => ( ) const CloseIcon = () => ( ) const PipelineIcon = () => ( ) // Step Icons als Emojis const STEP_ICONS: Record = { // Phase 1 'use-case-workshop': 'πŸ“‹', 'screening': 'πŸ”', 'modules': 'πŸ“¦', 'requirements': 'πŸ“œ', 'controls': 'πŸ›‘οΈ', 'evidence': 'πŸ“Ž', 'audit-checklist': 'βœ…', 'risks': '⚠️', // Phase 2 'ai-act': 'πŸ€–', 'obligations': 'πŸ“‘', 'dsfa': 'πŸ“„', 'tom': 'πŸ”’', 'einwilligungen': '✍️', 'loeschfristen': 'πŸ—‘οΈ', 'vvt': 'πŸ“Š', 'consent': 'πŸ“', 'cookie-banner': 'πŸͺ', 'dsr': 'πŸ‘€', 'escalations': '🚨', } // ============================================================================= // STEP ITEM // ============================================================================= interface StepItemProps { step: SDKStep isActive: boolean isCompleted: boolean onNavigate: () => void } function StepItem({ step, isActive, isCompleted, onNavigate }: StepItemProps) { const icon = STEP_ICONS[step.id] || 'β€’' return ( {icon} {step.nameShort} {isCompleted && !isActive && ( )} {isActive && ( )} ) } // ============================================================================= // PHASE SECTION // ============================================================================= interface PhaseSectionProps { phase: 1 | 2 title: string steps: SDKStep[] completion: number currentStepId: string completedSteps: string[] onNavigate: () => void isExpanded: boolean onToggle: () => void } function PhaseSection({ phase, title, steps, completion, currentStepId, completedSteps, onNavigate, isExpanded, onToggle, }: PhaseSectionProps) { return (
{/* Phase Header */} {/* Progress Bar */}
{/* Steps List */} {isExpanded && (
{steps.map(step => ( ))}
)}
) } // ============================================================================= // PIPELINE FLOW VISUALIZATION // ============================================================================= function PipelineFlow() { return (
Datenfluss
{/* Phase 1 Flow */}
Phase 1
πŸ“‹ πŸ” ⚠️ πŸ›‘οΈ
{/* Arrow Down */}
{/* Phase 2 Flow */}
Phase 2
πŸ“„ πŸ”’ πŸ“Š βœ…
) } // ============================================================================= // SIDEBAR CONTENT // ============================================================================= interface SidebarContentProps { onNavigate: () => void } function SidebarContent({ onNavigate }: SidebarContentProps) { const pathname = usePathname() const { state, phase1Completion, phase2Completion } = useSDK() const [expandedPhases, setExpandedPhases] = useState>({ 1: true, 2: false, }) const phase1Steps = getStepsForPhase(1) const phase2Steps = getStepsForPhase(2) // Find current step const currentStep = SDK_STEPS.find(s => s.url === pathname) const currentStepId = currentStep?.id || '' // Auto-expand current phase useEffect(() => { if (currentStep) { setExpandedPhases(prev => ({ ...prev, [currentStep.phase]: true, })) } }, [currentStep]) const togglePhase = (phase: number) => { setExpandedPhases(prev => ({ ...prev, [phase]: !prev[phase] })) } return (
{/* Phase 1 */} togglePhase(1)} /> {/* Phase 2 */} togglePhase(2)} /> {/* Pipeline Flow */} {/* Quick Info */} {currentStep && (
Aktuell:{' '} {currentStep.description}
)}
) } // ============================================================================= // MAIN COMPONENT - RESPONSIVE // ============================================================================= export interface SDKPipelineSidebarProps { /** Position des FAB auf Mobile */ fabPosition?: 'bottom-right' | 'bottom-left' } export function SDKPipelineSidebar({ fabPosition = 'bottom-right' }: SDKPipelineSidebarProps) { const [isMobileOpen, setIsMobileOpen] = useState(false) const [isDesktopCollapsed, setIsDesktopCollapsed] = useState(true) // Start collapsed const { completionPercentage } = useSDK() // Load collapsed state from localStorage useEffect(() => { const stored = localStorage.getItem('sdk-pipeline-sidebar-collapsed') if (stored !== null) { setIsDesktopCollapsed(stored === 'true') } }, []) // Save collapsed state to localStorage const toggleDesktopSidebar = () => { const newState = !isDesktopCollapsed setIsDesktopCollapsed(newState) localStorage.setItem('sdk-pipeline-sidebar-collapsed', String(newState)) } // Close drawer on route change or escape key useEffect(() => { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { setIsMobileOpen(false) setIsDesktopCollapsed(true) } } window.addEventListener('keydown', handleEscape) return () => window.removeEventListener('keydown', handleEscape) }, []) // Prevent body scroll when drawer is open useEffect(() => { if (isMobileOpen) { document.body.style.overflow = 'hidden' } else { document.body.style.overflow = '' } return () => { document.body.style.overflow = '' } }, [isMobileOpen]) const fabPositionClasses = fabPosition === 'bottom-right' ? 'right-4 bottom-20' : 'left-4 bottom-20' return ( <> {/* Desktop: Fixed Sidebar (when expanded) */} {!isDesktopCollapsed && (
{/* Header with close button */}
SDK Pipeline {completionPercentage}%
{/* Content */}
{}} />
)} {/* Desktop: FAB (when collapsed) */} {isDesktopCollapsed && ( )} {/* Mobile/Tablet: FAB */} {/* Mobile/Tablet: Drawer Overlay */} {isMobileOpen && (
{/* Backdrop */}
setIsMobileOpen(false)} /> {/* Drawer */}
{/* Drawer Header */}
SDK Pipeline {completionPercentage}%
{/* Drawer Content */}
setIsMobileOpen(false)} />
)} {/* CSS for slide-in animation */} ) } export default SDKPipelineSidebar