From d2dbe0ed090ac97e641dfbb0dbdb79c54da91a67 Mon Sep 17 00:00:00 2001 From: Yoann Fievez Date: Mon, 7 Oct 2024 14:09:13 +0200 Subject: [PATCH] feat(pci-load-balancer): add edit page policy ref: DTCORE-2641 Signed-off-by: Yoann Fievez --- .../src/api/data/l7Policies.ts | 20 + .../src/api/hook/useL7Policy.tsx | 44 +- .../detail/listeners/l7/Actions.component.tsx | 2 +- .../listeners/l7/PolicyForm.component.tsx | 493 +++++++++--------- .../components/listing/Actions.component.tsx | 2 +- .../listeners/l7/create/Create.page.tsx | 55 +- .../listeners/l7/delete/Delete.page.tsx | 4 +- .../detail/listeners/l7/edit/Edit.page.tsx | 98 ++++ .../detail/listeners/l7/list/List.page.tsx | 3 - .../apps/pci-load-balancer/src/routes.tsx | 5 + 10 files changed, 436 insertions(+), 290 deletions(-) create mode 100644 packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/edit/Edit.page.tsx diff --git a/packages/manager/apps/pci-load-balancer/src/api/data/l7Policies.ts b/packages/manager/apps/pci-load-balancer/src/api/data/l7Policies.ts index a635d0d1273f..e4a09bae0ab0 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/data/l7Policies.ts +++ b/packages/manager/apps/pci-load-balancer/src/api/data/l7Policies.ts @@ -69,3 +69,23 @@ export const createPolicy = async ( ); return data; }; + +export const updatePolicy = async ( + projectId: string, + region: string, + policy: TL7Policy, +) => { + const { data } = await v6.put( + `/cloud/project/${projectId}/region/${region}/loadbalancing/l7Policy/${policy.id}`, + { + name: policy.name, + position: policy.position, + action: policy.action, + redirectHttpCode: policy.redirectHttpCode, + redirectPoolId: policy.redirectPoolId, + redirectPrefix: policy.redirectPrefix, + redirectUrl: policy.redirectUrl, + }, + ); + return data; +}; diff --git a/packages/manager/apps/pci-load-balancer/src/api/hook/useL7Policy.tsx b/packages/manager/apps/pci-load-balancer/src/api/hook/useL7Policy.tsx index 2da51ce937a9..431f6ba290e1 100644 --- a/packages/manager/apps/pci-load-balancer/src/api/hook/useL7Policy.tsx +++ b/packages/manager/apps/pci-load-balancer/src/api/hook/useL7Policy.tsx @@ -8,6 +8,7 @@ import { getL7Policies, getPolicy, TL7Policy, + updatePolicy, } from '@/api/data/l7Policies'; import { paginateResults, sortResults } from '@/helpers'; import { ACTION_LABELS, ACTIONS } from '@/constants'; @@ -112,7 +113,7 @@ export const useDeletePolicy = ({ onError, onSuccess: async () => { await queryClient.invalidateQueries({ - queryKey: ['l7Policies'], + queryKey: ['l7Policies', projectId], }); onSuccess(); }, @@ -127,7 +128,6 @@ type CreatePolicyProps = { projectId: string; listenerId: string; region: string; - policy: TL7Policy; onError: (cause: Error) => void; onSuccess: (newPolicy: TL7Policy) => void; }; @@ -135,23 +135,53 @@ type CreatePolicyProps = { export const useCreatePolicy = ({ projectId, listenerId, - policy, region, onError, onSuccess, }: CreatePolicyProps) => { - const mutation = useMutation({ - mutationFn: async () => createPolicy(projectId, region, listenerId, policy), + const mutation = useMutation({ + mutationFn: async (policy: TL7Policy) => + createPolicy(projectId, region, listenerId, policy), onError, onSuccess: async (newPolicy) => { await queryClient.invalidateQueries({ - queryKey: ['l7Policies'], + queryKey: ['l7Policies', projectId], }); onSuccess(newPolicy); }, }); return { - createPolicy: () => mutation.mutate(), + createPolicy: (policy: TL7Policy) => mutation.mutate(policy), + ...mutation, + }; +}; + +type UpdatePolicyProps = { + projectId: string; + region: string; + onError: (cause: Error) => void; + onSuccess: (policy: TL7Policy) => void; +}; + +export const useUpdatePolicy = ({ + projectId, + region, + onError, + onSuccess, +}: UpdatePolicyProps) => { + const mutation = useMutation({ + mutationFn: async (policy: TL7Policy) => + updatePolicy(projectId, region, policy), + onError, + onSuccess: async (policy: TL7Policy) => { + await queryClient.invalidateQueries({ + queryKey: ['l7Policies', projectId], + }); + onSuccess(policy); + }, + }); + return { + updatePolicy: (policy: TL7Policy) => mutation.mutate(policy), ...mutation, }; }; diff --git a/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/Actions.component.tsx b/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/Actions.component.tsx index 046d2772c492..94853756cb47 100644 --- a/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/Actions.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/Actions.component.tsx @@ -13,7 +13,7 @@ export default function ActionsComponent({ const items = [ { id: 0, - href: '', + href: useHref(`../${l7PoliciesId}/edit`), label: t('octavia_load_balancer_list_l7_policies_actions_edit'), }, { diff --git a/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/PolicyForm.component.tsx b/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/PolicyForm.component.tsx index 0d9bd75abfef..70ad10444844 100644 --- a/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/PolicyForm.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/components/detail/listeners/l7/PolicyForm.component.tsx @@ -5,18 +5,13 @@ import { OsdsInput, OsdsSelect, OsdsSelectOption, - OsdsSpinner, OsdsText, } from '@ovhcloud/ods-components/react'; -import { - ODS_THEME_COLOR_INTENT, - ODS_THEME_TYPOGRAPHY_LEVEL, -} from '@ovhcloud/ods-common-theming'; +import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; import { useTranslation } from 'react-i18next'; import { ODS_BUTTON_VARIANT, ODS_INPUT_TYPE, - ODS_SPINNER_SIZE, OdsSelectValueChangeEventDetail, OsdsSelectCustomEvent, } from '@ovhcloud/ods-components'; @@ -32,39 +27,39 @@ import { URL_PATTERN, URL_PLACEHOLDER, } from '@/constants'; -import { useListener } from '@/api/hook/useListener'; -import { useAllLoadBalancerPools } from '@/api/hook/usePool'; import LabelComponent from '@/components/form/Label.component'; +import { TLoadBalancerListener } from '@/api/data/load-balancer'; +import { TLoadBalancerPool } from '@/api/data/pool'; type PolicyFormProps = { - policy: TL7Policy; - isPendingAction: boolean; - onChange: (policy: TL7Policy) => void; - onSubmit: () => void; + policy: TL7Policy | null; + pools: TLoadBalancerPool[]; + listener: TLoadBalancerListener; + onSubmit: (policy: TL7Policy) => void; + submitButtonText?: string; onCancel: () => void; }; export default function PolicyForm({ policy, - onChange, + pools, + listener, onSubmit, - isPendingAction, + submitButtonText, onCancel, -}: PolicyFormProps) { - const { projectId, loadBalancerId, region, listenerId } = useParams(); +}: Readonly) { const { t } = useTranslation('octavia-load-balancer-l7-policy-form'); const { t: tPciCommon } = useTranslation('pci-common'); - const [policyState, setPolicyState] = useState(policy); - const { data: listener, isPending: isPendingListener } = useListener({ - projectId, - region, - loadBalancerId, + const { listenerId } = useParams(); + const [policyState, setPolicyState] = useState({ listenerId, - }); - const { data: pools, isPending: isPendingPools } = useAllLoadBalancerPools({ - projectId, - region, - loadBalancerId, - }); + position: 1, + redirectHttpCode: undefined, + redirectPoolId: undefined, + redirectPrefix: undefined, + redirectUrl: undefined, + name: '', + action: '', + } as TL7Policy); const filteredPools = useMemo(() => { if (pools && listener) { @@ -88,16 +83,14 @@ export default function PolicyForm({ setHasErrorName(isTouchedName && policyState.name === ''); }, [policyState.name, isTouchedName]); - const isPending = isPendingListener || isPendingPools || isPendingAction; - const onActionChange = ( event: OsdsSelectCustomEvent, ) => { const action = event.detail.value as string; const policyUpdate = { - ...policyState, action, - }; + } as TL7Policy; + if ([ACTIONS.REDIRECT_TO_URL, ACTIONS.REDIRECT_PREFIX].includes(action)) { policyUpdate.redirectHttpCode = 302; } else { @@ -111,8 +104,11 @@ export default function PolicyForm({ policyUpdate.redirectPrefix = undefined; setIsTouchedPrefix(false); } - setPolicyState(policyUpdate); - onChange(policyUpdate); + setPolicyState((state) => ({ + ...state, + ...policyUpdate, + action, + })); }; const prefixError = useMemo(() => { @@ -148,258 +144,247 @@ export default function PolicyForm({ } }, [policyState]); + useEffect(() => { + if (policy) { + setPolicyState(() => policy); + } + }, [policy]); return ( <> - {isPending && } - {!isPending && ( -
+ + + { + setPolicyState((state) => ({ + ...state, + name: event.detail.value, + })); + }} + onOdsInputBlur={() => { + setIsTouchedName(true); + }} + /> + + { + setPolicyState((state) => ({ + ...state, + position: quantity, + })); + }} + /> + + + + + {t('octavia_load_balancer_create_l7_policy_action_default')} + + {ACTIONS_LIST.map((action) => ( + + {action.label} + + ))} + + {policyState.action && ( + + {t( + `octavia_load_balancer_create_l7_policy_action_${policyState.action}`, + )} + + )} + + {policyState.action === ACTIONS.REDIRECT_TO_URL && ( + <> { - const policyUpdate = { - ...policyState, - name: event.detail.value, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); + setPolicyState((state) => ({ + ...state, + redirectUrl: event.detail.value, + })); }} onOdsInputBlur={() => { - setIsTouchedName(true); + setIsTouchedURL(true); }} /> - { - const policyUpdate = { - ...policyState, - position: quantity, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - /> - - - {t('octavia_load_balancer_create_l7_policy_action_default')} - - {ACTIONS_LIST.map((action) => ( - - {action.label} + { + setPolicyState((state) => ({ + ...state, + redirectHttpCode: event.detail.value as number, + })); + }} + > + {REDIRECT_HTTP_CODES.map((httpCode) => ( + + {httpCode} ))} - {policyState.action && ( - - {t( - `octavia_load_balancer_create_l7_policy_action_${policyState.action}`, - )} - - )} - {policyState.action === ACTIONS.REDIRECT_TO_URL && ( - <> - - - { - const policyUpdate = { - ...policyState, - redirectUrl: event.detail.value, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - onOdsInputBlur={() => { - setIsTouchedURL(true); - }} - /> - - - - { - const policyUpdate = { - ...policyState, - redirectHttpCode: event.detail.value as number, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - > - {REDIRECT_HTTP_CODES.map((httpCode) => ( - - {httpCode} - - ))} - - - - )} - {policyState.action === ACTIONS.REDIRECT_PREFIX && ( - <> - - - { - const policyUpdate = { - ...policyState, - redirectPrefix: event.detail.value, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - onOdsInputBlur={() => { - setIsTouchedPrefix(true); - }} - /> - - - - { - const policyUpdate = { - ...policyState, - redirectHttpCode: event.detail.value as number, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - > - {REDIRECT_HTTP_CODES.map((httpCode) => ( - - {httpCode} - - ))} - - - - )} - {policyState.action === ACTIONS.REDIRECT_TO_POOL && ( - - - { - const policyUpdate = { - ...policyState, - redirectPoolId: event.detail.value as string, - }; - setPolicyState(() => policyUpdate); - onChange(policyUpdate); - }} - > - - {t('octavia_load_balancer_create_l7_policy_pool_default')} - - {filteredPools.map((pool) => ( - - {pool.name} - - ))} - - - )} -
- - {t('octavia_load_balancer_create_l7_policy_cancel')} - - + )} + {policyState.action === ACTIONS.REDIRECT_PREFIX && ( + <> + + + { + setPolicyState((state) => ({ + ...state, + redirectPrefix: event.detail.value, + })); + }} + onOdsInputBlur={() => { + setIsTouchedPrefix(true); + }} + /> + + + + { + setPolicyState((state) => ({ + ...state, + redirectHttpCode: event.detail.value as number, + })); + }} > - {t('octavia_load_balancer_create_l7_policy_submit')} - -
-
+ {REDIRECT_HTTP_CODES.map((httpCode) => ( + + {httpCode} + + ))} + + + + )} + {policyState.action === ACTIONS.REDIRECT_TO_POOL && ( + + + { + setPolicyState((state) => ({ + ...state, + redirectPoolId: event.detail.value as string, + })); + }} + > + + {t('octavia_load_balancer_create_l7_policy_pool_default')} + + {filteredPools.map((pool) => ( + + {pool.name} + + ))} + + )} +
+ + {t('octavia_load_balancer_create_l7_policy_cancel')} + + onSubmit(policyState)} + > + {submitButtonText || + t('octavia_load_balancer_create_l7_policy_submit')} + +
); } diff --git a/packages/manager/apps/pci-load-balancer/src/components/listing/Actions.component.tsx b/packages/manager/apps/pci-load-balancer/src/components/listing/Actions.component.tsx index e4bb1eaa8a6f..408ec1aad459 100644 --- a/packages/manager/apps/pci-load-balancer/src/components/listing/Actions.component.tsx +++ b/packages/manager/apps/pci-load-balancer/src/components/listing/Actions.component.tsx @@ -1,6 +1,6 @@ import { useHref } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { ActionMenu, useProjectUrl } from '@ovh-ux/manager-react-components'; +import { ActionMenu } from '@ovh-ux/manager-react-components'; import { TLoadBalancer } from '@/api/data/load-balancer'; type ActionsComponentProps = { diff --git a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/create/Create.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/create/Create.page.tsx index 28238fe2fd06..e483b421e751 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/create/Create.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/create/Create.page.tsx @@ -4,12 +4,14 @@ import { useProjectUrl, } from '@ovh-ux/manager-react-components'; import { Translation, useTranslation } from 'react-i18next'; -import { useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { ApiError } from '@ovh-ux/manager-core-api'; +import { ODS_SPINNER_SIZE } from '@ovhcloud/ods-components'; +import { OsdsSpinner } from '@ovhcloud/ods-components/react'; import PolicyForm from '@/components/detail/listeners/l7/PolicyForm.component'; -import { TL7Policy } from '@/api/data/l7Policies'; import { useCreatePolicy } from '@/api/hook/useL7Policy'; +import { useListener } from '@/api/hook/useListener'; +import { useAllLoadBalancerPools } from '@/api/hook/usePool'; export default function CreatePage() { const { addSuccess, addError } = useNotifications(); @@ -17,22 +19,24 @@ export default function CreatePage() { const projectUrl = useProjectUrl('public-cloud'); const { listenerId, projectId, region, loadBalancerId } = useParams(); const navigate = useNavigate(); - const [policy, setPolicy] = useState({ + + const { data: listener, isPending: isPendingListener } = useListener({ + projectId, + region, + loadBalancerId, listenerId, - position: 1, - redirectHttpCode: undefined, - redirectPoolId: undefined, - redirectPrefix: undefined, - redirectUrl: undefined, - name: '', - action: '', - } as TL7Policy); + }); - const { createPolicy, isPending } = useCreatePolicy({ + const { data: pools, isPending: isPendingPools } = useAllLoadBalancerPools({ + projectId, + region, + loadBalancerId, + }); + + const { createPolicy, isPending: isPendingCreate } = useCreatePolicy({ projectId, listenerId, region, - policy, onError(error: ApiError) { addError( @@ -45,7 +49,6 @@ export default function CreatePage() { , true, ); - navigate('..'); }, onSuccess(newPolicy) { const l7RulesCreationLink = `${projectUrl}/octavia-load-balancer/${region}/${loadBalancerId}/listeners/${listenerId}/l7/${newPolicy?.id}/rules/create`; @@ -55,7 +58,7 @@ export default function CreatePage() {
@@ -75,13 +79,20 @@ export default function CreatePage() { title={t('octavia_load_balancer_create_l7_policy_title')} />
- navigate('..')} - onSubmit={createPolicy} - isPendingAction={isPending} - onChange={(policyUpdated) => setPolicy(() => policyUpdated)} - /> + {isPending ? ( + + ) : ( + navigate('..')} + onSubmit={createPolicy} + listener={listener} + pools={pools} + submitButtonText={t( + 'octavia_load_balancer_create_l7_policy_create_submit', + )} + /> + )} ); } diff --git a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/delete/Delete.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/delete/Delete.page.tsx index 1e3abab63224..0a6a76f9f97b 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/delete/Delete.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/delete/Delete.page.tsx @@ -16,7 +16,7 @@ export default function DeletePage() { const onClose = () => { navigate('..'); }; - const { data: policy, isPending: isPendingLoadBalancer } = useGetPolicy( + const { data: policy, isPending: isPendingPolicy } = useGetPolicy( projectId, policyId, region, @@ -63,7 +63,7 @@ export default function DeletePage() { navigate('..'); }; - const isPending = isPendingLoadBalancer || isPendingDelete; + const isPending = isPendingPolicy || isPendingDelete; return ( + {(_t) => + _t('octavia_load_balancer_global_error', { + message: error?.response?.data?.message || error?.message || null, + requestId: error?.config?.headers['X-OVH-MANAGER-REQUEST-ID'], + }) + } + , + true, + ); + }, + onSuccess(updatedPolicy) { + addSuccess( + + {(_t) => ( + + )} + , + true, + ); + navigate('..'); + }, + }); + const isPending = + isPendingListener || isPendingPools || isPendingCreate || isPendingPolicy; + return ( +
+
+ +
+ {isPending ? ( + + ) : ( + navigate('..')} + onSubmit={updatePolicy} + /> + )} +
+ ); +} diff --git a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/list/List.page.tsx b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/list/List.page.tsx index 3c0949c48390..2c3ec961e56b 100644 --- a/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/list/List.page.tsx +++ b/packages/manager/apps/pci-load-balancer/src/pages/detail/listeners/l7/list/List.page.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next'; import { Outlet, useNavigate, useParams } from 'react-router-dom'; -import { useProject } from '@ovh-ux/manager-pci-common'; import { Datagrid, FilterAdd, @@ -10,11 +9,9 @@ import { useColumnFilters, useDataGrid, useNotifications, - useProjectUrl, } from '@ovh-ux/manager-react-components'; import { Suspense, useRef, useState } from 'react'; import { - OsdsBreadcrumb, OsdsButton, OsdsIcon, OsdsPopover, diff --git a/packages/manager/apps/pci-load-balancer/src/routes.tsx b/packages/manager/apps/pci-load-balancer/src/routes.tsx index 778a5df46841..4abd7bf2566e 100644 --- a/packages/manager/apps/pci-load-balancer/src/routes.tsx +++ b/packages/manager/apps/pci-load-balancer/src/routes.tsx @@ -17,6 +17,7 @@ export const ROUTE_PATHS = { L7_LIST: 'list', L7_CREATE: 'create', L7_DELETE: ':policyId/delete', + L7_EDIT: ':policyId/edit', POOLS: 'pools', POOLS_CREATE: 'create', POOLS_EDIT: ':poolId/edit', @@ -47,6 +48,9 @@ const L7PoliciesListPage = lazy(() => const L7PoliciesDeletePage = lazy(() => import('@/pages/detail/listeners/l7/delete/Delete.page'), ); +const L7PoliciesEditPage = lazy(() => + import('@/pages/detail/listeners/l7/edit/Edit.page'), +); const L7PoliciesCreatePage = lazy(() => import('@/pages/detail/listeners/l7/create/Create.page'), ); @@ -105,6 +109,7 @@ const Routes = ( } /> +