Files
route-commerce/src/components/admin/OfflineBanner.tsx
T

53 lines
1.6 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { getQueuedActions } from "@/lib/offline/queue";
export function OfflineBanner() {
const [online, setOnline] = useState(true);
const [pendingCount, setPendingCount] = useState(0);
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
setOnline(navigator.onLine);
const onOnline = () => setOnline(true);
const onOffline = () => setOnline(false);
window.addEventListener("online", onOnline);
window.addEventListener("offline", onOffline);
const interval = setInterval(async () => {
const actions = await getQueuedActions();
setPendingCount(actions.filter((a) => a.status === "pending" || a.status === "in-flight").length);
}, 1000);
return () => {
window.removeEventListener("online", onOnline);
window.removeEventListener("offline", onOffline);
clearInterval(interval);
};
}, []);
if (!mounted || (online && pendingCount === 0)) return null;
return (
<div
role="status"
aria-live="polite"
className="sticky top-0 z-40 w-full px-4 py-2 text-sm font-semibold"
style={{
backgroundColor: online ? "var(--color-warning-soft)" : "var(--color-danger-soft)",
color: online ? "var(--color-warning)" : "var(--color-danger)",
}}
>
{online ? (
<>Syncing {pendingCount} pending action{pendingCount === 1 ? "" : "s"}</>
) : (
<>You're offline. Changes will sync when you reconnect.</>
)}
</div>
);
}
export default OfflineBanner;