Files
breakpilot-compliance/breakpilot-compliance-sdk/packages/react/src/components/DSRPortal.tsx
Benjamin Boenisch 4435e7ea0a Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance,
AI-Compliance-SDK, Consent-SDK, Developer-Portal,
PCA-Platform, DSMS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:28 +01:00

241 lines
7.5 KiB
TypeScript

'use client'
import React, { useState, useCallback } from 'react'
import { useCompliance } from '../provider'
import type { DSRRequestType } from '@breakpilot/compliance-sdk-types'
export interface DSRPortalProps {
onSubmit?: (type: DSRRequestType, email: string, name: string) => void
className?: string
style?: React.CSSProperties
}
const DSR_TYPES: Record<DSRRequestType, { name: string; description: string }> = {
ACCESS: {
name: 'Auskunft (Art. 15)',
description: 'Welche Daten haben Sie über mich gespeichert?',
},
RECTIFICATION: {
name: 'Berichtigung (Art. 16)',
description: 'Korrigieren Sie falsche Daten über mich.',
},
ERASURE: {
name: 'Löschung (Art. 17)',
description: 'Löschen Sie alle meine personenbezogenen Daten.',
},
PORTABILITY: {
name: 'Datenübertragbarkeit (Art. 20)',
description: 'Exportieren Sie meine Daten in einem maschinenlesbaren Format.',
},
RESTRICTION: {
name: 'Einschränkung (Art. 18)',
description: 'Schränken Sie die Verarbeitung meiner Daten ein.',
},
OBJECTION: {
name: 'Widerspruch (Art. 21)',
description: 'Ich widerspreche der Verarbeitung meiner Daten.',
},
}
export function DSRPortal({ onSubmit, className, style }: DSRPortalProps) {
const { dsgvo } = useCompliance()
const [selectedType, setSelectedType] = useState<DSRRequestType | null>(null)
const [email, setEmail] = useState('')
const [name, setName] = useState('')
const [additionalInfo, setAdditionalInfo] = useState('')
const [isSubmitting, setIsSubmitting] = useState(false)
const [submitted, setSubmitted] = useState(false)
const [error, setError] = useState<string | null>(null)
const handleSubmit = useCallback(
async (e: React.FormEvent) => {
e.preventDefault()
if (!selectedType || !email || !name) return
setIsSubmitting(true)
setError(null)
try {
await dsgvo.submitDSR(selectedType, email, name)
onSubmit?.(selectedType, email, name)
setSubmitted(true)
} catch (err) {
setError(err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten')
} finally {
setIsSubmitting(false)
}
},
[selectedType, email, name, dsgvo, onSubmit]
)
const containerStyle: React.CSSProperties = {
fontFamily: 'system-ui, -apple-system, sans-serif',
maxWidth: '600px',
margin: '0 auto',
padding: '20px',
...style,
}
const inputStyle: React.CSSProperties = {
width: '100%',
padding: '12px',
fontSize: '14px',
border: '1px solid #ddd',
borderRadius: '4px',
marginBottom: '15px',
boxSizing: 'border-box',
}
const buttonStyle: React.CSSProperties = {
padding: '12px 24px',
fontSize: '16px',
fontWeight: 500,
color: '#fff',
backgroundColor: '#1a1a1a',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
width: '100%',
}
if (submitted) {
return (
<div style={containerStyle} className={className}>
<div
style={{
textAlign: 'center',
padding: '40px 20px',
backgroundColor: '#f0fdf4',
borderRadius: '8px',
}}
>
<div style={{ fontSize: '48px', marginBottom: '20px' }}></div>
<h2 style={{ margin: '0 0 10px', color: '#166534' }}>Anfrage eingereicht</h2>
<p style={{ margin: 0, color: '#166534' }}>
Wir werden Ihre Anfrage innerhalb von 30 Tagen bearbeiten. Sie erhalten eine
Bestätigung per E-Mail an {email}.
</p>
</div>
</div>
)
}
return (
<div style={containerStyle} className={className}>
<h2 style={{ margin: '0 0 10px' }}>Betroffenenrechte-Portal</h2>
<p style={{ margin: '0 0 20px', color: '#666' }}>
Hier können Sie Ihre Rechte gemäß DSGVO wahrnehmen. Wählen Sie die gewünschte Anfrage und
füllen Sie das Formular aus.
</p>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', fontWeight: 500, marginBottom: '10px' }}>
Art der Anfrage *
</label>
<div style={{ display: 'grid', gap: '10px' }}>
{Object.entries(DSR_TYPES).map(([type, { name, description }]) => (
<label
key={type}
style={{
display: 'flex',
padding: '15px',
border: `2px solid ${selectedType === type ? '#1a1a1a' : '#ddd'}`,
borderRadius: '8px',
cursor: 'pointer',
backgroundColor: selectedType === type ? '#f5f5f5' : '#fff',
}}
>
<input
type="radio"
name="dsrType"
value={type}
checked={selectedType === type}
onChange={() => setSelectedType(type as DSRRequestType)}
style={{ marginRight: '15px' }}
/>
<div>
<div style={{ fontWeight: 500 }}>{name}</div>
<div style={{ fontSize: '13px', color: '#666' }}>{description}</div>
</div>
</label>
))}
</div>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', fontWeight: 500, marginBottom: '5px' }}>
Ihr Name *
</label>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
required
placeholder="Max Mustermann"
style={inputStyle}
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', fontWeight: 500, marginBottom: '5px' }}>
E-Mail-Adresse *
</label>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
required
placeholder="max@example.com"
style={inputStyle}
/>
</div>
<div style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', fontWeight: 500, marginBottom: '5px' }}>
Zusätzliche Informationen (optional)
</label>
<textarea
value={additionalInfo}
onChange={e => setAdditionalInfo(e.target.value)}
placeholder="Weitere Details zu Ihrer Anfrage..."
rows={4}
style={{ ...inputStyle, resize: 'vertical' }}
/>
</div>
{error && (
<div
style={{
padding: '12px',
backgroundColor: '#fef2f2',
color: '#dc2626',
borderRadius: '4px',
marginBottom: '15px',
}}
>
{error}
</div>
)}
<button
type="submit"
disabled={!selectedType || !email || !name || isSubmitting}
style={{
...buttonStyle,
opacity: !selectedType || !email || !name || isSubmitting ? 0.5 : 1,
cursor: !selectedType || !email || !name || isSubmitting ? 'not-allowed' : 'pointer',
}}
>
{isSubmitting ? 'Wird gesendet...' : 'Anfrage einreichen'}
</button>
</form>
<p style={{ marginTop: '20px', fontSize: '12px', color: '#666' }}>
Ihre Anfrage wird gemäß Art. 12 DSGVO innerhalb von einem Monat bearbeitet. In komplexen
Fällen kann diese Frist um weitere zwei Monate verlängert werden.
</p>
</div>
)
}