(force) Update project: Added 60 new features and made various changes
{warning: this all chnaged}
This commit is contained in:
31
src/pages/404/index.tsx
Normal file
31
src/pages/404/index.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import Particle from "../../components/particle"
|
||||
import Svg404 from '../../assets/svg/404.svg'
|
||||
import { Helmet } from "react-helmet-async"
|
||||
const Page404 = () => {
|
||||
return (
|
||||
<>
|
||||
<Particle />
|
||||
<Helmet>
|
||||
<title>صفحه پیدا نشد 404</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="بهترین خدمات فروشگاه جم و سی پی برای بازیهای آنلاین با پرشین پینگ"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات بازی, بازی آنلاین, خرید یوسی پابجی موبایل"
|
||||
/>
|
||||
</Helmet>
|
||||
|
||||
|
||||
<div className="w-full h-full min-h-screen flex p-6 justify-center items-center">
|
||||
<div className="w-full max-w-96 relative glass h-96 flex flex-col justify-center items-center">
|
||||
<img src={Svg404} alt="404" />
|
||||
<h1 className="absolute bottom-24 -rotate-[30deg] text-[#407BFF]">صفحه مورد نظر یافت نشد</h1>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page404
|
||||
@@ -5,15 +5,16 @@ const About = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>درباره ما</title>
|
||||
<title>پرشین پینگ | درباره ما </title>
|
||||
<meta
|
||||
name="description"
|
||||
content="بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
content="درباره پرشین پینگ - تیم ما و ماموریت ما در ارائه بهترین پینگ برای بازیهای آنلاین ."
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات پینگ, بازی آنلاین, کاهش پینگ , درباره ما"
|
||||
/>
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Helmet>
|
||||
|
||||
<div className="relative w-full h-full py-2">
|
||||
|
||||
44
src/pages/inActivePayment/components/NoPayment.tsx
Normal file
44
src/pages/inActivePayment/components/NoPayment.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
import { BiError } from "react-icons/bi";
|
||||
import Button from "../../../components/button";
|
||||
import { FaTelegram } from "react-icons/fa6";
|
||||
|
||||
const NoPayment = () => {
|
||||
return (
|
||||
<div className="w-full h-full glass flex-1 p-5">
|
||||
<div className="w-full h-full flex flex-col gap-5 items-center justify-between">
|
||||
<div className="w-full h-full flex flex-col justify-center items-center">
|
||||
<BiError className="w-24 h-24" />
|
||||
<h1 className="font-bold md:text-lg">
|
||||
درگاه پرداخت به صورت موقت غیر فعال شده است
|
||||
</h1>
|
||||
</div>
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<p className="text-xs md:text-sm">
|
||||
کاربر گرامی بابت پیش آمدن این مشکل صمیمانه از شما عذر خواهی می کنیم
|
||||
و در تلاشیم هرچه زودتر مشکل درگاه پرداخت را بررسی کرده و سریع به
|
||||
روال قبلی برگردانیم، <br />
|
||||
<span className="text-secondTheme">
|
||||
برای خرید کانفیگ مورد نظر می توانید به پشتیبانی ما در تلگرام پیام
|
||||
دهید
|
||||
</span>{" "}
|
||||
، از صبر شما سپاسگذاریم .{" "}
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full h-full flex flex-col gap-3 justify-center items-center">
|
||||
<p className="text-xs md:text-sm text-[#229ED9]">
|
||||
برای خرید از پشتیبانی در تلگرام لطفا دکمه زیر را کلیک کنید
|
||||
</p>
|
||||
<div className="w-full h-full flex">
|
||||
<Button extra="!flex-1 !bg-[#229ED9]">
|
||||
<a href="https://t.me/PersianPingSupport" className="w-full h-full flex justify-between items-center gap-4">
|
||||
<FaTelegram className="w-7 h-7"/> <span>ورود به پشتیبانی تلگرام</span> <br />
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NoPayment;
|
||||
17
src/pages/inActivePayment/index.tsx
Normal file
17
src/pages/inActivePayment/index.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import Particle from "../../components/particle";
|
||||
import NoPayment from "./components/NoPayment";
|
||||
|
||||
const InActivePayment = () => {
|
||||
return (
|
||||
<>
|
||||
<Particle />
|
||||
<div className="w-full h-full overflow-hidden bg-noisy">
|
||||
<div className="max-w-5xl mx-auto min-h-screen flex justify-center items-center p-5">
|
||||
<NoPayment />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InActivePayment;
|
||||
37
src/pages/landing/index.tsx
Normal file
37
src/pages/landing/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import BestQuality from "./section/bestQuality/BestQuality";
|
||||
import FastSell from "./section/fastSell/FastSell";
|
||||
import Intro from "./section/intro/Intro";
|
||||
import NowSell from "./section/nowSell/NowSell";
|
||||
import ProductList from "./section/productList/ProductList";
|
||||
|
||||
const Landing = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>پرشین پینگ | فروشگاه </title>
|
||||
<meta
|
||||
name="description"
|
||||
content="پرشین پینگ - بهترین خدمات برای بازیهای آنلاین با پرشین پینگ"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات بازی آنلاین, فروشگاه"
|
||||
/>
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Helmet>
|
||||
|
||||
<div className="relative w-full h-full py-2">
|
||||
<div className="relative w-full h-full glass px-4 py-2">
|
||||
<Intro />
|
||||
<FastSell />
|
||||
<NowSell />
|
||||
<ProductList />
|
||||
<BestQuality />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Landing;
|
||||
25
src/pages/landing/section/bestQuality/BestQuality.tsx
Normal file
25
src/pages/landing/section/bestQuality/BestQuality.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import PulsatingCircle from "../../../../components/pulsatingCircle";
|
||||
|
||||
const BestQuality = () => {
|
||||
return (
|
||||
<div className="w-full h-full mt-8 md:mt-10 mb-10 flex gap-10 flex-col justify-center items-center">
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<PulsatingCircle />
|
||||
</div>
|
||||
<div className="w-full h-full flex flex-col gap-2 md:gap-4 justify-center items-center">
|
||||
<h1 className="text-base md:text-3xl lg:text-4xl font-bold">
|
||||
به عنوان{" "}
|
||||
<span className="text-base md:text-3xl lg:text-4xl text-secondTheme font-bold">
|
||||
اولین و بهترین
|
||||
</span>{" "}
|
||||
ارائه دهنده{" "}
|
||||
</h1>
|
||||
<h1 className="relative max-md:right-5 max-lg:right-20 lg:right-40 text-base md:text-3xl lg:text-4xl font-bold">
|
||||
مفتخریم که در خدمت شما باشیم
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BestQuality;
|
||||
19
src/pages/landing/section/fastSell/FastSell.tsx
Normal file
19
src/pages/landing/section/fastSell/FastSell.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { FaPowerOff } from "react-icons/fa6";
|
||||
import LoaderSpin from "../../../../components/loaderSpin";
|
||||
import Typewriter from "../../../../components/typeWriter";
|
||||
|
||||
const FastSell = () => {
|
||||
return (
|
||||
<div className="relative w-full h-full mb-6 flex justify-center items-center">
|
||||
<div className="w-full h-full flex flex-col justify-center items-center">
|
||||
<LoaderSpin>
|
||||
<FaPowerOff className="max-md:text-6xl text-8xl text-white" />
|
||||
</LoaderSpin>
|
||||
<Typewriter text="میبینی ؟ همینقدر" />
|
||||
<Typewriter text="ساده و سریــــــــــــــع" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FastSell;
|
||||
34
src/pages/landing/section/intro/Intro.tsx
Normal file
34
src/pages/landing/section/intro/Intro.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import Button from "../../../../components/button";
|
||||
|
||||
import Rocket3D from "../../../../assets/ui/Rocket.png";
|
||||
import SmoothScrollLink from "../../../../components/scrollLink";
|
||||
|
||||
const Intro = () => {
|
||||
return (
|
||||
<div className="relative w-full h-full gap-6 grid grid-cols-1 py-6 md:grid-cols-2">
|
||||
<div className="w-full h-full flex flex-col max-md:gap-5 gap-8 justify-center mx-auto items-center">
|
||||
<h1 className="text-start flex flex-col gap-3 md:gap-4 text-3xl md:text-4xl lg:text-5xl">
|
||||
<span className="inline-block text-center">بزرگترین فروشگاه</span>
|
||||
<span className="text-secondTheme font-bold max-md:text-center">
|
||||
خرید سی پی تنها با چند کلیک
|
||||
</span>
|
||||
</h1>
|
||||
<p className="text-center text-xl text-gray-600">
|
||||
سال ها تجربه پشت یک خرید شماست
|
||||
</p>
|
||||
<SmoothScrollLink to="#configs">
|
||||
<Button >برو به فروشگاه</Button>
|
||||
</SmoothScrollLink>
|
||||
</div>
|
||||
<div className="w-full h-full flex justify-center py-4 mx-auto items-center max-md:hidden">
|
||||
<img
|
||||
src={Rocket3D}
|
||||
alt="intro"
|
||||
className="w-60 md:w-72 lg:w-[25rem] animate-bounce"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Intro;
|
||||
13
src/pages/landing/section/nowSell/NowSell.tsx
Normal file
13
src/pages/landing/section/nowSell/NowSell.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import Content from "./components/Content";
|
||||
|
||||
const NowSell = () => {
|
||||
return (
|
||||
<div className="relative w-full h-full p-5">
|
||||
<div className="relative w-full max-md:h-[15rem] md:h-[20rem] lg:h-[25rem] p-2 rounded-2xl bg-white shadow-lg bg-banner bg-center bg-cover shadow-current flex justify-center items-center">
|
||||
<Content />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NowSell;
|
||||
23
src/pages/landing/section/nowSell/components/Content.tsx
Normal file
23
src/pages/landing/section/nowSell/components/Content.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import Button from "../../../../../components/button";
|
||||
|
||||
const Content = () => {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col text-center max-md:gap-3 gap-5 justify-center text-black items-center max-md:py-5 px-5 py-10">
|
||||
<h1 className="text-4xl max-md:text-2xl lg:text-5xl font-bold">
|
||||
با چندین سال
|
||||
</h1>
|
||||
<h1 className="text-4xl max-md:text-2xl lg:text-5xl font-bold">
|
||||
تجربـــــــــــــــــــه
|
||||
</h1>
|
||||
<p className="text-2xl max-md:text-sm text-gray-500">
|
||||
بهترین نتیجه رو بهتون تقدیم میکنیم
|
||||
</p>
|
||||
<Button extra="!bg-black text-white text-2xl max-md:text-lg max-md:!py-2 mt-4 !py-4 !px-10 !rounded-xl">
|
||||
{" "}
|
||||
همین الان میخرم
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Content;
|
||||
24
src/pages/landing/section/productList/ProductList.tsx
Normal file
24
src/pages/landing/section/productList/ProductList.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import { useState } from "react";
|
||||
import ButtonList from "./components/ButtonList";
|
||||
import ListAllProduct from "./components/ListAllProduct";
|
||||
|
||||
const ProductList = () => {
|
||||
const [activeId, setActiveId] = useState<number>(0);
|
||||
|
||||
return (
|
||||
<div className="relative w-full h-full max-md:mt-4 md:mt-8 " id="configs">
|
||||
<div className="w-full h-full gap-4 md:gap-6 flex flex-col p-5 justify-center items-center">
|
||||
<h1 className="max-md:text-sm text-center text-2xl font-bold">
|
||||
برای انتخاب سی پی یا جم مورد نظر کلیک کنید تا براتون نمایش داده شود
|
||||
</h1>
|
||||
<ButtonList
|
||||
activeId={activeId}
|
||||
setActiveId={setActiveId}
|
||||
/>
|
||||
<ListAllProduct activeId={activeId} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProductList;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import Button from "../../../../../components/button";
|
||||
|
||||
const ButtonList = ({ activeId, setActiveId } : {activeId:number;setActiveId:Dispatch<SetStateAction<number>>}) => {
|
||||
const ProductsList: { id: number; name: string; label?: string }[] = [
|
||||
{ id: 0, name: "یوسی پابجی", label: "uc pubg" },
|
||||
{ id: 1, name: "سی پی کالاف", label: "cp callof" },
|
||||
{ id: 2, name: "جم کلش", label: "gem clash" },
|
||||
];
|
||||
return (
|
||||
<div className="w-full h-full gap-4 flex flex-wrap flex-row justify-center">
|
||||
{ProductsList?.map((product) => (
|
||||
<Button
|
||||
key={product.id}
|
||||
active={product.id === activeId}
|
||||
extra="md:py-2 md:text-lg max-sm:!px-1"
|
||||
name={product.label || product.name}
|
||||
onClick={() => setActiveId(product.id)}
|
||||
>
|
||||
{product.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ButtonList;
|
||||
@@ -0,0 +1,67 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import Button from "../../../../../components/button";
|
||||
|
||||
import clash1 from "../../../../../assets/product-image/clash/clashofclans-gem500.png";
|
||||
import clash2 from "../../../../../assets/product-image/clash/clashofclans-gem1200.png";
|
||||
import clash3 from "../../../../../assets/product-image/clash/clashofclans-gem2500.png";
|
||||
import clash4 from "../../../../../assets/product-image/clash/clashofclans-gem6500.png";
|
||||
|
||||
import pubg1 from "../../../../../assets/product-image/pubg/uc-pubgmobile63.png";
|
||||
import pubg2 from "../../../../../assets/product-image/pubg/uc-pubgmobile340.png";
|
||||
import pubg3 from "../../../../../assets/product-image/pubg/uc-pubgmobile690.png";
|
||||
import pubg4 from "../../../../../assets/product-image/pubg/uc-pubgmobile1875.png";
|
||||
|
||||
import callof1 from "../../../../../assets/product-image/callof/cp-callofduty80.png";
|
||||
import callof2 from "../../../../../assets/product-image/callof/cp-callofduty360.png";
|
||||
import callof3 from "../../../../../assets/product-image/callof/cp-callofduty880.png";
|
||||
import callof4 from "../../../../../assets/product-image/callof/cp-callofduty2400.png";
|
||||
|
||||
const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
||||
const allProducts = [
|
||||
[
|
||||
{ id: 1, title: "پابجی 1", image: pubg1 },
|
||||
{ id: 2, title: "پابجی 2", image: pubg2 },
|
||||
{ id: 3, title: "پابجی 3", image: pubg3 },
|
||||
{ id: 4, title: "پابجی 4", image: pubg4 },
|
||||
],
|
||||
[
|
||||
{ id: 1, title: "کالاف 1", image: callof1 },
|
||||
{ id: 2, title: "کالاف 2", image: callof2 },
|
||||
{ id: 3, title: "کالاف 3", image: callof3 },
|
||||
{ id: 4, title: "کالاف 4", image: callof4 },
|
||||
],
|
||||
|
||||
[
|
||||
{ id: 1, title: "جم کلش 1", image: clash1 },
|
||||
{ id: 2, title: "جم کلش 2", image: clash2 },
|
||||
{ id: 3, title: "جم کلش 3", image: clash3 },
|
||||
{ id: 4, title: "جم کلش 4", image: clash4 },
|
||||
],
|
||||
];
|
||||
const [product, setProduct] = useState(allProducts[activeId]);
|
||||
useEffect(() => {
|
||||
setProduct(allProducts[activeId]);
|
||||
}, [activeId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="w-full h-full border-4 gap-5 border-spacing-1 shadow-lg shadow-white border-solid border-white rounded-lg p-3 grid max-sm:grid-cols-1 max-md:grid-cols-2 max-lg:grid-cols-3 lg:grid-cols-4"
|
||||
id="listConfig"
|
||||
>
|
||||
{product.map((item) => {
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col" key={item.id}>
|
||||
<img src={item.image} alt={item.title} />
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<Button extra="!w-4/5">خرید</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListAllProduct;
|
||||
@@ -74,7 +74,6 @@ const Profile = () => {
|
||||
return axios.get("/profile");
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
{!mutation.isPending && (
|
||||
|
||||
@@ -34,7 +34,7 @@ const ProductConfigList = ({
|
||||
لیست کانفیگ ها
|
||||
</p>
|
||||
<div
|
||||
className="w-full h-full p-3 flex flex-col gap-3
|
||||
className="w-full h-full p-3 max-sm:px-0 flex flex-col gap-3
|
||||
justify-center items-center"
|
||||
>
|
||||
{isLoading || isFetching ? (
|
||||
@@ -59,15 +59,15 @@ const ProductConfigList = ({
|
||||
return (
|
||||
<Fragment key={item.id}>
|
||||
<ConfigItem
|
||||
id={String(item.id)}
|
||||
configName={item.title}
|
||||
dateBuy={item.created_at || "تاریخ نامعتبر"}
|
||||
isDisabled={false}
|
||||
isDisabled={item.is_expired}
|
||||
isShowTraffic={item.show_traffic}
|
||||
textCopy={item.url}
|
||||
maxTraffic={item.max_traffic}
|
||||
traffic={item.traffic}
|
||||
type={item.type}
|
||||
categoryId={item.category_id}
|
||||
useMaxTraffic={item.use_max_traffic}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
@@ -9,37 +9,44 @@ import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setCategoryId } from "../../../../redux/slices/categoryId";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import useAxios from "../../../../hooks/useAxios";
|
||||
import LoaderSpaceConfig from "./LoaderSpaceConfig";
|
||||
|
||||
const ConfigItem = ({
|
||||
id,
|
||||
dateBuy,
|
||||
configName,
|
||||
isDisabled,
|
||||
isShowTraffic,
|
||||
textCopy,
|
||||
maxTraffic,
|
||||
traffic,
|
||||
type,
|
||||
categoryId,
|
||||
useMaxTraffic
|
||||
}: ConfigItemProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isOpenDropDown, setIsOpenDropDown] = useState(false);
|
||||
const [configId, setConfigId] = useState("");
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [spaceConfig, setSpaceConfig] = useState({
|
||||
maxTraffic: 0,
|
||||
traffic: 0,
|
||||
});
|
||||
const axios = useAxios();
|
||||
const handleToggle = () => {
|
||||
setIsOpenDropDown(!isOpen);
|
||||
console.log(id);
|
||||
setConfigId(id);
|
||||
setIsOpenDropDown(!isOpenDropDown);
|
||||
};
|
||||
const [copyText, setCopyText] = useState<string>("");
|
||||
const [_, setCopyText] = useState<string>("");
|
||||
const copyToClipboard = async (textToCopy: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(copyText);
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
}
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
setIsOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,6 +64,25 @@ const ConfigItem = ({
|
||||
}
|
||||
}, [textCopy]);
|
||||
|
||||
const queryUpdateConfig = useQuery({
|
||||
queryKey: ["updateConfig", isOpenDropDown || isOpen],
|
||||
queryFn: () => {
|
||||
if (configId) {
|
||||
console.log("fethed");
|
||||
return axios.get(`/update-config/${configId}`);
|
||||
}else{
|
||||
throw new Error("داده دریافتی یا پیدا نشده یا نامعتبر است")
|
||||
}
|
||||
},
|
||||
enabled: isOpenDropDown || isShowTraffic,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (!queryUpdateConfig.isLoading || !queryUpdateConfig.isFetching)
|
||||
setSpaceConfig({
|
||||
maxTraffic: queryUpdateConfig?.data?.data?.result?.max_traffic,
|
||||
traffic: queryUpdateConfig?.data?.data?.result?.traffic,
|
||||
});
|
||||
}, [queryUpdateConfig?.data, queryUpdateConfig.error]);
|
||||
return (
|
||||
<>
|
||||
<ModalConfigDetails
|
||||
@@ -67,24 +93,29 @@ const ConfigItem = ({
|
||||
nameConfig={configName}
|
||||
isShowTraffic={isShowTraffic}
|
||||
textCopy={textCopy}
|
||||
maxTraffic={maxTraffic}
|
||||
traffic={traffic}
|
||||
maxTraffic={spaceConfig.maxTraffic}
|
||||
traffic={spaceConfig.traffic}
|
||||
type={type}
|
||||
username={username}
|
||||
password={password}
|
||||
categoryId={categoryId}
|
||||
isLoadSpaceConfig={
|
||||
queryUpdateConfig.isLoading || queryUpdateConfig.isFetching
|
||||
}
|
||||
error={queryUpdateConfig?.error ? queryUpdateConfig?.error : null}
|
||||
useMaxTraffic={useMaxTraffic}
|
||||
/>
|
||||
<div className="relative w-full h-full rounded-md">
|
||||
{isDisabled && (
|
||||
{isDisabled || useMaxTraffic && (
|
||||
<div className="absolute inset-0 flex items-center justify-center bg-opacity-50 rounded-md backdrop-blur-sm z-10">
|
||||
<div className="text-white font-bold max-sm:p-[2px] p-1 bg-red-600 max-sm:text-[10px] text-xs md:text-base rounded-md shadow-md">
|
||||
<div className="text-white font-bold max-sm:p-[2px] p-1 bg-rose-600 max-sm:text-[9px] sm:text-xs md:text-base rounded-md shadow-md">
|
||||
کانفیگ <span className="text-secondTheme">{configName}</span> غیر
|
||||
فعال است .{" "}
|
||||
<Button
|
||||
extra="cursor-pointer !px-1 !py-0 rounded-md bg-white"
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
برای تمدید کلیک کنید
|
||||
مشاهده
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,16 +132,6 @@ const ConfigItem = ({
|
||||
<div className="max-md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
{type === "gaming" ? (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(username);
|
||||
copyToClipboard(username);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی یوزرنیم
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(password);
|
||||
@@ -121,6 +142,16 @@ const ConfigItem = ({
|
||||
کپی پسورد
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(username);
|
||||
copyToClipboard(username);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی یوزرنیم
|
||||
<IoCopy />
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
@@ -145,7 +176,7 @@ const ConfigItem = ({
|
||||
>
|
||||
آموزش
|
||||
</Button>
|
||||
{isShowTraffic && (
|
||||
{isShowTraffic && !useMaxTraffic && (
|
||||
<Dropdown isOpen={isOpenDropDown} setIsOpen={setIsOpenDropDown}>
|
||||
<Button
|
||||
id="dropdownDefaultButton"
|
||||
@@ -157,7 +188,21 @@ const ConfigItem = ({
|
||||
حجم
|
||||
</Button>
|
||||
<DropdownItem widthTailwind="w-[20rem] left-0">
|
||||
<ProgressSpace startSpace={traffic} endSpace={maxTraffic} />
|
||||
{queryUpdateConfig.isLoading ||
|
||||
queryUpdateConfig.isFetching ? (
|
||||
<div className="w-full h-full p-5 flex justify-center items-center">
|
||||
<LoaderSpaceConfig />
|
||||
</div>
|
||||
) : queryUpdateConfig.error ? (
|
||||
<div className="w-full h-full p-5 flex justify-center items-center">
|
||||
خطایی رخ داد : {queryUpdateConfig?.error?.message}
|
||||
</div>
|
||||
) : (
|
||||
<ProgressSpace
|
||||
startSpace={spaceConfig.traffic}
|
||||
endSpace={spaceConfig.maxTraffic}
|
||||
/>
|
||||
)}
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
)}
|
||||
@@ -165,7 +210,10 @@ const ConfigItem = ({
|
||||
<div className="md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
<Button
|
||||
extra="!m-0 !px-2 !py-1 shadow-md"
|
||||
onClick={() => setIsOpen(true)}
|
||||
onClick={() => {
|
||||
setConfigId(id);
|
||||
setIsOpen(true);
|
||||
}}
|
||||
>
|
||||
مشاهده جزئیات
|
||||
</Button>
|
||||
|
||||
60
src/pages/profile/section/components/LoaderSpaceConfig.tsx
Normal file
60
src/pages/profile/section/components/LoaderSpaceConfig.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
export default function LoaderSpaceConfig() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width={70}>
|
||||
<circle
|
||||
fill="#71FBB2"
|
||||
stroke="#71FBB2"
|
||||
strokeWidth="15"
|
||||
r="15"
|
||||
cx="40"
|
||||
cy="65"
|
||||
>
|
||||
<animate
|
||||
attributeName="cy"
|
||||
calcMode="spline"
|
||||
dur="2"
|
||||
values="65;135;65;"
|
||||
keySplines=".5 0 .5 1;.5 0 .5 1"
|
||||
repeatCount="indefinite"
|
||||
begin="-.4"
|
||||
></animate>
|
||||
</circle>
|
||||
<circle
|
||||
fill="#71FBB2"
|
||||
stroke="#71FBB2"
|
||||
strokeWidth="15"
|
||||
r="15"
|
||||
cx="100"
|
||||
cy="65"
|
||||
>
|
||||
<animate
|
||||
attributeName="cy"
|
||||
calcMode="spline"
|
||||
dur="2"
|
||||
values="65;135;65;"
|
||||
keySplines=".5 0 .5 1;.5 0 .5 1"
|
||||
repeatCount="indefinite"
|
||||
begin="-.2"
|
||||
></animate>
|
||||
</circle>
|
||||
<circle
|
||||
fill="#71FBB2"
|
||||
stroke="#71FBB2"
|
||||
strokeWidth="15"
|
||||
r="15"
|
||||
cx="160"
|
||||
cy="65"
|
||||
>
|
||||
<animate
|
||||
attributeName="cy"
|
||||
calcMode="spline"
|
||||
dur="2"
|
||||
values="65;135;65;"
|
||||
keySplines=".5 0 .5 1;.5 0 .5 1"
|
||||
repeatCount="indefinite"
|
||||
begin="0"
|
||||
></animate>
|
||||
</circle>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -8,6 +8,9 @@ import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setCategoryId } from "../../../../redux/slices/categoryId";
|
||||
import Input from "../../../../components/input";
|
||||
import LoaderSpaceConfig from "./LoaderSpaceConfig";
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
const ModalConfigDetails = ({
|
||||
isOpen,
|
||||
@@ -23,6 +26,9 @@ const ModalConfigDetails = ({
|
||||
username,
|
||||
password,
|
||||
categoryId,
|
||||
isLoadSpaceConfig,
|
||||
error,
|
||||
useMaxTraffic,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
@@ -37,10 +43,14 @@ const ModalConfigDetails = ({
|
||||
username: string;
|
||||
password: string;
|
||||
categoryId: number;
|
||||
isLoadSpaceConfig: boolean;
|
||||
error: AxiosError | any;
|
||||
useMaxTraffic: boolean;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const [copyText, setCopyText] = useState<string>("");
|
||||
const [_, setCopyText] = useState<string>("");
|
||||
const [isCopyError, setIsCopyError] = useState(false);
|
||||
|
||||
const copyToClipboard = async (textToCopy: string) => {
|
||||
try {
|
||||
@@ -48,12 +58,8 @@ const ModalConfigDetails = ({
|
||||
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(copyText);
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
}
|
||||
setIsCopyError(true);
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
}
|
||||
};
|
||||
return (
|
||||
@@ -68,48 +74,95 @@ const ModalConfigDetails = ({
|
||||
تاریخ خرید : {dateBuy}
|
||||
</h1>
|
||||
<div className="w-full h-full flex flex-row-reverse gap-2 flex-wrap justify-center items-center">
|
||||
{isDisabled ? (
|
||||
{isDisabled || useMaxTraffic ? (
|
||||
<div className="w-full h-full flex flex-wrap justify-center items-center">
|
||||
<Button extra="!m-0 !px-2 !py-1 shadow-md flex-1 shadow-secondTheme !bg-secondTheme font-bold hover:!bg-gray-800 hover:!shadow-gray-800 ">
|
||||
{/* <Button extra="!m-0 !px-2 !py-1 shadow-md flex-1 shadow-secondTheme !bg-secondTheme font-bold hover:!bg-gray-800 hover:!shadow-gray-800 ">
|
||||
تمدید
|
||||
</Button>
|
||||
</Button> */}
|
||||
<div className="w-full h-full text-xs sm:text-sm md:text-base">
|
||||
{useMaxTraffic ? (
|
||||
<h1>مشترک گرامی کانفیگ شما به دلیل اتمام حجم غیر فعال است</h1>
|
||||
) : isDisabled && (
|
||||
<h1>مشترک گرامی کانفیگ شما به دلیل پایان اعتبار غیر فعال است</h1>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{type === "gaming" ? (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(username);
|
||||
copyToClipboard(username);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی یوزرنیم
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(password);
|
||||
copyToClipboard(password);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی پسورد
|
||||
<IoCopy />
|
||||
</Button>
|
||||
{isCopyError ? (
|
||||
<div className="w-full h-full flex flex-col gap-2">
|
||||
<h1 className="bg-red-600 rounded-md p-1">
|
||||
کاربر گرامی کپی خودکار موفقیت آمیز نبود لطفا به صورت
|
||||
دستی از زیر انتخاب کرده و کپی کنید
|
||||
</h1>
|
||||
<Input
|
||||
type="text"
|
||||
value={`نام کاربری : ${username}`}
|
||||
disabled={true}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
value={`رمز عبور : ${password}`}
|
||||
disabled={true}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(password);
|
||||
copyToClipboard(password);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی پسورد
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(username);
|
||||
copyToClipboard(username);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی یوزرنیم
|
||||
<IoCopy />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(textCopy);
|
||||
copyToClipboard(textCopy);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<>
|
||||
{isCopyError ? (
|
||||
<>
|
||||
<div className="w-full h-full flex flex-col gap-2">
|
||||
<h1 className="bg-red-600 rounded-md p-1">
|
||||
کاربر گرامی کپی خودکار موفقیت آمیز نبود لطفا به صورت
|
||||
دستی از زیر انتخاب کرده و کپی کنید
|
||||
</h1>
|
||||
<textarea
|
||||
value={textCopy}
|
||||
className="outline-none border-none sm:text-sm rounded-lg block w-full p-2.5 md:p-3 bg-gray-700 border-gray-600 placeholder-gray-400 text-white"
|
||||
disabled={true}
|
||||
rows={8}
|
||||
></textarea>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setCopyText(textCopy);
|
||||
copyToClipboard(textCopy);
|
||||
}}
|
||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
||||
>
|
||||
کپی
|
||||
<IoCopy />
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<Button
|
||||
onClick={() => {
|
||||
@@ -127,7 +180,18 @@ const ModalConfigDetails = ({
|
||||
</div>
|
||||
{!isDisabled && isShowTraffic && (
|
||||
<div className="w-full h-full">
|
||||
<ProgressSpace startSpace={traffic} endSpace={maxTraffic} />
|
||||
{isLoadSpaceConfig ? (
|
||||
<div className="flex w-full h-full justify-center items-center">
|
||||
<LoaderSpaceConfig />
|
||||
</div>
|
||||
) : error ? (
|
||||
<div className="w-full h-full text-center">
|
||||
{" "}
|
||||
خطایی رخ داد : {error?.message}
|
||||
</div>
|
||||
) : (
|
||||
<ProgressSpace startSpace={traffic} endSpace={maxTraffic} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,16 @@ const Rule = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>قوانین</title>
|
||||
<title>پرشین پینگ | قوانین </title>
|
||||
<meta
|
||||
name="description"
|
||||
content="بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
content="قوانین و مقررات بازیهای پرشین پینگ - بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات پینگ, بازی آنلاین, کاهش پینگ , قوانین"
|
||||
/>
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Helmet>
|
||||
<div className="relative w-full h-full py-2">
|
||||
<div className="relative w-full h-full glass px-4 py-2">
|
||||
|
||||
@@ -9,15 +9,16 @@ const Shop = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>فروشگاه</title>
|
||||
<title>پرشین پینگ | فروشگاه </title>
|
||||
<meta
|
||||
name="description"
|
||||
content="بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
content="پرشین پینگ - بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات پینگ, بازی آنلاین, کاهش پینگ و فروشگاه"
|
||||
/>
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Helmet>
|
||||
|
||||
<div className="relative w-full h-full py-2">
|
||||
|
||||
@@ -148,6 +148,9 @@ const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
||||
) {
|
||||
errorMessage = (err.response.data as { alert: string }).alert;
|
||||
}
|
||||
toast.error(`خطایی رخ داد : ${errorMessage}`)
|
||||
// dispatch(setIsPaymentGateway(false));
|
||||
// navigate("/noactive-payment");
|
||||
return errorMessage;
|
||||
},
|
||||
});
|
||||
@@ -157,7 +160,7 @@ const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
||||
<MuiModal
|
||||
isOpenModal={isOpen}
|
||||
setIsOpenModal={setIsOpen}
|
||||
submitText="خرید"
|
||||
submitText={data?.data?.result?.max_config === 0 ? undefined : "خرید"}
|
||||
submitOnClick={submitHandler}
|
||||
disabled={isLoading || isFetching}
|
||||
nameService={
|
||||
@@ -212,66 +215,76 @@ const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
||||
)}
|
||||
</span>
|
||||
</h1>
|
||||
<h3 className="text-xs md:text-sm text-gray-400">
|
||||
در صورت نداشتن کد تخفیف پرداخت رو بزنید
|
||||
</h3>
|
||||
<form
|
||||
className="flex flex-col gap-3 justify-center items-center"
|
||||
onSubmit={formik.handleSubmit}
|
||||
>
|
||||
<div className="w-full h-full flex justify-center items-center gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
extra="max-md:!p-1 md:!p-2"
|
||||
{...formik.getFieldProps("discount")}
|
||||
/>
|
||||
<Button
|
||||
extra="md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={discountHandler}
|
||||
disabled={formik.values.discount === ""}
|
||||
{data?.data?.result?.max_config === 0 ? (
|
||||
<>
|
||||
<Alert type="info" extra="mt-5"> موجودی کانفیگ به اتمام رسیده </Alert>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h3 className="text-xs md:text-sm text-gray-400">
|
||||
در صورت نداشتن کد تخفیف پرداخت رو بزنید
|
||||
</h3>
|
||||
<form
|
||||
className="flex flex-col gap-3 justify-center items-center"
|
||||
onSubmit={formik.handleSubmit}
|
||||
>
|
||||
اعمال
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-2/3 h-full flex justify-center items-center gap-1">
|
||||
<Button
|
||||
extra="!px-2 max-md:!py-1.5 md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={() => {
|
||||
if (formik.values.count > 1) {
|
||||
chnageCountHandler("dec");
|
||||
}
|
||||
}}
|
||||
disabled={formik.values.count === 1}
|
||||
>
|
||||
<BiMinus />
|
||||
</Button>
|
||||
<Input
|
||||
type="text"
|
||||
extra="max-md:py-0 !p-1 !text-center"
|
||||
{...formik.getFieldProps("count")}
|
||||
/>
|
||||
<Button
|
||||
extra="!px-2 max-md:!py-1.5 md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={() => {
|
||||
if (
|
||||
formik.values.count.toString() <
|
||||
data?.data?.result?.max_config
|
||||
)
|
||||
chnageCountHandler("inc");
|
||||
}}
|
||||
disabled={
|
||||
formik.values.count.toString() ===
|
||||
data?.data?.result?.max_config
|
||||
}
|
||||
>
|
||||
<BiPlus />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
<div className="w-full h-full flex justify-center items-center gap-2">
|
||||
<Input
|
||||
type="text"
|
||||
extra="max-md:!p-1 md:!p-2"
|
||||
{...formik.getFieldProps("discount")}
|
||||
/>
|
||||
<Button
|
||||
extra="md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={discountHandler}
|
||||
disabled={formik.values.discount === ""}
|
||||
>
|
||||
اعمال
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-2/3 h-full flex justify-center items-center gap-1">
|
||||
<Button
|
||||
extra="!px-2 max-md:!py-1.5 md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={() => {
|
||||
if (formik.values.count > 1) {
|
||||
chnageCountHandler("dec");
|
||||
}
|
||||
}}
|
||||
disabled={formik.values.count === 1}
|
||||
>
|
||||
<BiMinus />
|
||||
</Button>
|
||||
<Input
|
||||
type="text"
|
||||
extra="max-md:py-0 !p-1 !text-center"
|
||||
{...formik.getFieldProps("count")}
|
||||
/>
|
||||
<Button
|
||||
extra="!px-2 max-md:!py-1.5 md:!py-1 disabled:!bg-gray-300"
|
||||
onClick={() => {
|
||||
if (
|
||||
formik.values.count.toString() <
|
||||
data?.data?.result?.max_config
|
||||
)
|
||||
chnageCountHandler("inc");
|
||||
}}
|
||||
disabled={
|
||||
formik.values.count.toString() ===
|
||||
data?.data?.result?.max_config
|
||||
}
|
||||
>
|
||||
<BiPlus />
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</>
|
||||
)}
|
||||
<div className="w-full h-full flex flex-col justify-between items-center gap-2 text-xs md:text-sm">
|
||||
<div className="w-full h-full bg-gray-900/50 rounded-md shadow flex justify-between items-center p-3">
|
||||
<p>حجم</p>
|
||||
<p dir="ltr">{`${data?.data?.result?.traffic} GB`}</p>
|
||||
<p dir="ltr">{`${data?.data?.result?.traffic} ${
|
||||
data?.data?.result?.is_test ? "MB" : "GB"
|
||||
}`}</p>
|
||||
</div>
|
||||
<div className="w-full h-full bg-gray-900/50 rounded-md shadow flex justify-between items-center p-3">
|
||||
<p>محدودیت کاربر</p>
|
||||
|
||||
@@ -5,15 +5,16 @@ const Tutorial = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>اموزش</title>
|
||||
<title>پرشین پینگ | آموزش </title>
|
||||
<meta
|
||||
name="description"
|
||||
content="بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
content="آموزش بازیهای آنلاین پرشین پینگ - بهترین خدمات پینگ برای بازیهای آنلاین با پرشین پینگ"
|
||||
/>
|
||||
<meta
|
||||
name="keywords"
|
||||
content="پرشین پینگ, خدمات پینگ, بازی آنلاین, کاهش پینگ و آموزش"
|
||||
/>
|
||||
<meta name="robots" content="index, follow" />
|
||||
</Helmet>
|
||||
|
||||
<div className="relative w-full h-full py-2">
|
||||
|
||||
@@ -90,7 +90,12 @@ const Learn = ({ activeTutorial }: LearnProps) => {
|
||||
|
||||
{/* Render learning items */}
|
||||
{data?.data?.result?.learning.map(
|
||||
(i: { id: string; title: string; medias: string[] }) => (
|
||||
(i: {
|
||||
id: string;
|
||||
title: string;
|
||||
medias: string[];
|
||||
url_file: string;
|
||||
}) => (
|
||||
<div
|
||||
key={i.id}
|
||||
className="relative w-full h-full flex flex-col justify-center items-center"
|
||||
@@ -125,6 +130,14 @@ const Learn = ({ activeTutorial }: LearnProps) => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
{i.url_file && (
|
||||
<>
|
||||
<Button extra="!mb-4" onClick={() => saveAs(i.url_file)}>
|
||||
دانلود برنامه
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
<hr className="border-2 w-full rounded-md my-5 border-gray-800" />
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user