Extend plan
This commit is contained in:
@@ -14,6 +14,7 @@ type Props = {
|
||||
isDisabled?: boolean,
|
||||
isQuikAccess?: boolean,
|
||||
onMoreClick?: () => void,
|
||||
subscriptionId?: string,
|
||||
}
|
||||
|
||||
const ServiceItem: FC<Props> = (props: Props) => {
|
||||
@@ -53,10 +54,10 @@ const ServiceItem: FC<Props> = (props: Props) => {
|
||||
}
|
||||
|
||||
|
||||
<div className='mt-4 flex-1 flex items-end'>
|
||||
<div className='mt-4 flex-1 flex gap-2 items-end'>
|
||||
<Link target={props.isLinkPanel && !props.isDisabled ? '_blank' : ''} to={props.isDisabled ? '#' : props.isLinkPanel ? item.link : Pages.services.detail + item.slug}>
|
||||
<Button
|
||||
className='h-8 w-fit px-5 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||
className='h-8 w-fit px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||
>
|
||||
<div className='flex gap-2 whitespace-nowrap'>
|
||||
{
|
||||
@@ -69,6 +70,20 @@ const ServiceItem: FC<Props> = (props: Props) => {
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
{
|
||||
props.subscriptionId &&
|
||||
<Link to={Pages.services.detail + item.slug + '?id=' + props.subscriptionId}>
|
||||
<Button
|
||||
className='h-8 w-fit px-2 text-xs text-black bg-description bg-opacity-20 rounded-xl'
|
||||
>
|
||||
<div className='flex gap-2 whitespace-nowrap'>
|
||||
{t('service.extend')}
|
||||
<ArrowLeft color='black' size={16} />
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
+3
-1
@@ -134,7 +134,9 @@
|
||||
"business_name": "نام کسب و کار",
|
||||
"phone_business": "شماره تماس کسب و کار",
|
||||
"short_description": "توضیح کوتاه",
|
||||
"confrim_and_invoice": "تایید و صدور صورت حساب"
|
||||
"confrim_and_invoice": "تایید و صدور صورت حساب",
|
||||
"extent": "تمدید پلن",
|
||||
"extend": "تمدید"
|
||||
},
|
||||
"loading": "در حال بارگذاری",
|
||||
"footer": {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ReviewItemType } from './types/ServiecTypes'
|
||||
const DetailService: FC = () => {
|
||||
|
||||
const { id } = useParams()
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ const MyServices: FC = () => {
|
||||
isLinkPanel
|
||||
businessName={item.businessName}
|
||||
isDisabled={item.status === 'INACTIVE'}
|
||||
subscriptionId={item.id}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { type FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '../../../components/Button'
|
||||
import { ArrowLeft } from 'iconsax-react'
|
||||
import { useExtentSubscription } from '../hooks/useServiceData'
|
||||
import { toast } from '../../../components/Toast'
|
||||
import { ErrorType } from '../../../helpers/types'
|
||||
import { Pages } from '../../../config/Pages'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
type Props = {
|
||||
subscriptionId: string
|
||||
planId: string,
|
||||
serviceId: string
|
||||
}
|
||||
const ExtendPlan: FC<Props> = (props) => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
const { subscriptionId, planId, serviceId } = props
|
||||
const { mutate: extentSubscription, isPending } = useExtentSubscription()
|
||||
const navigate = useNavigate()
|
||||
const handleExtentSubscription = () => {
|
||||
extentSubscription({
|
||||
subscriptionId: subscriptionId,
|
||||
planId: planId,
|
||||
serviceId: serviceId,
|
||||
}, {
|
||||
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 (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleExtentSubscription}
|
||||
isLoading={isPending}
|
||||
className='bg-description xl:max-w-full max-w-[100px] text-xs h-8 bg-opacity-20 text-black px-4'
|
||||
>
|
||||
<div className='flex gap-1 items-center'>
|
||||
<div>
|
||||
{t('service.extent')}
|
||||
</div>
|
||||
<ArrowLeft className='size-4' color='black' />
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ExtendPlan
|
||||
@@ -8,6 +8,7 @@ import { NumberFormat } from '../../../config/func'
|
||||
import { useBuyService } from '../hooks/useServiceData'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Pages } from '../../../config/Pages'
|
||||
import ExtendPlan from './ExtendPlan'
|
||||
|
||||
type Props = {
|
||||
data: ServiceDetailDataType,
|
||||
@@ -17,6 +18,10 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
||||
|
||||
const { data } = props
|
||||
const { t } = useTranslation('global')
|
||||
// Get query params from URL
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const [subscriptionId, setSubscriptionId] = useState<string>('')
|
||||
|
||||
const [planSelected, setPlanSelected] = useState<string>(data?.subscriptionPlans[0]?.id)
|
||||
const [price, setPrice] = useState<number>(data?.subscriptionPlans[0]?.price)
|
||||
const [finalPrice, setFinalPrice] = useState<number>(0)
|
||||
@@ -30,6 +35,12 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
||||
|
||||
}, [planSelected])
|
||||
|
||||
useEffect(() => {
|
||||
setSubscriptionId(urlParams.get('id') || '')
|
||||
}, [window.location.search])
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className='w-full p-6 bg-white rounded-3xl flex xl:flex-row flex-col items-end xl:justify-between'>
|
||||
@@ -89,21 +100,29 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
||||
}
|
||||
</div>
|
||||
{
|
||||
planSelected &&
|
||||
<Link to={Pages.services.buy + `${data.id}/${planSelected}`}>
|
||||
<Button
|
||||
// onClick={handleBuy}
|
||||
isLoading={buyService.isPending}
|
||||
className='bg-description xl:max-w-full max-w-[100px] text-xs h-8 bg-opacity-20 text-black px-4'
|
||||
>
|
||||
<div className='flex gap-1 items-center'>
|
||||
<div>
|
||||
{t('service.buy')}
|
||||
</div>
|
||||
<ArrowLeft className='size-4' color='black' />
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
subscriptionId ? (
|
||||
<ExtendPlan
|
||||
subscriptionId={subscriptionId}
|
||||
planId={planSelected}
|
||||
serviceId={data.id}
|
||||
/>
|
||||
) :
|
||||
planSelected ?
|
||||
<Link to={Pages.services.buy + `${data.id}/${planSelected}`}>
|
||||
<Button
|
||||
// onClick={handleBuy}
|
||||
isLoading={buyService.isPending}
|
||||
className='bg-description xl:max-w-full max-w-[100px] text-xs h-8 bg-opacity-20 text-black px-4'
|
||||
>
|
||||
<div className='flex gap-1 items-center'>
|
||||
<div>
|
||||
{t('service.buy')}
|
||||
</div>
|
||||
<ArrowLeft className='size-4' color='black' />
|
||||
</div>
|
||||
</Button>
|
||||
</Link>
|
||||
: null
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -56,3 +56,9 @@ export const useGetServiceBySlug = (slug: string) => {
|
||||
queryFn: () => api.getServiceBySlug(slug),
|
||||
});
|
||||
};
|
||||
|
||||
export const useExtentSubscription = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.extentSubscription,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import axios from "../../../config/axios";
|
||||
import { BuyServiceType, CreateReviewType } from "../types/ServiecTypes";
|
||||
import {
|
||||
BuyServiceType,
|
||||
CreateReviewType,
|
||||
ExtentSubscriptionType,
|
||||
ExtentSubscriptionResponseType,
|
||||
} from "../types/ServiecTypes";
|
||||
|
||||
export const getServiceSuggestedDanak = async () => {
|
||||
const { data } = await axios.get(`/danak-services/suggested`);
|
||||
@@ -47,3 +52,13 @@ export const getServiceBySlug = async (slug: string) => {
|
||||
const { data } = await axios.get(`/danak-services/slug/${slug}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const extentSubscription = async (
|
||||
params: ExtentSubscriptionType
|
||||
): Promise<ExtentSubscriptionResponseType> => {
|
||||
const { data } = await axios.put(
|
||||
`/subscriptions/${params.subscriptionId}/subscribe`,
|
||||
params
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -148,3 +148,132 @@ export type ReviewItemType = {
|
||||
profilePic: string | null;
|
||||
};
|
||||
};
|
||||
|
||||
export type ExtentSubscriptionType = {
|
||||
planId: string;
|
||||
serviceId: string;
|
||||
subscriptionId?: string;
|
||||
};
|
||||
|
||||
export type UserType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
userName: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
birthDate: string;
|
||||
nationalCode: string;
|
||||
profilePic: string | null;
|
||||
emailVerified: boolean;
|
||||
financialType: string;
|
||||
deletedAt: string | null;
|
||||
};
|
||||
|
||||
export type PlanType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
duration: number;
|
||||
isFree: boolean;
|
||||
price: number;
|
||||
originalPrice: number;
|
||||
isActive: boolean;
|
||||
service: ServiceType;
|
||||
deletedAt: string | null;
|
||||
};
|
||||
|
||||
export type ServiceType = {
|
||||
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;
|
||||
deletedAt: string | null;
|
||||
};
|
||||
|
||||
export type UserSubscriptionType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
user: UserType;
|
||||
plan: PlanType;
|
||||
startDate: string;
|
||||
endDate: string;
|
||||
businessName: string;
|
||||
businessPhone: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
status: "ACTIVE" | "INACTIVE" | "CANCELED";
|
||||
};
|
||||
|
||||
export type InvoiceItemType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
name: string;
|
||||
count: number;
|
||||
discount: number;
|
||||
unitPrice: number;
|
||||
totalPrice: number;
|
||||
subscriptionPlan: UserSubscriptionType;
|
||||
};
|
||||
|
||||
export type InvoiceType = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
numericId: number;
|
||||
user: UserType;
|
||||
totalPrice: {
|
||||
s: number;
|
||||
e: number;
|
||||
d: number[];
|
||||
};
|
||||
originalPrice: {
|
||||
s: number;
|
||||
e: number;
|
||||
d: number[];
|
||||
};
|
||||
status: "WAIT_PAYMENT" | "PAID" | "CANCELED";
|
||||
dueDate: string;
|
||||
paidAt: string | null;
|
||||
tax: number;
|
||||
lateFee: number | null;
|
||||
isRecurring: boolean;
|
||||
recurringPeriod: string | null;
|
||||
maxRecurringCycles: number;
|
||||
currentRecurringCycle: number;
|
||||
isExternal: boolean;
|
||||
externalBusinessId: string | null;
|
||||
items: InvoiceItemType[];
|
||||
};
|
||||
|
||||
export type ExtentSubscriptionResponseType = {
|
||||
statusCode: number;
|
||||
success: boolean;
|
||||
data: {
|
||||
message: string;
|
||||
userSubscription: UserSubscriptionType;
|
||||
invoice: InvoiceType;
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user