diff --git a/admin-compliance/app/sdk/document-generator/_components/LifecycleFilter.tsx b/admin-compliance/app/sdk/document-generator/_components/LifecycleFilter.tsx new file mode 100644 index 00000000..cacab558 --- /dev/null +++ b/admin-compliance/app/sdk/document-generator/_components/LifecycleFilter.tsx @@ -0,0 +1,124 @@ +'use client' + +/** + * Lifecycle-Phasen-Filter für den Document-Generator. + * + * Zeigt 5 Phasen-Tabs (Pre-Founding, Founding, Startup, KMU, Konzern) und + * filtert die angezeigten Templates entsprechend ihres `lifecycle_stage`-Arrays. + * + * Phasen-Definitionen synchron zu lib/sdk/founding/template-categories.ts + */ + +import { + LIFECYCLE_STAGE_LABELS, + type LifecycleStage, + TEMPLATE_CATEGORIES, +} from '@/lib/sdk/founding/template-categories' + +interface Props { + activeStage: LifecycleStage | 'all' + onChange: (stage: LifecycleStage | 'all') => void + /** Template-Counts pro Stage (optional, sonst aus Code-Registry berechnet) */ + countsByStage?: Record +} + +const STAGE_ORDER: (LifecycleStage | 'all')[] = [ + 'all', + 'pre_founding', + 'founding', + 'startup', + 'kmu', + 'konzern', +] + +const STAGE_ICONS: Record = { + all: '📚', + pre_founding: '🌱', + founding: '⚖️', + startup: '🚀', + kmu: '🏢', + konzern: '🏛️', +} + +const STAGE_HINTS: Record = { + pre_founding: 'Vor dem Notartermin — Term Sheet, IP-Sicherung, Wandeldarlehen', + founding: 'Für den Notartermin — Satzung, Gesellschafterliste, HRB-Anmeldung', + startup: '0–3 Jahre, <25 Mitarbeiter — Arbeitsverträge, AVV, Datenschutz', + kmu: '3+ Jahre, 25–250 MA — ISMS, Whistleblower, vollständige TOM', + konzern: '250+ MA — Konzern-Compliance, ISO 27001', +} + +export function LifecycleFilter({ activeStage, onChange, countsByStage }: Props) { + const counts = countsByStage || computeCountsFromRegistry() + + return ( +
+
+

Phase Deines Unternehmens

+ — filtert Dokumente nach Lifecycle +
+
+ {STAGE_ORDER.map(stage => { + const isAll = stage === 'all' + const count = isAll + ? Object.values(counts).reduce((s, c) => s + c, 0) + : (counts[stage] || 0) + const label = isAll ? 'Alle' : LIFECYCLE_STAGE_LABELS[stage as LifecycleStage].split(' (')[0] + const isActive = activeStage === stage + return ( + + ) + })} +
+ {activeStage !== 'all' && ( +

+ {STAGE_HINTS[activeStage as LifecycleStage]} +

+ )} +
+ ) +} + +function computeCountsFromRegistry(): Record { + const counts: Record = { + pre_founding: 0, founding: 0, startup: 0, kmu: 0, konzern: 0, + } + for (const cat of Object.values(TEMPLATE_CATEGORIES)) { + for (const stage of cat.lifecycle_stage) { + counts[stage] = (counts[stage] || 0) + 1 + } + } + return counts +} + +export function filterTemplatesByStage( + templates: T[], + stage: LifecycleStage | 'all' +): T[] { + if (stage === 'all') return templates + return templates.filter(t => { + const docType = t.document_type || t.type + if (!docType) return false + const cat = TEMPLATE_CATEGORIES[docType] + if (!cat) return stage === 'startup' // Fallback: unkategorisierte zeigen wir in Startup + return cat.lifecycle_stage.includes(stage) + }) +} diff --git a/admin-compliance/app/sdk/document-generator/page.tsx b/admin-compliance/app/sdk/document-generator/page.tsx index 22d8dde3..96a32b51 100644 --- a/admin-compliance/app/sdk/document-generator/page.tsx +++ b/admin-compliance/app/sdk/document-generator/page.tsx @@ -15,6 +15,8 @@ import { getGeneratorDefaults, getProfileLabel } from './scopeDefaults' import TemplateLibrary from './_components/TemplateLibrary' import GeneratorSection from './_components/GeneratorSection' import RecommendedDocuments from './_components/RecommendedDocuments' +import { LifecycleFilter, filterTemplatesByStage } from './_components/LifecycleFilter' +import type { LifecycleStage } from '@/lib/sdk/founding/template-categories' function DocumentGeneratorPageInner() { const { state } = useSDK() @@ -24,6 +26,7 @@ function DocumentGeneratorPageInner() { const [allTemplates, setAllTemplates] = useState([]) const [isLoadingLibrary, setIsLoadingLibrary] = useState(true) const [activeCategory, setActiveCategory] = useState('all') + const [activeStage, setActiveStage] = useState('all') const [activeLanguage, setActiveLanguage] = useState<'all' | 'de' | 'en'>('all') const [librarySearch, setLibrarySearch] = useState('') const [expandedPreviewId, setExpandedPreviewId] = useState(null) @@ -209,10 +212,15 @@ function DocumentGeneratorPageInner() { } }, [selectedDataPointsData]) - // Filtered templates (computed) + // Filtered templates (computed) — Lifecycle + Category + Language + Search const filteredTemplates = useMemo(() => { const category = CATEGORIES.find((c: { key: string }) => c.key === activeCategory) - return allTemplates.filter((t) => { + // 1. Lifecycle-Phase Filter via Code-Registry (mapped auf templateType) + const stageFiltered = filterTemplatesByStage( + allTemplates.map(t => ({ ...t, document_type: t.templateType || '' })), + activeStage + ) + return stageFiltered.filter((t) => { if (category && category.types !== null) { if (!category.types.includes(t.templateType || '')) return false } @@ -225,7 +233,22 @@ function DocumentGeneratorPageInner() { } return true }) - }, [allTemplates, activeCategory, activeLanguage, librarySearch]) + }, [allTemplates, activeCategory, activeStage, activeLanguage, librarySearch]) + + // Counts by stage for filter UI + const countsByStage = useMemo(() => { + const counts: Record = { pre_founding: 0, founding: 0, startup: 0, kmu: 0, konzern: 0 } + const stages: LifecycleStage[] = ['pre_founding', 'founding', 'startup', 'kmu', 'konzern'] + for (const t of allTemplates) { + const docType = t.templateType || '' + for (const s of stages) { + if (filterTemplatesByStage([{ document_type: docType }], s).length) { + counts[s]++ + } + } + } + return counts + }, [allTemplates]) const handleUseTemplate = useCallback((t: LegalTemplateResult) => { setActiveTemplate(t) @@ -292,6 +315,13 @@ function DocumentGeneratorPageInner() { + {/* Lifecycle-Phase Filter */} + + {/* Recommended documents based on scope profile */} =3.7 cryptography>=42.0.0 pillow>=12.1.1 +python-docx==1.2.0