Files
towercontrol/src/components/settings/AdvancedSettingsPageClient.tsx
Julien Froidefond c650c67627 feat: integrate UserPreferencesContext for improved preference management
- Added `UserPreferencesProvider` to `RootLayout` for centralized user preferences handling.
- Updated components to remove direct user preferences fetching, relying on context instead.
- Enhanced SSR data fetching by consolidating user preferences retrieval into a single service call.
- Cleaned up unused props in various components to streamline the codebase.
2025-09-21 15:03:19 +02:00

251 lines
10 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { useState } from 'react';
import { Header } from '@/components/ui/Header';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import { backupClient, BackupListResponse } from '@/clients/backup-client';
import Link from 'next/link';
import { parseDate, getToday, formatDateForDisplay } from '@/lib/date-utils';
interface DatabaseStats {
taskCount: number;
tagCount: number;
completionRate: number;
}
interface AdvancedSettingsPageClientProps {
initialDbStats: DatabaseStats;
initialBackupData: BackupListResponse;
}
export function AdvancedSettingsPageClient({
initialDbStats,
initialBackupData
}: AdvancedSettingsPageClientProps) {
const [backupData, setBackupData] = useState<BackupListResponse>(initialBackupData);
const [dbStats] = useState<DatabaseStats>(initialDbStats);
const [isCreatingBackup, setIsCreatingBackup] = useState(false);
const [isVerifying, setIsVerifying] = useState(false);
const reloadBackupData = async () => {
try {
const data = await backupClient.listBackups();
setBackupData(data);
} catch (error) {
console.error('Failed to reload backup data:', error);
}
};
const handleCreateBackup = async () => {
setIsCreatingBackup(true);
try {
const result = await backupClient.createBackup();
if (result === null) {
alert('⏭️ Sauvegarde sautée : aucun changement détecté');
} else {
alert('✅ Sauvegarde créée avec succès');
}
await reloadBackupData();
} catch (error) {
console.error('Failed to create backup:', error);
alert('❌ Erreur lors de la création de la sauvegarde');
} finally {
setIsCreatingBackup(false);
}
};
const handleVerifyDatabase = async () => {
setIsVerifying(true);
try {
await backupClient.verifyDatabase();
alert('✅ Base de données vérifiée avec succès');
} catch (error) {
console.error('Database verification failed:', error);
alert('❌ Erreur lors de la vérification de la base');
} finally {
setIsVerifying(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 formatTimeAgo = (date: Date): string => {
// Format fixe pour éviter les erreurs d'hydratation
return formatDateForDisplay(date, 'DISPLAY_MEDIUM');
};
const getNextBackupTime = (): string => {
if (!backupData.scheduler.nextBackup) return 'Non planifiée';
const nextBackup = parseDate(backupData.scheduler.nextBackup);
const now = getToday();
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`;
}
};
return (
<div className="min-h-screen bg-[var(--background)]">
<Header
title="TowerControl"
subtitle="Paramètres avancés"
/>
<div className="container mx-auto px-4 py-4">
<div className="max-w-4xl mx-auto">
{/* Breadcrumb */}
<div className="mb-4 text-sm">
<Link href="/settings" className="text-[var(--muted-foreground)] hover:text-[var(--primary)]">
Paramètres
</Link>
<span className="mx-2 text-[var(--muted-foreground)]">/</span>
<span className="text-[var(--foreground)]">Avancé</span>
</div>
{/* Page Header */}
<div className="mb-6">
<h1 className="text-2xl font-mono font-bold text-[var(--foreground)] mb-2">
🛠 Paramètres avancés
</h1>
<p className="text-[var(--muted-foreground)]">
Configuration système, sauvegarde et outils de développement
</p>
</div>
<div className="space-y-6">
{/* Sauvegarde et données */}
<Card>
<CardHeader>
<h2 className="text-lg font-semibold">💾 Sauvegarde et données</h2>
<p className="text-sm text-[var(--muted-foreground)]">
Gestion des sauvegardes automatiques et manuelles
</p>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-[var(--card)] rounded border border-dashed border-[var(--border)]">
<div className="flex items-center justify-between mb-2">
<h3 className="font-medium">Sauvegarde automatique</h3>
<span className={`h-2 w-2 rounded-full ${
backupData.scheduler.isRunning ? 'bg-green-500' : 'bg-red-500'
}`}></span>
</div>
<p className="text-sm text-[var(--muted-foreground)] mb-2">
{backupData.scheduler.isEnabled
? `Sauvegarde ${backupData.scheduler.interval === 'hourly' ? 'toutes les heures' :
backupData.scheduler.interval === 'daily' ? 'quotidienne' : 'hebdomadaire'}`
: 'Sauvegarde automatique désactivée'
}
</p>
<p className="text-xs text-[var(--muted-foreground)]">
{backupData.scheduler.isRunning
? `Prochaine sauvegarde: ${getNextBackupTime()}`
: 'Planificateur arrêté'
}
</p>
</div>
<div className="p-4 bg-[var(--card)] rounded border border-dashed border-[var(--border)]">
<h3 className="font-medium mb-2">Sauvegardes disponibles</h3>
<p className="text-sm text-[var(--muted-foreground)] mb-2">
{backupData.backups.length} sauvegarde{backupData.backups.length > 1 ? 's' : ''} conservée{backupData.backups.length > 1 ? 's' : ''}
</p>
{backupData.backups.length > 0 ? (
<p className="text-xs text-[var(--muted-foreground)]">
Dernière: {formatTimeAgo(backupData.backups[0].createdAt)}
({formatFileSize(backupData.backups[0].size)})
</p>
) : (
<p className="text-xs text-[var(--muted-foreground)]">
Aucune sauvegarde disponible
</p>
)}
</div>
</div>
<div className="flex gap-2 pt-2">
<Button
onClick={handleCreateBackup}
disabled={isCreatingBackup}
className="px-3 py-1.5 bg-[var(--primary)] text-[var(--primary-foreground)] rounded text-sm font-medium"
>
{isCreatingBackup ? 'Création...' : 'Créer une sauvegarde'}
</Button>
<Link href="/settings/backup">
<Button className="px-3 py-1.5 bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)] rounded text-sm font-medium">
Gérer les sauvegardes
</Button>
</Link>
<Button
onClick={handleVerifyDatabase}
disabled={isVerifying}
className="px-3 py-1.5 bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)] rounded text-sm font-medium"
>
{isVerifying ? 'Vérification...' : 'Vérifier DB'}
</Button>
</div>
</CardContent>
</Card>
{/* Base de données */}
<Card>
<CardHeader>
<h2 className="text-lg font-semibold">🗄 Base de données</h2>
<p className="text-sm text-[var(--muted-foreground)]">
Informations et maintenance de la base de données
</p>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="p-4 bg-[var(--card)] rounded border border-dashed border-[var(--border)]">
<h3 className="font-medium mb-1">Tâches</h3>
<p className="text-2xl font-bold text-[var(--primary)]">
{dbStats.taskCount}
</p>
<p className="text-xs text-[var(--muted-foreground)]">entrées</p>
</div>
<div className="p-4 bg-[var(--card)] rounded border border-dashed border-[var(--border)]">
<h3 className="font-medium mb-1">Tags</h3>
<p className="text-2xl font-bold text-[var(--primary)]">
{dbStats.tagCount}
</p>
<p className="text-xs text-[var(--muted-foreground)]">entrées</p>
</div>
<div className="p-4 bg-[var(--card)] rounded border border-dashed border-[var(--border)]">
<h3 className="font-medium mb-1">Taux complétion</h3>
<p className="text-2xl font-bold text-[var(--primary)]">
{dbStats.completionRate}
</p>
<p className="text-xs text-[var(--muted-foreground)]">%</p>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
);
}