Files
breakpilot-compliance/admin-compliance/components/sdk/Sidebar/SidebarModuleList.tsx
T
Benjamin Admin 400eba592e refactor(sdk): Sidebar-Doppelungen auflösen (A1) + Routen-Inventur
11 Modul-Eintraege entfernt, deren exakte Route bereits ein immer-sichtbarer
Pipeline-Schritt ist (advisory-board, ai-act, source-policy, loeschfristen,
einwilligungen, cookie-banner, dsr, vendor-compliance, consent-management,
email-templates, training) — Heimat bleibt die Pipeline, kein Feature-Verlust
(keiner dieser Schritte hat visibleWhen). "Datenschutz"-Gruppe zu "Cookie &
Consent" (Consent Dashboard + Cookie Live-Vorschau) verschlankt. Aehnlich
benannte, aber VERSCHIEDENE Seiten bewusst behalten (document-generator≠
catalog-manager, control-library≠coverage, consent≠consent-management,
cookie-banner≠/preview, vendor-compliance≠vendor-assessment).

Vollstaendige Routen-Inventur (Pipeline + Module + aufgeloeste Dups) in
docs-src/development/sdk-navigation-inventory.md — damit kein Feature
unsichtbar verloren geht.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-16 16:35:07 +02:00

189 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
// =============================================================================
// SIDEBAR MODULE LIST
// Customer-facing module groups (always shown) + an internal/developer section
// (catalog/template management, corpus curation, engine internals, admin tooling)
// gated by useInternalUI() so customers never see it. See lib/sdk/useInternalUI.
// =============================================================================
import React from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { AdditionalModuleItem, withProject } from './SidebarSubComponents'
import { useInternalUI } from '@/lib/sdk/useInternalUI'
interface SidebarModuleListProps {
collapsed: boolean
projectId?: string
pendingCRCount: number
}
const ic = (d: string) => (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d={d} />
</svg>
)
// Shared icon path data (de-duplicated from the previous inline SVGs).
const I = {
barChart: 'M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z',
shieldCheck: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z',
iace: 'M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z',
clipboardCheck: 'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4',
list: 'M4 6h16M4 10h16M4 14h16M4 18h16',
bolt: 'M13 10V3L4 14h7v7l9-11h-7z',
euReg: 'M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4',
optimizer: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6',
chip: 'M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z',
grid: 'M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z',
eyeCircle: 'M15 12a3 3 0 11-6 0 3 3 0 016 0z',
consent: 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10',
users: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0z',
user: 'M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z',
clock: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
envelope: 'M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z',
card: 'M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z',
rolesUsers: 'M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z',
book: 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253',
warning: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z',
catalog: 'M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4',
globe: 'M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9',
search: 'M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z',
checkCircle: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
architecture: 'M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01',
portfolio: 'M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10',
roadmap: 'M9 17V7m0 10a2 2 0 01-2 2H5a2 2 0 01-2-2V7a2 2 0 012-2h2a2 2 0 012 2m0 10a2 2 0 002 2h2a2 2 0 002-2M9 7a2 2 0 012-2h2a2 2 0 012 2m0 10V7m0 10a2 2 0 002 2h2a2 2 0 002-2V7a2 2 0 00-2-2h-2a2 2 0 00-2 2',
code: 'M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4',
}
function SectionHeader({ label, collapsed, tone = 'gray' }: { label: string; collapsed: boolean; tone?: 'gray' | 'indigo' | 'purple' | 'slate' }) {
if (collapsed) return null
const cls = {
gray: 'text-gray-400 font-medium',
indigo: 'text-indigo-600 font-semibold',
purple: 'text-purple-600 font-semibold',
slate: 'text-slate-500 font-semibold',
}[tone]
return <div className={`px-4 py-2 text-xs uppercase tracking-wider ${cls}`}>{label}</div>
}
export function SidebarModuleList({ collapsed, projectId, pendingCRCount }: SidebarModuleListProps) {
const pathname = usePathname()
const showInternal = useInternalUI()
const active = (href: string, prefix = false) =>
prefix ? (pathname?.startsWith(href) ?? false) : pathname === href
const item = (href: string, d: string, label: string, prefix = false) => (
<AdditionalModuleItem href={href} icon={ic(d)} label={label} isActive={active(href, prefix)} collapsed={collapsed} projectId={projectId} />
)
return (
<>
{/* === KUNDE === */}
{/* Produkt-Compliance (CE & Cyber) — die vier Kern-Module zusammen */}
<div className="border-t-2 border-indigo-200 py-2 bg-indigo-50/30">
<SectionHeader label="Produkt-Compliance (CE & Cyber)" collapsed={collapsed} tone="indigo" />
{item('/sdk/gap-analysis', I.barChart, 'Gap-Analyse', true)}
{item('/sdk/iace', I.iace, 'Maschinensicherheit (CE)', true)}
{item('/sdk/cra', I.shieldCheck, 'Cyber Resilience (CRA)', true)}
</div>
{/* KI-Compliance */}
<div className="border-t border-gray-100 py-2">
<SectionHeader label="KI-Compliance" collapsed={collapsed} />
{item('/sdk/use-cases', I.list, 'Use Cases', true)}
{item('/sdk/ai-registration', I.euReg, 'EU Registrierung', true)}
{item('/sdk/compliance-optimizer', I.optimizer, 'Compliance Optimizer', true)}
{item('/sdk/agent', I.chip, 'Compliance Agent', true)}
{item('/sdk/benchmark', I.barChart, 'Branchen-Benchmark', true)}
</div>
{/* Cookie & Consent — Schnellzugriff (vollständiger Datenschutz-Flow = Pipeline Phase 35) */}
<div className="border-t-2 border-purple-200 py-2 bg-purple-50/30">
<SectionHeader label="Cookie & Consent" collapsed={collapsed} tone="purple" />
{item('/sdk/cmp', I.grid, 'Consent Dashboard')}
{item('/sdk/cookie-banner/preview', I.eyeCircle, 'Cookie Live-Vorschau')}
</div>
{/* Verträge & Audit */}
<div className="border-t border-gray-100 py-2">
<SectionHeader label="Verträge & Audit" collapsed={collapsed} />
{item('/sdk/vendor-assessment', I.clipboardCheck, 'Vertragspruefung', true)}
{item('/sdk/audit-timeline', I.clock, 'Audit Timeline', true)}
</div>
{/* Payment / Terminal */}
<div className="border-t border-gray-100 py-2">
<SectionHeader label="Payment / Terminal" collapsed={collapsed} />
{item('/sdk/payment-compliance', I.card, 'Payment Compliance', true)}
</div>
{/* Betrieb & Nachweise */}
<div className="border-t border-gray-100 py-2">
<SectionHeader label="Betrieb & Nachweise" collapsed={collapsed} />
{item('/sdk/rollenkonzept', I.rolesUsers, 'Rollenkonzept', true)}
{item('/sdk/training/learner', I.user, 'Schulung (Learner)')}
{item('/sdk/security-backlog', I.warning, 'Security Backlog')}
{item('/sdk/compliance-hub', I.barChart, 'Compliance Hub')}
{item('/sdk/isms', I.shieldCheck, 'ISMS Readiness')}
{item('/sdk/wiki', I.book, 'Compliance Wiki', true)}
</div>
{/* === INTERN · ENTWICKLUNG (nur intern sichtbar, nie für Kunden) === */}
{showInternal && (
<div className="border-t-2 border-slate-300 py-2 bg-slate-100/60">
<SectionHeader label="Intern · Entwicklung" collapsed={collapsed} tone="slate" />
{item('/sdk/coverage', I.clipboardCheck, 'Abdeckung / Korpus', true)}
{item('/sdk/catalog-manager', I.catalog, 'Kataloge / Templates')}
{item('/sdk/rag', I.search, 'Legal RAG')}
{item('/sdk/quality', I.checkCircle, 'AI Quality')}
{item('/sdk/assertions', I.clipboardCheck, 'Assertions')}
{item('/sdk/dsms', I.shieldCheck, 'DSMS')}
{item('/sdk/sdk-flow', I.bolt, 'SDK Flow')}
{item('/sdk/architecture', I.architecture, 'Architektur')}
{item('/sdk/agents', I.chip, 'Agenten', true)}
{item('/sdk/workshop', I.users, 'Workshop')}
{item('/sdk/portfolio', I.portfolio, 'Portfolio')}
{item('/sdk/roadmap', I.roadmap, 'Roadmap')}
{item('/sdk/audit-llm', I.barChart, 'LLM Audit')}
{item('/sdk/rbac', I.shieldCheck, 'RBAC Admin')}
{item('/sdk/api-docs', I.code, 'API-Referenz')}
{/* Change Requests — needs badge so handled directly */}
<Link
href={withProject('/sdk/change-requests', projectId)}
className={`flex items-center gap-3 px-4 py-2.5 text-sm transition-colors ${
collapsed ? 'justify-center' : ''
} ${
pathname === '/sdk/change-requests'
? 'bg-purple-100 text-purple-900 font-medium'
: 'text-gray-600 hover:bg-gray-50 hover:text-gray-900'
}`}
title={collapsed ? `Änderungsanfragen (${pendingCRCount})` : undefined}
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01" />
</svg>
{!collapsed && (
<span className="flex items-center gap-2">
Änderungsanfragen
{pendingCRCount > 0 && (
<span className="px-1.5 py-0.5 text-xs font-bold bg-red-500 text-white rounded-full min-w-[1.25rem] text-center">
{pendingCRCount}
</span>
)}
</span>
)}
{collapsed && pendingCRCount > 0 && (
<span className="absolute top-1 right-1 w-2 h-2 bg-red-500 rounded-full" />
)}
</Link>
<AdditionalModuleItem href="https://macmini:3006" icon={ic(I.code)} label="Developer Portal" isActive={false} collapsed={collapsed} projectId={projectId} />
<AdditionalModuleItem href="https://macmini:8011" icon={ic(I.book)} label="SDK Dokumentation" isActive={false} collapsed={collapsed} projectId={projectId} />
</div>
)}
</>
)
}