From 60696a8569878f4cb63b15c4d3ec24bc92813354 Mon Sep 17 00:00:00 2001 From: Nicolas Pierre-charles Date: Wed, 19 Nov 2025 13:33:15 +0100 Subject: [PATCH] feat(ips): improve move ip for vrack ref: #MANAGER-18624 Signed-off-by: Nicolas Pierre-charles --- .../apps/ips/mocks/dedicated-server.ts | 5 + .../ips/mocks/organisations/organisations.ts | 7 + packages/manager/apps/ips/mocks/vrack.ts | 11 ++ .../translations/move-ip/Messages_fr_FR.json | 1 - .../apps/ips/src/data/api/get/index.ts | 1 + .../apps/ips/src/data/api/get/vrackTask.ts | 31 ++++ .../ips/src/data/api/postorput/postMoveIp.ts | 17 ++ .../ips/src/data/hooks/ip/useMoveIpService.ts | 149 +++++++++++++++++- .../__snapshots__/moveIp.spec.tsx.snap | 49 ------ .../actions/moveIp/__tests__/moveIp.spec.tsx | 15 +- .../src/pages/actions/moveIp/moveIp.page.tsx | 72 ++------- .../IpAttachedService.spec.tsx | 8 + .../IpAttachedService/IpAttachedService.tsx | 26 ++- packages/manager/apps/ips/src/types/index.ts | 3 + .../manager/apps/ips/src/types/vrackTask.ts | 23 +++ 15 files changed, 302 insertions(+), 116 deletions(-) create mode 100644 packages/manager/apps/ips/src/data/api/get/vrackTask.ts delete mode 100644 packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/__snapshots__/moveIp.spec.tsx.snap create mode 100644 packages/manager/apps/ips/src/types/vrackTask.ts diff --git a/packages/manager/apps/ips/mocks/dedicated-server.ts b/packages/manager/apps/ips/mocks/dedicated-server.ts index 40450ececf98..3a868cd577ed 100644 --- a/packages/manager/apps/ips/mocks/dedicated-server.ts +++ b/packages/manager/apps/ips/mocks/dedicated-server.ts @@ -118,4 +118,9 @@ export const getDedicatedServerMocks = ({ response: [], api: 'v6', }, + { + url: '/dedicated/server.json', + response: {}, + api: 'v6', + }, ]; diff --git a/packages/manager/apps/ips/mocks/organisations/organisations.ts b/packages/manager/apps/ips/mocks/organisations/organisations.ts index 856cfd7477ae..a8afda4e72a0 100644 --- a/packages/manager/apps/ips/mocks/organisations/organisations.ts +++ b/packages/manager/apps/ips/mocks/organisations/organisations.ts @@ -93,4 +93,11 @@ export const getOrganisationMocks = ({ }, api: 'v6', }, + { + url: '/me.json', + response: { + ovhSubsidiary: 'FR', + }, + api: 'v6', + }, ]; diff --git a/packages/manager/apps/ips/mocks/vrack.ts b/packages/manager/apps/ips/mocks/vrack.ts index fdde5b9cfcc9..583db6d831ce 100644 --- a/packages/manager/apps/ips/mocks/vrack.ts +++ b/packages/manager/apps/ips/mocks/vrack.ts @@ -31,6 +31,17 @@ export const getVrackMocks = ({ getVrackKo, isVrackExpired, }: GetVrackMocksParams): Handler[] => [ + { + url: '/vrack/:serviceName/task/:taskId', + response: {}, + api: 'v6', + status: 404, + }, + { + url: '/vrack/:serviceName/task', + response: [], + api: 'v6', + }, { url: '/vrack/:serviceName/serviceInfos', response: isVrackExpired ? expiredService : availableService, diff --git a/packages/manager/apps/ips/public/translations/move-ip/Messages_fr_FR.json b/packages/manager/apps/ips/public/translations/move-ip/Messages_fr_FR.json index 599b0592ec06..1c6ddaf359da 100644 --- a/packages/manager/apps/ips/public/translations/move-ip/Messages_fr_FR.json +++ b/packages/manager/apps/ips/public/translations/move-ip/Messages_fr_FR.json @@ -4,7 +4,6 @@ "step1EmptyCurrentServiceValue": "Parking des IP", "step1DestinationServiceLabel": "Vers", "step1NextHopLabel": "Sur", - "step1VrackMessage": "Veuillez d'abord détacher votre bloc IP de vRack (ici) pour pouvoir le connecter ailleurs.", "step2DescriptionWithNextHop": "Voulez vous vraiment déplacer l'IP {{ip}} vers {{destinationService}} sur {{nextHop}} ?", "step2DescriptionWithoutNextHop": "Voulez vous vraiment déplacer l'IP {{ip}} vers {{destinationService}} ?", "moveIpOnGoingTaskMessage": "Cette IP est déjà en cours de déplacement.", diff --git a/packages/manager/apps/ips/src/data/api/get/index.ts b/packages/manager/apps/ips/src/data/api/get/index.ts index fd4b1612b08c..38ab7d7c997b 100644 --- a/packages/manager/apps/ips/src/data/api/get/index.ts +++ b/packages/manager/apps/ips/src/data/api/get/index.ts @@ -17,3 +17,4 @@ export * from './productServices'; export * from './ipRipeInformation'; export * from './ipTask'; export * from './moveIp'; +export * from './vrackTask'; diff --git a/packages/manager/apps/ips/src/data/api/get/vrackTask.ts b/packages/manager/apps/ips/src/data/api/get/vrackTask.ts new file mode 100644 index 000000000000..0bf68b8087c1 --- /dev/null +++ b/packages/manager/apps/ips/src/data/api/get/vrackTask.ts @@ -0,0 +1,31 @@ +import { v6, ApiResponse } from '@ovh-ux/manager-core-api'; +import { VrackTask } from '@/types'; + +export type GetVrackTaskParams = { + serviceName: string; +}; + +export const getVrackTaskQueryKey = (params: GetVrackTaskParams) => [ + 'vRackTask', + params.serviceName, +]; + +export const getVrackTaskList = ({ + serviceName, +}: GetVrackTaskParams): Promise> => + v6.get(`/vrack/${serviceName}/task`); + +export type GetVrackTaskDetailsParams = { + serviceName: string; + taskId: number; +}; + +export const getVrackTaskDetailsQueryKey = ( + params: GetVrackTaskDetailsParams, +) => ['vRackTaskDetails', params.serviceName, params.taskId]; + +export const getVrackTaskDetails = ({ + serviceName, + taskId, +}: GetVrackTaskDetailsParams): Promise> => + v6.get(`/vrack/${serviceName}/task/${taskId}`); diff --git a/packages/manager/apps/ips/src/data/api/postorput/postMoveIp.ts b/packages/manager/apps/ips/src/data/api/postorput/postMoveIp.ts index 13ad5cdd85df..e0f82d2c43c4 100644 --- a/packages/manager/apps/ips/src/data/api/postorput/postMoveIp.ts +++ b/packages/manager/apps/ips/src/data/api/postorput/postMoveIp.ts @@ -1,9 +1,13 @@ import { ApiResponse, apiClient } from '@ovh-ux/manager-core-api'; +import ipaddr from 'ipaddr.js'; import { ipParkingOptionValue, IpTask } from '@/types'; +import { getTypeByServiceName } from '@/utils'; +import { IpTypeEnum } from '@/data/constants'; export type PostMoveIpParams = { ip: string; to: string | typeof ipParkingOptionValue; + serviceName: string; nexthop?: string; }; @@ -11,8 +15,21 @@ export const postMoveIp = async ({ ip, to, nexthop, + serviceName, }: PostMoveIpParams): Promise> => { const isDetachedToParking = to === ipParkingOptionValue; + const isAttachedToSomeVrack = + getTypeByServiceName(serviceName) === IpTypeEnum.VRACK; + + if (isDetachedToParking && isAttachedToSomeVrack) { + return apiClient.v6.delete( + `/vrack/${serviceName}/${ + ipaddr.IPv6.isIPv6(ip) ? 'ipv6' : 'ip' + }/${encodeURIComponent(ip)}`, + {}, + ); + } + return apiClient.v6.post( `/ip/${encodeURIComponent(ip)}/${isDetachedToParking ? 'park' : 'move'}`, isDetachedToParking diff --git a/packages/manager/apps/ips/src/data/hooks/ip/useMoveIpService.ts b/packages/manager/apps/ips/src/data/hooks/ip/useMoveIpService.ts index f5682100267d..f99df8e1ce39 100644 --- a/packages/manager/apps/ips/src/data/hooks/ip/useMoveIpService.ts +++ b/packages/manager/apps/ips/src/data/hooks/ip/useMoveIpService.ts @@ -19,15 +19,135 @@ import { getIpTaskDetailsQueryKey, getIpTaskDetails, MoveIpAvailableDestinationsResponse, + getVrackTaskList, + getVrackTaskQueryKey, + getVrackTaskDetailsQueryKey, + getVrackTaskDetails, } from '@/data/api'; -import { IpTaskStatus, IpTaskFunction, IpTask } from '@/types'; -import { INVALIDATED_REFRESH_PERIOD, TRANSLATION_NAMESPACES } from '@/utils'; +import { + IpTaskStatus, + IpTaskFunction, + IpTask, + VrackTask, + VrackTaskStatus, + VrackTaskFunction, +} from '@/types'; +import { + getTypeByServiceName, + INVALIDATED_REFRESH_PERIOD, + TRANSLATION_NAMESPACES, +} from '@/utils'; +import { IpTypeEnum } from '@/data/constants'; const getMoveIpOngoingTasksQueryKey = (ip: string) => [ 'ipMoveOngoingTasks', ip.includes('/') ? ip : `${ip}/32`, ]; +export function useVrackMoveTasks({ + ip, + serviceName, + enabled = true, +}: { + ip: string; + serviceName?: string; + enabled?: boolean; +}) { + const queryClient = useQueryClient(); + const { t } = useTranslation([ + TRANSLATION_NAMESPACES.ips, + TRANSLATION_NAMESPACES.moveIp, + ]); + const { clearNotifications, addSuccess } = useNotifications(); + const { trackPage } = useOvhTracking(); + const { data, isLoading } = useQuery({ + queryKey: getVrackTaskQueryKey({ serviceName }), + queryFn: () => getVrackTaskList({ serviceName }), + enabled: + !!serviceName && + getTypeByServiceName(serviceName) === IpTypeEnum.VRACK && + enabled, + }); + + const queries = useQueries({ + queries: (data?.data ?? []).map((taskId) => ({ + queryKey: getVrackTaskDetailsQueryKey({ serviceName, taskId }), + queryFn: async () => { + try { + return await getVrackTaskDetails({ serviceName, taskId }); + } catch (err) { + if ((err as ApiError).status === 404) { + clearNotifications(); + addSuccess( + t('moveIpDoneMessage', { + ip, + ns: TRANSLATION_NAMESPACES.moveIp, + }), + ); + trackPage({ + pageType: PageType.bannerSuccess, + pageName: 'move-ip_success', + }); + queryClient.invalidateQueries({ + queryKey: getIpDetailsQueryKey({ ip }), + }); + return {} as ApiResponse; + } + throw err; + } + }, + refetchInterval: (query: Query, ApiError>) => { + if ( + !query.state.error && + [ + VrackTaskFunction.addBlockToBridgeDomain, + VrackTaskFunction.removeBlockFromBridgeDomain, + ].includes(query.state.data?.data?.function) + ) { + if ( + [ + VrackTaskStatus.init, + VrackTaskStatus.todo, + VrackTaskStatus.doing, + ].includes(query.state.data?.data?.status) + ) { + return INVALIDATED_REFRESH_PERIOD; + } + + if (query.state.data?.data?.status === VrackTaskStatus.done) { + clearNotifications(); + addSuccess( + t('moveIpDoneMessage', { + ip, + ns: TRANSLATION_NAMESPACES.moveIp, + }), + ); + trackPage({ + pageType: PageType.bannerSuccess, + pageName: 'move-ip_success', + }); + queryClient.invalidateQueries({ + queryKey: getIpDetailsQueryKey({ ip }), + }); + } + + return undefined; + } + + return undefined; + }, + })), + }); + + return { + isVrackTasksLoading: isLoading || queries.some((q) => q.isLoading), + vrackTasksError: queries.find((q) => q.error)?.error as ApiError, + hasOnGoingVrackMoveTasks: + queries.map((q) => q.data).filter((d): d is ApiResponse => !!d) + .length > 0, + }; +} + export function useMoveIpTasks({ ip, enabled, @@ -140,12 +260,24 @@ export function useMoveIpTasks({ export function useMoveIpService({ ip, + serviceName, onMoveIpSuccess, }: { ip: string; + serviceName?: string; onMoveIpSuccess?: () => void; }) { const queryClient = useQueryClient(); + const { + hasOnGoingVrackMoveTasks, + isVrackTasksLoading, + vrackTasksError, + } = useVrackMoveTasks({ + ip, + serviceName, + enabled: + !!serviceName && getTypeByServiceName(serviceName) === IpTypeEnum.VRACK, + }); const { hasOnGoingMoveIpTask, isTasksLoading, taskError } = useMoveIpTasks({ ip, }); @@ -172,7 +304,13 @@ export function useMoveIpService({ }; } }, - enabled: !isTasksLoading && !taskError && !hasOnGoingMoveIpTask, + enabled: + !isTasksLoading && + !taskError && + !hasOnGoingMoveIpTask && + !hasOnGoingVrackMoveTasks && + !isVrackTasksLoading && + !vrackTasksError, }); const isDedicatedCloudService = useCallback( @@ -210,7 +348,10 @@ export function useMoveIpService({ mutationFn: apiPostMoveIp, onSuccess: async () => { queryClient.invalidateQueries({ - queryKey: getMoveIpOngoingTasksQueryKey(ip), + queryKey: + serviceName && getTypeByServiceName(serviceName) === IpTypeEnum.VRACK + ? getVrackTaskQueryKey({ serviceName }) + : getMoveIpOngoingTasksQueryKey(ip), }); onMoveIpSuccess?.(); }, diff --git a/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/__snapshots__/moveIp.spec.tsx.snap b/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/__snapshots__/moveIp.spec.tsx.snap deleted file mode 100644 index d62214d0890a..000000000000 --- a/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/__snapshots__/moveIp.spec.tsx.snap +++ /dev/null @@ -1,49 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`Move IP modal > displays a success message if the IP migration order is successful 1`] = `"Voulez vous vraiment déplacer l'IP 239.99.244.14/32 vers 1122456 ?"`; - -exports[`Move IP modal > lets you go to step 2 if you fill correctly destination service and next hop 1`] = ` - - - Voulez vous vraiment déplacer l'IP - - 239.99.244.14/32 - - vers - - 01234506 - - ? - - -`; - -exports[`Move IP modal > lets you go to step 2 if you fill correctly destination service and next hop 2`] = ` - - - Voulez vous vraiment déplacer l'IP - - 239.99.244.14/32 - - vers - - 01234506 - - sur - - 111.22.333.444 - - ? - - -`; diff --git a/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/moveIp.spec.tsx b/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/moveIp.spec.tsx index 69414862d1f8..3cea9d965d4b 100644 --- a/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/moveIp.spec.tsx +++ b/packages/manager/apps/ips/src/pages/actions/moveIp/__tests__/moveIp.spec.tsx @@ -1,4 +1,4 @@ -import { waitFor, screen, fireEvent } from '@testing-library/react'; +import { within, waitFor, screen, fireEvent } from '@testing-library/react'; import { describe } from 'vitest'; import { ODS_ICON_NAME } from '@ovhcloud/ods-components'; import { @@ -82,7 +82,10 @@ describe('Move IP modal', () => { ); const confirmNode = await screen.getByText(confirmText, { exact: false }); await waitFor( - () => expect(confirmNode.parentElement).toMatchSnapshot(), + () => + expect( + within(confirmNode.parentElement).getByText(service), + ).toBeInTheDocument(), WAIT_FOR_DEFAULT_OPTIONS, ); }); @@ -108,7 +111,13 @@ describe('Move IP modal', () => { '', ); const confirmNode = await screen.getByText(confirmText, { exact: false }); - expect(confirmNode.parentElement.innerHTML).toMatchSnapshot(); + await waitFor( + () => + expect( + within(confirmNode.parentElement).getByText(service), + ).toBeInTheDocument(), + WAIT_FOR_DEFAULT_OPTIONS, + ); const confirmButton = await getButtonByLabel({ container, diff --git a/packages/manager/apps/ips/src/pages/actions/moveIp/moveIp.page.tsx b/packages/manager/apps/ips/src/pages/actions/moveIp/moveIp.page.tsx index 2dd3a4f8c359..6f947e157c4c 100644 --- a/packages/manager/apps/ips/src/pages/actions/moveIp/moveIp.page.tsx +++ b/packages/manager/apps/ips/src/pages/actions/moveIp/moveIp.page.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { NAMESPACES } from '@ovh-ux/manager-common-translations'; -import { Trans, useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { ODS_MESSAGE_COLOR, ODS_MODAL_COLOR } from '@ovhcloud/ods-components'; import { useNavigate, useParams, useSearchParams } from 'react-router-dom'; import { OdsMessage } from '@ovhcloud/ods-components/react'; @@ -18,16 +18,10 @@ import { import { fromIdToIp, ipFormatter, TRANSLATION_NAMESPACES } from '@/utils'; import Step1 from './components/Step1'; import Step2 from './components/Step2'; -import { - useGetIpdetails, - useMoveIpService, - useGetProductServices, -} from '@/data/hooks'; +import { useGetIpdetails, useMoveIpService } from '@/data/hooks'; import { TOTAL_STEP_NUMBER } from './moveIp.constant'; import { ApiErrorMessage } from '@/components/ApiError/ApiErrorMessage'; import { ipParkingOptionValue } from '@/types'; -import { LinkToOtherApp } from '@/components/LinkToOtherApp/LinkToOtherApp'; -import { IpTypeEnum, PRODUCT_PATHS_AND_CATEGORIES } from '@/data/constants'; export default function MoveIpModal() { const { id } = useParams<{ id: string }>(); @@ -64,12 +58,6 @@ export default function MoveIpModal() { enabled: true, }); - const { - serviceByCategory: { [IpTypeEnum.VRACK]: vrack }, - isLoading: isServiceListLoading, - error: serviceListError, - } = useGetProductServices([PRODUCT_PATHS_AND_CATEGORIES[IpTypeEnum.VRACK]]); - const { isMoveIpServiceLoading, moveIpServiceError, @@ -81,6 +69,7 @@ export default function MoveIpModal() { isDedicatedCloudService, } = useMoveIpService({ ip, + serviceName: ipDetails?.routedTo?.serviceName, onMoveIpSuccess: () => { addSuccess( t('moveIpSuccessMessage', { ip: ipGroup, destinationService }), @@ -93,14 +82,14 @@ export default function MoveIpModal() { }, }); - const error = React.useMemo( - () => ipDetailsError || serviceListError || moveIpServiceError, - [ipDetailsError, serviceListError, moveIpServiceError], - ); + const error = React.useMemo(() => ipDetailsError || moveIpServiceError, [ + ipDetailsError, + moveIpServiceError, + ]); const isLoading = React.useMemo( - () => isIpDetailLoading || isServiceListLoading || isMoveIpServiceLoading, - [isIpDetailLoading, isServiceListLoading, isMoveIpServiceLoading], + () => isIpDetailLoading || isMoveIpServiceLoading, + [isIpDetailLoading, isMoveIpServiceLoading], ); const nextHopList = React.useMemo(() => getNextHopList(destinationService), [ @@ -108,14 +97,6 @@ export default function MoveIpModal() { destinationService, ]); - const isAttachedToSomeVrack = React.useMemo( - () => - vrack?.some( - ({ serviceName }) => serviceName === ipDetails?.routedTo?.serviceName, - ), - [vrack, ipDetails?.routedTo?.serviceName], - ); - const props: ModalProps = { isOpen: true, heading: `${t('move', { ns: NAMESPACES.ACTIONS })} Additional IP`, @@ -130,13 +111,12 @@ export default function MoveIpModal() { isPrimaryButtonDisabled: !!error || hasOnGoingMoveIpTask || - isAttachedToSomeVrack || !destinationService || (isDedicatedCloudService(destinationService) && !nextHop), primaryLabel: t(currentStep === 1 ? 'next' : 'confirm', { ns: NAMESPACES.ACTIONS, }), - secondaryLabel: t(currentStep === 1 ? 'cancel' : 'previous', { + secondaryLabel: t(currentStep === 1 || error ? 'cancel' : 'previous', { ns: NAMESPACES.ACTIONS, }), onPrimaryButtonClick: () => { @@ -147,13 +127,18 @@ export default function MoveIpModal() { actionType: 'action', actions: ['move_additional-ip', 'confirm'], }); - postMoveIp({ ip, to: destinationService, nexthop: nextHop }); + postMoveIp({ + ip, + to: destinationService, + nexthop: nextHop, + serviceName: ipDetails?.routedTo?.serviceName, + }); } else { setCurrentStep((prev) => prev + 1); } }, onSecondaryButtonClick: () => { - if (currentStep === 1) { + if (currentStep === 1 || error) { closeModal(); } else { setCurrentStep((prev) => prev - 1); @@ -179,29 +164,6 @@ export default function MoveIpModal() { ); } - if (isAttachedToSomeVrack) { - return ( - - -
- - ), - }} - /> -
-
-
- ); - } - return ( diff --git a/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.spec.tsx b/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.spec.tsx index 145842b1c041..b2a57d3735a9 100644 --- a/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.spec.tsx +++ b/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.spec.tsx @@ -21,9 +21,17 @@ const useMoveIpTasksMocks = vi.hoisted(() => vi.fn(() => ({ hasOnGoingMoveIpTask: false, isTasksLoading: false })), ); +const useVrackMoveTasksMock = vi.hoisted(() => + vi.fn(() => ({ + isVrackTasksLoading: false, + hasOnGoingVrackMoveTasks: false, + })), +); + vi.mock('@/data/hooks/ip', () => ({ useGetIpdetails: useGetIpDetailsMock, useMoveIpTasks: useMoveIpTasksMocks, + useVrackMoveTasks: useVrackMoveTasksMock, })); vi.mock('../SkeletonCell/SkeletonCell', () => ({ diff --git a/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.tsx b/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.tsx index 8cdb3e36bf3b..e79603c0eb2c 100644 --- a/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.tsx +++ b/packages/manager/apps/ips/src/pages/listing/ipListing/components/DatagridCells/IpAttachedService/IpAttachedService.tsx @@ -7,9 +7,14 @@ import { } from '@ovh-ux/manager-react-shell-client'; import { OdsLink } from '@ovhcloud/ods-components/react'; import { ODS_LINK_COLOR } from '@ovhcloud/ods-components'; -import { useGetIpdetails, useMoveIpTasks } from '@/data/hooks/ip'; +import { + useGetIpdetails, + useMoveIpTasks, + useVrackMoveTasks, +} from '@/data/hooks/ip'; import { SkeletonCell } from '../SkeletonCell/SkeletonCell'; -import { getLinkByServiceName } from '@/utils'; +import { getLinkByServiceName, getTypeByServiceName } from '@/utils'; +import { IpTypeEnum } from '@/data/constants'; export type IpAttachedServiceProps = { ip: string; @@ -28,9 +33,15 @@ export const IpAttachedService = ({ ip }: IpAttachedServiceProps) => { const { trackClick } = useOvhTracking(); const { ipDetails, isLoading } = useGetIpdetails({ ip }); + const { isVrackTasksLoading, hasOnGoingVrackMoveTasks } = useVrackMoveTasks({ + ip, + serviceName: ipDetails?.routedTo?.serviceName, + }); const { hasOnGoingMoveIpTask, isTasksLoading } = useMoveIpTasks({ ip, - enabled: ipDetails?.routedTo?.serviceName !== undefined, + enabled: + !!ipDetails?.routedTo?.serviceName && + getTypeByServiceName(ipDetails.routedTo.serviceName) !== IpTypeEnum.VRACK, }); useEffect(() => { @@ -42,12 +53,19 @@ export const IpAttachedService = ({ ip }: IpAttachedServiceProps) => { return ( {ipDetails?.routedTo?.serviceName ? ( <> {serviceUrl ? (