fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
'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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user