feat(sprint-6): Phase 2 — DSGVO consent management
- 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:
@@ -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",
|
||||
}),
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user