other services
This commit is contained in:
@@ -34,6 +34,7 @@ const queryClient = new QueryClient({
|
|||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
|
staleTime: 86400000 // 1 day in milliseconds
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { FC } from 'react'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { Pages } from '../config/Pages'
|
||||||
|
import { ArrowLeft } from 'iconsax-react'
|
||||||
|
import Button from './Button'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { ItemServiceType } from '../pages/service/types/ServiecTypes'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
item: ItemServiceType
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServiceItem: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const { item } = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex-1 min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4'>
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<div className='xl:size-[50px] size-10 overflow-hidden rounded-xl'>
|
||||||
|
<img src={item.icon} alt={item.name} className='w-full h-full' />
|
||||||
|
</div>
|
||||||
|
<div className='xl:text-base text-sm'>
|
||||||
|
{item.name}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='mt-4 text-xs'>
|
||||||
|
<div className='font-extralight'>{item.description} </div>
|
||||||
|
<p dangerouslySetInnerHTML={{ __html: item.metaDescription }} className='text-description mt-2'>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-4'>
|
||||||
|
<Link to={Pages.services.detail + item.id}>
|
||||||
|
<Button
|
||||||
|
className='h-8 w-fit px-5 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||||
|
>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div>{t('service.buy')}</div>
|
||||||
|
<ArrowLeft color='black' size={16} />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ServiceItem
|
||||||
@@ -1,15 +1,23 @@
|
|||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
|
import { ItemServiceType } from '../pages/service/types/ServiecTypes'
|
||||||
|
|
||||||
const ServiceSection: FC = () => {
|
type Props = {
|
||||||
|
item: ItemServiceType
|
||||||
|
}
|
||||||
|
|
||||||
|
const ServiceSection: FC<Props> = (props: Props) => {
|
||||||
|
const { item } = props
|
||||||
return (
|
return (
|
||||||
<div className='flex gap-4'>
|
<div className='flex gap-4'>
|
||||||
<div className='size-10 rounded-xl bg-green-300'></div>
|
<div className='size-10 rounded-xl'>
|
||||||
|
<img src={item.icon} alt={item.name} className='w-full h-full' />
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div className='text-sm'>
|
<div className='text-sm'>
|
||||||
دی منو
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
<div className='text-xs text-description'>
|
<div className='text-xs text-description'>
|
||||||
منو رستوران
|
{item.description}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { useGetNotification } from './hooks/useNotificationData';
|
|||||||
import { NotificationItemType } from './types/NotificationTypes';
|
import { NotificationItemType } from './types/NotificationTypes';
|
||||||
import { timeAgo } from '../../config/func';
|
import { timeAgo } from '../../config/func';
|
||||||
import InfiniteScroll from 'react-infinite-scroll-component';
|
import InfiniteScroll from 'react-infinite-scroll-component';
|
||||||
|
import MoonLoader from "react-spinners/MoonLoader"
|
||||||
|
|
||||||
const Notifications: FC = () => {
|
const Notifications: FC = () => {
|
||||||
const { t } = useTranslation('global');
|
const { t } = useTranslation('global');
|
||||||
@@ -29,7 +30,7 @@ const Notifications: FC = () => {
|
|||||||
<Notification onClick={() => setShowModal(!showModal)} size={18} color="black" />
|
<Notification onClick={() => setShowModal(!showModal)} size={18} color="black" />
|
||||||
|
|
||||||
{showModal && (
|
{showModal && (
|
||||||
<div className="xl:h-[calc(100%-110px)] h-[70%] p-6 z-50 xl:w-[300px] w-full xl:left-7 left-0 xl:top-[90px] top-0 overflow-y-auto xl:rounded-3xl rounded-b-3xl rounded-t-none fixed modalGlass3 ">
|
<div id='notificationsContainer' className="xl:h-[calc(100%-110px)] h-[70%] p-6 z-50 xl:w-[300px] w-full xl:left-7 left-0 xl:top-[90px] top-0 overflow-y-auto xl:rounded-3xl rounded-b-3xl rounded-t-none fixed modalGlass3 ">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>{t('notif.natification')}</div>
|
<div>{t('notif.natification')}</div>
|
||||||
<div className="size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center">
|
<div className="size-7 rounded-full bg-white bg-opacity-35 flex justify-center items-center">
|
||||||
@@ -67,23 +68,15 @@ const Notifications: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="notificationsContainer" className="mt-6 flex flex-col gap-4 text-xs overflow-auto">
|
<div className="mt-6 flex flex-col gap-4 text-xs overflow-auto">
|
||||||
<InfiniteScroll
|
<InfiniteScroll
|
||||||
dataLength={posts.length}
|
dataLength={posts.length}
|
||||||
next={fetchNextPage}
|
next={fetchNextPage}
|
||||||
hasMore={hasNextPage}
|
hasMore={hasNextPage}
|
||||||
loader={<h4>در حال بارگذاری...</h4>}
|
loader={<div className='flex justify-center'><MoonLoader color="black" size={20} /></div>}
|
||||||
scrollThreshold={0.9}
|
scrollableTarget="notificationsContainer"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{
|
||||||
<h4>در حال بارگذاری...</h4>
|
|
||||||
) : isFetching ? (
|
|
||||||
<h4>در حال بارگذاری صفحه بعدی...</h4>
|
|
||||||
) : (
|
|
||||||
posts.map((item: NotificationItemType) => (
|
posts.map((item: NotificationItemType) => (
|
||||||
<div className="bg-white h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 mb-4 flex" key={item.id}>
|
<div className="bg-white h-fit gap-4 items-start rounded-xl bg-opacity-60 p-3 mb-4 flex" key={item.id}>
|
||||||
<div className="mt-1">
|
<div className="mt-1">
|
||||||
@@ -101,7 +94,7 @@ const Notifications: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
)}
|
}
|
||||||
</InfiniteScroll>
|
</InfiniteScroll>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,206 +1,110 @@
|
|||||||
import { Carousel } from '@material-tailwind/react'
|
import { Carousel } from '@material-tailwind/react'
|
||||||
import { FC } from 'react'
|
import { FC, Fragment } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import BannerImage from '../../assets/images/banner2.jpeg'
|
import BannerImage from '../../assets/images/banner2.jpeg'
|
||||||
import RobotImage from '../../assets/images/robot.jpeg'
|
import RobotImage from '../../assets/images/robot.jpeg'
|
||||||
import { ArrowLeft } from 'iconsax-react'
|
import { ArrowLeft } from 'iconsax-react'
|
||||||
import TitleLine from '../../components/TitleLine'
|
import TitleLine from '../../components/TitleLine'
|
||||||
import Button from '../../components/Button'
|
import { useGetSuggestedServices } from './hooks/useServiceData'
|
||||||
import ServiceSection from '../../components/ServiceSection'
|
import PageLoading from '../../components/PageLoading'
|
||||||
import { Link } from 'react-router-dom'
|
import { ItemServiceType } from './types/ServiecTypes'
|
||||||
import { Pages } from '../../config/Pages'
|
import ServiceItem from '../../components/ServiceItem'
|
||||||
|
import OtherServicesComponent from './components/OtherServices'
|
||||||
|
|
||||||
|
|
||||||
const OtherServices: FC = () => {
|
const OtherServices: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
|
||||||
|
const getSuggestedServices = useGetSuggestedServices()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full flex gap-6'>
|
<div className='w-full flex gap-6'>
|
||||||
<div className='flex-1'>
|
<div className='flex-1'>
|
||||||
<Carousel autoplay className="rounded-xl h-fit dltr" placeholder="" onPointerEnterCapture={() => { }} onPointerLeaveCapture={() => { }} >
|
{
|
||||||
<div className='relative drtl'>
|
getSuggestedServices.isPending ?
|
||||||
<img src={BannerImage} className='w-full max-h-[300px] h-full object-cover rounded-3xl' />
|
<PageLoading />
|
||||||
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
|
:
|
||||||
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
|
<Fragment>
|
||||||
{t('home.new')}
|
<Carousel autoplay className="rounded-xl h-fit dltr" placeholder="" onPointerEnterCapture={() => { }} onPointerLeaveCapture={() => { }} >
|
||||||
</button>
|
<div className='relative drtl'>
|
||||||
|
<img src={BannerImage} className='w-full max-h-[300px] h-full object-cover rounded-3xl' />
|
||||||
|
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
|
||||||
|
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
|
||||||
|
{t('home.new')}
|
||||||
|
</button>
|
||||||
|
|
||||||
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
|
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
|
||||||
<div>
|
<div>
|
||||||
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
||||||
</div>
|
</div>
|
||||||
<div className='mt-3 text-border text-xs'>
|
<div className='mt-3 text-border text-xs'>
|
||||||
۸ دقیقه مطالعه
|
۸ دقیقه مطالعه
|
||||||
</div>
|
</div>
|
||||||
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
|
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
|
||||||
<div>
|
<div>
|
||||||
{t('home.study')}
|
{t('home.study')}
|
||||||
</div>
|
</div>
|
||||||
<ArrowLeft size={14} color='white' />
|
<ArrowLeft size={14} color='white' />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='relative drtl'>
|
|
||||||
<img src={BannerImage} className='w-full max-h-[300px] object-cover rounded-3xl' />
|
|
||||||
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
|
|
||||||
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
|
|
||||||
{t('home.new')}
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
|
|
||||||
<div>
|
|
||||||
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
|
||||||
</div>
|
|
||||||
<div className='mt-3 text-border text-xs'>
|
|
||||||
۸ دقیقه مطالعه
|
|
||||||
</div>
|
|
||||||
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
|
|
||||||
<div>
|
|
||||||
{t('home.study')}
|
|
||||||
</div>
|
|
||||||
<ArrowLeft size={14} color='white' />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Carousel>
|
|
||||||
|
|
||||||
|
|
||||||
<div className='w-full mt-8'>
|
|
||||||
<TitleLine title={t('service.selected_danak')} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex flex-wrap xl:gap-6 gap-4 items-center mt-8'>
|
|
||||||
<div className='flex-1 min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4'>
|
|
||||||
<div className='flex gap-2 items-center'>
|
|
||||||
<div className='xl:size-[50px] size-10 bg-[#FF7B00] rounded-xl'></div>
|
|
||||||
<div className='xl:text-base text-sm'>
|
|
||||||
دی منو
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='mt-4 text-xs'>
|
|
||||||
<div className='font-extralight'>منو رستوران </div>
|
|
||||||
<p className='text-description mt-2'>
|
|
||||||
دی منو، راهی ساده و هوشمند برای مدیریت منو شما!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4'>
|
|
||||||
<Link to={Pages.services.detail + '1'}>
|
|
||||||
<Button
|
|
||||||
className='h-8 w-fit px-5 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
|
||||||
>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div>{t('service.buy')}</div>
|
|
||||||
<ArrowLeft color='black' size={16} />
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 min-w-[40%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4'>
|
|
||||||
<div className='flex gap-2 items-center'>
|
|
||||||
<div className='xl:size-[50px] size-10 bg-[#FF7B00] rounded-xl'></div>
|
|
||||||
<div className='xl:text-base text-sm'>
|
|
||||||
دی منو
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='mt-4 text-xs'>
|
|
||||||
<div className='font-extralight'>منو رستوران </div>
|
|
||||||
<p className='text-description mt-2'>
|
|
||||||
دی منو، راهی ساده و هوشمند برای مدیریت منو شما!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4'>
|
|
||||||
<Link to={Pages.services.detail + '1'}>
|
|
||||||
<Button
|
|
||||||
className='h-8 w-fit px-5 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
|
||||||
>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div>{t('service.buy')}</div>
|
|
||||||
<ArrowLeft color='black' size={16} />
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 min-w-[40%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4'>
|
|
||||||
<div className='flex gap-2 items-center'>
|
|
||||||
<div className='xl:size-[50px] size-10 bg-[#FF7B00] rounded-xl'></div>
|
|
||||||
<div className='xl:text-base text-sm'>
|
|
||||||
دی منو
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='mt-4 text-xs'>
|
|
||||||
<div className='font-extralight'>منو رستوران </div>
|
|
||||||
<p className='text-description mt-2'>
|
|
||||||
دی منو، راهی ساده و هوشمند برای مدیریت منو شما!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-4'>
|
|
||||||
<Link to={Pages.services.detail + '1'}>
|
|
||||||
<Button
|
|
||||||
className='h-8 w-fit px-5 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
|
||||||
>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div>{t('service.buy')}</div>
|
|
||||||
<ArrowLeft color='black' size={16} />
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='flex-1 min-w-[40%] xl:min-w-[30%] xl:p-6 p-4'></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='w-full xl:-mt-7 mt-8'>
|
|
||||||
<TitleLine title={t('service.other_services')} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='flex w-full gap-6 items-center'>
|
|
||||||
<div className='flex w-full flex-wrap xl:gap-6 gap-4 items-center mt-8'>
|
|
||||||
<div className='flex-1 min-w-full xl:min-w-0 bg-white rounded-3xl py-4 px-6'>
|
|
||||||
<div className='flex justify-between items-center'>
|
|
||||||
<ServiceSection
|
|
||||||
/>
|
|
||||||
<Link to={Pages.services.detail + '1'}>
|
|
||||||
<Button
|
|
||||||
className='h-8 w-fit px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
|
||||||
>
|
|
||||||
<div className='flex gap-2'>
|
|
||||||
<div>{t('service.buy')}</div>
|
|
||||||
<ArrowLeft color='black' size={16} />
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</div>
|
||||||
</Link>
|
</div>
|
||||||
</div>
|
<div className='relative drtl'>
|
||||||
|
<img src={BannerImage} className='w-full max-h-[300px] object-cover rounded-3xl' />
|
||||||
|
<div className='absolute bg-black bg-opacity-20 size-full top-0 right-0 py-6 px-8 flex flex-col justify-between'>
|
||||||
|
<button className="px-8 w-fit py-1.5 bg-white bg-opacity-10 text-white text-sm rounded-full shadow-md ">
|
||||||
|
{t('home.new')}
|
||||||
|
</button>
|
||||||
|
|
||||||
</div>
|
<div className='max-w-[340px] font-medium text-white text-lg leading-10'>
|
||||||
<div className='flex-1 min-w-full xl:min-w-0 bg-white rounded-3xl py-4 px-6'>
|
<div>
|
||||||
<div className='flex justify-between items-center'>
|
سفارش نرمافزار اختصاصی: سرمایهای برای آینده یا فقط هزینهای برای امروز؟
|
||||||
<ServiceSection
|
</div>
|
||||||
/>
|
<div className='mt-3 text-border text-xs'>
|
||||||
<Link to={Pages.services.detail + '1'}>
|
۸ دقیقه مطالعه
|
||||||
<Button
|
</div>
|
||||||
className='h-8 w-fit px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
<button className='mt-3 px-4 h-9 flex gap-2 items-center text-xs rounded-2.5 bg-black text-white'>
|
||||||
>
|
<div>
|
||||||
<div className='flex gap-2'>
|
{t('home.study')}
|
||||||
<div>{t('service.buy')}</div>
|
</div>
|
||||||
<ArrowLeft color='black' size={16} />
|
<ArrowLeft size={14} color='white' />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</div>
|
||||||
</Link>
|
</div>
|
||||||
|
</Carousel>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='w-full mt-8'>
|
||||||
|
<TitleLine title={t('service.selected_danak')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<div className='flex flex-wrap xl:gap-6 gap-4 items-center mt-8'>
|
||||||
</div>
|
{
|
||||||
|
getSuggestedServices.data?.data?.danakServices?.map((item: ItemServiceType) => (
|
||||||
|
<ServiceItem
|
||||||
|
key={item.id}
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
<div className='flex-1 min-w-[40%] xl:min-w-[30%] xl:p-6 p-4'></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='h-16 xl:hidden'></div>
|
|
||||||
|
<OtherServicesComponent />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div className='h-16 xl:hidden'></div>
|
||||||
|
</Fragment>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react'
|
||||||
|
import TitleLine from '../../../components/TitleLine'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useGetCategoriesPublic, useGetServicesByCategory } from '../hooks/useServiceData'
|
||||||
|
import { SwiperSlide, Swiper } from 'swiper/react'
|
||||||
|
import { CategoryItemType, ItemServiceType } from '../types/ServiecTypes'
|
||||||
|
import { clx } from '../../../helpers/utils'
|
||||||
|
import ServiceSection from '../../../components/ServiceSection'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import Button from '../../../components/Button'
|
||||||
|
import { ArrowLeft } from 'iconsax-react'
|
||||||
|
import { Pages } from '../../../config/Pages'
|
||||||
|
|
||||||
|
const OtherServicesComponent: FC = () => {
|
||||||
|
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const [categoryId, setCategoryId] = useState<string>('')
|
||||||
|
const getCategory = useGetCategoriesPublic()
|
||||||
|
const getServices = useGetServicesByCategory(categoryId)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (getCategory.data) {
|
||||||
|
setCategoryId(getCategory?.data?.data?.categories[0]?.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [getCategory.data])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='w-full mt-8'>
|
||||||
|
<TitleLine title={t('service.other_services')} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-6'>
|
||||||
|
<Swiper
|
||||||
|
className='w-full'
|
||||||
|
spaceBetween={15} // فاصله پیشفرض بین اسلایدها
|
||||||
|
slidesPerView='auto'
|
||||||
|
>
|
||||||
|
{
|
||||||
|
getCategory.data?.data?.categories?.map((item: CategoryItemType) => {
|
||||||
|
return (
|
||||||
|
<SwiperSlide key={item.id} className='max-w-[158px]'>
|
||||||
|
<div onClick={() => setCategoryId(item.id)} className={clx(
|
||||||
|
'h-10 cursor-pointer rounded-full border-2 border-white bg-white/45 flex gap-2 justify-center items-center',
|
||||||
|
item.id === categoryId && 'bg-white'
|
||||||
|
)}>
|
||||||
|
<img src={item.icon} className='size-5 rounded-full object-cover' />
|
||||||
|
<div className='text-sm'>{item.title}</div>
|
||||||
|
</div>
|
||||||
|
</SwiperSlide>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</Swiper>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex w-full gap-6 items-center'>
|
||||||
|
<div className='flex w-full flex-wrap xl:gap-6 gap-4 items-center mt-8'>
|
||||||
|
{
|
||||||
|
getServices.data?.data?.category?.danakService?.map((item: ItemServiceType) => {
|
||||||
|
return (
|
||||||
|
<div className='flex-1 min-w-full xl:min-w-0 bg-white rounded-3xl py-4 px-6'>
|
||||||
|
<div className='flex justify-between items-center'>
|
||||||
|
<ServiceSection
|
||||||
|
item={item}
|
||||||
|
/>
|
||||||
|
<Link to={Pages.services.detail + item.id}>
|
||||||
|
<Button
|
||||||
|
className='h-8 w-fit px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||||
|
>
|
||||||
|
<div className='flex gap-2'>
|
||||||
|
<div>{t('service.buy')}</div>
|
||||||
|
<ArrowLeft color='black' size={16} />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OtherServicesComponent
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import * as api from "../service/ServiceService";
|
||||||
|
|
||||||
|
export const useGetSuggestedServices = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["suggested-services"],
|
||||||
|
queryFn: () => api.getServiceSuggestedDanak(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetCategoriesPublic = () => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["categories-public"],
|
||||||
|
queryFn: () => api.getCategoriesPublic(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetServicesByCategory = (categoryId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["services-category", categoryId],
|
||||||
|
queryFn: () => api.getServicesByCategory(categoryId),
|
||||||
|
enabled: !!categoryId,
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import axios from "../../../config/axios";
|
||||||
|
|
||||||
|
export const getServiceSuggestedDanak = async () => {
|
||||||
|
const { data } = await axios.get(`/danak-services/suggested`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
export const getCategoriesPublic = async () => {
|
||||||
|
const { data } = await axios.get(`/danak-services/categories/public`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
export const getServicesByCategory = async (categoryId: string) => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`/danak-services/categories/${categoryId}/services`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
export type ItemServiceType = {
|
||||||
|
author: string;
|
||||||
|
createDate: string;
|
||||||
|
createdAt: string;
|
||||||
|
description: string;
|
||||||
|
icon: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
|
isDanakSuggest: boolean;
|
||||||
|
link: string;
|
||||||
|
metaDescription: string;
|
||||||
|
name: string;
|
||||||
|
serviceLanguage: string;
|
||||||
|
softwareLanguage: string;
|
||||||
|
updatedAt: string;
|
||||||
|
userCount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CategoryItemType = {
|
||||||
|
createdAt: string;
|
||||||
|
icon: string;
|
||||||
|
id: string;
|
||||||
|
isActive: boolean;
|
||||||
|
parentId: string | null;
|
||||||
|
title: string;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user