feat: A4 preview + example data + company profile presets
Feature 1: DIN A4 Preview
- Markdown→HTML renderer (inline, no dependency)
- A4 page container (210mm × 297mm) with print styling
- Toggle between "Vorschau" (rendered A4) and "Markdown" (raw)
- Print button opens new window with @page A4 CSS
- Purple theme for headings, styled tables
Feature 2: Example Data Button
- "Beispieldaten" button in Generator header
- Loads examples/{templateType}_{lang}.json
- Prefills all context fields for instant full preview
Feature 3: Company Profile Presets
- 10 industry presets: SaaS Startup, Consumer App, E-Commerce,
IT-Agentur, Maschinenbau, Rechtsanwalt, Arztpraxis, Handwerk,
Bildung, Enterprise
- Each with pre-filled CompanyProfile + scope hints + recommended docs
- PresetSelector component (card grid with icons)
- "Manuell ausfuellen" skip option
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
|
||||
import { COMPANY_PROFILE_PRESETS, type CompanyProfilePreset } from '@/lib/sdk/company-profile-presets'
|
||||
|
||||
interface PresetSelectorProps {
|
||||
onSelect: (preset: CompanyProfilePreset) => void
|
||||
onSkip: () => void
|
||||
}
|
||||
|
||||
export function PresetSelector({ onSelect, onSkip }: PresetSelectorProps) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<h2 className="text-xl font-bold text-gray-900">Welcher Unternehmenstyp passt zu Ihnen?</h2>
|
||||
<p className="text-sm text-gray-500 mt-2">
|
||||
Waehlen Sie eine Vorlage fuer Ihre Branche — alle Felder werden vorbefuellt
|
||||
und Sie koennen anschliessend anpassen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-3">
|
||||
{COMPANY_PROFILE_PRESETS.map((preset) => (
|
||||
<button
|
||||
key={preset.id}
|
||||
onClick={() => onSelect(preset)}
|
||||
className="flex flex-col items-center gap-2 p-4 bg-white border border-gray-200 rounded-xl hover:border-purple-400 hover:shadow-md transition-all text-center group"
|
||||
>
|
||||
<span className="text-3xl">{preset.icon}</span>
|
||||
<span className="text-sm font-medium text-gray-900 group-hover:text-purple-700">
|
||||
{preset.label}
|
||||
</span>
|
||||
<span className="text-xs text-gray-500 leading-tight">
|
||||
{preset.description}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="text-center">
|
||||
<button
|
||||
onClick={onSkip}
|
||||
className="text-sm text-gray-400 hover:text-gray-600 underline"
|
||||
>
|
||||
Manuell ausfuellen (ohne Vorlage)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { LegalTemplateResult } from '@/lib/sdk/types'
|
||||
import { RuleEngineResult } from '../ruleEngine'
|
||||
|
||||
@@ -12,6 +13,72 @@ interface GeneratorPreviewTabProps {
|
||||
onExportMarkdown: () => void
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Lightweight Markdown → HTML (no dependency needed)
|
||||
// ============================================================================
|
||||
|
||||
function markdownToHtml(md: string): string {
|
||||
let html = md
|
||||
// Escape HTML entities first
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
|
||||
// Headings
|
||||
html = html.replace(/^#### (.+)$/gm, '<h4>$1</h4>')
|
||||
html = html.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
||||
html = html.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
||||
html = html.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
||||
|
||||
// Horizontal rules
|
||||
html = html.replace(/^---$/gm, '<hr/>')
|
||||
|
||||
// Bold + Italic
|
||||
html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
|
||||
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||
|
||||
// Links
|
||||
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="text-purple-600 underline">$1</a>')
|
||||
|
||||
// Tables (simple)
|
||||
html = html.replace(/^\|(.+)\|$/gm, (match) => {
|
||||
const cells = match.split('|').filter(c => c.trim())
|
||||
const isHeader = cells.every(c => /^[\s-:]+$/.test(c))
|
||||
if (isHeader) return '<!-- separator -->'
|
||||
const tag = 'td'
|
||||
return '<tr>' + cells.map(c => `<${tag}>${c.trim()}</${tag}>`).join('') + '</tr>'
|
||||
})
|
||||
|
||||
// Wrap consecutive table rows
|
||||
html = html.replace(/((?:<tr>.*<\/tr>\n?<!-- separator -->\n?)?(?:<tr>.*<\/tr>\n?)+)/g, (block) => {
|
||||
const rows = block.split('\n').filter(r => r.startsWith('<tr>'))
|
||||
if (rows.length === 0) return block
|
||||
const headerRow = rows[0].replace(/<td>/g, '<th>').replace(/<\/td>/g, '</th>')
|
||||
const bodyRows = rows.slice(1).join('\n')
|
||||
return `<table><thead>${headerRow}</thead><tbody>${bodyRows}</tbody></table>`
|
||||
})
|
||||
|
||||
// Remove separator comments
|
||||
html = html.replace(/<!-- separator -->\n?/g, '')
|
||||
|
||||
// Unordered lists
|
||||
html = html.replace(/^- (.+)$/gm, '<li>$1</li>')
|
||||
html = html.replace(/((?:<li>.*<\/li>\n?)+)/g, '<ul>$1</ul>')
|
||||
|
||||
// Paragraphs (lines that aren't already HTML)
|
||||
html = html.replace(/^(?!<[a-z/]|$)(.+)$/gm, '<p>$1</p>')
|
||||
|
||||
// Clean up empty paragraphs
|
||||
html = html.replace(/<p>\s*<\/p>/g, '')
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Component
|
||||
// ============================================================================
|
||||
|
||||
export default function GeneratorPreviewTab({
|
||||
template,
|
||||
ruleResult,
|
||||
@@ -20,12 +87,17 @@ export default function GeneratorPreviewTab({
|
||||
onCopy,
|
||||
onExportMarkdown,
|
||||
}: GeneratorPreviewTabProps) {
|
||||
const [viewMode, setViewMode] = useState<'preview' | 'markdown'>('preview')
|
||||
|
||||
const htmlContent = markdownToHtml(renderedContent)
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Violations */}
|
||||
{ruleResult && ruleResult.violations.length > 0 && (
|
||||
<div className="bg-red-50 border border-red-200 rounded-xl p-4">
|
||||
<p className="text-sm font-semibold text-red-700 mb-2">
|
||||
🔴 {ruleResult.violations.length} Fehler
|
||||
{ruleResult.violations.length} Fehler
|
||||
</p>
|
||||
<ul className="space-y-1">
|
||||
{ruleResult.violations.map((v) => (
|
||||
@@ -36,6 +108,8 @@ export default function GeneratorPreviewTab({
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Warnings */}
|
||||
{ruleResult && ruleResult.warnings.filter((w) => w.id !== 'WARN_LEGAL_REVIEW').length > 0 && (
|
||||
<div className="bg-yellow-50 border border-yellow-200 rounded-xl p-4">
|
||||
<ul className="space-y-1">
|
||||
@@ -43,69 +117,133 @@ export default function GeneratorPreviewTab({
|
||||
.filter((w) => w.id !== 'WARN_LEGAL_REVIEW')
|
||||
.map((w) => (
|
||||
<li key={w.id} className="text-xs text-yellow-700">
|
||||
🟡 <span className="font-mono font-medium">[{w.id}]</span> {w.message}
|
||||
<span className="font-mono font-medium">[{w.id}]</span> {w.message}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Legal notice */}
|
||||
{ruleResult && (
|
||||
<div className="bg-blue-50 border border-blue-200 rounded-xl p-3">
|
||||
<p className="text-xs text-blue-700">
|
||||
ℹ️ Rechtlicher Hinweis: Diese Vorlage ist MIT-lizenziert. Vor Produktionseinsatz
|
||||
wird eine rechtliche Überprüfung dringend empfohlen.
|
||||
Rechtlicher Hinweis: Diese Vorlage ist MIT-lizenziert. Vor Produktionseinsatz
|
||||
wird eine rechtliche Ueberpruefung dringend empfohlen.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{ruleResult && ruleResult.appliedDefaults.length > 0 && (
|
||||
<p className="text-xs text-gray-400">
|
||||
Defaults angewendet: {ruleResult.appliedDefaults.join(', ')}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Toolbar */}
|
||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||
<span className="text-sm text-gray-600">
|
||||
{missing.length > 0 && (
|
||||
<span className="text-orange-600">
|
||||
⚠ {missing.length} Platzhalter noch nicht ausgefüllt
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<div className="flex gap-2">
|
||||
<div className="flex gap-1 bg-gray-100 rounded-lg p-0.5">
|
||||
<button
|
||||
onClick={onCopy}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs border border-gray-200 rounded-lg hover:bg-gray-50 text-gray-600 transition-colors"
|
||||
onClick={() => setViewMode('preview')}
|
||||
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
|
||||
viewMode === 'preview' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
|
||||
</svg>
|
||||
Kopieren
|
||||
Vorschau
|
||||
</button>
|
||||
<button
|
||||
onClick={onExportMarkdown}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs border border-gray-200 rounded-lg hover:bg-gray-50 text-gray-600 transition-colors"
|
||||
onClick={() => setViewMode('markdown')}
|
||||
className={`px-3 py-1 text-xs font-medium rounded-md transition-colors ${
|
||||
viewMode === 'markdown' ? 'bg-white text-gray-900 shadow-sm' : 'text-gray-500'
|
||||
}`}
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
Markdown
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
{missing.length > 0 && (
|
||||
<span className="text-xs text-orange-600">
|
||||
{missing.length} Platzhalter offen
|
||||
</span>
|
||||
)}
|
||||
<button onClick={onCopy} className="px-3 py-1.5 text-xs border border-gray-200 rounded-lg hover:bg-gray-50 text-gray-600">
|
||||
Kopieren
|
||||
</button>
|
||||
<button onClick={onExportMarkdown} className="px-3 py-1.5 text-xs border border-gray-200 rounded-lg hover:bg-gray-50 text-gray-600">
|
||||
Markdown
|
||||
</button>
|
||||
<button
|
||||
onClick={() => window.print()}
|
||||
className="flex items-center gap-1.5 px-4 py-1.5 text-xs bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors"
|
||||
onClick={() => {
|
||||
const printWindow = window.open('', '_blank')
|
||||
if (!printWindow) return
|
||||
printWindow.document.write(`<!DOCTYPE html><html><head><title>${template.documentTitle || 'Dokument'}</title><style>
|
||||
@page { size: A4; margin: 25mm 20mm; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; font-size: 11pt; line-height: 1.6; color: #1a202c; max-width: 170mm; margin: 0 auto; }
|
||||
h1 { font-size: 18pt; color: #5b21b6; margin: 24pt 0 8pt; border-bottom: 2px solid #7c3aed; padding-bottom: 4pt; }
|
||||
h2 { font-size: 14pt; color: #1f2937; margin: 18pt 0 6pt; }
|
||||
h3 { font-size: 12pt; color: #374151; margin: 12pt 0 4pt; }
|
||||
h4 { font-size: 11pt; color: #4b5563; margin: 10pt 0 4pt; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 8pt 0; font-size: 10pt; }
|
||||
th { background: #f5f3ff; color: #5b21b6; font-weight: 600; text-align: left; padding: 6pt 8pt; border: 1px solid #e5e7eb; }
|
||||
td { padding: 5pt 8pt; border: 1px solid #e5e7eb; vertical-align: top; }
|
||||
ul { padding-left: 20pt; }
|
||||
li { margin: 2pt 0; }
|
||||
hr { border: none; border-top: 1px solid #e5e7eb; margin: 16pt 0; }
|
||||
a { color: #7c3aed; }
|
||||
p { margin: 4pt 0; }
|
||||
strong { font-weight: 600; }
|
||||
</style></head><body>${htmlContent}</body></html>`)
|
||||
printWindow.document.close()
|
||||
printWindow.print()
|
||||
}}
|
||||
className="px-4 py-1.5 text-xs bg-purple-600 text-white rounded-lg hover:bg-purple-700"
|
||||
>
|
||||
<svg className="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
|
||||
</svg>
|
||||
PDF drucken
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-50 rounded-xl border border-gray-200 p-6 max-h-[600px] overflow-y-auto">
|
||||
<pre className="text-sm text-gray-800 whitespace-pre-wrap leading-relaxed font-sans">
|
||||
{renderedContent}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{viewMode === 'markdown' ? (
|
||||
<div className="bg-gray-50 rounded-xl border border-gray-200 p-6 max-h-[800px] overflow-y-auto">
|
||||
<pre className="text-sm text-gray-800 whitespace-pre-wrap leading-relaxed font-mono">
|
||||
{renderedContent}
|
||||
</pre>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-gray-100 rounded-xl p-8 flex justify-center overflow-y-auto max-h-[85vh]">
|
||||
{/* A4 Page */}
|
||||
<div
|
||||
className="bg-white shadow-lg border border-gray-300"
|
||||
style={{
|
||||
width: '210mm',
|
||||
minHeight: '297mm',
|
||||
padding: '25mm 20mm',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
||||
fontSize: '11pt',
|
||||
lineHeight: '1.6',
|
||||
color: '#1a202c',
|
||||
}}
|
||||
>
|
||||
<style>{`
|
||||
.a4-content h1 { font-size: 18pt; color: #5b21b6; margin: 24pt 0 8pt; border-bottom: 2px solid #7c3aed; padding-bottom: 4pt; }
|
||||
.a4-content h2 { font-size: 14pt; color: #1f2937; margin: 18pt 0 6pt; }
|
||||
.a4-content h3 { font-size: 12pt; color: #374151; margin: 12pt 0 4pt; }
|
||||
.a4-content h4 { font-size: 11pt; color: #4b5563; margin: 10pt 0 4pt; }
|
||||
.a4-content table { width: 100%; border-collapse: collapse; margin: 8pt 0; font-size: 10pt; }
|
||||
.a4-content th { background: #f5f3ff; color: #5b21b6; font-weight: 600; text-align: left; padding: 6pt 8pt; border: 1px solid #e5e7eb; }
|
||||
.a4-content td { padding: 5pt 8pt; border: 1px solid #e5e7eb; vertical-align: top; }
|
||||
.a4-content ul { padding-left: 20pt; margin: 4pt 0; }
|
||||
.a4-content li { margin: 2pt 0; }
|
||||
.a4-content hr { border: none; border-top: 1px solid #e5e7eb; margin: 16pt 0; }
|
||||
.a4-content a { color: #7c3aed; text-decoration: underline; }
|
||||
.a4-content p { margin: 4pt 0; }
|
||||
.a4-content strong { font-weight: 600; }
|
||||
`}</style>
|
||||
<div
|
||||
className="a4-content"
|
||||
dangerouslySetInnerHTML={{ __html: htmlContent }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Attribution */}
|
||||
{template.attributionRequired && template.attributionText && (
|
||||
<div className="text-xs text-orange-600 bg-orange-50 p-3 rounded-lg border border-orange-200">
|
||||
<strong>Attribution erforderlich:</strong> {template.attributionText}
|
||||
|
||||
@@ -160,6 +160,33 @@ export default function GeneratorSection({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button
|
||||
onClick={() => {
|
||||
// Load example data for current template type
|
||||
const templateType = template.templateType || ''
|
||||
const lang = template.language || 'de'
|
||||
const exampleFile = `/sdk/document-generator/examples/${templateType}_${lang}.json`
|
||||
fetch(exampleFile)
|
||||
.then(r => r.ok ? r.json() : null)
|
||||
.then(data => {
|
||||
if (!data?.context) return
|
||||
const ctx = data.context
|
||||
for (const [section, fields] of Object.entries(ctx)) {
|
||||
if (typeof fields === 'object' && fields) {
|
||||
for (const [key, value] of Object.entries(fields as Record<string, unknown>)) {
|
||||
onContextChange(section as keyof TemplateContext, key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {/* no example available */})
|
||||
}}
|
||||
className="px-3 py-1 text-xs bg-blue-50 text-blue-600 border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors"
|
||||
>
|
||||
Beispieldaten
|
||||
</button>
|
||||
</div>
|
||||
<button onClick={onClose} className="text-gray-400 hover:text-gray-600 transition-colors shrink-0" aria-label="Schließen">
|
||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
|
||||
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Company Profile Presets — Branchenvorlagen fuer typische Kundenszenarien
|
||||
*
|
||||
* Jeder Preset enthaelt ein vorbefuelltes CompanyProfile + typische Scope-Antworten.
|
||||
* Der Kunde waehlt beim Onboarding ein Profil und passt es dann an.
|
||||
*/
|
||||
|
||||
export interface CompanyProfilePreset {
|
||||
id: string
|
||||
label: string
|
||||
description: string
|
||||
icon: string
|
||||
/** Vorbefuellte CompanyProfile-Felder */
|
||||
profile: {
|
||||
legalForm: string
|
||||
industry: string[]
|
||||
businessModel: string
|
||||
companySize: string
|
||||
employeeCount: string
|
||||
headquartersCountry: string
|
||||
targetMarkets: string[]
|
||||
isDataController: boolean
|
||||
isDataProcessor: boolean
|
||||
}
|
||||
/** Typische Scope-Antworten fuer diese Branche */
|
||||
scopeHints: Record<string, string>
|
||||
/** Typische Dokumente die diese Branche braucht */
|
||||
recommendedDocs: string[]
|
||||
}
|
||||
|
||||
export const COMPANY_PROFILE_PRESETS: CompanyProfilePreset[] = [
|
||||
{
|
||||
id: 'saas_startup',
|
||||
label: 'SaaS Startup',
|
||||
description: 'B2B Software-Startup, 1-5 Mitarbeiter, Cloud-basiert, remote-first',
|
||||
icon: '🚀',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['tech'],
|
||||
businessModel: 'b2b',
|
||||
companySize: 'micro',
|
||||
employeeCount: '1-9',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU'],
|
||||
isDataController: true,
|
||||
isDataProcessor: true,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1-9',
|
||||
org_industry: 'tech',
|
||||
org_business_model: 'b2b',
|
||||
proc_ai_usage: 'yes',
|
||||
tech_hosting_location: 'eu',
|
||||
tech_encryption_transit: 'yes',
|
||||
tech_encryption_rest: 'yes',
|
||||
comp_documentation_level: 'basic',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb', 'cookie_policy', 'dpa'],
|
||||
},
|
||||
{
|
||||
id: 'consumer_app',
|
||||
label: 'App Startup (Consumer)',
|
||||
description: 'B2C Mobile App, 1-5 Mitarbeiter, App Store, Nutzerdaten',
|
||||
icon: '📱',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['tech'],
|
||||
businessModel: 'b2c',
|
||||
companySize: 'micro',
|
||||
employeeCount: '1-9',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1-9',
|
||||
org_industry: 'tech',
|
||||
org_business_model: 'b2c',
|
||||
data_volume: '1000-10000',
|
||||
proc_tracking: 'yes',
|
||||
prod_consent_management: 'yes',
|
||||
tech_hosting_location: 'eu',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'terms_of_use', 'cookie_policy', 'community_guidelines'],
|
||||
},
|
||||
{
|
||||
id: 'ecommerce',
|
||||
label: 'E-Commerce / Online-Shop',
|
||||
description: 'Online-Handel B2C, 5-20 Mitarbeiter, Webshop, Zahlungsabwicklung',
|
||||
icon: '🛒',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['retail'],
|
||||
businessModel: 'b2c',
|
||||
companySize: 'small',
|
||||
employeeCount: '10-49',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '10-49',
|
||||
org_industry: 'retail',
|
||||
org_business_model: 'b2c',
|
||||
prod_webshop: 'yes',
|
||||
data_volume: '10000-100000',
|
||||
tech_hosting_location: 'eu',
|
||||
prod_consent_management: 'yes',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb', 'widerruf', 'cookie_policy', 'cookie_banner'],
|
||||
},
|
||||
{
|
||||
id: 'it_agency',
|
||||
label: 'IT-Dienstleister / Agentur',
|
||||
description: 'IT-Beratung oder Agentur, 10-50 Mitarbeiter, Kundenprojekte',
|
||||
icon: '💻',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['tech'],
|
||||
businessModel: 'b2b',
|
||||
companySize: 'small',
|
||||
employeeCount: '10-49',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU'],
|
||||
isDataController: true,
|
||||
isDataProcessor: true,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '10-49',
|
||||
org_industry: 'tech',
|
||||
org_business_model: 'b2b',
|
||||
proc_ai_usage: 'yes',
|
||||
tech_hosting_location: 'eu',
|
||||
comp_vendor_management: 'yes',
|
||||
comp_training: 'yes',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb', 'dpa', 'nda', 'employee_dsi'],
|
||||
},
|
||||
{
|
||||
id: 'maschinenbau',
|
||||
label: 'Maschinenbau KMU',
|
||||
description: 'Maschinenbau B2B, 50-200 Mitarbeiter, Produktion, CE-Kennzeichnung',
|
||||
icon: '🏭',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['manufacturing'],
|
||||
businessModel: 'b2b',
|
||||
companySize: 'medium',
|
||||
employeeCount: '50-249',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '50-249',
|
||||
org_industry: 'manufacturing',
|
||||
org_business_model: 'b2b',
|
||||
proc_employee_monitoring: 'no',
|
||||
tech_hosting_location: 'eu',
|
||||
comp_vendor_management: 'yes',
|
||||
comp_documentation_level: 'structured',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb', 'dpa', 'employee_dsi', 'applicant_dsi', 'whistleblower_policy', 'tom_documentation'],
|
||||
},
|
||||
{
|
||||
id: 'law_firm',
|
||||
label: 'Rechtsanwaltskanzlei',
|
||||
description: 'Kanzlei, 5-20 Mitarbeiter, Mandantendaten, besondere Vertraulichkeit',
|
||||
icon: '⚖️',
|
||||
profile: {
|
||||
legalForm: 'PartG',
|
||||
industry: ['legal'],
|
||||
businessModel: 'b2b',
|
||||
companySize: 'small',
|
||||
employeeCount: '1-9',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1-9',
|
||||
org_industry: 'legal',
|
||||
org_business_model: 'b2b',
|
||||
data_art9: 'no',
|
||||
tech_encryption_transit: 'yes',
|
||||
tech_encryption_rest: 'yes',
|
||||
comp_documentation_level: 'basic',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'dpa', 'employee_dsi', 'applicant_dsi'],
|
||||
},
|
||||
{
|
||||
id: 'healthcare',
|
||||
label: 'Arztpraxis / Gesundheit',
|
||||
description: 'Gesundheitswesen, 5-50 Mitarbeiter, Patientendaten (Art. 9), hoher Schutzbedarf',
|
||||
icon: '🏥',
|
||||
profile: {
|
||||
legalForm: 'GbR',
|
||||
industry: ['healthcare'],
|
||||
businessModel: 'b2c',
|
||||
companySize: 'small',
|
||||
employeeCount: '1-9',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1-9',
|
||||
org_industry: 'healthcare',
|
||||
org_business_model: 'b2c',
|
||||
data_art9: 'yes',
|
||||
tech_encryption_transit: 'yes',
|
||||
tech_encryption_rest: 'yes',
|
||||
comp_documentation_level: 'basic',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'dpa', 'employee_dsi', 'tom_documentation', 'vvt_register', 'dsfa'],
|
||||
},
|
||||
{
|
||||
id: 'handwerk',
|
||||
label: 'Handwerksbetrieb',
|
||||
description: 'Handwerk, 5-20 Mitarbeiter, Kundendaten, einfache IT',
|
||||
icon: '🔧',
|
||||
profile: {
|
||||
legalForm: 'GmbH',
|
||||
industry: ['crafts'],
|
||||
businessModel: 'b2c',
|
||||
companySize: 'small',
|
||||
employeeCount: '1-9',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1-9',
|
||||
org_industry: 'other',
|
||||
org_business_model: 'b2c',
|
||||
data_art9: 'no',
|
||||
tech_hosting_location: 'eu',
|
||||
comp_documentation_level: 'none',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb'],
|
||||
},
|
||||
{
|
||||
id: 'education',
|
||||
label: 'Bildungseinrichtung',
|
||||
description: 'Schule, Hochschule oder Weiterbildung, 20-100 Mitarbeiter, Schuelerdaten',
|
||||
icon: '🎓',
|
||||
profile: {
|
||||
legalForm: 'gGmbH',
|
||||
industry: ['education'],
|
||||
businessModel: 'b2c',
|
||||
companySize: 'medium',
|
||||
employeeCount: '10-49',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE'],
|
||||
isDataController: true,
|
||||
isDataProcessor: false,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '10-49',
|
||||
org_industry: 'education',
|
||||
org_business_model: 'b2c',
|
||||
data_minors: 'yes',
|
||||
tech_hosting_location: 'eu',
|
||||
comp_training: 'yes',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'dpa', 'employee_dsi', 'dsfa', 'tom_documentation'],
|
||||
},
|
||||
{
|
||||
id: 'enterprise',
|
||||
label: 'Konzern / Enterprise',
|
||||
description: 'Grossunternehmen, 500+ Mitarbeiter, international, reguliert, ISO 27001',
|
||||
icon: '🏢',
|
||||
profile: {
|
||||
legalForm: 'AG',
|
||||
industry: ['finance'],
|
||||
businessModel: 'b2b',
|
||||
companySize: 'enterprise',
|
||||
employeeCount: '1000+',
|
||||
headquartersCountry: 'DE',
|
||||
targetMarkets: ['DE', 'EU', 'US'],
|
||||
isDataController: true,
|
||||
isDataProcessor: true,
|
||||
},
|
||||
scopeHints: {
|
||||
org_employee_count: '1000+',
|
||||
org_industry: 'finance',
|
||||
org_business_model: 'b2b',
|
||||
org_cert_target: 'iso27001',
|
||||
data_art9: 'yes',
|
||||
data_volume: '>1000000',
|
||||
proc_ai_usage: 'yes',
|
||||
tech_third_country: 'yes',
|
||||
tech_hosting_location: 'eu_us_adequacy',
|
||||
comp_vendor_management: 'yes',
|
||||
comp_training: 'yes',
|
||||
comp_documentation_level: 'comprehensive',
|
||||
},
|
||||
recommendedDocs: ['privacy_policy', 'impressum', 'agb', 'dpa', 'nda', 'sla', 'employee_dsi', 'applicant_dsi', 'whistleblower_policy', 'tom_documentation', 'vvt_register', 'isms_manual', 'dsfa', 'transfer_impact_assessment'],
|
||||
},
|
||||
]
|
||||
Reference in New Issue
Block a user