Skip to content

Commit

Permalink
Settings UI: Add functionality to mark todos as complete on click
Browse files Browse the repository at this point in the history
  • Loading branch information
danieldudzic committed Feb 5, 2025
1 parent 05c7330 commit 04f0f45
Show file tree
Hide file tree
Showing 12 changed files with 163 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { selectTab, TAB_IDS } from '../../../utils/tabSelector';
import { useEffect, useState } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { useDispatch, useSelect } from '@wordpress/data';
import { STORE_NAME as TODOS_STORE_NAME } from '../../../data/todos';

const TodoSettingsBlock = ( {
Expand All @@ -21,6 +21,8 @@ const TodoSettingsBlock = ( {
[]
);

const { completeOnClick } = useDispatch( TODOS_STORE_NAME );

useEffect( () => {
if ( dismissedTodos.length === 0 ) {
setDismissingIds( new Set() );
Expand All @@ -41,6 +43,26 @@ const TodoSettingsBlock = ( {
}, 300 );
};

const handleClick = async ( todo ) => {
if ( todo.action.type === 'tab' ) {
const tabId = TAB_IDS[ todo.action.tab.toUpperCase() ];
await selectTab( tabId, todo.action.section );
} else if ( todo.action.type === 'external' ) {
window.open( todo.action.url, '_blank' );
// If it has completeOnClick flag, trigger the action
if ( todo.action.completeOnClick === true ) {
await completeOnClick( todo.id );
}
}

if ( todo.action.modal ) {
setActiveModal( todo.action.modal );
}
if ( todo.action.highlight ) {
setActiveHighlight( todo.action.highlight );
}
};

// Filter out dismissed todos for display
const visibleTodos = todosData.filter(
( todo ) => ! dismissedTodos.includes( todo.id )
Expand All @@ -59,22 +81,7 @@ const TodoSettingsBlock = ( {
isCompleted={ completedTodos.includes( todo.id ) }
isDismissing={ dismissingIds.has( todo.id ) }
onDismiss={ ( e ) => handleDismiss( todo.id, e ) }
onClick={ async () => {
if ( todo.action.type === 'tab' ) {
const tabId =
TAB_IDS[ todo.action.tab.toUpperCase() ];
await selectTab( tabId, todo.action.section );
} else if ( todo.action.type === 'external' ) {
window.open( todo.action.url, '_blank' );
}

if ( todo.action.modal ) {
setActiveModal( todo.action.modal );
}
if ( todo.action.highlight ) {
setActiveHighlight( todo.action.highlight );
}
} }
onClick={ () => handleClick( todo ) }
/>
) ) }
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export default {
DO_FETCH_TODOS: 'TODOS:DO_FETCH_TODOS',
DO_PERSIST_DATA: 'TODOS:DO_PERSIST_DATA',
DO_RESET_DISMISSED_TODOS: 'TODOS:DO_RESET_DISMISSED_TODOS',
DO_COMPLETE_ONCLICK: 'TODOS:DO_COMPLETE_ONCLICK',
};
19 changes: 17 additions & 2 deletions modules/ppcp-settings/resources/js/data/todos/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ export const resetDismissedTodos = function* () {
const result = yield { type: ACTION_TYPES.DO_RESET_DISMISSED_TODOS };

if ( result && result.success ) {
// After successful reset, fetch fresh todos
yield fetchTodos();
yield setDismissedTodos( [] );
}

return result;
Expand All @@ -50,3 +49,19 @@ export const setCompletedTodos = ( completedTodos ) => ( {
type: ACTION_TYPES.SET_COMPLETED_TODOS,
payload: completedTodos,
} );

export const completeOnClick = function* ( todoId ) {
const result = yield {
type: ACTION_TYPES.DO_COMPLETE_ONCLICK,
todoId,
};

if ( result && result.success ) {
// Set transient completed state for visual feedback
const currentTransientCompleted =
yield select( STORE_NAME ).getCompletedTodos();
yield setCompletedTodos( [ ...currentTransientCompleted, todoId ] );
}

return result;
};
1 change: 1 addition & 0 deletions modules/ppcp-settings/resources/js/data/todos/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export const REST_PATH = '/wc/v3/wc_paypal/todos';
export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/todos';
export const REST_RESET_DISMISSED_TODOS_PATH =
'/wc/v3/wc_paypal/reset-dismissed-todos';
export const REST_COMPLETE_ONCLICK_PATH = '/wc/v3/wc_paypal/complete-onclick';
18 changes: 18 additions & 0 deletions modules/ppcp-settings/resources/js/data/todos/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
REST_PATH,
REST_PERSIST_PATH,
REST_RESET_DISMISSED_TODOS_PATH,
REST_COMPLETE_ONCLICK_PATH,
} from './constants';
import ACTION_TYPES from './action-types';

Expand Down Expand Up @@ -44,4 +45,21 @@ export const controls = {
};
}
},
async [ ACTION_TYPES.DO_COMPLETE_ONCLICK ]( { todoId } ) {
try {
const response = await apiFetch( {
path: REST_COMPLETE_ONCLICK_PATH,
method: 'POST',
data: { todoId },
} );

return response;
} catch ( e ) {
return {
success: false,
error: e,
message: e.message,
};
}
},
};
1 change: 1 addition & 0 deletions modules/ppcp-settings/resources/js/data/todos/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const defaultTransient = Object.freeze( {
const defaultPersistent = Object.freeze( {
todos: [],
dismissedTodos: [],
completedOnClickTodos: [],
} );

// Reducer logic.
Expand Down
7 changes: 6 additions & 1 deletion modules/ppcp-settings/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use WooCommerce\PayPalCommerce\Settings\Data\Definition\TodosDefinition;
use WooCommerce\PayPalCommerce\Settings\Endpoint\AuthenticationRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\CompleteOnClickEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\PayLaterMessagingEndpoint;
Expand Down Expand Up @@ -265,7 +266,8 @@
},
'settings.data.definition.todos' => static function ( ContainerInterface $container ) : TodosDefinition {
return new TodosDefinition(
$container->get( 'settings.service.todos_eligibilities' )
$container->get( 'settings.service.todos_eligibilities' ),
$container->get( 'settings.data.general' )
);
},
'settings.service.todos_eligibilities' => static function( ContainerInterface $container ): TodosEligibilityService {
Expand Down Expand Up @@ -314,4 +316,7 @@
'settings.rest.reset_dismissed_todos' => static function( ContainerInterface $container ): ResetDismissedTodosEndpoint {
return new ResetDismissedTodosEndpoint();
},
'settings.rest.complete_onclick' => static function( ContainerInterface $container ): CompleteOnClickEndpoint {
return new CompleteOnClickEndpoint();
},
);
23 changes: 19 additions & 4 deletions modules/ppcp-settings/src/Data/Definition/TodosDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace WooCommerce\PayPalCommerce\Settings\Data\Definition;

use WooCommerce\PayPalCommerce\Settings\Service\TodosEligibilityService;
use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings;

/**
* Class TodosDefinition
Expand All @@ -26,13 +27,25 @@ class TodosDefinition {
*/
protected TodosEligibilityService $eligibilities;

/**
* The general settings service.
*
* @var GeneralSettings
*/
protected GeneralSettings $settings;

/**
* Constructor.
*
* @param TodosEligibilityService $eligibilities The todos eligibility service.
* @param GeneralSettings $settings The general settings service.
*/
public function __construct( TodosEligibilityService $eligibilities ) {
public function __construct(
TodosEligibilityService $eligibilities,
GeneralSettings $settings
) {
$this->eligibilities = $eligibilities;
$this->settings = $settings;
}

/**
Expand Down Expand Up @@ -110,9 +123,11 @@ public function get(): array {
'description' => __( 'To enable Apple Pay, you must register your domain with PayPal', 'woocommerce-paypal-payments' ),
'isEligible' => $eligibility_checks['register_domain_apple_pay'],
'action' => array(
'type' => 'tab',
'tab' => 'overview',
'section' => 'apple_pay',
'type' => 'external',
'url' => $this->settings->is_sandbox_merchant()
? 'https://www.sandbox.paypal.com/uccservicing/apm/applepay'
: 'https://www.paypal.com/uccservicing/apm/applepay',
'completeOnClick' => true,
),
),
'add_digital_wallets' => array(
Expand Down
54 changes: 54 additions & 0 deletions modules/ppcp-settings/src/Endpoint/CompleteOnClickEndpoint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php
declare(strict_types=1);

namespace WooCommerce\PayPalCommerce\Settings\Endpoint;

use WP_REST_Server;
use WP_REST_Response;
use WP_REST_Request;

class CompleteOnClickEndpoint extends RestEndpoint {
protected $rest_base = 'complete-onclick';

public function register_routes(): void {
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'complete_onclick' ),
'permission_callback' => array( $this, 'check_permission' ),
)
);
}

public function complete_onclick( WP_REST_Request $request ): WP_REST_Response {
$todo_id = $request->get_param( 'todoId' );

if ( ! $todo_id ) {
return $this->return_error( __( 'Todo ID is required.', 'woocommerce-paypal-payments' ) );
}

$settings = get_option( 'ppcp-settings', array() );

if ( ! isset( $settings['completedOnClickTodos'] ) ) {
$settings['completedOnClickTodos'] = array();
}

if ( ! in_array( $todo_id, $settings['completedOnClickTodos'] ) ) {
$settings['completedOnClickTodos'][] = $todo_id;
$update_result = update_option( 'ppcp-settings', $settings );

if ( ! $update_result ) {
return $this->return_error( __( 'Failed to mark todo as completed on click.', 'woocommerce-paypal-payments' ) );
}
}

return $this->return_success(
array(
'message' => __( 'Todo marked as completed on click successfully.', 'woocommerce-paypal-payments' ),
'todoId' => $todo_id,
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ public function reset_dismissed_todos( WP_REST_Request $request ): WP_REST_Respo
$settings = get_option( 'ppcp-settings', array() );

$settings['dismissedTodos'] = array();
$update_result = update_option( 'ppcp-settings', $settings );

// Clear the completedOnClickTodos for testing purposes.
// $settings['completedOnClickTodos'] = array();

$update_result = update_option( 'ppcp-settings', $settings );

if ( ! $update_result ) {
return $this->return_error( __( 'Failed to reset dismissed todos.', 'woocommerce-paypal-payments' ) );
Expand Down
20 changes: 16 additions & 4 deletions modules/ppcp-settings/src/Endpoint/TodosRestEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,22 @@ public function register_routes(): void {
* @return WP_REST_Response The response containing todos data.
*/
public function get_todos(): WP_REST_Response {
$settings = get_option( 'ppcp-settings', array() );
$dismissed_ids = $settings['dismissedTodos'] ?? array();
$settings = get_option( 'ppcp-settings', array() );
$dismissed_ids = $settings['dismissedTodos'] ?? array();
$completed_onclick_ids = $settings['completedOnClickTodos'] ?? array();

$todos = array();
foreach ( $this->todos_definition->get() as $id => $todo ) {
// Skip if todo has completeOnClick flag and is in completed list
if (
in_array( $id, $completed_onclick_ids, true ) &&
isset( $todo['action']['completeOnClick'] ) &&
$todo['action']['completeOnClick'] === true
) {
continue;
}

// Check eligibility and add to todos if eligible
if ( $todo['isEligible']() ) {
$todos[] = array_merge(
array( 'id' => $id ),
Expand All @@ -118,8 +129,9 @@ public function get_todos(): WP_REST_Response {

return $this->return_success(
array(
'todos' => $todos,
'dismissedTodos' => $dismissed_ids,
'todos' => $todos,
'dismissedTodos' => $dismissed_ids,
'completedOnClickTodos' => $completed_onclick_ids,
)
);
}
Expand Down
1 change: 1 addition & 0 deletions modules/ppcp-settings/src/SettingsModule.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ static function () use ( $container ) : void {
'todos' => $container->get( 'settings.rest.todos' ),
'reset_dismissed_todos' => $container->get( 'settings.rest.reset_dismissed_todos' ),
'pay_later_messaging' => $container->get( 'settings.rest.pay_later_messaging' ),
'complete_onclick' => $container->get( 'settings.rest.complete_onclick' ),
);

foreach ( $endpoints as $endpoint ) {
Expand Down

0 comments on commit 04f0f45

Please sign in to comment.