Vitron-Front/app/routes/sellers.tsx
Arda Samadi d694040bf7 fix(pwa): apply top safe-area inset to page headers (iOS notch)
MyLayout only pads the bottom inset globally; each top bar must add its own
top inset. Several inline page headers omitted it, so their content slid
under the notch in the iOS standalone PWA (home was fine — its bar has it).

Wrap the back-button headers the same way ProfilePagesHeader already does
(outer sticky element carries pt-[env(safe-area-inset-top)]; inner relative
bar holds the content) so the absolute back arrow stays out of the notch.
Fixed: /profile hub, /discounts, /collections, /collection/:id, /sellers,
seller store index, and financial transactions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:21:23 +03:30

146 lines
5.5 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 { MetaFunction } from "@remix-run/node";
import { Link, useNavigate } from "@remix-run/react";
import { safeGoBack } from "~/utils/helpers";
import { useGetAllSellerTiles } from "~/requestHandler/use-home-hooks";
import { Skeleton } from "~/components/ui/skeleton";
import { Store, RefreshCw, ArrowRight } from "lucide-react";
export default function Sellers() {
const navigate = useNavigate();
const { data, status, refetch } = useGetAllSellerTiles();
const allSellers = data || [];
// Loading skeleton
const SellersSkeleton = () => (
<div className="grid grid-cols-2 lg:grid-cols-5 gap-3 lg:gap-4 px-4 lg:px-0">
{Array.from({ length: 8 }).map((_, index) => (
<Skeleton key={index} className="aspect-square rounded-lg" />
))}
</div>
);
// Empty state
const EmptyState = () => (
<div className="flex flex-col items-center justify-center py-20 px-4">
<div className="w-20 h-20 rounded-full bg-WHITE2 flex items-center justify-center mb-4">
<Store size={40} className="text-GRAY" />
</div>
<h3 className="text-B18 font-bold text-black mb-2">فروشگاهی یافت نشد</h3>
<p className="text-R14 text-GRAY text-center max-w-md">
در حال حاضر فروشگاهی موجود نیست.
</p>
</div>
);
// Error state
const ErrorState = () => (
<div className="flex flex-col items-center justify-center py-20 px-4">
<div className="w-20 h-20 rounded-full bg-red-50 flex items-center justify-center mb-4">
<Store size={40} className="text-red-500" />
</div>
<h3 className="text-B18 font-bold text-black mb-2">
خطا در بارگذاری فروشگاهها
</h3>
<p className="text-R14 text-GRAY text-center max-w-md mb-6">
متاسفانه در بارگذاری فروشگاهها مشکلی پیش آمد.
</p>
<button
onClick={() => refetch()}
className="flex items-center gap-2 bg-black text-white px-6 py-3 rounded-full hover:bg-gray-800 transition-all"
>
<RefreshCw size={16} />
<span className="text-R14 font-bold">تلاش مجدد</span>
</button>
</div>
);
const handleBack = () => {
safeGoBack(navigate);
};
return (
<div className="flex flex-col bg-WHITE pb-20">
{/* Header (mobile) */}
<div className="lg:hidden sticky top-0 z-30 bg-WHITE pt-[env(safe-area-inset-top)]">
<div className="relative flex flex-row h-[65px] w-full items-center justify-center border-b border-inner-border">
<ArrowRight
onClick={handleBack}
className="absolute top-5 right-5 cursor-pointer"
/>
<p className="text-B16 font-bold">فروشگاهها</p>
</div>
</div>
{/* Content */}
<div className="flex flex-col py-6 lg:max-w-[1320px] lg:mx-auto lg:w-full lg:px-8">
<h1 className="hidden lg:block text-[28px] font-extrabold mb-6">
فروشگاهها
</h1>
{status === "pending" ? (
<SellersSkeleton />
) : status === "error" ? (
<ErrorState />
) : allSellers.length === 0 ? (
<EmptyState />
) : (
<div className="grid grid-cols-2 lg:grid-cols-5 gap-3 lg:gap-4 px-4 lg:px-0">
{allSellers.map((seller, index) => (
<Link
key={seller.id || index}
to={`/seller/${seller.sellerUsername || seller.sellerId}`}
className="relative aspect-square overflow-hidden rounded-lg group"
>
{/* Background Image */}
{seller.imageUrl ? (
<img
src={seller.imageUrl}
alt={seller.sellerName || "فروشگاه"}
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500"
/>
) : (
<div className="w-full h-full bg-gradient-to-br from-gray-200 to-gray-300 flex items-center justify-center">
<Store size={48} className="text-gray-400" />
</div>
)}
</Link>
))}
</div>
)}
</div>
</div>
);
}
export const meta: MetaFunction = () => {
const title = "فروشگاه‌های ویترون | برترین فروشندگان";
const description =
"مشاهده برترین فروشگاه‌های ویترون. خرید امن از فروشندگان معتبر با بالاترین کیفیت.";
const imageUrl =
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
return [
{ title },
{ name: "description", content: description },
{
name: "keywords",
content: "فروشگاه، فروشندگان، ویترون، خرید آنلاین، فروشگاه اینترنتی",
},
{ name: "robots", content: "index, follow" },
// Open Graph
{ property: "og:type", content: "website" },
{ property: "og:title", content: title },
{ property: "og:description", content: description },
{ property: "og:image", content: imageUrl },
{ property: "og:site_name", content: "ویترون" },
{ property: "og:locale", content: "fa_IR" },
// Twitter Card
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
{ name: "twitter:image", content: imageUrl },
];
};