Vitron-Front/app/components/UiProvider.tsx
2026-04-29 01:44:16 +03:30

207 lines
7.5 KiB
TypeScript
Raw 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 React from "react";
import basket from "../assets/icons/cart/basket.svg";
import paper from "../assets/icons/profile/orders/paper.svg";
import location from "../assets/icons/profile/location/location.svg";
import messageBox from "../assets/icons/profile/threads/messageBox.svg";
import { Button } from "./ui/button";
import { RefreshCw } from "lucide-react";
import { Skeleton } from "./ui/skeleton";
import { useNavigate } from "@remix-run/react";
import { safeGoBack } from "~/utils/helpers";
export default function UiProvider({
children,
isLoading,
isError,
isEmpty,
onRetry,
type,
}: {
children: React.ReactNode;
isLoading: boolean;
isError: boolean;
isEmpty: boolean;
onRetry?: () => void;
type:
| "cart"
| "following"
| "location"
| "order"
| "product"
| "threads"
| "seller"
| "wallet"
| "locationEdit";
}) {
return (
<>
{isLoading ? (
<LoadingState type={type} />
) : isError ? (
<ErrorState onRetry={onRetry} />
) : isEmpty ? (
<EmptyState type={type} />
) : (
children
)}
</>
);
}
const LoadingState = ({ type }: { type: string }) => {
if (type === "following")
return (
<div className="flex flex-col gap-4 w-full p-4">
{[...Array(3)].map((_, index) => (
<Skeleton key={index} className="w-full h-24 rounded-lg" />
))}
</div>
);
if (type === "location")
return (
<div className="flex items-center justify-center w-full py-6">
<div className="relative w-12 h-12">
<div className="absolute top-0 left-0 w-full h-full border-4 border-t-primary border-r-transparent border-b-transparent border-l-transparent rounded-full animate-spin"></div>
<div
className="absolute top-1 left-1 w-10 h-10 border-4 border-t-transparent border-r-primary border-b-transparent border-l-transparent rounded-full animate-spin"
style={{ animationDirection: "reverse", animationDuration: "0.8s" }}
></div>
<div
className="absolute top-2 left-2 w-8 h-8 border-4 border-t-transparent border-r-transparent border-b-primary border-l-transparent rounded-full animate-spin"
style={{ animationDuration: "1.2s" }}
></div>
</div>
</div>
);
if (type === "threads")
return (
<div className="flex flex-col gap-4 w-full">
{[...Array(3)].map((_, index) => (
<div
key={index}
className="w-full h-[80px] flex items-center px-4 gap-4"
>
<Skeleton className="w-[50px] h-[50px] rounded-full shrink-0" />
<div className="flex flex-col gap-2 w-full">
<Skeleton className="w-20 h-4 rounded-lg" />
<Skeleton className="w-40 h-4 rounded-lg" />
</div>
</div>
))}
</div>
);
if (type === "wallet")
return (
<div className="flex items-center justify-center w-full py-6">
<div className="relative w-12 h-12">
<div className="absolute top-0 left-0 w-full h-full border-4 border-t-primary border-r-transparent border-b-transparent border-l-transparent rounded-full animate-spin"></div>
<div
className="absolute top-1 left-1 w-10 h-10 border-4 border-t-transparent border-r-primary border-b-transparent border-l-transparent rounded-full animate-spin"
style={{ animationDirection: "reverse", animationDuration: "0.8s" }}
></div>
<div
className="absolute top-2 left-2 w-8 h-8 border-4 border-t-transparent border-r-transparent border-b-primary border-l-transparent rounded-full animate-spin"
style={{ animationDuration: "1.2s" }}
></div>
</div>
</div>
);
return (
<div className="flex justify-center py-12">
<p className="text-B14">در حال بارگذاری...</p>
</div>
);
};
export const ErrorState = ({ onRetry }: { onRetry?: () => void }) => {
return (
<div className="flex justify-center items-center flex-col gap-2 py-12">
<p className="text-B16 font-bold">مشکلی در دریافت اطلاعات پیش آمده است</p>
{onRetry && (
<Button variant="dark" size="lg" onClick={onRetry}>
تلاش مجدد
<RefreshCw className="w-4 h-4" />
</Button>
)}
</div>
);
};
export const EmptyState = ({
type,
mondrianImage,
mondrianTitle,
mondrianDescription,
}: {
type: string;
mondrianImage?: React.ReactNode;
mondrianTitle?: string;
mondrianDescription?: string;
}) => {
const navigate = useNavigate();
return (
<>
{type === "cart" && (
<div className="flex w-full mt-[200px] flex-col gap-2 items-center justify-center px-4">
<img alt="cartBanner" src={basket} className="w-12 h-12" />
<p className="text-R16 text-BLACK2">سبد خرید شما خالی است</p>
<p className="text-R12 text-GRAY">
در سبد خرید، جای انتخابهای خاص شما خالیست.
</p>
</div>
)}
{type === "following" && (
<div className="flex w-full mt-[200px] flex-col gap-2 items-center justify-center">
<img alt="cartBanner" src={paper} className="w-12 h-12" />
<p className="text-R16 text-BLACK2">فروشگاهی دنبال نشده است.</p>
<p className="text-R12 text-GRAY">
تمام فروشگاههایی که دنبال میکنید در این قسمت قابل مشاهده است.
</p>
</div>
)}
{type === "location" && (
<div className="flex flex-col gap-2 items-center mt-[200px]">
<img alt="" src={location} className="w-12 h-12" />
<p className="text-R16 text-BLACK2">هنوز آدرسی ثبت نشده است</p>
<p className="text-R12 text-GRAY">اولین آدرس خود را ثبت کنید</p>
</div>
)}
{type === "locationEdit" && (
<div className="h-[500px] w-full bg-WHITE3 flex items-center justify-center flex-col gap-2 mt-[200px]">
<p className="text-lg font-medium">آدرس مورد نظر یافت نشد</p>
<Button
onClick={() => safeGoBack(navigate)}
size={"sm"}
variant="primary"
>
بازگشت
</Button>
</div>
)}
{type === "threads" && (
<div className="flex w-full mt-[200px] flex-col gap-2 items-center justify-center">
<img alt="messageBox" src={messageBox} className="w-12 h-12" />
<p className="text-R16 text-BLACK2">لیست پیامها خالی است.</p>
<p className="text-R12 text-GRAY">
هنوز پیامی دریافت یا ارسال نکردهاید
</p>
</div>
)}
{type === "orders" && (
<div className="flex w-full mt-[200px] flex-col gap-2 items-center justify-center">
<img alt="cartBanner" src={basket} className="w-12 h-12" />
<p className="text-R16 text-BLACK2">هنوز سفارشی ثبت نشده است</p>
<p className="text-R12 text-GRAY">
از محصولات دیدن کنید و سفارش خود را ثبت کنید.
</p>
</div>
)}
{type === "mondrian" && (
<div className="flex w-full mt-[200px] flex-col gap-2 items-center justify-center">
{mondrianImage}
<p className="text-R16 text-BLACK2">{mondrianTitle}</p>
<p className="text-R12 text-GRAY">{mondrianDescription}</p>
</div>
)}
</>
);
};