From 60e8d47d2bec690367c9628a81bd3c03a0e9da76 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:01:59 +0200 Subject: [PATCH 01/50] WIP --- classes/class-base.php | 4 + classes/class-suggested-tasks.php | 15 + .../front-end/class-front-end-onboarding.php | 630 ++++++++++++++++++ 3 files changed, 649 insertions(+) create mode 100644 classes/front-end/class-front-end-onboarding.php diff --git a/classes/class-base.php b/classes/class-base.php index f952929cce..a4cb427114 100644 --- a/classes/class-base.php +++ b/classes/class-base.php @@ -55,6 +55,7 @@ * @method \Progress_Planner\Admin\Widgets\Challenge get_admin__widgets__challenge() * @method \Progress_Planner\Admin\Widgets\Activity_Scores get_admin__widgets__activity_scores() * @method \Progress_Planner\Utils\Date get_utils__date() + * @method \Progress_Planner\Front_End\Front_End_Onboarding get_front_end_onboarding() */ class Base { @@ -170,6 +171,9 @@ public function init() { // Init the enqueue class. $this->get_admin__enqueue()->init(); + + // TODO: Decide when this needs to be initialized. + $this->get_front_end__front_end_onboarding(); } /** diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index 5b1f443621..8f997e7460 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -197,6 +197,17 @@ public function maybe_complete_task() { return; } + $this->complete_task( $task_id ); + } + + /** + * Complete a task. + * + * @param string $task_id The task ID. + * + * @return bool + */ + public function complete_task( $task_id ) { if ( ! $this->was_task_completed( $task_id ) ) { $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); @@ -205,8 +216,12 @@ public function maybe_complete_task() { // Insert an activity. $this->insert_activity( $task_id ); + + return true; } } + + return false; } /** diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php new file mode 100644 index 0000000000..8814eb580e --- /dev/null +++ b/classes/front-end/class-front-end-onboarding.php @@ -0,0 +1,630 @@ +add_node( + [ + 'id' => 'progress-planner-tour', + 'title' => 'Progress Planner Tour', + 'href' => '#', + 'meta' => [ + 'onclick' => 'prplStartTour(); return false;', + ], + ] + ); + } + + /** + * Save the tour progress. + * + * @return void + */ + public function ajax_save_tour_progress() { + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['state'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'State is required.', 'progress-planner' ) ] ); + } + $progress = \sanitize_text_field( \wp_unslash( $_POST['state'] ) ); + + \error_log( print_r( $progress, true ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r, WordPress.PHP.DevelopmentFunctions.error_log_error_log + + // Save as user meta? + \update_user_meta( \get_current_user_id(), '_prpl_tour_progress', $progress ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Tour progress saved.', 'progress-planner' ) ] ); + } + + /** + * Complete a task. + * + * @return void + */ + public function ajax_complete_task() { + + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['task_id'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Task ID is required.', 'progress-planner' ) ] ); + } + + $task_id = \sanitize_text_field( \wp_unslash( $_POST['task_id'] ) ); + + // Note: Completing task will set it it to pending, so user will get celebration. + // Do we want that? + $result = \progress_planner()->get_suggested_tasks()->complete_task( $task_id ); + + if ( ! $result ) { + \error_log( 'Task not completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + \wp_send_json_error( [ 'message' => \esc_html__( 'Task not completed.', 'progress-planner' ) ] ); + } + + \error_log( 'Task completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log + \wp_send_json_success( [ 'message' => \esc_html__( 'Task completed.', 'progress-planner' ) ] ); + } + + /** + * Add the popover. + * + * @return void + */ + public function add_popover() { + ?> +
+ +
+

+ +

+
+
+ +
+ +
+ + + get_suggested_tasks_db()->get_tasks_by( + [ + 'post_status' => 'publish', + 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query + [ + 'taxonomy' => 'prpl_recommendations_provider', + 'field' => 'slug', + 'terms' => 'user', + 'operator' => 'NOT IN', + ], + ], + ] + ); + $tasks = []; + foreach ( $ravis_recommendations as $recommendation ) { + + $tasks[] = [ + 'task_id' => $recommendation->task_id, + 'title' => $recommendation->post_title, + 'url' => $recommendation->url, + 'provider_id' => $recommendation->get_provider_id(), + 'points' => $recommendation->points, + ]; + } + ?> + + + + + + + + + + + + + + + + + Date: Mon, 22 Sep 2025 12:17:02 +0200 Subject: [PATCH 02/50] split stlyes --- assets/css/front-end-onboarding.css | 83 ++++++++++++ .../front-end/class-front-end-onboarding.php | 121 +++--------------- 2 files changed, 98 insertions(+), 106 deletions(-) create mode 100644 assets/css/front-end-onboarding.css diff --git a/assets/css/front-end-onboarding.css b/assets/css/front-end-onboarding.css new file mode 100644 index 0000000000..44ed6f928b --- /dev/null +++ b/assets/css/front-end-onboarding.css @@ -0,0 +1,83 @@ +#prpl-popover-front-end-onboarding { + + padding: 24px 24px 14px 24px; + box-sizing: border-box; + + background: #fff; + border: 1px solid #9ca3af; + border-radius: 8px; + font-weight: 400; + max-height: 82vh; + width: 1200px; + max-width: 80vw; + + &::backdrop { + background: rgba(0, 0, 0, 0.5); + } + + .prpl-btn { + display: inline-block; + margin: 1rem 0; + padding: 0.75rem 1.25rem; + color: #fff; + text-decoration: none; + cursor: pointer; + font-size: 16px; + background: #dd3244; + line-height: 1.25; + box-shadow: none; + border: none; + border-radius: 6px; + transition: all 0.25s ease-in-out; + font-weight: 600; + text-align: center; + box-sizing: border-box; + position: relative; + z-index: 1; + + &:disabled { + opacity: 0.5; + pointer-events: none; + } + + &:not([disabled]):hover, + &:not([disabled]):focus { + background: #cf2441; + } + } + + .prpl-complete-task-btn { + border: none; + background: none; + cursor: pointer; + padding: 0; + margin: 0; + font-size: 16px; + color: #1e40af; + } + + .prpl-complete-task-btn-completed { + color: #059669; + } + + .prpl-complete-task-btn-error { + color: #9f0712; + } + + .prpl-complete-task-item { + margin-bottom: 1rem; + display: flex; + align-items: center; + justify-content: space-between; + } + + #prpl-more-tasks-list { + list-style: none; + padding: 0; + margin: 0; + + li:not(:last-child) { + margin-bottom: 10px; + } + } +} diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 8814eb580e..564c0f3654 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -12,25 +12,20 @@ */ class Front_End_Onboarding { - /** - * The popover ID for front end onboarding. - * - * @var string - */ - const POPOVER_ID = 'front-end-onboarding'; - /** * Constructor. * * @return void */ public function __construct() { + // Add popover markup. \add_action( 'wp_footer', [ $this, 'add_popover' ] ); - \add_action( 'wp_footer', [ $this, 'add_popover_style' ] ); - \add_action( 'wp_footer', [ $this, 'add_popover_inline_script' ] ); + // Add popover scripts. + \add_action( 'wp_enqueue_scripts', [ $this, 'add_popover_scripts' ] ); + // Add admin toolbar item. \add_action( 'admin_bar_menu', [ $this, 'add_admin_toolbar_item' ] ); @@ -39,6 +34,15 @@ public function __construct() { \add_action( 'wp_ajax_progress_planner_tour_save_progress', [ $this, 'ajax_save_tour_progress' ] ); } + /** + * Add popover scripts. + * + * @return void + */ + public function add_popover_scripts() { + \wp_enqueue_style( 'prpl-popover-front-end-onboarding', \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/css/front-end-onboarding.css', [], \progress_planner()->get_plugin_version() ); + } + /** * Add admin toolbar item. * @@ -127,7 +131,7 @@ public function ajax_complete_task() { */ public function add_popover() { ?> -
+

@@ -146,101 +150,6 @@ public function add_popover() { - - From fb45a78ffe5164c9accdfd009d7adbbf25761450 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:54:19 +0200 Subject: [PATCH 05/50] no need for tasks on backend, yet --- classes/front-end/class-front-end-onboarding.php | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 2e8621d49b..ebc09e930a 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -59,16 +59,6 @@ public function add_popover_scripts() { ], ] ); - $tasks = []; - foreach ( $ravis_recommendations as $recommendation ) { - $tasks[] = [ - 'task_id' => $recommendation->task_id, - 'title' => $recommendation->post_title, - 'url' => $recommendation->url, - 'provider_id' => $recommendation->get_provider_id(), - 'points' => $recommendation->points, - ]; - } \wp_localize_script( 'prpl-popover-front-end-onboarding', @@ -76,7 +66,6 @@ public function add_popover_scripts() { [ 'adminAjaxUrl' => \esc_url_raw( admin_url( 'admin-ajax.php' ) ), 'nonceProgressPlanner' => \esc_js( \wp_create_nonce( 'progress_planner' ) ), - 'tasks' => $tasks, ] ); } From ec8b8dce6d45e2a2ee923886bf0a7985869a5204 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 12:59:52 +0200 Subject: [PATCH 06/50] no need to mount finish step --- assets/js/front-end-onboarding.js | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 6b281d6253..a9eab42094 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -67,7 +67,6 @@ class ProgressPlannerTour { title: 'Setup complete', render: () => document.getElementById( 'tour-step-finish' ).innerHTML, - onMount: ( state ) => this.mountFinishStep( state ), }, ]; } @@ -134,23 +133,6 @@ class ProgressPlannerTour { }; } - /** - * Mount finish step - * @param {Object} state - */ - mountFinishStep( state ) { - const btn = document.querySelector( '#prpl-finish-btn' ); - if ( ! btn ) return () => {}; - - const handler = () => { - state.data.finished = true; - this.closeTour(); - }; - - btn.addEventListener( 'click', handler ); - return () => btn.removeEventListener( 'click', handler ); - } - /** * Complete a task via AJAX * @param {string} taskId @@ -355,9 +337,11 @@ class ProgressPlannerTour { const nextBtn = popover.querySelector( '.prpl-tour-next' ); const prevBtn = popover.querySelector( '.prpl-tour-prev' ); + const finishBtn = popover.querySelector( '#prpl-finish-btn' ); console.log( 'Next button found:', nextBtn ); console.log( 'Prev button found:', prevBtn ); + console.log( 'Finish button found:', finishBtn ); if ( nextBtn ) { nextBtn.addEventListener( 'click', () => { @@ -372,6 +356,13 @@ class ProgressPlannerTour { this.prevStep(); } ); } + + if ( finishBtn ) { + finishBtn.addEventListener( 'click', () => { + console.log( 'Finish button clicked!' ); + this.closeTour(); + } ); + } } else { console.error( 'Popover not found!' ); } From e4b2d071f3f36b754f1d81248fb298acaf826149 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 16:33:08 +0200 Subject: [PATCH 07/50] a bit of localization (titles needs to be localized as well) --- classes/front-end/class-front-end-onboarding.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index ebc09e930a..9d33b00e7b 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -225,7 +225,7 @@ public function add_popover_inline_script() { @@ -234,7 +234,7 @@ public function add_popover_inline_script() {

- +

@@ -243,13 +243,13 @@ public function add_popover_inline_script() { Date: Mon, 22 Sep 2025 16:41:47 +0200 Subject: [PATCH 08/50] get proxy comment --- assets/js/front-end-onboarding.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index a9eab42094..73137f2c93 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -384,6 +384,8 @@ class ProgressPlannerTour { */ createDeepProxy( target, callback ) { return new Proxy( target, { + // Note: Maybe hook into get here as well, to handle reactivity better. + set: ( obj, prop, value ) => { if ( value && From fbda2d1b6f057a131d38c028f64328d23021b6bb Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 22 Sep 2025 17:05:06 +0200 Subject: [PATCH 09/50] dont reset state --- assets/js/front-end-onboarding.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 73137f2c93..1cefed6f60 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -103,8 +103,6 @@ class ProgressPlannerTour { * @param {Object} state */ mountMoreTasksStep( state ) { - state.data.moreTasksCompleted = {}; - const handler = ( e ) => { const thisBtn = e.target.closest( 'button' ); this.completeTask( thisBtn.dataset.taskId ) From e2ed1750d2c00ecf70ca15ec006700cf2836985c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 23 Sep 2025 09:01:41 +0200 Subject: [PATCH 10/50] better proxy --- assets/js/front-end-onboarding.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index 1cefed6f60..d961b07398 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -381,9 +381,18 @@ class ProgressPlannerTour { * @param {Function} callback */ createDeepProxy( target, callback ) { - return new Proxy( target, { - // Note: Maybe hook into get here as well, to handle reactivity better. + // Recursively wrap existing nested objects first + for ( const key of Object.keys( target ) ) { + if ( + target[ key ] && + typeof target[ key ] === 'object' && + ! Array.isArray( target[ key ] ) + ) { + target[ key ] = this.createDeepProxy( target[ key ], callback ); + } + } + return new Proxy( target, { set: ( obj, prop, value ) => { if ( value && From 19e585682ecccb722700b69be6632f5fcb621239 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 23 Sep 2025 09:25:41 +0200 Subject: [PATCH 11/50] titles to templates --- assets/js/front-end-onboarding.js | 9 +- .../front-end/class-front-end-onboarding.php | 114 ++++++++++-------- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index d961b07398..60d71ef36a 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -28,13 +28,11 @@ class ProgressPlannerTour { return [ { id: 'welcome', - title: 'Welcome', render: () => document.getElementById( 'tour-step-welcome' ).innerHTML, }, { id: 'first-task', - title: 'Complete your first task', render: () => document.getElementById( 'tour-step-first-task' ).innerHTML, onMount: ( state ) => this.mountFirstTaskStep( state ), @@ -42,13 +40,11 @@ class ProgressPlannerTour { }, { id: 'badges', - title: 'Our badges are waiting for you', render: () => document.getElementById( 'tour-step-badges' ).innerHTML, }, { id: 'more-tasks', - title: 'Complete more tasks', render: () => document.getElementById( 'tour-step-more-tasks' ).innerHTML, onMount: ( state ) => this.mountMoreTasksStep( state ), @@ -64,7 +60,6 @@ class ProgressPlannerTour { }, { id: 'finish', - title: 'Setup complete', render: () => document.getElementById( 'tour-step-finish' ).innerHTML, }, @@ -159,8 +154,8 @@ class ProgressPlannerTour { const step = this.tourSteps[ this.state.currentStep ]; const popover = this.getPopover(); - popover.querySelector( '.tour-title' ).innerHTML = step.title; - popover.querySelector( '.tour-content' ).innerHTML = step.render(); + popover.querySelector( '.tour-content-wrapper' ).innerHTML = + step.render(); // Cleanup previous step if ( this.state.cleanup ) { diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 9d33b00e7b..dc242bcaae 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -46,20 +46,6 @@ public function add_popover_scripts() { // Enqueue front-end-onboarding.js. \wp_enqueue_script( 'prpl-popover-front-end-onboarding', \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/js/front-end-onboarding.js', [], \progress_planner()->get_plugin_version(), true ); - $ravis_recommendations = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( - [ - 'post_status' => 'publish', - 'tax_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query - [ - 'taxonomy' => 'prpl_recommendations_provider', - 'field' => 'slug', - 'terms' => 'user', - 'operator' => 'NOT IN', - ], - ], - ] - ); - \wp_localize_script( 'prpl-popover-front-end-onboarding', 'ProgressPlannerData', @@ -92,7 +78,7 @@ public function add_admin_toolbar_item_callback( $admin_bar ) { 'title' => 'Progress Planner Tour', 'href' => '#', 'meta' => [ - 'onclick' => 'prplStartTour(); return false;', + 'onclick' => 'window.prplTour.startTour(); return false;', ], ] ); @@ -160,14 +146,10 @@ public function add_popover() { ?>
-
-

- -

-
-
+
+

-

- -

+
+
+

+ +

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing, elit nullam hendrerit porttitor torquent, nec molestie hac parturient vehicula. Fames condimentum netus nisl tempus potenti curabitur iaculis nam velit, etiam sapien mollis dictum vitae eu bibendum per mus, hendrerit dis blandit parturient dictumst cum ridiculus libero. +

+
+
+ Badges +
+
diff --git a/views/front-end-onboarding/finish.php b/views/front-end-onboarding/finish.php index 98a6d711b6..e1a949945b 100644 --- a/views/front-end-onboarding/finish.php +++ b/views/front-end-onboarding/finish.php @@ -20,5 +20,12 @@

+

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam, nibh accumsan ultricies ad placerat maecenas. +

+ Id sollicitudin ac auctor odio luctus ornare donec duis maecenas sodales montes nostra mi aliquam ultricies augue, posuere torquent imperdiet lobortis cras gravida nascetur venenatis malesuada potenti et mattis massa parturient. +

diff --git a/views/front-end-onboarding/first-task.php b/views/front-end-onboarding/first-task.php index 03722aeebb..531914952c 100644 --- a/views/front-end-onboarding/first-task.php +++ b/views/front-end-onboarding/first-task.php @@ -19,18 +19,37 @@
-

You have pending tasks to complete.

- -
-

- -

-
- - -
+
+
+

You have pending tasks to complete.

+ +

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam, nibh accumsan ultricies ad placerat maecenas. Id sollicitudin ac auctor odio luctus ornare donec duis maecenas sodales montes nostra mi aliquam ultricies augue, posuere torquent imperdiet lobortis cras gravida nascetur venenatis malesuada potenti et mattis massa parturient. +

+
+
+ +
+
+

+ +

+

+ Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

+ +

+ Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

+
+
+ + +
+
+
+ +
-
-
diff --git a/views/front-end-onboarding/more-tasks.php b/views/front-end-onboarding/more-tasks.php index 183fdd4261..90f9d73205 100644 --- a/views/front-end-onboarding/more-tasks.php +++ b/views/front-end-onboarding/more-tasks.php @@ -27,7 +27,7 @@ break; } ?>
  • - +
    diff --git a/views/front-end-onboarding/welcome.php b/views/front-end-onboarding/welcome.php index ff58405cca..90c9ee84fd 100644 --- a/views/front-end-onboarding/welcome.php +++ b/views/front-end-onboarding/welcome.php @@ -19,6 +19,18 @@
  • -

    +
    +
    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit facilisis interdum rhoncus magnis, curabitur pretium molestie posuere massa tristique commodo pellentesque ullamcorper vulputate risus, netus sem est metus habitasse sodales diam aliquam scelerisque mi. +

    +

    + Egestas turpis class nostra sociis felis nunc rhoncus, semper tristique fermentum mus scelerisque habitasse accumsan imperdiet, nisl in vehicula magna pretium ullamcorper. Dis dictumst semper urna hendrerit a pretium lectus luctus justo proin, elementum cras sodales dictum habitant curae iaculis nibh nisi tortor tempus, malesuada torquent etiam litora facilisis arcu montes bibendum nisl. Ultrices arcu varius id pellentesque metus quis litora odio vestibulum taciti molestie, maecenas fames quam conubia ultricies donec aliquet pharetra tristique. +

    +
    +
    + +
    +
    From aa9fa8dfcf26b3979bdea0cb868c44963756a9eb Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 29 Sep 2025 15:46:43 +0200 Subject: [PATCH 20/50] dashboard button --- assets/js/front-end-onboarding.js | 2 ++ classes/front-end/class-front-end-onboarding.php | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/assets/js/front-end-onboarding.js b/assets/js/front-end-onboarding.js index d96b4d0434..03a3503590 100644 --- a/assets/js/front-end-onboarding.js +++ b/assets/js/front-end-onboarding.js @@ -28,6 +28,7 @@ class ProgressPlannerTour { this.prevBtn = this.popover.querySelector( '.prpl-tour-prev' ); this.nextBtn = this.popover.querySelector( '.prpl-tour-next' ); this.finishBtn = this.popover.querySelector( '#prpl-finish-btn' ); + this.dashboardBtn = this.popover.querySelector( '#prpl-dashboard-btn' ); // Setup event listeners after DOM is ready this.setupEventListeners(); @@ -222,6 +223,7 @@ class ProgressPlannerTour { isFirstStep || isLastStep ? 'none' : 'inline-block'; this.nextBtn.style.display = isLastStep ? 'none' : 'inline-block'; this.finishBtn.style.display = isLastStep ? 'inline-block' : 'none'; + this.dashboardBtn.style.display = isLastStep ? 'inline-block' : 'none'; } /** diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index fe1d2b437e..457e1564ad 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -192,9 +192,10 @@ public function add_popover() { Date: Mon, 29 Sep 2025 15:49:36 +0200 Subject: [PATCH 21/50] add inline redirect --- classes/front-end/class-front-end-onboarding.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 457e1564ad..d0598170d2 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -195,7 +195,7 @@ public function add_popover() { - + Date: Thu, 2 Oct 2025 14:45:05 +0200 Subject: [PATCH 22/50] rename method --- classes/class-suggested-tasks.php | 4 ++-- classes/front-end/class-front-end-onboarding.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index d0879a9ba5..fb72ebe9a1 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -200,7 +200,7 @@ public function maybe_complete_task() { return; } - $this->complete_task( $task_id ); + $this->mark_task_as_completed( $task_id ); } /** @@ -210,7 +210,7 @@ public function maybe_complete_task() { * * @return bool */ - public function complete_task( $task_id ) { + public function mark_task_as_completed( $task_id ) { if ( ! $this->was_task_completed( $task_id ) ) { $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index d0598170d2..ac7fb216b8 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -167,7 +167,7 @@ public function ajax_complete_task() { // Note: Completing task will set it it to pending, so user will get celebration. // Do we want that? - $result = \progress_planner()->get_suggested_tasks()->complete_task( $task_id ); + $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); if ( ! $result ) { \error_log( 'Task not completed: ' . $task_id ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log From bdfc8680e9b3bd1e856c050691d31a3c04f4a6c2 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 2 Oct 2025 15:02:22 +0200 Subject: [PATCH 23/50] update comment --- classes/front-end/class-front-end-onboarding.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index ac7fb216b8..7fd8476d10 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -165,8 +165,7 @@ public function ajax_complete_task() { $form_values = \json_decode( $form_values, true ); } - // Note: Completing task will set it it to pending, so user will get celebration. - // Do we want that? + // Note: Marking task as completed will set it it to pending, so user will get celebration. Do we want that? $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); if ( ! $result ) { From 5a544f353093fb8262f873ec6dae7ac597e7af09 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 2 Oct 2025 15:03:19 +0200 Subject: [PATCH 24/50] WIP: prototype of how task can be programmatically completed --- .../front-end/class-front-end-onboarding.php | 15 +++++++++++++++ .../suggested-tasks/class-tasks-interface.php | 10 ++++++++++ .../providers/class-blog-description.php | 17 +++++++++++++++++ .../suggested-tasks/providers/class-tasks.php | 11 +++++++++++ 4 files changed, 53 insertions(+) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 7fd8476d10..2e64b3a127 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -165,6 +165,21 @@ public function ajax_complete_task() { $form_values = \json_decode( $form_values, true ); } + // Get the task. + $task = \progress_planner()->get_suggested_tasks_db()->get_post( $task_id ); + if ( ! $task ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Task not found.', 'progress-planner' ) ] ); + } + + // To get the provider and complete the task, we need to use the provider. + $provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( $task->get_provider_id() ); + if ( ! $provider ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Provider not found.', 'progress-planner' ) ] ); + } + + // Complete the task. + $provider->complete_task( $form_values, $task_id ); + // Note: Marking task as completed will set it it to pending, so user will get celebration. Do we want that? $result = \progress_planner()->get_suggested_tasks()->mark_task_as_completed( $task_id ); diff --git a/classes/suggested-tasks/class-tasks-interface.php b/classes/suggested-tasks/class-tasks-interface.php index 66c550b221..f93a1b9f6f 100644 --- a/classes/suggested-tasks/class-tasks-interface.php +++ b/classes/suggested-tasks/class-tasks-interface.php @@ -129,4 +129,14 @@ public function add_task_actions( $data = [], $actions = [] ); * @return bool */ public function task_has_activity( $task_id = '' ); + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ); } diff --git a/classes/suggested-tasks/providers/class-blog-description.php b/classes/suggested-tasks/providers/class-blog-description.php index fce46d6dc0..2dbc8fa930 100644 --- a/classes/suggested-tasks/providers/class-blog-description.php +++ b/classes/suggested-tasks/providers/class-blog-description.php @@ -139,4 +139,21 @@ public function add_task_actions( $data = [], $actions = [] ) { return $actions; } + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ) { + + if ( ! isset( $args['blogdescription'] ) ) { + return; + } + + \update_option( 'blogdescription', \sanitize_text_field( $args['blogdescription'] ) ); + } } diff --git a/classes/suggested-tasks/providers/class-tasks.php b/classes/suggested-tasks/providers/class-tasks.php index 00b18bbb6b..5bc8d239a7 100644 --- a/classes/suggested-tasks/providers/class-tasks.php +++ b/classes/suggested-tasks/providers/class-tasks.php @@ -721,4 +721,15 @@ public function task_has_activity( $task_id = '' ) { return ! empty( $activity ); } + + /** + * Complete the task. + * + * @param array $args The task data. + * @param string $task_id The task ID. + * + * @return void + */ + public function complete_task( $args = [], $task_id = '' ) { + } } From 4302f41d55b107849bf941ca2dfd940518dd993f Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 11:39:04 +0200 Subject: [PATCH 25/50] blog description as first task --- .../front-end/class-front-end-onboarding.php | 36 +++++++++++++++++-- views/front-end-onboarding/first-task.php | 10 ++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 2e64b3a127..c2ad0bc7a0 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -221,6 +221,34 @@ public function add_popover() { * @return void */ public function add_popover_step_templates() { + + // First task. + $first_task = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'task_id' => 'core-blogdescription' ] ); + + // If there is no 'blog description' task, create it. + if ( ! $first_task ) { + $first_task_provider = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_provider( 'core-blogdescription' ); + + if ( $first_task_provider ) { + $first_task_data = $first_task_provider->get_task_details(); + + \progress_planner()->get_suggested_tasks_db()->add( $first_task_data ); + + // Now get the task. + $first_task = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'task_id' => 'core-blogdescription' ] ); + } + } + + $first_task = [ + 'task_id' => $first_task[0]->task_id, + 'title' => $first_task[0]->post_title, + 'url' => $first_task[0]->url, + 'provider_id' => $first_task[0]->get_provider_id(), + 'points' => $first_task[0]->points, + 'site_description' => \get_bloginfo( 'description' ), + ]; + + // Other tasks. $ravis_recommendations = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'post_status' => 'publish', @@ -235,7 +263,8 @@ public function add_popover_step_templates() { ], ] ); - $tasks = []; + + $tasks = []; foreach ( $ravis_recommendations as $recommendation ) { $tasks[] = [ @@ -246,10 +275,11 @@ public function add_popover_step_templates() { 'points' => $recommendation->points, ]; } + \progress_planner()->the_view( 'front-end-onboarding/welcome.php' ); - \progress_planner()->the_view( 'front-end-onboarding/first-task.php', [ 'task' => $tasks[0] ] ); // WIP: We need only 1 task for this step. + \progress_planner()->the_view( 'front-end-onboarding/first-task.php', [ 'task' => $first_task ] ); // WIP: We need only 1 task for this step. \progress_planner()->the_view( 'front-end-onboarding/badges.php' ); - \progress_planner()->the_view( 'front-end-onboarding/more-tasks.php', [ 'tasks' => array_slice( $tasks, 1, 5 ) ] ); // WIP: We need up to 5 tasks for this step. + \progress_planner()->the_view( 'front-end-onboarding/more-tasks.php', [ 'tasks' => $tasks ] ); // WIP: We need up to 5 tasks for this step. \progress_planner()->the_view( 'front-end-onboarding/finish.php' ); ?> + + + + diff --git a/views/front-end-onboarding/tasks/blog-description.php b/views/front-end-onboarding/tasks/blog-description.php new file mode 100644 index 0000000000..16749e5780 --- /dev/null +++ b/views/front-end-onboarding/tasks/blog-description.php @@ -0,0 +1,37 @@ + + +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    + + + + +
    diff --git a/views/front-end-onboarding/tasks/select-locale.php b/views/front-end-onboarding/tasks/select-locale.php new file mode 100644 index 0000000000..f7e8c5061c --- /dev/null +++ b/views/front-end-onboarding/tasks/select-locale.php @@ -0,0 +1,61 @@ + + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + 'language', + 'id' => 'language', + 'selected' => $prpl_locale, + 'languages' => $prpl_languages, + 'translations' => $prpl_translations, + 'show_available_translations' => \current_user_can( 'install_languages' ) && \wp_can_install_language_pack(), + ] + ); + ?> + +
    +
    +
    diff --git a/views/front-end-onboarding/tasks/select-timezone.php b/views/front-end-onboarding/tasks/select-timezone.php new file mode 100644 index 0000000000..1a151a6409 --- /dev/null +++ b/views/front-end-onboarding/tasks/select-timezone.php @@ -0,0 +1,41 @@ + + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + + +
    +
    +
    From e8d945f1bddab84458fd767e228813de297bc7d9 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 16:46:28 +0200 Subject: [PATCH 27/50] secondary button styling --- assets/css/front-end-onboarding.css | 11 +++++++++++ classes/front-end/class-front-end-onboarding.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/assets/css/front-end-onboarding.css b/assets/css/front-end-onboarding.css index d5ac31d6bb..8591c325c0 100644 --- a/assets/css/front-end-onboarding.css +++ b/assets/css/front-end-onboarding.css @@ -111,6 +111,17 @@ &:not([disabled]):focus { background: #cf2441; } + + &.prpl-btn-secondary { + background: #f9b23c; + color: #4b5563; + + &:not([disabled]):hover, + &:not([disabled]):focus { + background: #f9b23c; + color: #4b5563; + } + } } .prpl-complete-task-btn:not(.prpl-btn) { diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index 6c068b914c..a94cb03dfb 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -208,7 +208,7 @@ public function add_popover() { From 2440fd23f025373a6215a028d1d5c638f3c10d2a Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 16:47:47 +0200 Subject: [PATCH 28/50] add permission check --- classes/front-end/class-front-end-onboarding.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/front-end/class-front-end-onboarding.php b/classes/front-end/class-front-end-onboarding.php index a94cb03dfb..dca0019eb5 100644 --- a/classes/front-end/class-front-end-onboarding.php +++ b/classes/front-end/class-front-end-onboarding.php @@ -146,6 +146,10 @@ public function ajax_save_tour_progress() { */ public function ajax_complete_task() { + if ( ! \current_user_can( 'manage_options' ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to complete this task.', 'progress-planner' ) ] ); + } + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); } From 090761c8007f82e75e7160b592a78fc592a0341c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 6 Oct 2025 17:35:51 +0200 Subject: [PATCH 29/50] wip styling for + diff --git a/views/front-end-onboarding/tasks/core-siteicon.php b/views/front-end-onboarding/tasks/core-siteicon.php new file mode 100644 index 0000000000..cc2e5c8acb --- /dev/null +++ b/views/front-end-onboarding/tasks/core-siteicon.php @@ -0,0 +1,50 @@ + +
    +
    +
    +

    + +

    +

    + Lorem ipsum dolor sit amet consectetur adipiscing elit, eget interdum nostra tortor vestibulum ultrices, quisque congue nibh ullamcorper sapien natoque. +

    + +

    + Venenatis parturient suspendisse massa cursus litora dapibus auctor, et vestibulum blandit condimentum quis ultrices sagittis aliquam. +

    +
    +
    + +
    +

    + ', + '' + ); + ?> +

    + +
    +
    + +
    +
    +
    diff --git a/views/front-end-onboarding/tasks/select-locale.php b/views/front-end-onboarding/tasks/select-locale.php index f2b361fecf..65a1ed17ab 100644 --- a/views/front-end-onboarding/tasks/select-locale.php +++ b/views/front-end-onboarding/tasks/select-locale.php @@ -44,8 +44,8 @@
    'language', 'id' => 'language', @@ -56,7 +56,6 @@ 'echo' => false, ] ); - $prpl_dropdown_html = ob_get_clean(); // Add data-validate attribute to the select element. $prpl_dropdown_html = str_replace( From 566b1db6afd9def8771d1d420f44de703d0f2473 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Tue, 7 Oct 2025 16:57:55 +0200 Subject: [PATCH 34/50] small tweak --- views/front-end-onboarding/tasks/core-siteicon.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/front-end-onboarding/tasks/core-siteicon.php b/views/front-end-onboarding/tasks/core-siteicon.php index cc2e5c8acb..57f40ffb8d 100644 --- a/views/front-end-onboarding/tasks/core-siteicon.php +++ b/views/front-end-onboarding/tasks/core-siteicon.php @@ -34,16 +34,16 @@ printf( // translators: %1$s is opening label tag, %2$s is the closing label tag. \esc_html__( 'Drag & drop a file here, or %1$s browse %2$s', 'progress-planner' ), - '