Vitron-Front/app/components/home/HomePageTopBar.tsx
fazli 0b18438a97 fix(notif+auth): camelCase badge fields, seller push prompt, only 401 signs out
- app/components/MyLayout.tsx / app/components/home/HomePageTopBar.tsx /
  app/routes/profile._index.tsx: the BadgeCounts type was declared with
  snake_case fields (order_updates, seller_orders) while the API's global
  camelCase renderer sends orderUpdates / sellerOrders on the wire — every
  read went to undefined, so the "buyer activity" dot on the profile icon
  and the order-badge on the store dashboard were silently stuck at 0.
  Fixed by renaming the interface to match the wire and adding sellerChat
  (per-store roll-up) so the store dashboard sidebar reads
  badges.sellerChat[storeId] instead of the buyer-side chat count.
- app/hooks/useBadgeCounts.ts: interface + EMPTY constant updated to
  camelCase + new sellerChat map.
- app/utils/api-error-handler.ts: 403 no longer force-redirects to /logout.
  It used to force-log-out on ANY 403/401, so an approved-but-unbadged
  owner (Ehsan / Noelabel) whose store dashboard fired the Instagram-tab
  query got a 403 → forced logout → the store page vanished mid-load
  and the seller landed on the home screen convinced they'd been kicked
  off their own store. Only 401 signs the session out now.
- app/components/store/StorePushPromptCard.tsx: new dashboard-level card
  that nudges the store owner/staff to enable browser push. Unlike the
  global buyer banner it's not dismissible-forever; "بعداً یادآوری کن"
  hides for 24h and it comes back until the user actually subscribes.
  Handles the permission="denied" path with an in-browser-settings hint.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-18 13:18:08 +00:00

80 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { memo, useCallback } from "react";
import { Link } from "@remix-run/react";
import { Bell, Search, ShoppingBag } from "lucide-react";
import logo from "../../assets/logo/SVG-07.svg";
import { useCartCount } from "~/requestHandler/use-cart-hooks";
import { useBadgeCounts } from "~/hooks/useBadgeCounts";
import { useRootData } from "~/hooks/use-root-data";
const HomePageTopBar = memo(() => {
const { user } = useRootData();
const isLoggedIn = !!user?.access_token;
const cartCount = useCartCount();
const { data: badgeCounts } = useBadgeCounts();
const accountUnread =
(badgeCounts?.chat || 0) + (badgeCounts?.orderUpdates || 0);
const scrollToTop = useCallback(() => {
window.scrollTo({ top: 0, behavior: "smooth" });
}, []);
return (
<div className="bg-WHITE sticky top-0 z-30 pt-[env(safe-area-inset-top)]">
<div className="px-4 pt-2.5 pb-3 flex flex-col gap-3">
{/* Brand row */}
<div className="flex items-center gap-2.5">
<button
onClick={scrollToTop}
className="flex items-center gap-2"
aria-label="ویترون"
>
<img src={logo} className="w-7 h-8 object-contain" alt="" />
<b className="text-[21px] font-extrabold text-BLACK tracking-tight">
ویترون
</b>
</button>
<span className="flex-1" />
<Link
to="/profile"
prefetch="intent"
aria-label="اعلان‌ها"
className="relative w-10 h-10 rounded-xl bg-WHITE2 grid place-items-center text-BLACK"
>
{isLoggedIn && accountUnread > 0 ? (
<span className="absolute top-2 left-2.5 w-2 h-2 rounded-full bg-RED border-[1.5px] border-WHITE2" />
) : null}
<Bell size={21} />
</Link>
<Link
to="/cart"
prefetch="intent"
aria-label="سبد خرید"
className="relative w-10 h-10 rounded-xl bg-WHITE2 grid place-items-center text-BLACK"
>
{isLoggedIn && cartCount > 0 ? (
<span className="absolute top-1 left-1 min-w-[16px] h-4 px-1 rounded-full bg-RED text-white text-[10px] font-bold grid place-items-center border-[1.5px] border-WHITE2">
{cartCount > 99 ? "99+" : cartCount}
</span>
) : null}
<ShoppingBag size={21} />
</Link>
</div>
{/* Search-first */}
<Link
to="/explore"
prefetch="intent"
className="flex items-center gap-2.5 h-[46px] px-3.5 bg-WHITE3 rounded-2xl text-GRAY"
>
<Search size={19} className="shrink-0" />
<span className="text-R14">جستجوی محصول، برند یا فروشگاه</span>
</Link>
</div>
</div>
);
});
HomePageTopBar.displayName = "HomePageTopBar";
export default HomePageTopBar;