feat(company-profile): Datenkategorien-Infoboxen, AVV-Hinweise, Qualifikationsdaten
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 34s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 23s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 34s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 23s
CI / test-python-dsms-gateway (push) Successful in 23s
- Info-Tooltips fuer alle Datenkategorien mit typischen Beispieldaten - Neue Kategorie: Qualifikations-/Schulungsdaten (Fortbildungen, Zertifikate) - Externer Dienstleister bei Lohn-/Gehaltsabrechnung (AVV-relevant) - Externer Dienstleister bei Website-Betrieb (AVV nach Art. 28 DSGVO) - Arbeitszeiterfassung als Pflicht markiert (§3 ArbZG) - Gesundheitsdaten-Abgrenzung: Krankenkassenname ist KEIN Art. 9 Datum - Bewerbermanagement: Religion als Art. 9 ergaenzt - Online-Shop/SaaS Beschreibungen praezisiert + Warnbanner bei Doppelauswahl - Rechtsgrundlage aus Firmenprofil entfernt (gehoert in VVT-Schritt) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -281,6 +281,18 @@ function StepBusinessModel({
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Hint when both webshop and SaaS are selected */}
|
||||
{(data.offerings || []).includes('webshop') && (data.offerings || []).includes('software_saas') && (
|
||||
<div className="mt-3 flex items-start gap-2 p-3 bg-amber-50 border border-amber-200 rounded-lg">
|
||||
<svg className="w-5 h-5 text-amber-500 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<p className="text-sm text-amber-800">
|
||||
<strong>Hinweis:</strong> Wenn Sie reine Software verkaufen, genuegt <em>SaaS/Cloud</em> — <em>Online-Shop</em> ist nur fuer physische Produkte oder Hardware mit Abo-Modell gedacht.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* URL fields for selected offerings */}
|
||||
@@ -638,35 +650,30 @@ function StepDataProtection({
|
||||
|
||||
// DSGVO-Standard Datenkategorien
|
||||
const ALL_DATA_CATEGORIES = [
|
||||
{ id: 'stammdaten', label: 'Stammdaten', desc: 'Name, Geburtsdatum, Geschlecht' },
|
||||
{ id: 'kontaktdaten', label: 'Kontaktdaten', desc: 'E-Mail, Telefon, Adresse' },
|
||||
{ id: 'vertragsdaten', label: 'Vertragsdaten', desc: 'Vertragsnummer, Laufzeit, Konditionen' },
|
||||
{ id: 'zahlungsdaten', label: 'Zahlungs-/Bankdaten', desc: 'IBAN, Kreditkarte, Rechnungen' },
|
||||
{ id: 'beschaeftigtendaten', label: 'Beschäftigtendaten', desc: 'Gehalt, Arbeitszeiten, Urlaub' },
|
||||
{ id: 'kommunikation', label: 'Kommunikationsdaten', desc: 'E-Mail-Inhalte, Chat-Verläufe' },
|
||||
{ id: 'nutzungsdaten', label: 'Nutzungs-/Logdaten', desc: 'IP-Adressen, Login-Zeiten, Klicks' },
|
||||
{ id: 'standortdaten', label: 'Standortdaten', desc: 'GPS, Check-in, Lieferadressen' },
|
||||
{ id: 'bilddaten', label: 'Bild-/Videodaten', desc: 'Fotos, Videoaufnahmen, Profilbilder' },
|
||||
{ id: 'bewerberdaten', label: 'Bewerberdaten', desc: 'Lebenslauf, Zeugnisse, Anschreiben' },
|
||||
{ id: 'stammdaten', label: 'Stammdaten', desc: 'Name, Geburtsdatum, Geschlecht', info: 'Vor- und Nachname, Geburtsdatum, Geschlecht, Anrede, Titel, Familienstand, Staatsangehörigkeit, Personalnummer, Kundennummer' },
|
||||
{ id: 'kontaktdaten', label: 'Kontaktdaten', desc: 'E-Mail, Telefon, Adresse', info: 'E-Mail-Adresse, Telefonnummer, Mobilnummer, Postanschrift, Faxnummer, Messenger-IDs, Ansprechpartner-Namen bei Kunden/Lieferanten (z.B. SAP-Kontaktpersonen)' },
|
||||
{ id: 'vertragsdaten', label: 'Vertragsdaten', desc: 'Vertragsnummer, Laufzeit, Konditionen', info: 'Vertragsnummer, Vertragsbeginn/-ende, Laufzeit, Konditionen, Kündigungsfristen, Vertragsgegenstand, Bestellhistorie' },
|
||||
{ id: 'zahlungsdaten', label: 'Zahlungs-/Bankdaten', desc: 'IBAN, Kreditkarte, Rechnungen', info: 'IBAN, BIC, Kontoinhaber, Kreditkartennummer, Rechnungsbeträge, Zahlungshistorie, Steuer-ID, USt-IdNr.' },
|
||||
{ id: 'beschaeftigtendaten', label: 'Beschäftigtendaten', desc: 'Gehalt, Arbeitszeiten, Urlaub', info: 'Gehalt/Lohn, Steuerklasse, SV-Nummer, Krankenkasse (z.B. AOK, TK), Arbeitszeiten, Urlaubstage, Abwesenheiten, Beurteilungen, Eintrittsdatum' },
|
||||
{ id: 'kommunikation', label: 'Kommunikationsdaten', desc: 'E-Mail-Inhalte, Chat-Verläufe', info: 'E-Mail-Inhalte und -Metadaten, Chat-Nachrichten, Gesprächsprotokolle, Support-Tickets, Briefkorrespondenz' },
|
||||
{ id: 'nutzungsdaten', label: 'Nutzungs-/Logdaten', desc: 'IP-Adressen, Login-Zeiten, Klicks', info: 'IP-Adressen, Login-Zeitpunkte, Seitenaufrufe, Klickverhalten, Geräteinformationen, Browser-Typ, Session-Dauer' },
|
||||
{ id: 'standortdaten', label: 'Standortdaten', desc: 'GPS, Check-in, Lieferadressen', info: 'GPS-Koordinaten, Check-in/Check-out-Zeiten, Lieferadressen, Reiserouten, WLAN-Standortbestimmung' },
|
||||
{ id: 'bilddaten', label: 'Bild-/Videodaten', desc: 'Fotos, Videoaufnahmen, Profilbilder', info: 'Profilfotos, Ausweiskopien, Videoaufnahmen (Überwachung), Bewerbungsfotos, Schulungsvideos' },
|
||||
{ id: 'bewerberdaten', label: 'Bewerberdaten', desc: 'Lebenslauf, Zeugnisse, Anschreiben', info: 'Lebenslauf, Anschreiben, Zeugnisse, Referenzen, Gehaltsvorstellungen, Verfügbarkeit, Bewerbungsquelle' },
|
||||
{ id: 'qualifikationsdaten', label: 'Qualifikations-/Schulungsdaten', desc: 'Fortbildungen, Zertifikate, Abschlüsse', info: 'Besuchte Seminare und Schulungen, Zertifikate, Abschlüsse, Qualifikationsnachweise, Schulungsdaten und -ergebnisse, Weiterbildungshistorie' },
|
||||
] as const
|
||||
|
||||
const ALL_SPECIAL_CATEGORIES = [
|
||||
{ id: 'gesundheit', label: 'Gesundheitsdaten', desc: 'Krankheitstage, Atteste, Diagnosen' },
|
||||
{ id: 'biometrie', label: 'Biometrische Daten', desc: 'Fingerabdruck, Gesichtserkennung' },
|
||||
{ id: 'religion', label: 'Religiöse Überzeugungen', desc: 'Konfession, Feiertage' },
|
||||
{ id: 'gewerkschaft', label: 'Gewerkschaftszugehörigkeit', desc: 'Mitgliedschaft' },
|
||||
{ id: 'genetik', label: 'Genetische Daten', desc: 'DNA, Erbkrankheiten' },
|
||||
] as const
|
||||
|
||||
// Rechtsgrundlagen nach DSGVO Art. 6
|
||||
const LEGAL_BASES = [
|
||||
{ id: 'consent', label: 'Einwilligung (Art. 6 Abs. 1a)' },
|
||||
{ id: 'contract', label: 'Vertragserfüllung (Art. 6 Abs. 1b)' },
|
||||
{ id: 'legal', label: 'Rechtliche Verpflichtung (Art. 6 Abs. 1c)' },
|
||||
{ id: 'interest', label: 'Berechtigtes Interesse (Art. 6 Abs. 1f)' },
|
||||
{ id: 'gesundheit', label: 'Gesundheitsdaten', desc: 'Krankheitstage, Atteste, Diagnosen', info: 'Krankheitstage, AU-Bescheinigungen, Diagnosen, Behinderungsgrad (GdB), BEM-Daten, arbeitsmedizinische Untersuchungen, Impfstatus, Allergien. NICHT: Krankenkassenname (z.B. AOK, TK) — das sind normale Beschäftigtendaten.' },
|
||||
{ id: 'biometrie', label: 'Biometrische Daten', desc: 'Fingerabdruck, Gesichtserkennung', info: 'Fingerabdruck, Gesichtserkennung, Iris-Scan, Stimmerkennung, Handvenenscan (zur eindeutigen Identifizierung)' },
|
||||
{ id: 'religion', label: 'Religiöse Überzeugungen', desc: 'Konfession, Feiertage', info: 'Konfession (relevant für Kirchensteuer), religiöse Feiertage, Ernährungsvorschriften' },
|
||||
{ id: 'gewerkschaft', label: 'Gewerkschaftszugehörigkeit', desc: 'Mitgliedschaft', info: 'Gewerkschaftsmitgliedschaft, Betriebsratszugehörigkeit, Tarifzugehörigkeit' },
|
||||
{ id: 'genetik', label: 'Genetische Daten', desc: 'DNA, Erbkrankheiten', info: 'DNA-Analysen, genetische Prädispositionen, Erbkrankheitsrisiken (nur in Spezialfällen relevant)' },
|
||||
] as const
|
||||
|
||||
// Verarbeitungstätigkeiten mit aktivitätsspezifischen Datenkategorien
|
||||
// Hinweis: Rechtsgrundlage (legal_basis) wird hier im Profil nicht angezeigt —
|
||||
// die juristische Zuordnung erfolgt im VVT-Schritt (Art. 30 DSGVO).
|
||||
interface ActivityTemplate {
|
||||
id: string
|
||||
name: string
|
||||
@@ -674,6 +681,8 @@ interface ActivityTemplate {
|
||||
primary_categories: string[] // Sichtbar + vorausgewählt
|
||||
art9_relevant: string[] // Art. 9 Kategorien die plausibel relevant sind
|
||||
default_legal_basis: string
|
||||
legalHint?: string // Gesetzlicher Hinweis (z.B. ArbZG-Pflicht)
|
||||
hasServiceProvider?: boolean // Kann ein externer Dienstleister eingesetzt werden?
|
||||
}
|
||||
|
||||
interface ActivityDepartment {
|
||||
@@ -690,17 +699,17 @@ const UNIVERSAL_DEPARTMENTS: ActivityDepartment[] = [
|
||||
id: 'personal', name: 'Personal / HR', icon: '👥',
|
||||
activities: [
|
||||
{ id: 'personalverwaltung', name: 'Personalverwaltung', purpose: 'Verwaltung von Beschäftigtendaten für das Arbeitsverhältnis', primary_categories: ['stammdaten', 'kontaktdaten', 'beschaeftigtendaten', 'zahlungsdaten'], art9_relevant: ['gesundheit', 'religion', 'gewerkschaft'], default_legal_basis: 'contract' },
|
||||
{ id: 'lohnbuchhaltung', name: 'Lohn- und Gehaltsabrechnung', purpose: 'Berechnung und Auszahlung von Löhnen und Gehältern', primary_categories: ['beschaeftigtendaten', 'zahlungsdaten', 'stammdaten'], art9_relevant: ['gesundheit', 'religion'], default_legal_basis: 'legal' },
|
||||
{ id: 'bewerbermanagement', name: 'Bewerbermanagement', purpose: 'Entgegennahme, Prüfung und Bearbeitung von Bewerbungen', primary_categories: ['bewerberdaten', 'stammdaten', 'kontaktdaten', 'kommunikation'], art9_relevant: ['gesundheit'], default_legal_basis: 'consent' },
|
||||
{ id: 'arbeitszeiterfassung', name: 'Arbeitszeiterfassung', purpose: 'Erfassung und Dokumentation der Arbeitszeiten', primary_categories: ['beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'legal' },
|
||||
{ id: 'weiterbildung', name: 'Fort- und Weiterbildung', purpose: 'Verwaltung von Schulungen und Weiterbildungsmaßnahmen', primary_categories: ['beschaeftigtendaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'contract' },
|
||||
{ id: 'lohnbuchhaltung', name: 'Lohn- und Gehaltsabrechnung', purpose: 'Berechnung und Auszahlung von Löhnen und Gehältern', primary_categories: ['beschaeftigtendaten', 'zahlungsdaten', 'stammdaten'], art9_relevant: ['gesundheit', 'religion'], default_legal_basis: 'legal', hasServiceProvider: true },
|
||||
{ id: 'bewerbermanagement', name: 'Bewerbermanagement', purpose: 'Entgegennahme, Prüfung und Bearbeitung von Bewerbungen', primary_categories: ['bewerberdaten', 'stammdaten', 'kontaktdaten', 'kommunikation', 'qualifikationsdaten'], art9_relevant: ['gesundheit', 'religion'], default_legal_basis: 'consent' },
|
||||
{ id: 'arbeitszeiterfassung', name: 'Arbeitszeiterfassung', purpose: 'Erfassung und Dokumentation der Arbeitszeiten', primary_categories: ['beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'legal', legalHint: 'Gesetzlich vorgeschrieben (§ 3 ArbZG). Fehlende Arbeitszeiterfassung ist ein Compliance-Risiko.' },
|
||||
{ id: 'weiterbildung', name: 'Fort- und Weiterbildung', purpose: 'Verwaltung von Schulungen und Weiterbildungsmaßnahmen', primary_categories: ['qualifikationsdaten', 'beschaeftigtendaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'contract' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'finanzen', name: 'Finanzen / Buchhaltung', icon: '💰',
|
||||
activities: [
|
||||
{ id: 'finanzbuchhaltung', name: 'Finanzbuchhaltung', purpose: 'Buchführung, Rechnungsstellung, steuerliche Dokumentation', primary_categories: ['stammdaten', 'zahlungsdaten', 'vertragsdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'legal' },
|
||||
{ id: 'zahlungsverkehr', name: 'Zahlungsverkehr', purpose: 'Abwicklung von ein- und ausgehenden Zahlungen', primary_categories: ['zahlungsdaten', 'stammdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'contract' },
|
||||
{ id: 'zahlungsverkehr', name: 'Zahlungsverkehr', purpose: 'Abwicklung von ein- und ausgehenden Zahlungen', primary_categories: ['zahlungsdaten', 'stammdaten', 'kontaktdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'contract' },
|
||||
{ id: 'mahnwesen', name: 'Mahnwesen / Inkasso', purpose: 'Überwachung offener Forderungen und Mahnverfahren', primary_categories: ['stammdaten', 'kontaktdaten', 'zahlungsdaten', 'vertragsdaten'], art9_relevant: [], default_legal_basis: 'interest' },
|
||||
{ id: 'reisekostenabrechnung', name: 'Reisekostenabrechnung', purpose: 'Abrechnung und Erstattung von Dienstreisekosten', primary_categories: ['beschaeftigtendaten', 'zahlungsdaten', 'standortdaten'], art9_relevant: [], default_legal_basis: 'contract' },
|
||||
],
|
||||
@@ -728,7 +737,7 @@ const UNIVERSAL_DEPARTMENTS: ActivityDepartment[] = [
|
||||
{ id: 'zugangsverwaltung', name: 'Zugangsverwaltung (IAM)', purpose: 'Verwaltung von Benutzerkonten, Passwörtern und Zugriffsrechten', primary_categories: ['beschaeftigtendaten', 'stammdaten', 'nutzungsdaten'], art9_relevant: ['biometrie'], default_legal_basis: 'contract' },
|
||||
{ id: 'email_kommunikation', name: 'E-Mail-Kommunikation', purpose: 'Geschäftliche E-Mail-Korrespondenz', primary_categories: ['kontaktdaten', 'kommunikation', 'stammdaten'], art9_relevant: [], default_legal_basis: 'interest' },
|
||||
{ id: 'datensicherung', name: 'Datensicherung / Backup', purpose: 'Sicherung von Unternehmensdaten zum Schutz vor Datenverlust', primary_categories: ['nutzungsdaten', 'beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'interest' },
|
||||
{ id: 'website_betrieb', name: 'Website-Betrieb', purpose: 'Bereitstellung der Unternehmenswebsite und Kontaktformulare', primary_categories: ['nutzungsdaten', 'kontaktdaten'], art9_relevant: [], default_legal_basis: 'interest' },
|
||||
{ id: 'website_betrieb', name: 'Website-Betrieb', purpose: 'Bereitstellung der Unternehmenswebsite und Kontaktformulare', primary_categories: ['nutzungsdaten', 'kontaktdaten', 'stammdaten'], art9_relevant: [], default_legal_basis: 'interest', hasServiceProvider: true, legalHint: 'Bei externem Website-Management: AVV nach Art. 28 DSGVO mit dem Dienstleister erforderlich. Cookies, Analytics und Kontaktformulare verarbeiten personenbezogene Daten — auch wenn der Dienstleister sie technisch betreibt, bleibt Ihr Unternehmen verantwortlich.' },
|
||||
{ id: 'it_sicherheit', name: 'IT-Sicherheit / Logging', purpose: 'Überwachung der IT-Sicherheit, Log-Analyse, Vorfallbehandlung', primary_categories: ['nutzungsdaten', 'beschaeftigtendaten'], art9_relevant: [], default_legal_basis: 'interest' },
|
||||
],
|
||||
},
|
||||
@@ -858,6 +867,8 @@ interface ProcessingActivity {
|
||||
legal_basis: string
|
||||
department?: string
|
||||
custom?: boolean
|
||||
usesServiceProvider?: boolean
|
||||
serviceProviderName?: string
|
||||
}
|
||||
|
||||
interface AISystem {
|
||||
@@ -889,6 +900,7 @@ function StepProcessingAndAI({
|
||||
const [expandedActivity, setExpandedActivity] = useState<string | null>(null)
|
||||
const [collapsedDepts, setCollapsedDepts] = useState<Set<string>>(new Set())
|
||||
const [showExtraCategories, setShowExtraCategories] = useState<Set<string>>(new Set())
|
||||
const [expandedInfoCat, setExpandedInfoCat] = useState<string | null>(null)
|
||||
|
||||
const departments = getRelevantDepartments(industry, data.businessModel, data.companySize)
|
||||
const activeIds = new Set(activities.map(a => a.id))
|
||||
@@ -978,6 +990,36 @@ function StepProcessingAndAI({
|
||||
onChange({ aiSystems: updated })
|
||||
}
|
||||
|
||||
// Render a data category checkbox with info tooltip
|
||||
const renderCategoryCheckbox = (cat: { id: string; label: string; desc: string; info: string }, activity: ProcessingActivity, variant: 'normal' | 'extra' | 'art9' | 'art9-extra') => {
|
||||
const isInfoExpanded = expandedInfoCat === `${activity.id}-${cat.id}`
|
||||
const colorClasses = variant.startsWith('art9')
|
||||
? { check: 'text-red-600 focus:ring-red-500', hover: 'hover:bg-red-100', text: variant === 'art9-extra' ? 'text-gray-500' : 'text-gray-700' }
|
||||
: { check: 'text-purple-600 focus:ring-purple-500', hover: 'hover:bg-gray-100', text: variant === 'extra' ? 'text-gray-500' : 'text-gray-700' }
|
||||
|
||||
return (
|
||||
<div key={cat.id}>
|
||||
<label className={`flex items-center gap-2 text-xs p-1.5 rounded ${colorClasses.hover} cursor-pointer`}>
|
||||
<input type="checkbox" checked={activity.data_categories.includes(cat.id)} onChange={() => toggleDataCategory(activity.id, cat.id)} className={`w-3.5 h-3.5 ${colorClasses.check} rounded`} />
|
||||
<span className={colorClasses.text}>{cat.label}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={e => { e.preventDefault(); e.stopPropagation(); setExpandedInfoCat(isInfoExpanded ? null : `${activity.id}-${cat.id}`) }}
|
||||
className="ml-auto w-4 h-4 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 text-gray-500 text-[10px] font-bold flex-shrink-0"
|
||||
title={cat.info}
|
||||
>
|
||||
i
|
||||
</button>
|
||||
</label>
|
||||
{isInfoExpanded && (
|
||||
<div className="ml-7 mt-1 mb-1 px-2 py-1.5 bg-blue-50 border border-blue-100 rounded text-[11px] text-blue-800">
|
||||
{cat.info}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render activity detail panel (shared between template and custom)
|
||||
const renderActivityDetail = (activity: ProcessingActivity, template: ActivityTemplate | null) => {
|
||||
const primaryIds = new Set(template?.primary_categories || [])
|
||||
@@ -992,6 +1034,14 @@ function StepProcessingAndAI({
|
||||
|
||||
return (
|
||||
<div className="ml-4 mt-2 p-4 bg-gray-50 rounded-lg border border-gray-200 space-y-4">
|
||||
{/* Legal hint (e.g. ArbZG for Arbeitszeiterfassung) */}
|
||||
{template?.legalHint && (
|
||||
<div className="flex items-start gap-2 px-3 py-2 bg-amber-50 border border-amber-200 rounded-lg">
|
||||
<span className="text-amber-600 text-sm mt-0.5">⚠</span>
|
||||
<span className="text-xs text-amber-800">{template.legalHint}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Custom: name + purpose fields */}
|
||||
{isCustom && (
|
||||
<div className="grid grid-cols-1 gap-3">
|
||||
@@ -1000,16 +1050,43 @@ function StepProcessingAndAI({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Service Provider option (e.g. for Lohn- und Gehaltsabrechnung) */}
|
||||
{template?.hasServiceProvider && (
|
||||
<div className="bg-blue-50 rounded-lg p-3 border border-blue-100 space-y-2">
|
||||
<label className="flex items-center gap-2 text-xs cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={activity.usesServiceProvider || false}
|
||||
onChange={e => updateActivity(activity.id, {
|
||||
usesServiceProvider: e.target.checked,
|
||||
...(!e.target.checked ? { serviceProviderName: '' } : {})
|
||||
})}
|
||||
className="w-3.5 h-3.5 text-blue-600 rounded focus:ring-blue-500"
|
||||
/>
|
||||
<span className="text-blue-800 font-medium">Externer Dienstleister wird eingesetzt</span>
|
||||
</label>
|
||||
{activity.usesServiceProvider && (
|
||||
<div className="ml-6">
|
||||
<input
|
||||
type="text"
|
||||
value={activity.serviceProviderName || ''}
|
||||
onChange={e => updateActivity(activity.id, { serviceProviderName: e.target.value })}
|
||||
placeholder="Name des Dienstleisters (optional)"
|
||||
className="w-full px-3 py-1.5 border border-blue-200 rounded text-xs focus:ring-2 focus:ring-blue-400 focus:border-transparent bg-white"
|
||||
/>
|
||||
<p className="text-[10px] text-blue-600 mt-1">Wird als Auftragsverarbeiter (AVV) im VVT erfasst.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Primary Data Categories */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 mb-2">Betroffene Datenkategorien</label>
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{(isCustom ? ALL_DATA_CATEGORIES : primaryCats).map(cat => (
|
||||
<label key={cat.id} className="flex items-center gap-2 text-xs p-1.5 rounded hover:bg-gray-100 cursor-pointer">
|
||||
<input type="checkbox" checked={activity.data_categories.includes(cat.id)} onChange={() => toggleDataCategory(activity.id, cat.id)} className="w-3.5 h-3.5 text-purple-600 rounded focus:ring-purple-500" />
|
||||
<span className="text-gray-700" title={cat.desc}>{cat.label}</span>
|
||||
</label>
|
||||
))}
|
||||
{(isCustom ? ALL_DATA_CATEGORIES : primaryCats).map(cat =>
|
||||
renderCategoryCheckbox(cat, activity, isCustom ? 'normal' : 'normal')
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1021,12 +1098,7 @@ function StepProcessingAndAI({
|
||||
</button>
|
||||
{showingExtra && (
|
||||
<div className="grid grid-cols-2 gap-1.5 mt-2">
|
||||
{extraCats.map(cat => (
|
||||
<label key={cat.id} className="flex items-center gap-2 text-xs p-1.5 rounded hover:bg-gray-100 cursor-pointer">
|
||||
<input type="checkbox" checked={activity.data_categories.includes(cat.id)} onChange={() => toggleDataCategory(activity.id, cat.id)} className="w-3.5 h-3.5 text-purple-600 rounded focus:ring-purple-500" />
|
||||
<span className="text-gray-500" title={cat.desc}>{cat.label}</span>
|
||||
</label>
|
||||
))}
|
||||
{extraCats.map(cat => renderCategoryCheckbox(cat, activity, 'extra'))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1037,40 +1109,21 @@ function StepProcessingAndAI({
|
||||
<div className="bg-red-50 rounded-lg p-3 border border-red-100">
|
||||
<label className="block text-xs font-medium text-red-700 mb-2">
|
||||
Besondere Kategorien (Art. 9 DSGVO)
|
||||
<span className="font-normal text-red-400 ml-1">— erfordern zusätzliche Rechtsgrundlage</span>
|
||||
</label>
|
||||
<div className="grid grid-cols-2 gap-1.5">
|
||||
{(isCustom ? ALL_SPECIAL_CATEGORIES : relevantArt9).map(cat => (
|
||||
<label key={cat.id} className="flex items-center gap-2 text-xs p-1.5 rounded hover:bg-red-100 cursor-pointer">
|
||||
<input type="checkbox" checked={activity.data_categories.includes(cat.id)} onChange={() => toggleDataCategory(activity.id, cat.id)} className="w-3.5 h-3.5 text-red-600 rounded focus:ring-red-500" />
|
||||
<span className="text-gray-700" title={cat.desc}>{cat.label}</span>
|
||||
</label>
|
||||
))}
|
||||
{(isCustom ? ALL_SPECIAL_CATEGORIES : relevantArt9).map(cat =>
|
||||
renderCategoryCheckbox(cat, activity, 'art9')
|
||||
)}
|
||||
</div>
|
||||
{/* Show remaining Art. 9 categories if expanded */}
|
||||
{!isCustom && otherArt9.length > 0 && showingExtra && (
|
||||
<div className="grid grid-cols-2 gap-1.5 mt-2 pt-2 border-t border-red-100">
|
||||
{otherArt9.map(cat => (
|
||||
<label key={cat.id} className="flex items-center gap-2 text-xs p-1.5 rounded hover:bg-red-100 cursor-pointer">
|
||||
<input type="checkbox" checked={activity.data_categories.includes(cat.id)} onChange={() => toggleDataCategory(activity.id, cat.id)} className="w-3.5 h-3.5 text-red-600 rounded focus:ring-red-500" />
|
||||
<span className="text-gray-500" title={cat.desc}>{cat.label}</span>
|
||||
</label>
|
||||
))}
|
||||
{otherArt9.map(cat => renderCategoryCheckbox(cat, activity, 'art9-extra'))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Legal Basis */}
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-600 mb-2">Rechtsgrundlage</label>
|
||||
<select value={activity.legal_basis} onChange={e => updateActivity(activity.id, { legal_basis: e.target.value })} className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-purple-500 focus:border-transparent">
|
||||
{LEGAL_BASES.map(lb => (
|
||||
<option key={lb.id} value={lb.id}>{lb.label}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="button" onClick={() => removeActivity(activity.id)} className="text-xs text-red-500 hover:text-red-700">
|
||||
Verarbeitungstätigkeit entfernen
|
||||
</button>
|
||||
@@ -1142,7 +1195,15 @@ function StepProcessingAndAI({
|
||||
className="w-4 h-4 text-purple-600 rounded focus:ring-purple-500 flex-shrink-0"
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="text-sm font-medium text-gray-900">{template.name}</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium text-gray-900">{template.name}</span>
|
||||
{template.legalHint && (
|
||||
<span className="text-[10px] bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded font-medium whitespace-nowrap">Pflicht</span>
|
||||
)}
|
||||
{template.hasServiceProvider && (
|
||||
<span className="text-[10px] bg-blue-100 text-blue-700 px-1.5 py-0.5 rounded font-medium whitespace-nowrap">AVV-relevant</span>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-gray-500 truncate">{template.purpose}</p>
|
||||
</div>
|
||||
{isActive && (
|
||||
|
||||
@@ -232,9 +232,9 @@ export const OFFERING_TYPE_LABELS: Record<OfferingType, { label: string; descrip
|
||||
app_mobile: { label: 'Mobile App', description: 'iOS/Android Anwendungen' },
|
||||
app_web: { label: 'Web-Anwendung', description: 'Browser-basierte Software' },
|
||||
website: { label: 'Website', description: 'Informationsseiten, Landing Pages' },
|
||||
webshop: { label: 'Online-Shop', description: 'E-Commerce, Produktverkauf' },
|
||||
webshop: { label: 'Online-Shop', description: 'Physische Produkte oder Hardware-Abos verkaufen' },
|
||||
hardware: { label: 'Hardware-Verkauf', description: 'Physische Produkte' },
|
||||
software_saas: { label: 'SaaS/Cloud', description: 'Software as a Service' },
|
||||
software_saas: { label: 'SaaS/Cloud', description: 'Software online bereitstellen (auch wenn ueber einen Shop verkauft)' },
|
||||
software_onpremise: { label: 'On-Premise Software', description: 'Lokale Installation' },
|
||||
services_consulting: { label: 'Beratung', description: 'Consulting, Professional Services' },
|
||||
services_agency: { label: 'Agentur', description: 'Marketing, Design, Entwicklung' },
|
||||
|
||||
Reference in New Issue
Block a user