feat : formating code by prettier
This commit is contained in:
@@ -44,7 +44,7 @@ const Profile = () => {
|
|||||||
});
|
});
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const trackId = query.get("trackId");
|
const trackId = query.get("trackId");
|
||||||
|
|
||||||
if (trackId) {
|
if (trackId) {
|
||||||
toast.promise(mutation.mutateAsync({ trackId }), {
|
toast.promise(mutation.mutateAsync({ trackId }), {
|
||||||
loading: "درحال تایید درگاه پرداخت...",
|
loading: "درحال تایید درگاه پرداخت...",
|
||||||
@@ -68,12 +68,12 @@ const Profile = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const {data,isLoading,isFetching,error} = useQuery({
|
const { data, isLoading, isFetching, error } = useQuery({
|
||||||
queryKey:["profile",mutation],
|
queryKey: ["profile", mutation],
|
||||||
queryFn:()=>{
|
queryFn: () => {
|
||||||
return axios.get('/profile');
|
return axios.get("/profile");
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!mutation.isPending && (
|
{!mutation.isPending && (
|
||||||
@@ -85,8 +85,19 @@ const Profile = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className="relative h-full w-full flex flex-col gap-4">
|
<div className="relative h-full w-full flex flex-col gap-4">
|
||||||
<ProfileDetails data={data?.data} isLoading={isLoading} isFetching={isFetching} error={error} userTitle=""/>
|
<ProfileDetails
|
||||||
<ProductConfigList data={data?.data} userTitle={data?.data?.result?.user_profile?.name} isLoading={isLoading} isFetching={isFetching} error={error} />
|
data={data?.data}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isFetching={isFetching}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
|
<ProductConfigList
|
||||||
|
data={data?.data}
|
||||||
|
userTitle={data?.data?.result?.user_profile?.name}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isFetching={isFetching}
|
||||||
|
error={error}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -66,6 +66,8 @@ const ProductConfigList = ({
|
|||||||
textCopy={item.url}
|
textCopy={item.url}
|
||||||
maxTraffic={item.max_traffic}
|
maxTraffic={item.max_traffic}
|
||||||
traffic={item.traffic}
|
traffic={item.traffic}
|
||||||
|
type={item.type}
|
||||||
|
categoryId={item.category_id}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ import { IoCopy } from "react-icons/io5";
|
|||||||
import Button from "../../../../components/button";
|
import Button from "../../../../components/button";
|
||||||
import { ConfigItemProps } from "../../../../types/props";
|
import { ConfigItemProps } from "../../../../types/props";
|
||||||
import ModalConfigDetails from "./ModalConfigDetails";
|
import ModalConfigDetails from "./ModalConfigDetails";
|
||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Dropdown, { DropdownItem } from "../../../../components/dropdown";
|
import Dropdown, { DropdownItem } from "../../../../components/dropdown";
|
||||||
import ProgressSpace from "../../../../components/progressSpace";
|
import ProgressSpace from "../../../../components/progressSpace";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setCategoryId } from "../../../../redux/slices/categoryId";
|
||||||
|
|
||||||
const ConfigItem = ({
|
const ConfigItem = ({
|
||||||
dateBuy,
|
dateBuy,
|
||||||
@@ -16,22 +18,45 @@ const ConfigItem = ({
|
|||||||
textCopy,
|
textCopy,
|
||||||
maxTraffic,
|
maxTraffic,
|
||||||
traffic,
|
traffic,
|
||||||
|
type,
|
||||||
|
categoryId,
|
||||||
}: ConfigItemProps) => {
|
}: ConfigItemProps) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [isOpenDropDown, setIsOpenDropDown] = useState(false);
|
const [isOpenDropDown, setIsOpenDropDown] = useState(false);
|
||||||
|
const dispatch = useDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const handleToggle = () => {
|
const handleToggle = () => {
|
||||||
setIsOpenDropDown(!isOpen);
|
setIsOpenDropDown(!isOpen);
|
||||||
};
|
};
|
||||||
|
const [copyText, setCopyText] = useState<string>("");
|
||||||
const copyToClipboard = async (textToCopy: string) => {
|
const copyToClipboard = async (textToCopy: string) => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(textToCopy);
|
await navigator.clipboard.writeText(textToCopy);
|
||||||
toast.success("متن با موفقیت کپی شد!");
|
toast.success("متن با موفقیت کپی شد!");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(`Failed to copy: , ${err}`);
|
try {
|
||||||
|
await navigator.clipboard.writeText(copyText);
|
||||||
|
toast.success("متن با موفقیت کپی شد!");
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(`Failed to copy: , ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (type === "gaming" && textCopy) {
|
||||||
|
const [user, pass] = textCopy.split(",");
|
||||||
|
setUsername(user);
|
||||||
|
setPassword(pass);
|
||||||
|
} else {
|
||||||
|
setUsername("");
|
||||||
|
setPassword("");
|
||||||
|
}
|
||||||
|
}, [textCopy]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ModalConfigDetails
|
<ModalConfigDetails
|
||||||
@@ -44,6 +69,10 @@ const ConfigItem = ({
|
|||||||
textCopy={textCopy}
|
textCopy={textCopy}
|
||||||
maxTraffic={maxTraffic}
|
maxTraffic={maxTraffic}
|
||||||
traffic={traffic}
|
traffic={traffic}
|
||||||
|
type={type}
|
||||||
|
username={username}
|
||||||
|
password={password}
|
||||||
|
categoryId={categoryId}
|
||||||
/>
|
/>
|
||||||
<div className="relative w-full h-full rounded-md">
|
<div className="relative w-full h-full rounded-md">
|
||||||
{isDisabled && (
|
{isDisabled && (
|
||||||
@@ -70,16 +99,49 @@ const ConfigItem = ({
|
|||||||
{configName}
|
{configName}
|
||||||
</li>
|
</li>
|
||||||
<div className="max-md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
<div className="max-md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||||
<Button
|
{type === "gaming" ? (
|
||||||
onClick={() => copyToClipboard(textCopy)}
|
<>
|
||||||
extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white"
|
<Button
|
||||||
>
|
onClick={() => {
|
||||||
کپی
|
setCopyText(username);
|
||||||
<IoCopy />
|
copyToClipboard(username);
|
||||||
</Button>
|
}}
|
||||||
|
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>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<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
|
<Button
|
||||||
extra="!m-0 !px-2 !py-1.5 shadow-md"
|
extra="!m-0 !px-2 !py-1.5 shadow-md"
|
||||||
onClick={() => navigate("/tutorial")}
|
onClick={() => {
|
||||||
|
if (categoryId) {
|
||||||
|
dispatch(setCategoryId(categoryId));
|
||||||
|
navigate("/tutorial");
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
آموزش
|
آموزش
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, SetStateAction, useState } from "react";
|
||||||
import MuiModal from "../../../../common/modal";
|
import MuiModal from "../../../../common/modal";
|
||||||
import Button from "../../../../components/button";
|
import Button from "../../../../components/button";
|
||||||
import { IoCopy } from "react-icons/io5";
|
import { IoCopy } from "react-icons/io5";
|
||||||
@@ -6,6 +6,8 @@ import Alert from "../../../../components/alert/Alert";
|
|||||||
import ProgressSpace from "../../../../components/progressSpace";
|
import ProgressSpace from "../../../../components/progressSpace";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useDispatch } from "react-redux";
|
||||||
|
import { setCategoryId } from "../../../../redux/slices/categoryId";
|
||||||
|
|
||||||
const ModalConfigDetails = ({
|
const ModalConfigDetails = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
@@ -16,7 +18,11 @@ const ModalConfigDetails = ({
|
|||||||
isShowTraffic,
|
isShowTraffic,
|
||||||
textCopy,
|
textCopy,
|
||||||
maxTraffic,
|
maxTraffic,
|
||||||
traffic
|
traffic,
|
||||||
|
type,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
categoryId,
|
||||||
}: {
|
}: {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
@@ -27,17 +33,27 @@ const ModalConfigDetails = ({
|
|||||||
textCopy: string;
|
textCopy: string;
|
||||||
maxTraffic: number | "unlimited";
|
maxTraffic: number | "unlimited";
|
||||||
traffic: number;
|
traffic: number;
|
||||||
|
type: string;
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
categoryId: number;
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate();
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [copyText, setCopyText] = useState<string>("");
|
||||||
const copyToClipboard = async (textToCopy: string) => {
|
const copyToClipboard = async (textToCopy: string) => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(textToCopy);
|
await navigator.clipboard.writeText(textToCopy);
|
||||||
toast.success("متن با موفقیت کپی شد!");
|
toast.success("متن با موفقیت کپی شد!");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toast.error(`Failed to copy: , ${err}`);
|
try {
|
||||||
|
await navigator.clipboard.writeText(copyText);
|
||||||
|
toast.success("متن با موفقیت کپی شد!");
|
||||||
|
} catch (err) {
|
||||||
|
toast.error(`Failed to copy: , ${err}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MuiModal
|
<MuiModal
|
||||||
isOpenModal={isOpen}
|
isOpenModal={isOpen}
|
||||||
@@ -49,20 +65,59 @@ const ModalConfigDetails = ({
|
|||||||
<h1 className="w-full h-full text-center rounded-md p-1 md:p-2 bg-gray-800">
|
<h1 className="w-full h-full text-center rounded-md p-1 md:p-2 bg-gray-800">
|
||||||
تاریخ خرید : {dateBuy}
|
تاریخ خرید : {dateBuy}
|
||||||
</h1>
|
</h1>
|
||||||
<div className="w-full h-full flex flex-row-reverse gap-2 justify-center items-center">
|
<div className="w-full h-full flex flex-row-reverse gap-2 flex-wrap justify-center items-center">
|
||||||
{isDisabled ? (
|
{isDisabled ? (
|
||||||
<div className="w-full h-full grid grid-cols-1 justify-center items-center">
|
<div className="w-full h-full flex flex-wrap justify-center items-center">
|
||||||
<Button extra="!m-0 !px-2 !py-1 shadow-md 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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Button onClick={() => copyToClipboard(textCopy)} extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white">
|
{type === "gaming" ? (
|
||||||
کپی
|
<>
|
||||||
<IoCopy />
|
<Button
|
||||||
</Button>
|
onClick={() => {
|
||||||
<Button onClick={()=> navigate('/tutorial')} extra="!m-0 !px-2 !py-1.5 shadow-md shadow-white">
|
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>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<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={() => {
|
||||||
|
if (categoryId) {
|
||||||
|
dispatch(setCategoryId(categoryId));
|
||||||
|
navigate("/tutorial");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
extra="!m-0 !px-2 !py-1.5 shadow-md shadow-white"
|
||||||
|
>
|
||||||
آموزش
|
آموزش
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ import PulsatingCircle from "../../../../components/pulsatingCircle";
|
|||||||
|
|
||||||
const BestQuality = () => {
|
const BestQuality = () => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full mt-4 md:mt-10 mb-10 flex gap-10 flex-col justify-center items-center">
|
<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">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
<PulsatingCircle />
|
<PulsatingCircle />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-full flex flex-col gap-2 md:gap-4 justify-center items-center">
|
<div className="w-full h-full flex flex-col gap-2 md:gap-4 justify-center items-center">
|
||||||
<h1 className="max-md:text-xl md:text-4xl font-bold">
|
<h1 className="text-base md:text-3xl lg:text-4xl font-bold">
|
||||||
به عنوان{" "}
|
به عنوان{" "}
|
||||||
<span className="max-md:text-xl md:text-5xl text-secondTheme font-bold">
|
<span className="text-base md:text-3xl lg:text-4xl text-secondTheme font-bold">
|
||||||
اولین و بهترین
|
اولین و بهترین
|
||||||
</span>{" "}
|
</span>{" "}
|
||||||
ارائه دهنده{" "}
|
ارائه دهنده{" "}
|
||||||
</h1>
|
</h1>
|
||||||
<h1 className="relative max-md:right-10 max-lg:right-20 lg:right-40 max-md:text-xl md:text-4xl font-bold">
|
<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>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import Typewriter from "../../../../components/typeWriter";
|
|||||||
|
|
||||||
const FastSell = () => {
|
const FastSell = () => {
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full md:mt-14 mb-8 flex justify-center items-center">
|
<div className="relative w-full h-full mb-6 flex justify-center items-center">
|
||||||
<div className="w-full h-full flex flex-col gap-5 justify-center items-center">
|
<div className="w-full h-full flex flex-col justify-center items-center">
|
||||||
<LoaderSpin>
|
<LoaderSpin>
|
||||||
<FaPowerOff className="max-md:text-6xl text-8xl text-white" />
|
<FaPowerOff className="max-md:text-6xl text-8xl text-white" />
|
||||||
</LoaderSpin>
|
</LoaderSpin>
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import SmoothScrollLink from "../../../../components/scrollLink";
|
|||||||
|
|
||||||
const Intro = () => {
|
const Intro = () => {
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full md:pt-20 mb-6 md:mb-10 gap-6 grid grid-cols-1 py-8 md:grid-cols-2">
|
<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">
|
<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">
|
<h1 className="text-start flex flex-col gap-3 md:gap-4 text-3xl md:text-4xl lg:text-5xl">
|
||||||
<span className="inline-block">بزرگترین فروشگاه</span>
|
<span className="inline-block text-center">بزرگترین فروشگاه</span>
|
||||||
<span className="text-secondTheme font-bold max-md:text-center">
|
<span className="text-secondTheme font-bold max-md:text-center">
|
||||||
کاهش پینگ در ایران
|
کاهش پینگ در ایران
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import Content from "./components/Content";
|
|||||||
|
|
||||||
const NowSell = () => {
|
const NowSell = () => {
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full mt-16 p-5">
|
<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">
|
<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 />
|
<Content />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import ButtonList from "./components/ButtonList";
|
import ButtonList from "./components/ButtonList";
|
||||||
import ListAllProduct from "./components/ListAllProduct";
|
import ListAllProduct from "./components/ListAllProduct";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import useAxios from "../../../../hooks/useAxios";
|
import useAxios from "../../../../hooks/useAxios";
|
||||||
|
import { RootState } from "../../../../redux/store";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
|
||||||
const ProductList = () => {
|
const ProductList = () => {
|
||||||
const [activeId, setActiveId] = useState(1);
|
const { id } = useSelector((state: RootState) => state.configIdState);
|
||||||
|
const [activeId, setActiveId] = useState(3);
|
||||||
const axios = useAxios();
|
const axios = useAxios();
|
||||||
const { data, error, isLoading, isFetching } = useQuery({
|
const { data, error, isLoading, isFetching } = useQuery({
|
||||||
queryKey: ["categories"],
|
queryKey: ["categories"],
|
||||||
@@ -13,12 +16,15 @@ const ProductList = () => {
|
|||||||
return axios.get("/home/categories");
|
return axios.get("/home/categories");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
if (id !== "") setActiveId(Number(id));
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full max-md:mt-4 md:mt-8 " id="configs">
|
<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">
|
<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 className="max-md:text-sm text-center text-2xl font-bold">
|
||||||
برای انتخاب سرویس مورد نظر کلیک کنید تا براتون نمایش داده
|
برای انتخاب سرویس مورد نظر کلیک کنید تا براتون نمایش داده شود
|
||||||
شود
|
|
||||||
</h1>
|
</h1>
|
||||||
<ButtonList
|
<ButtonList
|
||||||
ProductsList={data?.data?.result}
|
ProductsList={data?.data?.result}
|
||||||
|
|||||||
@@ -14,11 +14,11 @@ const ButtonList: React.FC<ButtonListProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
if (isLoading || isFetching) {
|
if (isLoading || isFetching) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full gap-4 flex flex-wrap justify-center max-sm:grid max-sm:grid-cols-1">
|
<div className="w-full h-full gap-4 flex flex-wrap justify-center items-center">
|
||||||
{Array(3)
|
{Array(3)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map((_, i) => (
|
.map((_, i) => (
|
||||||
<Button extra="py-2 text-lg flex justify-center" key={i}>
|
<Button extra="py-2 text-lg flex justify-center items-center" key={i}>
|
||||||
<Skeleton animation="wave" variant="text">
|
<Skeleton animation="wave" variant="text">
|
||||||
<Skeleton animation="pulse" width={100} variant="text" />
|
<Skeleton animation="pulse" width={100} variant="text" />
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
@@ -36,12 +36,12 @@ const ButtonList: React.FC<ButtonListProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full gap-4 flex flex-wrap justify-center max-sm:grid max-sm:grid-cols-1">
|
<div className="w-full h-full gap-4 flex flex-wrap flex-row-reverse justify-center">
|
||||||
{ProductsList?.map((product) => (
|
{ProductsList?.map((product) => (
|
||||||
<Button
|
<Button
|
||||||
key={product.id}
|
key={product.id}
|
||||||
active={product.id === activeId}
|
active={product.id === activeId}
|
||||||
extra="py-2 text-lg"
|
extra="md:py-2 md:text-lg max-sm:!px-1"
|
||||||
name={product.label || product.name}
|
name={product.label || product.name}
|
||||||
onClick={() => setActiveId(product.id)}
|
onClick={() => setActiveId(product.id)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ModalBuy isOpen={isOpen} setIsOpen={setIsOpen} configId={configId} />
|
<ModalBuy isOpen={isOpen} setIsOpen={setIsOpen} configId={configId} />
|
||||||
<div className="w-full h-full border-4 gap-5 border-spacing-1 shadow-lg shadow-secondTheme border-solid border-secondTheme rounded-lg p-3 grid max-sm:grid-cols-1 max-md:grid-cols-2 max-lg:grid-cols-3 lg:grid-cols-4">
|
<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">
|
||||||
{data?.data?.result?.map((item: Record<string, string>) => {
|
{data?.data?.result?.map((item: Record<string, string>) => {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col" key={item.id}>
|
<div className="w-full h-full flex flex-col" key={item.id}>
|
||||||
@@ -75,7 +75,7 @@ const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
|||||||
<div className="w-full h-full flex justify-center items-center">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
{token ? (
|
{token ? (
|
||||||
<Button
|
<Button
|
||||||
extra="!w-4/5 !bg-secondTheme"
|
extra="!w-4/5"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
setConfigId(Number(item.id));
|
setConfigId(Number(item.id));
|
||||||
@@ -85,13 +85,13 @@ const ListAllProduct = ({ activeId }: { activeId: number }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button
|
<Button
|
||||||
extra="!w-4/5 !bg-secondTheme"
|
extra="!w-4/5"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toast.error("برای خرید باید وارد شوید");
|
toast.error("برای خرید باید وارد شوید");
|
||||||
navigate("/login");
|
navigate("/login");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
ورود
|
خرید
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -159,6 +159,7 @@ const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
|||||||
setIsOpenModal={setIsOpen}
|
setIsOpenModal={setIsOpen}
|
||||||
submitText="خرید"
|
submitText="خرید"
|
||||||
submitOnClick={submitHandler}
|
submitOnClick={submitHandler}
|
||||||
|
disabled={isLoading || isFetching}
|
||||||
nameService={
|
nameService={
|
||||||
isLoading || isFetching
|
isLoading || isFetching
|
||||||
? "درحال بارگذاری..."
|
? "درحال بارگذاری..."
|
||||||
@@ -211,7 +212,7 @@ const ModalBuy = ({ isOpen, setIsOpen, configId }: ModalBuyProps) => {
|
|||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</h1>
|
</h1>
|
||||||
<h3 className="text-xs md:text-sm text-gray-700">
|
<h3 className="text-xs md:text-sm text-gray-400">
|
||||||
در صورت نداشتن کد تخفیف پرداخت رو بزنید
|
در صورت نداشتن کد تخفیف پرداخت رو بزنید
|
||||||
</h3>
|
</h3>
|
||||||
<form
|
<form
|
||||||
|
|||||||
@@ -1,18 +1,27 @@
|
|||||||
import { useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import Learn from "./components/Leran";
|
import Learn from "./components/Leran";
|
||||||
import ListTutorials from "./components/ListTutorial";
|
import ListTutorials from "./components/ListTutorial";
|
||||||
import useAxios from "../../../hooks/useAxios";
|
import useAxios from "../../../hooks/useAxios";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { useSelector } from "react-redux";
|
||||||
|
import { RootState } from "../../../redux/store";
|
||||||
|
|
||||||
const Tutorials = () => {
|
const Tutorials = () => {
|
||||||
const axios = useAxios()
|
const axios = useAxios();
|
||||||
const {data,error,isLoading,isFetching} = useQuery({
|
const { data, error, isLoading, isFetching } = useQuery({
|
||||||
queryKey:["categoryLearning"],
|
queryKey: ["categoryLearning"],
|
||||||
queryFn:()=>{
|
queryFn: () => {
|
||||||
return axios.get('/home/category/learning')
|
return axios.get("/home/category/learning");
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
const [activeTutorial, setActiveTutorial] = useState<number>(1);
|
const { id } = useSelector((state: RootState) => state.categoryIdState);
|
||||||
|
|
||||||
|
const [activeTutorial, setActiveTutorial] = useState<number>(2);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id !== 0) setActiveTutorial(id);
|
||||||
|
}, [id]);
|
||||||
|
useEffect(() => {}, []);
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full h-full flex flex-col px-4 py-4 text-start">
|
<div className="relative w-full h-full flex flex-col px-4 py-4 text-start">
|
||||||
<h1 className="font-bold text-xl md:text-2xl text-center">آموزش</h1>
|
<h1 className="font-bold text-xl md:text-2xl text-center">آموزش</h1>
|
||||||
@@ -27,7 +36,10 @@ const Tutorials = () => {
|
|||||||
error={error}
|
error={error}
|
||||||
setActiveTutorial={setActiveTutorial}
|
setActiveTutorial={setActiveTutorial}
|
||||||
/>
|
/>
|
||||||
<Learn activeTutorial={activeTutorial} tutorialList={data?.data?.result} />
|
<Learn
|
||||||
|
activeTutorial={activeTutorial}
|
||||||
|
tutorialList={data?.data?.result}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,19 @@ import { MdErrorOutline } from "react-icons/md";
|
|||||||
import Button from "../../../../components/button";
|
import Button from "../../../../components/button";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
|
|
||||||
|
const getMediaType = (url: string) => {
|
||||||
|
const extension = url.split(".").pop()?.toLowerCase();
|
||||||
|
if (extension) {
|
||||||
|
if (["jpg", "jpeg", "png", "gif"].includes(extension)) {
|
||||||
|
return "image";
|
||||||
|
}
|
||||||
|
if (["mp4", "webm", "ogg"].includes(extension)) {
|
||||||
|
return "video";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
};
|
||||||
|
|
||||||
const Learn = ({ activeTutorial }: LearnProps) => {
|
const Learn = ({ activeTutorial }: LearnProps) => {
|
||||||
const axios = useAxios();
|
const axios = useAxios();
|
||||||
const { data, isLoading, isFetching, error } = useQuery({
|
const { data, isLoading, isFetching, error } = useQuery({
|
||||||
@@ -86,14 +99,31 @@ const Learn = ({ activeTutorial }: LearnProps) => {
|
|||||||
{i.title}
|
{i.title}
|
||||||
</span>
|
</span>
|
||||||
<div className="relative w-full h-full flex flex-col justify-center items-center">
|
<div className="relative w-full h-full flex flex-col justify-center items-center">
|
||||||
{i.medias.map((media, index) => (
|
{i.medias.map((media, index) => {
|
||||||
<img
|
const mediaType = getMediaType(media);
|
||||||
className="w-full md:w-1/2 mb-3 rounded-lg mx-auto h-full"
|
return (
|
||||||
src={media}
|
<div
|
||||||
alt="images"
|
key={index}
|
||||||
key={index}
|
className="w-full md:w-1/2 mb-3 rounded-lg mx-auto h-full"
|
||||||
/>
|
>
|
||||||
))}
|
{mediaType === "image" ? (
|
||||||
|
<img
|
||||||
|
src={media}
|
||||||
|
alt="media"
|
||||||
|
className="w-full h-full object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
) : mediaType === "video" ? (
|
||||||
|
<video
|
||||||
|
src={media}
|
||||||
|
controls
|
||||||
|
className="w-full h-full object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<p>نوع فایل ناشناخته است</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,16 +13,16 @@ const ListTutorials = ({
|
|||||||
}: ListTutorialsProps) => {
|
}: ListTutorialsProps) => {
|
||||||
if (isLoading || isFetching) {
|
if (isLoading || isFetching) {
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full gap-4 flex flex-wrap justify-center max-sm:grid max-sm:grid-cols-1 mt-4">
|
<div className="w-full h-full gap-4 flex flex-wrap justify-center mt-4">
|
||||||
{Array(3)
|
{Array(3)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map((_, i) => (
|
.map((_, i) => (
|
||||||
<div
|
<div
|
||||||
className="flex flex-wrap max-sm:grid max-sm:grid-cols-1 gap-2 justify-center items-center"
|
className="flex flex-wrap flex-1 gap-2 justify-center items-center"
|
||||||
key={i}
|
key={i}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
extra={`!h-auto !text-center !text-sm md:!text-lg !bg-gray-800 !rounded-md !px-2 !py-2 !cursor-pointer !flex justify-center`}
|
extra={`!h-auto !text-center flex-1 !text-sm md:!text-lg !bg-gray-800 !rounded-md !px-2 !py-2 !cursor-pointer !flex justify-center`}
|
||||||
>
|
>
|
||||||
<Skeleton animation="pulse" width={150} variant="text" />
|
<Skeleton animation="pulse" width={150} variant="text" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
23
src/redux/slices/categoryId.ts
Normal file
23
src/redux/slices/categoryId.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
interface categoryIdState {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: categoryIdState = {
|
||||||
|
id: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const categoryIdSlice = createSlice({
|
||||||
|
name: "categoryId",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setCategoryId: (state, action: PayloadAction<number>) => {
|
||||||
|
state.id = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setCategoryId } = categoryIdSlice.actions;
|
||||||
|
|
||||||
|
export default categoryIdSlice.reducer;
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { Navigate, Outlet } from "react-router-dom";
|
import { Navigate, Outlet } from "react-router-dom";
|
||||||
import { isAuthenticated } from "../utils/auth";
|
import { IsAuthenticated } from "../utils/auth";
|
||||||
|
|
||||||
const PrivateRoute = () => {
|
const PrivateRoute = () => {
|
||||||
return isAuthenticated() ? <Outlet /> : <Navigate to="/login" />;
|
return IsAuthenticated() ? <Outlet /> : <Navigate to="/login" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PrivateRoute;
|
export default PrivateRoute;
|
||||||
|
|||||||
11
src/types/props.d.ts
vendored
11
src/types/props.d.ts
vendored
@@ -35,9 +35,6 @@ export interface MobileMenuButtonProps {
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin NavbarLink.tsx
|
|
||||||
import { NavLinkProps } from "react-router-dom";
|
|
||||||
|
|
||||||
export interface NavbarLinkProps {
|
export interface NavbarLinkProps {
|
||||||
to: string;
|
to: string;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -52,7 +49,6 @@ export interface NavData {
|
|||||||
// begin ListTutorial.tsx
|
// begin ListTutorial.tsx
|
||||||
import React, {
|
import React, {
|
||||||
ButtonHTMLAttributes,
|
ButtonHTMLAttributes,
|
||||||
Component,
|
|
||||||
Dispatch,
|
Dispatch,
|
||||||
InputHTMLAttributes,
|
InputHTMLAttributes,
|
||||||
ReactNode,
|
ReactNode,
|
||||||
@@ -130,6 +126,8 @@ export interface ConfigItemProps {
|
|||||||
textCopy: string;
|
textCopy: string;
|
||||||
maxTraffic: number | "unlimited";
|
maxTraffic: number | "unlimited";
|
||||||
traffic: number;
|
traffic: number;
|
||||||
|
type: string;
|
||||||
|
categoryId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// نوع Item برای آیتمهای داخل Dropdown
|
// نوع Item برای آیتمهای داخل Dropdown
|
||||||
@@ -203,6 +201,8 @@ type Config = {
|
|||||||
show_traffic: boolean;
|
show_traffic: boolean;
|
||||||
max_traffic: number;
|
max_traffic: number;
|
||||||
traffic: number;
|
traffic: number;
|
||||||
|
type: string;
|
||||||
|
category_id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// تعریف تایپ دادههای برگشتی
|
// تعریف تایپ دادههای برگشتی
|
||||||
@@ -225,7 +225,8 @@ interface ProfileDetailsProps {
|
|||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
isFetching: boolean;
|
isFetching: boolean;
|
||||||
error: Error | AxiosError | null;
|
error: Error | AxiosError | null;
|
||||||
userTitle: string;
|
userTitle?: string;
|
||||||
|
categoryId?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScrollLinkProps {
|
interface ScrollLinkProps {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../redux/store";
|
import { RootState } from "../redux/store";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
const isTokenValid = (token: string): boolean => {
|
const isTokenValid = (token: string): boolean => {
|
||||||
try {
|
try {
|
||||||
@@ -8,11 +9,12 @@ const isTokenValid = (token: string): boolean => {
|
|||||||
return !!token;
|
return !!token;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Token validation error:", error);
|
console.error("Token validation error:", error);
|
||||||
|
toast.error(`Token validation error:${error}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isAuthenticated = () : boolean => {
|
export const IsAuthenticated = (): boolean => {
|
||||||
const {token} = useSelector((state : RootState) => state.token)
|
const { token } = useSelector((state: RootState) => state.token);
|
||||||
return isTokenValid(token);
|
return isTokenValid(token);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user