+
{/* Store Status */}
@@ -190,6 +191,9 @@ export default function StoreSetting() {
+
+ {/* Push notifications */}
+
{/* Menu Items */}
diff --git a/app/routes/store.$storeId.tsx b/app/routes/store.$storeId.tsx
index bbb3923..e4e2b61 100644
--- a/app/routes/store.$storeId.tsx
+++ b/app/routes/store.$storeId.tsx
@@ -11,6 +11,7 @@ import {
} from "lucide-react";
import { useSellerData } from "~/requestHandler/use-seller-hooks";
import SellerLogo from "~/components/SellerLogo";
+import { useBadgeCounts } from "~/hooks/useBadgeCounts";
/**
* Desktop layout for the seller dashboard. Renders a sticky admin sidebar
@@ -21,15 +22,26 @@ export default function StoreLayout() {
const { storeId } = useParams();
const location = useLocation();
const { data } = useSellerData(storeId);
+ const { data: badges } = useBadgeCounts();
const base = `/store/${storeId}`;
const NAV = [
{ label: "فروشگاه", to: base, icon: LayoutGrid, exact: true },
{ label: "محصولات", to: `${base}/products`, icon: Package },
- { label: "سفارشها", to: `${base}/orders`, icon: ClipboardList },
+ {
+ label: "سفارشها",
+ to: `${base}/orders`,
+ icon: ClipboardList,
+ badge: badges?.seller_orders || 0,
+ },
{ label: "مالی", to: `${base}/financial-dashboard`, icon: Wallet },
{ label: "روش ارسال", to: `${base}/shipping-method`, icon: Truck },
- { label: "پیامها", to: `${base}/threads`, icon: MessageCircle },
+ {
+ label: "پیامها",
+ to: `${base}/threads`,
+ icon: MessageCircle,
+ badge: badges?.chat || 0,
+ },
{ label: "تنظیمات", to: `${base}/setting`, icon: Settings },
];
@@ -65,6 +77,11 @@ export default function StoreLayout() {
>
{n.label}
+ {n.badge && n.badge > 0 ? (
+
+ {n.badge > 99 ? "99+" : n.badge}
+
+ ) : null}
))}
{
self.skipWaiting();
}
});
+
+// ---- Web Push ----
+// Show a notification when a push arrives. Payload is JSON:
+// { title, body, url, tag, notification_id }
+self.addEventListener("push", (event) => {
+ let data = {};
+ try {
+ data = event.data ? event.data.json() : {};
+ } catch (e) {
+ data = { title: "ویترون", body: event.data ? event.data.text() : "" };
+ }
+
+ const title = data.title || "ویترون";
+ const options = {
+ body: data.body || "",
+ icon: "/icon-192x192.png",
+ badge: "/icon-96x96.png",
+ dir: "rtl",
+ lang: "fa",
+ tag: data.tag || "vitrown",
+ renotify: true,
+ data: { url: data.url || "/", notificationId: data.notification_id },
+ };
+
+ event.waitUntil(self.registration.showNotification(title, options));
+});
+
+// Focus an existing tab (or open a new one) on the target URL when clicked.
+self.addEventListener("notificationclick", (event) => {
+ event.notification.close();
+ const targetUrl = (event.notification.data && event.notification.data.url) || "/";
+
+ event.waitUntil(
+ clients
+ .matchAll({ type: "window", includeUncontrolled: true })
+ .then((clientList) => {
+ for (const client of clientList) {
+ // If a tab is already open, navigate it and focus.
+ if ("focus" in client) {
+ client.focus();
+ if ("navigate" in client) {
+ client.navigate(targetUrl).catch(() => {});
+ }
+ return;
+ }
+ }
+ if (clients.openWindow) {
+ return clients.openWindow(targetUrl);
+ }
+ })
+ );
+});