buy dmenu
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { FC } from 'react'
|
import { FC, useState, useEffect } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link, useNavigate } from 'react-router-dom'
|
||||||
import { Pages } from '../config/Pages'
|
import { Pages } from '../config/Pages'
|
||||||
import { ArrowLeft, More } from 'iconsax-react'
|
import { ArrowLeft, More } from 'iconsax-react'
|
||||||
import Button from './Button'
|
import Button from './Button'
|
||||||
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { ItemServiceType } from '../pages/service/types/ServiecTypes'
|
import { ItemServiceType } from '../pages/service/types/ServiecTypes'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import { NumberFormat } from '../config/func'
|
import { NumberFormat } from '../config/func'
|
||||||
|
import { useGetDmenuSubscription } from '../pages/service/hooks/useServiceData'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
item: ItemServiceType,
|
item: ItemServiceType,
|
||||||
@@ -23,8 +24,13 @@ type Props = {
|
|||||||
const ServiceItem: FC<Props> = (props: Props) => {
|
const ServiceItem: FC<Props> = (props: Props) => {
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [isChecked, setIsChecked] = useState<boolean>(false)
|
||||||
const { item } = props
|
const { item } = props
|
||||||
|
|
||||||
|
const isDmenu = item?.slug === 'Dmenu'
|
||||||
|
const { data: dmenuSubscription, isPending, isError } = useGetDmenuSubscription(isChecked && isDmenu && props.isLinkPanel ? props.subscriptionId! : '',)
|
||||||
|
|
||||||
const getRemainingDays = (endDate: string): number => {
|
const getRemainingDays = (endDate: string): number => {
|
||||||
const now = moment()
|
const now = moment()
|
||||||
const end = moment(endDate)
|
const end = moment(endDate)
|
||||||
@@ -53,6 +59,39 @@ const ServiceItem: FC<Props> = (props: Props) => {
|
|||||||
return `${NumberFormat(days)} روز باقی مانده`
|
return `${NumberFormat(days)} روز باقی مانده`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isChecked && isDmenu && props.isLinkPanel) {
|
||||||
|
alert(props.subscriptionId)
|
||||||
|
if (dmenuSubscription && !isPending) {
|
||||||
|
if (item.link) {
|
||||||
|
window.open(item.link, '_blank')
|
||||||
|
}
|
||||||
|
setIsChecked(false)
|
||||||
|
} else if (isError && !isPending) {
|
||||||
|
if (props.subscriptionId) {
|
||||||
|
navigate(Pages.services.manageRestaurant + props.subscriptionId)
|
||||||
|
}
|
||||||
|
setIsChecked(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [dmenuSubscription, isError, isPending, isChecked, isDmenu, props.isLinkPanel, props.subscriptionId, item.link, navigate])
|
||||||
|
|
||||||
|
const handleCheckSubscription = () => {
|
||||||
|
if (props.isDisabled) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.isLinkPanel && item.link) {
|
||||||
|
if (isDmenu && props.subscriptionId) {
|
||||||
|
setIsChecked(true)
|
||||||
|
} else {
|
||||||
|
window.open(item.link, '_blank')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
navigate(Pages.services.detail + item.slug)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`flex-1 relative flex flex-col self-stretch min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4 ${props.className} ${props.isDisabled ? 'opacity-50' : ''}`}>
|
<div className={`flex-1 relative flex flex-col self-stretch min-w-[45%] xl:min-w-[30%] bg-white rounded-3xl xl:p-6 p-4 ${props.className} ${props.isDisabled ? 'opacity-50' : ''}`}>
|
||||||
{
|
{
|
||||||
@@ -94,21 +133,22 @@ const ServiceItem: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
|
|
||||||
<div className='mt-4 flex-1 flex sm:flex-row flex-col gap-2 items-end'>
|
<div className='mt-4 flex-1 flex sm:flex-row flex-col gap-2 items-end'>
|
||||||
<Link className='sm:w-fit w-full' target={props.isLinkPanel && !props.isDisabled ? '_blank' : ''} to={props.isDisabled ? '#' : props.isLinkPanel ? item.link : Pages.services.detail + item.slug}>
|
<Button
|
||||||
<Button
|
className='h-8 sm:w-fit w-full px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||||
className='h-8 sm:w-fit w-full px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
onClick={handleCheckSubscription}
|
||||||
>
|
isLoading={isChecked && isDmenu && props.isLinkPanel ? isPending : false}
|
||||||
<div className='flex gap-2 whitespace-nowrap'>
|
|
||||||
{
|
>
|
||||||
props.isLinkPanel ?
|
<div className='flex gap-2 whitespace-nowrap'>
|
||||||
t('panel_service')
|
{
|
||||||
:
|
props.isLinkPanel ?
|
||||||
t('service.detail')
|
t('panel_service')
|
||||||
}
|
:
|
||||||
<ArrowLeft color='black' size={16} />
|
t('service.detail')
|
||||||
</div>
|
}
|
||||||
</Button>
|
<ArrowLeft color='black' size={16} />
|
||||||
</Link>
|
</div>
|
||||||
|
</Button>
|
||||||
|
|
||||||
{
|
{
|
||||||
props.subscriptionId &&
|
props.subscriptionId &&
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const Pages = {
|
|||||||
},
|
},
|
||||||
dashboard: "/dashboard",
|
dashboard: "/dashboard",
|
||||||
services: {
|
services: {
|
||||||
|
manageRestaurant: "/services/manage-restaurant/",
|
||||||
mine: "/services",
|
mine: "/services",
|
||||||
other: "/other-service",
|
other: "/other-service",
|
||||||
detail: "/services/detail/",
|
detail: "/services/detail/",
|
||||||
|
|||||||
@@ -524,5 +524,16 @@
|
|||||||
"duration": "مدت",
|
"duration": "مدت",
|
||||||
"features": "ویژگی ها",
|
"features": "ویژگی ها",
|
||||||
"actions": "عملیات"
|
"actions": "عملیات"
|
||||||
|
},
|
||||||
|
"restaurant": {
|
||||||
|
"manage": "مدیریت رستوران",
|
||||||
|
"restaurant_info": "اطلاعات رستوران",
|
||||||
|
"enter_restaurant_info": "لطفا اطلاعات رستوران خود را وارد کنید",
|
||||||
|
"name": "نام رستوران",
|
||||||
|
"enter_name": "نام رستوران را وارد کنید",
|
||||||
|
"slug": "اسلاگ",
|
||||||
|
"enter_slug": "اسلاگ را وارد کنید",
|
||||||
|
"established_year": "سال تأسیس",
|
||||||
|
"enter_year": "سال تأسیس را وارد کنید"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import { type FC } from 'react'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useCreateDmenuRestaurant, useGetSubscription } from './hooks/useServiceData'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import { DmenuCreateRestaurantType } from './types/ServiecTypes'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
import { toast } from '../../components/Toast'
|
||||||
|
import { ErrorType } from '../../helpers/types'
|
||||||
|
import Input from '../../components/Input'
|
||||||
|
import Button from '../../components/Button'
|
||||||
|
import { TickCircle } from 'iconsax-react'
|
||||||
|
import { Helmet } from 'react-helmet-async'
|
||||||
|
|
||||||
|
export const ManageRestaurant: FC = () => {
|
||||||
|
|
||||||
|
const { t } = useTranslation('global')
|
||||||
|
const { subscriptionId } = useParams()
|
||||||
|
const createDmenuRestaurant = useCreateDmenuRestaurant()
|
||||||
|
const getSubscription = useGetSubscription(subscriptionId!)
|
||||||
|
|
||||||
|
const formik = useFormik<DmenuCreateRestaurantType>({
|
||||||
|
initialValues: {
|
||||||
|
name: '',
|
||||||
|
slug: '',
|
||||||
|
establishedYear: 0,
|
||||||
|
phone: '',
|
||||||
|
userSubscriptionId: subscriptionId || ''
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
name: Yup.string().required(t('errors.required')),
|
||||||
|
slug: Yup.string().required(t('errors.required')),
|
||||||
|
establishedYear: Yup.number()
|
||||||
|
.required(t('errors.required'))
|
||||||
|
.min(1300, 'سال باید حداقل ۱۳۰۰ باشد')
|
||||||
|
.max(1500, 'سال باید حداکثر ۱۵۰۰ باشد'),
|
||||||
|
phone: Yup.string().required(t('errors.required')),
|
||||||
|
userSubscriptionId: Yup.string().required(t('errors.required'))
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
const submitValues = {
|
||||||
|
...values,
|
||||||
|
establishedYear: Number(values.establishedYear)
|
||||||
|
}
|
||||||
|
createDmenuRestaurant.mutate(submitValues, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast(t('success'), 'success')
|
||||||
|
formik.resetForm()
|
||||||
|
window.location.href = getSubscription?.data?.data?.userSubscription?.plan?.service?.link as string
|
||||||
|
},
|
||||||
|
onError: (error: ErrorType) => {
|
||||||
|
toast(error.response?.data?.error?.message[0], 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Helmet>
|
||||||
|
<title>{t('restaurant.manage')}</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className='mt-4'>
|
||||||
|
<div className='text-lg font-semibold mb-2'>
|
||||||
|
{t('restaurant.manage') || 'مدیریت رستوران'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='bg-white rounded-3xl xl:p-6 p-4 mt-8 text-sm'>
|
||||||
|
<div className='mt-4'>
|
||||||
|
<div className='mb-6'>
|
||||||
|
<div className='text-base font-medium mb-2'>
|
||||||
|
{t('restaurant.restaurant_info') || 'اطلاعات رستوران'}
|
||||||
|
</div>
|
||||||
|
<div className='text-description text-xs'>
|
||||||
|
{t('restaurant.enter_restaurant_info') || 'لطفا اطلاعات رستوران خود را وارد کنید'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex flex-col gap-6'>
|
||||||
|
<div className='flex xl:flex-row flex-col xl:gap-6 gap-3'>
|
||||||
|
<Input
|
||||||
|
label={t('restaurant.name') || 'نام رستوران'}
|
||||||
|
{...formik.getFieldProps('name')}
|
||||||
|
error_text={formik.touched.name && formik.errors.name ? formik.errors.name : ''}
|
||||||
|
placeholder={t('restaurant.enter_name') || 'نام رستوران را وارد کنید'}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('restaurant.slug') || 'اسلاگ'}
|
||||||
|
{...formik.getFieldProps('slug')}
|
||||||
|
error_text={formik.touched.slug && formik.errors.slug ? formik.errors.slug : ''}
|
||||||
|
placeholder={t('restaurant.enter_slug') || 'اسلاگ را وارد کنید'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='flex xl:flex-row flex-col xl:gap-6 gap-3'>
|
||||||
|
<Input
|
||||||
|
label={t('restaurant.established_year') || 'سال تأسیس'}
|
||||||
|
{...formik.getFieldProps('establishedYear')}
|
||||||
|
error_text={formik.touched.establishedYear && formik.errors.establishedYear ? formik.errors.establishedYear : ''}
|
||||||
|
placeholder={t('restaurant.enter_year') || 'سال تأسیس را وارد کنید'}
|
||||||
|
type='number'
|
||||||
|
min={1300}
|
||||||
|
max={1500}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('auth.phonen_number') || 'شماره تماس'}
|
||||||
|
{...formik.getFieldProps('phone')}
|
||||||
|
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
|
||||||
|
placeholder={t('auth.enter_phone_number') || 'شماره تماس را وارد کنید'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-4 flex justify-end'>
|
||||||
|
<Button
|
||||||
|
className='xl:w-fit px-7'
|
||||||
|
onClick={() => formik.handleSubmit()}
|
||||||
|
isLoading={createDmenuRestaurant.isPending}
|
||||||
|
>
|
||||||
|
<div className='flex gap-2 items-center'>
|
||||||
|
<TickCircle
|
||||||
|
size={24}
|
||||||
|
color='white'
|
||||||
|
/>
|
||||||
|
<div>{t('save') || 'ذخیره'}</div>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -62,3 +62,25 @@ export const useExtentSubscription = () => {
|
|||||||
mutationFn: api.extentSubscription,
|
mutationFn: api.extentSubscription,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetDmenuSubscription = (subscriptionId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["dmenu-subscription", subscriptionId],
|
||||||
|
queryFn: () => api.getDmenuSubscription(subscriptionId),
|
||||||
|
enabled: !!subscriptionId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useCreateDmenuRestaurant = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.createDmenuRestaurant,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetSubscription = (subscriptionId: string) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["subscription", subscriptionId],
|
||||||
|
queryFn: () => api.getSubscription(subscriptionId),
|
||||||
|
enabled: !!subscriptionId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import {
|
|||||||
CreateReviewType,
|
CreateReviewType,
|
||||||
ExtentSubscriptionType,
|
ExtentSubscriptionType,
|
||||||
ExtentSubscriptionResponseType,
|
ExtentSubscriptionResponseType,
|
||||||
|
DmenuCreateRestaurantType,
|
||||||
|
GetSubscriptionResponseType,
|
||||||
} from "../types/ServiecTypes";
|
} from "../types/ServiecTypes";
|
||||||
|
|
||||||
export const getServiceSuggestedDanak = async () => {
|
export const getServiceSuggestedDanak = async () => {
|
||||||
@@ -62,3 +64,24 @@ export const extentSubscription = async (
|
|||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDmenuSubscription = async (subscriptionId: string) => {
|
||||||
|
const { data } = await axios.get(
|
||||||
|
`/dmenu/restaurants/subscription/${subscriptionId}`
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDmenuRestaurant = async (
|
||||||
|
params: DmenuCreateRestaurantType
|
||||||
|
) => {
|
||||||
|
const { data } = await axios.post(`/dmenu/restaurants`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSubscription = async (
|
||||||
|
subscriptionId: string
|
||||||
|
): Promise<GetSubscriptionResponseType> => {
|
||||||
|
const { data } = await axios.get(`/subscriptions/user/${subscriptionId}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -277,3 +277,94 @@ export type ExtentSubscriptionResponseType = {
|
|||||||
invoice: InvoiceType;
|
invoice: InvoiceType;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DmenuCreateRestaurantType = {
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
establishedYear: number;
|
||||||
|
phone: string;
|
||||||
|
userSubscriptionId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceImageType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
imageUrl: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SubscriptionPlanItemType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
name: string;
|
||||||
|
duration: number;
|
||||||
|
isFree: boolean;
|
||||||
|
price: number;
|
||||||
|
originalPrice: number;
|
||||||
|
isActive: boolean;
|
||||||
|
deletedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceWithDetailsType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
pageTitle: string;
|
||||||
|
pageDescription: string;
|
||||||
|
slug: string;
|
||||||
|
isDanakSuggest: boolean;
|
||||||
|
description: string;
|
||||||
|
author: string;
|
||||||
|
createDate: string;
|
||||||
|
userCount: number;
|
||||||
|
serviceLanguage: string;
|
||||||
|
softwareLanguage: string;
|
||||||
|
metaDescription: string;
|
||||||
|
isActive: boolean;
|
||||||
|
showInSlider: boolean;
|
||||||
|
link: string;
|
||||||
|
icon: string;
|
||||||
|
coverUrl: string;
|
||||||
|
images: ServiceImageType[];
|
||||||
|
subscriptionPlans: SubscriptionPlanItemType[];
|
||||||
|
deletedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PlanWithServiceType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
name: string;
|
||||||
|
duration: number;
|
||||||
|
isFree: boolean;
|
||||||
|
price: number;
|
||||||
|
originalPrice: number;
|
||||||
|
isActive: boolean;
|
||||||
|
service: ServiceWithDetailsType;
|
||||||
|
deletedAt: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UserSubscriptionWithDetailsType = {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
plan: PlanWithServiceType;
|
||||||
|
startDate: string;
|
||||||
|
endDate: string;
|
||||||
|
businessName: string;
|
||||||
|
businessPhone: string;
|
||||||
|
description: string | null;
|
||||||
|
slug: string;
|
||||||
|
status: "ACTIVE" | "INACTIVE" | "CANCELED";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetSubscriptionResponseType = {
|
||||||
|
statusCode: number;
|
||||||
|
success: boolean;
|
||||||
|
data: {
|
||||||
|
userSubscription: UserSubscriptionWithDetailsType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import BuyService from '../pages/service/BuyService'
|
|||||||
import Support from '../pages/support/Support'
|
import Support from '../pages/support/Support'
|
||||||
import SupportInfo from '../pages/support/SupportInfo'
|
import SupportInfo from '../pages/support/SupportInfo'
|
||||||
import Invoice from '../pages/receipts/Invoice'
|
import Invoice from '../pages/receipts/Invoice'
|
||||||
|
import { ManageRestaurant } from '../pages/service/ManageRestaurant'
|
||||||
|
|
||||||
const MainRouter: FC = () => {
|
const MainRouter: FC = () => {
|
||||||
return (
|
return (
|
||||||
@@ -45,6 +46,7 @@ const MainRouter: FC = () => {
|
|||||||
<Route path={Pages.dashboard} element={<Home />} />
|
<Route path={Pages.dashboard} element={<Home />} />
|
||||||
<Route path={Pages.support.index} element={<Support />} />
|
<Route path={Pages.support.index} element={<Support />} />
|
||||||
<Route path={Pages.support.info} element={<SupportInfo />} />
|
<Route path={Pages.support.info} element={<SupportInfo />} />
|
||||||
|
<Route path={Pages.services.manageRestaurant + ':subscriptionId'} element={<ManageRestaurant />} />
|
||||||
<Route path={Pages.services.mine} element={<MyServices />} />
|
<Route path={Pages.services.mine} element={<MyServices />} />
|
||||||
<Route path={Pages.services.other} element={<OtherServices />} />
|
<Route path={Pages.services.other} element={<OtherServices />} />
|
||||||
<Route path={Pages.services.detail + ':id'} element={<DetailService />} />
|
<Route path={Pages.services.detail + ':id'} element={<DetailService />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user