feat : dynamic profile api and add logout btn
This commit is contained in:
@@ -1,10 +1,34 @@
|
||||
import { LoaderIcon } from "react-hot-toast";
|
||||
import Alert from "../../../components/alert/Alert";
|
||||
import Button from "../../../components/button";
|
||||
import { ProfileDetailsProps } from "../../../types/props";
|
||||
import ConfigItem from "./components/ConfigItem";
|
||||
import ConfigItemLoading from "./components/ConfigItemLoading";
|
||||
import ProfileImage from "./components/ProfileImage";
|
||||
import { Fragment } from "react/jsx-runtime";
|
||||
|
||||
const ProductConfigList = () => {
|
||||
const ProductConfigList = ({
|
||||
data,
|
||||
isLoading,
|
||||
isFetching,
|
||||
error,
|
||||
userTitle,
|
||||
}: ProfileDetailsProps) => {
|
||||
return (
|
||||
<div className="w-full h-full p-5 glass flex flex-col gap-3">
|
||||
<ProfileImage />
|
||||
<div className="w-full h-full flex justify-center items-center">
|
||||
<Button extra="!py-3 max-md:!py-1 max-md:px-2 !bg-gray-800 !text-white">
|
||||
{isLoading || isFetching ? (
|
||||
<LoaderIcon className="m-1.5" />
|
||||
) : error ? (
|
||||
"error"
|
||||
) : (
|
||||
userTitle
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<p className="max-md:text-lg text-xl font-bold text-center">
|
||||
لیست کانفیگ ها
|
||||
</p>
|
||||
@@ -12,21 +36,33 @@ const ProductConfigList = () => {
|
||||
className="w-full h-full p-3 flex flex-col gap-3
|
||||
justify-center items-center"
|
||||
>
|
||||
<ConfigItem
|
||||
configName="سرویس تست 1 روزه"
|
||||
dateBuy="1402/2/3"
|
||||
isDisabled={false}
|
||||
/>
|
||||
<ConfigItem
|
||||
configName="سرویس مولتی 1"
|
||||
dateBuy="1403/2/3"
|
||||
isDisabled={false}
|
||||
/>
|
||||
<ConfigItem
|
||||
configName="سرویس کاهش پینگ"
|
||||
dateBuy="1402/4/3"
|
||||
isDisabled={true}
|
||||
/>
|
||||
{isLoading || isFetching ? (
|
||||
Array(3)
|
||||
.fill(null)
|
||||
.map((_, i) => (
|
||||
<Fragment key={i}>
|
||||
<ConfigItemLoading />
|
||||
</Fragment>
|
||||
))
|
||||
) : error ? (
|
||||
<Alert type="danger" message="خطایی با این موضوع رخ داد: ">
|
||||
{error.message}
|
||||
</Alert>
|
||||
) : (
|
||||
data?.result?.configs?.map((item) => {
|
||||
return (
|
||||
<Fragment key={item.id}>
|
||||
<ConfigItem
|
||||
configName={item.title}
|
||||
dateBuy={item.created_at || "تاریخ نامعتبر"}
|
||||
isDisabled={false}
|
||||
isShowTraffic={item.show_traffic}
|
||||
textCopy={item.url}
|
||||
/>
|
||||
</Fragment>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,27 @@
|
||||
import { MdEdit } from "react-icons/md";
|
||||
import { MdLogout } from "react-icons/md";
|
||||
import Button from "../../../components/button";
|
||||
import ModalEditProfile from "./components/ModalEditProfile";
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { setToken } from "../../../redux/slices/token";
|
||||
import { AxiosError } from "axios";
|
||||
import { LoaderIcon } from "react-hot-toast";
|
||||
import { ProfileDetailsProps } from "../../../types/props";
|
||||
|
||||
const ProfileDetails = () => {
|
||||
const ProfileDetails = ({
|
||||
data,
|
||||
isLoading,
|
||||
isFetching,
|
||||
error,
|
||||
}: ProfileDetailsProps) => {
|
||||
return (
|
||||
<div className="relative z-[10] w-full h-32 max-md:h-20 flex flex-row-reverse justify-between glass items-center py-2 px-4">
|
||||
<EditProfile />
|
||||
<ProductDetails />
|
||||
<ProductDetails
|
||||
enable_config={data?.result?.enable_config}
|
||||
disable_config={data?.result?.disable_config}
|
||||
isLoading={isLoading}
|
||||
isFetching={isFetching}
|
||||
error={error}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -15,32 +29,67 @@ const ProfileDetails = () => {
|
||||
export default ProfileDetails;
|
||||
|
||||
const EditProfile = () => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
// const [isOpen, setIsOpen] = useState(false);
|
||||
const dispatch = useDispatch();
|
||||
return (
|
||||
<div className="w-full h-full flex justify-end items-center">
|
||||
<ModalEditProfile isOpen={isOpen} setIsOpen={setIsOpen} />
|
||||
<Button
|
||||
<div className="w-full h-full flex justify-end items-center gap-2">
|
||||
{/* <ModalEditProfile isOpen={isOpen} setIsOpen={setIsOpen} /> */}
|
||||
{/* <Button
|
||||
extra="!py-4 max-md:!py-2 max-md:px-2"
|
||||
onClick={() => setIsOpen(true)}
|
||||
>
|
||||
<MdEdit className="scale-[1.4] max-md:scale-100" />
|
||||
</Button> */}
|
||||
<Button
|
||||
extra="!py-4 max-md:!py-2 max-md:px-2 !bg-rose-600 !text-white"
|
||||
onClick={() => dispatch(setToken(""))}
|
||||
>
|
||||
<MdLogout className="scale-[1.4] max-md:scale-100" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ProductDetails = () => {
|
||||
const ProductDetails = ({
|
||||
enable_config,
|
||||
disable_config,
|
||||
isLoading,
|
||||
isFetching,
|
||||
error,
|
||||
}: {
|
||||
enable_config: number;
|
||||
disable_config: number;
|
||||
isLoading: boolean;
|
||||
isFetching: boolean;
|
||||
error: Error | AxiosError | null;
|
||||
}) => {
|
||||
return (
|
||||
<div className="w-full h-full flex justify-start items-center">
|
||||
<div className="md:mr-4 w-auto h-auto flex flex-col max-md:text-[12px] text-lg md:gap-2">
|
||||
<p className="text-nowrap">
|
||||
<div className="text-nowrap">
|
||||
کانفیگ های فعال :{" "}
|
||||
<span className="px-1 md:px-2 rounded-md bg-green-600"> 2 </span>
|
||||
</p>
|
||||
<p className="text-nowrap">
|
||||
<span className="px-1 md:px-2 rounded-md bg-green-600">
|
||||
{isLoading || isFetching ? (
|
||||
<LoaderIcon className="inline-block" />
|
||||
) : error ? (
|
||||
"error"
|
||||
) : (
|
||||
enable_config
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-nowrap">
|
||||
کانفیگ های غیر فعال :{" "}
|
||||
<span className="px-1 md:px-2 rounded-md bg-yellow-400"> 10 </span>
|
||||
</p>
|
||||
<span className="px-1 md:px-2 rounded-md bg-yellow-400">
|
||||
{isLoading || isFetching ? (
|
||||
<LoaderIcon className="inline-block" />
|
||||
) : error ? (
|
||||
"error"
|
||||
) : (
|
||||
disable_config
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,13 +5,30 @@ import ModalConfigDetails from "./ModalConfigDetails";
|
||||
import { useState } from "react";
|
||||
import Dropdown, { DropdownItem } from "../../../../components/dropdown";
|
||||
import ProgressSpace from "../../../../components/progressSpace";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const ConfigItem = ({ dateBuy, configName, isDisabled }: ConfigItemProps) => {
|
||||
const ConfigItem = ({
|
||||
dateBuy,
|
||||
configName,
|
||||
isDisabled,
|
||||
isShowTraffic,
|
||||
textCopy,
|
||||
}: ConfigItemProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isOpenDropDown, setIsOpenDropDown] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const handleToggle = () => {
|
||||
setIsOpenDropDown(!isOpen);
|
||||
};
|
||||
const copyToClipboard = async (textToCopy: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -21,11 +38,13 @@ const ConfigItem = ({ dateBuy, configName, isDisabled }: ConfigItemProps) => {
|
||||
dateBuy={dateBuy}
|
||||
isDisabled={isDisabled}
|
||||
nameConfig={configName}
|
||||
isShowTraffic={isShowTraffic}
|
||||
textCopy={textCopy}
|
||||
/>
|
||||
<div className="relative w-full h-full rounded-md">
|
||||
{isDisabled && (
|
||||
<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 p-1 bg-red-600 text-xs md:text-base rounded-md shadow-md">
|
||||
<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">
|
||||
کانفیگ <span className="text-secondTheme">{configName}</span> غیر
|
||||
فعال است .{" "}
|
||||
<Button
|
||||
@@ -47,27 +66,35 @@ const ConfigItem = ({ dateBuy, configName, isDisabled }: ConfigItemProps) => {
|
||||
{configName}
|
||||
</li>
|
||||
<div className="max-md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
<Button extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white">
|
||||
<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"
|
||||
>
|
||||
کپی
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button extra="!m-0 !px-2 !py-1.5 shadow-md">
|
||||
<Button
|
||||
extra="!m-0 !px-2 !py-1.5 shadow-md"
|
||||
onClick={() => navigate("/tutorial")}
|
||||
>
|
||||
آموزش
|
||||
</Button>
|
||||
<Dropdown isOpen={isOpenDropDown} setIsOpen={setIsOpenDropDown}>
|
||||
<Button
|
||||
id="dropdownDefaultButton"
|
||||
data-dropdown-toggle="dropdown"
|
||||
type="button"
|
||||
onClick={handleToggle}
|
||||
extra="!m-0 !px-2 !py-1.5 shadow-md"
|
||||
>
|
||||
حجم
|
||||
</Button>
|
||||
<DropdownItem widthTailwind="w-[20rem] left-0">
|
||||
<ProgressSpace startSpace={100} endSpace={200} />
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
{isShowTraffic && (
|
||||
<Dropdown isOpen={isOpenDropDown} setIsOpen={setIsOpenDropDown}>
|
||||
<Button
|
||||
id="dropdownDefaultButton"
|
||||
data-dropdown-toggle="dropdown"
|
||||
type="button"
|
||||
onClick={handleToggle}
|
||||
extra="!m-0 !px-2 !py-1.5 shadow-md"
|
||||
>
|
||||
حجم
|
||||
</Button>
|
||||
<DropdownItem widthTailwind="w-[20rem] left-0">
|
||||
<ProgressSpace startSpace={100} endSpace={200} />
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
)}
|
||||
</div>
|
||||
<div className="md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
<Button
|
||||
|
||||
25
src/pages/profile/section/components/ConfigItemLoading.tsx
Normal file
25
src/pages/profile/section/components/ConfigItemLoading.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Skeleton } from "@mui/material";
|
||||
|
||||
const ConfigItemLoading = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="relative w-full h-full rounded-md">
|
||||
<div
|
||||
className={`w-full h-full p-3 rounded-md text-xs md:text-base gap-2 flex justify-between items-center text-secondTheme bg-gray-900 shadow-md shadow-gray-900`}
|
||||
>
|
||||
<Skeleton variant="rounded" animation="wave" width={100} />
|
||||
<li className="px-2 py-1 shadow-md rounded-md w-auto h-auto text-gray-900 font-bold list-none">
|
||||
<Skeleton variant="rounded" animation="wave" width={100} />
|
||||
</li>
|
||||
<div className="max-md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
<Skeleton variant="rounded" animation="wave" width={100} />
|
||||
</div>
|
||||
<div className="md:hidden flex flex-row-reverse gap-2 justify-end items-center">
|
||||
<Skeleton variant="rounded" animation="wave" width={100} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default ConfigItemLoading;
|
||||
@@ -4,6 +4,8 @@ import Button from "../../../../components/button";
|
||||
import { IoCopy } from "react-icons/io5";
|
||||
import Alert from "../../../../components/alert/Alert";
|
||||
import ProgressSpace from "../../../../components/progressSpace";
|
||||
import toast from "react-hot-toast";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const ModalConfigDetails = ({
|
||||
isOpen,
|
||||
@@ -11,13 +13,27 @@ const ModalConfigDetails = ({
|
||||
isDisabled,
|
||||
dateBuy,
|
||||
nameConfig,
|
||||
isShowTraffic,
|
||||
textCopy
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
isDisabled: boolean;
|
||||
dateBuy: string;
|
||||
nameConfig: string;
|
||||
isShowTraffic: boolean;
|
||||
textCopy: string;
|
||||
}) => {
|
||||
const navigate = useNavigate()
|
||||
const copyToClipboard = async (textToCopy: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
toast.success("متن با موفقیت کپی شد!");
|
||||
} catch (err) {
|
||||
toast.error(`Failed to copy: , ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<MuiModal
|
||||
isOpenModal={isOpen}
|
||||
@@ -38,17 +54,17 @@ const ModalConfigDetails = ({
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button extra="!m-0 !px-2 !py-1.5 flex flex-row-reverse justify-center items-center gap-1 shadow-md shadow-white">
|
||||
<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">
|
||||
کپی
|
||||
<IoCopy />
|
||||
</Button>
|
||||
<Button extra="!m-0 !px-2 !py-1.5 shadow-md shadow-white">
|
||||
<Button onClick={()=> navigate('/tutorial')} extra="!m-0 !px-2 !py-1.5 shadow-md shadow-white">
|
||||
آموزش
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!isDisabled && (
|
||||
{!isDisabled || isShowTraffic && (
|
||||
<div className="w-full h-full">
|
||||
<ProgressSpace startSpace={100} endSpace={200} />
|
||||
</div>
|
||||
|
||||
50
src/pages/profile/section/components/ModalPayment.tsx
Normal file
50
src/pages/profile/section/components/ModalPayment.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
import MuiModal from "../../../../common/modal";
|
||||
import { MdError, MdVerified } from "react-icons/md";
|
||||
|
||||
const ModalPayment = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
isPayment,
|
||||
message,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
isPayment: boolean;
|
||||
message: string;
|
||||
}) => {
|
||||
console.log(isPayment);
|
||||
|
||||
return (
|
||||
<MuiModal isOpenModal={isOpen} setIsOpenModal={setIsOpen} title="وضعیت">
|
||||
<div className="relative w-full h-full px-4 py-2">
|
||||
<div className="w-full h-full flex flex-col gap-4 justify-center items-center">
|
||||
{isPayment ? (
|
||||
<>
|
||||
{" "}
|
||||
<MdVerified className="w-[6rem] h-[6rem] text-green-600" />
|
||||
<p className="text-base md:text-lg text-green-600">
|
||||
پرداخت موفقیت امیز بود {message}
|
||||
</p>
|
||||
<p className="text-xs md:text-sm">از اعتماد شما سپاسگذاریم</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{" "}
|
||||
<MdError className="w-[6rem] h-[6rem] text-rose-600" />
|
||||
<p className="text-base md:text-lg text-rose-600">
|
||||
پرداخت موفقیت امیز نبود {message}
|
||||
</p>
|
||||
<p className="text-xs md:text-sm">
|
||||
در صورت کسر وجه اگر تا 48 ساعت برگشت داده نشد با پشتیبانی تماس
|
||||
بگیرید
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</MuiModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalPayment;
|
||||
Reference in New Issue
Block a user