- 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.
127 lines
5.1 KiB
TypeScript
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>
|
|
);
|
|
}
|