plan
This commit is contained in:
@@ -3,27 +3,20 @@ import { Helmet } from 'react-helmet-async'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import SidebarHint from './components/SidebarHint'
|
import SidebarHint from './components/SidebarHint'
|
||||||
import SupportImage from '../../assets/images/support.png'
|
import SupportImage from '../../assets/images/support.png'
|
||||||
import Button from '../../components/Button'
|
|
||||||
import { CloseCircle, Document, Element3, TickCircle } from 'iconsax-react'
|
import { CloseCircle, Document, Element3, TickCircle } from 'iconsax-react'
|
||||||
import Tabs from '../../components/Tabs'
|
import Tabs from '../../components/Tabs'
|
||||||
import { useBuySupportPlan, useGetSupportPlans } from './hooks/useSupportData'
|
import { useGetSupportPlans } from './hooks/useSupportData'
|
||||||
import { NumberFormat } from '../../config/func'
|
import { NumberFormat } from '../../config/func'
|
||||||
import { PlanItemType, featureItemType } from './types/SupportTypes'
|
import { PlanItemType, featureItemType } from './types/SupportTypes'
|
||||||
import SupportSkeleton from './components/SupportSkeleton'
|
import SupportSkeleton from './components/SupportSkeleton'
|
||||||
import SupportIcon from './components/SupportIcon'
|
import SupportIcon from './components/SupportIcon'
|
||||||
import { SupportPlanFeatureKey } from './enum/SupportEnum'
|
import { SupportPlanFeatureKey } from './enum/SupportEnum'
|
||||||
import { toast } from '../../components/Toast'
|
import BuyPlan from './components/BuyPlan'
|
||||||
import { ErrorType } from '../../helpers/types'
|
|
||||||
import { useNavigate } from 'react-router-dom'
|
|
||||||
import { Pages } from '../../config/Pages'
|
|
||||||
|
|
||||||
const Support: FC = () => {
|
const Support: FC = () => {
|
||||||
const { data, isLoading } = useGetSupportPlans()
|
const { data, isLoading } = useGetSupportPlans()
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const [activeTab, setActiveTab] = useState<string>('')
|
const [activeTab, setActiveTab] = useState<string>('')
|
||||||
const [loadingPlanId, setLoadingPlanId] = useState<string>('')
|
|
||||||
const { mutate: buySupportPlan } = useBuySupportPlan()
|
|
||||||
const navigate = useNavigate()
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data?.data?.supportPlans?.length > 0 && !activeTab) {
|
if (data?.data?.supportPlans?.length > 0 && !activeTab) {
|
||||||
@@ -31,20 +24,6 @@ const Support: FC = () => {
|
|||||||
}
|
}
|
||||||
}, [data, activeTab])
|
}, [data, activeTab])
|
||||||
|
|
||||||
const handleBuy = (planId: string) => {
|
|
||||||
setLoadingPlanId(planId)
|
|
||||||
buySupportPlan(planId, {
|
|
||||||
onSuccess: (data) => {
|
|
||||||
toast(data?.data?.message, 'success')
|
|
||||||
navigate(Pages.receipts.detail + data?.data?.invoice?.id)
|
|
||||||
setLoadingPlanId('')
|
|
||||||
},
|
|
||||||
onError: (error: ErrorType) => {
|
|
||||||
toast(error?.response?.data?.error.message[0], 'error')
|
|
||||||
setLoadingPlanId('')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLoading || !data?.data) {
|
if (isLoading || !data?.data) {
|
||||||
return <SupportSkeleton />
|
return <SupportSkeleton />
|
||||||
@@ -92,17 +71,14 @@ const Support: FC = () => {
|
|||||||
ماهیانه
|
ماهیانه
|
||||||
</div>
|
</div>
|
||||||
<div className='font-bold text-sm mt-1'>
|
<div className='font-bold text-sm mt-1'>
|
||||||
{NumberFormat(item.price)}
|
{item.price === 0 ? 'رایگان' : NumberFormat(item.price)}
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-1 text-sm'>
|
<div className='mt-1 text-sm'>
|
||||||
تومان
|
تومان
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
<BuyPlan
|
||||||
label='ثبت پلن'
|
id={item.id}
|
||||||
className='mt-4 mx-auto w-fit px-5'
|
|
||||||
onClick={() => handleBuy(item.id)}
|
|
||||||
isLoading={loadingPlanId === item.id}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -163,11 +139,8 @@ const Support: FC = () => {
|
|||||||
<div className='text-sm text-[#595C67] mt-2'>ماهیانه</div>
|
<div className='text-sm text-[#595C67] mt-2'>ماهیانه</div>
|
||||||
<div className='font-bold text-sm mt-1'>{NumberFormat(plan.price)}</div>
|
<div className='font-bold text-sm mt-1'>{NumberFormat(plan.price)}</div>
|
||||||
<div className='mt-1 text-sm'>تومان</div>
|
<div className='mt-1 text-sm'>تومان</div>
|
||||||
<Button
|
<BuyPlan
|
||||||
label='ثبت پلن'
|
id={plan.id}
|
||||||
className='mt-4 w-fit px-5 mx-auto'
|
|
||||||
onClick={() => handleBuy(plan.id)}
|
|
||||||
isLoading={loadingPlanId === plan.id}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='space-y-4'>
|
<div className='space-y-4'>
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react'
|
||||||
|
import { useBuySupportPlan, useGetUserSupportPlan, useUpgradeSupportPlan } from '../hooks/useSupportData'
|
||||||
|
import { toast } from '../../../components/Toast'
|
||||||
|
import { Pages } from '../../../config/Pages'
|
||||||
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { ErrorType } from '../../../helpers/types'
|
||||||
|
import Button from '../../../components/Button'
|
||||||
|
import { clx } from '../../../helpers/utils'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BuyPlan: FC<Props> = ({ id }) => {
|
||||||
|
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [isActive, setIsActive] = useState(false)
|
||||||
|
const [isCanUpgrade, setIsCanUpgrade] = useState(false)
|
||||||
|
const { data: userSupportPlan } = useGetUserSupportPlan()
|
||||||
|
const { mutate: buySupportPlan, isPending } = useBuySupportPlan()
|
||||||
|
const { mutate: upgradeSupportPlan, isPending: isUpgradePending } = useUpgradeSupportPlan()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsActive(userSupportPlan?.data?.userSupportPlan?.supportPlan?.id === id)
|
||||||
|
setIsCanUpgrade(userSupportPlan?.data?.userSupportPlan ? true : false)
|
||||||
|
}, [userSupportPlan])
|
||||||
|
|
||||||
|
const handleUpgrade = () => {
|
||||||
|
upgradeSupportPlan(id, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
toast(data?.data?.message, 'success')
|
||||||
|
navigate(Pages.receipts.detail + data?.data?.invoice?.id)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast(error?.response?.data?.error.message[0], 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const handleBuy = (planId: string) => {
|
||||||
|
buySupportPlan(planId, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
toast(data?.data?.message, 'success')
|
||||||
|
navigate(Pages.receipts.detail + data?.data?.invoice?.id)
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast(error?.response?.data?.error.message[0], 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
label={isActive ? 'پلن فعال' : isCanUpgrade ? 'تغییر پلن' : 'خرید پلن'}
|
||||||
|
className={clx(
|
||||||
|
'mt-4 mx-auto w-fit px-5',
|
||||||
|
isActive && 'bg-[#01A560] text-white'
|
||||||
|
)}
|
||||||
|
onClick={() => isActive ? undefined : isCanUpgrade ? handleUpgrade() : handleBuy(id)}
|
||||||
|
isLoading={isPending || isUpgradePending}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BuyPlan
|
||||||
@@ -3,7 +3,7 @@ import { FC } from 'react'
|
|||||||
|
|
||||||
const SidebarHint: FC = () => {
|
const SidebarHint: FC = () => {
|
||||||
return (
|
return (
|
||||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
<div className='bg-white w-sidebar xl:hidden hidden py-10 px-5 h-fit rounded-3xl'>
|
||||||
<div className='text-sm'>
|
<div className='text-sm'>
|
||||||
جهت انتخاب بهتر به مسائل زیر توجه بفرمایید:
|
جهت انتخاب بهتر به مسائل زیر توجه بفرمایید:
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const SupportSkeleton: FC = () => {
|
|||||||
<div className='w-[160px] h-[160px] bg-gray-200 rounded-xl animate-pulse' />
|
<div className='w-[160px] h-[160px] bg-gray-200 rounded-xl animate-pulse' />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{[1, 2].map((item) => (
|
{[1, 2, 3].map((item) => (
|
||||||
<div key={item} className='flex-1 flex flex-col items-center'>
|
<div key={item} className='flex-1 flex flex-col items-center'>
|
||||||
<div className='text-center w-full'>
|
<div className='text-center w-full'>
|
||||||
<div className='h-6 w-32 bg-gray-200 rounded-lg animate-pulse mx-auto' />
|
<div className='h-6 w-32 bg-gray-200 rounded-lg animate-pulse mx-auto' />
|
||||||
@@ -35,7 +35,7 @@ const SupportSkeleton: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{[1, 2].map((item) => (
|
{[1, 2, 3].map((item) => (
|
||||||
<div key={item} className='flex-1 flex flex-col'>
|
<div key={item} className='flex-1 flex flex-col'>
|
||||||
{[1, 2, 3, 4, 5].map((feature) => (
|
{[1, 2, 3, 4, 5].map((feature) => (
|
||||||
<div key={feature} className='flex h-16 px-4 plan items-center justify-center'>
|
<div key={feature} className='flex h-16 px-4 plan items-center justify-center'>
|
||||||
@@ -71,20 +71,6 @@ const SupportSkeleton: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='xl:block hidden w-[300px]'>
|
|
||||||
<div className='bg-white p-6 rounded-3xl'>
|
|
||||||
<div className='h-6 w-32 bg-gray-200 rounded-lg animate-pulse mb-4' />
|
|
||||||
<div className='space-y-3'>
|
|
||||||
{[1, 2, 3].map((item) => (
|
|
||||||
<div key={item} className='flex items-center gap-2'>
|
|
||||||
<div className='size-6 bg-gray-200 rounded-full animate-pulse' />
|
|
||||||
<div className='h-4 w-40 bg-gray-200 rounded-lg animate-pulse' />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||||
import { getSupportPlans, buySupportPlan } from "../service/SupportService";
|
import {
|
||||||
|
getSupportPlans,
|
||||||
|
buySupportPlan,
|
||||||
|
getUserSupportPlan,
|
||||||
|
upgradeSupportPlan,
|
||||||
|
} from "../service/SupportService";
|
||||||
|
|
||||||
export const useGetSupportPlans = () => {
|
export const useGetSupportPlans = () => {
|
||||||
return useQuery({ queryKey: ["support-plans"], queryFn: getSupportPlans });
|
return useQuery({ queryKey: ["support-plans"], queryFn: getSupportPlans });
|
||||||
@@ -10,3 +15,16 @@ export const useBuySupportPlan = () => {
|
|||||||
mutationFn: (planId: string) => buySupportPlan(planId),
|
mutationFn: (planId: string) => buySupportPlan(planId),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetUserSupportPlan = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["user-support-plan"],
|
||||||
|
queryFn: getUserSupportPlan,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpgradeSupportPlan = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (id: string) => upgradeSupportPlan(id),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -9,3 +9,13 @@ export const buySupportPlan = async (planId: string) => {
|
|||||||
const { data } = await axios.post(`/support-plans/${planId}/subscribe`);
|
const { data } = await axios.post(`/support-plans/${planId}/subscribe`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUserSupportPlan = async () => {
|
||||||
|
const { data } = await axios.get(`/support-plans/user`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const upgradeSupportPlan = async (id: string) => {
|
||||||
|
const { data } = await axios.post(`/support-plans/${id}/upgrade`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user