Files
btpxpress-frontend/app/(main)/admin/sauvegarde/page.tsx
2025-10-13 05:29:32 +02:00

1282 lines
57 KiB
TypeScript

'use client';
import React, { useState, useRef, useEffect } from 'react';
import { Card } from 'primereact/card';
import { Button } from 'primereact/button';
import { DataTable } from 'primereact/datatable';
import { Column } from 'primereact/column';
import { Toast } from 'primereact/toast';
import { Toolbar } from 'primereact/toolbar';
import { Tag } from 'primereact/tag';
import { Dialog } from 'primereact/dialog';
import { Calendar } from 'primereact/calendar';
import { Dropdown } from 'primereact/dropdown';
import { InputSwitch } from 'primereact/inputswitch';
import { InputNumber } from 'primereact/inputnumber';
import { ProgressBar } from 'primereact/progressbar';
import { FileUpload } from 'primereact/fileupload';
import { Checkbox } from 'primereact/checkbox';
import { RadioButton } from 'primereact/radiobutton';
import { Timeline } from 'primereact/timeline';
import { ConfirmDialog, confirmDialog } from 'primereact/confirmdialog';
import { Divider } from 'primereact/divider';
import { Panel } from 'primereact/panel';
import { Badge } from 'primereact/badge';
import { TabView, TabPanel } from 'primereact/tabview';
import { SpeedDial } from 'primereact/speeddial';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { Avatar } from 'primereact/avatar';
import { AvatarGroup } from 'primereact/avatargroup';
import { Chip } from 'primereact/chip';
import { Message } from 'primereact/message';
import { Messages } from 'primereact/messages';
import { Skeleton } from 'primereact/skeleton';
import { ScrollPanel } from 'primereact/scrollpanel';
import { Splitter, SplitterPanel } from 'primereact/splitter';
import { DataView } from 'primereact/dataview';
import { Knob } from 'primereact/knob';
// import { MeterGroup } from 'primereact/metergroup'; // Non disponible dans cette version
import { Chart } from 'primereact/chart';
import { Sidebar } from 'primereact/sidebar';
import { OverlayPanel } from 'primereact/overlaypanel';
import { ContextMenu } from 'primereact/contextmenu';
import { Menu } from 'primereact/menu';
import { TieredMenu } from 'primereact/tieredmenu';
import { Fieldset } from 'primereact/fieldset';
import { Steps } from 'primereact/steps';
import { MenuItem } from 'primereact/menuitem';
import { InputText } from 'primereact/inputtext';
import { Rating } from 'primereact/rating';
import { Slider } from 'primereact/slider';
import { ToggleButton } from 'primereact/togglebutton';
import { SelectButton } from 'primereact/selectbutton';
import { ColorPicker } from 'primereact/colorpicker';
import { MultiSelect } from 'primereact/multiselect';
import { ListBox } from 'primereact/listbox';
import { OrderList } from 'primereact/orderlist';
import { PickList } from 'primereact/picklist';
import { DataScroller } from 'primereact/datascroller';
import { VirtualScroller } from 'primereact/virtualscroller';
import { Galleria } from 'primereact/galleria';
import { Image } from 'primereact/image';
import { Carousel } from 'primereact/carousel';
import { Terminal } from 'primereact/terminal';
import { DeferredContent } from 'primereact/deferredcontent';
import { ScrollTop } from 'primereact/scrolltop';
import { BlockUI } from 'primereact/blockui';
import { TreeTable } from 'primereact/treetable';
import { Tree } from 'primereact/tree';
import { Mention } from 'primereact/mention';
import { Editor } from 'primereact/editor';
import { AutoComplete } from 'primereact/autocomplete';
import { InputMask } from 'primereact/inputmask';
import { Password } from 'primereact/password';
import { CascadeSelect } from 'primereact/cascadeselect';
import { TreeSelect } from 'primereact/treeselect';
interface Backup {
id: string;
nom: string;
type: string;
statut: 'EN_COURS' | 'TERMINE' | 'ERREUR';
dateCreation: Date;
dateModification?: Date;
taille: number;
destination: string;
checksum?: string;
description?: string;
}
interface BackupSchedule {
id: string;
nom: string;
frequence: string;
heure: Date;
actif: boolean;
prochainExecution: Date;
derniereExecution?: Date;
nombreBackups: number;
description?: string;
inclureBase: boolean;
inclureFichiers: boolean;
inclureConfig: boolean;
compression: boolean;
joursSelection?: string[];
}
interface RestorePoint {
id: string;
nom: string;
dateCreation: Date;
taille: number;
description?: string;
}
const SauvegardePage = () => {
const [backups, setBackups] = useState<Backup[]>([]);
const [schedules, setSchedules] = useState<BackupSchedule[]>([]);
const [restorePoints, setRestorePoints] = useState<RestorePoint[]>([]);
const [loading, setLoading] = useState(false);
const [blocked, setBlocked] = useState(false);
const [backupInProgress, setBackupInProgress] = useState(false);
const [restoreInProgress, setRestoreInProgress] = useState(false);
const [progress, setProgress] = useState(0);
const [restoreProgress, setRestoreProgress] = useState(0);
const [globalFilter, setGlobalFilter] = useState('');
const [scheduleDialog, setScheduleDialog] = useState(false);
const [restoreDialog, setRestoreDialog] = useState(false);
const [monitoringDialog, setMonitoringDialog] = useState(false);
const [analyticsDialog, setAnalyticsDialog] = useState(false);
const [configDialog, setConfigDialog] = useState(false);
const [selectedBackup, setSelectedBackup] = useState<Backup | null>(null);
const [selectedSchedule, setSelectedSchedule] = useState<BackupSchedule | null>(null);
const [activeIndex, setActiveIndex] = useState(0);
const [sidebarVisible, setSidebarVisible] = useState(false);
const [autoRefresh, setAutoRefresh] = useState(true);
const [refreshInterval, setRefreshInterval] = useState(30);
const [viewMode, setViewMode] = useState<'table' | 'grid' | 'timeline'>('table');
const [storageQuota, setStorageQuota] = useState(85);
const [systemHealth, setSystemHealth] = useState(92);
const [backupSpeed, setBackupSpeed] = useState(45);
const [securityLevel, setSecurityLevel] = useState(88);
const [compressionRatio, setCompressionRatio] = useState(67);
const [networkBandwidth, setNetworkBandwidth] = useState(234);
const [activeConnections, setActiveConnections] = useState(12);
const [encryptionStrength, setEncryptionStrength] = useState(256);
const [retentionCompliance, setRetentionCompliance] = useState(95);
const [replicationStatus, setReplicationStatus] = useState('active');
const [cloudSync, setCloudSync] = useState(true);
const [incrementalBackup, setIncrementalBackup] = useState(true);
const [verificationEnabled, setVerificationEnabled] = useState(true);
const [compressionLevel, setCompressionLevel] = useState(6);
const [notificationLevel, setNotificationLevel] = useState('all');
const [searchTerm, setSearchTerm] = useState('');
const [selectedBackups, setSelectedBackups] = useState<Backup[]>([]);
const [kpiData, setKpiData] = useState({
totalBackups: 0,
successRate: 0,
storageUsed: 0,
avgDuration: 0,
lastBackup: null,
scheduledCount: 0
});
const [chartData, setChartData] = useState({});
const [performanceMetrics, setPerformanceMetrics] = useState([]);
const [alertsData, setAlertsData] = useState([]);
const [storageAnalytics, setStorageAnalytics] = useState([]);
const [complianceReport, setComplianceReport] = useState([]);
const [auditTrail, setAuditTrail] = useState([]);
const [backupVerification, setBackupVerification] = useState([]);
const [cloudProviders, setCloudProviders] = useState([]);
const [replicationTargets, setReplicationTargets] = useState([]);
const [comparisonMode, setComparisonMode] = useState(false);
const [selectedForComparison, setSelectedForComparison] = useState<Backup[]>([]);
const [exportProgress, setExportProgress] = useState(0);
const [importProgress, setImportProgress] = useState(0);
const [terminalVisible, setTerminalVisible] = useState(false);
const [wizardMode, setWizardMode] = useState(false);
const [wizardStep, setWizardStep] = useState(0);
const [smartRecommendations, setSmartRecommendations] = useState([]);
const [predictiveAnalysis, setPredictiveAnalysis] = useState({});
const [costOptimization, setCostOptimization] = useState([]);
const toast = useRef<Toast>(null);
const messages = useRef<Messages>(null);
const overlayPanel = useRef<OverlayPanel>(null);
const contextMenu = useRef<ContextMenu>(null);
const terminal = useRef<Terminal>(null);
const dt = useRef<DataTable<any>>(null);
// Configuration pour l'interface utilisateur ultra-avancée
const frequenceOptions = [
{ label: 'Toutes les heures', value: 'HORAIRE', icon: 'pi pi-clock' },
{ label: 'Quotidien', value: 'QUOTIDIEN', icon: 'pi pi-calendar' },
{ label: 'Hebdomadaire', value: 'HEBDOMADAIRE', icon: 'pi pi-calendar-times' },
{ label: 'Mensuel', value: 'MENSUEL', icon: 'pi pi-calendar-plus' },
{ label: 'Temps réel', value: 'REALTIME', icon: 'pi pi-bolt' },
{ label: 'Sur événement', value: 'EVENT', icon: 'pi pi-exclamation-triangle' }
];
const viewModeOptions = [
{ label: 'Tableau', value: 'table', icon: 'pi pi-table' },
{ label: 'Grille', value: 'grid', icon: 'pi pi-th-large' },
{ label: 'Timeline', value: 'timeline', icon: 'pi pi-calendar' }
];
const notificationOptions = [
{ label: 'Toutes', value: 'all' },
{ label: 'Erreurs uniquement', value: 'errors' },
{ label: 'Critiques', value: 'critical' },
{ label: 'Aucune', value: 'none' }
];
const speedDialActions = [
{ label: 'Sauvegarde rapide', icon: 'pi pi-save', command: () => startManualBackup() },
{ label: 'Restauration', icon: 'pi pi-replay', command: () => setRestoreDialog(true) },
{ label: 'Nouvelle planification', icon: 'pi pi-calendar-plus', command: () => setScheduleDialog(true) },
{ label: 'Nettoyage', icon: 'pi pi-trash', command: () => cleanupOldBackups() },
{ label: 'Synchronisation cloud', icon: 'pi pi-cloud-upload', command: () => syncToCloud() },
{ label: 'Vérification intégrité', icon: 'pi pi-shield', command: () => verifyBackups() }
];
const wizardSteps = [
{ label: 'Configuration', icon: 'pi pi-cog' },
{ label: 'Destination', icon: 'pi pi-folder' },
{ label: 'Planification', icon: 'pi pi-calendar' },
{ label: 'Sécurité', icon: 'pi pi-shield' },
{ label: 'Validation', icon: 'pi pi-check' }
];
const cloudProvidersData = [
{ name: 'AWS S3', status: 'connected', quota: 1000, used: 345, icon: 'pi pi-amazon' },
{ name: 'Google Cloud', status: 'connected', quota: 500, used: 123, icon: 'pi pi-google' },
{ name: 'Azure Blob', status: 'disconnected', quota: 750, used: 0, icon: 'pi pi-microsoft' },
{ name: 'Dropbox', status: 'error', quota: 100, used: 89, icon: 'pi pi-cloud' }
];
const alertsExample = [
{ type: 'warning', message: 'Espace de stockage à 85%', time: new Date(), severity: 'warn' },
{ type: 'error', message: 'Échec sauvegarde AWS S3', time: new Date(), severity: 'error' },
{ type: 'info', message: 'Sauvegarde programmée terminée', time: new Date(), severity: 'success' },
{ type: 'critical', message: 'Corruption détectée dans backup_2024_01_15', time: new Date(), severity: 'error' }
];
const performanceData = [
{ label: 'Vitesse sauvegarde', value: backupSpeed, color: '#3B82F6', unit: 'MB/s' },
{ label: 'Compression', value: compressionRatio, color: '#10B981', unit: '%' },
{ label: 'Bande passante', value: networkBandwidth, color: '#F59E0B', unit: 'Mbps' },
{ label: 'Vérifications', value: retentionCompliance, color: '#8B5CF6', unit: '%' }
];
// Fonctions utilitaires
const formatFileSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const formatDuration = (seconds: number) => {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`;
} else if (minutes > 0) {
return `${minutes}m ${secs}s`;
}
return `${secs}s`;
};
const formatDateTime = (date: Date) => {
return new Date(date).toLocaleString('fr-FR');
};
const formatDate = (date: Date) => {
return new Date(date).toLocaleDateString('fr-FR');
};
// Effets et chargement des données
useEffect(() => {
loadBackupData();
loadKPIData();
loadAnalytics();
loadAlertsData();
setCloudProviders(cloudProvidersData);
setAlertsData(alertsExample);
setPerformanceMetrics(performanceData);
if (autoRefresh) {
const interval = setInterval(() => {
loadBackupData();
loadKPIData();
updateRealtimeMetrics();
}, refreshInterval * 1000);
return () => clearInterval(interval);
}
}, [autoRefresh, refreshInterval]);
const loadBackupData = async () => {
setLoading(true);
// TODO: Remplacer par un appel API réel
const mockBackups: Backup[] = [];
setBackups(mockBackups);
const mockSchedules: BackupSchedule[] = [];
setSchedules(mockSchedules);
const mockRestorePoints: RestorePoint[] = [];
setRestorePoints(mockRestorePoints);
setLoading(false);
};
const loadKPIData = async () => {
// TODO: Remplacer par un appel API réel
const mockKPI = {
totalBackups: 156,
successRate: 94.2,
storageUsed: 2.4,
avgDuration: 4.2,
lastBackup: new Date(),
scheduledCount: 8
};
setKpiData(mockKPI);
};
const loadAnalytics = async () => {
// TODO: Remplacer par un appel API réel pour les analytics
const mockChartData = {
labels: ['Jan', 'Fév', 'Mar', 'Avr', 'Mai', 'Jun'],
datasets: [
{
label: 'Sauvegardes réussies',
data: [25, 32, 28, 45, 38, 42],
backgroundColor: '#10B981',
borderColor: '#047857'
},
{
label: 'Sauvegardes échouées',
data: [2, 1, 3, 2, 1, 2],
backgroundColor: '#EF4444',
borderColor: '#DC2626'
}
]
};
setChartData(mockChartData);
};
const loadAlertsData = async () => {
// TODO: Remplacer par un appel API réel
setAlertsData(alertsExample);
};
const updateRealtimeMetrics = () => {
setStorageQuota(Math.floor(Math.random() * 15) + 80);
setSystemHealth(Math.floor(Math.random() * 10) + 85);
setBackupSpeed(Math.floor(Math.random() * 50) + 30);
setNetworkBandwidth(Math.floor(Math.random() * 100) + 200);
setActiveConnections(Math.floor(Math.random() * 20) + 5);
};
// Actions principales
const startManualBackup = () => {
setBackupInProgress(true);
setProgress(0);
const interval = setInterval(() => {
setProgress(prev => {
if (prev >= 100) {
clearInterval(interval);
setBackupInProgress(false);
toast.current?.show({
severity: 'success',
summary: 'Sauvegarde terminée',
detail: 'La sauvegarde manuelle a été créée avec succès',
life: 3000
});
loadBackupData();
return 100;
}
return prev + 5;
});
}, 200);
};
const cleanupOldBackups = () => {
confirmDialog({
message: 'Supprimer toutes les sauvegardes de plus de 90 jours ?',
header: 'Nettoyage automatique',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Nettoyer',
rejectLabel: 'Annuler',
acceptClassName: 'p-button-warning',
accept: () => {
setBlocked(true);
setTimeout(() => {
setBlocked(false);
toast.current?.show({
severity: 'success',
summary: 'Nettoyage terminé',
detail: '12 anciennes sauvegardes supprimées - 4.2 GB libérés',
life: 5000
});
}, 3000);
}
});
};
const syncToCloud = () => {
setBlocked(true);
let progress = 0;
const interval = setInterval(() => {
progress += 10;
if (progress >= 100) {
clearInterval(interval);
setBlocked(false);
toast.current?.show({
severity: 'success',
summary: 'Synchronisation terminée',
detail: 'Toutes les sauvegardes ont été synchronisées avec le cloud',
life: 3000
});
}
}, 300);
};
const verifyBackups = () => {
setBlocked(true);
setTimeout(() => {
setBlocked(false);
toast.current?.show({
severity: 'success',
summary: 'Vérification terminée',
detail: 'Toutes les sauvegardes ont été vérifiées avec succès',
life: 3000
});
}, 5000);
};
// Composants de rendu
const renderKPIDashboard = () => (
<div className="grid mb-4">
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-blue-500 to-blue-600 text-white text-center">
<div className="flex flex-column align-items-center">
<Knob
value={kpiData.successRate}
readOnly
size={60}
strokeWidth={8}
valueColor="white"
rangeColor="rgba(255,255,255,0.3)"
/>
<div className="text-sm mt-2 opacity-90">Taux de succès</div>
</div>
</Card>
</div>
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-green-500 to-green-600 text-white text-center">
<div className="flex flex-column align-items-center">
<div className="text-2xl font-bold">{kpiData.totalBackups}</div>
<div className="text-sm opacity-90">Sauvegardes</div>
<Avatar icon="pi pi-database" size="large" className="mt-2 bg-white-alpha-20" />
</div>
</Card>
</div>
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-purple-500 to-purple-600 text-white text-center">
<div className="flex flex-column align-items-center">
<div className="text-2xl font-bold">{kpiData.storageUsed}TB</div>
<div className="text-sm opacity-90">Stockage utilisé</div>
<ProgressBar
value={storageQuota}
className="mt-2 w-full"
style={{ height: '4px' }}
/>
</div>
</Card>
</div>
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-orange-500 to-orange-600 text-white text-center">
<div className="flex flex-column align-items-center">
<div className="text-2xl font-bold">{kpiData.avgDuration}min</div>
<div className="text-sm opacity-90">Durée moyenne</div>
<Avatar icon="pi pi-clock" size="large" className="mt-2 bg-white-alpha-20" />
</div>
</Card>
</div>
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-teal-500 to-teal-600 text-white text-center">
<div className="flex flex-column align-items-center">
<div className="text-2xl font-bold">{kpiData.scheduledCount}</div>
<div className="text-sm opacity-90">Planifiées actives</div>
<Avatar icon="pi pi-calendar" size="large" className="mt-2 bg-white-alpha-20" />
</div>
</Card>
</div>
<div className="col-12 md:col-2">
<Card className="bg-gradient-to-r from-pink-500 to-pink-600 text-white text-center">
<div className="flex flex-column align-items-center">
<Knob
value={systemHealth}
readOnly
size={60}
strokeWidth={8}
valueColor="white"
rangeColor="rgba(255,255,255,0.3)"
/>
<div className="text-sm mt-2 opacity-90">Santé système</div>
</div>
</Card>
</div>
</div>
);
const renderRealtimeMonitoring = () => (
<Card title="Monitoring temps réel" className="mb-4">
<div className="grid">
<div className="col-12 md:col-8">
<div className="mb-4">
{performanceMetrics.map((metric, index) => (
<div key={index} className="mb-3">
<div className="flex justify-content-between mb-1">
<span className="text-sm font-medium">{metric.label}</span>
<span className="text-sm font-bold">{metric.value}{metric.unit}</span>
</div>
<ProgressBar
value={metric.value}
className="h-1rem"
style={{ '--p-progressbar-value-bg': metric.color } as any}
/>
</div>
))}
</div>
<Chart
type="line"
data={{
labels: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00'],
datasets: [{
label: 'Vitesse de sauvegarde (MB/s)',
data: [20, 35, 60, 45, 70, 30],
borderColor: '#3B82F6',
backgroundColor: 'rgba(59, 130, 245, 0.1)',
tension: 0.4
}]
}}
options={{
responsive: true,
plugins: {
legend: { position: 'top' }
},
scales: {
y: {
beginAtZero: true
}
}
}}
/>
</div>
<div className="col-12 md:col-4">
<div className="grid">
<div className="col-12">
<div className="bg-blue-50 p-3 border-round text-center">
<i className="pi pi-server text-blue-500 text-2xl mb-2" />
<div className="text-lg font-bold text-blue-700">{activeConnections}</div>
<div className="text-sm text-600">Connexions actives</div>
</div>
</div>
<div className="col-12">
<div className="bg-green-50 p-3 border-round text-center">
<i className="pi pi-shield text-green-500 text-2xl mb-2" />
<div className="text-lg font-bold text-green-700">AES-{encryptionStrength}</div>
<div className="text-sm text-600">Chiffrement</div>
</div>
</div>
<div className="col-12">
<div className="bg-purple-50 p-3 border-round text-center">
<i className="pi pi-cloud text-purple-500 text-2xl mb-2" />
<div className="text-lg font-bold text-purple-700">
{replicationStatus === 'active' ? 'Actif' : 'Inactif'}
</div>
<div className="text-sm text-600">Réplication</div>
</div>
</div>
</div>
</div>
</div>
</Card>
);
const renderCloudProviders = () => (
<Card title="Fournisseurs Cloud" className="mb-4">
<DataView
value={cloudProviders}
itemTemplate={(provider) => (
<div className="col-12 md:col-6 lg:col-3 p-2">
<Card className="h-full">
<div className="flex align-items-center justify-content-between mb-3">
<div className="flex align-items-center">
<Avatar
icon={provider.icon}
className="mr-2"
style={{ backgroundColor: '#DEE2E6', color: '#495057' }}
/>
<span className="font-bold">{provider.name}</span>
</div>
<Tag
value={provider.status}
severity={
provider.status === 'connected' ? 'success' :
provider.status === 'error' ? 'danger' : 'info'
}
/>
</div>
<div className="mb-3">
<div className="flex justify-content-between text-sm mb-1">
<span>Utilisation</span>
<span>{provider.used}GB / {provider.quota}GB</span>
</div>
<ProgressBar
value={(provider.used / provider.quota) * 100}
className="h-1rem"
/>
</div>
<div className="flex gap-2">
<Button
label="Configurer"
size="small"
className="flex-1"
/>
<Button
icon="pi pi-cog"
size="small"
outlined
/>
</div>
</Card>
</div>
)}
layout="grid"
/>
</Card>
);
const renderAlertsPanel = () => (
<Card title="Alertes et notifications" className="mb-4">
<div className="flex align-items-center justify-content-between mb-3">
<div className="flex align-items-center gap-2">
<Badge value={alertsData.length} severity="warning" />
<span>alertes actives</span>
</div>
<Button
label="Tout marquer comme lu"
size="small"
text
/>
</div>
<Timeline
value={alertsData}
content={(item) => (
<div className="flex align-items-center">
<Badge
value={item.type}
severity={item.severity}
className="mr-2"
/>
<div className="flex-1">
<div className="font-medium">{item.message}</div>
<div className="text-sm text-600">{item.time.toLocaleTimeString('fr-FR')}</div>
</div>
<Button
icon="pi pi-times"
size="small"
text
rounded
/>
</div>
)}
className="w-full"
/>
</Card>
);
const renderAdvancedToolbar = () => (
<div className="flex flex-wrap align-items-center justify-content-between gap-3 p-3 bg-gradient-to-r from-indigo-500 to-purple-600 text-white border-round mb-4">
<div className="flex align-items-center gap-3">
<Avatar icon="pi pi-database" className="bg-white-alpha-20" style={{ color: 'white' }} />
<div>
<div className="text-xl font-bold">Centre de Sauvegarde Avancé</div>
<div className="text-sm opacity-90">
{kpiData.totalBackups} sauvegardes | {kpiData.successRate}% de réussite | {kpiData.storageUsed}TB utilisés
</div>
</div>
</div>
<div className="flex align-items-center gap-2">
<SelectButton
value={viewMode}
options={viewModeOptions}
onChange={(e) => setViewMode(e.value)}
optionLabel="label"
optionValue="value"
className="view-mode-selector"
/>
<div className="flex align-items-center gap-2 bg-white-alpha-20 px-3 py-1 border-round">
<i className="pi pi-wifi" />
<span className="text-sm">Auto-refresh</span>
<ToggleButton
checked={autoRefresh}
onChange={(e) => setAutoRefresh(e.value)}
onIcon="pi pi-pause"
offIcon="pi pi-play"
className="ml-2"
/>
</div>
<Button
icon="pi pi-chart-line"
rounded
text
onClick={() => setAnalyticsDialog(true)}
tooltip="Analytics avancées"
className="text-white"
/>
<Button
icon="pi pi-desktop"
rounded
text
onClick={() => setTerminalVisible(true)}
tooltip="Terminal"
className="text-white"
/>
<Button
icon="pi pi-cog"
rounded
text
onClick={() => setSidebarVisible(true)}
tooltip="Configuration"
className="text-white"
/>
</div>
</div>
);
const renderBackupList = () => (
<Card title="Liste des sauvegardes">
<DataTable
ref={dt}
value={backups}
paginator
rows={10}
rowsPerPageOptions={[10, 25, 50]}
loading={loading}
globalFilter={globalFilter}
emptyMessage="Aucune sauvegarde trouvée"
responsiveLayout="stack"
breakpoint="768px"
>
<Column field="nom" header="Nom" sortable />
<Column field="type" header="Type" sortable />
<Column field="statut" header="Statut" sortable />
<Column field="dateCreation" header="Date" sortable />
<Column field="taille" header="Taille" sortable />
<Column header="Actions" />
</DataTable>
</Card>
);
const renderSchedules = () => (
<Card title="Planifications">
<DataTable
value={schedules}
emptyMessage="Aucune planification configurée"
>
<Column field="nom" header="Nom" />
<Column field="frequence" header="Fréquence" />
<Column field="actif" header="Actif" />
<Column header="Actions" />
</DataTable>
</Card>
);
return (
<div className="backup-management-system">
<BlockUI blocked={blocked}>
<Toast ref={toast} />
<Messages ref={messages} />
<ConfirmDialog />
{renderAdvancedToolbar()}
{(backupInProgress || restoreInProgress || exportProgress > 0) && (
<Card className="mb-4">
<div className="grid">
{backupInProgress && (
<div className="col-12 md:col-4">
<div className="flex align-items-center gap-3">
<i className="pi pi-spin pi-save text-blue-500" />
<div className="flex-1">
<p className="m-0 font-bold">Sauvegarde en cours...</p>
<ProgressBar value={progress} className="mt-2" />
</div>
</div>
</div>
)}
{restoreInProgress && (
<div className="col-12 md:col-4">
<div className="flex align-items-center gap-3">
<i className="pi pi-spin pi-replay text-green-500" />
<div className="flex-1">
<p className="m-0 font-bold">Restauration en cours...</p>
<ProgressBar value={restoreProgress} className="mt-2" />
</div>
</div>
</div>
)}
{exportProgress > 0 && exportProgress < 100 && (
<div className="col-12 md:col-4">
<div className="flex align-items-center gap-3">
<i className="pi pi-spin pi-download text-purple-500" />
<div className="flex-1">
<p className="m-0 font-bold">Export en cours...</p>
<ProgressBar value={exportProgress} className="mt-2" />
</div>
</div>
</div>
)}
</div>
</Card>
)}
<TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
<TabPanel header="Vue d'ensemble" leftIcon="pi pi-home mr-2">
{renderKPIDashboard()}
{renderRealtimeMonitoring()}
{renderAlertsPanel()}
</TabPanel>
<TabPanel header="Sauvegardes" leftIcon="pi pi-database mr-2">
{renderBackupList()}
</TabPanel>
<TabPanel header="Planifications" leftIcon="pi pi-calendar mr-2">
{renderSchedules()}
</TabPanel>
<TabPanel header="Cloud" leftIcon="pi pi-cloud mr-2">
{renderCloudProviders()}
</TabPanel>
<TabPanel header="Analytics" leftIcon="pi pi-chart-bar mr-2">
<div className="grid">
<div className="col-12 md:col-8">
<Card title="Tendances des sauvegardes">
<Chart type="bar" data={chartData} className="w-full" />
</Card>
</div>
<div className="col-12 md:col-4">
<Card title="Répartition par destination">
<Chart
type="doughnut"
data={{
labels: ['Local', 'AWS S3', 'Google Cloud', 'Azure'],
datasets: [{
data: [40, 30, 20, 10],
backgroundColor: ['#3B82F6', '#10B981', '#F59E0B', '#8B5CF6']
}]
}}
options={{
plugins: {
legend: { position: 'bottom' }
}
}}
/>
</Card>
</div>
</div>
</TabPanel>
<TabPanel header="Sécurité" leftIcon="pi pi-shield mr-2">
<div className="grid">
<div className="col-12 md:col-6">
<Card title="Chiffrement et sécurité">
<Fieldset legend="Configuration du chiffrement">
<div className="field">
<label>Algorithme de chiffrement</label>
<Dropdown
value="AES-256"
options={[
{ label: 'AES-128', value: 'AES-128' },
{ label: 'AES-256', value: 'AES-256' },
{ label: 'RSA-2048', value: 'RSA-2048' }
]}
className="w-full"
/>
</div>
<div className="field">
<div className="flex align-items-center gap-2">
<InputSwitch checked={verificationEnabled} onChange={(e) => setVerificationEnabled(e.value)} />
<label>Vérification d'intégrité</label>
</div>
</div>
</Fieldset>
</Card>
</div>
<div className="col-12 md:col-6">
<Card title="Audit Trail">
<Timeline
value={[
{ event: 'Sauvegarde créée', time: new Date(), user: 'Admin' },
{ event: 'Accès à la sauvegarde', time: new Date(), user: 'User1' },
{ event: 'Restauration initialisée', time: new Date(), user: 'Admin' }
]}
content={(item) => (
<div>
<div className="font-bold">{item.event}</div>
<div className="text-sm text-600">{item.user} - {item.time.toLocaleTimeString()}</div>
</div>
)}
/>
</Card>
</div>
</div>
</TabPanel>
<TabPanel header="Maintenance" leftIcon="pi pi-wrench mr-2">
<Splitter style={{ height: '500px' }}>
<SplitterPanel size={30}>
<ScrollPanel style={{ width: '100%', height: '100%' }}>
<div className="p-3">
<h6>Actions de maintenance</h6>
<Menu
model={[
{ label: 'Nettoyage automatique', icon: 'pi pi-trash', command: cleanupOldBackups },
{ label: 'Vérification intégrité', icon: 'pi pi-shield', command: verifyBackups },
{ label: 'Optimisation stockage', icon: 'pi pi-cog' },
{ label: 'Synchronisation cloud', icon: 'pi pi-cloud-upload', command: syncToCloud },
{ label: 'Rapport de santé', icon: 'pi pi-chart-line' },
{ label: 'Réindexation', icon: 'pi pi-refresh' }
]}
/>
</div>
</ScrollPanel>
</SplitterPanel>
<SplitterPanel size={70}>
<ScrollPanel style={{ width: '100%', height: '100%' }}>
<div className="p-3">
<Card title="Console de maintenance">
<Terminal
ref={terminal}
welcomeMessage="Console de maintenance BTpXpress - Tapez 'help' pour les commandes disponibles"
prompt="backup $"
className="bg-gray-900 text-white"
/>
</Card>
</div>
</ScrollPanel>
</SplitterPanel>
</Splitter>
</TabPanel>
</TabView>
<SpeedDial
model={speedDialActions}
radius={80}
type="semi-circle"
direction="up-left"
style={{ left: 'calc(50% - 2rem)', bottom: 0 }}
buttonClassName="p-button-help"
maskClassName="bg-black-alpha-30"
/>
<Sidebar
visible={sidebarVisible}
onHide={() => setSidebarVisible(false)}
position="right"
className="w-25rem"
>
<h3>Configuration avancée</h3>
<Accordion multiple>
<AccordionTab header="Paramètres généraux">
<div className="field">
<label>Mode d'affichage</label>
<SelectButton
value={viewMode}
options={viewModeOptions}
onChange={(e) => setViewMode(e.value)}
optionLabel="label"
optionValue="value"
className="w-full mt-2"
/>
</div>
<div className="field">
<div className="flex align-items-center gap-2">
<InputSwitch
checked={autoRefresh}
onChange={(e) => setAutoRefresh(e.value)}
/>
<label>Actualisation automatique</label>
</div>
</div>
<div className="field">
<label>Intervalle (secondes)</label>
<Slider
value={refreshInterval}
onChange={(e) => setRefreshInterval(e.value as number)}
min={10}
max={300}
step={10}
className="w-full mt-2"
/>
</div>
</AccordionTab>
<AccordionTab header="Compression et optimisation">
<div className="field">
<label>Niveau de compression</label>
<Knob
value={compressionLevel}
onChange={(e) => setCompressionLevel(e.value)}
min={1}
max={9}
size={80}
className="mt-2"
/>
</div>
<div className="field">
<div className="flex align-items-center gap-2">
<InputSwitch
checked={incrementalBackup}
onChange={(e) => setIncrementalBackup(e.value)}
/>
<label>Sauvegarde incrémentale</label>
</div>
</div>
</AccordionTab>
<AccordionTab header="Notifications">
<div className="field">
<label>Niveau de notification</label>
<Dropdown
value={notificationLevel}
options={notificationOptions}
onChange={(e) => setNotificationLevel(e.value)}
className="w-full"
/>
</div>
<div className="field">
<div className="flex align-items-center gap-2">
<InputSwitch
checked={cloudSync}
onChange={(e) => setCloudSync(e.value)}
/>
<label>Synchronisation cloud</label>
</div>
</div>
</AccordionTab>
</Accordion>
</Sidebar>
<Dialog
header="Assistant de configuration"
visible={wizardMode}
onHide={() => setWizardMode(false)}
style={{ width: '70vw' }}
modal
>
<Steps
model={wizardSteps}
activeIndex={wizardStep}
onSelect={(e) => setWizardStep(e.index)}
readOnly={false}
className="mb-4"
/>
<div className="wizard-content">
{wizardStep === 0 && (
<div>
<h4>Configuration de base</h4>
<p>Définissons les paramètres fondamentaux de votre système de sauvegarde.</p>
<div className="field">
<label>Fréquence par défaut</label>
<Dropdown options={frequenceOptions} className="w-full" />
</div>
</div>
)}
{wizardStep === 4 && (
<div className="text-center">
<i className="pi pi-check-circle text-green-500 text-6xl mb-3" />
<h4>Configuration terminée !</h4>
<p>Votre système de sauvegarde est maintenant configuré et opérationnel.</p>
</div>
)}
</div>
<div className="flex justify-content-between mt-4">
<Button
label="Précédent"
icon="pi pi-chevron-left"
outlined
disabled={wizardStep === 0}
onClick={() => setWizardStep(wizardStep - 1)}
/>
<Button
label={wizardStep === wizardSteps.length - 1 ? 'Terminer' : 'Suivant'}
icon={wizardStep === wizardSteps.length - 1 ? 'pi pi-check' : 'pi pi-chevron-right'}
iconPos="right"
onClick={() => {
if (wizardStep === wizardSteps.length - 1) {
setWizardMode(false);
toast.current?.show({
severity: 'success',
summary: 'Configuration terminée',
detail: 'Votre système de sauvegarde a été configuré avec succès',
life: 5000
});
} else {
setWizardStep(wizardStep + 1);
}
}}
/>
</div>
</Dialog>
<ScrollTop threshold={600} className="w-3rem h-3rem border-round bg-primary" />
</BlockUI>
</div>
);
};
export default SauvegardePage;
// Styles CSS pour l'interface ultra-professionnelle de sauvegarde
const customStyles = `
.backup-management-system .p-tabview-nav {
background: linear-gradient(90deg, #4f46e5 0%, #7c3aed 100%);
border-radius: 10px 10px 0 0;
}
.backup-management-system .p-tabview-nav li .p-tabview-nav-link {
color: white;
border: none;
}
.backup-management-system .p-tabview-nav li.p-highlight .p-tabview-nav-link {
background: rgba(255, 255, 255, 0.2);
border-radius: 8px;
margin: 4px;
}
.view-mode-selector .p-button {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
}
.view-mode-selector .p-button.p-highlight {
background: rgba(255, 255, 255, 0.3);
}
.hover\\:shadow-lg:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.3s ease;
}
.transition-shadow {
transition: box-shadow 0.3s ease;
}
.bg-gradient-to-r {
background: linear-gradient(90deg, var(--tw-gradient-stops));
}
.from-blue-500 {
--tw-gradient-from: #3b82f6;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(59, 130, 246, 0));
}
.to-blue-600 {
--tw-gradient-to: #2563eb;
}
.from-green-500 {
--tw-gradient-from: #22c55e;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(34, 197, 94, 0));
}
.to-green-600 {
--tw-gradient-to: #16a34a;
}
.from-purple-500 {
--tw-gradient-from: #a855f7;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(168, 85, 247, 0));
}
.to-purple-600 {
--tw-gradient-to: #9333ea;
}
.from-orange-500 {
--tw-gradient-from: #f97316;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(249, 115, 22, 0));
}
.to-orange-600 {
--tw-gradient-to: #ea580c;
}
.from-teal-500 {
--tw-gradient-from: #14b8a6;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(20, 184, 166, 0));
}
.to-teal-600 {
--tw-gradient-to: #0d9488;
}
.from-pink-500 {
--tw-gradient-from: #ec4899;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(236, 72, 153, 0));
}
.to-pink-600 {
--tw-gradient-to: #db2777;
}
.from-indigo-500 {
--tw-gradient-from: #6366f1;
--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to, rgba(99, 102, 241, 0));
}
.to-purple-600 {
--tw-gradient-to: #9333ea;
}
.bg-white-alpha-20 {
background: rgba(255, 255, 255, 0.2);
}
.wizard-content {
min-height: 300px;
}
`;
// Injection du style dans le document
if (typeof document !== 'undefined') {
const style = document.createElement('style');
style.textContent = customStyles;
document.head.appendChild(style);
}