diff --git a/packages/manager/apps/vrack-services/src/components/CreateVrack.component.tsx b/packages/manager/apps/vrack-services/src/components/CreateVrack.component.tsx index e1ffcfd4f980..349c646a0b29 100644 --- a/packages/manager/apps/vrack-services/src/components/CreateVrack.component.tsx +++ b/packages/manager/apps/vrack-services/src/components/CreateVrack.component.tsx @@ -16,7 +16,6 @@ import { OsdsMessage, } from '@ovhcloud/ods-components/react'; import { - CreateCartResult, OrderDescription, getDeliveringOrderQueryKey, useOrderPollingStatus, @@ -29,14 +28,13 @@ import { } from '@ovh-ux/manager-react-shell-client'; import { useNavigate } from 'react-router-dom'; import { handleClick } from '@ovh-ux/manager-react-components'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { ApiError } from '@ovh-ux/manager-core-api'; +import { useQueryClient } from '@tanstack/react-query'; import { getVrackListQueryKey } from '@/data/api'; import { DeliveringMessages } from '@/components/DeliveringMessages.component'; import { MessagesContext } from './feedback-messages/Messages.context'; import { LoadingText } from './LoadingText.component'; import { OrderSubmitModalContent } from './OrderSubmitModalContent.component'; -import { createVrackOnlyCart } from '@/utils/cart'; +import { useCreateCartWithVrack } from '@/data/hooks'; const trackingParams = { location: PageLocation.popup, @@ -55,12 +53,13 @@ export const CreateVrack: React.FC = ({ closeModal }) => { const { trackClick } = useOvhTracking(); const navigate = useNavigate(); - const { mutate: createCart, data, error, isError, isPending } = useMutation< - CreateCartResult, - ApiError - >({ - mutationFn: () => createVrackOnlyCart(environment.user.ovhSubsidiary), - }); + const { + createCart, + data, + error, + isError, + isPending, + } = useCreateCartWithVrack(environment.user.ovhSubsidiary); const { data: vrackDeliveringOrders, diff --git a/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.component.tsx b/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.component.tsx index 4a6eedc6b88a..97f491f12881 100644 --- a/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.component.tsx +++ b/packages/manager/apps/vrack-services/src/components/OrderSubmitModalContent.component.tsx @@ -26,14 +26,10 @@ import { PageLocation, useOvhTracking, } from '@ovh-ux/manager-react-shell-client'; -import { - Contract, - Order, - postOrderCartCartIdCheckout, -} from '@ovh-ux/manager-module-order'; -import { useMutation } from '@tanstack/react-query'; +import { Contract, Order } from '@ovh-ux/manager-module-order'; import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api'; import { LoadingText } from '@/components/LoadingText.component'; +import { SendOrderState, useSendOrder } from '@/data/hooks'; export type OrderSubmitModalContentProps = { submitButtonLabel: string; @@ -53,38 +49,23 @@ export const OrderSubmitModalContent: React.FC = ( const { t } = useTranslation('vrack-services'); const { trackClick } = useOvhTracking(); const [isContractAccepted, setIsContractAccepted] = React.useState(false); - const { mutate: sendOrder, isPending, error, isError } = useMutation< - ApiResponse, - ApiError - >({ - mutationFn: () => - postOrderCartCartIdCheckout({ - cartId, - autoPayWithPreferredPaymentMethod: true, - waiveRetractationPeriod: true, - }), - onSuccess, - onError: async (response) => { - const { - request: { status }, - } = response; + const { + sendOrder, + isPending, + error, + isError, + data, + sendOrderState, + } = useSendOrder(); - if (status === 400) { - try { - const { data } = await postOrderCartCartIdCheckout({ - cartId, - autoPayWithPreferredPaymentMethod: false, - waiveRetractationPeriod: true, - }); - window.top.location.href = data.url; - } catch (err) { - onError(response); - } - } else { - onError(response); - } - }, - }); + React.useEffect(() => { + if (!isPending && sendOrderState === SendOrderState.DONE) { + onSuccess(data); + } + if (sendOrderState === SendOrderState.ERROR && onError) { + onError(error); + } + }, [isPending, sendOrderState, error]); return ( <> @@ -151,13 +132,13 @@ export const OrderSubmitModalContent: React.FC = ( variant={ODS_BUTTON_VARIANT.flat} disabled={!isContractAccepted || undefined} color={ODS_THEME_COLOR_INTENT.primary} - {...handleClick(() => { + {...handleClick(async () => { trackClick({ location: PageLocation.popup, buttonType: ButtonType.button, actions: ['order', 'confirm'], }); - sendOrder(); + sendOrder({ cartId }); })} > {submitButtonLabel} diff --git a/packages/manager/apps/vrack-services/src/data/hooks/index.ts b/packages/manager/apps/vrack-services/src/data/hooks/index.ts index bc28ce5c1c78..64298f191243 100644 --- a/packages/manager/apps/vrack-services/src/data/hooks/index.ts +++ b/packages/manager/apps/vrack-services/src/data/hooks/index.ts @@ -1,3 +1,5 @@ +import { useSendOrder } from './useSendOrder'; + export * from './useVrackServicesList'; export * from './useVrackServices'; export * from './useUpdateVrackServices'; @@ -6,3 +8,6 @@ export * from './useAssociateVrack'; export * from './useDissociateVrack'; export * from './useAllowedVrackList'; export * from './useVrackList'; +export * from './useCreateCartWithVrack'; +export * from './useSendOrder'; +export * from './useCreateCart'; diff --git a/packages/manager/apps/vrack-services/src/data/hooks/useCreateCart.ts b/packages/manager/apps/vrack-services/src/data/hooks/useCreateCart.ts new file mode 100644 index 000000000000..abe04412a6fa --- /dev/null +++ b/packages/manager/apps/vrack-services/src/data/hooks/useCreateCart.ts @@ -0,0 +1,44 @@ +import { useMutation } from '@tanstack/react-query'; +import { ApiError } from '@ovh-ux/manager-core-api'; +import { CreateCartResult } from '@ovh-ux/manager-module-order'; +import { createVrackServicesCart } from '@/utils/cart'; +import { useSendOrder } from '@/data/hooks'; + +export const useCreateCart = () => { + const { + sendOrder, + isPending: isSendOrderPending, + error: sendOrderError, + isError: isSendOrderError, + sendOrderState, + } = useSendOrder(); + + const { mutate: createCart, data, error, isError, isPending } = useMutation< + CreateCartResult, + ApiError, + { hasVrack?: boolean; region: string; ovhSubsidiary: string } + >({ + mutationFn: async (params) => { + const createCartResponse = await createVrackServicesCart({ + ...params, + }); + + if (createCartResponse.contractList.length === 0) + await sendOrder({ cartId: createCartResponse.cartId }); + + return Promise.resolve(createCartResponse); + }, + }); + + return { + createCart, + data, + error, + isError, + isPending, + isSendOrderPending, + isSendOrderError, + sendOrderError, + sendOrderState, + }; +}; diff --git a/packages/manager/apps/vrack-services/src/data/hooks/useCreateCartWithVrack.ts b/packages/manager/apps/vrack-services/src/data/hooks/useCreateCartWithVrack.ts new file mode 100644 index 000000000000..8d69ccf19eb6 --- /dev/null +++ b/packages/manager/apps/vrack-services/src/data/hooks/useCreateCartWithVrack.ts @@ -0,0 +1,24 @@ +import { useMutation } from '@tanstack/react-query'; +import { ApiError } from '@ovh-ux/manager-core-api'; +import { CreateCartResult } from '@ovh-ux/manager-module-order'; +import { createVrackOnlyCart } from '@/utils/cart'; + +/** + * @returns create a cart with 1 vrack inside + */ +export const useCreateCartWithVrack = (ovhSubsidiary: string) => { + const { mutate: createCart, data, error, isError, isPending } = useMutation< + CreateCartResult, + ApiError + >({ + mutationFn: () => createVrackOnlyCart(ovhSubsidiary), + }); + + return { + createCart, + data, + error, + isError, + isPending, + }; +}; diff --git a/packages/manager/apps/vrack-services/src/data/hooks/useSendOrder.ts b/packages/manager/apps/vrack-services/src/data/hooks/useSendOrder.ts new file mode 100644 index 000000000000..662488563c96 --- /dev/null +++ b/packages/manager/apps/vrack-services/src/data/hooks/useSendOrder.ts @@ -0,0 +1,71 @@ +import { useState } from 'react'; +import { useMutation } from '@tanstack/react-query'; +import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api'; +import { + Order, + postOrderCartCartIdCheckout, +} from '@ovh-ux/manager-module-order'; + +export enum SendOrderState { + INACTIVE = 'inactive', + PENDING = 'pending', + DONE = 'done', + ERROR = 'error', +} +/** + * Try to create an order with automatic validation + * If it fails, redirect to express order + */ +export const useSendOrder = () => { + const [sendOrderState, setSendOrderState] = useState( + SendOrderState.INACTIVE, + ); + const { + mutate: sendOrder, + isPending, + error: orderError, + isError, + data, + } = useMutation, ApiError, { cartId: string }>({ + mutationFn: ({ cartId }) => { + setSendOrderState(SendOrderState.PENDING); + return postOrderCartCartIdCheckout({ + cartId, + autoPayWithPreferredPaymentMethod: true, + waiveRetractationPeriod: true, + }); + }, + onSuccess: () => setSendOrderState(SendOrderState.DONE), + onError: async (error, { cartId }) => { + const { + request: { status }, + } = error; + if (status === 400) { + try { + const sendOrderResponse = await postOrderCartCartIdCheckout({ + cartId, + autoPayWithPreferredPaymentMethod: false, + waiveRetractationPeriod: true, + }); + setSendOrderState(SendOrderState.DONE); + window.top.location.href = sendOrderResponse.data.url; + return Promise.resolve(sendOrderResponse); + } catch (e) { + setSendOrderState(SendOrderState.ERROR); + return Promise.reject(e); + } + } + setSendOrderState(SendOrderState.ERROR); + return Promise.reject(error); + }, + }); + + return { + sendOrder, + isPending: isPending && sendOrderState === SendOrderState.PENDING, + sendOrderState, + error: orderError, + isError, + data, + }; +}; diff --git a/packages/manager/apps/vrack-services/src/pages/create-vs/confirm/CreateConfirmModal.page.tsx b/packages/manager/apps/vrack-services/src/pages/create-vs/confirm/CreateConfirmModal.page.tsx index f171421780fc..28dd5e088e5d 100644 --- a/packages/manager/apps/vrack-services/src/pages/create-vs/confirm/CreateConfirmModal.page.tsx +++ b/packages/manager/apps/vrack-services/src/pages/create-vs/confirm/CreateConfirmModal.page.tsx @@ -1,12 +1,9 @@ import React, { useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useQueryClient } from '@tanstack/react-query'; import { - CreateCartResult, - Order, OrderDescription, getDeliveringOrderQueryKey, - postOrderCartCartIdCheckout, } from '@ovh-ux/manager-module-order'; import { ShellContext, @@ -16,7 +13,6 @@ import { TrackingClickParams, } from '@ovh-ux/manager-react-shell-client'; import { useTranslation } from 'react-i18next'; -import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api'; import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; import { ODS_BUTTON_TYPE, @@ -34,8 +30,8 @@ import { import { handleClick } from '@ovh-ux/manager-react-components'; import { LoadingText } from '@/components/LoadingText.component'; import { OrderSubmitModalContent } from '@/components/OrderSubmitModalContent.component'; -import { createVrackServicesCart } from '@/utils/cart'; import { urls } from '@/routes/routes.constants'; +import { useCreateCart } from '@/data/hooks'; const trackingParams: TrackingClickParams = { buttonType: ButtonType.button, @@ -55,53 +51,15 @@ export default function CreateConfirmModal() { const queryClient = useQueryClient(); const { - mutate: sendOrder, - isPending: isSendOrderPending, - error: sendOrderError, - isError: isSendOrderError, - } = useMutation, ApiError, { cartId: string }>({ - mutationFn: ({ cartId }) => - postOrderCartCartIdCheckout({ - cartId, - autoPayWithPreferredPaymentMethod: true, - waiveRetractationPeriod: true, - }), - onError: async (error, { cartId }) => { - const { - request: { status }, - } = error; - - if (status === 400) { - const sendOrderResponse = await postOrderCartCartIdCheckout({ - cartId, - autoPayWithPreferredPaymentMethod: false, - waiveRetractationPeriod: true, - }); - window.top.location.href = sendOrderResponse.data.url; - return Promise.resolve(sendOrderResponse); - } - - return Promise.resolve(error); - }, - }); - - const { mutate: createCart, data, error, isError, isPending } = useMutation< - CreateCartResult, - ApiError, - { hasVrack?: boolean; region: string } - >({ - mutationFn: async (params) => { - const createCartResponse = await createVrackServicesCart({ - ovhSubsidiary: environment.user.ovhSubsidiary, - ...params, - }); - - if (createCartResponse.contractList.length === 0) - await sendOrder({ cartId: createCartResponse.cartId }); - - return Promise.resolve(createCartResponse); - }, - }); + createCart, + data, + error, + isError, + isPending, + sendOrderError, + isSendOrderPending, + isSendOrderError, + } = useCreateCart(); const cancel = () => { trackClick({ @@ -238,7 +196,11 @@ export default function CreateConfirmModal() { ...trackingParams, actions: ['no-vrack', 'confirm'], }); - createCart({ region, hasVrack: false }); + createCart({ + region, + hasVrack: false, + ovhSubsidiary: environment.user.ovhSubsidiary, + }); })} > {t('modalNoVrackButtonLabel')} @@ -256,7 +218,11 @@ export default function CreateConfirmModal() { ...trackingParams, actions: ['create-vrack', 'confirm'], }); - createCart({ region, hasVrack: true }); + createCart({ + region, + hasVrack: true, + ovhSubsidiary: environment.user.ovhSubsidiary, + }); })} > {t('modalConfirmVrackButtonLabel')}