/** * OfflineBanner — persistent top bar indicating connectivity + sync state. * * States: * offline + N queued → amber bar "Offline — N changes queued" * offline + 0 queued → amber bar "Offline" * online + N pending → blue bar "Syncing N changes…" * online + 0 pending → hidden */ import React, { useState, useEffect } from 'react' import { WifiOff, RefreshCw } from 'lucide-react' import { mutationQueue } from '../../sync/mutationQueue' const POLL_MS = 3_000 export default function OfflineBanner(): React.ReactElement | null { const [isOnline, setIsOnline] = useState(navigator.onLine) const [pendingCount, setPendingCount] = useState(0) useEffect(() => { const onOnline = () => setIsOnline(true) const onOffline = () => setIsOnline(false) window.addEventListener('online', onOnline) window.addEventListener('offline', onOffline) return () => { window.removeEventListener('online', onOnline) window.removeEventListener('offline', onOffline) } }, []) useEffect(() => { let cancelled = false async function poll() { const n = await mutationQueue.pendingCount() if (!cancelled) setPendingCount(n) } poll() const id = setInterval(poll, POLL_MS) return () => { cancelled = true; clearInterval(id) } }, []) const hidden = isOnline && pendingCount === 0 if (hidden) return null const offline = !isOnline const bg = offline ? '#92400e' : '#1e40af' const text = '#fff' const label = offline ? pendingCount > 0 ? `Offline — ${pendingCount} change${pendingCount !== 1 ? 's' : ''} queued` : 'Offline' : `Syncing ${pendingCount} change${pendingCount !== 1 ? 's' : ''}…` return (