Skip to content

Commit 6339207

Browse files
authored
Merge pull request #3037 from woocommerce/PCP-4102-save-and-display-pay-later-messaging-configuration
Save and display Pay Later Messaging configuration (4102)
2 parents 0e7df1e + f907558 commit 6339207

File tree

17 files changed

+555
-13
lines changed

17 files changed

+555
-13
lines changed

modules/ppcp-paylater-configurator/src/Endpoint/SaveConfig.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function handle_request(): bool {
9494
*
9595
* @param array $config The configurator config.
9696
*/
97-
private function save_config( array $config ): void {
97+
public function save_config( array $config ): void {
9898
$this->settings->set( 'pay_later_enable_styling_per_messaging_location', true );
9999
$this->settings->set( 'pay_later_messaging_enabled', true );
100100

modules/ppcp-settings/resources/js/Components/Screens/Overview/TabPayLaterMessaging.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import React, { useEffect } from 'react';
2+
import { PayLaterMessagingHooks } from '../../../data';
23

34
const TabPayLaterMessaging = () => {
4-
const config = {}; // Replace with the appropriate/saved configuration.
5+
const {
6+
config,
7+
setCart,
8+
setCheckout,
9+
setProduct,
10+
setShop,
11+
setHome,
12+
setCustom_placement,
13+
} = PayLaterMessagingHooks.usePayLaterMessaging();
514
const PcpPayLaterConfigurator =
615
window.ppcpSettings?.PcpPayLaterConfigurator;
716

@@ -27,17 +36,16 @@ const TabPayLaterMessaging = () => {
2736
subheader: 'ppcp-r-paylater-configurator__subheader',
2837
},
2938
onSave: ( data ) => {
30-
/*
31-
TODO:
32-
- The saving will be handled in a separate PR.
33-
- One option could be:
34-
- When saving the settings, programmatically click on the configurator's
35-
"Save Changes" button and send the request to PHP.
36-
*/
39+
setCart( data.config.cart );
40+
setCheckout( data.config.checkout );
41+
setProduct( data.config.product );
42+
setShop( data.config.shop );
43+
setHome( data.config.home );
44+
setCustom_placement( data.config.custom_placement );
3745
},
3846
} );
3947
}
40-
}, [ PcpPayLaterConfigurator ] );
48+
}, [ PcpPayLaterConfigurator, config ] );
4149

4250
return (
4351
<div

modules/ppcp-settings/resources/js/data/_example/hooks.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const useHooks = () => {
3434
};
3535
};
3636

37-
export const useState = () => {
37+
export const useStore = () => {
3838
const { persist, isReady } = useHooks();
3939
return { persist, isReady };
4040
};

modules/ppcp-settings/resources/js/data/index.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,17 @@ import * as Payment from './payment';
55
import * as Settings from './settings';
66
import * as Styling from './styling';
77
import * as Todos from './todos';
8+
import * as PayLaterMessaging from './pay-later-messaging';
89

9-
const stores = [ Onboarding, Common, Payment, Settings, Styling, Todos ];
10+
const stores = [
11+
Onboarding,
12+
Common,
13+
Payment,
14+
Settings,
15+
Styling,
16+
Todos,
17+
PayLaterMessaging,
18+
];
1019

1120
stores.forEach( ( store ) => {
1221
try {
@@ -30,13 +39,15 @@ export const PaymentHooks = Payment.hooks;
3039
export const SettingsHooks = Settings.hooks;
3140
export const StylingHooks = Styling.hooks;
3241
export const TodosHooks = Todos.hooks;
42+
export const PayLaterMessagingHooks = PayLaterMessaging.hooks;
3343

3444
export const OnboardingStoreName = Onboarding.STORE_NAME;
3545
export const CommonStoreName = Common.STORE_NAME;
3646
export const PaymentStoreName = Payment.STORE_NAME;
3747
export const SettingsStoreName = Settings.STORE_NAME;
3848
export const StylingStoreName = Styling.STORE_NAME;
3949
export const TodosStoreName = Todos.STORE_NAME;
50+
export const PayLaterMessagingStoreName = PayLaterMessaging.STORE_NAME;
4051

4152
export * from './configuration';
4253

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* Action Types: Define unique identifiers for actions across all store modules.
3+
*
4+
* @file
5+
*/
6+
7+
export default {
8+
// Transient data.
9+
SET_TRANSIENT: 'PAY_LATER_MESSAGING:SET_TRANSIENT',
10+
11+
// Persistent data.
12+
SET_PERSISTENT: 'PAY_LATER_MESSAGING:SET_PERSISTENT',
13+
RESET: 'PAY_LATER_MESSAGING:RESET',
14+
HYDRATE: 'PAY_LATER_MESSAGING:HYDRATE',
15+
16+
// Controls - always start with "DO_".
17+
DO_PERSIST_DATA: 'PAY_LATER_MESSAGING:DO_PERSIST_DATA',
18+
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Action Creators: Define functions to create action objects.
3+
*
4+
* These functions update state or trigger side effects (e.g., async operations).
5+
* Actions are categorized as Transient, Persistent, or Side effect.
6+
*
7+
* @file
8+
*/
9+
10+
import { select } from '@wordpress/data';
11+
12+
import ACTION_TYPES from './action-types';
13+
import { STORE_NAME } from './constants';
14+
15+
/**
16+
* @typedef {Object} Action An action object that is handled by a reducer or control.
17+
* @property {string} type - The action type.
18+
* @property {Object?} payload - Optional payload for the action.
19+
*/
20+
21+
/**
22+
* Special. Resets all values in the store to initial defaults.
23+
*
24+
* @return {Action} The action.
25+
*/
26+
export const reset = () => ( { type: ACTION_TYPES.RESET } );
27+
28+
/**
29+
* Persistent. Set the full store details during app initialization.
30+
*
31+
* @param {{data: {}, flags?: {}}} payload
32+
* @return {Action} The action.
33+
*/
34+
export const hydrate = ( payload ) => ( {
35+
type: ACTION_TYPES.HYDRATE,
36+
payload,
37+
} );
38+
39+
/**
40+
* Generic transient-data updater.
41+
*
42+
* @param {string} prop Name of the property to update.
43+
* @param {any} value The new value of the property.
44+
* @return {Action} The action.
45+
*/
46+
export const setTransient = ( prop, value ) => ( {
47+
type: ACTION_TYPES.SET_TRANSIENT,
48+
payload: { [ prop ]: value },
49+
} );
50+
51+
/**
52+
* Generic persistent-data updater.
53+
*
54+
* @param {string} prop Name of the property to update.
55+
* @param {any} value The new value of the property.
56+
* @return {Action} The action.
57+
*/
58+
export const setPersistent = ( prop, value ) => ( {
59+
type: ACTION_TYPES.SET_PERSISTENT,
60+
payload: { [ prop ]: value },
61+
} );
62+
63+
/**
64+
* Transient. Marks the store as "ready", i.e., fully initialized.
65+
*
66+
* @param {boolean} isReady
67+
* @return {Action} The action.
68+
*/
69+
export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady );
70+
71+
/**
72+
* Side effect. Triggers the persistence of store data to the server.
73+
*
74+
* @return {Action} The action.
75+
*/
76+
export const persist = function* () {
77+
const data = yield select( STORE_NAME ).persistentData();
78+
79+
yield { type: ACTION_TYPES.DO_PERSIST_DATA, data };
80+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Name of the Redux store module.
3+
*
4+
* Used by: Reducer, Selector, Index
5+
*
6+
* @type {string}
7+
*/
8+
export const STORE_NAME = 'wc/paypal/pay_later_messaging';
9+
10+
/**
11+
* REST path to hydrate data of this module by loading data from the WP DB.
12+
*
13+
* Used by: Resolvers
14+
* See: PayLaterMessagingEndpoint.php
15+
*
16+
* @type {string}
17+
*/
18+
export const REST_HYDRATE_PATH = '/wc/v3/wc_paypal/pay_later_messaging';
19+
20+
/**
21+
* REST path to persist data of this module to the WP DB.
22+
*
23+
* Used by: Controls
24+
* See: PayLaterMessagingEndpoint.php
25+
*
26+
* @type {string}
27+
*/
28+
export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/pay_later_messaging';
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Controls: Implement side effects, typically asynchronous operations.
3+
*
4+
* Controls use ACTION_TYPES keys as identifiers.
5+
* They are triggered by corresponding actions and handle external interactions.
6+
*
7+
* @file
8+
*/
9+
10+
import apiFetch from '@wordpress/api-fetch';
11+
12+
import { REST_PERSIST_PATH } from './constants';
13+
import ACTION_TYPES from './action-types';
14+
15+
export const controls = {
16+
async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) {
17+
return await apiFetch( {
18+
path: REST_PERSIST_PATH,
19+
method: 'POST',
20+
data,
21+
} );
22+
},
23+
};
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Hooks: Provide the main API for components to interact with the store.
3+
*
4+
* These encapsulate store interactions, offering a consistent interface.
5+
* Hooks simplify data access and manipulation for components.
6+
*
7+
* @file
8+
*/
9+
10+
import { useDispatch } from '@wordpress/data';
11+
12+
import { createHooksForStore } from '../utils';
13+
import { STORE_NAME } from './constants';
14+
15+
const useHooks = () => {
16+
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
17+
const { persist } = useDispatch( STORE_NAME );
18+
19+
// Read-only flags and derived state.
20+
// Nothing here yet.
21+
22+
// Transient accessors.
23+
const [ isReady ] = useTransient( 'isReady' );
24+
25+
// Persistent accessors.
26+
const [ cart, setCart ] = usePersistent( 'cart' );
27+
const [ checkout, setCheckout ] = usePersistent( 'checkout' );
28+
const [ product, setProduct ] = usePersistent( 'product' );
29+
const [ shop, setShop ] = usePersistent( 'shop' );
30+
const [ home, setHome ] = usePersistent( 'home' );
31+
const [ custom_placement, setCustom_placement ] =
32+
usePersistent( 'custom_placement' );
33+
34+
return {
35+
persist,
36+
isReady,
37+
cart,
38+
setCart,
39+
checkout,
40+
setCheckout,
41+
product,
42+
setProduct,
43+
shop,
44+
setShop,
45+
home,
46+
setHome,
47+
custom_placement,
48+
setCustom_placement,
49+
};
50+
};
51+
52+
export const useStore = () => {
53+
const { persist, isReady } = useHooks();
54+
return { persist, isReady };
55+
};
56+
57+
export const usePayLaterMessaging = () => {
58+
const {
59+
cart,
60+
setCart,
61+
checkout,
62+
setCheckout,
63+
product,
64+
setProduct,
65+
shop,
66+
setShop,
67+
home,
68+
setHome,
69+
custom_placement,
70+
setCustom_placement,
71+
} = useHooks();
72+
73+
return {
74+
config: {
75+
cart,
76+
checkout,
77+
product,
78+
shop,
79+
home,
80+
custom_placement,
81+
},
82+
setCart,
83+
setCheckout,
84+
setProduct,
85+
setShop,
86+
setHome,
87+
setCustom_placement,
88+
};
89+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { createReduxStore, register } from '@wordpress/data';
2+
import { controls as wpControls } from '@wordpress/data-controls';
3+
4+
import { STORE_NAME } from './constants';
5+
import reducer from './reducer';
6+
import * as selectors from './selectors';
7+
import * as actions from './actions';
8+
import * as hooks from './hooks';
9+
import { resolvers } from './resolvers';
10+
import { controls } from './controls';
11+
12+
/**
13+
* Initializes and registers the settings store with WordPress data layer.
14+
* Combines custom controls with WordPress data controls.
15+
*
16+
* @return {boolean} True if initialization succeeded, false otherwise.
17+
*/
18+
export const initStore = () => {
19+
const store = createReduxStore( STORE_NAME, {
20+
reducer,
21+
controls: { ...wpControls, ...controls },
22+
actions,
23+
selectors,
24+
resolvers,
25+
} );
26+
27+
register( store );
28+
29+
return Boolean( wp.data.select( STORE_NAME ) );
30+
};
31+
32+
export { hooks, selectors, STORE_NAME };

0 commit comments

Comments
 (0)