classify web
This commit is contained in:
141
components/PagesComponent/Seller/Seller.jsx
Normal file
141
components/PagesComponent/Seller/Seller.jsx
Normal file
@@ -0,0 +1,141 @@
|
||||
"use client";
|
||||
import { useEffect, useState } from "react";
|
||||
import SellerLsitings from "./SellerLsitings";
|
||||
import SellerDetailCard from "./SellerDetailCard";
|
||||
import { getSellerApi } from "@/utils/api";
|
||||
import { t } from "@/utils";
|
||||
import SellerRating from "./SellerRating";
|
||||
import SellerSkeleton from "./SellerSkeleton";
|
||||
import NoData from "@/components/EmptyStates/NoData";
|
||||
import Layout from "@/components/Layout/Layout";
|
||||
import OpenInAppDrawer from "@/components/Common/OpenInAppDrawer";
|
||||
import BreadCrumb from "@/components/BreadCrumb/BreadCrumb";
|
||||
import { useSelector } from "react-redux";
|
||||
import { CurrentLanguageData } from "@/redux/reducer/languageSlice";
|
||||
|
||||
const Seller = ({ id, searchParams }) => {
|
||||
|
||||
const CurrentLanguage = useSelector(CurrentLanguageData);
|
||||
const [steps, setSteps] = useState(1);
|
||||
const [IsNoUserFound, setIsNoUserFound] = useState(false);
|
||||
|
||||
const [seller, setSeller] = useState(null);
|
||||
const [ratings, setRatings] = useState(null);
|
||||
const [isSellerDataLoading, setIsSellerDataLoading] = useState(false);
|
||||
|
||||
const [isLoadMoreReview, setIsLoadMoreReview] = useState(false);
|
||||
const [reviewHasMore, setReviewHasMore] = useState(false);
|
||||
const [reviewCurrentPage, setReviewCurrentPage] = useState(1);
|
||||
|
||||
const [isOpenInApp, setIsOpenInApp] = useState(false);
|
||||
const isShare = searchParams?.share == "true" ? true : false;
|
||||
|
||||
useEffect(() => {
|
||||
if (window.innerWidth <= 768 && isShare) {
|
||||
setIsOpenInApp(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getSeller(reviewCurrentPage);
|
||||
}, []);
|
||||
|
||||
const getSeller = async (page) => {
|
||||
if (page === 1) {
|
||||
setIsSellerDataLoading(true);
|
||||
}
|
||||
try {
|
||||
const res = await getSellerApi.getSeller({ id: Number(id), page });
|
||||
if (res?.data.error && res?.data?.code === 103) {
|
||||
setIsNoUserFound(true);
|
||||
} else {
|
||||
const sellerData = res?.data?.data?.ratings;
|
||||
if (page === 1) {
|
||||
setRatings(sellerData);
|
||||
} else {
|
||||
setRatings({
|
||||
...ratings,
|
||||
data: [...ratings?.data, ...sellerData?.data],
|
||||
});
|
||||
}
|
||||
setSeller(res?.data?.data?.seller);
|
||||
setReviewCurrentPage(res?.data?.data?.ratings?.current_page);
|
||||
if (
|
||||
res?.data?.data?.ratings?.current_page <
|
||||
res?.data?.data?.ratings?.last_page
|
||||
) {
|
||||
setReviewHasMore(true);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsSellerDataLoading(false);
|
||||
setIsLoadMoreReview(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSteps = (step) => {
|
||||
setSteps(step);
|
||||
};
|
||||
|
||||
if (IsNoUserFound) {
|
||||
return <NoData name={t("noSellerFound")} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
{isSellerDataLoading ? (
|
||||
<SellerSkeleton steps={steps} />
|
||||
) : (
|
||||
<>
|
||||
<BreadCrumb title2={seller?.name} />
|
||||
<div className="container mx-auto mt-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-4">
|
||||
<div className="col-span-12 lg:col-span-4">
|
||||
<SellerDetailCard seller={seller} ratings={ratings} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-8 col-span-12 lg:col-span-8">
|
||||
<div className="p-4 flex items-center gap-4 bg-muted border rounded-md w-full">
|
||||
<button
|
||||
onClick={() => handleSteps(1)}
|
||||
className={`py-2 px-4 rounded-md ${
|
||||
steps === 1 ? "bg-primary text-white" : ""
|
||||
}`}
|
||||
>
|
||||
{t("liveAds")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSteps(2)}
|
||||
className={`py-2 px-4 rounded-md ${
|
||||
steps === 2 ? "bg-primary text-white" : ""
|
||||
}`}
|
||||
>
|
||||
{t("reviews")}
|
||||
</button>
|
||||
</div>
|
||||
{steps === 1 && <SellerLsitings id={id} />}
|
||||
{steps === 2 && (
|
||||
<SellerRating
|
||||
ratingsData={ratings}
|
||||
seller={seller}
|
||||
isLoadMoreReview={isLoadMoreReview}
|
||||
reviewHasMore={reviewHasMore}
|
||||
reviewCurrentPage={reviewCurrentPage}
|
||||
getSeller={getSeller}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<OpenInAppDrawer
|
||||
isOpenInApp={isOpenInApp}
|
||||
setIsOpenInApp={setIsOpenInApp}
|
||||
/>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default Seller;
|
||||
114
components/PagesComponent/Seller/SellerDetailCard.jsx
Normal file
114
components/PagesComponent/Seller/SellerDetailCard.jsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { MdOutlineMailOutline, MdVerifiedUser } from "react-icons/md";
|
||||
import { IoMdStar } from "react-icons/io";
|
||||
import { FiPhoneCall } from "react-icons/fi";
|
||||
import { extractYear, t } from "@/utils";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useSelector } from "react-redux";
|
||||
import { getCompanyName } from "@/redux/reducer/settingSlice";
|
||||
import ShareDropdown from "@/components/Common/ShareDropdown";
|
||||
import CustomLink from "@/components/Common/CustomLink";
|
||||
import CustomImage from "@/components/Common/CustomImage";
|
||||
import Link from "next/link";
|
||||
|
||||
const SellerDetailCard = ({ seller, ratings }) => {
|
||||
const pathname = usePathname();
|
||||
const memberSinceYear = seller?.created_at
|
||||
? extractYear(seller.created_at)
|
||||
: "";
|
||||
const currentUrl = `${process.env.NEXT_PUBLIC_WEB_URL}${pathname}`;
|
||||
const CompanyName = useSelector(getCompanyName);
|
||||
const FbTitle = seller?.name + " | " + CompanyName;
|
||||
|
||||
|
||||
return (
|
||||
<div className="rounded-lg border overflow-hidden">
|
||||
<div className="flex justify-between items-center p-4 bg-muted">
|
||||
<h1 className="text-lg font-bold">{t("seller_info")}</h1>
|
||||
<ShareDropdown
|
||||
url={currentUrl}
|
||||
title={FbTitle}
|
||||
headline={FbTitle}
|
||||
companyName={CompanyName}
|
||||
className="rounded-md p-2 border bg-white"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{(seller?.is_verified === 1 || memberSinceYear) && (
|
||||
<div className="border-t p-4">
|
||||
<div className="flex items-center">
|
||||
{seller?.is_verified === 1 && (
|
||||
<Badge className="p-1 bg-[#FA6E53] flex items-center gap-1 rounded-md text-white text-sm">
|
||||
<MdVerifiedUser size={22} />
|
||||
{t("verified")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{memberSinceYear && (
|
||||
<div className="ltr:ml-auto rtl:mr-auto text-sm text-muted-foreground">
|
||||
{t("memberSince")}: {memberSinceYear}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="border-t flex flex-col justify-center items-center p-4 gap-4">
|
||||
<CustomImage
|
||||
src={seller?.profile}
|
||||
alt="Seller Image"
|
||||
width={120}
|
||||
height={120}
|
||||
className="aspect-square rounded-xl object-cover"
|
||||
/>
|
||||
|
||||
<div className="text-center w-full">
|
||||
<h3 className="text-xl font-bold">{seller?.name}</h3>
|
||||
<div className="flex items-center justify-center gap-1 text-sm mt-1">
|
||||
<IoMdStar />
|
||||
<span>
|
||||
{Number(seller?.average_rating).toFixed(2)} |{" "}
|
||||
{ratings?.data?.length} {t("ratings")}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{seller?.show_personal_details === 1 &&
|
||||
(seller?.email || seller?.mobile) && (
|
||||
<div className="border-t p-4 flex flex-col gap-4">
|
||||
{seller?.email && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-3 bg-muted rounded-md border">
|
||||
<MdOutlineMailOutline className="size-4" />
|
||||
</div>
|
||||
<CustomLink
|
||||
href={`mailto:${seller?.email}`}
|
||||
className="break-all"
|
||||
>
|
||||
{seller?.email}
|
||||
</CustomLink>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{seller?.mobile && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="p-3 bg-muted rounded-md border">
|
||||
<FiPhoneCall className="size-4" />
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={`tel:${seller?.mobile}`}
|
||||
className="break-all"
|
||||
>
|
||||
{seller?.mobile}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SellerDetailCard;
|
||||
200
components/PagesComponent/Seller/SellerLsitings.jsx
Normal file
200
components/PagesComponent/Seller/SellerLsitings.jsx
Normal file
@@ -0,0 +1,200 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { TbTransferVertical } from "react-icons/tb";
|
||||
import { IoGrid } from "react-icons/io5";
|
||||
import { allItemApi } from "@/utils/api";
|
||||
import ProductHorizontalCardSkeleton from "@/components/Common/ProductHorizontalCardSkeleton";
|
||||
import ProductCardSkeleton from "@/components/Common/ProductCardSkeleton";
|
||||
import ProductCard from "@/components/Common/ProductCard";
|
||||
import NoData from "@/components/EmptyStates/NoData";
|
||||
import ProductHorizontalCard from "@/components/Common/ProductHorizontalCard";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import { MdViewStream } from "react-icons/md";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { CurrentLanguageData } from "@/redux/reducer/languageSlice";
|
||||
import { useSelector } from "react-redux";
|
||||
import { t } from "@/utils";
|
||||
|
||||
const SellerLsitings = ({ id }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const view = searchParams.get("view") || "grid";
|
||||
const sortBy = searchParams.get("sort") || "new-to-old";
|
||||
const CurrentLanguage = useSelector(CurrentLanguageData);
|
||||
const [isSellerItemsLoading, setIsSellerItemsLoading] = useState(false);
|
||||
const [sellerItems, setSellerItems] = useState([]);
|
||||
const [isSellerItemLoadMore, setIsSellerItemLoadMore] = useState(false);
|
||||
const [CurrentPage, setCurrentPage] = useState(1);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
getSellerItems(1);
|
||||
}, [sortBy, CurrentLanguage.id]);
|
||||
|
||||
const getSellerItems = async (page) => {
|
||||
try {
|
||||
if (page === 1) {
|
||||
setIsSellerItemsLoading(true);
|
||||
}
|
||||
const res = await allItemApi.getItems({
|
||||
user_id: id,
|
||||
sort_by: sortBy,
|
||||
page,
|
||||
});
|
||||
|
||||
if (page > 1) {
|
||||
// Append new data to existing sellerItems
|
||||
setSellerItems((prevItems) => [...prevItems, ...res?.data?.data?.data]);
|
||||
} else {
|
||||
// Set new data if CurrentPage is 1 or initial load
|
||||
setSellerItems(res?.data?.data?.data);
|
||||
}
|
||||
|
||||
setCurrentPage(res?.data?.data?.current_page);
|
||||
if (res?.data?.data.current_page === res?.data?.data.last_page) {
|
||||
setHasMore(false); // Check if there's more data
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setIsSellerItemsLoading(false);
|
||||
setIsSellerItemLoadMore(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLike = (id) => {
|
||||
const updatedItems = sellerItems.map((item) => {
|
||||
if (item.id === id) {
|
||||
return { ...item, is_liked: !item.is_liked };
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setSellerItems(updatedItems);
|
||||
};
|
||||
|
||||
const handleProdLoadMore = () => {
|
||||
setIsSellerItemLoadMore(true);
|
||||
getSellerItems(CurrentPage + 1);
|
||||
};
|
||||
|
||||
const toggleView = (newView) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("view", newView);
|
||||
window.history.pushState(null, "", `?${params.toString()}`);
|
||||
};
|
||||
|
||||
const handleSortBy = (value) => {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
params.set("sort", value);
|
||||
window.history.pushState(null, "", `?${params.toString()}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center gap-2">
|
||||
<div className="flex items-center gap-1">
|
||||
<TbTransferVertical />
|
||||
<span className="whitespace-nowrap">{t("sortBy")}</span>
|
||||
</div>
|
||||
<Select value={sortBy} onValueChange={handleSortBy}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("sortBy")} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectGroup>
|
||||
<SelectItem value="new-to-old">
|
||||
{t("newestToOldest")}
|
||||
</SelectItem>
|
||||
<SelectItem value="old-to-new">
|
||||
{t("oldestToNewest")}
|
||||
</SelectItem>
|
||||
<SelectItem value="price-high-to-low">
|
||||
{t("priceHighToLow")}
|
||||
</SelectItem>
|
||||
<SelectItem value="price-low-to-high">
|
||||
{t("priceLowToHigh")}
|
||||
</SelectItem>
|
||||
<SelectItem value="popular_items">{t("popular")}</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={() => toggleView("grid")}
|
||||
className={`flex items-center justify-center size-8 sm:size-10 text-muted-foreground transition-colors duration-300 cursor-pointer gap-2 rounded-full ${
|
||||
view === "grid"
|
||||
? "bg-primary text-white"
|
||||
: "hover:bg-black/15 hover:text-black"
|
||||
}`}
|
||||
>
|
||||
<IoGrid className="size-5 sm:size-6" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => toggleView("list")}
|
||||
className={`flex items-center justify-center size-8 sm:size-10 text-muted-foreground hover:text-black transition-colors duration-300 cursor-pointer gap-2 rounded-full ${
|
||||
view === "list"
|
||||
? "bg-primary text-white"
|
||||
: "hover:text-black hover:bg-black/15"
|
||||
}`}
|
||||
>
|
||||
<MdViewStream className="size-5 sm:size-6" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
{isSellerItemsLoading ? (
|
||||
Array.from({ length: 12 }).map((_, index) =>
|
||||
view === "list" ? (
|
||||
<div className="col-span-12" key={index}>
|
||||
<ProductHorizontalCardSkeleton />
|
||||
</div>
|
||||
) : (
|
||||
<div key={index} className="col-span-6 lg:col-span-4">
|
||||
<ProductCardSkeleton />
|
||||
</div>
|
||||
)
|
||||
)
|
||||
) : sellerItems && sellerItems.length > 0 ? (
|
||||
sellerItems?.map((item, index) =>
|
||||
view === "list" ? (
|
||||
<div className="col-span-12" key={index}>
|
||||
<ProductHorizontalCard item={item} handleLike={handleLike} />
|
||||
</div>
|
||||
) : (
|
||||
<div className="col-span-6 lg:col-span-4" key={index}>
|
||||
<ProductCard item={item} handleLike={handleLike} />
|
||||
</div>
|
||||
)
|
||||
)
|
||||
) : (
|
||||
<div className="col-span-12">
|
||||
<NoData name={t("ads")} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{sellerItems && sellerItems.length > 0 && hasMore && (
|
||||
<div className="text-center mt-6">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-sm sm:text-base text-primary w-[256px]"
|
||||
disabled={isSellerItemsLoading || isSellerItemLoadMore}
|
||||
onClick={handleProdLoadMore}
|
||||
>
|
||||
{isSellerItemLoadMore ? t("loading") : t("loadMore")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SellerLsitings;
|
||||
39
components/PagesComponent/Seller/SellerRating.jsx
Normal file
39
components/PagesComponent/Seller/SellerRating.jsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { t } from '@/utils';
|
||||
import RatingsSummary from '../Reviews/RatingsSummary';
|
||||
import SellerReviewCard from "@/components/PagesComponent/Reviews/SellerReviewCard";
|
||||
import { Button } from '@/components/ui/button';
|
||||
import NoData from '@/components/EmptyStates/NoData';
|
||||
|
||||
|
||||
const SellerRating = ({ ratingsData, seller, isLoadMoreReview, reviewHasMore, reviewCurrentPage, getSeller }) => {
|
||||
|
||||
return (
|
||||
ratingsData?.data?.length > 0 ?
|
||||
<>
|
||||
<RatingsSummary averageRating={seller?.average_rating} reviews={ratingsData?.data} />
|
||||
<div className='flex flex-col gap-4 bg-muted p-4 rounded-lg'>
|
||||
{ratingsData?.data?.map((rating) => (
|
||||
<SellerReviewCard key={rating.id} rating={rating} />
|
||||
))}
|
||||
{
|
||||
ratingsData?.data?.length > 0 && reviewHasMore && (
|
||||
<div className="text-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-sm sm:text-base text-primary w-[256px]"
|
||||
disabled={isLoadMoreReview}
|
||||
onClick={() => getSeller(reviewCurrentPage + 1)}
|
||||
>
|
||||
{isLoadMoreReview ? t("loading") : t("loadMore")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
:
|
||||
<NoData name={t('reviews')} />
|
||||
);
|
||||
};
|
||||
|
||||
export default SellerRating;
|
||||
75
components/PagesComponent/Seller/SellerSkeleton.jsx
Normal file
75
components/PagesComponent/Seller/SellerSkeleton.jsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
|
||||
const SellerSkeleton = ({ steps }) => {
|
||||
return (
|
||||
<div className="container mx-auto mt-6">
|
||||
<div className="grid grid-cols-1 md:grid-cols-12 gap-4">
|
||||
{/* Left Side - Seller Detail Card Skeleton */}
|
||||
<div className="col-span-12 md:col-span-4">
|
||||
<div className="bg-white rounded-lg shadow-sm border p-6">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<Skeleton className="h-32 w-32 rounded-full" />
|
||||
<Skeleton className="h-6 w-3/4" />
|
||||
<Skeleton className="h-4 w-1/2" />
|
||||
<div className="flex gap-2">
|
||||
{[1, 2, 3, 4, 5].map(i => (
|
||||
<Skeleton key={i} className="h-5 w-5 rounded-full" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Side - Content Skeleton */}
|
||||
<div className="flex flex-col gap-4 col-span-12 md:col-span-8">
|
||||
{/* Tabs Skeleton */}
|
||||
<div className="p-4 flex items-center gap-4 bg-muted rounded-md w-full">
|
||||
<Skeleton className="h-10 w-24" />
|
||||
<Skeleton className="h-10 w-24" />
|
||||
</div>
|
||||
|
||||
{/* Content Area Skeleton */}
|
||||
|
||||
{steps === 1 ? (
|
||||
// Listings Skeleton
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{[1, 2, 3, 4, 5, 6].map(i => (
|
||||
<div key={i} className="border rounded-lg p-4">
|
||||
<Skeleton className="h-40 w-full mb-4" />
|
||||
<Skeleton className="h-4 w-3/4 mb-2" />
|
||||
<Skeleton className="h-4 w-1/2" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
// Ratings Skeleton
|
||||
<div className="flex flex-col gap-6">
|
||||
<div className="flex gap-6 items-center">
|
||||
<div className="flex flex-col gap-3 items-center">
|
||||
<Skeleton className="h-16 w-16" />
|
||||
<div className="flex gap-2">
|
||||
{[1, 2, 3, 4, 5].map(i => (
|
||||
<Skeleton key={i} className="h-5 w-5" />
|
||||
))}
|
||||
</div>
|
||||
<Skeleton className="h-4 w-20" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
{[5, 4, 3, 2, 1].map(i => (
|
||||
<div key={i} className="flex items-center space-x-3 mb-3">
|
||||
<Skeleton className="h-4 w-4" />
|
||||
<Skeleton className="h-2 flex-1" />
|
||||
<Skeleton className="h-4 w-8" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SellerSkeleton;
|
||||
Reference in New Issue
Block a user