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.
This commit is contained in:
126
components/offline/OfflineSyncDetails.tsx
Normal file
126
components/offline/OfflineSyncDetails.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user