feat: add system info and backup functionalities to settings page

- Integrated system info fetching in `SettingsPage` for improved user insights.
- Enhanced `SettingsIndexPageClient` with manual backup creation and Jira connection testing features.
- Added loading states and auto-dismiss messages for user feedback during actions.
- Updated UI to display system info and backup statistics dynamically.
This commit is contained in:
Julien Froidefond
2025-09-20 16:38:33 +02:00
parent da0565472d
commit 3c20df95d9
5 changed files with 434 additions and 27 deletions

View File

@@ -5,12 +5,102 @@ import { Header } from '@/components/ui/Header';
import { Card, CardHeader, CardContent } from '@/components/ui/Card';
import { UserPreferencesProvider } from '@/contexts/UserPreferencesContext';
import Link from 'next/link';
import { useState, useEffect, useTransition } from 'react';
import { backupClient } from '@/clients/backup-client';
import { jiraClient } from '@/clients/jira-client';
import { getSystemInfo } from '@/actions/system-info';
import { SystemInfo } from '@/services/system-info';
interface SettingsIndexPageClientProps {
initialPreferences: UserPreferences;
initialSystemInfo?: SystemInfo;
}
export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPageClientProps) {
export function SettingsIndexPageClient({ initialPreferences, initialSystemInfo }: SettingsIndexPageClientProps) {
// États pour les actions
const [isBackupLoading, setIsBackupLoading] = useState(false);
const [isJiraTestLoading, setIsJiraTestLoading] = useState(false);
const [systemInfo, setSystemInfo] = useState<SystemInfo | null>(initialSystemInfo || null);
const [messages, setMessages] = useState<{
backup?: { type: 'success' | 'error', text: string };
jira?: { type: 'success' | 'error', text: string };
}>({});
// useTransition pour le server action
const [isSystemInfoLoading, startTransition] = useTransition();
// Fonction pour recharger les infos système (server action)
const loadSystemInfo = () => {
startTransition(async () => {
try {
const result = await getSystemInfo();
if (result.success && result.data) {
setSystemInfo(result.data);
} else {
console.error('Error loading system info:', result.error);
}
} catch (error) {
console.error('Error loading system info:', error);
}
});
};
// Fonction pour créer une sauvegarde manuelle
const handleCreateBackup = async () => {
setIsBackupLoading(true);
try {
const backup = await backupClient.createBackup();
setMessages(prev => ({
...prev,
backup: { type: 'success', text: `Sauvegarde créée: ${backup.filename}` }
}));
// Recharger les infos système pour mettre à jour le nombre de sauvegardes
loadSystemInfo();
} catch {
setMessages(prev => ({
...prev,
backup: { type: 'error', text: 'Erreur lors de la création de la sauvegarde' }
}));
} finally {
setIsBackupLoading(false);
}
};
// Fonction pour tester la connexion Jira
const handleTestJira = async () => {
setIsJiraTestLoading(true);
try {
const status = await jiraClient.testConnection();
setMessages(prev => ({
...prev,
jira: {
type: status.connected ? 'success' : 'error',
text: status.connected ? 'Connexion Jira réussie !' : `Erreur: ${status.message || 'Connexion échouée'}`
}
}));
} catch {
setMessages(prev => ({
...prev,
jira: { type: 'error', text: 'Erreur lors du test de connexion Jira' }
}));
} finally {
setIsJiraTestLoading(false);
}
};
// Auto-dismiss des messages après 5 secondes
useEffect(() => {
Object.keys(messages).forEach(key => {
if (messages[key as keyof typeof messages]) {
const timer = setTimeout(() => {
setMessages(prev => ({ ...prev, [key]: undefined }));
}, 5000);
return () => clearTimeout(timer);
}
});
}, [messages]);
const settingsPages = [
{
href: '/settings/general',
@@ -63,7 +153,7 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
</div>
{/* Quick Stats */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
<Card>
<CardContent className="p-4">
<div className="flex items-center gap-3">
@@ -82,9 +172,14 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
<span className="text-2xl">🔌</span>
<div>
<p className="text-sm text-[var(--muted-foreground)]">Jira</p>
<p className="font-medium">
{initialPreferences.jiraConfig.enabled ? 'Configuré' : 'Non configuré'}
</p>
<div className="flex items-center gap-2">
<p className="font-medium">
{initialPreferences.jiraConfig.enabled ? 'Configuré' : 'Non configuré'}
</p>
{initialPreferences.jiraConfig.enabled && (
<span className="w-2 h-2 bg-green-500 rounded-full" title="Jira configuré"></span>
)}
</div>
</div>
</div>
</CardContent>
@@ -101,6 +196,20 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
</div>
</CardContent>
</Card>
<Card>
<CardContent className="p-4">
<div className="flex items-center gap-3">
<span className="text-2xl">💾</span>
<div>
<p className="text-sm text-[var(--muted-foreground)]">Sauvegardes</p>
<p className="font-medium">
{systemInfo ? systemInfo.database.totalBackups : '...'}
</p>
</div>
</div>
</CardContent>
</Card>
</div>
{/* Settings Sections */}
@@ -168,9 +277,22 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
<p className="text-sm text-[var(--muted-foreground)]">
Créer une sauvegarde des données
</p>
{messages.backup && (
<p className={`text-xs mt-1 ${
messages.backup.type === 'success'
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}>
{messages.backup.text}
</p>
)}
</div>
<button className="px-3 py-1.5 bg-[var(--primary)] text-[var(--primary-foreground)] rounded text-sm">
Sauvegarder
<button
onClick={handleCreateBackup}
disabled={isBackupLoading}
className="px-3 py-1.5 bg-[var(--primary)] text-[var(--primary-foreground)] rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed"
>
{isBackupLoading ? 'En cours...' : 'Sauvegarder'}
</button>
</div>
</CardContent>
@@ -184,12 +306,22 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
<p className="text-sm text-[var(--muted-foreground)]">
Tester la connexion Jira
</p>
{messages.jira && (
<p className={`text-xs mt-1 ${
messages.jira.type === 'success'
? 'text-green-600 dark:text-green-400'
: 'text-red-600 dark:text-red-400'
}`}>
{messages.jira.text}
</p>
)}
</div>
<button
className="px-3 py-1.5 bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)] rounded text-sm"
disabled={!initialPreferences.jiraConfig.enabled}
onClick={handleTestJira}
disabled={!initialPreferences.jiraConfig.enabled || isJiraTestLoading}
className="px-3 py-1.5 bg-[var(--card)] text-[var(--foreground)] border border-[var(--border)] rounded text-sm disabled:opacity-50 disabled:cursor-not-allowed"
>
Tester
{isJiraTestLoading ? 'Test...' : 'Tester'}
</button>
</div>
</CardContent>
@@ -200,23 +332,66 @@ export function SettingsIndexPageClient({ initialPreferences }: SettingsIndexPag
{/* System Info */}
<Card className="mt-8">
<CardHeader>
<h2 className="text-lg font-semibold"> Informations système</h2>
<div className="flex items-center justify-between">
<h2 className="text-lg font-semibold"> Informations système</h2>
<button
onClick={loadSystemInfo}
disabled={isSystemInfoLoading}
className="text-xs px-2 py-1 bg-[var(--card)] border border-[var(--border)] rounded hover:bg-[var(--card-hover)] disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSystemInfoLoading ? '🔄 Chargement...' : '🔄 Actualiser'}
</button>
</div>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
<div>
<p className="text-[var(--muted-foreground)]">Version</p>
<p className="font-medium">TowerControl v1.0.0</p>
{systemInfo ? (
<>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 text-sm mb-4">
<div>
<p className="text-[var(--muted-foreground)]">Version</p>
<p className="font-medium">TowerControl v{systemInfo.version}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Dernière maj</p>
<p className="font-medium">{systemInfo.lastUpdate}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Environnement</p>
<p className="font-medium capitalize">{systemInfo.environment}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Uptime</p>
<p className="font-medium">{systemInfo.uptime}</p>
</div>
</div>
<div className="border-t border-[var(--border)] pt-4">
<h3 className="text-sm font-medium mb-3 text-[var(--muted-foreground)]">Base de données</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 text-sm">
<div>
<p className="text-[var(--muted-foreground)]">Tâches</p>
<p className="font-medium">{systemInfo.database.totalTasks}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Utilisateurs</p>
<p className="font-medium">{systemInfo.database.totalUsers}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Sauvegardes</p>
<p className="font-medium">{systemInfo.database.totalBackups}</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Taille DB</p>
<p className="font-medium">{systemInfo.database.databaseSize}</p>
</div>
</div>
</div>
</>
) : (
<div className="text-center py-4">
<p className="text-[var(--muted-foreground)]">Chargement des informations système...</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Dernière maj</p>
<p className="font-medium">Il y a 2 jours</p>
</div>
<div>
<p className="text-[var(--muted-foreground)]">Env</p>
<p className="font-medium">Development</p>
</div>
</div>
)}
</CardContent>
</Card>
</div>