diff --git a/src/app/dashboard/privacy/export/page.tsx b/src/app/dashboard/privacy/export/page.tsx new file mode 100644 index 0000000..91684a6 --- /dev/null +++ b/src/app/dashboard/privacy/export/page.tsx @@ -0,0 +1,249 @@ +'use client'; + +import { useState } from 'react'; +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; +import { apiFetch } from '../../../../lib/api'; +import { useSession } from '../../../../components/session-provider'; + +interface ExportCounts { + organizations: number; + tasks: number; + goals: number; + decisions: number; + transactions: number; + aiRequests: number; + consentRecords: number; + notifications: number; + researchBriefs: number; + savedSegments: number; + opportunities: number; +} + +interface ExportData { + exportedAt: string; + tenantId: string; + userId: string; + schema: string; + counts: ExportCounts; + data: Record; +} + +interface ConsentRecord { + id: string; + purpose: string; + grantedAt: string; + revokedAt: string | null; +} + +interface DeletionResult { + submitted: boolean; + alreadyPending: boolean; + submittedAt: string; +} + +const ENTITY_LABELS: Record = { + organizations: 'Organizații', + tasks: 'Taskuri', + goals: 'Obiective', + decisions: 'Decizii', + transactions: 'Tranzacții', + aiRequests: 'Cereri AI', + consentRecords: 'Consimțăminte', + notifications: 'Notificări', + researchBriefs: 'Research Briefs', + savedSegments: 'Segmente', + opportunities: 'Oportunități', +}; + +function downloadJson(data: unknown, filename: string) { + 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 = filename; + document.body.appendChild(a); + a.click(); + setTimeout(() => { URL.revokeObjectURL(url); a.remove(); }, 1000); +} + +export default function ExportPage() { + const { activeTenant } = useSession(); + const tenantId = activeTenant?.tenantId ?? ''; + const qc = useQueryClient(); + + const [deletionReason, setDeletionReason] = useState(''); + const [showDeletionForm, setShowDeletionForm] = useState(false); + const [exportPreview, setExportPreview] = useState(null); + const [isDownloading, setIsDownloading] = useState(false); + + const { data: consents = [] } = useQuery({ + queryKey: ['consents', tenantId], + queryFn: () => apiFetch('/v1/consents', { tenantId }), + enabled: Boolean(tenantId), + }); + + const deletionPending = consents.some( + (c) => c.purpose === 'account_deletion_requested' && !c.revokedAt, + ); + + const handleExport = async () => { + if (!tenantId) return; + setIsDownloading(true); + try { + const data = await apiFetch('/v1/export/data', { tenantId }); + setExportPreview(data.counts); + const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); + downloadJson(data, `ceo-os-export-${ts}.json`); + } finally { + setIsDownloading(false); + } + }; + + const { mutate: submitDeletion, isPending: isDeletionPending, data: deletionResult } = useMutation({ + mutationFn: () => + apiFetch('/v1/export/deletion-request', { + method: 'POST', + body: { reason: deletionReason }, + tenantId, + }), + onSuccess: () => { + qc.invalidateQueries({ queryKey: ['consents', tenantId] }); + setShowDeletionForm(false); + }, + }); + + return ( +
+
+

Export & Ștergere Date

+

+ Dreptul tău GDPR la portabilitate și ștergere (Art. 20, Art. 17) +

+
+ + {/* Export section */} +
+
+
+

Descarcă datele tale

+

+ Primești un fișier JSON cu toate datele din CEO OS — organizații, taskuri, obiective, + decizii, tranzacții, consimțăminte și mai mult. +

+
+ +
+ + {exportPreview && ( +
+

+ Export realizat cu succes +

+
+ {Object.entries(exportPreview).map(([key, count]) => ( +
+ {ENTITY_LABELS[key] ?? key} + {count} +
+ ))} +
+
+ )} +
+ + {/* GDPR rights info */} +
+ {[ + { icon: '📋', title: 'Art. 15 — Acces', desc: 'Dreptul de a cunoaște ce date deținem despre tine.' }, + { icon: '✏️', title: 'Art. 16 — Rectificare', desc: 'Corectează datele inexacte direct din platformă.' }, + { icon: '🗑️', title: 'Art. 17 — Ștergere', desc: 'Solicită ștergerea completă a contului și datelor.' }, + ].map(({ icon, title, desc }) => ( +
+

{icon}

+

{title}

+

{desc}

+
+ ))} +
+ + {/* Deletion request section */} +
+
+

Cerere de ștergere cont

+

+ Solicită ștergerea permanentă a contului și tuturor datelor asociate. + Cererea va fi procesată în maxim 30 de zile conform GDPR Art. 17. +

+
+ + {deletionPending || deletionResult?.submitted ? ( +
+

Cerere de ștergere în curs

+

+ Cererea ta a fost înregistrată și va fi procesată în 30 de zile. + Dacă dorești să anulezi, contactează suportul. +

+
+ ) : showDeletionForm ? ( +
+
+ +