support plan
This commit is contained in:
@@ -9,6 +9,8 @@ import PageLoading from '../../../components/PageLoading'
|
||||
import { useGetQuikAccess, useQuikAccess, useRemoveQuikAccess } from '../hooks/useHomeData'
|
||||
import ChangeQuikAccess from './ChangeQuikAccess'
|
||||
import { useHomeStore } from '../store/HomeStore'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import { toast } from '../../../components/Toast'
|
||||
|
||||
const BoxNewAccessbility: FC = () => {
|
||||
|
||||
@@ -25,12 +27,18 @@ const BoxNewAccessbility: FC = () => {
|
||||
quikAccess.mutate(id, {
|
||||
onSuccess: () => {
|
||||
getQuikAccess.refetch()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
removeQuikAceess.mutate(id, {
|
||||
onSuccess: () => {
|
||||
getQuikAccess.refetch()
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import { toast } from '../../components/Toast';
|
||||
import { useGetDashboardSummary } from '../home/hooks/useHomeData';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Pages } from '../../config/Pages';
|
||||
|
||||
import { ErrorType } from '../../helpers/types';
|
||||
const Notifications: FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const { t } = useTranslation('global');
|
||||
@@ -33,6 +33,9 @@ const Notifications: FC = () => {
|
||||
getDashboard.refetch()
|
||||
refetch()
|
||||
toast(t('success'), 'success')
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Rate from 'rc-rate'
|
||||
import ServiceImages from './components/ServiceImages'
|
||||
import AvatarImage from '../../assets/images/avatar_image.png'
|
||||
import ServiceHeader from './components/ServiceHeader'
|
||||
import { useGetDetailService } from './hooks/useServiceData'
|
||||
import { useGetServiceBySlug } from './hooks/useServiceData'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import PageLoading from '../../components/PageLoading'
|
||||
import moment from 'moment-jalaali'
|
||||
@@ -19,7 +19,7 @@ const DetailService: FC = () => {
|
||||
const { t } = useTranslation('global')
|
||||
const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
||||
|
||||
const getDetailService = useGetDetailService(id ? id : '')
|
||||
const getDetailService = useGetServiceBySlug(id ? id : '')
|
||||
|
||||
return (
|
||||
<div className='w-full flex gap-6 mt-4'>
|
||||
|
||||
@@ -33,7 +33,7 @@ const HeaderBuy: FC<Props> = ({ data, planId }) => {
|
||||
<div>
|
||||
<div className='p-6 bg-white rounded-3xl'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<div onClick={() => navigate(Pages.services.detail + data.id)} className='flex cursor-pointer gap-1.5'>
|
||||
<div onClick={() => navigate(Pages.services.detail + data.slug)} className='flex cursor-pointer gap-1.5'>
|
||||
<ArrowRight className='size-4' color='#888888' />
|
||||
<div className='text-xs text-description'>
|
||||
{t('service.back')}
|
||||
|
||||
@@ -49,3 +49,10 @@ export const useCreateReview = () => {
|
||||
mutationFn: (variables: CreateReviewType) => api.createReview(variables),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetServiceBySlug = (slug: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["service-by-slug", slug],
|
||||
queryFn: () => api.getServiceBySlug(slug),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -42,3 +42,8 @@ export const createReview = async (params: CreateReviewType) => {
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getServiceBySlug = async (slug: string) => {
|
||||
const { data } = await axios.get(`/danak-services/slug/${slug}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ export type ItemServiceType = {
|
||||
updatedAt: string;
|
||||
userCount: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type CategoryItemType = {
|
||||
@@ -46,6 +47,7 @@ export type ServiceDetailDataType = {
|
||||
updatedAt: string;
|
||||
userCount: number;
|
||||
title: string;
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type PlanItemType = {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { FC, useState } from 'react'
|
||||
import SwitchComponent from '../../../components/Switch'
|
||||
import { useUpdateSetting } from '../hooks/useSettingData'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import { toast } from '../../../components/Toast'
|
||||
|
||||
type Props = {
|
||||
sms: boolean,
|
||||
@@ -13,7 +15,11 @@ const Sms: FC<Props> = (props: Props) => {
|
||||
|
||||
const handleChange = (value: boolean) => {
|
||||
setSms(value)
|
||||
updateSetting.mutate({ sms: value, id: props.id })
|
||||
updateSetting.mutate({ sms: value, id: props.id }, {
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SidebarHint from './components/SidebarHint'
|
||||
import SupportImage from '../../assets/images/support.png'
|
||||
import Button from '../../components/Button'
|
||||
import { CloseCircle, Document, Element3, TickCircle } from 'iconsax-react'
|
||||
import Tabs from '../../components/Tabs'
|
||||
import { useBuySupportPlan, useGetSupportPlans } from './hooks/useSupportData'
|
||||
import { NumberFormat } from '../../config/func'
|
||||
import { PlanItemType, featureItemType } from './types/SupportTypes'
|
||||
import SupportSkeleton from './components/SupportSkeleton'
|
||||
import SupportIcon from './components/SupportIcon'
|
||||
import { SupportPlanFeatureKey } from './enum/SupportEnum'
|
||||
import { toast } from '../../components/Toast'
|
||||
import { ErrorType } from '../../helpers/types'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { Pages } from '../../config/Pages'
|
||||
|
||||
const Support: FC = () => {
|
||||
const { data, isLoading } = useGetSupportPlans()
|
||||
const { t } = useTranslation('global')
|
||||
const [activeTab, setActiveTab] = useState<string>('')
|
||||
const [loadingPlanId, setLoadingPlanId] = useState<string>('')
|
||||
const { mutate: buySupportPlan } = useBuySupportPlan()
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.data?.supportPlans?.length > 0 && !activeTab) {
|
||||
setActiveTab(data.data.supportPlans[0].id)
|
||||
}
|
||||
}, [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) {
|
||||
return <SupportSkeleton />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<Helmet>
|
||||
<title>
|
||||
داناک | {t('support.title')}
|
||||
</title>
|
||||
</Helmet>
|
||||
<div>
|
||||
{t('support.title')}
|
||||
</div>
|
||||
|
||||
<div className='xl:hidden mt-5'>
|
||||
<Tabs
|
||||
items={data?.data?.supportPlans?.map((plan: PlanItemType) => ({
|
||||
icon: <Element3 color={activeTab === plan.id ? 'black' : '#8C90A3'} size={22} />,
|
||||
label: plan.name,
|
||||
value: plan.id
|
||||
}))}
|
||||
active={activeTab}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||
<div className='xl:flex hidden justify-between mt-10'>
|
||||
<div className='flex-1 flex justify-center'>
|
||||
<img src={SupportImage} alt='support' className='w-[160px]' />
|
||||
</div>
|
||||
|
||||
{
|
||||
data?.data?.supportPlans?.map((item: PlanItemType) => {
|
||||
return (
|
||||
<div key={item.id} className='flex-1 flex flex-col items-center'>
|
||||
<div className='text-center w-full'>
|
||||
<div className='font-bold'>
|
||||
{item.name}
|
||||
</div>
|
||||
<div className='text-sm text-[#595C67] mt-4'>
|
||||
ماهیانه
|
||||
</div>
|
||||
<div className='font-bold text-sm mt-1'>
|
||||
{NumberFormat(item.price)}
|
||||
</div>
|
||||
<div className='mt-1 text-sm'>
|
||||
تومان
|
||||
</div>
|
||||
|
||||
<Button
|
||||
label='ثبت پلن'
|
||||
className='mt-4 mx-auto w-fit px-5'
|
||||
onClick={() => handleBuy(item.id)}
|
||||
isLoading={loadingPlanId === item.id}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className='xl:flex hidden flex-1 mt-5'>
|
||||
<div className='flex-1 flex flex-col'>
|
||||
{
|
||||
data?.data?.supportPlans?.[0]?.features.map((row: featureItemType) => (
|
||||
<div className='flex h-16 px-4 plan items-center justify-between'>
|
||||
<div className='flex-1 flex items-center gap-2'>
|
||||
<div className='size-8 rounded-2.5 bg-[#EEF0F7] flex items-center justify-center'>
|
||||
<SupportIcon keySupport={row.featureKey as SupportPlanFeatureKey} />
|
||||
</div>
|
||||
<div className='text-xs'>{t(`support.${row.featureKey}`)}</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{
|
||||
data?.data?.supportPlans?.map((item: PlanItemType) => (
|
||||
<div className='flex-1 flex flex-col'>
|
||||
{
|
||||
item.features.map((row) => (
|
||||
<div className='flex h-16 px-4 plan items-center justify-center'>
|
||||
{
|
||||
row.featureType === 'boolean' ?
|
||||
row.featureValue === 'true' ?
|
||||
<TickCircle className='xl:size-8 size-6' color='#01A560' variant='Bold' />
|
||||
:
|
||||
<CloseCircle className='xl:size-8 size-6' color='#D50303' variant='Bold' />
|
||||
:
|
||||
<div className='text-xs'>{row.featureValue}</div>
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* Mobile View */}
|
||||
<div className='xl:hidden block mt-5'>
|
||||
{
|
||||
data?.data?.supportPlans?.map((plan: PlanItemType) => (
|
||||
activeTab === plan.id && (
|
||||
<div key={plan.id}>
|
||||
<div className='flex justify-center mb-6'>
|
||||
<img src={SupportImage} alt='support' className='w-[120px]' />
|
||||
</div>
|
||||
<div className='text-center mb-6'>
|
||||
<div className='font-bold text-lg'>{plan.name}</div>
|
||||
<div className='text-sm text-[#595C67] mt-2'>ماهیانه</div>
|
||||
<div className='font-bold text-sm mt-1'>{NumberFormat(plan.price)}</div>
|
||||
<div className='mt-1 text-sm'>تومان</div>
|
||||
<Button
|
||||
label='ثبت پلن'
|
||||
className='mt-4 w-fit px-5 mx-auto'
|
||||
onClick={() => handleBuy(plan.id)}
|
||||
isLoading={loadingPlanId === plan.id}
|
||||
/>
|
||||
</div>
|
||||
<div className='space-y-4'>
|
||||
{
|
||||
plan.features.map((row: featureItemType) => (
|
||||
<div className='flex items-center justify-between p-4 bg-gray-50 rounded-xl'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='size-8 rounded-2.5 bg-[#EEF0F7] flex items-center justify-center'>
|
||||
<Document size={16} color='#595C67' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-xs'>{t(`support.${row.featureKey}`)}</div>
|
||||
</div>
|
||||
{
|
||||
row.featureType === 'boolean' ?
|
||||
row.featureValue === 'true' ?
|
||||
<TickCircle className='size-6' color='#01A560' variant='Bold' />
|
||||
:
|
||||
<CloseCircle className='size-6' color='#D50303' variant='Bold' />
|
||||
:
|
||||
<div className='text-sm'>{row.featureValue}</div>
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SidebarHint />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Support
|
||||
@@ -0,0 +1,67 @@
|
||||
import { TickSquare } from 'iconsax-react'
|
||||
import { FC } from 'react'
|
||||
|
||||
const SidebarHint: FC = () => {
|
||||
return (
|
||||
<div className='bg-white w-sidebar xl:block hidden py-10 px-5 h-fit rounded-3xl'>
|
||||
<div className='text-sm'>
|
||||
جهت انتخاب بهتر به مسائل زیر توجه بفرمایید:
|
||||
</div>
|
||||
|
||||
<div className='mt-6'>
|
||||
<div className='flex items-start gap-2 border-b pb-5'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
سوالات - مشکلاتی که به یک موضوع مربوط میشوند را در یک درخواست پشتیبانی پیگیر باشید و چند درخواست برای یک موضوع باز نکنید.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6 border-b pb-5'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex items-start gap-2 mt-6'>
|
||||
<div>
|
||||
<TickSquare size={20} color='black' variant='Bold' />
|
||||
</div>
|
||||
<div className='text-description text-xs leading-5'>
|
||||
لطفاً برای بررسی و رفع مشکلات احتمالی صبور باشید بررسی و رفع مشکلات در برخی موارد زمان گیر است.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SidebarHint
|
||||
@@ -0,0 +1,45 @@
|
||||
import { FC } from 'react'
|
||||
import { SupportPlanFeatureKey } from '../enum/SupportEnum'
|
||||
import {
|
||||
Document,
|
||||
MessageQuestion,
|
||||
Clock,
|
||||
Call,
|
||||
Save2,
|
||||
Profile2User,
|
||||
Location,
|
||||
Teacher,
|
||||
Ticket
|
||||
} from 'iconsax-react'
|
||||
|
||||
type Props = {
|
||||
keySupport: SupportPlanFeatureKey
|
||||
}
|
||||
|
||||
const SupportIcon: FC<Props> = ({ keySupport }) => {
|
||||
|
||||
switch (keySupport) {
|
||||
case SupportPlanFeatureKey.LEARNING_DOCS:
|
||||
return <Document size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.TICKET_SUPPORT:
|
||||
return <MessageQuestion size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.RESPONSE_TIME:
|
||||
return <Clock size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.PHONE_SUPPORT:
|
||||
return <Call size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.BACKUP_VERSION:
|
||||
return <Save2 size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.TECHNICAL_EXPERT_ACCESS:
|
||||
return <Profile2User size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.ON_SITE_SUPPORT:
|
||||
return <Location size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.ON_SITE_TRAINING:
|
||||
return <Teacher size={16} color='#595C67' variant='Bold' />
|
||||
case SupportPlanFeatureKey.TICKET_LIMIT:
|
||||
return <Ticket size={16} color='#595C67' variant='Bold' />
|
||||
default:
|
||||
return <Document size={16} color='#595C67' variant='Bold' />
|
||||
}
|
||||
}
|
||||
|
||||
export default SupportIcon
|
||||
@@ -0,0 +1,94 @@
|
||||
import { FC } from 'react'
|
||||
import { Document } from 'iconsax-react'
|
||||
|
||||
const SupportSkeleton: FC = () => {
|
||||
return (
|
||||
<div className='mt-4'>
|
||||
<div className='flex gap-6 xl:mt-8 mt-4'>
|
||||
<div className='flex-1 bg-white py-8 xl:px-10 px-4 rounded-3xl'>
|
||||
{/* Desktop View */}
|
||||
<div className='xl:flex hidden justify-between mt-10'>
|
||||
<div className='flex-1 flex justify-center'>
|
||||
<div className='w-[160px] h-[160px] bg-gray-200 rounded-xl animate-pulse' />
|
||||
</div>
|
||||
|
||||
{[1, 2].map((item) => (
|
||||
<div key={item} className='flex-1 flex flex-col items-center'>
|
||||
<div className='text-center w-full'>
|
||||
<div className='h-6 w-32 bg-gray-200 rounded-lg animate-pulse mx-auto' />
|
||||
<div className='h-4 w-20 bg-gray-200 rounded-lg animate-pulse mx-auto mt-4' />
|
||||
<div className='h-5 w-24 bg-gray-200 rounded-lg animate-pulse mx-auto mt-1' />
|
||||
<div className='h-4 w-16 bg-gray-200 rounded-lg animate-pulse mx-auto mt-1' />
|
||||
<div className='h-10 w-28 bg-gray-200 rounded-lg animate-pulse mx-auto mt-4' />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className='xl:flex hidden flex-1 mt-5'>
|
||||
<div className='flex-1 flex flex-col'>
|
||||
{[1, 2, 3, 4, 5].map((item) => (
|
||||
<div key={item} className='flex h-16 px-4 plan items-center justify-between'>
|
||||
<div className='flex-1 flex items-center gap-2'>
|
||||
<div className='size-8 rounded-2.5 bg-gray-200 animate-pulse' />
|
||||
<div className='h-4 w-32 bg-gray-200 rounded-lg animate-pulse' />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{[1, 2].map((item) => (
|
||||
<div key={item} className='flex-1 flex flex-col'>
|
||||
{[1, 2, 3, 4, 5].map((feature) => (
|
||||
<div key={feature} className='flex h-16 px-4 plan items-center justify-center'>
|
||||
<div className='size-8 bg-gray-200 rounded-full animate-pulse' />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Mobile View */}
|
||||
<div className='xl:hidden block mt-5'>
|
||||
<div className='flex justify-center mb-6'>
|
||||
<div className='w-[120px] h-[120px] bg-gray-200 rounded-xl animate-pulse' />
|
||||
</div>
|
||||
<div className='text-center mb-6'>
|
||||
<div className='h-6 w-40 bg-gray-200 rounded-lg animate-pulse mx-auto' />
|
||||
<div className='h-4 w-24 bg-gray-200 rounded-lg animate-pulse mx-auto mt-2' />
|
||||
<div className='h-5 w-32 bg-gray-200 rounded-lg animate-pulse mx-auto mt-1' />
|
||||
<div className='h-4 w-20 bg-gray-200 rounded-lg animate-pulse mx-auto mt-1' />
|
||||
<div className='h-10 w-32 bg-gray-200 rounded-lg animate-pulse mx-auto mt-4' />
|
||||
</div>
|
||||
<div className='space-y-4'>
|
||||
{[1, 2, 3, 4, 5].map((item) => (
|
||||
<div key={item} className='flex items-center justify-between p-4 bg-gray-50 rounded-xl'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className='size-8 rounded-2.5 bg-gray-200 animate-pulse' />
|
||||
<div className='h-4 w-32 bg-gray-200 rounded-lg animate-pulse' />
|
||||
</div>
|
||||
<div className='size-6 bg-gray-200 rounded-full animate-pulse' />
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
export default SupportSkeleton
|
||||
@@ -0,0 +1,11 @@
|
||||
export enum SupportPlanFeatureKey {
|
||||
LEARNING_DOCS = "learning_docs", // راهنما و مستندات آموزشی
|
||||
TICKET_SUPPORT = "ticket_support", // ارسال تیکت
|
||||
RESPONSE_TIME = "response_time", // زمان پاسخگویی به تیکت
|
||||
PHONE_SUPPORT = "phone_support", // پاسخگویی تلفنی
|
||||
BACKUP_VERSION = "backup_version", // نسخه بکاپ
|
||||
TECHNICAL_EXPERT_ACCESS = "technical_expert_access", // دسترسی به متخصصین فنی
|
||||
ON_SITE_SUPPORT = "on_site_support", // پشتیبانی در محل
|
||||
ON_SITE_TRAINING = "on_site_training", // آموزش در محل
|
||||
TICKET_LIMIT = "ticket_limit", // ظرفیت ارسال تیکت
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { getSupportPlans, buySupportPlan } from "../service/SupportService";
|
||||
|
||||
export const useGetSupportPlans = () => {
|
||||
return useQuery({ queryKey: ["support-plans"], queryFn: getSupportPlans });
|
||||
};
|
||||
|
||||
export const useBuySupportPlan = () => {
|
||||
return useMutation({
|
||||
mutationFn: (planId: string) => buySupportPlan(planId),
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import axios from "../../../config/axios";
|
||||
|
||||
export const getSupportPlans = async () => {
|
||||
const { data } = await axios.get("/support-plans");
|
||||
return data;
|
||||
};
|
||||
|
||||
export const buySupportPlan = async (planId: string) => {
|
||||
const { data } = await axios.post(`/support-plans/${planId}/subscribe`);
|
||||
return data;
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
export type featureItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
featureKey: string;
|
||||
featureValue: string;
|
||||
featureType: string;
|
||||
icon: string | null;
|
||||
};
|
||||
|
||||
export type PlanItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
description: string;
|
||||
price: number;
|
||||
duration: number;
|
||||
isActive: boolean;
|
||||
features: featureItemType[];
|
||||
deletedAt: string | null;
|
||||
};
|
||||
@@ -51,6 +51,9 @@ const CardtoCard: FC = () => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
},
|
||||
onError: (error: ErrorType) => {
|
||||
toast(error.response?.data?.error?.message[0], 'error')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user