'use client'; import React, { useState, useEffect, useCallback, useRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { Database, Upload, Download, History, Trash2, FileText, FileSpreadsheet, AlertCircle, CheckCircle2, XCircle, Loader2, ChevronRight, ChevronDown, MapPin, Eye, Play, RefreshCw, Archive, Shield, Users, GraduationCap, ClipboardCheck, CalendarCheck, BarChart3, X, Filter, Calendar, Clock, HardDrive, Sparkles, Info, } from 'lucide-react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Badge } from '@/components/ui/badge'; import { Progress } from '@/components/ui/progress'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Separator } from '@/components/ui/separator'; import { Checkbox } from '@/components/ui/checkbox'; import { useAppStore } from '@/lib/store'; import { t } from '@/lib/i18n'; import { toast } from 'sonner'; // ── Types ────────────────────────────────────────────────────────── interface ImportJob { id: string; type: string; fileName: string; fileSize: number; status: string; totalRows: number; successRows: number; errorRows: number; errors: string | null; createdAt: string; } interface ExportJob { id: string; type: string; format: string; fileName: string | null; fileSize: number; status: string; fileData: string | null; createdAt: string; } interface DbStatistics { students: number; teachers: number; grades: number; attendance: number; competencies: number; classes: number; subjects: number; assessments: number; reports: number; total: number; } interface CleanupInfo { orphanedStudents: number; orphanedEnrollments: number; totalOrphans: number; } interface ParsedData { headers: string[]; rows: Record[]; } // ── Column mapping schema per import type ────────────────────────── const IMPORT_FIELD_SCHEMAS: Record = { STUDENT: [ { value: 'firstName', label: 'First Name', required: true }, { value: 'lastName', label: 'Last Name', required: true }, { value: 'externalId', label: 'External ID', required: false }, { value: 'dateOfBirth', label: 'Date of Birth', required: false }, { value: 'email', label: 'Email', required: false }, ], TEACHER: [ { value: 'firstName', label: 'First Name', required: true }, { value: 'lastName', label: 'Last Name', required: true }, { value: 'email', label: 'Email', required: true }, { value: 'role', label: 'Role', required: false }, ], GRADE: [ { value: 'studentId', label: 'Student ID', required: true }, { value: 'assessmentId', label: 'Assessment ID', required: true }, { value: 'score', label: 'Score', required: false }, { value: 'note', label: 'Note', required: false }, ], ATTENDANCE: [ { value: 'studentId', label: 'Student ID', required: true }, { value: 'date', label: 'Date', required: true }, { value: 'status', label: 'Status', required: false }, { value: 'sessionId', label: 'Session ID', required: false }, ], COMPETENCY: [ { value: 'studentId', label: 'Student ID', required: true }, { value: 'competencyId', label: 'Competency ID', required: true }, { value: 'level', label: 'Level', required: false }, { value: 'note', label: 'Note', required: false }, ], }; // ── Animated Counter ─────────────────────────────────────────────── function AnimatedCounter({ value, duration = 1000 }: { value: number; duration?: number }) { const [display, setDisplay] = useState(0); const prevRef = useRef(0); useEffect(() => { const start = prevRef.current; const diff = value - start; if (diff === 0) { setDisplay(value); return; } const startTime = performance.now(); let raf: number; const animate = (now: number) => { const elapsed = now - startTime; const progress = Math.min(elapsed / duration, 1); const eased = 1 - Math.pow(1 - progress, 3); setDisplay(Math.round(start + diff * eased)); if (progress < 1) raf = requestAnimationFrame(animate); }; raf = requestAnimationFrame(animate); prevRef.current = value; return () => cancelAnimationFrame(raf); }, [value, duration]); return {display.toLocaleString()}; } // ── Status Badge ─────────────────────────────────────────────────── function StatusBadge({ status }: { status: string }) { const config: Record = { pending: { color: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300', icon: Clock, label: t('data_import_export.status_pending') }, processing: { color: 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300', icon: Loader2, label: t('data_import_export.status_processing') }, completed: { color: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900/30 dark:text-emerald-300', icon: CheckCircle2, label: t('data_import_export.status_completed') }, failed: { color: 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-300', icon: XCircle, label: t('data_import_export.status_failed') }, }; const c = config[status] || config.pending; const Icon = c.icon; return ( {c.label} ); } // ── Format file size ────────────────────────────────────────────── function formatFileSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } // ── Main Component ───────────────────────────────────────────────── export default function DataImportExportView() { const currentUser = useAppStore((s) => s.currentUser); const [activeTab, setActiveTab] = useState('import'); const [importType, setImportType] = useState('STUDENT'); const [exportType, setExportType] = useState('STUDENT'); const [exportFormat, setExportFormat] = useState('CSV'); const [dateFrom, setDateFrom] = useState(''); const [dateTo, setDateTo] = useState(''); const [classFilter, setClassFilter] = useState(''); const [subjectFilter, setSubjectFilter] = useState(''); const [includeHeaders, setIncludeHeaders] = useState(true); const [includeMetadata, setIncludeMetadata] = useState(false); // Import state const [dragActive, setDragActive] = useState(false); const [selectedFile, setSelectedFile] = useState(null); const [parsedData, setParsedData] = useState(null); const [columnMapping, setColumnMapping] = useState>({}); const [importing, setImporting] = useState(false); const [importProgress, setImportProgress] = useState(0); const [importResult, setImportResult] = useState | null>(null); // Export state const [exporting, setExporting] = useState(false); const [exportResult, setExportResult] = useState | null>(null); // History state const [importJobs, setImportJobs] = useState([]); const [exportJobs, setExportJobs] = useState([]); const [historyLoading, setHistoryLoading] = useState(false); // Management state const [dbStats, setDbStats] = useState(null); const [cleanupInfo, setCleanupInfo] = useState(null); const [statsLoading, setStatsLoading] = useState(false); const [confirmDialog, setConfirmDialog] = useState<{ open: boolean; type: string; onConfirm: () => void }>({ open: false, type: '', onConfirm: () => {}, }); const fileInputRef = useRef(null); // Check access const role = currentUser?.role || ''; const isAdmin = role === 'SUPER_ADMIN' || role === 'SCHOOL_ADMIN'; const isVicePrincipal = role === 'VICE_PRINCIPAL'; const isTeacher = role === 'TEACHER'; const canImport = isAdmin || isVicePrincipal || isTeacher; const canExport = isAdmin || isVicePrincipal || isTeacher; const canManage = isAdmin || isVicePrincipal; const canCleanup = isAdmin; const schoolId = currentUser?.schoolId || ''; // ── Parse CSV/JSON locally ────────────────────────────────────── const parseLocalFile = useCallback(async (file: File) => { const text = await file.text(); try { if (file.name.endsWith('.json')) { const data = JSON.parse(text); const arr = Array.isArray(data) ? data : data.records || data.data || []; if (arr.length === 0) { toast.error(t('data_import_export.invalid_format')); return; } const headers = Object.keys(arr[0]); const rows = arr.slice(0, 10).map((item: Record) => { const row: Record = {}; headers.forEach((h) => { row[h] = item[h] !== undefined && item[h] !== null ? String(item[h]) : ''; }); return row; }); setParsedData({ headers, rows }); // Auto-map columns const mapping: Record = {}; const fields = IMPORT_FIELD_SCHEMAS[importType] || []; headers.forEach((h) => { const lower = h.toLowerCase().replace(/[^a-z0-9]/g, ''); const match = fields.find((f) => f.value.toLowerCase() === lower); if (match) mapping[h] = match.value; }); setColumnMapping(mapping); } else { // Parse CSV const lines = text.split(/\r?\n/).filter((l) => l.trim()); if (lines.length < 2) { toast.error(t('data_import_export.invalid_format')); return; } const headers = lines[0].split(',').map((h) => h.trim().replace(/^"|"$/g, '')); const rows: Record[] = []; for (let i = 1; i < Math.min(lines.length, 11); i++) { const values: string[] = []; let current = ''; let inQuotes = false; for (const char of lines[i]) { if (char === '"') inQuotes = !inQuotes; else if (char === ',' && !inQuotes) { values.push(current.trim()); current = ''; } else current += char; } values.push(current.trim()); const row: Record = {}; headers.forEach((h, idx) => { row[h] = values[idx] ?? ''; }); rows.push(row); } setParsedData({ headers, rows }); // Auto-map columns const mapping: Record = {}; const fields = IMPORT_FIELD_SCHEMAS[importType] || []; headers.forEach((h) => { const lower = h.toLowerCase().replace(/[^a-z0-9]/g, ''); const match = fields.find((f) => f.value.toLowerCase() === lower); if (match) mapping[h] = match.value; }); setColumnMapping(mapping); } } catch { toast.error(t('data_import_export.invalid_format')); } }, [importType]); // ── Handle file selection ─────────────────────────────────────── const handleFileSelect = useCallback((file: File) => { if (file.size > 10 * 1024 * 1024) { toast.error(t('data_import_export.file_too_large')); return; } if (!file.name.endsWith('.csv') && !file.name.endsWith('.json')) { toast.error(t('data_import_export.invalid_format')); return; } setSelectedFile(file); setImportResult(null); setImportProgress(0); parseLocalFile(file); }, [parseLocalFile]); // ── Drag & Drop handlers ──────────────────────────────────────── const handleDrag = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }, []); const handleDragIn = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(true); }, []); const handleDragOut = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); }, []); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setDragActive(false); if (e.dataTransfer.files && e.dataTransfer.files[0]) { handleFileSelect(e.dataTransfer.files[0]); } }, [handleFileSelect]); // ── Import handler ────────────────────────────────────────────── const handleImport = useCallback(async () => { if (!selectedFile || !schoolId) return; setImporting(true); setImportProgress(0); // Simulate progress const progressInterval = setInterval(() => { setImportProgress((prev) => { if (prev >= 90) { clearInterval(progressInterval); return 90; } return prev + Math.random() * 15; }); }, 300); try { const formData = new FormData(); formData.append('file', selectedFile); formData.append('type', importType); formData.append('schoolId', schoolId); if (Object.keys(columnMapping).length > 0) { formData.append('columnMapping', JSON.stringify(columnMapping)); } const res = await fetch('/api/data-import', { method: 'POST', body: formData }); const data = await res.json(); clearInterval(progressInterval); setImportProgress(100); if (res.ok) { setImportResult(data); toast.success(t('data_import_export.import_success')); } else { toast.error(data.error || t('data_import_export.import_error')); } } catch { clearInterval(progressInterval); toast.error(t('data_import_export.import_error')); } finally { setImporting(false); } }, [selectedFile, schoolId, importType, columnMapping]); // ── Export handler ────────────────────────────────────────────── const handleExport = useCallback(async () => { if (!schoolId) return; setExporting(true); setExportResult(null); try { const filters: Record = { schoolId }; if (classFilter) filters.classId = classFilter; if (subjectFilter) filters.subjectId = subjectFilter; if (dateFrom) filters.dateFrom = dateFrom; if (dateTo) filters.dateTo = dateTo; const res = await fetch('/api/data-export/jobs', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: exportType, format: exportFormat, filters, }), }); const data = await res.json(); if (res.ok) { setExportResult(data); toast.success(t('data_import_export.export_success')); } else { toast.error(data.error || t('data_import_export.export_error')); } } catch { toast.error(t('data_import_export.export_error')); } finally { setExporting(false); } }, [schoolId, exportType, exportFormat, classFilter, subjectFilter, dateFrom, dateTo]); // ── Download handler ──────────────────────────────────────────── const handleDownload = useCallback((fileData: string, fileName: string) => { try { const binary = atob(fileData); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i); const blob = new Blob([bytes]); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = fileName; a.click(); URL.revokeObjectURL(url); } catch { toast.error(t('data_import_export.operation_error')); } }, []); // ── Load history ──────────────────────────────────────────────── const loadHistory = useCallback(async () => { if (!schoolId) return; setHistoryLoading(true); try { const [importRes, exportRes] = await Promise.all([ fetch(`/api/data-import?schoolId=${schoolId}`), fetch(`/api/data-export/jobs?schoolId=${schoolId}`), ]); if (importRes.ok) setImportJobs(await importRes.json()); if (exportRes.ok) setExportJobs(await exportRes.json()); } catch { // ignore } finally { setHistoryLoading(false); } }, [schoolId]); // ── Load statistics ───────────────────────────────────────────── const loadStats = useCallback(async () => { if (!schoolId) return; setStatsLoading(true); try { const res = await fetch(`/api/data-cleanup?schoolId=${schoolId}`); if (res.ok) { const data = await res.json(); setDbStats(data.statistics); setCleanupInfo(data.cleanup); } } catch { // ignore } finally { setStatsLoading(false); } }, [schoolId]); // ── Cleanup handler ───────────────────────────────────────────── const handleCleanup = useCallback(async (operation: string) => { if (!schoolId) return; try { const res = await fetch('/api/data-cleanup', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ operation, schoolId }), }); const data = await res.json(); if (res.ok) { toast.success(t('data_import_export.operation_success')); loadStats(); } else { toast.error(data.error || t('data_import_export.operation_error')); } } catch { toast.error(t('data_import_export.operation_error')); } setConfirmDialog({ open: false, type: '', onConfirm: () => {} }); }, [schoolId, loadStats]); // ── Delete export job ─────────────────────────────────────────── const handleDeleteExport = useCallback(async (id: string) => { try { const res = await fetch(`/api/data-export/jobs/${id}`, { method: 'DELETE' }); if (res.ok) { setExportJobs((prev) => prev.filter((j) => j.id !== id)); toast.success(t('data_import_export.operation_success')); } } catch { toast.error(t('data_import_export.operation_error')); } }, []); // ── Load data on tab change ───────────────────────────────────── useEffect(() => { if (activeTab === 'history') loadHistory(); if (activeTab === 'management') loadStats(); }, [activeTab, loadHistory, loadStats]); // ── No access view ────────────────────────────────────────────── if (!canImport && !canExport) { return (

{t('data_import_export.no_access')}

{t('data_import_export.no_access_desc')}

); } // ── Stat cards data ───────────────────────────────────────────── const statCards = dbStats ? [ { label: t('data_import_export.students'), value: dbStats.students, icon: GraduationCap, color: 'from-emerald-500 to-teal-600' }, { label: t('data_import_export.teachers'), value: dbStats.teachers, icon: Users, color: 'from-teal-500 to-cyan-600' }, { label: t('data_import_export.grades'), value: dbStats.grades, icon: ClipboardCheck, color: 'from-cyan-500 to-sky-600' }, { label: t('data_import_export.attendance_records'), value: dbStats.attendance, icon: CalendarCheck, color: 'from-sky-500 to-blue-600' }, { label: t('data_import_export.competency_records'), value: dbStats.competencies, icon: BarChart3, color: 'from-violet-500 to-purple-600' }, { label: t('data_import_export.total_records'), value: dbStats.total, icon: Database, color: 'from-emerald-500 to-emerald-700' }, ] : []; // ── Render ─────────────────────────────────────────────────────── return (
{/* Header Banner */}

{t('data_import_export.title')}

{t('data_import_export.subtitle')}

{/* Main Tabs */} {t('data_import_export.tab_import')} {t('data_import_export.tab_export')} {t('data_import_export.tab_history')} {t('data_import_export.tab_management')} {/* ── IMPORT TAB ──────────────────────────────────────────── */} {canImport ? ( <> {/* Import Type Selection */} {t('data_import_export.import_type')}
{[ { value: 'STUDENT', icon: GraduationCap, label: t('data_import_export.type_student') }, { value: 'TEACHER', icon: Users, label: t('data_import_export.type_teacher') }, { value: 'GRADE', icon: ClipboardCheck, label: t('data_import_export.type_grade') }, { value: 'ATTENDANCE', icon: CalendarCheck, label: t('data_import_export.type_attendance') }, { value: 'COMPETENCY', icon: BarChart3, label: t('data_import_export.type_competency') }, ].map((item) => ( { setImportType(item.value); setParsedData(null); setSelectedFile(null); setColumnMapping({}); setImportResult(null); }} className={`flex flex-col items-center gap-2 p-4 rounded-xl border-2 transition-all ${ importType === item.value ? 'border-emerald-500 bg-emerald-50 dark:bg-emerald-950/30 shadow-md' : 'border-muted hover:border-emerald-300 dark:hover:border-emerald-700' }`} > {item.label} ))}
{/* File Upload Zone */} {t('data_import_export.upload_title')} {t('data_import_export.upload_subtitle')}
fileInputRef.current?.click()} className={`relative cursor-pointer rounded-xl border-2 border-dashed p-8 md:p-12 text-center transition-all ${ dragActive ? 'border-emerald-500 bg-emerald-50 dark:bg-emerald-950/30 scale-[1.02]' : selectedFile ? 'border-emerald-400 bg-emerald-50/50 dark:bg-emerald-950/20' : 'border-muted hover:border-emerald-300 dark:hover:border-emerald-700' }`} > { if (e.target.files?.[0]) handleFileSelect(e.target.files[0]); }} />

{dragActive ? t('data_import_export.drag_drop_active') : t('data_import_export.drag_drop')}

{t('data_import_export.supported_formats')} • {t('data_import_export.max_file_size')}

{selectedFile && !dragActive && (
{selectedFile.name} ({formatFileSize(selectedFile.size)})
)}
{/* Column Mapping */} {parsedData && ( {t('data_import_export.column_mapping')} {t('data_import_export.column_mapping_desc')}
{parsedData.headers.map((header) => (
{header}
))}
)}
{/* Data Preview */} {parsedData && parsedData.rows.length > 0 && ( {t('data_import_export.preview')} {t('data_import_export.preview_desc')}
{parsedData.headers.map((h) => ( ))} {parsedData.rows.map((row, idx) => ( {parsedData.headers.map((h) => ( ))} ))}
#{h}
{idx + 1}{row[h] || '—'}
)}
{/* Import Progress & Result */} {(importing || importResult) && ( {importing ? ( ) : ( )} {t('data_import_export.import_progress')} {importing && (

{t('data_import_export.importing')}

)} {importResult && (

{String(importResult.totalRows ?? 0)}

{t('data_import_export.total_rows')}

{String(importResult.created ?? 0)}

{t('data_import_export.success_rows')}

{String(importResult.skipped ?? 0)}

{t('data_import_export.error_rows')}

{Array.isArray(importResult.errors) && importResult.errors.length > 0 && (

{t('data_import_export.validation_errors')}

{(importResult.errors as string[]).map((err: string, idx: number) => (

{err}

))}
)}
)}
)}
{/* Start Import Button */} ) : ( )}
{/* ── EXPORT TAB ──────────────────────────────────────────── */} {canExport ? ( <>
{/* Export Configuration */} {t('data_import_export.export_title')} {/* Export Type */}
{[ { value: 'STUDENT', icon: GraduationCap, label: t('data_import_export.type_student') }, { value: 'GRADE', icon: ClipboardCheck, label: t('data_import_export.type_grade') }, { value: 'ATTENDANCE', icon: CalendarCheck, label: t('data_import_export.type_attendance') }, { value: 'COMPETENCY', icon: BarChart3, label: t('data_import_export.type_competency') }, { value: 'REPORT', icon: FileText, label: t('data_import_export.type_report') }, ].map((item) => ( setExportType(item.value)} className={`flex items-center gap-2 p-2.5 rounded-lg border-2 transition-all text-sm ${ exportType === item.value ? 'border-emerald-500 bg-emerald-50 dark:bg-emerald-950/30' : 'border-muted hover:border-emerald-300' }`} > {item.label} ))}
{/* Export Format */}
{[ { value: 'CSV', icon: FileSpreadsheet, label: t('data_import_export.format_csv') }, { value: 'JSON', icon: FileText, label: t('data_import_export.format_json') }, ].map((item) => ( setExportFormat(item.value)} className={`flex items-center gap-2 px-4 py-2.5 rounded-lg border-2 transition-all ${ exportFormat === item.value ? 'border-emerald-500 bg-emerald-50 dark:bg-emerald-950/30' : 'border-muted hover:border-emerald-300' }`} > {item.label} ))}
{/* Date Range */}
setDateFrom(e.target.value)} />
setDateTo(e.target.value)} />
{/* Class & Subject Filters */}
setClassFilter(e.target.value)} />
setSubjectFilter(e.target.value)} />
{/* Options */}
setIncludeHeaders(!!v)} />
setIncludeMetadata(!!v)} />
{/* Export Button */}
{/* Export Templates & Result */}
{/* Export Templates */} {t('data_import_export.export_templates')} {[ { key: 'template_full', types: ['STUDENT', 'GRADE', 'ATTENDANCE', 'COMPETENCY'], formats: ['CSV', 'JSON'] }, { key: 'template_grades_only', types: ['GRADE'], formats: ['CSV'] }, { key: 'template_attendance_only', types: ['ATTENDANCE'], formats: ['CSV'] }, { key: 'template_competency_only', types: ['COMPETENCY'], formats: ['CSV', 'JSON'] }, ].map((template) => ( { setExportType(template.types[0]); setExportFormat(template.formats[0]); }} className="w-full flex items-center justify-between p-3 rounded-lg border border-muted hover:border-emerald-300 dark:hover:border-emerald-700 transition-all" >
{t(`data_import_export.${template.key}`)}
))}
{/* Export Result */} {exportResult && ( {t('data_import_export.export_success')}

{String(exportResult.recordCount ?? 0)}

{t('data_import_export.records')}

{formatFileSize(Number(exportResult.fileSize ?? 0))}

{t('data_import_export.file_size')}

{exportResult.fileData && ( )}
)}
) : ( )}
{/* ── HISTORY TAB ─────────────────────────────────────────── */}

{t('data_import_export.history_title')}

{/* Import History */} {t('data_import_export.import_history')} {importJobs.length === 0 ? (

{t('data_import_export.no_history')}

) : (
{importJobs.map((job) => ( ))}
{t('data_import_export.file_name')} {t('data_import_export.status')} {t('data_import_export.total_rows')} {t('data_import_export.success_rows')} {t('data_import_export.error_rows')} {t('data_import_export.created_at')}
{job.fileName}
{job.totalRows} {job.successRows} {job.errorRows} {new Date(job.createdAt).toLocaleDateString()}
)}
{/* Export History */} {t('data_import_export.history_title')} {exportJobs.length === 0 ? (

{t('data_import_export.no_history')}

) : (
{exportJobs.map((job) => ( ))}
{t('data_import_export.file_name')} {t('data_import_export.export_format')} {t('data_import_export.file_size')} {t('data_import_export.status')} {t('data_import_export.created_at')}
{job.fileName || '—'}
{job.format} {formatFileSize(job.fileSize)} {new Date(job.createdAt).toLocaleDateString()}
{job.status === 'completed' && job.fileData && ( )} {isAdmin && ( )}
)}
{/* ── MANAGEMENT TAB ──────────────────────────────────────── */} {canManage ? ( <> {/* Database Statistics */}
{t('data_import_export.db_statistics')}
{statsLoading && !dbStats ? (
{[1, 2, 3, 4, 5, 6].map((i) => (
))}
) : dbStats ? (
{statCards.map((card) => (

{card.label}

))}
) : null}
{/* Cleanup Tools */}
{t('data_import_export.cleanup_tools')} {/* Orphaned Records */}

{t('data_import_export.remove_orphans')}

{t('data_import_export.remove_orphans_desc')} {cleanupInfo && ` (${cleanupInfo.totalOrphans} ${t('data_import_export.records')})`}

{/* Bulk Delete */}

{t('data_import_export.bulk_delete')}

{t('data_import_export.bulk_delete_desc')}

{/* Backup & Restore */} {t('data_import_export.backup')} {/* Create Backup */}

{t('data_import_export.backup')}

{t('data_import_export.backup_desc')}

{/* Restore Backup */}

{t('data_import_export.restore')}

{t('data_import_export.restore_desc')}

{/* Info Banner */}

Data Management Best Practices

  • Always create a backup before performing bulk operations
  • Review the data preview before importing to catch errors early
  • Use column mapping to ensure correct field alignment
  • Run cleanup periodically to remove orphaned records
  • Export data regularly for archival purposes
) : ( )} {/* Confirm Dialog */} setConfirmDialog({ open, type: confirmDialog.type, onConfirm: confirmDialog.onConfirm })}> {confirmDialog.type === 'cleanup' ? t('data_import_export.confirm_cleanup') : t('data_import_export.confirm_delete')} {confirmDialog.type === 'cleanup' ? t('data_import_export.confirm_cleanup_msg') : t('data_import_export.confirm_delete_msg')}
); } // ── No Access Card ───────────────────────────────────────────────── function NoAccessCard() { return (

{t('data_import_export.no_access')}

{t('data_import_export.no_access_desc')}

); }