Files
ERitors-Scribe-Desktop/components/offline/OfflineSyncDetails.tsx
natreex d5b8191996 Add offline mode components and enhance synchronization logic
- Introduced `OfflineSyncManager`, `OfflineToggle`, `OfflinePinSetup`, `OfflineIndicator`, and `OfflineSyncDetails` components to support offline mode functionality.
- Added PIN verification (`OfflinePinVerify`) for secure offline access.
- Implemented sync options for books (current, recent, all) with progress tracking and improved user feedback.
- Enhanced offline context integration and error handling in sync processes.
- Refined UI elements for better online/offline status visibility and manual sync controls.
2025-12-22 16:46:43 -05:00

127 lines
5.1 KiB
TypeScript

'use client';
import React, { useContext } from 'react';
import OfflineContext from '@/context/OfflineContext';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faDatabase, faSync, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
/**
* OfflineSyncDetails - Detailed view of sync status per table
* Shows pending changes and last sync time for each data type
*/
export default function OfflineSyncDetails() {
const { offlineMode } = useContext(OfflineContext);
const { syncProgress, isDatabaseInitialized } = offlineMode;
if (!isDatabaseInitialized) {
return (
<div className="p-4 text-center text-gray-500">
<FontAwesomeIcon icon={faDatabase} className="text-3xl mb-2" />
<p>Base de données non initialisée</p>
</div>
);
}
if (!syncProgress.tables || syncProgress.tables.length === 0) {
return (
<div className="p-4 text-center text-gray-500">
<FontAwesomeIcon icon={faSync} className="text-3xl mb-2" />
<p>Aucune donnée de synchronisation disponible</p>
</div>
);
}
// Format timestamp
const formatTime = (timestamp: number) => {
if (!timestamp) return 'Jamais';
const date = new Date(timestamp);
return date.toLocaleString('fr-FR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
// Get friendly name for table
const getTableName = (table: string): string => {
const names: { [key: string]: string } = {
'erit_books': 'Livres',
'book_chapters': 'Chapitres',
'book_chapter_content': 'Contenu des chapitres',
'book_characters': 'Personnages',
'book_world': 'Monde',
'book_world_elements': 'Éléments du monde',
'ai_conversations': 'Conversations IA',
'ai_messages_history': 'Messages IA',
'book_guide_line': 'Lignes directrices',
'book_plot_points': 'Points de l\'intrigue',
'book_incidents': 'Incidents'
};
return names[table] || table;
};
return (
<div className="p-4">
<h3 className="text-lg font-semibold mb-4 flex items-center gap-2">
<FontAwesomeIcon icon={faDatabase} />
État de synchronisation
</h3>
<div className="space-y-2">
{syncProgress.tables.map((tableStatus) => {
const hasPending = tableStatus.pending > 0;
return (
<div
key={tableStatus.table}
className={`p-3 rounded-lg border ${
hasPending
? 'border-orange-200 bg-orange-50'
: 'border-gray-200 bg-gray-50'
}`}
>
<div className="flex items-center justify-between">
<div className="flex-1">
<div className="font-medium text-sm">
{getTableName(tableStatus.table)}
</div>
<div className="text-xs text-gray-500 mt-1">
Dernière sync: {formatTime(tableStatus.lastSync)}
</div>
</div>
{hasPending && (
<div className="flex items-center gap-2 text-orange-600">
<FontAwesomeIcon
icon={faExclamationCircle}
className="text-sm"
/>
<span className="text-sm font-medium">
{tableStatus.pending} en attente
</span>
</div>
)}
{!hasPending && tableStatus.lastSync > 0 && (
<div className="text-green-600 text-sm">
Synchronisé
</div>
)}
</div>
</div>
);
})}
</div>
{/* Summary */}
<div className="mt-4 p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="text-sm text-blue-800">
<strong>Total:</strong> {syncProgress.pendingChanges} changement(s) en attente
</div>
</div>
</div>
);
}