refactor(admin): split 8 oversized lib/ files into focused modules under 500 LOC

Split these files that exceeded the 500-line hard cap:
- privacy-policy.ts (965 LOC) -> sections + renderers
- academy/api.ts (787 LOC) -> courses + mock-data
- whistleblower/api.ts (755 LOC) -> operations + mock-data
- vvt-profiling.ts (659 LOC) -> data + logic
- cookie-banner.ts (595 LOC) -> config + embed
- dsr/types.ts (581 LOC) -> core + api types
- tom-generator/rules-engine.ts (560 LOC) -> evaluator + gap-analysis
- datapoint-helpers.ts (548 LOC) -> generators + validators

Each original file becomes a barrel re-export for backward compatibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-10 21:05:59 +02:00
parent be4d58009a
commit 528abc86ab
24 changed files with 4471 additions and 5402 deletions

View File

@@ -0,0 +1,243 @@
/**
* DSR Types — API Types, Communication, Audit, Templates, Statistics & Helpers
*/
import type {
DSRType,
DSRStatus,
DSRPriority,
DSRSource,
DSRRequester,
DSRAssignment,
DSRRequest,
DSRDataExport,
IdentityVerificationMethod,
CommunicationType,
CommunicationChannel,
DSRTypeInfo,
} from './types-core'
export { DSR_TYPE_INFO } from './types-core'
// =============================================================================
// COMMUNICATION
// =============================================================================
export interface DSRCommunication {
id: string
dsrId: string
type: CommunicationType
channel: CommunicationChannel
subject?: string
content: string
templateUsed?: string
attachments?: {
name: string
url: string
size: number
type: string
}[]
sentAt?: string
sentBy?: string
receivedAt?: string
createdAt: string
createdBy: string
}
// =============================================================================
// AUDIT LOG
// =============================================================================
export interface DSRAuditEntry {
id: string
dsrId: string
action: string
previousValue?: string
newValue?: string
performedBy: string
performedAt: string
notes?: string
}
// =============================================================================
// EMAIL TEMPLATES
// =============================================================================
export interface DSREmailTemplate {
id: string
name: string
subject: string
body: string
type: DSRType | 'general'
stage: DSRStatus | 'identity_request' | 'deadline_extension' | 'completion'
language: 'de' | 'en'
variables: string[]
}
export const DSR_EMAIL_TEMPLATES: DSREmailTemplate[] = [
{
id: 'intake_confirmation',
name: 'Eingangsbestaetigung',
subject: 'Bestaetigung Ihrer Anfrage - {{referenceNumber}}',
body: `Sehr geehrte(r) {{requesterName}},
wir bestaetigen den Eingang Ihrer Anfrage vom {{receivedDate}}.
Referenznummer: {{referenceNumber}}
Art der Anfrage: {{requestType}}
Wir werden Ihre Anfrage innerhalb der gesetzlichen Frist von {{deadline}} bearbeiten.
Mit freundlichen Gruessen
{{senderName}}
Datenschutzbeauftragter`,
type: 'general',
stage: 'intake',
language: 'de',
variables: ['requesterName', 'receivedDate', 'referenceNumber', 'requestType', 'deadline', 'senderName']
},
{
id: 'identity_request',
name: 'Identitaetsanfrage',
subject: 'Identitaetspruefung erforderlich - {{referenceNumber}}',
body: `Sehr geehrte(r) {{requesterName}},
um Ihre Anfrage bearbeiten zu koennen, benoetigen wir einen Nachweis Ihrer Identitaet.
Bitte senden Sie uns eines der folgenden Dokumente:
- Kopie Ihres Personalausweises (Vorder- und Rueckseite)
- Kopie Ihres Reisepasses
Ihre Daten werden ausschliesslich zur Identitaetspruefung verwendet und anschliessend geloescht.
Mit freundlichen Gruessen
{{senderName}}
Datenschutzbeauftragter`,
type: 'general',
stage: 'identity_request',
language: 'de',
variables: ['requesterName', 'referenceNumber', 'senderName']
}
]
// =============================================================================
// API TYPES
// =============================================================================
export interface DSRFilters {
status?: DSRStatus | DSRStatus[]
type?: DSRType | DSRType[]
priority?: DSRPriority
assignedTo?: string
overdue?: boolean
search?: string
dateFrom?: string
dateTo?: string
}
export interface DSRListResponse {
requests: DSRRequest[]
total: number
page: number
pageSize: number
}
export interface DSRCreateRequest {
type: DSRType
requester: DSRRequester
source: DSRSource
sourceDetails?: string
requestText?: string
priority?: DSRPriority
}
export interface DSRUpdateRequest {
status?: DSRStatus
priority?: DSRPriority
notes?: string
internalNotes?: string
assignment?: DSRAssignment
}
export interface DSRVerifyIdentityRequest {
method: IdentityVerificationMethod
notes?: string
documentRef?: string
}
export interface DSRCompleteRequest {
completionNotes?: string
dataExport?: DSRDataExport
}
export interface DSRRejectRequest {
reason: string
legalBasis?: string
}
export interface DSRExtendDeadlineRequest {
extensionMonths: 1 | 2
reason: string
}
export interface DSRSendCommunicationRequest {
type: CommunicationType
channel: CommunicationChannel
subject?: string
content: string
templateId?: string
}
// =============================================================================
// STATISTICS
// =============================================================================
export interface DSRStatistics {
total: number
byStatus: Record<DSRStatus, number>
byType: Record<DSRType, number>
overdue: number
dueThisWeek: number
averageProcessingDays: number
completedThisMonth: number
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
export function getDaysRemaining(deadline: string): number {
const deadlineDate = new Date(deadline)
const now = new Date()
const diff = deadlineDate.getTime() - now.getTime()
return Math.ceil(diff / (1000 * 60 * 60 * 24))
}
export function isOverdue(request: DSRRequest): boolean {
if (request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled') {
return false
}
return getDaysRemaining(request.deadline.currentDeadline) < 0
}
export function isUrgent(request: DSRRequest, thresholdDays: number = 7): boolean {
if (request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled') {
return false
}
const daysRemaining = getDaysRemaining(request.deadline.currentDeadline)
return daysRemaining >= 0 && daysRemaining <= thresholdDays
}
export function generateReferenceNumber(year: number, sequence: number): string {
return `DSR-${year}-${String(sequence).padStart(6, '0')}`
}
export function getTypeInfo(type: DSRType): DSRTypeInfo {
const { DSR_TYPE_INFO } = require('./types-core')
return DSR_TYPE_INFO[type]
}
export function getStatusInfo(status: DSRStatus) {
const { DSR_STATUS_INFO } = require('./types-core')
return DSR_STATUS_INFO[status]
}

View File

@@ -0,0 +1,235 @@
/**
* DSR (Data Subject Request) Types — Core Types & Constants
*
* Enums, constants, metadata, and main interfaces for GDPR Art. 15-21
*/
// =============================================================================
// ENUMS & CONSTANTS
// =============================================================================
export type DSRType =
| 'access' // Art. 15
| 'rectification' // Art. 16
| 'erasure' // Art. 17
| 'restriction' // Art. 18
| 'portability' // Art. 20
| 'objection' // Art. 21
export type DSRStatus =
| 'intake'
| 'identity_verification'
| 'processing'
| 'completed'
| 'rejected'
| 'cancelled'
export type DSRPriority = 'low' | 'normal' | 'high' | 'critical'
export type DSRSource =
| 'web_form' | 'email' | 'letter' | 'phone' | 'in_person' | 'other'
export type IdentityVerificationMethod =
| 'id_document' | 'email' | 'phone' | 'postal' | 'existing_account' | 'other'
export type CommunicationType = 'incoming' | 'outgoing' | 'internal'
export type CommunicationChannel =
| 'email' | 'letter' | 'phone' | 'portal' | 'internal_note'
// =============================================================================
// DSR TYPE METADATA
// =============================================================================
export interface DSRTypeInfo {
type: DSRType
article: string
label: string
labelShort: string
description: string
defaultDeadlineDays: number
maxExtensionMonths: number
color: string
bgColor: string
processDocument?: string
}
export const DSR_TYPE_INFO: Record<DSRType, DSRTypeInfo> = {
access: {
type: 'access', article: 'Art. 15', label: 'Auskunftsrecht', labelShort: 'Auskunft',
description: 'Recht auf Auskunft ueber gespeicherte personenbezogene Daten',
defaultDeadlineDays: 30, maxExtensionMonths: 2,
color: 'text-blue-700', bgColor: 'bg-blue-100',
processDocument: 'Prozessbeschreibung Art. 15 DSGVO_v02.pdf'
},
rectification: {
type: 'rectification', article: 'Art. 16', label: 'Berichtigungsrecht', labelShort: 'Berichtigung',
description: 'Recht auf Berichtigung unrichtiger personenbezogener Daten',
defaultDeadlineDays: 14, maxExtensionMonths: 2,
color: 'text-yellow-700', bgColor: 'bg-yellow-100',
processDocument: 'Prozessbeschriebung Art. 16 DSGVO_v02.pdf'
},
erasure: {
type: 'erasure', article: 'Art. 17', label: 'Loeschungsrecht', labelShort: 'Loeschung',
description: 'Recht auf Loeschung personenbezogener Daten ("Recht auf Vergessenwerden")',
defaultDeadlineDays: 14, maxExtensionMonths: 2,
color: 'text-red-700', bgColor: 'bg-red-100',
processDocument: 'Prozessbeschreibung Art. 17 DSGVO_v03.pdf'
},
restriction: {
type: 'restriction', article: 'Art. 18', label: 'Einschraenkungsrecht', labelShort: 'Einschraenkung',
description: 'Recht auf Einschraenkung der Verarbeitung',
defaultDeadlineDays: 14, maxExtensionMonths: 2,
color: 'text-orange-700', bgColor: 'bg-orange-100',
processDocument: 'Prozessbeschreibung Art. 18 DSGVO_v01.pdf'
},
portability: {
type: 'portability', article: 'Art. 20', label: 'Datenuebertragbarkeit', labelShort: 'Uebertragung',
description: 'Recht auf Datenuebertragbarkeit in maschinenlesbarem Format',
defaultDeadlineDays: 30, maxExtensionMonths: 2,
color: 'text-purple-700', bgColor: 'bg-purple-100',
processDocument: 'Prozessbeschreibung Art. 20 DSGVO_v02.pdf'
},
objection: {
type: 'objection', article: 'Art. 21', label: 'Widerspruchsrecht', labelShort: 'Widerspruch',
description: 'Recht auf Widerspruch gegen die Verarbeitung',
defaultDeadlineDays: 30, maxExtensionMonths: 0,
color: 'text-gray-700', bgColor: 'bg-gray-100'
}
}
export const DSR_STATUS_INFO: Record<DSRStatus, { label: string; color: string; bgColor: string; borderColor: string }> = {
intake: { label: 'Eingang', color: 'text-blue-700', bgColor: 'bg-blue-100', borderColor: 'border-blue-200' },
identity_verification: { label: 'ID-Pruefung', color: 'text-yellow-700', bgColor: 'bg-yellow-100', borderColor: 'border-yellow-200' },
processing: { label: 'In Bearbeitung', color: 'text-purple-700', bgColor: 'bg-purple-100', borderColor: 'border-purple-200' },
completed: { label: 'Abgeschlossen', color: 'text-green-700', bgColor: 'bg-green-100', borderColor: 'border-green-200' },
rejected: { label: 'Abgelehnt', color: 'text-red-700', bgColor: 'bg-red-100', borderColor: 'border-red-200' },
cancelled: { label: 'Storniert', color: 'text-gray-700', bgColor: 'bg-gray-100', borderColor: 'border-gray-200' },
}
// =============================================================================
// MAIN INTERFACES
// =============================================================================
export interface DSRRequester {
name: string
email: string
phone?: string
address?: string
customerId?: string
}
export interface DSRIdentityVerification {
verified: boolean
method?: IdentityVerificationMethod
verifiedAt?: string
verifiedBy?: string
notes?: string
documentRef?: string
}
export interface DSRAssignment {
assignedTo: string | null
assignedAt?: string
assignedBy?: string
}
export interface DSRDeadline {
originalDeadline: string
currentDeadline: string
extended: boolean
extensionReason?: string
extensionApprovedBy?: string
extensionApprovedAt?: string
}
export interface DSRRequest {
id: string
referenceNumber: string
type: DSRType
status: DSRStatus
priority: DSRPriority
requester: DSRRequester
source: DSRSource
sourceDetails?: string
requestText?: string
receivedAt: string
deadline: DSRDeadline
completedAt?: string
identityVerification: DSRIdentityVerification
assignment: DSRAssignment
notes?: string
internalNotes?: string
erasureChecklist?: DSRErasureChecklist
dataExport?: DSRDataExport
rectificationDetails?: DSRRectificationDetails
objectionDetails?: DSRObjectionDetails
createdAt: string
createdBy: string
updatedAt: string
updatedBy?: string
tenantId: string
}
// =============================================================================
// TYPE-SPECIFIC INTERFACES
// =============================================================================
export interface DSRErasureChecklistItem {
id: string
article: string
label: string
description: string
checked: boolean
applies: boolean
notes?: string
}
export interface DSRErasureChecklist {
items: DSRErasureChecklistItem[]
canProceedWithErasure: boolean
reviewedBy?: string
reviewedAt?: string
}
export const ERASURE_EXCEPTIONS: Omit<DSRErasureChecklistItem, 'checked' | 'applies' | 'notes'>[] = [
{ id: 'art17_3_a', article: '17(3)(a)', label: 'Meinungs- und Informationsfreiheit', description: 'Ausuebung des Rechts auf freie Meinungsaeusserung und Information' },
{ id: 'art17_3_b', article: '17(3)(b)', label: 'Rechtliche Verpflichtung', description: 'Erfuellung einer rechtlichen Verpflichtung (z.B. Aufbewahrungspflichten)' },
{ id: 'art17_3_c', article: '17(3)(c)', label: 'Oeffentliches Interesse', description: 'Gruende des oeffentlichen Interesses im Bereich Gesundheit' },
{ id: 'art17_3_d', article: '17(3)(d)', label: 'Archivzwecke', description: 'Archivzwecke, wissenschaftliche/historische Forschung, Statistik' },
{ id: 'art17_3_e', article: '17(3)(e)', label: 'Rechtsansprueche', description: 'Geltendmachung, Ausuebung oder Verteidigung von Rechtsanspruechen' },
]
export interface DSRDataExport {
format: 'json' | 'csv' | 'xml' | 'pdf'
generatedAt?: string
generatedBy?: string
fileUrl?: string
fileName?: string
fileSize?: number
includesThirdPartyData: boolean
anonymizedFields?: string[]
transferMethod?: 'download' | 'email' | 'third_party'
transferRecipient?: string
}
export interface DSRRectificationDetails {
fieldsToCorrect: {
field: string
currentValue: string
requestedValue: string
corrected: boolean
correctedAt?: string
correctedBy?: string
}[]
}
export interface DSRObjectionDetails {
processingPurpose: string
legalBasis: string
objectionGrounds: string
decision: 'accepted' | 'rejected' | 'pending'
decisionReason?: string
decisionBy?: string
decisionAt?: string
}

View File

@@ -1,581 +1,60 @@
/**
* DSR (Data Subject Request) Types
* DSR (Data Subject Request) Types — barrel re-export
*
* TypeScript definitions for GDPR Art. 15-21 Data Subject Requests
* Based on the Go Consent Service backend API structure
* Split into:
* - types-core.ts (enums, constants, metadata, main interfaces)
* - types-api.ts (API types, communication, audit, templates, helpers)
*/
// =============================================================================
// ENUMS & CONSTANTS
// =============================================================================
export type DSRType =
| 'access' // Art. 15 - Auskunftsrecht
| 'rectification' // Art. 16 - Berichtigungsrecht
| 'erasure' // Art. 17 - Loeschungsrecht
| 'restriction' // Art. 18 - Einschraenkungsrecht
| 'portability' // Art. 20 - Datenuebertragbarkeit
| 'objection' // Art. 21 - Widerspruchsrecht
export type DSRStatus =
| 'intake' // Eingang - Anfrage dokumentiert
| 'identity_verification' // Identitaetspruefung
| 'processing' // In Bearbeitung
| 'completed' // Abgeschlossen
| 'rejected' // Abgelehnt
| 'cancelled' // Storniert
export type DSRPriority = 'low' | 'normal' | 'high' | 'critical'
export type DSRSource =
| 'web_form' // Kontaktformular/Portal
| 'email' // E-Mail
| 'letter' // Brief
| 'phone' // Telefon
| 'in_person' // Persoenlich
| 'other' // Sonstiges
export type IdentityVerificationMethod =
| 'id_document' // Ausweiskopie
| 'email' // E-Mail-Bestaetigung
| 'phone' // Telefonische Bestaetigung
| 'postal' // Postalische Bestaetigung
| 'existing_account' // Bestehendes Kundenkonto
| 'other' // Sonstiges
export type CommunicationType =
| 'incoming' // Eingehend (vom Betroffenen)
| 'outgoing' // Ausgehend (an Betroffenen)
| 'internal' // Intern (Notizen)
export type CommunicationChannel =
| 'email'
| 'letter'
| 'phone'
| 'portal'
| 'internal_note'
// =============================================================================
// DSR TYPE METADATA
// =============================================================================
export interface DSRTypeInfo {
type: DSRType
article: string
label: string
labelShort: string
description: string
defaultDeadlineDays: number
maxExtensionMonths: number
color: string
bgColor: string
processDocument?: string // Reference to process document
}
export const DSR_TYPE_INFO: Record<DSRType, DSRTypeInfo> = {
access: {
type: 'access',
article: 'Art. 15',
label: 'Auskunftsrecht',
labelShort: 'Auskunft',
description: 'Recht auf Auskunft ueber gespeicherte personenbezogene Daten',
defaultDeadlineDays: 30,
maxExtensionMonths: 2,
color: 'text-blue-700',
bgColor: 'bg-blue-100',
processDocument: 'Prozessbeschreibung Art. 15 DSGVO_v02.pdf'
},
rectification: {
type: 'rectification',
article: 'Art. 16',
label: 'Berichtigungsrecht',
labelShort: 'Berichtigung',
description: 'Recht auf Berichtigung unrichtiger personenbezogener Daten',
defaultDeadlineDays: 14,
maxExtensionMonths: 2,
color: 'text-yellow-700',
bgColor: 'bg-yellow-100',
processDocument: 'Prozessbeschriebung Art. 16 DSGVO_v02.pdf'
},
erasure: {
type: 'erasure',
article: 'Art. 17',
label: 'Loeschungsrecht',
labelShort: 'Loeschung',
description: 'Recht auf Loeschung personenbezogener Daten ("Recht auf Vergessenwerden")',
defaultDeadlineDays: 14,
maxExtensionMonths: 2,
color: 'text-red-700',
bgColor: 'bg-red-100',
processDocument: 'Prozessbeschreibung Art. 17 DSGVO_v03.pdf'
},
restriction: {
type: 'restriction',
article: 'Art. 18',
label: 'Einschraenkungsrecht',
labelShort: 'Einschraenkung',
description: 'Recht auf Einschraenkung der Verarbeitung',
defaultDeadlineDays: 14,
maxExtensionMonths: 2,
color: 'text-orange-700',
bgColor: 'bg-orange-100',
processDocument: 'Prozessbeschreibung Art. 18 DSGVO_v01.pdf'
},
portability: {
type: 'portability',
article: 'Art. 20',
label: 'Datenuebertragbarkeit',
labelShort: 'Uebertragung',
description: 'Recht auf Datenuebertragbarkeit in maschinenlesbarem Format',
defaultDeadlineDays: 30,
maxExtensionMonths: 2,
color: 'text-purple-700',
bgColor: 'bg-purple-100',
processDocument: 'Prozessbeschreibung Art. 20 DSGVO_v02.pdf'
},
objection: {
type: 'objection',
article: 'Art. 21',
label: 'Widerspruchsrecht',
labelShort: 'Widerspruch',
description: 'Recht auf Widerspruch gegen die Verarbeitung',
defaultDeadlineDays: 30,
maxExtensionMonths: 0, // No extension allowed for objections
color: 'text-gray-700',
bgColor: 'bg-gray-100'
}
}
export const DSR_STATUS_INFO: Record<DSRStatus, { label: string; color: string; bgColor: string; borderColor: string }> = {
intake: {
label: 'Eingang',
color: 'text-blue-700',
bgColor: 'bg-blue-100',
borderColor: 'border-blue-200'
},
identity_verification: {
label: 'ID-Pruefung',
color: 'text-yellow-700',
bgColor: 'bg-yellow-100',
borderColor: 'border-yellow-200'
},
processing: {
label: 'In Bearbeitung',
color: 'text-purple-700',
bgColor: 'bg-purple-100',
borderColor: 'border-purple-200'
},
completed: {
label: 'Abgeschlossen',
color: 'text-green-700',
bgColor: 'bg-green-100',
borderColor: 'border-green-200'
},
rejected: {
label: 'Abgelehnt',
color: 'text-red-700',
bgColor: 'bg-red-100',
borderColor: 'border-red-200'
},
cancelled: {
label: 'Storniert',
color: 'text-gray-700',
bgColor: 'bg-gray-100',
borderColor: 'border-gray-200'
}
}
// =============================================================================
// MAIN INTERFACES
// =============================================================================
export interface DSRRequester {
name: string
email: string
phone?: string
address?: string
customerId?: string // If existing customer
}
export interface DSRIdentityVerification {
verified: boolean
method?: IdentityVerificationMethod
verifiedAt?: string
verifiedBy?: string
notes?: string
documentRef?: string // Reference to uploaded ID document
}
export interface DSRAssignment {
assignedTo: string | null
assignedAt?: string
assignedBy?: string
}
export interface DSRDeadline {
originalDeadline: string
currentDeadline: string
extended: boolean
extensionReason?: string
extensionApprovedBy?: string
extensionApprovedAt?: string
}
export interface DSRRequest {
id: string
referenceNumber: string // e.g., "DSR-2025-000042"
type: DSRType
status: DSRStatus
priority: DSRPriority
// Requester info
requester: DSRRequester
// Request details
source: DSRSource
sourceDetails?: string // e.g., "Kontaktformular auf website.de"
requestText?: string // Original request text
// Dates
receivedAt: string
deadline: DSRDeadline
completedAt?: string
// Verification
identityVerification: DSRIdentityVerification
// Assignment
assignment: DSRAssignment
// Processing
notes?: string
internalNotes?: string
// Type-specific data
erasureChecklist?: DSRErasureChecklist // For Art. 17
dataExport?: DSRDataExport // For Art. 15, 20
rectificationDetails?: DSRRectificationDetails // For Art. 16
objectionDetails?: DSRObjectionDetails // For Art. 21
// Audit
createdAt: string
createdBy: string
updatedAt: string
updatedBy?: string
// Metadata
tenantId: string
}
// =============================================================================
// TYPE-SPECIFIC INTERFACES
// =============================================================================
// Art. 17(3) Erasure Exceptions Checklist
export interface DSRErasureChecklistItem {
id: string
article: string // e.g., "17(3)(a)"
label: string
description: string
checked: boolean
applies: boolean
notes?: string
}
export interface DSRErasureChecklist {
items: DSRErasureChecklistItem[]
canProceedWithErasure: boolean
reviewedBy?: string
reviewedAt?: string
}
export const ERASURE_EXCEPTIONS: Omit<DSRErasureChecklistItem, 'checked' | 'applies' | 'notes'>[] = [
{
id: 'art17_3_a',
article: '17(3)(a)',
label: 'Meinungs- und Informationsfreiheit',
description: 'Ausuebung des Rechts auf freie Meinungsaeusserung und Information'
},
{
id: 'art17_3_b',
article: '17(3)(b)',
label: 'Rechtliche Verpflichtung',
description: 'Erfuellung einer rechtlichen Verpflichtung (z.B. Aufbewahrungspflichten)'
},
{
id: 'art17_3_c',
article: '17(3)(c)',
label: 'Oeffentliches Interesse',
description: 'Gruende des oeffentlichen Interesses im Bereich Gesundheit'
},
{
id: 'art17_3_d',
article: '17(3)(d)',
label: 'Archivzwecke',
description: 'Archivzwecke, wissenschaftliche/historische Forschung, Statistik'
},
{
id: 'art17_3_e',
article: '17(3)(e)',
label: 'Rechtsansprueche',
description: 'Geltendmachung, Ausuebung oder Verteidigung von Rechtsanspruechen'
}
]
// Data Export for Art. 15, 20
export interface DSRDataExport {
format: 'json' | 'csv' | 'xml' | 'pdf'
generatedAt?: string
generatedBy?: string
fileUrl?: string
fileName?: string
fileSize?: number
includesThirdPartyData: boolean
anonymizedFields?: string[]
transferMethod?: 'download' | 'email' | 'third_party' // For Art. 20 transfer
transferRecipient?: string // For Art. 20 transfer to another controller
}
// Rectification Details for Art. 16
export interface DSRRectificationDetails {
fieldsToCorrect: {
field: string
currentValue: string
requestedValue: string
corrected: boolean
correctedAt?: string
correctedBy?: string
}[]
}
// Objection Details for Art. 21
export interface DSRObjectionDetails {
processingPurpose: string
legalBasis: string
objectionGrounds: string
decision: 'accepted' | 'rejected' | 'pending'
decisionReason?: string
decisionBy?: string
decisionAt?: string
}
// =============================================================================
// COMMUNICATION
// =============================================================================
export interface DSRCommunication {
id: string
dsrId: string
type: CommunicationType
channel: CommunicationChannel
subject?: string
content: string
templateUsed?: string // Reference to email template
attachments?: {
name: string
url: string
size: number
type: string
}[]
sentAt?: string
sentBy?: string
receivedAt?: string
createdAt: string
createdBy: string
}
// =============================================================================
// AUDIT LOG
// =============================================================================
export interface DSRAuditEntry {
id: string
dsrId: string
action: string // e.g., "status_changed", "identity_verified", "assigned"
previousValue?: string
newValue?: string
performedBy: string
performedAt: string
notes?: string
}
// =============================================================================
// EMAIL TEMPLATES
// =============================================================================
export interface DSREmailTemplate {
id: string
name: string
subject: string
body: string
type: DSRType | 'general'
stage: DSRStatus | 'identity_request' | 'deadline_extension' | 'completion'
language: 'de' | 'en'
variables: string[] // e.g., ["requesterName", "referenceNumber", "deadline"]
}
export const DSR_EMAIL_TEMPLATES: DSREmailTemplate[] = [
{
id: 'intake_confirmation',
name: 'Eingangsbestaetigung',
subject: 'Bestaetigung Ihrer Anfrage - {{referenceNumber}}',
body: `Sehr geehrte(r) {{requesterName}},
wir bestaetigen den Eingang Ihrer Anfrage vom {{receivedDate}}.
Referenznummer: {{referenceNumber}}
Art der Anfrage: {{requestType}}
Wir werden Ihre Anfrage innerhalb der gesetzlichen Frist von {{deadline}} bearbeiten.
Mit freundlichen Gruessen
{{senderName}}
Datenschutzbeauftragter`,
type: 'general',
stage: 'intake',
language: 'de',
variables: ['requesterName', 'receivedDate', 'referenceNumber', 'requestType', 'deadline', 'senderName']
},
{
id: 'identity_request',
name: 'Identitaetsanfrage',
subject: 'Identitaetspruefung erforderlich - {{referenceNumber}}',
body: `Sehr geehrte(r) {{requesterName}},
um Ihre Anfrage bearbeiten zu koennen, benoetigen wir einen Nachweis Ihrer Identitaet.
Bitte senden Sie uns eines der folgenden Dokumente:
- Kopie Ihres Personalausweises (Vorder- und Rueckseite)
- Kopie Ihres Reisepasses
Ihre Daten werden ausschliesslich zur Identitaetspruefung verwendet und anschliessend geloescht.
Mit freundlichen Gruessen
{{senderName}}
Datenschutzbeauftragter`,
type: 'general',
stage: 'identity_request',
language: 'de',
variables: ['requesterName', 'referenceNumber', 'senderName']
}
]
// =============================================================================
// API TYPES
// =============================================================================
export interface DSRFilters {
status?: DSRStatus | DSRStatus[]
type?: DSRType | DSRType[]
priority?: DSRPriority
assignedTo?: string
overdue?: boolean
search?: string
dateFrom?: string
dateTo?: string
}
export interface DSRListResponse {
requests: DSRRequest[]
total: number
page: number
pageSize: number
}
export interface DSRCreateRequest {
type: DSRType
requester: DSRRequester
source: DSRSource
sourceDetails?: string
requestText?: string
priority?: DSRPriority
}
export interface DSRUpdateRequest {
status?: DSRStatus
priority?: DSRPriority
notes?: string
internalNotes?: string
assignment?: DSRAssignment
}
export interface DSRVerifyIdentityRequest {
method: IdentityVerificationMethod
notes?: string
documentRef?: string
}
export interface DSRCompleteRequest {
completionNotes?: string
dataExport?: DSRDataExport
}
export interface DSRRejectRequest {
reason: string
legalBasis?: string // e.g., Art. 17(3) exception
}
export interface DSRExtendDeadlineRequest {
extensionMonths: 1 | 2
reason: string
}
export interface DSRSendCommunicationRequest {
type: CommunicationType
channel: CommunicationChannel
subject?: string
content: string
templateId?: string
}
// =============================================================================
// STATISTICS
// =============================================================================
export interface DSRStatistics {
total: number
byStatus: Record<DSRStatus, number>
byType: Record<DSRType, number>
overdue: number
dueThisWeek: number
averageProcessingDays: number
completedThisMonth: number
}
// =============================================================================
// HELPER FUNCTIONS
// =============================================================================
export function getDaysRemaining(deadline: string): number {
const deadlineDate = new Date(deadline)
const now = new Date()
const diff = deadlineDate.getTime() - now.getTime()
return Math.ceil(diff / (1000 * 60 * 60 * 24))
}
export function isOverdue(request: DSRRequest): boolean {
if (request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled') {
return false
}
return getDaysRemaining(request.deadline.currentDeadline) < 0
}
export function isUrgent(request: DSRRequest, thresholdDays: number = 7): boolean {
if (request.status === 'completed' || request.status === 'rejected' || request.status === 'cancelled') {
return false
}
const daysRemaining = getDaysRemaining(request.deadline.currentDeadline)
return daysRemaining >= 0 && daysRemaining <= thresholdDays
}
export function generateReferenceNumber(year: number, sequence: number): string {
return `DSR-${year}-${String(sequence).padStart(6, '0')}`
}
export function getTypeInfo(type: DSRType): DSRTypeInfo {
return DSR_TYPE_INFO[type]
}
export function getStatusInfo(status: DSRStatus) {
return DSR_STATUS_INFO[status]
}
export type {
DSRType,
DSRStatus,
DSRPriority,
DSRSource,
IdentityVerificationMethod,
CommunicationType,
CommunicationChannel,
DSRTypeInfo,
DSRRequester,
DSRIdentityVerification,
DSRAssignment,
DSRDeadline,
DSRRequest,
DSRErasureChecklistItem,
DSRErasureChecklist,
DSRDataExport,
DSRRectificationDetails,
DSRObjectionDetails,
} from './types-core'
export {
DSR_TYPE_INFO,
DSR_STATUS_INFO,
ERASURE_EXCEPTIONS,
} from './types-core'
export type {
DSRCommunication,
DSRAuditEntry,
DSREmailTemplate,
DSRFilters,
DSRListResponse,
DSRCreateRequest,
DSRUpdateRequest,
DSRVerifyIdentityRequest,
DSRCompleteRequest,
DSRRejectRequest,
DSRExtendDeadlineRequest,
DSRSendCommunicationRequest,
DSRStatistics,
} from './types-api'
export {
DSR_EMAIL_TEMPLATES,
getDaysRemaining,
isOverdue,
isUrgent,
generateReferenceNumber,
getTypeInfo,
getStatusInfo,
} from './types-api'