Extend plan
This commit is contained in:
@@ -14,6 +14,7 @@ type Props = {
|
|||||||
isDisabled?: boolean,
|
isDisabled?: boolean,
|
||||||
isQuikAccess?: boolean,
|
isQuikAccess?: boolean,
|
||||||
onMoreClick?: () => void,
|
onMoreClick?: () => void,
|
||||||
|
subscriptionId?: string,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ServiceItem: FC<Props> = (props: Props) => {
|
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}>
|
<Link target={props.isLinkPanel && !props.isDisabled ? '_blank' : ''} to={props.isDisabled ? '#' : props.isLinkPanel ? item.link : Pages.services.detail + item.slug}>
|
||||||
<Button
|
<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'>
|
<div className='flex gap-2 whitespace-nowrap'>
|
||||||
{
|
{
|
||||||
@@ -69,6 +70,20 @@ const ServiceItem: FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
+3
-1
@@ -134,7 +134,9 @@
|
|||||||
"business_name": "نام کسب و کار",
|
"business_name": "نام کسب و کار",
|
||||||
"phone_business": "شماره تماس کسب و کار",
|
"phone_business": "شماره تماس کسب و کار",
|
||||||
"short_description": "توضیح کوتاه",
|
"short_description": "توضیح کوتاه",
|
||||||
"confrim_and_invoice": "تایید و صدور صورت حساب"
|
"confrim_and_invoice": "تایید و صدور صورت حساب",
|
||||||
|
"extent": "تمدید پلن",
|
||||||
|
"extend": "تمدید"
|
||||||
},
|
},
|
||||||
"loading": "در حال بارگذاری",
|
"loading": "در حال بارگذاری",
|
||||||
"footer": {
|
"footer": {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { ReviewItemType } from './types/ServiecTypes'
|
|||||||
const DetailService: FC = () => {
|
const DetailService: FC = () => {
|
||||||
|
|
||||||
const { id } = useParams()
|
const { id } = useParams()
|
||||||
|
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
const getAdsLeft = useGetAds(AdsDisplayLocation.SINGLE_SERVICE_PAGE)
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ const MyServices: FC = () => {
|
|||||||
isLinkPanel
|
isLinkPanel
|
||||||
businessName={item.businessName}
|
businessName={item.businessName}
|
||||||
isDisabled={item.status === 'INACTIVE'}
|
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 { useBuyService } from '../hooks/useServiceData'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Pages } from '../../../config/Pages'
|
import { Pages } from '../../../config/Pages'
|
||||||
|
import ExtendPlan from './ExtendPlan'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: ServiceDetailDataType,
|
data: ServiceDetailDataType,
|
||||||
@@ -17,6 +18,10 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
const { data } = props
|
const { data } = props
|
||||||
const { t } = useTranslation('global')
|
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 [planSelected, setPlanSelected] = useState<string>(data?.subscriptionPlans[0]?.id)
|
||||||
const [price, setPrice] = useState<number>(data?.subscriptionPlans[0]?.price)
|
const [price, setPrice] = useState<number>(data?.subscriptionPlans[0]?.price)
|
||||||
const [finalPrice, setFinalPrice] = useState<number>(0)
|
const [finalPrice, setFinalPrice] = useState<number>(0)
|
||||||
@@ -30,6 +35,12 @@ const ServiceHeader: FC<Props> = (props: Props) => {
|
|||||||
|
|
||||||
}, [planSelected])
|
}, [planSelected])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSubscriptionId(urlParams.get('id') || '')
|
||||||
|
}, [window.location.search])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full p-6 bg-white rounded-3xl flex xl:flex-row flex-col items-end xl:justify-between'>
|
<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>
|
</div>
|
||||||
{
|
{
|
||||||
planSelected &&
|
subscriptionId ? (
|
||||||
<Link to={Pages.services.buy + `${data.id}/${planSelected}`}>
|
<ExtendPlan
|
||||||
<Button
|
subscriptionId={subscriptionId}
|
||||||
// onClick={handleBuy}
|
planId={planSelected}
|
||||||
isLoading={buyService.isPending}
|
serviceId={data.id}
|
||||||
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'>
|
planSelected ?
|
||||||
<div>
|
<Link to={Pages.services.buy + `${data.id}/${planSelected}`}>
|
||||||
{t('service.buy')}
|
<Button
|
||||||
</div>
|
// onClick={handleBuy}
|
||||||
<ArrowLeft className='size-4' color='black' />
|
isLoading={buyService.isPending}
|
||||||
</div>
|
className='bg-description xl:max-w-full max-w-[100px] text-xs h-8 bg-opacity-20 text-black px-4'
|
||||||
</Button>
|
>
|
||||||
</Link>
|
<div className='flex gap-1 items-center'>
|
||||||
|
<div>
|
||||||
|
{t('service.buy')}
|
||||||
|
</div>
|
||||||
|
<ArrowLeft className='size-4' color='black' />
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
: null
|
||||||
}
|
}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,3 +56,9 @@ export const useGetServiceBySlug = (slug: string) => {
|
|||||||
queryFn: () => api.getServiceBySlug(slug),
|
queryFn: () => api.getServiceBySlug(slug),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useExtentSubscription = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: api.extentSubscription,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import axios from "../../../config/axios";
|
import axios from "../../../config/axios";
|
||||||
import { BuyServiceType, CreateReviewType } from "../types/ServiecTypes";
|
import {
|
||||||
|
BuyServiceType,
|
||||||
|
CreateReviewType,
|
||||||
|
ExtentSubscriptionType,
|
||||||
|
ExtentSubscriptionResponseType,
|
||||||
|
} from "../types/ServiecTypes";
|
||||||
|
|
||||||
export const getServiceSuggestedDanak = async () => {
|
export const getServiceSuggestedDanak = async () => {
|
||||||
const { data } = await axios.get(`/danak-services/suggested`);
|
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}`);
|
const { data } = await axios.get(`/danak-services/slug/${slug}`);
|
||||||
return data;
|
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;
|
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