- 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>
80 lines
2.9 KiB
TypeScript
80 lines
2.9 KiB
TypeScript
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;
|