131ecdbbe6
Complete merchant dashboard upgrade:
Next.js 16 compatibility:
- Fix params/searchParams typed as Promise<{}> throughout App Router
- Replace middleware.ts with proxy.ts (Next.js 16 convention)
- Remove unused @ts-expect-error directives caught by stricter TS
- Cast dynamic next-intl t() keys to fix TranslateArgs type errors
Offline POS:
- IndexedDB queue (meezi_pos_offline) for orders created while offline
- Zustand sync store tracking queueCount, isSyncing, isOnline
- useOfflineSync hook: auto-syncs on reconnect/visibility-change
- SyncStatusIndicator chip in topbar (amber=offline, blue=syncing)
- submitOrderToApi falls back to local order on network failure
- Local orders skip payment flow; sync on reconnect
PWA (installable):
- @ducanh2912/next-pwa with Workbox runtime caching rules
- Web App Manifest (manifest.ts) — RTL/Farsi, theme #0F6E56
- PWA icons: 192px, 512px, maskable 512px
- next.config.ts replaces next.config.mjs
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
117 lines
3.5 KiB
TypeScript
117 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import type { ReactNode } from "react";
|
|
import { useLocale } from "next-intl";
|
|
import { Toaster } from "sonner";
|
|
import {
|
|
AlertCircle,
|
|
CheckCircle2,
|
|
Info,
|
|
Loader2,
|
|
TriangleAlert,
|
|
} from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
function iconWrap(className: string, icon: ReactNode) {
|
|
return (
|
|
<span
|
|
className={cn(
|
|
"flex h-9 w-9 shrink-0 items-center justify-center rounded-full",
|
|
className
|
|
)}
|
|
>
|
|
{icon}
|
|
</span>
|
|
);
|
|
}
|
|
|
|
export function MeeziToaster() {
|
|
const locale = useLocale();
|
|
const isRtl = locale !== "en";
|
|
const isEn = locale === "en";
|
|
|
|
const fontClass = isEn
|
|
? "font-[family-name:var(--font-inter)]"
|
|
: "font-[family-name:var(--font-vazirmatn)]";
|
|
|
|
const toastBase = cn(
|
|
"group relative flex w-[min(calc(100vw-2rem),400px)] items-start gap-3 overflow-hidden",
|
|
"rounded-xl border border-border/60 bg-card/95 py-3.5 ps-3.5 pe-10",
|
|
"shadow-[0_10px_40px_-8px_rgba(15,23,42,0.16)] backdrop-blur-md",
|
|
"transition-[transform,opacity] duration-200",
|
|
fontClass
|
|
);
|
|
|
|
const titleClass = "text-[13px] font-semibold leading-snug tracking-tight text-foreground";
|
|
const descriptionClass = "text-xs leading-relaxed text-muted-foreground";
|
|
|
|
return (
|
|
<Toaster
|
|
dir={isRtl ? "rtl" : "ltr"}
|
|
position={isRtl ? "top-left" : "top-right"}
|
|
closeButton
|
|
richColors={false}
|
|
expand
|
|
gap={12}
|
|
offset={20}
|
|
visibleToasts={4}
|
|
className={fontClass}
|
|
toastOptions={{
|
|
unstyled: true,
|
|
style: {
|
|
fontFamily: isEn
|
|
? "var(--font-inter), system-ui, sans-serif"
|
|
: "var(--font-vazirmatn), system-ui, sans-serif",
|
|
},
|
|
classNames: {
|
|
toast: toastBase,
|
|
title: titleClass,
|
|
description: descriptionClass,
|
|
content: "flex flex-1 flex-col gap-0.5 min-w-0",
|
|
closeButton: cn(
|
|
"absolute end-2.5 top-2.5 flex h-7 w-7 items-center justify-center rounded-lg",
|
|
"border-0 bg-muted/50 text-muted-foreground opacity-80",
|
|
"transition hover:bg-muted hover:opacity-100",
|
|
fontClass
|
|
),
|
|
actionButton: cn(
|
|
"rounded-lg bg-primary px-3 py-1.5 text-xs font-medium text-primary-foreground",
|
|
fontClass
|
|
),
|
|
cancelButton: cn(
|
|
"rounded-lg border border-border/80 bg-background px-3 py-1.5 text-xs font-medium",
|
|
fontClass
|
|
),
|
|
success: "border-s-[3px] border-s-[#0F6E56]",
|
|
error: "border-s-[3px] border-s-[#A32D2D]",
|
|
warning: "border-s-[3px] border-s-[#BA7517]",
|
|
info: "border-s-[3px] border-s-[#0C447C]",
|
|
loading: "border-s-[3px] border-s-primary/40",
|
|
},
|
|
}}
|
|
icons={{
|
|
success: iconWrap(
|
|
"bg-[#E1F5EE]",
|
|
<CheckCircle2 className="h-4 w-4 text-[#0F6E56]" strokeWidth={2.25} />
|
|
),
|
|
error: iconWrap(
|
|
"bg-red-50",
|
|
<AlertCircle className="h-4 w-4 text-[#A32D2D]" strokeWidth={2.25} />
|
|
),
|
|
warning: iconWrap(
|
|
"bg-amber-50",
|
|
<TriangleAlert className="h-4 w-4 text-[#BA7517]" strokeWidth={2.25} />
|
|
),
|
|
info: iconWrap(
|
|
"bg-[#0C447C]/10",
|
|
<Info className="h-4 w-4 text-[#0C447C]" strokeWidth={2.25} />
|
|
),
|
|
loading: iconWrap(
|
|
"bg-primary/10",
|
|
<Loader2 className="h-4 w-4 animate-spin text-primary" strokeWidth={2.25} />
|
|
),
|
|
}}
|
|
/>
|
|
);
|
|
}
|