feat(sprint-6): Phase 2 — DSGVO consent management
Deploy to Production / test (push) Has been cancelled
Deploy to Production / deploy (push) Has been cancelled

- V6 migration: consents table with audit columns
- Consent entity, repository, service (grant/revoke/check)
- ConsentController: GET/POST/DELETE consent endpoints
- DSGVO export (Art. 15): full personal data JSON download
- DSGVO deletion (Art. 17): anonymization + account deactivation
- Frontend: consent banner (modal, cannot dismiss), privacy settings page
- React Query hooks for consent + DSGVO operations
- Full i18n (de/en) for consent and DSGVO namespaces
This commit is contained in:
Patrick Plate
2026-06-12 22:22:48 +02:00
parent b38902a7ee
commit 3232d2f7fd
17 changed files with 2227 additions and 0 deletions
@@ -0,0 +1,102 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
import { apiClient } from "@/lib/api-client"
// --- Types ---
export interface ConsentRecord {
id: string
type: "DATA_PROCESSING" | "MARKETING" | "ANALYTICS"
granted: boolean
grantedAt: string | null
revokedAt: string | null
version: number
}
export interface ConsentCheckResponse {
hasDataProcessingConsent: boolean
}
export interface GrantConsentRequest {
type: "DATA_PROCESSING" | "MARKETING" | "ANALYTICS"
version?: number
}
export interface DsgvoExportData {
exportDate: string
legalBasis: string
personalData: Record<string, unknown>
memberProfile?: Record<string, unknown>
distributions?: Record<string, unknown>[]
consents?: Record<string, unknown>[]
}
// --- Query Hooks ---
export function useConsentsQuery() {
return useQuery({
queryKey: ["consent", "list"],
queryFn: () => apiClient<ConsentRecord[]>("/consent"),
})
}
export function useConsentCheckQuery() {
return useQuery({
queryKey: ["consent", "check"],
queryFn: () => apiClient<ConsentCheckResponse>("/consent/check"),
})
}
// --- Mutation Hooks ---
export function useGrantConsentMutation() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (data: GrantConsentRequest) =>
apiClient<ConsentRecord>("/consent", { method: "POST", body: data }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["consent"] })
},
})
}
export function useRevokeConsentMutation() {
const queryClient = useQueryClient()
return useMutation({
mutationFn: (type: string) =>
apiClient<void>(`/consent/${type}`, { method: "DELETE" }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["consent"] })
},
})
}
export function useExportDataMutation() {
return useMutation({
mutationFn: async () => {
const data = await apiClient<DsgvoExportData>("/dsgvo/export")
// Trigger download
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: "application/json",
})
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = `meine-daten-${new Date().toISOString().slice(0, 10)}.json`
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
return data
},
})
}
export function useDeleteAccountMutation() {
return useMutation({
mutationFn: () =>
apiClient<{ status: string; message: string }>("/dsgvo/delete", {
method: "DELETE",
}),
})
}