Files
meezi/web/dashboard/src/components/layout/sync-status-indicator.tsx
T

103 lines
3.1 KiB
TypeScript
Raw Normal View History

"use client";
import { WifiOff, CloudUpload, RefreshCw } from "lucide-react";
import { cn } from "@/lib/utils";
import { useSyncQueueStore } from "@/lib/stores/sync-queue.store";
import { useLocale } from "next-intl";
import {
getAllQueueItems,
getQueueCount,
removeQueueItem,
markQueueItemFailed,
} from "@/lib/offline/offline-db";
import { apiPost } from "@/lib/api/client";
/** Manual retry — fires one sync pass immediately (used as onClick). */
async function runManualSync(
setSyncing: (v: boolean) => void,
setQueueCount: (n: number) => void
) {
if (!navigator.onLine) return;
setSyncing(true);
try {
const items = await getAllQueueItems();
for (const item of items) {
try {
if (item.type === "create_order") {
const { cafeId, body } = item.payload as { cafeId: string; body: unknown };
await apiPost(`/api/cafes/${cafeId}/orders`, body as Record<string, unknown>);
} else if (item.type === "add_items") {
const { cafeId, orderId, body } = item.payload as {
cafeId: string;
orderId: string;
body: unknown;
};
await apiPost(
`/api/cafes/${cafeId}/orders/${orderId}/items`,
body as Record<string, unknown>
);
}
await removeQueueItem(item.id);
} catch {
await markQueueItemFailed(item.id);
}
}
} finally {
setSyncing(false);
setQueueCount(await getQueueCount());
}
}
export function SyncStatusIndicator() {
const { queueCount, isSyncing, isOnline, setSyncing, setQueueCount } =
useSyncQueueStore();
const locale = useLocale();
const isFa = locale !== "en";
const show = !isOnline || queueCount > 0 || isSyncing;
if (!show) return null;
const label = isFa
? !isOnline
? "آفلاین"
: isSyncing
? "همگام‌سازی..."
: `${queueCount} مورد در صف`
: !isOnline
? "Offline"
: isSyncing
? "Syncing..."
: `${queueCount} pending`;
return (
<button
type="button"
onClick={() => void runManualSync(setSyncing, setQueueCount)}
disabled={isSyncing || !isOnline}
title={
isFa
? "برای همگام‌سازی دستی کلیک کنید"
: "Click to retry sync"
}
className={cn(
"flex cursor-pointer items-center gap-1.5 rounded-full px-2.5 py-1 text-[11px] font-medium transition-colors",
"disabled:cursor-not-allowed",
!isOnline
? "bg-amber-100 text-amber-800 dark:bg-amber-900/30 dark:text-amber-300"
: isSyncing
? "bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-300"
: "bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-300"
)}
>
{!isOnline ? (
<WifiOff className="h-3 w-3 shrink-0" aria-hidden />
) : isSyncing ? (
<RefreshCw className="h-3 w-3 shrink-0 animate-spin" aria-hidden />
) : (
<CloudUpload className="h-3 w-3 shrink-0" aria-hidden />
)}
<span>{label}</span>
</button>
);
}