/** * Offline settings tab — shows cached trips, storage info, and controls * to re-sync or clear the offline cache. */ import React, { useState, useEffect, useCallback } from 'react' import { Wifi, RefreshCw, Trash2, Database } from 'lucide-react' import Section from './Section' import { offlineDb, clearAll } from '../../db/offlineDb' import { tripSyncManager } from '../../sync/tripSyncManager' import { mutationQueue } from '../../sync/mutationQueue' import type { SyncMeta } from '../../db/offlineDb' import type { Trip } from '../../types' interface CachedTripRow { trip: Trip meta: SyncMeta placeCount: number fileCount: number } export default function OfflineTab(): React.ReactElement { const [rows, setRows] = useState([]) const [pendingCount, setPendingCount] = useState(0) const [syncing, setSyncing] = useState(false) const [clearing, setClearing] = useState(false) const [loading, setLoading] = useState(true) const load = useCallback(async () => { setLoading(true) try { const [metas, pending] = await Promise.all([ offlineDb.syncMeta.toArray(), mutationQueue.pendingCount(), ]) setPendingCount(pending) const result: CachedTripRow[] = [] for (const meta of metas) { const trip = await offlineDb.trips.get(meta.tripId) if (!trip) continue const [placeCount, fileCount] = await Promise.all([ offlineDb.places.where('trip_id').equals(meta.tripId).count(), offlineDb.tripFiles.where('trip_id').equals(meta.tripId).count(), ]) result.push({ trip, meta, placeCount, fileCount }) } result.sort((a, b) => (a.trip.start_date ?? '').localeCompare(b.trip.start_date ?? '')) setRows(result) } finally { setLoading(false) } }, []) useEffect(() => { load() }, [load]) async function handleResync() { setSyncing(true) try { await tripSyncManager.syncAll() await load() } finally { setSyncing(false) } } async function handleClear() { if (!window.confirm('Clear all offline trip data? You can re-sync anytime while online.')) return setClearing(true) try { await clearAll() await load() } finally { setClearing(false) } } const formatDate = (d: string | null | undefined) => d ? new Date(d).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }) : '—' return (
{/* Stats row */}
{/* Actions */}
{/* Cached trip list */} {loading ? (

Loading…

) : rows.length === 0 ? (

No trips cached yet. Connect to internet to sync.

) : (
{rows.map(({ trip, meta, placeCount, fileCount }) => (
{trip.name} {meta.lastSyncedAt ? new Date(meta.lastSyncedAt).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }) : '—'}
{formatDate(trip.start_date)} – {formatDate(trip.end_date)} {' · '} {placeCount} place{placeCount !== 1 ? 's' : ''} {' · '} {fileCount} file{fileCount !== 1 ? 's' : ''}
))}
)}
) } function Stat({ label, value }: { label: string; value: number }) { return (
{value}
{label}
) }