Skip to content

Commit 4d4095b

Browse files
committed
refactor(pci-project): refactor payment section in order to separate logic
ref: #MANAGER-19774 Signed-off-by: LIDRISSI Hamid <[email protected]>
1 parent ea926b2 commit 4d4095b

File tree

9 files changed

+228
-72
lines changed

9 files changed

+228
-72
lines changed

packages/manager/apps/pci-project/src/pages/creation/Creation.page.tsx

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ import {
1919
} from '@/data/hooks/useCart';
2020
import FullPageSpinner from '@/components/FullPageSpinner';
2121
import { useConfigForm } from './hooks/useConfigForm';
22-
import { useCreation } from './hooks/useCreation';
22+
import { useProjectCreation } from './hooks/useProjectCreation';
2323
import { useStepper } from './hooks/useStepper';
2424
import ConfigStep from './steps/ConfigStep';
2525
import PaymentStep from './steps/PaymentStep';
2626
import { useWillPayment } from './hooks/useWillPayment';
27+
import { usePaymentSubmission } from './hooks/usePaymentSubmission';
2728

2829
export default function ProjectCreation() {
2930
const { t } = useTranslation([
@@ -88,7 +89,7 @@ export default function ProjectCreation() {
8889
needToCheckCustomerInfo,
8990
billingHref,
9091
handleProjectCreation,
91-
} = useCreation({ t, cart, projectItem });
92+
} = useProjectCreation({ t, cart, projectItem });
9293

9394
const {
9495
isPaymentMethodSaveRequired,
@@ -100,6 +101,14 @@ export default function ProjectCreation() {
100101
handleRegisteredPaymentMethodSelected,
101102
} = useWillPayment();
102103

104+
const { handlePaymentSubmit } = usePaymentSubmission({
105+
isPaymentMethodSaveRequired,
106+
isPaymentMethodSaved,
107+
hasDefaultPaymentMethod,
108+
triggerSavePaymentMethod,
109+
onProjectCreation: handleProjectCreation,
110+
});
111+
103112
useEffect(() => {
104113
if (isLoadingConfigForm) {
105114
return;
@@ -152,23 +161,6 @@ export default function ProjectCreation() {
152161

153162
const handleCancel = useCallback(() => navigate('..'), [navigate]);
154163

155-
const handlePaymentSubmit = () => {
156-
if (isPaymentMethodSaveRequired) {
157-
triggerSavePaymentMethod();
158-
} else if (hasDefaultPaymentMethod) {
159-
handleProjectCreation();
160-
}
161-
};
162-
163-
/**
164-
* If the payment method is saved, proceed to Creation directly
165-
*/
166-
useEffect(() => {
167-
if (isPaymentMethodSaved) {
168-
handleProjectCreation();
169-
}
170-
}, [isPaymentMethodSaved]);
171-
172164
if (!cart || !projectItem) {
173165
return <FullPageSpinner />;
174166
}

packages/manager/apps/pci-project/src/pages/creation/components/payment/WillPayment.component.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { OdsSpinner } from '@ovhcloud/ods-components/react';
21
import React, { lazy, Suspense, useEffect, useRef } from 'react';
32
import { TWillPaymentConfig } from '@/types/WillPayment.type';
3+
import { setupRegisteredPaymentMethodListener } from '../../utils/paymentEvents';
44

55
type WillPaymentModuleProps = {
66
slotRef: React.RefObject<HTMLDivElement>;
@@ -24,7 +24,10 @@ const WillPaymentModule = lazy(() =>
2424
useEffect(() => {
2525
const { slotRef, config } = props;
2626

27-
// Initialize the payment module with configuration
27+
if (!slotRef.current) {
28+
return undefined;
29+
}
30+
2831
setUpWillPayment((slotRef.current as unknown) as HTMLSlotElement, {
2932
configuration: config,
3033
});
@@ -35,7 +38,7 @@ const WillPaymentModule = lazy(() =>
3538
slotRef.current.innerHTML = '';
3639
}
3740
};
38-
}, [props.slotRef]);
41+
}, [props.slotRef, props.config]);
3942

4043
return null;
4144
},
@@ -53,20 +56,17 @@ function WillPaymentComponent({
5356
const slotRef = useRef<HTMLDivElement>(null);
5457

5558
useEffect(() => {
56-
if (slotRef.current) {
57-
slotRef.current.addEventListener(
58-
'WP::USER_ACTION::REGISTERED_PM_SELECTED',
59-
(event) => onRegisteredPaymentMethodSelected(event as CustomEvent),
60-
);
61-
}
59+
const cleanup = setupRegisteredPaymentMethodListener(
60+
onRegisteredPaymentMethodSelected,
61+
);
62+
63+
return cleanup || undefined;
6264
}, [slotRef, onRegisteredPaymentMethodSelected]);
6365

6466
return (
6567
<div id="will-payment-event-bus" ref={slotRef}>
66-
<Suspense fallback={<OdsSpinner />}>
67-
{slotRef.current && (
68-
<WillPaymentModule slotRef={slotRef} config={config} />
69-
)}
68+
<Suspense>
69+
<WillPaymentModule slotRef={slotRef} config={config} />
7070
</Suspense>
7171
</div>
7272
);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { useCallback, useEffect } from 'react';
2+
3+
export type PaymentSubmissionOptions = {
4+
isPaymentMethodSaveRequired: boolean;
5+
isPaymentMethodSaved: boolean;
6+
hasDefaultPaymentMethod: boolean;
7+
triggerSavePaymentMethod: () => void;
8+
onProjectCreation: () => void;
9+
};
10+
11+
/**
12+
* Hook that manages payment submission logic and orchestration
13+
* Centralizes the decision making for when and how to proceed with payment
14+
*/
15+
export const usePaymentSubmission = ({
16+
isPaymentMethodSaveRequired,
17+
isPaymentMethodSaved,
18+
hasDefaultPaymentMethod,
19+
triggerSavePaymentMethod,
20+
onProjectCreation,
21+
}: PaymentSubmissionOptions) => {
22+
/**
23+
* Handles the payment submission based on current payment state
24+
*/
25+
const handlePaymentSubmit = useCallback(() => {
26+
if (isPaymentMethodSaveRequired) {
27+
// Need to save the payment method first
28+
triggerSavePaymentMethod();
29+
} else if (hasDefaultPaymentMethod) {
30+
// Can proceed directly with project creation
31+
onProjectCreation();
32+
}
33+
}, [
34+
isPaymentMethodSaveRequired,
35+
hasDefaultPaymentMethod,
36+
triggerSavePaymentMethod,
37+
onProjectCreation,
38+
]);
39+
40+
/**
41+
* Auto-proceed with project creation when payment method is saved
42+
*/
43+
useEffect(() => {
44+
if (isPaymentMethodSaved) {
45+
onProjectCreation();
46+
}
47+
}, [isPaymentMethodSaved, onProjectCreation]);
48+
49+
return {
50+
handlePaymentSubmit,
51+
};
52+
};

packages/manager/apps/pci-project/src/pages/creation/hooks/useCreation.tsx renamed to packages/manager/apps/pci-project/src/pages/creation/hooks/useProjectCreation.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ import { AntiFraudError, PCI_PROJECT_ORDER_CART } from '@/constants';
1818
import useAntiFraud from './useAntiFraud';
1919
import { payWithRegisteredPaymentMean } from '@/data/api/payment';
2020

21-
export type UseCreationProps = {
21+
export type UseProjectCreationProps = {
2222
t: TFunction;
2323
cart?: Cart;
2424
projectItem?: OrderedProduct;
2525
};
2626

27-
export const useCreation = ({
27+
export const useProjectCreation = ({
2828
t,
2929
cart,
3030
projectItem,
31-
}: Readonly<UseCreationProps>) => {
31+
}: Readonly<UseProjectCreationProps>) => {
3232
const [isSubmitting, setIsSubmitting] = useState(false);
3333
const [needToCheckCustomerInfo, setNeedToCheckCustomerInfo] = useState(false);
3434
const [billingHref, setBillingHref] = useState('');

packages/manager/apps/pci-project/src/pages/creation/hooks/useWillPayment.tsx

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import { useCallback, useState } from 'react';
2+
import { GlobalStateStatus } from '@/types/WillPayment.type';
3+
import { triggerSavePaymentMethodEvent } from '../utils/paymentEvents';
24
import {
3-
ComponentStatus,
4-
GlobalStateStatus,
5-
PaymentMethodStatus,
6-
} from '@/types/WillPayment.type';
5+
isPaymentMethodSaveRequired as checkIsPaymentMethodSaveRequired,
6+
isPaymentMethodSaved as checkIsPaymentMethodSaved,
7+
isSubmittingEnabled as checkIsSubmittingEnabled,
8+
} from '../utils/paymentLogic';
79

810
/**
911
* Simplified hook for WillPayment integration
@@ -17,11 +19,14 @@ export const useWillPayment = () => {
1719

1820
const [hasDefaultPaymentMethod, setHasDefaultPaymentMethod] = useState(false);
1921

20-
const handleRegisteredPaymentMethodSelected = (event: CustomEvent) => {
21-
if (event && event.detail) {
22-
setHasDefaultPaymentMethod(true);
23-
}
24-
};
22+
const handleRegisteredPaymentMethodSelected = useCallback(
23+
(event: CustomEvent) => {
24+
if (event && event.detail) {
25+
setHasDefaultPaymentMethod(true);
26+
}
27+
},
28+
[],
29+
);
2530

2631
/**
2732
* Handles payment status changes from the WillPayment module
@@ -37,23 +42,17 @@ export const useWillPayment = () => {
3742
* Triggers payment method saving via DOM event
3843
*/
3944
const triggerSavePaymentMethod = useCallback(() => {
40-
const eventBus = document.getElementById('will-payment-event-bus');
41-
if (eventBus) {
42-
eventBus.dispatchEvent(new CustomEvent('GO_SAVE_PAYMENT_METHOD'));
43-
}
45+
triggerSavePaymentMethodEvent();
4446
}, []);
4547

46-
const isPaymentMethodSaveRequired =
47-
globalStateStatus?.componentStatus === ComponentStatus.READY_TO_GO_FORWARD;
48-
49-
const isPaymentMethodSaved =
50-
globalStateStatus?.componentStatus ===
51-
ComponentStatus.PAYMENT_METHOD_SAVED ||
52-
globalStateStatus?.paymentMethodStatus ===
53-
PaymentMethodStatus.PAYMENT_METHOD_SAVED;
54-
55-
const isSubmittingEnabled =
56-
hasDefaultPaymentMethod || isPaymentMethodSaveRequired;
48+
const isPaymentMethodSaveRequired = checkIsPaymentMethodSaveRequired(
49+
globalStateStatus,
50+
);
51+
const isPaymentMethodSaved = checkIsPaymentMethodSaved(globalStateStatus);
52+
const isSubmittingEnabled = checkIsSubmittingEnabled(
53+
hasDefaultPaymentMethod,
54+
isPaymentMethodSaveRequired,
55+
);
5756

5857
return {
5958
hasDefaultPaymentMethod,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useContext } from 'react';
2+
import { ShellContext } from '@ovh-ux/manager-react-shell-client';
3+
import {
4+
TWillPaymentConfig,
5+
GlobalStateStatus,
6+
} from '@/types/WillPayment.type';
7+
8+
export type WillPaymentConfigOptions = {
9+
onPaymentStatusChange?: (status: GlobalStateStatus) => void;
10+
};
11+
12+
/**
13+
* Hook that generates WillPayment configuration automatically
14+
* Centralizes configuration logic and reduces boilerplate
15+
*/
16+
export const useWillPaymentConfig = ({
17+
onPaymentStatusChange,
18+
}: WillPaymentConfigOptions = {}): TWillPaymentConfig => {
19+
const { environment } = useContext(ShellContext);
20+
const user = environment.getUser();
21+
22+
return {
23+
baseUrl: window.location.origin,
24+
onChange: (state: GlobalStateStatus) => onPaymentStatusChange?.(state),
25+
subsidiary: user.ovhSubsidiary,
26+
language: user.language,
27+
hostApp: 'pci',
28+
};
29+
};

packages/manager/apps/pci-project/src/pages/creation/steps/PaymentStep.tsx

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { useContext, useState } from 'react';
2-
3-
import { ShellContext } from '@ovh-ux/manager-react-shell-client';
1+
import { useState } from 'react';
42
import {
53
useIsStartupProgramAvailable,
64
useStartupProgramAmountText,
@@ -15,6 +13,7 @@ import { GlobalStateStatus } from '@/types/WillPayment.type';
1513
import WillPaymentComponent from '../components/payment/WillPayment.component';
1614
import StartupProgram from '../components/startup-program/StartupProgram';
1715
import Voucher from '../components/voucher/Voucher';
16+
import { useWillPaymentConfig } from '../hooks/useWillPaymentConfig';
1817

1918
export type PaymentStepProps = {
2019
cart: Cart;
@@ -41,8 +40,9 @@ export default function PaymentStep({
4140
isAsDefault: false,
4241
});
4342

44-
const { environment } = useContext(ShellContext);
45-
const user = environment.getUser();
43+
const willPaymentConfig = useWillPaymentConfig({
44+
onPaymentStatusChange,
45+
});
4646

4747
const handleVoucherConfigurationChange = (
4848
voucherConfiguration: CartConfiguration | undefined,
@@ -68,14 +68,7 @@ export default function PaymentStep({
6868
/>
6969

7070
<WillPaymentComponent
71-
config={{
72-
baseUrl: window.location.origin,
73-
onChange: (state: GlobalStateStatus) =>
74-
onPaymentStatusChange?.(state),
75-
subsidiary: user.ovhSubsidiary,
76-
language: user.language,
77-
hostApp: 'pci',
78-
}}
71+
config={willPaymentConfig}
7972
onRegisteredPaymentMethodSelected={onRegisteredPaymentMethodSelected}
8073
/>
8174

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Utility functions for WillPayment DOM events
3+
* Centralizes DOM manipulation for better testability
4+
*/
5+
6+
const EVENT_BUS_ID = 'will-payment-event-bus';
7+
const SAVE_PAYMENT_METHOD_EVENT = 'GO_SAVE_PAYMENT_METHOD';
8+
const REGISTERED_PM_EVENT = 'WP::USER_ACTION::REGISTERED_PM_SELECTED';
9+
10+
/**
11+
* Triggers the save payment method event via DOM
12+
* @returns boolean indicating if the event was dispatched successfully
13+
*/
14+
export const triggerSavePaymentMethodEvent = (): boolean => {
15+
const eventBus = document.getElementById(EVENT_BUS_ID);
16+
if (eventBus) {
17+
eventBus.dispatchEvent(new CustomEvent(SAVE_PAYMENT_METHOD_EVENT));
18+
return true;
19+
}
20+
return false;
21+
};
22+
23+
/**
24+
* Sets up listener for registered payment method selection
25+
* @param handler - Function to handle the event
26+
* @returns cleanup function to remove the listener
27+
*/
28+
export const setupRegisteredPaymentMethodListener = (
29+
handler: (event: CustomEvent) => void,
30+
): (() => void) | null => {
31+
const eventBus = document.getElementById(EVENT_BUS_ID);
32+
if (!eventBus) {
33+
return null;
34+
}
35+
36+
const eventHandler = (event: Event) => {
37+
handler(event as CustomEvent);
38+
};
39+
40+
eventBus.addEventListener(REGISTERED_PM_EVENT, eventHandler);
41+
42+
// Return cleanup function
43+
return () => {
44+
eventBus.removeEventListener(REGISTERED_PM_EVENT, eventHandler);
45+
};
46+
};

0 commit comments

Comments
 (0)