'use client'; import { useState, useEffect } from 'react'; import { backupClient, BackupListResponse } from '@/clients/backup-client'; import { BackupConfig } from '@/services/backup'; import { Button } from '@/components/ui/Button'; import { Card, CardHeader, CardContent } from '@/components/ui/Card'; import { Input } from '@/components/ui/Input'; import { Modal } from '@/components/ui/Modal'; import { Header } from '@/components/ui/Header'; import Link from 'next/link'; interface BackupSettingsPageClientProps { initialData?: BackupListResponse; } export default function BackupSettingsPageClient({ initialData }: BackupSettingsPageClientProps) { const [data, setData] = useState(initialData || null); const [isLoading, setIsLoading] = useState(!initialData); const [isCreatingBackup, setIsCreatingBackup] = useState(false); const [isVerifying, setIsVerifying] = useState(false); const [showRestoreConfirm, setShowRestoreConfirm] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(null); const [config, setConfig] = useState(initialData?.config || null); const [isSavingConfig, setIsSavingConfig] = useState(false); const [messages, setMessages] = useState<{[key: string]: {type: 'success' | 'error', text: string} | null}>({ verify: null, config: null, restore: null, delete: null, }); useEffect(() => { if (!initialData) { loadData(); } }, [initialData]); // Helper pour définir un message contextuel const setMessage = (key: string, message: {type: 'success' | 'error', text: string} | null) => { setMessages(prev => ({ ...prev, [key]: message })); // Auto-dismiss après 3 secondes if (message) { setTimeout(() => { setMessages(prev => ({ ...prev, [key]: null })); }, 3000); } }; const loadData = async () => { try { console.log('🔄 Loading backup data...'); const response = await backupClient.listBackups(); console.log('✅ Backup data loaded:', response); setData(response); setConfig(response.config); } catch (error) { console.error('❌ Failed to load backup data:', error); // Afficher l'erreur spécifique à l'utilisateur if (error instanceof Error) { console.error('Error details:', error.message); } } finally { setIsLoading(false); } }; const handleCreateBackup = async () => { setIsCreatingBackup(true); try { await backupClient.createBackup(); await loadData(); } catch (error) { console.error('Failed to create backup:', error); } finally { setIsCreatingBackup(false); } }; const handleVerifyDatabase = async () => { setIsVerifying(true); setMessage('verify', null); try { await backupClient.verifyDatabase(); setMessage('verify', {type: 'success', text: 'Intégrité vérifiée'}); } catch (error) { console.error('Database verification failed:', error); setMessage('verify', {type: 'error', text: 'Vérification échouée'}); } finally { setIsVerifying(false); } }; const handleDeleteBackup = async (filename: string) => { try { await backupClient.deleteBackup(filename); setShowDeleteConfirm(null); setMessage('restore', {type: 'success', text: `Sauvegarde ${filename} supprimée`}); await loadData(); } catch (error) { console.error('Failed to delete backup:', error); setMessage('restore', {type: 'error', text: 'Suppression échouée'}); } }; const handleRestoreBackup = async (filename: string) => { try { await backupClient.restoreBackup(filename); setShowRestoreConfirm(null); setMessage('restore', {type: 'success', text: 'Restauration réussie'}); await loadData(); } catch (error) { console.error('Failed to restore backup:', error); setMessage('restore', {type: 'error', text: 'Restauration échouée'}); } }; const handleToggleScheduler = async () => { if (!data) return; try { await backupClient.toggleScheduler(!data.scheduler.isRunning); await loadData(); } catch (error) { console.error('Failed to toggle scheduler:', error); } }; const handleSaveConfig = async () => { if (!config) return; setIsSavingConfig(true); setMessage('config', null); try { await backupClient.updateConfig(config); await loadData(); setMessage('config', {type: 'success', text: 'Configuration sauvegardée'}); } catch (error) { console.error('Failed to save config:', error); setMessage('config', {type: 'error', text: 'Sauvegarde échouée'}); } finally { setIsSavingConfig(false); } }; const formatFileSize = (bytes: number): string => { const units = ['B', 'KB', 'MB', 'GB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(1)} ${units[unitIndex]}`; }; const formatDate = (date: string | Date): string => { // Format cohérent serveur/client pour éviter les erreurs d'hydratation const d = new Date(date); return d.toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); }; if (isLoading) { return
Loading backup settings...
; } if (!data || !config) { return
Failed to load backup settings
; } const getNextBackupTime = (): string => { if (!data?.scheduler.nextBackup) return 'Non planifiée'; const nextBackup = new Date(data.scheduler.nextBackup); const now = new Date(); const diffMs = nextBackup.getTime() - now.getTime(); const diffMins = Math.floor(diffMs / (1000 * 60)); const diffHours = Math.floor(diffMins / 60); if (diffMins < 60) { return `dans ${diffMins}min`; } else { return `dans ${diffHours}h ${diffMins % 60}min`; } }; // Composant pour les messages inline const InlineMessage = ({ messageKey }: { messageKey: string }) => { const message = messages[messageKey]; if (!message) return null; return (
{message.text}
); }; return (
{/* Breadcrumb */}
Paramètres / Avancé / Sauvegardes
{/* Page Header */}

💾 Gestion des sauvegardes

Configuration et gestion des sauvegardes automatiques de votre base de données

{/* Layout en 2 colonnes pour optimiser l'espace */}
{/* Colonne principale: Configuration */}

⚙️ Configuration automatique

Paramètres des sauvegardes programmées

setConfig({ ...config, maxBackups: parseInt(e.target.value) || 7 })} className="bg-[var(--background)] border-[var(--border)] text-[var(--foreground)]" />
{/* Actions manuelles */}

🚀 Actions manuelles

Créer une sauvegarde ou vérifier l'intégrité de la base

{/* Colonne latérale: Statut et historique */}
{/* Statut du système */}

📊 Statut système

{/* Status planificateur */}
{data.scheduler.isRunning ? 'Actif' : 'Arrêté'}

Prochaine: {getNextBackupTime()}

{/* Statistiques */}

{data.backups.length}

sauvegardes

{config.maxBackups}

max conservées

{/* Chemin */}

Stockage

{data.config.backupPath}
{/* Historique des sauvegardes */}

📁 Historique

{data.backups.length === 0 ? (

Aucune sauvegarde disponible

) : (
{data.backups.slice(0, 10).map((backup) => (

{backup.filename.replace('towercontrol_', '').replace('.db.gz', '').replace('.db', '')}

{formatFileSize(backup.size)} {backup.type === 'manual' ? 'Manuel' : 'Auto'}

{formatDate(backup.createdAt)}

{process.env.NODE_ENV !== 'production' && ( )}
))} {data.backups.length > 10 && (

... et {data.backups.length - 10} autres

)}
)}
{/* Modal de confirmation de restauration */} {showRestoreConfirm && ( setShowRestoreConfirm(null)} title="Confirmer la restauration" >

⚠️ Attention : Cette action va remplacer complètement la base de données actuelle par la sauvegarde sélectionnée.

Une sauvegarde de la base actuelle sera créée automatiquement avant la restauration.

Fichier à restaurer: {showRestoreConfirm}

)} {/* Modal de confirmation de suppression */} {showDeleteConfirm && ( setShowDeleteConfirm(null)} title="Confirmer la suppression" >

Êtes-vous sûr de vouloir supprimer cette sauvegarde ?

Fichier: {showDeleteConfirm}

Cette action est irréversible.

)}
); }