'use client' import { useState } from 'react' import { Download, Upload, Trash2, Loader2, RefreshCw, Sparkles, Database, ShieldAlert, FolderArchive } from 'lucide-react' import { toast } from 'sonner' import { useLanguage } from '@/lib/i18n' import { useSession } from 'next-auth/react' import { DeleteAccountDialog } from '@/components/legal/delete-account-dialog' import { useRouter } from 'next/navigation' import { motion } from 'motion/react' import { cn } from '@/lib/utils' import { SettingsHelpBox } from '@/components/settings/settings-help-box' export default function DataSettingsPage() { const { t } = useLanguage() const router = useRouter() const { data: session } = useSession() const [deleteOpen, setDeleteOpen] = useState(false) const [isExporting, setIsExporting] = useState(false) const [isImporting, setIsImporting] = useState(false) const [isDeleting, setIsDeleting] = useState(false) const [isReindexing, setIsReindexing] = useState(false) const [isCleaningUp, setIsCleaningUp] = useState(false) const [isZipExporting, setIsZipExporting] = useState(false) const handleZipExport = async () => { setIsZipExporting(true) try { const response = await fetch('/api/user/export') if (!response.ok) { let message = t('dataManagement.zipExport.failed') try { const body = (await response.json()) as { error?: string } if (body.error) message = body.error } catch { /* non-JSON error body */ } toast.error(message) return } const blob = await response.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `memento-workspace-export-${new Date().toISOString().split('T')[0]}.zip` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) toast.success(t('dataManagement.zipExport.success')) } catch { toast.error(t('dataManagement.zipExport.failed')) } finally { setIsZipExporting(false) } } const handleExport = async () => { setIsExporting(true) try { const response = await fetch('/api/notes/export') if (response.ok) { const blob = await response.blob() const url = window.URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = `memento-export-${new Date().toISOString().split('T')[0]}.json` document.body.appendChild(a) a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) toast.success(t('dataManagement.export.success')) } else { throw new Error() } } catch { toast.error(t('dataManagement.export.failed')) } finally { setIsExporting(false) } } const handleImport = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) return setIsImporting(true) try { const formData = new FormData() formData.append('file', file) const response = await fetch('/api/notes/import', { method: 'POST', body: formData }) if (response.ok) { const result = await response.json() toast.success(t('dataManagement.import.success', { count: result.count })) router.refresh() } else { const error = await response.json() throw new Error(error.error || 'Import failed') } } catch (err: any) { toast.error(err.message || t('dataManagement.import.failed')) } finally { setIsImporting(false) event.target.value = '' } } const handleReindex = async () => { setIsReindexing(true) try { const response = await fetch('/api/notes/reindex', { method: 'POST' }) if (response.ok) { const result = await response.json() toast.success(t('dataManagement.indexing.success', { count: result.count })) } else { throw new Error() } } catch { toast.error(t('dataManagement.indexing.failed')) } finally { setIsReindexing(false) } } const handleCleanup = async () => { setIsCleaningUp(true) try { const response = await fetch('/api/notes/cleanup', { method: 'POST' }) if (response.ok) { const result = await response.json() toast.success(t('dataManagement.cleanup.success', { count: result.deletedLabels })) } else { throw new Error() } } catch { toast.error(t('dataManagement.cleanup.failed')) } finally { setIsCleaningUp(false) } } const handleDeleteAll = async () => { if (!confirm(t('dataManagement.delete.confirm'))) return setIsDeleting(true) try { const response = await fetch('/api/notes/delete-all', { method: 'POST' }) if (response.ok) { toast.success(t('dataManagement.delete.success')) router.refresh() } else { throw new Error() } } catch { toast.error(t('dataManagement.delete.failed')) } finally { setIsDeleting(false) } } const cards = [ { icon: Download, iconColor: 'text-zinc-600 dark:text-zinc-400', iconBg: 'bg-zinc-500/10 dark:bg-zinc-500/20', title: t('dataManagement.export.title'), description: t('dataManagement.export.description'), loading: isExporting, loadingText: t('dataManagement.exporting'), buttonText: t('dataManagement.export.button'), onAction: handleExport, btnClass: 'bg-ink text-paper shadow-xl shadow-ink/20 hover:scale-[1.02] active:scale-95', }, { icon: FolderArchive, iconColor: 'text-amber-600 dark:text-amber-400', iconBg: 'bg-amber-500/10 dark:bg-amber-500/20', title: t('dataManagement.zipExport.title'), description: t('dataManagement.zipExport.description'), loading: isZipExporting, loadingText: t('dataManagement.zipExporting'), buttonText: t('dataManagement.zipExport.button'), onAction: handleZipExport, btnClass: 'bg-ink text-paper shadow-xl shadow-ink/20 hover:scale-[1.02] active:scale-95', }, { icon: Upload, iconColor: 'text-primary/80 dark:text-primary', iconBg: 'bg-primary/10 dark:bg-primary/20', title: t('dataManagement.import.title'), description: t('dataManagement.import.description'), loading: isImporting, loadingText: t('dataManagement.importing'), buttonText: t('dataManagement.import.button'), onAction: () => document.getElementById('import-file')?.click(), btnClass: 'bg-white dark:bg-white/10 text-ink dark:text-paper border border-border hover:scale-[1.02] active:scale-95', fileInput: true, }, { icon: Sparkles, iconColor: 'text-amber-600 dark:text-amber-400', iconBg: 'bg-amber-500/10 dark:bg-amber-500/20', title: t('dataManagement.indexing.title'), description: t('dataManagement.indexing.description'), loading: isReindexing, loadingText: t('dataManagement.exporting'), buttonText: t('dataManagement.indexing.button'), onAction: handleReindex, btnClass: 'bg-white dark:bg-white/10 text-ink dark:text-paper border border-border hover:scale-[1.02] active:scale-95', }, { icon: Database, iconColor: 'text-purple-600 dark:text-purple-400', iconBg: 'bg-purple-500/10 dark:bg-purple-500/20', title: t('dataManagement.cleanup.title'), description: t('dataManagement.cleanup.description'), loading: isCleaningUp, loadingText: t('dataManagement.exporting'), buttonText: t('dataManagement.cleanup.button'), onAction: handleCleanup, btnClass: 'bg-white dark:bg-white/10 text-ink dark:text-paper border border-border hover:scale-[1.02] active:scale-95', }, ] return (

{t('dataManagement.toolsDescription')}

{cards.map((card) => (

{card.title}

{card.description}

{card.fileInput && ( )}
))}

{t('dataManagement.dangerZone')}

{t('dataManagement.dangerZoneDescription')}

{t('dataManagement.delete.title')}

{t('dataManagement.delete.description')}

{t('account.deleteAccount.sectionTitle')}

{t('account.deleteAccount.sectionDescription')}

{t('account.deleteAccount.dialogTitle')}

{t('account.deleteAccount.sectionDescription')}

{session?.user?.email && ( )}
) }