From 36228ddca17c8f1fdf7722b7fc6506199197f1a8 Mon Sep 17 00:00:00 2001
From: Ari Stathopoulos { __( 'Loading…', 'progress-planner' ) } { error } { __( 'No badge data available.', 'progress-planner' ) }
+ { __(
+ 'The more you work on meaninful content, the sooner you unlock new badges.',
+ 'progress-planner'
+ ) }
+
+
+ { sprintf(
+ /* translators: %s: The badge name. */
+ __( 'Progress %s', 'progress-planner' ),
+ currentBadge.name
+ ) }
+
+
+ { currentBadge.progress }%
+
+
+ { sprintf(
+ /* translators: %s: The remaining number of posts or pages to write. */
+ _n(
+ 'Write %s new post or page and earn your next badge!',
+ 'Write %s new posts or pages and earn your next badge!',
+ currentBadge.remaining,
+ 'progress-planner'
+ ),
+ currentBadge.remaining
+ ) }
+ { badge.name }
-
- get_name() ) );
- ?>
-
- get_progress()['progress']; ?>%
-
- get_progress()['remaining'],
- 'progress-planner'
- )
- ),
- \esc_html( \number_format_i18n( $prpl_widget_details->get_progress()['remaining'] ) )
- )
- ?>
- get_name() ); ?> { __( 'Loading…', 'progress-planner' ) } { error } { __( 'No badge data available.', 'progress-planner' ) }
+ { __(
+ 'Execute at least one website maintenance task every week.',
+ 'progress-planner'
+ ) }
+
+
+ { sprintf(
+ /* translators: %s: The badge name. */
+ __( 'Progress %s', 'progress-planner' ),
+ currentBadge.name
+ ) }
+
+
+ { currentBadge.progress }%
+
+
+ { sprintf(
+ /* translators: %s: The remaining number of weeks. */
+ _n(
+ '%s week to go to complete this streak!',
+ '%s weeks to go to complete this streak!',
+ currentBadge.remaining,
+ 'progress-planner'
+ ),
+ currentBadge.remaining
+ ) }
+ { badge.name }
-
- get_name() ) );
- ?>
-
- get_progress()['progress']; ?>%
-
- get_progress()['remaining'],
- 'progress-planner'
- )
- ),
- \esc_html( \number_format_i18n( $prpl_widget_details->get_progress()['remaining'] ) )
- );
- ?>
- get_name() ); ?> { __( 'Loading…', 'progress-planner' ) } { error } { __( 'No data available.', 'progress-planner' ) }
+ { __(
+ 'Check out your website activity in the past months:',
+ 'progress-planner'
+ ) }
+
+ { __( 'Loading…', 'progress-planner' ) }
+
+ { post.excerpt }
+
+
+
+ );
+}
diff --git a/assets/src/widgets/ContentActivity/index.js b/assets/src/widgets/ContentActivity/index.js
new file mode 100644
index 0000000000..65e6996cdb
--- /dev/null
+++ b/assets/src/widgets/ContentActivity/index.js
@@ -0,0 +1,128 @@
+/**
+ * ContentActivity Widget
+ *
+ * Main widget component for displaying content activity statistics.
+ */
+
+import { useState, useEffect } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import apiFetch from '@wordpress/api-fetch';
+import BigCounter from '../../components/BigCounter';
+import LineChart from '../../components/LineChart';
+import ActivityTable from './ActivityTable';
+
+/**
+ * Loading spinner component.
+ *
+ * @return {JSX.Element} The loading spinner.
+ */
+function LoadingSpinner() {
+ const spinnerStyle = {
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ padding: '2em',
+ };
+
+ return (
+
+
+
+
+ { Object.keys( activityTypes ).map( ( key, index ) => {
+ const rowStyle = {
+ backgroundColor:
+ index % 2 === 0
+ ? 'var(--prpl-background-table)'
+ : 'transparent',
+ };
+
+ return (
+
+ { __( 'Content managed', 'progress-planner' ) }
+
+
+ { __( 'Last week', 'progress-planner' ) }
+
+
+
+ );
+ } ) }
+
+
+
+ { activityTypes[ key ].label }
+
+
+ { weeklyActivity[ key ] || 0 }
+
+
+
+
+
+ { __( 'Total', 'progress-planner' ) }
+
+
+ { totalCount }
+
+
@@ -93,38 +20,4 @@
-
-
-
+
diff --git a/webpack.config.js b/webpack.config.js
new file mode 100644
index 0000000000..36184cda54
--- /dev/null
+++ b/webpack.config.js
@@ -0,0 +1,13 @@
+const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );
+const path = require( 'path' );
+
+module.exports = {
+ ...defaultConfig,
+ entry: {
+ 'content-activity': './assets/src/content-activity.js',
+ },
+ output: {
+ path: path.resolve( __dirname, 'build' ),
+ filename: '[name].js',
+ },
+};
From 54a4c46292d07aed700c23cf68d55288a94650a5 Mon Sep 17 00:00:00 2001
From: Ari Stathopoulos
-
-
-
- $prpl_activity_data ) : ?>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
- get_ui__branding()->get_widget_title( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- 'monthly-badges',
- \esc_html__( 'Your monthly badge', 'progress-planner' )
- );
- ?>
-
-
+ get_ui__branding()->get_widget_title( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ 'monthly-badges',
+ \esc_html__( 'Your monthly badge', 'progress-planner' )
+ );
+ ?>
+
-
-
+
-
-get_previous_incomplete_months_badges() ) ) : ?>
-
-
- get_score()['target'] - $prpl_widget->get_score()['target_score'];
- $prpl_days_remaining = (int) \gmdate( 't' ) - (int) \gmdate( 'j' );
- ?>
-
-
diff --git a/webpack.config.js b/webpack.config.js
index 36184cda54..ef00379a83 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -5,6 +5,7 @@ module.exports = {
...defaultConfig,
entry: {
'content-activity': './assets/src/content-activity.js',
+ 'monthly-badges': './assets/src/monthly-badges.js',
},
output: {
path: path.resolve( __dirname, 'build' ),
From 5eae1ae67bbfc4cd795a3b5be31296b7d436513f Mon Sep 17 00:00:00 2001
From: Ari Stathopoulos
-
);
}
diff --git a/assets/src/content-badges.js b/assets/src/content-badges.js
new file mode 100644
index 0000000000..331b27c6d0
--- /dev/null
+++ b/assets/src/content-badges.js
@@ -0,0 +1,26 @@
+/**
+ * Content Badges Entry Point
+ *
+ * Mounts the ContentBadges React widget to the DOM.
+ */
+
+import { createRoot } from '@wordpress/element';
+import ContentBadges from './widgets/ContentBadges';
+
+/**
+ * Initialize the Content Badges widget.
+ */
+function init() {
+ const container = document.getElementById( 'prpl-content-badges-root' );
+ if ( container ) {
+ const root = createRoot( container );
+ root.render(
+
+
@@ -29,72 +24,7 @@
?>
-
-
-
-
-
+
+
@@ -29,72 +24,7 @@
?>
-
-
-
-
-
+
+
+
+
get_ui__branding()->get_widget_title( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -38,101 +33,4 @@
-
-
-
-
-
-
+
+
+ { posts.map( ( post, index ) => (
+
+
+
+
+ { post.title }
+
+
+
+
get_ui__branding()->get_widget_title( // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
@@ -26,32 +26,5 @@
);
?>
-
-
-
+
+
diff --git a/webpack.config.js b/webpack.config.js
index e8e98e715b..ea6d96b526 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -9,6 +9,7 @@ module.exports = {
'content-badges': './assets/src/content-badges.js',
'streak-badges': './assets/src/streak-badges.js',
'activity-scores': './assets/src/activity-scores.js',
+ 'whats-new': './assets/src/whats-new.js',
},
output: {
path: path.resolve( __dirname, 'build' ),
From 7e6bc6ee219f4ce31617b56251e5166582f22c95 Mon Sep 17 00:00:00 2001
From: Ari Stathopoulos
+ { isUserTask ? (
+
+ ) : (
+
+ ) }
+
+
- -
- - - -- -
-
-
-
-
-
element right after the form
- const existingErrorElement = formElement.parentNode.querySelector(
- 'p.prpl-interactive-task-error-message'
- );
-
- if ( ! existingErrorElement ) {
- // Add paragraph with error message.
- const errorParagraph = document.createElement( 'p' );
- errorParagraph.classList.add(
- 'prpl-note',
- 'prpl-note-error',
- 'prpl-interactive-task-error-message'
- );
- errorParagraph.textContent = prplL10n( 'somethingWentWrong' );
-
- // Append after the form element.
- formElement.insertAdjacentElement( 'afterend', errorParagraph );
- }
- },
-
- /**
- * Show loading state.
- *
- * @param {HTMLFormElement} formElement - The form element.
- * @return {void}
- */
- showLoading: ( formElement ) => {
- let submitButton = formElement.querySelector( 'button[type="submit"]' );
-
- if ( ! submitButton ) {
- submitButton = formElement.querySelector(
- 'button[data-action="completeTask"]'
- );
- }
-
- submitButton.disabled = true;
-
- // Add spinner.
- const spinner = document.createElement( 'span' );
- spinner.classList.add( 'prpl-spinner' );
- spinner.innerHTML =
- ''; // WP spinner.
-
- // Append spinner after submit button.
- submitButton.after( spinner );
- },
-
- /**
- * Hide loading state.
- *
- * @param {HTMLFormElement} formElement - The form element.
- * @return {void}
- */
- hideLoading: ( formElement ) => {
- let submitButton = formElement.querySelector( 'button[type="submit"]' );
-
- if ( ! submitButton ) {
- submitButton = formElement.querySelector(
- 'button[data-action="completeTask"]'
- );
- }
-
- submitButton.disabled = false;
- const spinner = formElement.querySelector( 'span.prpl-spinner' );
- if ( spinner ) {
- spinner.remove();
- }
- },
-};
diff --git a/assets/js/recommendations/remove-terms-without-posts.js b/assets/js/recommendations/remove-terms-without-posts.js
deleted file mode 100644
index 2ed1fbbf67..0000000000
--- a/assets/js/recommendations/remove-terms-without-posts.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/* global progressPlanner, prplInteractiveTaskFormListener */
-/**
- * Remove Terms Without Posts recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task, progress-planner/ajax-request, progress-planner/suggested-task
- */
-( function () {
- /**
- * Remove Terms Without Posts class.
- */
- class RemoveTermsWithoutPosts {
- /**
- * Constructor.
- */
- constructor() {
- this.popoverId = 'prpl-popover-remove-terms-without-posts';
-
- // Early return if the popover is not found.
- if ( ! document.getElementById( this.popoverId ) ) {
- return;
- }
-
- this.currentTermData = null;
- this.currentTaskElement = null;
- this.elements = this.getElements();
- this.init();
- }
-
- /**
- * Get all DOM elements.
- *
- * @return {Object} Object containing all DOM elements.
- */
- getElements() {
- const popover = document.getElementById( this.popoverId );
- return {
- popover,
- popoverTitle: popover.querySelector( '.prpl-popover-title' ),
-
- termNameElement: popover.querySelector(
- '#prpl-delete-term-name'
- ),
- taxonomyElement: popover.querySelector(
- '#prpl-delete-term-taxonomy'
- ),
- taxonomyNameElement: popover.querySelector(
- '#prpl-delete-term-taxonomy-name'
- ),
- termIdField: popover.querySelector( '#prpl-delete-term-id' ),
- taxonomyField: popover.querySelector( '#prpl-delete-taxonomy' ),
- };
- }
-
- /**
- * Initialize the component.
- */
- init() {
- this.bindEvents();
- }
-
- /**
- * Bind event listeners.
- */
- bindEvents() {
- // Listen for the generic interactive task action event.
- document.addEventListener(
- 'prpl-interactive-task-action-remove-terms-without-posts',
- ( event ) => {
- this.handleInteractiveTaskAction( event );
-
- // After the event is handled, initialize the form listener.
- this.initFormListener();
- }
- );
- }
-
- /**
- * Handle interactive task action event.
- *
- * @param {CustomEvent} event The custom event with task context data.
- */
- handleInteractiveTaskAction( event ) {
- this.currentTermData = {
- termId: this.decodeHtmlEntities( event.detail.target_term_id ),
- taxonomy: this.decodeHtmlEntities(
- event.detail.target_taxonomy
- ),
- taxonomyName: this.decodeHtmlEntities(
- event.detail.target_taxonomy_name
- ),
- termName: this.decodeHtmlEntities(
- event.detail.target_term_name
- ),
- };
-
- // Store reference to the task element that triggered this.
- this.currentTaskElement = event.target.closest(
- '.prpl-suggested-task'
- );
-
- // Update the popover content with the term data.
- this.updatePopoverContent(
- this.currentTermData.termId,
- this.currentTermData.taxonomy,
- this.currentTermData.termName,
- this.currentTermData.taxonomyName,
- this.decodeHtmlEntities( event.detail.post_title )
- );
- }
-
- /**
- * Update the popover content.
- *
- * @param {string} termId The term ID.
- * @param {string} taxonomy The taxonomy.
- * @param {string} termName The term name.
- * @param {string} taxonomyName The taxonomy name.
- * @param {string} postTitle The post title.
- */
- updatePopoverContent(
- termId,
- taxonomy,
- termName,
- taxonomyName,
- postTitle
- ) {
- if ( this.elements.popoverTitle ) {
- this.elements.popoverTitle.textContent = postTitle;
- }
-
- if ( this.elements.termNameElement ) {
- this.elements.termNameElement.textContent = termName;
- }
-
- if ( this.elements.taxonomyElement ) {
- this.elements.taxonomyElement.textContent = taxonomy;
- }
-
- if ( this.elements.taxonomyNameElement ) {
- this.elements.taxonomyNameElement.textContent = taxonomyName;
- }
-
- if ( this.elements.termIdField ) {
- this.elements.termIdField.value = termId;
- }
-
- if ( this.elements.taxonomyField ) {
- this.elements.taxonomyField.value = taxonomy;
- }
- }
-
- /**
- * Initialize the form listener.
- */
- initFormListener() {
- if ( ! this.currentTermData || ! this.currentTaskElement ) {
- return;
- }
-
- prplInteractiveTaskFormListener.customSubmit( {
- taskId: this.currentTaskElement.dataset.taskId,
- popoverId: this.popoverId,
- callback: () => {
- return new Promise( ( resolve, reject ) => {
- fetch( progressPlanner.ajaxUrl, {
- method: 'POST',
- headers: {
- 'Content-Type':
- 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams( {
- action: 'prpl_interactive_task_submit_remove-terms-without-posts',
- nonce: progressPlanner.nonce,
- term_id: this.elements.termIdField.value,
- taxonomy: this.elements.taxonomyField.value,
- } ),
- } )
- .then( () => {
- this.currentTaskElement = null;
- this.currentTermData = null;
- } )
- .then( ( response ) => {
- resolve( { response, success: true } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } );
- },
- } );
- }
-
- /**
- * Decodes HTML entities in a string (like ", &, etc.)
- * @param {string} str The string to decode.
- * @return {string} The decoded string.
- */
- decodeHtmlEntities( str ) {
- if ( typeof str !== 'string' ) {
- return str;
- }
-
- return str
- .replace( /"/g, '"' )
- .replace( /'/g, "'" )
- .replace( /</g, '<' )
- .replace( />/g, '>' )
- .replace( /&/g, '&' );
- }
- }
-
- // Initialize the component.
- new RemoveTermsWithoutPosts();
-} )();
diff --git a/assets/js/recommendations/rename-uncategorized-category.js b/assets/js/recommendations/rename-uncategorized-category.js
deleted file mode 100644
index 0adc4b9128..0000000000
--- a/assets/js/recommendations/rename-uncategorized-category.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/* global prplInteractiveTaskFormListener, progressPlanner, prplDocumentReady */
-
-/*
- * Rename the Uncategorized category.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-
-prplInteractiveTaskFormListener.customSubmit( {
- taskId: 'rename-uncategorized-category',
- popoverId: 'prpl-popover-rename-uncategorized-category',
- callback: () => {
- const name = document.querySelector(
- '#prpl-popover-rename-uncategorized-category input[name="prpl_uncategorized_category_name"]'
- );
- const slug = document.querySelector(
- '#prpl-popover-rename-uncategorized-category input[name="prpl_uncategorized_category_slug"]'
- );
-
- return new Promise( ( resolve, reject ) => {
- fetch( progressPlanner.ajaxUrl, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams( {
- action: 'prpl_interactive_task_submit_rename-uncategorized-category',
- nonce: progressPlanner.nonce,
- uncategorized_category_name: name.value,
- uncategorized_category_slug: slug.value,
- } ),
- } )
- .then( ( response ) => {
- resolve( { response, success: true } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } );
- },
-} );
-
-prplDocumentReady( () => {
- const name = document.querySelector(
- '#prpl-popover-rename-uncategorized-category input[name="prpl_uncategorized_category_name"]'
- );
- const slug = document.querySelector(
- '#prpl-popover-rename-uncategorized-category input[name="prpl_uncategorized_category_slug"]'
- );
-
- if ( ! name || ! slug ) {
- return;
- }
-
- // Function to check if both fields are valid and toggle button state
- const toggleSubmitButton = () => {
- const submitButton = document.querySelector(
- '#prpl-popover-rename-uncategorized-category button[type="submit"]'
- );
- const isNameValid =
- name.value &&
- name.value.toLowerCase() !== name.placeholder.toLowerCase();
- const isSlugValid =
- slug.value &&
- slug.value.toLowerCase() !== slug.placeholder.toLowerCase();
-
- submitButton.disabled = ! ( isNameValid && isSlugValid );
- };
-
- // If there is no name or slug or it is the same as placeholder the submit button should be disabled.
- toggleSubmitButton();
-
- // Add event listeners to both fields
- name.addEventListener( 'input', toggleSubmitButton );
- slug.addEventListener( 'input', toggleSubmitButton );
-} );
diff --git a/assets/js/recommendations/sample-page.js b/assets/js/recommendations/sample-page.js
deleted file mode 100644
index 7b10ed1cb4..0000000000
--- a/assets/js/recommendations/sample-page.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* global prplInteractiveTaskFormListener, samplePageData */
-
-/*
- * Core Blog Description recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-
-prplInteractiveTaskFormListener.customSubmit( {
- taskId: 'sample-page',
- popoverId: 'prpl-popover-sample-page',
- callback: () => {
- return new Promise( ( resolve, reject ) => {
- const post = new wp.api.models.Page( {
- id: samplePageData.postId,
- } );
- post.fetch()
- .then( () => {
- // Handle the case when plain URL structure is used, it used to result in invalid URL (404): http://localhost:8080/index.php?rest_route=/wp/v2/prpl_recommendations/35?force=true
- const url = post.url().includes( 'rest_route=' )
- ? post.url() + '&force=true'
- : post.url() + '?force=true';
-
- post.destroy( { url } )
- .then( () => {
- resolve( { success: true } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } );
- },
-} );
diff --git a/assets/js/recommendations/search-engine-visibility.js b/assets/js/recommendations/search-engine-visibility.js
deleted file mode 100644
index 9d4e13139a..0000000000
--- a/assets/js/recommendations/search-engine-visibility.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Search Engine Visibility recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- settingAPIKey: 'blog_public',
- setting: 'blog_public',
- taskId: 'search-engine-visibility',
- popoverId: 'prpl-popover-search-engine-visibility',
- action: 'prpl_interactive_task_submit_search-engine-visibility',
-} );
diff --git a/assets/js/recommendations/select-locale.js b/assets/js/recommendations/select-locale.js
deleted file mode 100644
index 425fbf87da..0000000000
--- a/assets/js/recommendations/select-locale.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Select Locale recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-
-prplInteractiveTaskFormListener.settings( {
- settingAPIKey: 'language',
- setting: 'language',
- taskId: 'select-locale',
- popoverId: 'prpl-popover-select-locale',
- action: 'prpl_interactive_task_submit_select-locale',
-} );
diff --git a/assets/js/recommendations/select-timezone.js b/assets/js/recommendations/select-timezone.js
deleted file mode 100644
index 35f2607a3a..0000000000
--- a/assets/js/recommendations/select-timezone.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* global prplInteractiveTaskFormListener, prplDocumentReady */
-
-/*
- * Set the site timezone.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-
-prplInteractiveTaskFormListener.settings( {
- settingAPIKey: 'timezone',
- setting: 'timezone',
- taskId: 'select-timezone',
- popoverId: 'prpl-popover-select-timezone',
- action: 'prpl_interactive_task_submit_select-timezone',
-} );
-
-prplDocumentReady( () => {
- const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- const timezoneSelect = document.querySelector( 'select#timezone' );
- const timezoneSaved = timezoneSelect?.dataset?.timezoneSaved || 'false';
-
- // Try to preselect the timezone.
- if ( timezone && timezoneSelect && 'false' === timezoneSaved ) {
- timezoneSelect.value = timezone;
- }
-} );
diff --git a/assets/js/recommendations/set-date-format.js b/assets/js/recommendations/set-date-format.js
deleted file mode 100644
index 82a00f13f4..0000000000
--- a/assets/js/recommendations/set-date-format.js
+++ /dev/null
@@ -1,129 +0,0 @@
-/* global prplInteractiveTaskFormListener, prplDocumentReady, progressPlanner */
-
-/*
- * Set the site date format.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-
-prplInteractiveTaskFormListener.customSubmit( {
- taskId: 'set-date-format',
- popoverId: 'prpl-popover-set-date-format',
- callback: () => {
- return new Promise( ( resolve, reject ) => {
- const format = document.querySelector(
- '#prpl-popover-set-date-format input[name="date_format"]:checked'
- );
- const customFormat = document.querySelector(
- '#prpl-popover-set-date-format input[name="date_format_custom"]'
- );
-
- fetch( progressPlanner.ajaxUrl, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams( {
- action: 'prpl_interactive_task_submit_set-date-format',
- nonce: progressPlanner.nonce,
- date_format: format.value,
- date_format_custom: customFormat.value,
- } ),
- } )
- .then( ( response ) => {
- resolve( { response, success: true } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } );
- },
-} );
-
-prplDocumentReady( () => {
- // Handle date format radio button clicks
- document
- .querySelectorAll(
- '#prpl-popover-set-date-format input[name="date_format"]'
- )
- .forEach( function ( input ) {
- input.addEventListener( 'click', function () {
- if ( 'date_format_custom_radio' !== this.id ) {
- const customInput = document.querySelector(
- '#prpl-popover-set-date-format input[name="date_format_custom"]'
- );
- const fieldset = customInput.closest( 'fieldset' );
- const exampleElement = fieldset.querySelector( '.example' );
- const formatText =
- this.parentElement.querySelector(
- '.format-i18n'
- ).textContent;
-
- customInput.value = this.value;
- exampleElement.textContent = formatText;
- }
- } );
- } );
-
- // Handle custom date format input
- const customDateInput = document.querySelector(
- 'input[name="date_format_custom"]'
- );
-
- if ( customDateInput ) {
- customDateInput.addEventListener( 'click', function () {
- document.getElementById(
- 'date_format_custom_radio'
- ).checked = true;
- } );
-
- customDateInput.addEventListener( 'input', function () {
- document.getElementById(
- 'date_format_custom_radio'
- ).checked = true;
-
- const format = this;
- const fieldset = format.closest( 'fieldset' );
- const example = fieldset.querySelector( '.example' );
-
- // Debounce the event callback while users are typing.
- clearTimeout( format.dataset.timer );
- format.dataset.timer = setTimeout( function () {
- // If custom date is not empty.
- if ( format.value ) {
- // Find the spinner element within the fieldset
- const spinner = fieldset.querySelector( '.spinner' );
- if ( spinner ) {
- spinner.classList.add( 'is-active' );
- }
-
- // Use fetch instead of $.post
- fetch( progressPlanner.ajaxUrl, {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams( {
- action: 'date_format',
- date: format.value,
- } ),
- } )
- .then( function ( response ) {
- return response.text();
- } )
- .then( function ( data ) {
- example.textContent = data;
- } )
- .catch( function ( error ) {
- console.error( 'Error:', error );
- } )
- .finally( function () {
- if ( spinner ) {
- spinner.classList.remove( 'is-active' );
- }
- } );
- }
- }, 500 );
- } );
- }
-} );
diff --git a/assets/js/recommendations/update-term-description.js b/assets/js/recommendations/update-term-description.js
deleted file mode 100644
index 3eb925e176..0000000000
--- a/assets/js/recommendations/update-term-description.js
+++ /dev/null
@@ -1,250 +0,0 @@
-/* global progressPlanner, prplInteractiveTaskFormListener */
-/**
- * Update Term Description recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task, progress-planner/ajax-request, progress-planner/suggested-task
- */
-( function () {
- /**
- * Update Term Description class.
- */
- class UpdateTermDescription {
- /**
- * Constructor.
- */
- constructor() {
- this.popoverId = 'prpl-popover-update-term-description';
-
- // Early return if the popover is not found.
- if ( ! document.getElementById( this.popoverId ) ) {
- return;
- }
-
- this.currentTermData = null;
- this.currentTaskElement = null;
- this.elements = this.getElements();
- this.init();
- }
-
- /**
- * Get all DOM elements.
- *
- * @return {Object} Object containing all DOM elements.
- */
- getElements() {
- const popover = document.getElementById( this.popoverId );
-
- return {
- popover,
- popoverTitle: popover.querySelector( '.prpl-popover-title' ),
-
- termNameElement: popover.querySelector(
- '#prpl-update-term-name'
- ),
- taxonomyElement: popover.querySelector(
- '#prpl-update-term-taxonomy'
- ),
- taxonomyNameElement: popover.querySelector(
- '#prpl-update-term-taxonomy-name'
- ),
- termIdField: popover.querySelector( '#prpl-update-term-id' ),
- taxonomyField: popover.querySelector( '#prpl-update-taxonomy' ),
- descriptionField: popover.querySelector(
- '#prpl-term-description'
- ),
- };
- }
-
- /**
- * Initialize the component.
- */
- init() {
- this.bindEvents();
- }
-
- /**
- * Bind event listeners.
- */
- bindEvents() {
- // Listen for the generic interactive task action event.
- document.addEventListener(
- 'prpl-interactive-task-action-update-term-description',
- ( event ) => {
- this.handleInteractiveTaskAction( event );
-
- // After the event is handled, initialize the form listener.
- this.initFormListener();
- }
- );
- }
-
- /**
- * Handle interactive task action event.
- *
- * @param {CustomEvent} event The custom event with task context data.
- */
- handleInteractiveTaskAction( event ) {
- this.currentTermData = {
- termId: this.decodeHtmlEntities( event.detail.target_term_id ),
- taxonomy: this.decodeHtmlEntities(
- event.detail.target_taxonomy
- ),
- taxonomyName: this.decodeHtmlEntities(
- event.detail.target_taxonomy_name
- ),
- termName: this.decodeHtmlEntities(
- event.detail.target_term_name
- ),
- };
-
- // Store reference to the task element that triggered this.
- this.currentTaskElement = event.target.closest(
- '.prpl-suggested-task'
- );
-
- // Update the popover content with the term data.
- this.updatePopoverContent(
- this.currentTermData.termId,
- this.currentTermData.taxonomy,
- this.currentTermData.termName,
- this.currentTermData.taxonomyName,
- this.decodeHtmlEntities( event.detail.post_title )
- );
- }
-
- /**
- * Update the popover content.
- *
- * @param {string} termId The term ID.
- * @param {string} taxonomy The taxonomy.
- * @param {string} termName The term name.
- * @param {string} taxonomyName The taxonomy name.
- * @param {string} postTitle The post title.
- */
- updatePopoverContent(
- termId,
- taxonomy,
- termName,
- taxonomyName,
- postTitle
- ) {
- if ( this.elements.popoverTitle ) {
- this.elements.popoverTitle.textContent = postTitle;
- }
-
- if ( this.elements.termNameElement ) {
- this.elements.termNameElement.textContent = termName;
- }
-
- if ( this.elements.taxonomyElement ) {
- this.elements.taxonomyElement.textContent = taxonomy;
- }
-
- if ( this.elements.taxonomyNameElement ) {
- this.elements.taxonomyNameElement.textContent = taxonomyName;
- }
-
- if ( this.elements.termIdField ) {
- this.elements.termIdField.value = termId;
- }
-
- if ( this.elements.taxonomyField ) {
- this.elements.taxonomyField.value = taxonomy;
- }
-
- // Clear the description field.
- if ( this.elements.descriptionField ) {
- this.elements.descriptionField.value = '';
- }
- }
-
- /**
- * Initialize the form listener.
- */
- initFormListener() {
- if ( ! this.currentTermData || ! this.currentTaskElement ) {
- return;
- }
-
- const formElement = this.elements.popover.querySelector( 'form' );
-
- if ( ! formElement ) {
- return;
- }
-
- // Submit button should be disabled if description is empty.
- const submitButton = document.getElementById(
- 'prpl-update-term-description-button'
- );
-
- if ( submitButton ) {
- submitButton.disabled = true;
- }
-
- // Add event listener to description field.
- const descriptionField = formElement.querySelector(
- '#prpl-term-description'
- );
- if ( descriptionField ) {
- descriptionField.addEventListener( 'input', () => {
- submitButton.disabled = ! descriptionField.value.trim();
- } );
- }
-
- prplInteractiveTaskFormListener.customSubmit( {
- taskId: this.currentTaskElement.dataset.taskId,
- popoverId: this.popoverId,
- callback: () => {
- return new Promise( ( resolve, reject ) => {
- fetch( progressPlanner.ajaxUrl, {
- method: 'POST',
- headers: {
- 'Content-Type':
- 'application/x-www-form-urlencoded',
- },
- body: new URLSearchParams( {
- action: 'prpl_interactive_task_submit_update-term-description',
- nonce: progressPlanner.nonce,
- term_id: this.elements.termIdField.value,
- taxonomy: this.elements.taxonomyField.value,
- description:
- this.elements.descriptionField.value,
- } ),
- } )
- .then( () => {
- this.currentTaskElement = null;
- this.currentTermData = null;
- } )
- .then( ( response ) => {
- resolve( { response, success: true } );
- } )
- .catch( ( error ) => {
- reject( { success: false, error } );
- } );
- } );
- },
- } );
- }
-
- /**
- * Decodes HTML entities in a string (like ", &, etc.)
- * @param {string} str The string to decode.
- * @return {string} The decoded string.
- */
- decodeHtmlEntities( str ) {
- if ( typeof str !== 'string' ) {
- return str;
- }
-
- return str
- .replace( /"/g, '"' )
- .replace( /'/g, "'" )
- .replace( /</g, '<' )
- .replace( />/g, '>' )
- .replace( /&/g, '&' );
- }
- }
-
- // Initialize the component.
- new UpdateTermDescription();
-} )();
diff --git a/assets/js/recommendations/yoast-author-archive.js b/assets/js/recommendations/yoast-author-archive.js
deleted file mode 100644
index 78cd23658b..0000000000
--- a/assets/js/recommendations/yoast-author-archive.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast author archive recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo_titles',
- settingPath: JSON.stringify( [ 'disable-author' ] ),
- taskId: 'yoast-author-archive',
- popoverId: 'prpl-popover-yoast-author-archive',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-crawl-settings-emoji-scripts.js b/assets/js/recommendations/yoast-crawl-settings-emoji-scripts.js
deleted file mode 100644
index db5ad25e04..0000000000
--- a/assets/js/recommendations/yoast-crawl-settings-emoji-scripts.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast remove emoji scripts recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo',
- settingPath: JSON.stringify( [ 'remove_emoji_scripts' ] ),
- taskId: 'yoast-crawl-settings-emoji-scripts',
- popoverId: 'prpl-popover-yoast-crawl-settings-emoji-scripts',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-crawl-settings-feed-authors.js b/assets/js/recommendations/yoast-crawl-settings-feed-authors.js
deleted file mode 100644
index e871be7f71..0000000000
--- a/assets/js/recommendations/yoast-crawl-settings-feed-authors.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast remove post authors feeds recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo',
- settingPath: JSON.stringify( [ 'remove_feed_authors' ] ),
- taskId: 'yoast-crawl-settings-feed-authors',
- popoverId: 'prpl-popover-yoast-crawl-settings-feed-authors',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-crawl-settings-feed-global-comments.js b/assets/js/recommendations/yoast-crawl-settings-feed-global-comments.js
deleted file mode 100644
index 4643dbdb6c..0000000000
--- a/assets/js/recommendations/yoast-crawl-settings-feed-global-comments.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast remove global comment feeds recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo',
- settingPath: JSON.stringify( [ 'remove_feed_global_comments' ] ),
- taskId: 'yoast-crawl-settings-feed-global-comments',
- popoverId: 'prpl-popover-yoast-crawl-settings-feed-global-comments',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-date-archive.js b/assets/js/recommendations/yoast-date-archive.js
deleted file mode 100644
index 5f9ed45bbd..0000000000
--- a/assets/js/recommendations/yoast-date-archive.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast date archive recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo_titles',
- settingPath: JSON.stringify( [ 'disable-date' ] ),
- taskId: 'yoast-date-archive',
- popoverId: 'prpl-popover-yoast-date-archive',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-format-archive.js b/assets/js/recommendations/yoast-format-archive.js
deleted file mode 100644
index 4beef5357b..0000000000
--- a/assets/js/recommendations/yoast-format-archive.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast format archive recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo_titles',
- settingPath: JSON.stringify( [ 'disable-post_format' ] ),
- taskId: 'yoast-format-archive',
- popoverId: 'prpl-popover-yoast-format-archive',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-media-pages.js b/assets/js/recommendations/yoast-media-pages.js
deleted file mode 100644
index 36fa395e42..0000000000
--- a/assets/js/recommendations/yoast-media-pages.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* global prplInteractiveTaskFormListener */
-
-/*
- * Yoast remove global comment feeds recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task
- */
-prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo_titles',
- settingPath: JSON.stringify( [ 'disable-attachment' ] ),
- taskId: 'yoast-media-pages',
- popoverId: 'prpl-popover-yoast-media-pages',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => true,
-} );
diff --git a/assets/js/recommendations/yoast-organization-logo.js b/assets/js/recommendations/yoast-organization-logo.js
deleted file mode 100644
index dc70f09c47..0000000000
--- a/assets/js/recommendations/yoast-organization-logo.js
+++ /dev/null
@@ -1,217 +0,0 @@
-/* global prplInteractiveTaskFormListener, prplYoastOrganizationLogo */
-/**
- * Core Site Icon recommendation.
- *
- * Dependencies: progress-planner/recommendations/interactive-task, wp-api
- */
-( function () {
- /**
- * Core Site Icon class.
- */
- class CoreSiteIcon {
- /**
- * Constructor.
- */
- constructor() {
- this.mediaUploader = null;
- this.elements = this.getElements();
- this.init();
- }
-
- /**
- * Get all DOM elements.
- *
- * @return {Object} Object containing all DOM elements.
- */
- getElements() {
- return {
- uploadButton: document.getElementById(
- 'prpl-upload-organization-logo-button'
- ),
- popover: document.getElementById(
- 'prpl-popover-yoast-organization-logo'
- ),
- hiddenField: document.getElementById(
- 'prpl-yoast-organization-logo-id'
- ),
- preview: document.getElementById( 'organization-logo-preview' ),
- submitButton: document.getElementById(
- 'prpl-set-organization-logo-button'
- ),
- };
- }
-
- /**
- * Initialize the component.
- */
- init() {
- if ( this.elements.uploadButton ) {
- this.bindEvents();
- }
- this.initFormListener();
- }
-
- /**
- * Bind event listeners.
- */
- bindEvents() {
- this.elements.uploadButton.addEventListener( 'click', ( e ) => {
- this.handleUploadButtonClick( e );
- } );
- }
-
- /**
- * Handle upload button click.
- *
- * @param {Event} e The click event.
- */
- handleUploadButtonClick( e ) {
- e.preventDefault();
-
- // If the uploader object has already been created, reopen the dialog.
- if ( this.mediaUploader ) {
- this.mediaUploader.open();
- return;
- }
-
- this.createMediaUploader();
- this.bindMediaUploaderEvents();
- this.mediaUploader.open();
- }
-
- /**
- * Create the media uploader.
- */
- createMediaUploader() {
- this.mediaUploader = wp.media.frames.file_frame = wp.media( {
- title:
- prplYoastOrganizationLogo?.mediaTitle || 'Choose Site Icon',
- button: {
- text:
- prplYoastOrganizationLogo?.mediaButtonText ||
- 'Use as Site Icon',
- },
- multiple: false,
- library: {
- type: 'image',
- },
- } );
- }
-
- /**
- * Bind media uploader events.
- */
- bindMediaUploaderEvents() {
- // Hide popover when media library opens.
- this.mediaUploader.on( 'open', () => {
- if ( this.elements.popover ) {
- this.elements.popover.hidePopover();
- }
- } );
-
- // Show popover when media library closes.
- this.mediaUploader.on( 'close', () => {
- if ( this.elements.popover ) {
- this.elements.popover.showPopover();
- }
- } );
-
- // Handle image selection.
- this.mediaUploader.on( 'select', () => {
- this.handleImageSelection();
- } );
- }
-
- /**
- * Handle image selection.
- */
- handleImageSelection() {
- const attachment = this.mediaUploader
- .state()
- .get( 'selection' )
- .first()
- .toJSON();
-
- this.updateHiddenField( attachment );
- this.updatePreview( attachment );
- this.enableSubmitButton();
- }
-
- /**
- * Update the hidden field with attachment ID.
- *
- * @param {Object} attachment The selected attachment.
- */
- updateHiddenField( attachment ) {
- if ( this.elements.hiddenField ) {
- this.elements.hiddenField.value = attachment.id;
- }
- }
-
- /**
- * Update the preview with the selected image.
- *
- * @param {Object} attachment The selected attachment.
- */
- updatePreview( attachment ) {
- if ( ! this.elements.preview ) {
- return;
- }
-
- // Use thumbnail size if available, otherwise use full size.
- const imageUrl =
- attachment.sizes && attachment.sizes.thumbnail
- ? attachment.sizes.thumbnail.url
- : attachment.url;
-
- this.elements.preview.innerHTML =
- '';
- }
-
- /**
- * Enable the submit button.
- */
- enableSubmitButton() {
- if ( this.elements.submitButton ) {
- this.elements.submitButton.disabled = false;
- }
- }
-
- /**
- * Initialize the form listener.
- */
- initFormListener() {
- prplInteractiveTaskFormListener.settings( {
- setting: 'wpseo_titles',
- settingPath:
- 'company' === prplYoastOrganizationLogo.companyOrPerson
- ? JSON.stringify( [ 'company_logo_id' ] )
- : JSON.stringify( [ 'person_logo_id' ] ),
- taskId: 'yoast-organization-logo',
- popoverId: 'prpl-popover-yoast-organization-logo',
- action: 'prpl_interactive_task_submit',
- settingCallbackValue: () => {
- const popover = document.getElementById(
- 'prpl-popover-yoast-organization-logo'
- );
-
- if ( ! popover ) {
- return false;
- }
-
- const organizationLogoId = popover.querySelector(
- 'input[name="prpl_yoast_organization_logo_id"]'
- ).value;
- return parseInt( organizationLogoId, 10 );
- },
- } );
- }
- }
-
- // Initialize the component.
- new CoreSiteIcon();
-} )();
diff --git a/assets/js/suggested-task.js b/assets/js/suggested-task.js
deleted file mode 100644
index 42bd863074..0000000000
--- a/assets/js/suggested-task.js
+++ /dev/null
@@ -1,653 +0,0 @@
-/* global HTMLElement, prplSuggestedTask, prplL10n, prplUpdateRaviGauge, prplTerms */
-/*
- * Suggested Task scripts & helpers.
- *
- * Dependencies: wp-api, progress-planner/l10n, progress-planner/suggested-task-terms, progress-planner/web-components/prpl-gauge, progress-planner/widgets/suggested-tasks
- */
-/* eslint-disable camelcase, jsdoc/require-param-type, jsdoc/require-param, jsdoc/check-param-names */
-
-prplSuggestedTask = {
- ...prplSuggestedTask,
- injectedItemIds: [],
- l10n: {
- info: prplL10n( 'info' ),
- moveUp: prplL10n( 'moveUp' ),
- moveDown: prplL10n( 'moveDown' ),
- snooze: prplL10n( 'snooze' ),
- disabledRRCheckboxTooltip: prplL10n( 'disabledRRCheckboxTooltip' ),
- markAsComplete: prplL10n( 'markAsComplete' ),
- taskDelete: prplL10n( 'taskDelete' ),
- delete: prplL10n( 'delete' ),
- whyIsThisImportant: prplL10n( 'whyIsThisImportant' ),
- },
-
- /**
- * Fetch items for arguments.
- *
- * @param {Object} args The arguments to pass to the injectItems method.
- * @return {Promise} A promise that resolves with the collection of posts.
- */
- fetchItems: ( args ) => {
- console.info(
- `Fetching recommendations with args: ${ JSON.stringify( args ) }...`
- );
-
- const fetchData = {
- status: args.status,
- per_page: args.per_page || 100,
- _embed: true,
- exclude: prplSuggestedTask.injectedItemIds,
- filter: {
- orderby: 'menu_order',
- order: 'ASC',
- },
- };
-
- // Pass through provider and exclude_provider if provided.
- if ( args.provider ) {
- fetchData.provider = args.provider;
- }
- if ( args.exclude_provider ) {
- fetchData.exclude_provider = args.exclude_provider;
- }
-
- return prplSuggestedTask
- .getPostsCollectionPromise( { data: fetchData } )
- .then( ( response ) => response.data );
- },
-
- /**
- * Inject items.
- *
- * @param {Object[]} items The items to inject.
- */
- injectItems: ( items ) => {
- if ( items.length ) {
- // Inject the items into the DOM.
- items.forEach( ( item ) => {
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/injectItem', {
- detail: {
- item,
- listId: 'prpl-suggested-tasks-list',
- insertPosition: 'beforeend',
- },
- } )
- );
- prplSuggestedTask.injectedItemIds.push( item.id );
- } );
- }
-
- // Trigger the grid resize event.
- window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) );
- },
-
- /**
- * Get a collection of posts.
- *
- * @param {Object} fetchArgs The arguments to pass to the fetch method.
- * @return {Promise} A promise that resolves with the collection of posts.
- */
- getPostsCollectionPromise: ( fetchArgs ) => {
- const collectionsPromise = new Promise( ( resolve ) => {
- const postsCollection =
- new wp.api.collections.Prpl_recommendations();
- postsCollection
- .fetch( fetchArgs )
- .done( ( data ) => resolve( { data, postsCollection } ) );
- } );
-
- return collectionsPromise;
- },
-
- /**
- * Render a new item.
- *
- * @param {Object} post The post object.
- */
- getNewItemTemplatePromise: ( { post = {}, listId = '' } ) =>
- new Promise( ( resolve ) => {
- const { prpl_recommendations_provider } = post;
- const terms = { prpl_recommendations_provider };
-
- Object.values( prplTerms.get( 'provider' ) ).forEach( ( term ) => {
- if ( term.id === terms[ prplTerms.provider ][ 0 ] ) {
- terms[ prplTerms.provider ] = term;
- }
- } );
-
- const template = wp.template( 'prpl-suggested-task' );
- const data = {
- post,
- terms,
- listId,
- assets: prplSuggestedTask.assets,
- action: 'pending' === post.status ? 'celebrate' : '',
- l10n: prplSuggestedTask.l10n,
- };
-
- resolve( template( data ) );
- } ),
-
- /**
- * Run a task action.
- *
- * @param {number} postId The post ID.
- * @param {string} actionType The action type.
- * @return {Promise} A promise that resolves with the response from the server.
- */
- runTaskAction: ( postId, actionType ) =>
- wp.ajax.post( 'progress_planner_suggested_task_action', {
- post_id: postId,
- nonce: prplSuggestedTask.nonce,
- action_type: actionType,
- } ),
-
- /**
- * Trash (delete) a task.
- * Only user tasks can be trashed.
- *
- * @param {number} postId The post ID.
- */
- trash: ( postId ) => {
- const post = new wp.api.models.Prpl_recommendations( {
- id: postId,
- } );
- post.fetch().then( () => {
- // Handle the case when plain URL structure is used, it used to result in invalid URL (404): http://localhost:8080/index.php?rest_route=/wp/v2/prpl_recommendations/35?force=true
- const url = post.url().includes( 'rest_route=' )
- ? post.url() + '&force=true'
- : post.url() + '?force=true';
-
- post.destroy( { url } ).then( () => {
- // Remove the task from the todo list.
- prplSuggestedTask.removeTaskElement( postId );
-
- // Fetch and inject a replacement task
- prplSuggestedTask.fetchAndInjectReplacementTask();
-
- setTimeout(
- () =>
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- ),
- 500
- );
-
- prplSuggestedTask.runTaskAction( postId, 'delete' );
- } );
- } );
- },
-
- /**
- * Maybe complete a task.
- *
- * @param {number} postId The post ID.
- */
- maybeComplete: ( postId ) => {
- // Return the promise chain so callers can wait for completion
- return new Promise( ( resolve, reject ) => {
- // Get the task.
- const post = new wp.api.models.Prpl_recommendations( {
- id: postId,
- } );
- post.fetch()
- .then( ( postData ) => {
- const taskProviderId = prplTerms.getTerm(
- postData?.[ prplTerms.provider ],
- prplTerms.provider
- ).slug;
-
- const el = prplSuggestedTask.getTaskElement( postId );
-
- // Dismissable tasks don't have pending status, it's either publish or trash.
- const newStatus =
- 'publish' === postData.status ? 'trash' : 'publish';
-
- // Disable the checkbox for RR tasks, to prevent multiple clicks.
- el.querySelector(
- '.prpl-suggested-task-checkbox'
- )?.setAttribute( 'disabled', 'disabled' );
-
- post.set( 'status', newStatus )
- .save()
- .then( () => {
- prplSuggestedTask.runTaskAction(
- postId,
- 'trash' === newStatus ? 'complete' : 'pending'
- );
- const eventPoints = parseInt(
- postData?.prpl_points
- );
-
- // Task is trashed, check if we need to celebrate.
- if ( 'trash' === newStatus ) {
- el.setAttribute(
- 'data-task-action',
- 'celebrate'
- );
- if ( 'user' === taskProviderId ) {
- // Set class to trigger strike through animation.
- el.classList.add(
- 'prpl-suggested-task-celebrated'
- );
-
- setTimeout( () => {
- // Move task from published to trash.
- document
- .getElementById(
- 'todo-list-completed'
- )
- .insertAdjacentElement(
- 'beforeend',
- el
- );
-
- // Remove the class to trigger the strike through animation.
- el.classList.remove(
- 'prpl-suggested-task-celebrated'
- );
-
- window.dispatchEvent(
- new CustomEvent(
- 'prpl/grid/resize'
- )
- );
-
- // Remove the disabled attribute for user tasks, so they can be clicked again.
- el.querySelector(
- '.prpl-suggested-task-checkbox'
- )?.removeAttribute( 'disabled' );
-
- // Resolve the promise after the timeout completes
- resolve( {
- postId,
- newStatus,
- eventPoints,
- } );
- }, 2000 );
- } else {
- // Check the chekcbox, since completing task can be triggered in different ways ("Mark as done" button), without triggering the onchange event.
- const checkbox = el.querySelector(
- '.prpl-suggested-task-checkbox'
- );
- if ( checkbox ) {
- checkbox.checked = true;
- }
-
- /**
- * Strike completed tasks and remove them from the DOM.
- */
- document.dispatchEvent(
- new CustomEvent(
- 'prpl/removeCelebratedTasks'
- )
- );
-
- // Fetch and inject a replacement task for non-user tasks
- prplSuggestedTask.fetchAndInjectReplacementTask();
-
- // Resolve immediately for non-user tasks
- resolve( {
- postId,
- newStatus,
- eventPoints,
- } );
- }
-
- // We trigger celebration only if the task has points.
- if ( 0 < eventPoints ) {
- prplUpdateRaviGauge( eventPoints );
-
- // Trigger the celebration event (confetti).
- document.dispatchEvent(
- new CustomEvent(
- 'prpl/celebrateTasks',
- {
- detail: { element: el },
- }
- )
- );
- }
- } else if (
- 'publish' === newStatus &&
- 'user' === taskProviderId
- ) {
- // This is only possible for user tasks.
- // Set the task action to publish.
- el.setAttribute(
- 'data-task-action',
- 'publish'
- );
-
- // Update the Ravi gauge.
- prplUpdateRaviGauge( 0 - eventPoints );
-
- // Move task from trash to published, tasks with points go to the beginning of the list.
- document
- .getElementById( 'todo-list' )
- .insertAdjacentElement(
- 0 < eventPoints
- ? 'afterbegin'
- : 'beforeend',
- el
- );
-
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- );
-
- // Remove the disabled attribute for user tasks, so they can be clicked again.
- el.querySelector(
- '.prpl-suggested-task-checkbox'
- )?.removeAttribute( 'disabled' );
-
- // Resolve immediately for publish actions
- resolve( { postId, newStatus, eventPoints } );
- }
- } )
- .catch( reject );
- } )
- .catch( reject );
- } );
- },
-
- /**
- * Snooze a task.
- *
- * @param {number} postId The post ID.
- * @param {string} snoozeDuration The snooze duration.
- */
- snooze: ( postId, snoozeDuration ) => {
- const snoozeDurationMap = {
- '1-week': 7,
- '2-weeks': 14,
- '1-month': 30,
- '3-months': 90,
- '6-months': 180,
- '1-year': 365,
- forever: 3650,
- };
-
- const snoozeDurationDays = snoozeDurationMap[ snoozeDuration ];
- const date = new Date(
- Date.now() + snoozeDurationDays * 24 * 60 * 60 * 1000
- )
- .toISOString()
- .split( '.' )[ 0 ];
- const postModelToSave = new wp.api.models.Prpl_recommendations( {
- id: postId,
- status: 'future',
- date,
- date_gmt: date,
- } );
- postModelToSave.save().then( () => {
- prplSuggestedTask.removeTaskElement( postId );
-
- // Fetch and inject a replacement task
- prplSuggestedTask.fetchAndInjectReplacementTask();
- } );
- },
-
- /**
- * Run a tooltip action.
- *
- * @param {HTMLElement} button The button that was clicked.
- */
- runButtonAction: ( button ) => {
- let action = button.getAttribute( 'data-action' );
- const target = button.getAttribute( 'data-target' );
- const item = button.closest( 'li.prpl-suggested-task' );
- const tooltipActions = item.querySelector( '.tooltip-actions' );
- const elClass = '.prpl-suggested-task-' + target;
-
- // If the tooltip was already open, close it.
- if (
- !! tooltipActions.querySelector(
- `${ elClass }[data-tooltip-visible]`
- )
- ) {
- action = 'close-' + target;
- } else {
- const closestTaskListVisible = item
- .closest( '.prpl-suggested-tasks-list' )
- .querySelector( `[data-tooltip-visible]` );
- // Close the any opened radio group.
- closestTaskListVisible?.classList.remove(
- 'prpl-toggle-radio-group-open'
- );
- // Remove any existing tooltip visible attribute, in the entire list.
- closestTaskListVisible?.removeAttribute( 'data-tooltip-visible' );
- }
-
- switch ( action ) {
- case 'move-up':
- case 'move-down':
- if ( 'move-up' === action && item.previousElementSibling ) {
- item.parentNode.insertBefore(
- item,
- item.previousElementSibling
- );
- } else if (
- 'move-down' === action &&
- item.nextElementSibling
- ) {
- item.parentNode.insertBefore(
- item.nextElementSibling,
- item
- );
- }
- // Trigger a custom event.
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/move', {
- detail: { node: item },
- } )
- );
- break;
- }
- },
-
- /**
- * Update the task title.
- *
- * @param {HTMLElement} el The element that was edited.
- */
- updateTaskTitle: ( el ) => {
- // Add debounce to the input event.
- clearTimeout( this.debounceTimeout );
- this.debounceTimeout = setTimeout( () => {
- // Update an existing post.
- const title = el.textContent.replace( /\n/g, '' );
- const postModel = new wp.api.models.Prpl_recommendations( {
- id: parseInt( el.getAttribute( 'data-post-id' ) ),
- title,
- } );
- postModel.save().then( () =>
- // Update the task title.
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/update', {
- detail: {
- node: el.closest( 'li.prpl-suggested-task' ),
- },
- } )
- )
- );
- el
- .closest( 'li.prpl-suggested-task' )
- .querySelector(
- 'label:has(.prpl-suggested-task-checkbox) .screen-reader-text'
- ).innerHTML = `${ title }: ${ prplL10n( 'markAsComplete' ) }`;
- }, 300 );
- },
-
- /**
- * Prevent Enter key in contenteditable elements.
- *
- * @param {Event} event The keydown event.
- */
- preventEnterKey: ( event ) => {
- if ( event.key === 'Enter' ) {
- event.preventDefault();
- event.stopPropagation();
- event.target.blur();
- return false;
- }
- },
-
- /**
- * Get the task element.
- *
- * @param {number} postId The post ID.
- * @return {HTMLElement} The task element.
- */
- getTaskElement: ( postId ) =>
- document.querySelector(
- `.prpl-suggested-task[data-post-id="${ postId }"]`
- ),
-
- /**
- * Remove the task element.
- *
- * @param {number} postId The post ID.
- */
- removeTaskElement: ( postId ) =>
- prplSuggestedTask.getTaskElement( postId )?.remove(),
-
- /**
- * Fetch and inject a replacement task after one is removed.
- *
- * Replacement tasks are always fetched for the suggested-tasks-list,
- * which excludes user tasks (user tasks have their own todo list).
- */
- fetchAndInjectReplacementTask: () => {
- // Collect all currently visible task IDs from the DOM
- const visibleTaskIds = Array.from(
- document.querySelectorAll( '.prpl-suggested-task[data-post-id]' )
- ).map( ( el ) => parseInt( el.getAttribute( 'data-post-id' ) ) );
-
- // Combine with injectedItemIds to ensure we have a complete exclusion list
- const allTaskIds = [
- ...new Set( [
- ...prplSuggestedTask.injectedItemIds,
- ...visibleTaskIds,
- ] ),
- ];
-
- // Update injectedItemIds to include any tasks that might have been missed
- prplSuggestedTask.injectedItemIds = allTaskIds;
-
- const fetchArgs = {
- status: 'publish',
- per_page: 1,
- exclude_provider: 'user', // Always exclude user tasks from suggested-tasks-list
- };
-
- prplSuggestedTask.fetchItems( fetchArgs ).then( ( items ) => {
- if ( items && items.length > 0 ) {
- prplSuggestedTask.injectItems( items );
- }
- } );
- },
-};
-
-/**
- * Inject an item.
- */
-document.addEventListener( 'prpl/suggestedTask/injectItem', ( event ) => {
- prplSuggestedTask
- .getNewItemTemplatePromise( {
- post: event.detail.item,
- listId: event.detail.listId,
- } )
- .then( ( itemHTML ) => {
- /**
- * @todo Implement the parent task functionality.
- * Use this code: `const parent = event.detail.item.parent && '' !== event.detail.item.parent ? event.detail.item.parent : null;
- */
- const parent = false;
-
- if ( ! parent ) {
- // Inject the item into the list.
- document
- .getElementById( event.detail.listId )
- .insertAdjacentHTML(
- event.detail.insertPosition,
- itemHTML
- );
-
- // Trigger the grid resize event.
- window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) );
-
- return;
- }
-
- // If we could not find the parent item, try again after 500ms.
- window.prplRenderAttempts = window.prplRenderAttempts || 0;
- if ( window.prplRenderAttempts > 20 ) {
- return;
- }
- const parentItem = document.querySelector(
- `.prpl-suggested-task[data-task-id="${ parent }"]`
- );
- if ( ! parentItem ) {
- setTimeout( () => {
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/injectItem', {
- detail: {
- item: event.detail.item,
- listId: event.detail.listId,
- insertPosition: event.detail.insertPosition,
- },
- } )
- );
- window.prplRenderAttempts++;
- }, 100 );
- return;
- }
-
- // If the child list does not exist, create it.
- if (
- ! parentItem.querySelector( '.prpl-suggested-task-children' )
- ) {
- const childListElement = document.createElement( 'ul' );
- childListElement.classList.add(
- 'prpl-suggested-task-children'
- );
- parentItem.appendChild( childListElement );
- }
-
- // Inject the item into the child list.
- parentItem
- .querySelector( '.prpl-suggested-task-children' )
- .insertAdjacentHTML( 'beforeend', itemHTML );
- } );
-} );
-
-// When the 'prpl/suggestedTask/move' event is triggered,
-// update the menu_order of the todo items.
-document.addEventListener( 'prpl/suggestedTask/move', ( event ) => {
- const listUl = event.detail.node.closest( 'ul' );
- const todoItemsIDs = [];
- // Get all the todo items.
- const todoItems = listUl.querySelectorAll( '.prpl-suggested-task' );
- let menuOrder = 0;
- todoItems.forEach( ( todoItem ) => {
- const itemID = parseInt( todoItem.getAttribute( 'data-post-id' ) );
- todoItemsIDs.push( itemID );
- todoItem.setAttribute( 'data-task-order', menuOrder );
-
- listUl
- .querySelector( `.prpl-suggested-task[data-post-id="${ itemID }"]` )
- .setAttribute( 'data-task-order', menuOrder );
-
- // Update an existing post.
- const post = new wp.api.models.Prpl_recommendations( {
- id: itemID,
- menu_order: menuOrder,
- } );
- post.save();
- menuOrder++;
- } );
-} );
-
-/* eslint-enable camelcase, jsdoc/require-param-type, jsdoc/require-param, jsdoc/check-param-names */
diff --git a/assets/js/widgets/suggested-tasks.js b/assets/js/widgets/suggested-tasks.js
deleted file mode 100644
index 102529bdea..0000000000
--- a/assets/js/widgets/suggested-tasks.js
+++ /dev/null
@@ -1,258 +0,0 @@
-/* global prplSuggestedTask, prplTerms, prplTodoWidget, prplL10nStrings, history, prplDocumentReady */
-/*
- * Widget: Suggested Tasks
- *
- * A widget that displays a list of suggested tasks.
- *
- * Dependencies: wp-api, progress-planner/document-ready, progress-planner/suggested-task, progress-planner/widgets/todo, progress-planner/celebrate, progress-planner/web-components/prpl-tooltip, progress-planner/suggested-task-terms
- */
-/* eslint-disable camelcase */
-
-const prplSuggestedTasksWidget = {
- /**
- * Remove the "Loading..." text and resize the grid items.
- */
- removeLoadingItems: () => {
- document.querySelector( '.prpl-suggested-tasks-loading' )?.remove();
- setTimeout(
- () => window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) ),
- 2000
- );
- },
-
- /**
- * Populate the suggested tasks list.
- */
- populateList: () => {
- // Do nothing if the list does not exist.
- if ( ! document.querySelector( '.prpl-suggested-tasks-list' ) ) {
- return;
- }
-
- // If preloaded tasks are available, inject them.
- if ( 'undefined' !== typeof prplSuggestedTask.tasks ) {
- // Inject the pending tasks.
- if (
- Array.isArray( prplSuggestedTask.tasks.pendingTasks ) &&
- prplSuggestedTask.tasks.pendingTasks.length
- ) {
- prplSuggestedTask.injectItems(
- prplSuggestedTask.tasks.pendingTasks
- );
- }
-
- // Inject the pending celebration tasks, but only on Progress Planner dashboard page.
- if (
- ! prplSuggestedTask.delayCelebration &&
- Array.isArray(
- prplSuggestedTask.tasks.pendingCelebrationTasks
- ) &&
- prplSuggestedTask.tasks.pendingCelebrationTasks.length
- ) {
- prplSuggestedTask.injectItems(
- prplSuggestedTask.tasks.pendingCelebrationTasks
- );
-
- // Set post status to trash.
- prplSuggestedTask.tasks.pendingCelebrationTasks.forEach(
- ( task ) => {
- const post = new wp.api.models.Prpl_recommendations( {
- id: task.id,
- } );
- // Destroy the post, without the force parameter.
- post.destroy( { url: post.url() } );
- }
- );
-
- // Trigger the celebration event (trigger confetti, strike through tasks, remove from DOM).
- setTimeout( () => {
- // Trigger the celebration event.
- document.dispatchEvent(
- new CustomEvent( 'prpl/celebrateTasks' )
- );
-
- /**
- * Strike completed tasks and remove them from the DOM.
- */
- document.dispatchEvent(
- new CustomEvent( 'prpl/removeCelebratedTasks' )
- );
-
- // Trigger the grid resize event.
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- );
- }, 3000 );
- }
-
- // Toggle the "Loading..." text.
- prplSuggestedTasksWidget.removeLoadingItems();
- } else {
- // Otherwise, inject tasks from the API.
- // Inject published tasks (excluding user tasks).
- const tasksPerPage =
- 'undefined' !== typeof prplSuggestedTask.tasksPerPage &&
- -1 === prplSuggestedTask.tasksPerPage
- ? 100
- : prplSuggestedTask.tasksPerPage ||
- prplSuggestedTask.perPageDefault;
-
- prplSuggestedTask
- .fetchItems( {
- status: [ 'publish' ],
- per_page: tasksPerPage,
- exclude_provider: 'user',
- } )
- .then( ( data ) => {
- if ( data.length ) {
- prplSuggestedTask.injectItems( data );
- }
- } );
-
- // We trigger celebration only on Progress Planner dashboard page.
- if ( ! prplSuggestedTask.delayCelebration ) {
- // Inject pending celebration tasks.
- prplSuggestedTask
- .fetchItems( {
- status: [ 'pending' ],
- per_page: tasksPerPage,
- exclude_provider: 'user',
- } )
- .then( ( data ) => {
- // If there were pending tasks.
- if ( data.length ) {
- prplSuggestedTask.injectItems( data );
-
- // Set post status to trash.
- data.forEach( ( task ) => {
- const post =
- new wp.api.models.Prpl_recommendations( {
- id: task.id,
- } );
- // Destroy the post, without the force parameter.
- post.destroy( { url: post.url() } );
- } );
-
- // Trigger the celebration event (trigger confetti, strike through tasks, remove from DOM).
- setTimeout( () => {
- // Trigger the celebration event.
- document.dispatchEvent(
- new CustomEvent( 'prpl/celebrateTasks' )
- );
-
- /**
- * Strike completed tasks and remove them from the DOM.
- */
- document.dispatchEvent(
- new CustomEvent(
- 'prpl/removeCelebratedTasks'
- )
- );
-
- // Trigger the grid resize event.
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- );
- }, 3000 );
- }
- } );
- }
- }
- },
-};
-
-/**
- * Populate the suggested tasks list when the terms are loaded.
- */
-prplTerms.getCollectionsPromises().then( () => {
- prplSuggestedTasksWidget.populateList();
- prplTodoWidget.populateList();
-} );
-
-/**
- * Handle the "Show all recommendations" / "Show fewer recommendations" toggle.
- */
-prplDocumentReady( () => {
- const toggleButton = document.getElementById(
- 'prpl-toggle-all-recommendations'
- );
- if ( ! toggleButton ) {
- return;
- }
-
- toggleButton.addEventListener( 'click', () => {
- const showAll = toggleButton.dataset.showAll === '1';
- const newPerPage = showAll ? prplSuggestedTask.perPageDefault : 100;
-
- // Update button text and state.
- toggleButton.textContent = showAll
- ? prplL10nStrings.showAllRecommendations
- : prplL10nStrings.showFewerRecommendations;
- toggleButton.dataset.showAll = showAll ? '0' : '1';
- toggleButton.disabled = true;
-
- // Clear existing tasks.
- const tasksList = document.getElementById(
- 'prpl-suggested-tasks-list'
- );
- tasksList.innerHTML = '';
-
- // Clear the injected items tracking array so tasks can be fetched again.
- prplSuggestedTask.injectedItemIds = [];
-
- // Show loading message.
- const loadingMessage = document.createElement( 'p' );
- loadingMessage.className = 'prpl-suggested-tasks-loading';
- loadingMessage.textContent = prplL10nStrings.loadingTasks;
- tasksList.parentNode.insertBefore(
- loadingMessage,
- tasksList.nextSibling
- );
-
- // Fetch and inject new tasks.
- prplSuggestedTask
- .fetchItems( {
- status: [ 'publish' ],
- per_page: newPerPage,
- exclude_provider: 'user',
- } )
- .then( ( data ) => {
- if ( data.length ) {
- prplSuggestedTask.injectItems( data );
- }
-
- // Remove loading message.
- loadingMessage?.remove();
-
- // Re-enable button.
- toggleButton.disabled = false;
-
- // Trigger grid resize.
- setTimeout( () => {
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- );
- }, 100 );
- } )
- .catch( () => {
- // On error, restore button state.
- toggleButton.textContent = showAll
- ? prplL10nStrings.showFewerRecommendations
- : prplL10nStrings.showAllRecommendations;
- toggleButton.dataset.showAll = showAll ? '1' : '0';
- toggleButton.disabled = false;
- loadingMessage?.remove();
- } );
-
- // Update URL without reload.
- const url = new URL( window.location );
- if ( showAll ) {
- url.searchParams.delete( 'prpl_show_all_recommendations' );
- } else {
- url.searchParams.set( 'prpl_show_all_recommendations', '' );
- }
- history.pushState( {}, '', url );
- } );
-} );
-
-/* eslint-enable camelcase */
diff --git a/assets/js/widgets/todo.js b/assets/js/widgets/todo.js
deleted file mode 100644
index 64d55a433c..0000000000
--- a/assets/js/widgets/todo.js
+++ /dev/null
@@ -1,336 +0,0 @@
-/* global prplSuggestedTask, prplTerms, prplL10n */
-/*
- * Widget: Todo
- *
- * A widget that displays a todo list.
- *
- * Dependencies: wp-api, progress-planner/suggested-task, wp-util, wp-a11y, progress-planner/celebrate, progress-planner/suggested-task-terms, progress-planner/l10n
- */
-
-const prplTodoWidget = {
- /**
- * Get the highest `order` value from the todo items.
- *
- * @return {number} The highest `order` value.
- */
- getHighestItemOrder: () => {
- const items = document.querySelectorAll(
- '#todo-list .prpl-suggested-task'
- );
- let highestOrder = 0;
- items.forEach( ( item ) => {
- highestOrder = Math.max(
- parseInt( item.getAttribute( 'data-task-order' ) ),
- highestOrder
- );
- } );
- return highestOrder;
- },
-
- /**
- * Remove the "Loading..." text and resize the grid items.
- */
- removeLoadingItems: () => {
- // Remove the "Loading..." text.
- document.querySelector( '#prpl-todo-list-loading' )?.remove();
-
- // Resize the grid items.
- window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) );
- },
-
- /**
- * Populate the todo list.
- */
- populateList: () => {
- // If preloaded tasks are available, inject them.
- if ( 'undefined' !== typeof prplSuggestedTask.tasks ) {
- // Inject the tasks.
- if (
- Array.isArray( prplSuggestedTask.tasks.userTasks ) &&
- prplSuggestedTask.tasks.userTasks.length
- ) {
- prplSuggestedTask.tasks.userTasks.forEach( ( item ) => {
- // Inject the items into the DOM.
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/injectItem', {
- detail: {
- item,
- insertPosition:
- 1 === item?.prpl_points
- ? 'afterbegin' // Add golden task to the start of the list.
- : 'beforeend',
- listId:
- item.status === 'publish'
- ? 'todo-list'
- : 'todo-list-completed',
- },
- } )
- );
- prplSuggestedTask.injectedItemIds.push( item.id );
- } );
- }
- prplTodoWidget.removeLoadingItems();
- } else {
- // Otherwise, inject tasks from the API.
- prplSuggestedTask
- .fetchItems( {
- provider: 'user',
- status: [ 'publish', 'trash' ],
- per_page: 100,
- } )
- .then( ( data ) => {
- if ( ! data.length ) {
- return data;
- }
-
- // Inject the items into the DOM.
- data.forEach( ( item ) => {
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/injectItem', {
- detail: {
- item,
- insertPosition:
- 1 === item?.prpl_points
- ? 'afterbegin' // Add golden task to the start of the list.
- : 'beforeend',
- listId:
- item.status === 'publish'
- ? 'todo-list'
- : 'todo-list-completed',
- },
- } )
- );
- prplSuggestedTask.injectedItemIds.push( item.id );
- } );
-
- return data;
- } )
- .then( () => prplTodoWidget.removeLoadingItems() );
- }
-
- // When the '#create-todo-item' form is submitted,
- // add a new todo item to the list
- document
- .getElementById( 'create-todo-item' )
- ?.addEventListener( 'submit', ( event ) => {
- event.preventDefault();
-
- // Add the loader.
- prplTodoWidget.addLoader();
-
- // Create a new post
- const post = new wp.api.models.Prpl_recommendations( {
- // Set the post title.
- title: document.getElementById( 'new-todo-content' ).value,
- status: 'publish',
- // Set the `prpl_recommendations_provider` term.
- prpl_recommendations_provider:
- prplTerms.get( 'provider' ).user.id,
- menu_order: prplTodoWidget.getHighestItemOrder() + 1,
- } );
- post.save().then( ( response ) => {
- if ( ! response.id ) {
- return;
- }
- const newTask = {
- ...response,
- meta: {
- prpl_url: '',
- ...( response.meta || {} ),
- },
- provider: 'user',
- order: prplTodoWidget.getHighestItemOrder() + 1,
- prpl_points: 0,
- };
-
- // Inject the new task into the DOM.
- document.dispatchEvent(
- new CustomEvent( 'prpl/suggestedTask/injectItem', {
- detail: {
- item: newTask,
- insertPosition:
- 1 === newTask.points
- ? 'afterbegin'
- : 'beforeend', // Add golden task to the start of the list.
- listId: 'todo-list',
- },
- } )
- );
-
- // Remove the loader.
- prplTodoWidget.removeLoader();
-
- // Announce to screen readers.
- prplTodoWidget.announceToScreenReader(
- prplL10n( 'taskAddedSuccessfully' )
- );
-
- // Resize the grid items.
- window.dispatchEvent(
- new CustomEvent( 'prpl/grid/resize' )
- );
- } );
-
- // Clear the new task input element.
- document.getElementById( 'new-todo-content' ).value = '';
-
- // Focus the new task input element.
- document.getElementById( 'new-todo-content' ).focus();
- } );
- },
-
- /**
- * Announce to screen readers.
- *
- * @param {string} message The message to announce.
- * @param {string} priority The priority ('polite' or 'assertive').
- */
- announceToScreenReader: ( message, priority = 'polite' ) => {
- // Use WordPress a11y speak if available.
- if ( 'undefined' !== typeof wp && wp.a11y && wp.a11y.speak ) {
- wp.a11y.speak( message, priority );
- } else {
- // Fallback to ARIA live region.
- const liveRegion = document.getElementById(
- 'todo-aria-live-region'
- );
- if ( liveRegion ) {
- liveRegion.textContent = message;
- setTimeout( () => {
- liveRegion.textContent = '';
- }, 1000 );
- }
- }
- },
-
- /**
- * Add the loader.
- */
- addLoader: () => {
- const loader = document.createElement( 'span' );
- const loadingTasksText = prplL10n( 'loadingTasks' );
- loader.className = 'prpl-loader';
- loader.setAttribute( 'role', 'status' );
- loader.setAttribute( 'aria-live', 'polite' );
- loader.innerHTML = `${ loadingTasksText }`;
- document.getElementById( 'todo-list' ).appendChild( loader );
- },
-
- /**
- * Remove the loader.
- */
- removeLoader: () => {
- document.querySelector( '#todo-list .prpl-loader' )?.remove();
- },
-
- /**
- * Show the delete all popover.
- */
- showDeleteAllPopover: () => {
- document
- .getElementById( 'todo-list-completed-delete-all-popover' )
- .showPopover();
- },
-
- /**
- * Close the delete all popover.
- */
- closeDeleteAllPopover: () => {
- document
- .getElementById( 'todo-list-completed-delete-all-popover' )
- .hidePopover();
- },
-
- /**
- * Delete all completed tasks and close the popover.
- */
- deleteAllCompletedTasksAndClosePopover: () => {
- prplTodoWidget.deleteAllCompletedTasks();
- prplTodoWidget.closeDeleteAllPopover();
- },
-
- /**
- * Delete all completed tasks.
- */
- deleteAllCompletedTasks: () => {
- const items = document.querySelectorAll(
- '#todo-list-completed .prpl-suggested-task'
- );
- const itemCount = items.length;
-
- items.forEach( ( item ) => {
- const postId = parseInt( item.getAttribute( 'data-post-id' ) );
- prplSuggestedTask.trash( postId );
- } );
-
- // Announce to screen readers.
- const tasksWord =
- itemCount === 1
- ? prplL10n( 'taskDeleted' )
- : prplL10n( 'tasksDeleted' );
- prplTodoWidget.announceToScreenReader(
- `${ itemCount } ${ tasksWord }`,
- 'assertive'
- );
-
- // Resize event will be triggered by the trash function.
- },
-};
-
-document
- .getElementById( 'todo-list-completed-details' )
- ?.addEventListener( 'toggle', () => {
- window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) );
- } );
-
-// Add event listener for delete all button.
-document
- .getElementById( 'todo-list-completed-delete-all' )
- ?.addEventListener( 'click', () => {
- prplTodoWidget.showDeleteAllPopover();
- } );
-
-// Add event listener for cancel button in delete all popover.
-document
- .getElementById( 'todo-list-completed-delete-all-cancel' )
- ?.addEventListener( 'click', () => {
- prplTodoWidget.closeDeleteAllPopover();
- } );
-
-// Add event listener for confirm button in delete all popover.
-document
- .getElementById( 'todo-list-completed-delete-all-confirm' )
- ?.addEventListener( 'click', () => {
- prplTodoWidget.deleteAllCompletedTasksAndClosePopover();
- } );
-
-document.addEventListener( 'prpl/suggestedTask/itemInjected', ( event ) => {
- if ( 'todo-list' !== event.detail.listId ) {
- return;
- }
- setTimeout( () => {
- // Get all items in the list.
- const items = document.querySelectorAll(
- `#${ event.detail.listId } .prpl-suggested-task`
- );
-
- // Reorder items based on their `data-task-order` attribute.
- const orderedItems = Array.from( items ).sort( ( a, b ) => {
- return (
- parseInt( a.getAttribute( 'data-task-order' ) ) -
- parseInt( b.getAttribute( 'data-task-order' ) )
- );
- } );
-
- // Remove all items from the list.
- items.forEach( ( item ) => item.remove() );
-
- // Inject the ordered items back into the list.
- orderedItems.forEach( ( item ) =>
- document.getElementById( event.detail.listId ).appendChild( item )
- );
-
- // Resize the grid items.
- window.dispatchEvent( new CustomEvent( 'prpl/grid/resize' ) );
- } );
-} );
diff --git a/assets/src/hooks/useGridMasonry.js b/assets/src/hooks/useGridMasonry.js
new file mode 100644
index 0000000000..0beba79ce2
--- /dev/null
+++ b/assets/src/hooks/useGridMasonry.js
@@ -0,0 +1,96 @@
+/**
+ * useGridMasonry Hook
+ *
+ * Handles CSS grid masonry layout for dashboard widgets.
+ * Listens for 'prpl/grid/resize' events and calculates grid-row-end spans.
+ */
+
+import { useEffect } from '@wordpress/element';
+
+/**
+ * Hook to handle grid masonry layout for widgets.
+ *
+ * This hook sets up event listeners for:
+ * - 'prpl/grid/resize' custom event (dispatched by widgets when content changes)
+ * - 'resize' window event (for responsive layout)
+ * - 'load' window event (for initial layout after all resources load)
+ */
+export function useGridMasonry() {
+ useEffect( () => {
+ /**
+ * Handle grid resize by calculating row spans for each widget.
+ */
+ const handleGridResize = () => {
+ document
+ .querySelectorAll( '.prpl-widget-wrapper' )
+ .forEach( ( item ) => {
+ if ( ! item || item.classList.contains( 'in-popover' ) ) {
+ return;
+ }
+
+ const innerContainer = item.querySelector(
+ '.widget-inner-container'
+ );
+ if ( ! innerContainer ) {
+ return;
+ }
+
+ const container = document.querySelector(
+ '.prpl-widgets-container'
+ );
+ if ( ! container ) {
+ return;
+ }
+
+ const rowHeight = parseInt(
+ window
+ .getComputedStyle( container )
+ .getPropertyValue( 'grid-auto-rows' )
+ );
+
+ const paddingTop = parseInt(
+ window
+ .getComputedStyle( item )
+ .getPropertyValue( 'padding-top' )
+ );
+ const paddingBottom = parseInt(
+ window
+ .getComputedStyle( item )
+ .getPropertyValue( 'padding-bottom' )
+ );
+
+ const rowSpan = Math.ceil(
+ ( innerContainer.getBoundingClientRect().height +
+ paddingTop +
+ paddingBottom ) /
+ rowHeight
+ );
+
+ item.style.gridRowEnd = 'span ' + ( rowSpan + 1 );
+ } );
+ };
+
+ /**
+ * Trigger resize with a small delay.
+ */
+ const triggerResize = () => {
+ setTimeout( handleGridResize, 0 );
+ };
+
+ // Listen for custom event from widgets.
+ window.addEventListener( 'prpl/grid/resize', handleGridResize );
+ window.addEventListener( 'resize', triggerResize );
+ window.addEventListener( 'load', triggerResize );
+
+ // Initial calls.
+ triggerResize();
+ setTimeout( triggerResize, 1000 );
+
+ // Cleanup on unmount.
+ return () => {
+ window.removeEventListener( 'prpl/grid/resize', handleGridResize );
+ window.removeEventListener( 'resize', triggerResize );
+ window.removeEventListener( 'load', triggerResize );
+ };
+ }, [] );
+}
diff --git a/assets/src/widgets/SuggestedTasks/index.js b/assets/src/widgets/SuggestedTasks/index.js
index db6dfb9e96..0350de2f1b 100644
--- a/assets/src/widgets/SuggestedTasks/index.js
+++ b/assets/src/widgets/SuggestedTasks/index.js
@@ -16,6 +16,7 @@ import {
updateTask,
sendTaskAction,
} from './hooks/useTasksApi';
+import { useGridMasonry } from '../../hooks/useGridMasonry';
/**
* Suggested Tasks widget component.
@@ -32,6 +33,9 @@ export default function SuggestedTasks() {
const listRef = useRef( null );
const injectedTaskIdsRef = useRef( new Set() );
+ // Initialize grid masonry layout.
+ useGridMasonry();
+
/**
* Load tasks on component mount.
*/
diff --git a/assets/src/widgets/TodoWidget/index.js b/assets/src/widgets/TodoWidget/index.js
index 0f41e9902e..688279baa5 100644
--- a/assets/src/widgets/TodoWidget/index.js
+++ b/assets/src/widgets/TodoWidget/index.js
@@ -7,6 +7,7 @@
import { useState, useEffect, useRef, useCallback } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
+import { useGridMasonry } from '../../hooks/useGridMasonry';
/**
* Fetch user tasks from API.
@@ -257,6 +258,9 @@ export default function TodoWidget() {
const [ showDeletePopover, setShowDeletePopover ] = useState( false );
const inputRef = useRef( null );
+ // Initialize grid masonry layout.
+ useGridMasonry();
+
/**
* Load tasks on mount.
*/
diff --git a/build/suggested-tasks.asset.php b/build/suggested-tasks.asset.php
index 24cf00715a..658ef26c03 100644
--- a/build/suggested-tasks.asset.php
+++ b/build/suggested-tasks.asset.php
@@ -1 +1 @@
- array('react-jsx-runtime', 'wp-api-fetch', 'wp-element', 'wp-i18n'), 'version' => 'ba74c51066ae5072eacb');
+ array('react-jsx-runtime', 'wp-api-fetch', 'wp-element', 'wp-i18n'), 'version' => 'b0b7c1f5b951f955ff02');
diff --git a/build/suggested-tasks.js b/build/suggested-tasks.js
index 16865d1e64..cd2096408a 100644
--- a/build/suggested-tasks.js
+++ b/build/suggested-tasks.js
@@ -1 +1 @@
-(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var n in s)e.o(s,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:s[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.element,s=window.wp.i18n,n=window.ReactJSXRuntime;function a({task:e,isUserTask:a,onComplete:r,onSnooze:o,onDelete:i}){const l=(0,t.useRef)(null);(0,t.useEffect)(()=>{if(!l.current)return;const t=l.current,s=t.querySelectorAll('[data-action="complete"]');s.forEach(t=>{t.addEventListener("click",t=>{t.preventDefault(),r(e.id,e)})});const n=t.querySelectorAll('.prpl-snooze-duration-radio-group input[type="radio"]');return n.forEach(t=>{t.addEventListener("change",()=>{o(e.id,t.value)})}),t.querySelectorAll('a[onclick*="showPopover"]').forEach(e=>{const t=e.getAttribute("onclick"),s=t?.match(/getElementById\(['"]([^'"]+)['"]\)/);if(s){const t=s[1];e.removeAttribute("onclick"),e.addEventListener("click",e=>{e.preventDefault();const s=document.getElementById(t);s&&"function"==typeof s.showPopover&&s.showPopover()})}}),t.querySelectorAll(".prpl-suggested-task-button.trash").forEach(t=>{t.addEventListener("click",t=>{t.preventDefault(),i(e.id)})}),()=>{s.forEach(e=>{e.replaceWith(e.cloneNode(!0))}),n.forEach(e=>{e.replaceWith(e.cloneNode(!0))})}},[e,r,o,i]);const c=e.prpl_task_actions||[];return 0!==c.length||a?(0,n.jsxs)("div",{className:"tooltip-actions",ref:l,children:[c.map((e,t)=>(0,n.jsx)("span",{className:"tooltip-action",dangerouslySetInnerHTML:{__html:e}},t)),a&&(0,n.jsx)("span",{className:"tooltip-action",children:(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button trash","data-post-id":e.id,title:(0,s.__)("Delete","progress-planner"),onClick:()=>i(e.id),children:[(0,n.jsx)("span",{className:"prpl-tooltip-action-text",children:(0,s.__)("Delete","progress-planner")}),(0,n.jsxs)("span",{className:"screen-reader-text",children:[(0,s.__)("Delete","progress-planner"),":"," ",e.title?.rendered||e.title]})]})})]}):(0,n.jsx)("div",{className:"tooltip-actions"})}function r(){return(0,n.jsx)("span",{style:{width:"0.75rem",height:"100%",display:"flex",alignItems:"center",justifyContent:"center"},children:(0,n.jsx)("svg",{role:"img","aria-hidden":"true",focusable:"false",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 17",children:(0,n.jsx)("path",{fill:"#6b7280",d:"M19.92 8.12c-.05-.12-.12-.23-.22-.33L12.21.29A.996.996 0 1 0 10.8 1.7l5.79 5.79H1c-.55 0-1 .45-1 1s.45 1 1 1h15.59l-5.79 5.79a.996.996 0 0 0 .71 1.7c.26 0 .51-.1.71-.29l7.5-7.5c.1-.1.17-.21.22-.33.05-.12.07-.24.08-.38 0-.14-.03-.27-.08-.38Z"})})})}function o(){return(0,n.jsx)("svg",{role:"img","aria-hidden":"true",focusable:"false",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",children:(0,n.jsx)("path",{fill:"#9ca3af",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Zm-17.98-3h17.97c1.9 0 3.51-1.48 3.65-3.38l2.34-30.46c-2.15-.3-4.33-.53-6.48-.7h-.03c-5.62-.43-11.32-.43-16.95 0h-.03c-2.15.17-4.33.4-6.48.7l2.34 30.46c.15 1.9 1.75 3.38 3.65 3.38ZM24 7.01c2.37 0 4.74.07 7.11.22v-.49c0-1.93-1.47-3.49-3.34-3.55-2.5-.08-5.03-.08-7.52 0-1.88.06-3.34 1.62-3.34 3.55v.49c2.36-.15 4.73-.22 7.11-.22Zm5.49 32.26h-.06c-.83-.03-1.47-.73-1.44-1.56l.79-20.65c.03-.83.75-1.45 1.56-1.44.83.03 1.47.73 1.44 1.56l-.79 20.65c-.03.81-.7 1.44-1.5 1.44Zm-10.98 0c-.8 0-1.47-.63-1.5-1.44l-.79-20.65c-.03-.83.61-1.52 1.44-1.56.84 0 1.52.61 1.56 1.44l.79 20.65c.03.83-.61 1.52-1.44 1.56h-.06Z"})})}function i({task:e,isUserTask:i,isCelebrating:l,onComplete:c,onSnooze:p,onDelete:d,onMove:u,onTitleChange:g}){const m=(0,t.useRef)(null),h=(0,t.useRef)(null),w="trash"===e.status||"pending"===e.status,y=(0,t.useCallback)(()=>{c(e.id,e)},[e,c]),f=(0,t.useCallback)(e=>{if("Enter"===e.key)return e.preventDefault(),e.stopPropagation(),e.target.blur(),!1},[]),v=(0,t.useCallback)(()=>{clearTimeout(h.current),h.current=setTimeout(()=>{if(m.current){const t=m.current.textContent.replace(/\n/g,"");g(e.id,t)}},300)},[e.id,g]),b=(0,t.useCallback)(()=>{u(e.id,"up")},[e.id,u]),_=(0,t.useCallback)(()=>{u(e.id,"down")},[e.id,u]),k=(0,t.useCallback)(()=>{d(e.id)},[e.id,d]),S=e.slug||e.id,x=e.prpl_provider?.slug||"",C=["prpl-suggested-task",l?"prpl-suggested-task-celebrated":""].filter(Boolean).join(" ");return(0,n.jsxs)("li",{className:C,"data-task-id":S,"data-post-id":e.id,"data-task-action":"pending"===e.status||l?"celebrate":"","data-task-provider-id":x,"data-task-points":e.prpl_points||0,"data-task-order":e.menu_order||0,children:[(0,n.jsx)("div",{className:"prpl-suggested-task-checkbox-wrapper",children:i?(0,n.jsxs)("label",{children:[(0,n.jsx)("input",{type:"checkbox",className:"prpl-suggested-task-checkbox",onChange:y,style:{margin:0},checked:w,disabled:l}),(0,n.jsxs)("span",{className:"screen-reader-text",children:[e.title?.rendered||e.title,":"," ",(0,s.__)("Mark as complete","progress-planner")]})]}):(0,n.jsx)(r,{})}),(0,n.jsx)("div",{className:"prpl-suggested-task-title-wrapper",children:(0,n.jsx)("h3",{className:"prpl-task-title",children:i?(0,n.jsx)("span",{ref:m,contentEditable:"plaintext-only",role:"textbox",tabIndex:0,"aria-label":(0,s.__)("Edit task title","progress-planner"),"aria-multiline":"false",onKeyDown:f,onInput:v,suppressContentEditableWarning:!0,dangerouslySetInnerHTML:{__html:e.title?.rendered||e.title}}):(0,n.jsx)("span",{dangerouslySetInnerHTML:{__html:e.title?.rendered||e.title}})})}),(0,n.jsxs)("div",{className:"prpl-suggested-task-points-wrapper",children:[e.prpl_points>0&&(0,n.jsxs)("span",{className:"prpl-suggested-task-points",children:["+",e.prpl_points]}),i&&(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button trash","data-post-id":e.id,title:(0,s.__)("Delete","progress-planner"),onClick:k,children:[(0,n.jsx)(o,{}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Delete","progress-planner")})]})]}),i&&(0,n.jsx)("div",{className:"tooltip-actions prpl-move-buttons-wrapper",children:(0,n.jsxs)("span",{className:"prpl-move-buttons",children:[(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button move-up","data-task-id":S,"data-task-title":e.title?.rendered||e.title,"data-action":"move-up","data-target":"move-up",title:(0,s.__)("Move up","progress-planner"),onClick:b,children:[(0,n.jsx)("span",{className:"dashicons dashicons-arrow-up-alt2"}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Move up","progress-planner")})]}),(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button move-down","data-task-id":S,"data-task-title":e.title?.rendered||e.title,"data-action":"move-down","data-target":"move-down",title:(0,s.__)("Move down","progress-planner"),onClick:_,children:[(0,n.jsx)("span",{className:"dashicons dashicons-arrow-down-alt2"}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Move down","progress-planner")})]})]})}),(0,n.jsx)("div",{className:"prpl-suggested-task-actions-wrapper",children:(0,n.jsx)(a,{task:e,isUserTask:i,onComplete:c,onSnooze:p,onDelete:d})})]})}const l=window.wp.apiFetch;var c=e.n(l);function p(e){let t=e.querySelector('button[type="submit"]');if(t||(t=e.querySelector('button[data-action="completeTask"]')),t){t.disabled=!0;const e=document.createElement("span");e.classList.add("prpl-spinner"),e.innerHTML='',t.after(e)}}function d(e){let t=e.querySelector('button[type="submit"]');t||(t=e.querySelector('button[data-action="completeTask"]')),t&&(t.disabled=!1);const s=e.querySelector("span.prpl-spinner");s&&s.remove()}function u(e){const t=document.querySelector(`#${e} form`);if(t&&!t.parentNode.querySelector("p.prpl-interactive-task-error-message")){const e=document.createElement("p");e.classList.add("prpl-note","prpl-note-error","prpl-interactive-task-error-message"),e.textContent=(0,s.__)("Something went wrong. Please try again.","progress-planner"),t.insertAdjacentElement("afterend",e)}}function g(e){const t=document.querySelector(`#${e} form`);if(!t)return;const s=t.parentNode.querySelector("p.prpl-interactive-task-error-message");s&&s.remove()}async function m({setting:e,settingPath:t=!1,popoverId:s,action:n="prpl_interactive_task_submit",settingCallbackValue:a=e=>e}){const r=document.querySelector(`#${s} form`);if(!r)throw new Error("Form not found");p(r),g(s);try{const o=a(new FormData(r).get(e)),i=window.prplSuggestedTasksConfig?.ajaxUrl||window.progressPlanner?.ajaxUrl||"/wp-admin/admin-ajax.php",l=window.prplSuggestedTasksConfig?.nonce||window.progressPlanner?.nonce||"",c=new URLSearchParams({action:n,_ajax_nonce:l,setting:e,value:o});t&&c.append("setting_path",t);const p=await fetch(i,{method:"POST",body:c,credentials:"same-origin"}),g=await p.json();if(d(r),!0!==g.success)throw u(s),new Error("Settings update failed");return g}catch(e){throw d(r),u(s),e}}async function h(e,t="posts"){return c()({path:`/wp/v2/${t}/${e}?force=true`,method:"DELETE"})}const w={"core-blogdescription":{type:"siteSettings",settingAPIKey:"description",setting:"blogdescription"},"disable-comments":{type:"siteSettings",settingAPIKey:"default_comment_status",setting:"default_comment_status",settingCallbackValue:()=>"closed"},"disable-comment-pagination":{type:"siteSettings",settingAPIKey:"page_comments",setting:"page_comments",settingCallbackValue:()=>!1},"select-locale":{type:"siteSettings",settingAPIKey:"WPLANG",setting:"WPLANG"},"select-timezone":{type:"siteSettings",settingAPIKey:"timezone_string",setting:"timezone_string"},"search-engine-visibility":{type:"pluginSettings",setting:"blog_public",action:"prpl_interactive_task_submit",settingCallbackValue:()=>"1"},"set-date-format":{type:"siteSettings",settingAPIKey:"date_format",setting:"date_format"},"core-permalink-structure":{type:"siteSettings",settingAPIKey:"permalink_structure",setting:"permalink_structure"},"rename-uncategorized-category":{type:"customSubmit"},"hello-world":{type:"customSubmit"},"sample-page":{type:"customSubmit"},"yoast-author-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-author"]),settingCallbackValue:()=>!0},"yoast-date-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-date"]),settingCallbackValue:()=>!0},"yoast-format-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-post_format"]),settingCallbackValue:()=>!0},"yoast-media-pages":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-attachment"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-emoji-scripts":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_emoji_scripts"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-feed-authors":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_feed_authors"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-feed-global-comments":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_feed_global_comments"]),settingCallbackValue:()=>!0},"yoast-organization-logo":{type:"customSubmit"},"aioseo-author-archive":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["archives","author","show"]),settingCallbackValue:()=>!1},"aioseo-date-archive":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["archives","date","show"]),settingCallbackValue:()=>!1},"aioseo-media-pages":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["postTypes","attachment","show"]),settingCallbackValue:()=>!1},"aioseo-crawl-settings-feed-authors":{type:"pluginSettings",setting:"aioseo_options_rss_content",settingPath:JSON.stringify(["authorFeed"]),settingCallbackValue:()=>!1},"aioseo-crawl-settings-feed-comments":{type:"pluginSettings",setting:"aioseo_options_rss_content",settingPath:JSON.stringify(["commentFeed"]),settingCallbackValue:()=>!1},"core-siteicon":{type:"customSubmit"},"update-term-description":{type:"customSubmit"},"remove-terms-without-posts":{type:"customSubmit"}};function y({tasks:e,onComplete:s}){const n=(0,t.useCallback)(async(e,t)=>{switch(e){case"hello-world":{const e=window.helloWorldData?.postId;return e&&await h(e,"posts"),{success:!0}}case"sample-page":{const e=window.samplePageData?.postId;return e&&await h(e,"pages"),{success:!0}}case"rename-uncategorized-category":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("category_name"),s=window.renameUncategorizedCategoryData?.termId;s&&t&&await c()({path:`/wp/v2/categories/${s}`,method:"POST",data:{name:t}})}return{success:!0}}case"core-siteicon":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("site_icon");t&&await c()({path:"/wp/v2/settings",method:"POST",data:{site_icon:parseInt(t)}})}return{success:!0}}case"yoast-organization-logo":{const e=document.querySelector(`#${t} form`);if(e){const s=new FormData(e).get("company_logo_id");s&&await m({setting:"wpseo",settingPath:JSON.stringify(["company_logo_id"]),popoverId:t,settingCallbackValue:()=>parseInt(s)})}return{success:!0}}case"update-term-description":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("description"),s=window.updateTermDescriptionData?.termId,n=window.updateTermDescriptionData?.taxonomy||"category";if(s&&t){const e="category"===n?"categories":n;await c()({path:`/wp/v2/${e}/${s}`,method:"POST",data:{description:t}})}}return{success:!0}}case"remove-terms-without-posts":if(document.querySelector(`#${t} form`)){const e=window.removeTermsWithoutPostsData?.termIds||[],t=window.removeTermsWithoutPostsData?.taxonomy||"category",s="category"===t?"categories":t;await Promise.all(e.map(e=>c()({path:`/wp/v2/${s}/${e}?force=true`,method:"DELETE"})))}return{success:!0};default:return{success:!0}}},[]),a=(0,t.useCallback)(async(t,a,r)=>{try{const o=e.find(e=>e.slug===t||e.prpl_provider?.slug===t||`${e.id}`===t);if(!o)return;let i;switch(r.type){case"siteSettings":i=async function({settingAPIKey:e,setting:t,popoverId:s,settingCallbackValue:n=e=>e}){const a=document.querySelector(`#${s} form`);if(!a)throw new Error("Form not found");p(a),g(s);try{const s=n(new FormData(a).get(t)),r=await c()({path:"/wp/v2/settings",method:"POST",data:{[e]:s}});return d(a),r}catch(e){throw d(a),u(s),e}}({settingAPIKey:r.settingAPIKey,setting:r.setting,popoverId:a,settingCallbackValue:r.settingCallbackValue});break;case"pluginSettings":i=m({setting:r.setting,settingPath:r.settingPath,popoverId:a,action:r.action||"prpl_interactive_task_submit",settingCallbackValue:r.settingCallbackValue});break;case"customSubmit":i=n(t,a);break;default:return}await i,await s(o.id,o),function(e){const t=document.getElementById(e);t&&"function"==typeof t.hidePopover&&t.hidePopover()}(a)}catch(e){console.error("Popover form submission error:",e)}},[e,s,n]);return(0,t.useEffect)(()=>{const e=new Map;Object.entries(w).forEach(([t,s])=>{const n=`prpl-popover-${t}`,r=document.querySelector(`#${n} form`);if(!r)return;const o=e=>{e.preventDefault(),a(t,n,s)};r.addEventListener("submit",o),e.set(n,{formElement:r,handler:o})});const t=document.querySelector("input#blogdescription");if(t){const e=document.querySelector('#prpl-popover-core-blogdescription button[type="submit"]');if(e){const s=t=>{e.disabled=0===t.target.value.length};t.addEventListener("input",s)}}return function(){const e=document.querySelectorAll('#prpl-popover-set-date-format input[name="date_format"]'),t=document.querySelector('#prpl-popover-set-date-format input[name="date_format_custom"]');if(!e.length||!t)return;let s;e.forEach(e=>{e.addEventListener("change",()=>{"custom"===e.value?(t.disabled=!1,t.focus()):t.disabled=!0})}),t.addEventListener("input",()=>{clearTimeout(s),s=setTimeout(async()=>{const e=t.value;if(e)try{const s=window.prplSuggestedTasksConfig?.ajaxUrl||window.progressPlanner?.ajaxUrl||"/wp-admin/admin-ajax.php",n=window.prplSuggestedTasksConfig?.nonce||window.progressPlanner?.nonce||"",a=await fetch(`${s}?action=prpl_date_format_preview&format=${encodeURIComponent(e)}&_ajax_nonce=${n}`,{credentials:"same-origin"}),r=await a.json();if(r.success&&r.data){const e=t.closest(".prpl-radio-wrapper")?.querySelector(".date-time-text");e&&(e.textContent=r.data)}}catch{}},300)})}(),function(){const e=document.querySelectorAll('#prpl-popover-core-permalink-structure input[name="permalink_structure"]');if(!e.length)return;const t=document.querySelector('#prpl-popover-core-permalink-structure input[name="permalink_custom"]');e.forEach(e=>{e.addEventListener("change",()=>{"custom"===e.value&&t?(t.disabled=!1,t.focus()):t&&(t.disabled=!0)})})}(),function(){const e=document.querySelector("#prpl-popover-core-siteicon .prpl-upload-site-icon");if(e&&window.wp?.media){let t;e.addEventListener("click",s=>{s.preventDefault(),t||(t=window.wp.media({title:e.dataset.title||"Select Site Icon",button:{text:e.dataset.button||"Use as site icon"},multiple:!1,library:{type:"image"}}),t.on("select",()=>{const e=t.state().get("selection").first().toJSON(),s=document.querySelector('#prpl-popover-core-siteicon input[name="site_icon"]');s&&(s.value=e.id);const n=document.querySelector("#prpl-popover-core-siteicon .prpl-site-icon-preview img");n&&(n.src=e.url);const a=document.querySelector('#prpl-popover-core-siteicon button[type="submit"]');a&&(a.disabled=!1)})),t.open()})}const t=document.querySelector("#prpl-popover-yoast-organization-logo .prpl-upload-logo");if(t&&window.wp?.media){let e;t.addEventListener("click",s=>{s.preventDefault(),e||(e=window.wp.media({title:t.dataset.title||"Select Logo",button:{text:t.dataset.button||"Use as logo"},multiple:!1,library:{type:"image"}}),e.on("select",()=>{const t=e.state().get("selection").first().toJSON(),s=document.querySelector('#prpl-popover-yoast-organization-logo input[name="company_logo_id"]');s&&(s.value=t.id);const n=document.querySelector("#prpl-popover-yoast-organization-logo .prpl-logo-preview img");n&&(n.src=t.url);const a=document.querySelector('#prpl-popover-yoast-organization-logo button[type="submit"]');a&&(a.disabled=!1)})),e.open()})}}(),()=>{e.forEach(({formElement:e,handler:t})=>{e.removeEventListener("submit",t)})}},[e,a]),null}const f={"1-week":7,"2-weeks":14,"1-month":30,"3-months":90,"6-months":180,"1-year":365,forever:3650};async function v({status:e="publish",perPage:t=100,excludeProvider:s,provider:n,excludeIds:a=[]}={}){const r={status:e,per_page:t,_embed:!0,"filter[orderby]":"menu_order","filter[order]":"ASC"};s&&(r.exclude_provider=s),n&&(r.provider=n),a.length>0&&(r.exclude=a.join(","));const o=function(e){const t=new URLSearchParams;return Object.entries(e).forEach(([e,s])=>{Array.isArray(s)?s.forEach(s=>t.append(e,s)):null!=s&&""!==s&&t.append(e,s)}),t.toString()}(r);try{return await c()({path:`/wp/v2/prpl_recommendations?${o}`})||[]}catch(e){return console.error("Error fetching tasks:",e),[]}}async function b(e){return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:{status:"trash"}})}async function _(e,t){return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:t})}async function k(e,t){const s=window.prplSuggestedTasksConfig?.nonce||"",n=window.prplSuggestedTasksConfig?.ajaxUrl||"/wp-admin/admin-ajax.php",a=new FormData;a.append("action","progress_planner_suggested_task_action"),a.append("post_id",e),a.append("action_type",t),a.append("nonce",s);try{return(await fetch(n,{method:"POST",body:a,credentials:"same-origin"})).json()}catch(e){return console.error("Error sending task action:",e),null}}function S(){const[e,a]=(0,t.useState)([]),[r,o]=(0,t.useState)(!0),[l,p]=(0,t.useState)(window.prplSuggestedTasksConfig?.showAll||!1),[d,u]=(0,t.useState)(new Set),g=(0,t.useRef)(null),m=(0,t.useRef)(new Set);(0,t.useEffect)(()=>{(async()=>{try{const e=l?100:window.prplSuggestedTasksConfig?.perPage||5,t=await v({status:"publish",perPage:e,excludeProvider:"user"});if(t.forEach(e=>{m.current.add(e.id)}),a(t),o(!1),!window.prplSuggestedTasksConfig?.delayCelebration){const t=await v({status:"pending",perPage:e,excludeProvider:"user"});t.length>0&&(a(e=>[...e,...t]),t.forEach(e=>{m.current.add(e.id)}),t.forEach(e=>{b(e.id).catch(()=>{})}),setTimeout(()=>{const e=new Set(t.map(e=>e.id));u(e),document.dispatchEvent(new CustomEvent("prpl/celebrateTasks")),setTimeout(()=>{a(t=>t.filter(t=>!e.has(t.id))),u(new Set),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},2e3)},3e3))}setTimeout(()=>{window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},100)}catch{o(!1)}})()},[l]);const h=(0,t.useCallback)(async(e,t)=>{try{u(t=>new Set([...t,e])),await b(e),k(e,"complete");const s=parseInt(t.prpl_points)||0;if(s>0&&"function"==typeof window.prplUpdateRaviGauge&&window.prplUpdateRaviGauge(s),s>0&&g.current){const t=g.current.querySelector(`[data-post-id="${e}"]`);document.dispatchEvent(new CustomEvent("prpl/celebrateTasks",{detail:{element:t}}))}setTimeout(async()=>{a(t=>t.filter(t=>t.id!==e)),u(t=>{const s=new Set(t);return s.delete(e),s});const t=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});t.length>0&&(a(e=>[...e,t[0]]),m.current.add(t[0].id)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},2e3)}catch{u(t=>{const s=new Set(t);return s.delete(e),s})}},[]),w=(0,t.useCallback)(async(e,t)=>{try{await async function(e,t){const s=f[t]||7,n=new Date(Date.now()+24*s*60*60*1e3).toISOString().split(".")[0];return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:{status:"future",date:n,date_gmt:n}})}(e,t),a(t=>t.filter(t=>t.id!==e));const s=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});s.length>0&&(a(e=>[...e,s[0]]),m.current.add(s[0].id)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch{}},[]),S=(0,t.useCallback)(async e=>{try{await async function(e){return c()({path:`/wp/v2/prpl_recommendations/${e}?force=true`,method:"DELETE"})}(e),k(e,"delete"),a(t=>t.filter(t=>t.id!==e));const t=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});t.length>0&&(a(e=>[...e,t[0]]),m.current.add(t[0].id)),setTimeout(()=>{window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},500)}catch{}},[]),x=(0,t.useCallback)(async(e,t)=>{try{await _(e,{title:t})}catch{}},[]),C=(0,t.useCallback)(async(t,s)=>{const n=e.findIndex(e=>e.id===t);if(-1===n)return;const r="up"===s?n-1:n+1;if(r<0||r>=e.length)return;const o=[...e],[i]=o.splice(n,1);o.splice(r,0,i),a(o),o.forEach((e,t)=>{_(e.id,{menu_order:t}).catch(()=>{})})},[e]),j=(0,t.useCallback)(async()=>{const e=!l;p(e),o(!0),m.current.clear();const t=new URL(window.location);e?t.searchParams.set("prpl_show_all_recommendations",""):t.searchParams.delete("prpl_show_all_recommendations"),window.history.pushState({},"",t)},[l]);return r?(0,n.jsx)("p",{className:"prpl-suggested-tasks-loading",children:(0,s.__)("Loading tasks…","progress-planner")}):0===e.length?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)("ul",{id:"prpl-suggested-tasks-list",className:"prpl-suggested-tasks-list",ref:g}),(0,n.jsxs)("p",{className:"prpl-no-suggested-tasks",children:[(0,s.__)("You have completed all recommended tasks.","progress-planner"),(0,n.jsx)("br",{}),(0,s.__)("Check back later for new tasks!","progress-planner")]})]}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(y,{tasks:e,onComplete:h}),(0,n.jsx)("ul",{style:{display:"none"}}),(0,n.jsx)("ul",{id:"prpl-suggested-tasks-list",className:"prpl-suggested-tasks-list",ref:g,children:e.map(e=>(0,n.jsx)(i,{task:e,isUserTask:"user"===e.prpl_provider?.slug,isCelebrating:d.has(e.id),onComplete:h,onSnooze:w,onDelete:S,onMove:C,onTitleChange:x},e.id))}),(0,n.jsx)("p",{className:"prpl-show-all-tasks",children:(0,n.jsx)("button",{type:"button",id:"prpl-toggle-all-recommendations",className:"prpl-toggle-all-recommendations-button",onClick:j,children:l?(0,s.__)("Show fewer recommendations","progress-planner"):(0,s.__)("Show all recommendations","progress-planner")})})]})}function x(){const e=document.getElementById("prpl-suggested-tasks-root");e&&(0,t.createRoot)(e).render((0,n.jsx)(S,{}))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",x):x()})();
\ No newline at end of file
+(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var n in s)e.o(s,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:s[n]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.element,s=window.wp.i18n,n=window.ReactJSXRuntime;function a({task:e,isUserTask:a,onComplete:r,onSnooze:o,onDelete:i}){const l=(0,t.useRef)(null);(0,t.useEffect)(()=>{if(!l.current)return;const t=l.current,s=t.querySelectorAll('[data-action="complete"]');s.forEach(t=>{t.addEventListener("click",t=>{t.preventDefault(),r(e.id,e)})});const n=t.querySelectorAll('.prpl-snooze-duration-radio-group input[type="radio"]');return n.forEach(t=>{t.addEventListener("change",()=>{o(e.id,t.value)})}),t.querySelectorAll('a[onclick*="showPopover"]').forEach(e=>{const t=e.getAttribute("onclick"),s=t?.match(/getElementById\(['"]([^'"]+)['"]\)/);if(s){const t=s[1];e.removeAttribute("onclick"),e.addEventListener("click",e=>{e.preventDefault();const s=document.getElementById(t);s&&"function"==typeof s.showPopover&&s.showPopover()})}}),t.querySelectorAll(".prpl-suggested-task-button.trash").forEach(t=>{t.addEventListener("click",t=>{t.preventDefault(),i(e.id)})}),()=>{s.forEach(e=>{e.replaceWith(e.cloneNode(!0))}),n.forEach(e=>{e.replaceWith(e.cloneNode(!0))})}},[e,r,o,i]);const c=e.prpl_task_actions||[];return 0!==c.length||a?(0,n.jsxs)("div",{className:"tooltip-actions",ref:l,children:[c.map((e,t)=>(0,n.jsx)("span",{className:"tooltip-action",dangerouslySetInnerHTML:{__html:e}},t)),a&&(0,n.jsx)("span",{className:"tooltip-action",children:(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button trash","data-post-id":e.id,title:(0,s.__)("Delete","progress-planner"),onClick:()=>i(e.id),children:[(0,n.jsx)("span",{className:"prpl-tooltip-action-text",children:(0,s.__)("Delete","progress-planner")}),(0,n.jsxs)("span",{className:"screen-reader-text",children:[(0,s.__)("Delete","progress-planner"),":"," ",e.title?.rendered||e.title]})]})})]}):(0,n.jsx)("div",{className:"tooltip-actions"})}function r(){return(0,n.jsx)("span",{style:{width:"0.75rem",height:"100%",display:"flex",alignItems:"center",justifyContent:"center"},children:(0,n.jsx)("svg",{role:"img","aria-hidden":"true",focusable:"false",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 17",children:(0,n.jsx)("path",{fill:"#6b7280",d:"M19.92 8.12c-.05-.12-.12-.23-.22-.33L12.21.29A.996.996 0 1 0 10.8 1.7l5.79 5.79H1c-.55 0-1 .45-1 1s.45 1 1 1h15.59l-5.79 5.79a.996.996 0 0 0 .71 1.7c.26 0 .51-.1.71-.29l7.5-7.5c.1-.1.17-.21.22-.33.05-.12.07-.24.08-.38 0-.14-.03-.27-.08-.38Z"})})})}function o(){return(0,n.jsx)("svg",{role:"img","aria-hidden":"true",focusable:"false",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",children:(0,n.jsx)("path",{fill:"#9ca3af",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Zm-17.98-3h17.97c1.9 0 3.51-1.48 3.65-3.38l2.34-30.46c-2.15-.3-4.33-.53-6.48-.7h-.03c-5.62-.43-11.32-.43-16.95 0h-.03c-2.15.17-4.33.4-6.48.7l2.34 30.46c.15 1.9 1.75 3.38 3.65 3.38ZM24 7.01c2.37 0 4.74.07 7.11.22v-.49c0-1.93-1.47-3.49-3.34-3.55-2.5-.08-5.03-.08-7.52 0-1.88.06-3.34 1.62-3.34 3.55v.49c2.36-.15 4.73-.22 7.11-.22Zm5.49 32.26h-.06c-.83-.03-1.47-.73-1.44-1.56l.79-20.65c.03-.83.75-1.45 1.56-1.44.83.03 1.47.73 1.44 1.56l-.79 20.65c-.03.81-.7 1.44-1.5 1.44Zm-10.98 0c-.8 0-1.47-.63-1.5-1.44l-.79-20.65c-.03-.83.61-1.52 1.44-1.56.84 0 1.52.61 1.56 1.44l.79 20.65c.03.83-.61 1.52-1.44 1.56h-.06Z"})})}function i({task:e,isUserTask:i,isCelebrating:l,onComplete:c,onSnooze:p,onDelete:d,onMove:u,onTitleChange:g}){const m=(0,t.useRef)(null),w=(0,t.useRef)(null),h="trash"===e.status||"pending"===e.status,y=(0,t.useCallback)(()=>{c(e.id,e)},[e,c]),f=(0,t.useCallback)(e=>{if("Enter"===e.key)return e.preventDefault(),e.stopPropagation(),e.target.blur(),!1},[]),v=(0,t.useCallback)(()=>{clearTimeout(w.current),w.current=setTimeout(()=>{if(m.current){const t=m.current.textContent.replace(/\n/g,"");g(e.id,t)}},300)},[e.id,g]),b=(0,t.useCallback)(()=>{u(e.id,"up")},[e.id,u]),_=(0,t.useCallback)(()=>{u(e.id,"down")},[e.id,u]),S=(0,t.useCallback)(()=>{d(e.id)},[e.id,d]),k=e.slug||e.id,x=e.prpl_provider?.slug||"",C=["prpl-suggested-task",l?"prpl-suggested-task-celebrated":""].filter(Boolean).join(" ");return(0,n.jsxs)("li",{className:C,"data-task-id":k,"data-post-id":e.id,"data-task-action":"pending"===e.status||l?"celebrate":"","data-task-provider-id":x,"data-task-points":e.prpl_points||0,"data-task-order":e.menu_order||0,children:[(0,n.jsx)("div",{className:"prpl-suggested-task-checkbox-wrapper",children:i?(0,n.jsxs)("label",{children:[(0,n.jsx)("input",{type:"checkbox",className:"prpl-suggested-task-checkbox",onChange:y,style:{margin:0},checked:h,disabled:l}),(0,n.jsxs)("span",{className:"screen-reader-text",children:[e.title?.rendered||e.title,":"," ",(0,s.__)("Mark as complete","progress-planner")]})]}):(0,n.jsx)(r,{})}),(0,n.jsx)("div",{className:"prpl-suggested-task-title-wrapper",children:(0,n.jsx)("h3",{className:"prpl-task-title",children:i?(0,n.jsx)("span",{ref:m,contentEditable:"plaintext-only",role:"textbox",tabIndex:0,"aria-label":(0,s.__)("Edit task title","progress-planner"),"aria-multiline":"false",onKeyDown:f,onInput:v,suppressContentEditableWarning:!0,dangerouslySetInnerHTML:{__html:e.title?.rendered||e.title}}):(0,n.jsx)("span",{dangerouslySetInnerHTML:{__html:e.title?.rendered||e.title}})})}),(0,n.jsxs)("div",{className:"prpl-suggested-task-points-wrapper",children:[e.prpl_points>0&&(0,n.jsxs)("span",{className:"prpl-suggested-task-points",children:["+",e.prpl_points]}),i&&(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button trash","data-post-id":e.id,title:(0,s.__)("Delete","progress-planner"),onClick:S,children:[(0,n.jsx)(o,{}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Delete","progress-planner")})]})]}),i&&(0,n.jsx)("div",{className:"tooltip-actions prpl-move-buttons-wrapper",children:(0,n.jsxs)("span",{className:"prpl-move-buttons",children:[(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button move-up","data-task-id":k,"data-task-title":e.title?.rendered||e.title,"data-action":"move-up","data-target":"move-up",title:(0,s.__)("Move up","progress-planner"),onClick:b,children:[(0,n.jsx)("span",{className:"dashicons dashicons-arrow-up-alt2"}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Move up","progress-planner")})]}),(0,n.jsxs)("button",{type:"button",className:"prpl-suggested-task-button move-down","data-task-id":k,"data-task-title":e.title?.rendered||e.title,"data-action":"move-down","data-target":"move-down",title:(0,s.__)("Move down","progress-planner"),onClick:_,children:[(0,n.jsx)("span",{className:"dashicons dashicons-arrow-down-alt2"}),(0,n.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Move down","progress-planner")})]})]})}),(0,n.jsx)("div",{className:"prpl-suggested-task-actions-wrapper",children:(0,n.jsx)(a,{task:e,isUserTask:i,onComplete:c,onSnooze:p,onDelete:d})})]})}const l=window.wp.apiFetch;var c=e.n(l);function p(e){let t=e.querySelector('button[type="submit"]');if(t||(t=e.querySelector('button[data-action="completeTask"]')),t){t.disabled=!0;const e=document.createElement("span");e.classList.add("prpl-spinner"),e.innerHTML='',t.after(e)}}function d(e){let t=e.querySelector('button[type="submit"]');t||(t=e.querySelector('button[data-action="completeTask"]')),t&&(t.disabled=!1);const s=e.querySelector("span.prpl-spinner");s&&s.remove()}function u(e){const t=document.querySelector(`#${e} form`);if(t&&!t.parentNode.querySelector("p.prpl-interactive-task-error-message")){const e=document.createElement("p");e.classList.add("prpl-note","prpl-note-error","prpl-interactive-task-error-message"),e.textContent=(0,s.__)("Something went wrong. Please try again.","progress-planner"),t.insertAdjacentElement("afterend",e)}}function g(e){const t=document.querySelector(`#${e} form`);if(!t)return;const s=t.parentNode.querySelector("p.prpl-interactive-task-error-message");s&&s.remove()}async function m({setting:e,settingPath:t=!1,popoverId:s,action:n="prpl_interactive_task_submit",settingCallbackValue:a=e=>e}){const r=document.querySelector(`#${s} form`);if(!r)throw new Error("Form not found");p(r),g(s);try{const o=a(new FormData(r).get(e)),i=window.prplSuggestedTasksConfig?.ajaxUrl||window.progressPlanner?.ajaxUrl||"/wp-admin/admin-ajax.php",l=window.prplSuggestedTasksConfig?.nonce||window.progressPlanner?.nonce||"",c=new URLSearchParams({action:n,_ajax_nonce:l,setting:e,value:o});t&&c.append("setting_path",t);const p=await fetch(i,{method:"POST",body:c,credentials:"same-origin"}),g=await p.json();if(d(r),!0!==g.success)throw u(s),new Error("Settings update failed");return g}catch(e){throw d(r),u(s),e}}async function w(e,t="posts"){return c()({path:`/wp/v2/${t}/${e}?force=true`,method:"DELETE"})}const h={"core-blogdescription":{type:"siteSettings",settingAPIKey:"description",setting:"blogdescription"},"disable-comments":{type:"siteSettings",settingAPIKey:"default_comment_status",setting:"default_comment_status",settingCallbackValue:()=>"closed"},"disable-comment-pagination":{type:"siteSettings",settingAPIKey:"page_comments",setting:"page_comments",settingCallbackValue:()=>!1},"select-locale":{type:"siteSettings",settingAPIKey:"WPLANG",setting:"WPLANG"},"select-timezone":{type:"siteSettings",settingAPIKey:"timezone_string",setting:"timezone_string"},"search-engine-visibility":{type:"pluginSettings",setting:"blog_public",action:"prpl_interactive_task_submit",settingCallbackValue:()=>"1"},"set-date-format":{type:"siteSettings",settingAPIKey:"date_format",setting:"date_format"},"core-permalink-structure":{type:"siteSettings",settingAPIKey:"permalink_structure",setting:"permalink_structure"},"rename-uncategorized-category":{type:"customSubmit"},"hello-world":{type:"customSubmit"},"sample-page":{type:"customSubmit"},"yoast-author-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-author"]),settingCallbackValue:()=>!0},"yoast-date-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-date"]),settingCallbackValue:()=>!0},"yoast-format-archive":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-post_format"]),settingCallbackValue:()=>!0},"yoast-media-pages":{type:"pluginSettings",setting:"wpseo_titles",settingPath:JSON.stringify(["disable-attachment"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-emoji-scripts":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_emoji_scripts"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-feed-authors":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_feed_authors"]),settingCallbackValue:()=>!0},"yoast-crawl-settings-feed-global-comments":{type:"pluginSettings",setting:"wpseo",settingPath:JSON.stringify(["remove_feed_global_comments"]),settingCallbackValue:()=>!0},"yoast-organization-logo":{type:"customSubmit"},"aioseo-author-archive":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["archives","author","show"]),settingCallbackValue:()=>!1},"aioseo-date-archive":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["archives","date","show"]),settingCallbackValue:()=>!1},"aioseo-media-pages":{type:"pluginSettings",setting:"aioseo_options_search_appearance",settingPath:JSON.stringify(["postTypes","attachment","show"]),settingCallbackValue:()=>!1},"aioseo-crawl-settings-feed-authors":{type:"pluginSettings",setting:"aioseo_options_rss_content",settingPath:JSON.stringify(["authorFeed"]),settingCallbackValue:()=>!1},"aioseo-crawl-settings-feed-comments":{type:"pluginSettings",setting:"aioseo_options_rss_content",settingPath:JSON.stringify(["commentFeed"]),settingCallbackValue:()=>!1},"core-siteicon":{type:"customSubmit"},"update-term-description":{type:"customSubmit"},"remove-terms-without-posts":{type:"customSubmit"}};function y({tasks:e,onComplete:s}){const n=(0,t.useCallback)(async(e,t)=>{switch(e){case"hello-world":{const e=window.helloWorldData?.postId;return e&&await w(e,"posts"),{success:!0}}case"sample-page":{const e=window.samplePageData?.postId;return e&&await w(e,"pages"),{success:!0}}case"rename-uncategorized-category":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("category_name"),s=window.renameUncategorizedCategoryData?.termId;s&&t&&await c()({path:`/wp/v2/categories/${s}`,method:"POST",data:{name:t}})}return{success:!0}}case"core-siteicon":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("site_icon");t&&await c()({path:"/wp/v2/settings",method:"POST",data:{site_icon:parseInt(t)}})}return{success:!0}}case"yoast-organization-logo":{const e=document.querySelector(`#${t} form`);if(e){const s=new FormData(e).get("company_logo_id");s&&await m({setting:"wpseo",settingPath:JSON.stringify(["company_logo_id"]),popoverId:t,settingCallbackValue:()=>parseInt(s)})}return{success:!0}}case"update-term-description":{const e=document.querySelector(`#${t} form`);if(e){const t=new FormData(e).get("description"),s=window.updateTermDescriptionData?.termId,n=window.updateTermDescriptionData?.taxonomy||"category";if(s&&t){const e="category"===n?"categories":n;await c()({path:`/wp/v2/${e}/${s}`,method:"POST",data:{description:t}})}}return{success:!0}}case"remove-terms-without-posts":if(document.querySelector(`#${t} form`)){const e=window.removeTermsWithoutPostsData?.termIds||[],t=window.removeTermsWithoutPostsData?.taxonomy||"category",s="category"===t?"categories":t;await Promise.all(e.map(e=>c()({path:`/wp/v2/${s}/${e}?force=true`,method:"DELETE"})))}return{success:!0};default:return{success:!0}}},[]),a=(0,t.useCallback)(async(t,a,r)=>{try{const o=e.find(e=>e.slug===t||e.prpl_provider?.slug===t||`${e.id}`===t);if(!o)return;let i;switch(r.type){case"siteSettings":i=async function({settingAPIKey:e,setting:t,popoverId:s,settingCallbackValue:n=e=>e}){const a=document.querySelector(`#${s} form`);if(!a)throw new Error("Form not found");p(a),g(s);try{const s=n(new FormData(a).get(t)),r=await c()({path:"/wp/v2/settings",method:"POST",data:{[e]:s}});return d(a),r}catch(e){throw d(a),u(s),e}}({settingAPIKey:r.settingAPIKey,setting:r.setting,popoverId:a,settingCallbackValue:r.settingCallbackValue});break;case"pluginSettings":i=m({setting:r.setting,settingPath:r.settingPath,popoverId:a,action:r.action||"prpl_interactive_task_submit",settingCallbackValue:r.settingCallbackValue});break;case"customSubmit":i=n(t,a);break;default:return}await i,await s(o.id,o),function(e){const t=document.getElementById(e);t&&"function"==typeof t.hidePopover&&t.hidePopover()}(a)}catch(e){console.error("Popover form submission error:",e)}},[e,s,n]);return(0,t.useEffect)(()=>{const e=new Map;Object.entries(h).forEach(([t,s])=>{const n=`prpl-popover-${t}`,r=document.querySelector(`#${n} form`);if(!r)return;const o=e=>{e.preventDefault(),a(t,n,s)};r.addEventListener("submit",o),e.set(n,{formElement:r,handler:o})});const t=document.querySelector("input#blogdescription");if(t){const e=document.querySelector('#prpl-popover-core-blogdescription button[type="submit"]');if(e){const s=t=>{e.disabled=0===t.target.value.length};t.addEventListener("input",s)}}return function(){const e=document.querySelectorAll('#prpl-popover-set-date-format input[name="date_format"]'),t=document.querySelector('#prpl-popover-set-date-format input[name="date_format_custom"]');if(!e.length||!t)return;let s;e.forEach(e=>{e.addEventListener("change",()=>{"custom"===e.value?(t.disabled=!1,t.focus()):t.disabled=!0})}),t.addEventListener("input",()=>{clearTimeout(s),s=setTimeout(async()=>{const e=t.value;if(e)try{const s=window.prplSuggestedTasksConfig?.ajaxUrl||window.progressPlanner?.ajaxUrl||"/wp-admin/admin-ajax.php",n=window.prplSuggestedTasksConfig?.nonce||window.progressPlanner?.nonce||"",a=await fetch(`${s}?action=prpl_date_format_preview&format=${encodeURIComponent(e)}&_ajax_nonce=${n}`,{credentials:"same-origin"}),r=await a.json();if(r.success&&r.data){const e=t.closest(".prpl-radio-wrapper")?.querySelector(".date-time-text");e&&(e.textContent=r.data)}}catch{}},300)})}(),function(){const e=document.querySelectorAll('#prpl-popover-core-permalink-structure input[name="permalink_structure"]');if(!e.length)return;const t=document.querySelector('#prpl-popover-core-permalink-structure input[name="permalink_custom"]');e.forEach(e=>{e.addEventListener("change",()=>{"custom"===e.value&&t?(t.disabled=!1,t.focus()):t&&(t.disabled=!0)})})}(),function(){const e=document.querySelector("#prpl-popover-core-siteicon .prpl-upload-site-icon");if(e&&window.wp?.media){let t;e.addEventListener("click",s=>{s.preventDefault(),t||(t=window.wp.media({title:e.dataset.title||"Select Site Icon",button:{text:e.dataset.button||"Use as site icon"},multiple:!1,library:{type:"image"}}),t.on("select",()=>{const e=t.state().get("selection").first().toJSON(),s=document.querySelector('#prpl-popover-core-siteicon input[name="site_icon"]');s&&(s.value=e.id);const n=document.querySelector("#prpl-popover-core-siteicon .prpl-site-icon-preview img");n&&(n.src=e.url);const a=document.querySelector('#prpl-popover-core-siteicon button[type="submit"]');a&&(a.disabled=!1)})),t.open()})}const t=document.querySelector("#prpl-popover-yoast-organization-logo .prpl-upload-logo");if(t&&window.wp?.media){let e;t.addEventListener("click",s=>{s.preventDefault(),e||(e=window.wp.media({title:t.dataset.title||"Select Logo",button:{text:t.dataset.button||"Use as logo"},multiple:!1,library:{type:"image"}}),e.on("select",()=>{const t=e.state().get("selection").first().toJSON(),s=document.querySelector('#prpl-popover-yoast-organization-logo input[name="company_logo_id"]');s&&(s.value=t.id);const n=document.querySelector("#prpl-popover-yoast-organization-logo .prpl-logo-preview img");n&&(n.src=t.url);const a=document.querySelector('#prpl-popover-yoast-organization-logo button[type="submit"]');a&&(a.disabled=!1)})),e.open()})}}(),()=>{e.forEach(({formElement:e,handler:t})=>{e.removeEventListener("submit",t)})}},[e,a]),null}const f={"1-week":7,"2-weeks":14,"1-month":30,"3-months":90,"6-months":180,"1-year":365,forever:3650};async function v({status:e="publish",perPage:t=100,excludeProvider:s,provider:n,excludeIds:a=[]}={}){const r={status:e,per_page:t,_embed:!0,"filter[orderby]":"menu_order","filter[order]":"ASC"};s&&(r.exclude_provider=s),n&&(r.provider=n),a.length>0&&(r.exclude=a.join(","));const o=function(e){const t=new URLSearchParams;return Object.entries(e).forEach(([e,s])=>{Array.isArray(s)?s.forEach(s=>t.append(e,s)):null!=s&&""!==s&&t.append(e,s)}),t.toString()}(r);try{return await c()({path:`/wp/v2/prpl_recommendations?${o}`})||[]}catch(e){return console.error("Error fetching tasks:",e),[]}}async function b(e){return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:{status:"trash"}})}async function _(e,t){return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:t})}async function S(e,t){const s=window.prplSuggestedTasksConfig?.nonce||"",n=window.prplSuggestedTasksConfig?.ajaxUrl||"/wp-admin/admin-ajax.php",a=new FormData;a.append("action","progress_planner_suggested_task_action"),a.append("post_id",e),a.append("action_type",t),a.append("nonce",s);try{return(await fetch(n,{method:"POST",body:a,credentials:"same-origin"})).json()}catch(e){return console.error("Error sending task action:",e),null}}function k(){const[e,a]=(0,t.useState)([]),[r,o]=(0,t.useState)(!0),[l,p]=(0,t.useState)(window.prplSuggestedTasksConfig?.showAll||!1),[d,u]=(0,t.useState)(new Set),g=(0,t.useRef)(null),m=(0,t.useRef)(new Set);(0,t.useEffect)(()=>{const e=()=>{document.querySelectorAll(".prpl-widget-wrapper").forEach(e=>{if(!e||e.classList.contains("in-popover"))return;const t=e.querySelector(".widget-inner-container");if(!t)return;const s=document.querySelector(".prpl-widgets-container");if(!s)return;const n=parseInt(window.getComputedStyle(s).getPropertyValue("grid-auto-rows")),a=parseInt(window.getComputedStyle(e).getPropertyValue("padding-top")),r=parseInt(window.getComputedStyle(e).getPropertyValue("padding-bottom")),o=Math.ceil((t.getBoundingClientRect().height+a+r)/n);e.style.gridRowEnd="span "+(o+1)})},t=()=>{setTimeout(e,0)};return window.addEventListener("prpl/grid/resize",e),window.addEventListener("resize",t),window.addEventListener("load",t),t(),setTimeout(t,1e3),()=>{window.removeEventListener("prpl/grid/resize",e),window.removeEventListener("resize",t),window.removeEventListener("load",t)}},[]),(0,t.useEffect)(()=>{(async()=>{try{const e=l?100:window.prplSuggestedTasksConfig?.perPage||5,t=await v({status:"publish",perPage:e,excludeProvider:"user"});if(t.forEach(e=>{m.current.add(e.id)}),a(t),o(!1),!window.prplSuggestedTasksConfig?.delayCelebration){const t=await v({status:"pending",perPage:e,excludeProvider:"user"});t.length>0&&(a(e=>[...e,...t]),t.forEach(e=>{m.current.add(e.id)}),t.forEach(e=>{b(e.id).catch(()=>{})}),setTimeout(()=>{const e=new Set(t.map(e=>e.id));u(e),document.dispatchEvent(new CustomEvent("prpl/celebrateTasks")),setTimeout(()=>{a(t=>t.filter(t=>!e.has(t.id))),u(new Set),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},2e3)},3e3))}setTimeout(()=>{window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},100)}catch{o(!1)}})()},[l]);const w=(0,t.useCallback)(async(e,t)=>{try{u(t=>new Set([...t,e])),await b(e),S(e,"complete");const s=parseInt(t.prpl_points)||0;if(s>0&&"function"==typeof window.prplUpdateRaviGauge&&window.prplUpdateRaviGauge(s),s>0&&g.current){const t=g.current.querySelector(`[data-post-id="${e}"]`);document.dispatchEvent(new CustomEvent("prpl/celebrateTasks",{detail:{element:t}}))}setTimeout(async()=>{a(t=>t.filter(t=>t.id!==e)),u(t=>{const s=new Set(t);return s.delete(e),s});const t=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});t.length>0&&(a(e=>[...e,t[0]]),m.current.add(t[0].id)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},2e3)}catch{u(t=>{const s=new Set(t);return s.delete(e),s})}},[]),h=(0,t.useCallback)(async(e,t)=>{try{await async function(e,t){const s=f[t]||7,n=new Date(Date.now()+24*s*60*60*1e3).toISOString().split(".")[0];return c()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:{status:"future",date:n,date_gmt:n}})}(e,t),a(t=>t.filter(t=>t.id!==e));const s=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});s.length>0&&(a(e=>[...e,s[0]]),m.current.add(s[0].id)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch{}},[]),k=(0,t.useCallback)(async e=>{try{await async function(e){return c()({path:`/wp/v2/prpl_recommendations/${e}?force=true`,method:"DELETE"})}(e),S(e,"delete"),a(t=>t.filter(t=>t.id!==e));const t=await v({status:"publish",perPage:1,excludeProvider:"user",excludeIds:Array.from(m.current)});t.length>0&&(a(e=>[...e,t[0]]),m.current.add(t[0].id)),setTimeout(()=>{window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},500)}catch{}},[]),x=(0,t.useCallback)(async(e,t)=>{try{await _(e,{title:t})}catch{}},[]),C=(0,t.useCallback)(async(t,s)=>{const n=e.findIndex(e=>e.id===t);if(-1===n)return;const r="up"===s?n-1:n+1;if(r<0||r>=e.length)return;const o=[...e],[i]=o.splice(n,1);o.splice(r,0,i),a(o),o.forEach((e,t)=>{_(e.id,{menu_order:t}).catch(()=>{})})},[e]),E=(0,t.useCallback)(async()=>{const e=!l;p(e),o(!0),m.current.clear();const t=new URL(window.location);e?t.searchParams.set("prpl_show_all_recommendations",""):t.searchParams.delete("prpl_show_all_recommendations"),window.history.pushState({},"",t)},[l]);return r?(0,n.jsx)("p",{className:"prpl-suggested-tasks-loading",children:(0,s.__)("Loading tasks…","progress-planner")}):0===e.length?(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)("ul",{id:"prpl-suggested-tasks-list",className:"prpl-suggested-tasks-list",ref:g}),(0,n.jsxs)("p",{className:"prpl-no-suggested-tasks",children:[(0,s.__)("You have completed all recommended tasks.","progress-planner"),(0,n.jsx)("br",{}),(0,s.__)("Check back later for new tasks!","progress-planner")]})]}):(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(y,{tasks:e,onComplete:w}),(0,n.jsx)("ul",{style:{display:"none"}}),(0,n.jsx)("ul",{id:"prpl-suggested-tasks-list",className:"prpl-suggested-tasks-list",ref:g,children:e.map(e=>(0,n.jsx)(i,{task:e,isUserTask:"user"===e.prpl_provider?.slug,isCelebrating:d.has(e.id),onComplete:w,onSnooze:h,onDelete:k,onMove:C,onTitleChange:x},e.id))}),(0,n.jsx)("p",{className:"prpl-show-all-tasks",children:(0,n.jsx)("button",{type:"button",id:"prpl-toggle-all-recommendations",className:"prpl-toggle-all-recommendations-button",onClick:E,children:l?(0,s.__)("Show fewer recommendations","progress-planner"):(0,s.__)("Show all recommendations","progress-planner")})})]})}function x(){const e=document.getElementById("prpl-suggested-tasks-root");e&&(0,t.createRoot)(e).render((0,n.jsx)(k,{}))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",x):x()})();
\ No newline at end of file
diff --git a/build/todo.asset.php b/build/todo.asset.php
index 4155a15e35..23ec0774d1 100644
--- a/build/todo.asset.php
+++ b/build/todo.asset.php
@@ -1 +1 @@
- array('react-jsx-runtime', 'wp-api-fetch', 'wp-element', 'wp-i18n'), 'version' => '57c33252d20fa7a21373');
+ array('react-jsx-runtime', 'wp-api-fetch', 'wp-element', 'wp-i18n'), 'version' => '947eebde62f548265720');
diff --git a/build/todo.js b/build/todo.js
index aefd781e2d..a19337dd45 100644
--- a/build/todo.js
+++ b/build/todo.js
@@ -1 +1 @@
-(()=>{"use strict";var e={n:t=>{var s=t&&t.__esModule?()=>t.default:()=>t;return e.d(s,{a:s}),s},d:(t,s)=>{for(var r in s)e.o(s,r)&&!e.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:s[r]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.element,s=window.wp.i18n,r=window.wp.apiFetch;var n=e.n(r);const a=window.ReactJSXRuntime;async function l(e,t){return n()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:t})}async function o(e){return n()({path:`/wp/v2/prpl_recommendations/${e}?force=true`,method:"DELETE"})}function i({task:e,isGolden:r,isCompleted:n,onToggle:l,onDelete:o,onMove:i,onTitleChange:d}){const p=(0,t.useRef)(null),c=(0,t.useRef)(null),u=(0,t.useCallback)(()=>{c.current&&clearTimeout(c.current),c.current=setTimeout(()=>{if(p.current){const t=p.current.textContent.replace(/\n/g,"");d(e.id,t)}},300)},[e.id,d]),h=(0,t.useCallback)(e=>{"Enter"===e.key&&(e.preventDefault(),e.target.blur())},[]);return(0,a.jsxs)("li",{className:"prpl-suggested-task"+(r?" prpl-golden-task":""),"data-task-id":e.slug||e.id,"data-post-id":e.id,"data-task-action":n?"completed":"publish","data-task-provider-id":"user","data-task-points":e.prpl_points||0,"data-task-order":e.menu_order||0,children:[(0,a.jsx)("div",{className:"prpl-suggested-task-checkbox-wrapper",children:(0,a.jsxs)("label",{children:[(0,a.jsx)("input",{type:"checkbox",className:"prpl-suggested-task-checkbox",checked:n,onChange:()=>l(e.id)}),(0,a.jsxs)("span",{className:"screen-reader-text",children:[e.title?.rendered||e.title,":"," ",(0,s.__)("Mark as completed","progress-planner")]})]})}),(0,a.jsx)("div",{className:"prpl-suggested-task-title-wrapper",children:(0,a.jsx)("h3",{className:"prpl-task-title",children:(0,a.jsx)("span",{ref:p,contentEditable:!0,suppressContentEditableWarning:!0,onInput:u,onKeyDown:h,"data-post-id":e.id,tabIndex:0,role:"textbox","aria-label":(0,s.__)("Edit task title","progress-planner"),children:e.title?.rendered||e.title})})}),(0,a.jsxs)("div",{className:"prpl-suggested-task-points-wrapper",children:[(e.prpl_points||0)>0&&(0,a.jsxs)("span",{className:"prpl-suggested-task-points",children:["+",e.prpl_points]}),(0,a.jsx)("button",{type:"button",className:"prpl-suggested-task-delete",onClick:()=>o(e.id),"aria-label":(0,s.__)("Delete task","progress-planner"),children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",width:"16",height:"16","aria-hidden":"true",children:(0,a.jsx)("path",{fill:"currentColor",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Z"})})})]}),!n&&(0,a.jsxs)("div",{className:"tooltip-actions prpl-move-buttons-wrapper",children:[(0,a.jsx)("button",{type:"button",className:"prpl-move-up",onClick:()=>i(e.id,"up"),"aria-label":(0,s.__)("Move up","progress-planner"),children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"16",height:"16","aria-hidden":"true",children:(0,a.jsx)("path",{fill:"currentColor",d:"M12 4l-8 8h6v8h4v-8h6z"})})}),(0,a.jsx)("button",{type:"button",className:"prpl-move-down",onClick:()=>i(e.id,"down"),"aria-label":(0,s.__)("Move down","progress-planner"),children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"16",height:"16","aria-hidden":"true",children:(0,a.jsx)("path",{fill:"currentColor",d:"M12 20l8-8h-6v-8h-4v8h-6z"})})})]})]})}function d(){const[e,r]=(0,t.useState)([]),[d,p]=(0,t.useState)([]),[c,u]=(0,t.useState)(!0),[h,m]=(0,t.useState)(""),[g,w]=(0,t.useState)(!1),x=(0,t.useRef)(null);(0,t.useEffect)(()=>{(async()=>{try{const e=await async function({status:e=["publish","trash"]}={}){const t=Array.isArray(e)?e.map(e=>`status[]=${e}`).join("&"):`status=${e}`;return n()({path:`/wp/v2/prpl_recommendations?${t}&provider=user&per_page=100&_embed=true&filter[orderby]=menu_order&filter[order]=ASC`})}(),t=e.filter(e=>"publish"===e.status),s=e.filter(e=>"trash"===e.status);t.sort((e,t)=>(e.menu_order||0)-(t.menu_order||0)),s.sort((e,t)=>(e.menu_order||0)-(t.menu_order||0)),r(t),p(s)}catch(e){console.error("Error loading tasks:",e)}finally{u(!1),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}})()},[]);const v=(0,t.useCallback)(async t=>{if(t.preventDefault(),h.trim())try{const t=e.reduce((e,t)=>Math.max(e,t.menu_order||0),0),a=await async function({title:e,order:t}){return n()({path:"/wp/v2/prpl_recommendations",method:"POST",data:{title:e,status:"publish",menu_order:t,prpl_recommendations_provider:window.prplTodoConfig?.userProviderId}})}({title:h,order:t+1});r(e=>[...e,a]),m(""),window.wp?.a11y?.speak&&window.wp.a11y.speak((0,s.__)("Task added successfully","progress-planner"),"polite"),x.current?.focus(),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error creating task:",e)}},[h,e]),k=(0,t.useCallback)(async t=>{const s=e.find(e=>e.id===t)||d.find(e=>e.id===t);if(!s)return;const n="trash"===s.status,a=n?"publish":"trash";try{await l(t,{status:a}),n?(p(e=>e.filter(e=>e.id!==t)),r(e=>[...e,{...s,status:"publish"}])):(r(e=>e.filter(e=>e.id!==t)),p(e=>[...e,{...s,status:"trash"}]),s.prpl_points>0&&("function"==typeof window.prplUpdateRaviGauge&&window.prplUpdateRaviGauge(s.prpl_points),document.dispatchEvent(new CustomEvent("prpl/celebrateTasks",{detail:{}})))),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error toggling task:",e)}},[e,d]),_=(0,t.useCallback)(async e=>{try{await o(e),r(t=>t.filter(t=>t.id!==e)),p(t=>t.filter(t=>t.id!==e)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error deleting task:",e)}},[]),b=(0,t.useCallback)(async(t,s)=>{const n=e.findIndex(e=>e.id===t);if(-1===n)return;const a="up"===s?n-1:n+1;if(a<0||a>=e.length)return;const o=[...e];[o[n],o[a]]=[o[a],o[n]];const i=o.map((e,t)=>({...e,menu_order:t}));r(i);try{await Promise.all(i.map(e=>l(e.id,{menu_order:e.menu_order})))}catch(e){console.error("Error saving task order:",e)}window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},[e]),f=(0,t.useCallback)(async(e,t)=>{try{await l(e,{title:t}),r(s=>s.map(s=>s.id===e?{...s,title:{rendered:t}}:s)),p(s=>s.map(s=>s.id===e?{...s,title:{rendered:t}}:s))}catch(e){console.error("Error updating task title:",e)}},[]),j=(0,t.useCallback)(async()=>{try{await Promise.all(d.map(e=>o(e.id))),p([]),w(!1),window.wp?.a11y?.speak&&window.wp.a11y.speak((0,s.__)("All completed tasks deleted","progress-planner"),"assertive"),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error deleting completed tasks:",e)}},[d]);return c?(0,a.jsx)("p",{id:"prpl-todo-list-loading",children:(0,s.__)("Loading items…","progress-planner")}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{id:"todo-aria-live-region","aria-live":"polite",style:{position:"absolute",left:"-9999px"}}),(0,a.jsx)("ul",{id:"todo-list",className:"prpl-todo-list prpl-suggested-tasks-list",children:e.map((e,t)=>(0,a.jsx)(i,{task:e,isGolden:0===t&&e.prpl_points>0,isCompleted:!1,onToggle:k,onDelete:_,onMove:b,onTitleChange:f},e.id))}),(0,a.jsxs)("form",{id:"create-todo-item",onSubmit:v,children:[(0,a.jsx)("input",{ref:x,type:"text",id:"new-todo-content",placeholder:(0,s.__)("Add a new task","progress-planner"),"aria-label":(0,s.__)("Add a new task","progress-planner"),required:!0,value:h,onChange:e=>m(e.target.value)}),(0,a.jsxs)("button",{type:"submit","aria-label":(0,s.__)("Add task","progress-planner"),children:[(0,a.jsx)("span",{className:"dashicons dashicons-plus-alt2","aria-hidden":"true"}),(0,a.jsx)("span",{className:"screen-reader-text",children:(0,s.__)("Add task","progress-planner")})]})]}),d.length>0&&(0,a.jsxs)("details",{id:"todo-list-completed-details",children:[(0,a.jsxs)("summary",{children:[(0,s.__)("Completed tasks","progress-planner"),(0,a.jsx)("span",{className:"prpl-todo-list-completed-summary-icon",children:(0,a.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:"1.5",stroke:"currentColor",children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m19.5 8.25-7.5 7.5-7.5-7.5"})})})]}),(0,a.jsx)("div",{id:"todo-list-completed-delete-all-wrapper",children:(0,a.jsxs)("button",{id:"todo-list-completed-delete-all",onClick:()=>w(!0),children:[(0,a.jsx)("span",{style:{display:"inline-block",width:"18px",height:"18px"},children:(0,a.jsx)("svg",{role:"img","aria-hidden":"true",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",children:(0,a.jsx)("path",{fill:"#9ca3af",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Z"})})}),(0,s.__)("Delete all completed tasks","progress-planner")]})}),(0,a.jsx)("ul",{id:"todo-list-completed",className:"prpl-todo-list prpl-suggested-tasks-list",children:d.map(e=>(0,a.jsx)(i,{task:e,isGolden:!1,isCompleted:!0,onToggle:k,onDelete:_,onMove:b,onTitleChange:f},e.id))})]}),g&&(0,a.jsxs)("div",{id:"todo-list-completed-delete-all-popover",className:"prpl-popover",style:{position:"fixed",top:"50%",left:"50%",transform:"translate(-50%, -50%)",zIndex:1e4,background:"white",padding:"20px",borderRadius:"8px",boxShadow:"0 4px 20px rgba(0,0,0,0.15)"},children:[(0,a.jsx)("div",{className:"prpl-note",children:(0,a.jsx)("span",{className:"prpl-note-text",children:(0,s.__)("Are you sure you want to delete all completed tasks? This action cannot be undone.","progress-planner")})}),(0,a.jsxs)("div",{className:"prpl-buttons-wrapper",style:{display:"flex",gap:"10px",marginTop:"15px"},children:[(0,a.jsxs)("button",{id:"todo-list-completed-delete-all-cancel",onClick:()=>w(!1),children:[(0,a.jsx)("strong",{children:(0,s.__)("No","progress-planner")}),", ",(0,s.__)("keep this list","progress-planner")]}),(0,a.jsxs)("button",{id:"todo-list-completed-delete-all-confirm",onClick:j,children:[(0,a.jsx)("strong",{children:(0,s.__)("Yes","progress-planner")}),", ",(0,s.__)("delete all completed tasks","progress-planner")]})]})]}),g&&(0,a.jsx)("div",{role:"button",tabIndex:0,"aria-label":(0,s.__)("Close dialog","progress-planner"),style:{position:"fixed",top:0,left:0,right:0,bottom:0,background:"rgba(0,0,0,0.3)",zIndex:9999},onClick:()=>w(!1),onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||w(!1)}})]})}function p(){const e=document.getElementById("prpl-todo-root");e&&(0,t.createRoot)(e).render((0,a.jsx)(d,{}))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",p):p()})();
\ No newline at end of file
+(()=>{"use strict";var e={n:t=>{var r=t&&t.__esModule?()=>t.default:()=>t;return e.d(r,{a:r}),r},d:(t,r)=>{for(var s in r)e.o(r,s)&&!e.o(t,s)&&Object.defineProperty(t,s,{enumerable:!0,get:r[s]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)};const t=window.wp.element,r=window.wp.i18n,s=window.wp.apiFetch;var n=e.n(s);const o=window.ReactJSXRuntime;async function a(e,t){return n()({path:`/wp/v2/prpl_recommendations/${e}`,method:"POST",data:t})}async function l(e){return n()({path:`/wp/v2/prpl_recommendations/${e}?force=true`,method:"DELETE"})}function i({task:e,isGolden:s,isCompleted:n,onToggle:a,onDelete:l,onMove:i,onTitleChange:d}){const p=(0,t.useRef)(null),c=(0,t.useRef)(null),u=(0,t.useCallback)(()=>{c.current&&clearTimeout(c.current),c.current=setTimeout(()=>{if(p.current){const t=p.current.textContent.replace(/\n/g,"");d(e.id,t)}},300)},[e.id,d]),h=(0,t.useCallback)(e=>{"Enter"===e.key&&(e.preventDefault(),e.target.blur())},[]);return(0,o.jsxs)("li",{className:"prpl-suggested-task"+(s?" prpl-golden-task":""),"data-task-id":e.slug||e.id,"data-post-id":e.id,"data-task-action":n?"completed":"publish","data-task-provider-id":"user","data-task-points":e.prpl_points||0,"data-task-order":e.menu_order||0,children:[(0,o.jsx)("div",{className:"prpl-suggested-task-checkbox-wrapper",children:(0,o.jsxs)("label",{children:[(0,o.jsx)("input",{type:"checkbox",className:"prpl-suggested-task-checkbox",checked:n,onChange:()=>a(e.id)}),(0,o.jsxs)("span",{className:"screen-reader-text",children:[e.title?.rendered||e.title,":"," ",(0,r.__)("Mark as completed","progress-planner")]})]})}),(0,o.jsx)("div",{className:"prpl-suggested-task-title-wrapper",children:(0,o.jsx)("h3",{className:"prpl-task-title",children:(0,o.jsx)("span",{ref:p,contentEditable:!0,suppressContentEditableWarning:!0,onInput:u,onKeyDown:h,"data-post-id":e.id,tabIndex:0,role:"textbox","aria-label":(0,r.__)("Edit task title","progress-planner"),children:e.title?.rendered||e.title})})}),(0,o.jsxs)("div",{className:"prpl-suggested-task-points-wrapper",children:[(e.prpl_points||0)>0&&(0,o.jsxs)("span",{className:"prpl-suggested-task-points",children:["+",e.prpl_points]}),(0,o.jsx)("button",{type:"button",className:"prpl-suggested-task-delete",onClick:()=>l(e.id),"aria-label":(0,r.__)("Delete task","progress-planner"),children:(0,o.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",width:"16",height:"16","aria-hidden":"true",children:(0,o.jsx)("path",{fill:"currentColor",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Z"})})})]}),!n&&(0,o.jsxs)("div",{className:"tooltip-actions prpl-move-buttons-wrapper",children:[(0,o.jsx)("button",{type:"button",className:"prpl-move-up",onClick:()=>i(e.id,"up"),"aria-label":(0,r.__)("Move up","progress-planner"),children:(0,o.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"16",height:"16","aria-hidden":"true",children:(0,o.jsx)("path",{fill:"currentColor",d:"M12 4l-8 8h6v8h4v-8h6z"})})}),(0,o.jsx)("button",{type:"button",className:"prpl-move-down",onClick:()=>i(e.id,"down"),"aria-label":(0,r.__)("Move down","progress-planner"),children:(0,o.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",width:"16",height:"16","aria-hidden":"true",children:(0,o.jsx)("path",{fill:"currentColor",d:"M12 20l8-8h-6v-8h-4v8h-6z"})})})]})]})}function d(){const[e,s]=(0,t.useState)([]),[d,p]=(0,t.useState)([]),[c,u]=(0,t.useState)(!0),[h,g]=(0,t.useState)(""),[w,m]=(0,t.useState)(!1),x=(0,t.useRef)(null);(0,t.useEffect)(()=>{const e=()=>{document.querySelectorAll(".prpl-widget-wrapper").forEach(e=>{if(!e||e.classList.contains("in-popover"))return;const t=e.querySelector(".widget-inner-container");if(!t)return;const r=document.querySelector(".prpl-widgets-container");if(!r)return;const s=parseInt(window.getComputedStyle(r).getPropertyValue("grid-auto-rows")),n=parseInt(window.getComputedStyle(e).getPropertyValue("padding-top")),o=parseInt(window.getComputedStyle(e).getPropertyValue("padding-bottom")),a=Math.ceil((t.getBoundingClientRect().height+n+o)/s);e.style.gridRowEnd="span "+(a+1)})},t=()=>{setTimeout(e,0)};return window.addEventListener("prpl/grid/resize",e),window.addEventListener("resize",t),window.addEventListener("load",t),t(),setTimeout(t,1e3),()=>{window.removeEventListener("prpl/grid/resize",e),window.removeEventListener("resize",t),window.removeEventListener("load",t)}},[]),(0,t.useEffect)(()=>{(async()=>{try{const e=await async function({status:e=["publish","trash"]}={}){const t=Array.isArray(e)?e.map(e=>`status[]=${e}`).join("&"):`status=${e}`;return n()({path:`/wp/v2/prpl_recommendations?${t}&provider=user&per_page=100&_embed=true&filter[orderby]=menu_order&filter[order]=ASC`})}(),t=e.filter(e=>"publish"===e.status),r=e.filter(e=>"trash"===e.status);t.sort((e,t)=>(e.menu_order||0)-(t.menu_order||0)),r.sort((e,t)=>(e.menu_order||0)-(t.menu_order||0)),s(t),p(r)}catch(e){console.error("Error loading tasks:",e)}finally{u(!1),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}})()},[]);const v=(0,t.useCallback)(async t=>{if(t.preventDefault(),h.trim())try{const t=e.reduce((e,t)=>Math.max(e,t.menu_order||0),0),o=await async function({title:e,order:t}){return n()({path:"/wp/v2/prpl_recommendations",method:"POST",data:{title:e,status:"publish",menu_order:t,prpl_recommendations_provider:window.prplTodoConfig?.userProviderId}})}({title:h,order:t+1});s(e=>[...e,o]),g(""),window.wp?.a11y?.speak&&window.wp.a11y.speak((0,r.__)("Task added successfully","progress-planner"),"polite"),x.current?.focus(),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error creating task:",e)}},[h,e]),k=(0,t.useCallback)(async t=>{const r=e.find(e=>e.id===t)||d.find(e=>e.id===t);if(!r)return;const n="trash"===r.status,o=n?"publish":"trash";try{await a(t,{status:o}),n?(p(e=>e.filter(e=>e.id!==t)),s(e=>[...e,{...r,status:"publish"}])):(s(e=>e.filter(e=>e.id!==t)),p(e=>[...e,{...r,status:"trash"}]),r.prpl_points>0&&("function"==typeof window.prplUpdateRaviGauge&&window.prplUpdateRaviGauge(r.prpl_points),document.dispatchEvent(new CustomEvent("prpl/celebrateTasks",{detail:{}})))),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error toggling task:",e)}},[e,d]),_=(0,t.useCallback)(async e=>{try{await l(e),s(t=>t.filter(t=>t.id!==e)),p(t=>t.filter(t=>t.id!==e)),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error deleting task:",e)}},[]),y=(0,t.useCallback)(async(t,r)=>{const n=e.findIndex(e=>e.id===t);if(-1===n)return;const o="up"===r?n-1:n+1;if(o<0||o>=e.length)return;const l=[...e];[l[n],l[o]]=[l[o],l[n]];const i=l.map((e,t)=>({...e,menu_order:t}));s(i);try{await Promise.all(i.map(e=>a(e.id,{menu_order:e.menu_order})))}catch(e){console.error("Error saving task order:",e)}window.dispatchEvent(new CustomEvent("prpl/grid/resize"))},[e]),b=(0,t.useCallback)(async(e,t)=>{try{await a(e,{title:t}),s(r=>r.map(r=>r.id===e?{...r,title:{rendered:t}}:r)),p(r=>r.map(r=>r.id===e?{...r,title:{rendered:t}}:r))}catch(e){console.error("Error updating task title:",e)}},[]),f=(0,t.useCallback)(async()=>{try{await Promise.all(d.map(e=>l(e.id))),p([]),m(!1),window.wp?.a11y?.speak&&window.wp.a11y.speak((0,r.__)("All completed tasks deleted","progress-planner"),"assertive"),window.dispatchEvent(new CustomEvent("prpl/grid/resize"))}catch(e){console.error("Error deleting completed tasks:",e)}},[d]);return c?(0,o.jsx)("p",{id:"prpl-todo-list-loading",children:(0,r.__)("Loading items…","progress-planner")}):(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)("div",{id:"todo-aria-live-region","aria-live":"polite",style:{position:"absolute",left:"-9999px"}}),(0,o.jsx)("ul",{id:"todo-list",className:"prpl-todo-list prpl-suggested-tasks-list",children:e.map((e,t)=>(0,o.jsx)(i,{task:e,isGolden:0===t&&e.prpl_points>0,isCompleted:!1,onToggle:k,onDelete:_,onMove:y,onTitleChange:b},e.id))}),(0,o.jsxs)("form",{id:"create-todo-item",onSubmit:v,children:[(0,o.jsx)("input",{ref:x,type:"text",id:"new-todo-content",placeholder:(0,r.__)("Add a new task","progress-planner"),"aria-label":(0,r.__)("Add a new task","progress-planner"),required:!0,value:h,onChange:e=>g(e.target.value)}),(0,o.jsxs)("button",{type:"submit","aria-label":(0,r.__)("Add task","progress-planner"),children:[(0,o.jsx)("span",{className:"dashicons dashicons-plus-alt2","aria-hidden":"true"}),(0,o.jsx)("span",{className:"screen-reader-text",children:(0,r.__)("Add task","progress-planner")})]})]}),d.length>0&&(0,o.jsxs)("details",{id:"todo-list-completed-details",children:[(0,o.jsxs)("summary",{children:[(0,r.__)("Completed tasks","progress-planner"),(0,o.jsx)("span",{className:"prpl-todo-list-completed-summary-icon",children:(0,o.jsx)("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:"1.5",stroke:"currentColor",children:(0,o.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"m19.5 8.25-7.5 7.5-7.5-7.5"})})})]}),(0,o.jsx)("div",{id:"todo-list-completed-delete-all-wrapper",children:(0,o.jsxs)("button",{id:"todo-list-completed-delete-all",onClick:()=>m(!0),children:[(0,o.jsx)("span",{style:{display:"inline-block",width:"18px",height:"18px"},children:(0,o.jsx)("svg",{role:"img","aria-hidden":"true",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 48 48",children:(0,o.jsx)("path",{fill:"#9ca3af",d:"M32.99 47.88H15.01c-3.46 0-6.38-2.7-6.64-6.15L6.04 11.49l-.72.12c-.82.14-1.59-.41-1.73-1.22-.14-.82.41-1.59 1.22-1.73.79-.14 1.57-.26 2.37-.38h.02c2.21-.33 4.46-.6 6.69-.81v-.72c0-3.56 2.74-6.44 6.25-6.55 2.56-.08 5.15-.08 7.71 0 3.5.11 6.25 2.99 6.25 6.55v.72c2.24.2 4.48.47 6.7.81.79.12 1.59.25 2.38.39.82.14 1.36.92 1.22 1.73-.14.82-.92 1.36-1.73 1.22l-.72-.12-2.33 30.24c-.27 3.45-3.18 6.15-6.64 6.15Z"})})}),(0,r.__)("Delete all completed tasks","progress-planner")]})}),(0,o.jsx)("ul",{id:"todo-list-completed",className:"prpl-todo-list prpl-suggested-tasks-list",children:d.map(e=>(0,o.jsx)(i,{task:e,isGolden:!1,isCompleted:!0,onToggle:k,onDelete:_,onMove:y,onTitleChange:b},e.id))})]}),w&&(0,o.jsxs)("div",{id:"todo-list-completed-delete-all-popover",className:"prpl-popover",style:{position:"fixed",top:"50%",left:"50%",transform:"translate(-50%, -50%)",zIndex:1e4,background:"white",padding:"20px",borderRadius:"8px",boxShadow:"0 4px 20px rgba(0,0,0,0.15)"},children:[(0,o.jsx)("div",{className:"prpl-note",children:(0,o.jsx)("span",{className:"prpl-note-text",children:(0,r.__)("Are you sure you want to delete all completed tasks? This action cannot be undone.","progress-planner")})}),(0,o.jsxs)("div",{className:"prpl-buttons-wrapper",style:{display:"flex",gap:"10px",marginTop:"15px"},children:[(0,o.jsxs)("button",{id:"todo-list-completed-delete-all-cancel",onClick:()=>m(!1),children:[(0,o.jsx)("strong",{children:(0,r.__)("No","progress-planner")}),", ",(0,r.__)("keep this list","progress-planner")]}),(0,o.jsxs)("button",{id:"todo-list-completed-delete-all-confirm",onClick:f,children:[(0,o.jsx)("strong",{children:(0,r.__)("Yes","progress-planner")}),", ",(0,r.__)("delete all completed tasks","progress-planner")]})]})]}),w&&(0,o.jsx)("div",{role:"button",tabIndex:0,"aria-label":(0,r.__)("Close dialog","progress-planner"),style:{position:"fixed",top:0,left:0,right:0,bottom:0,background:"rgba(0,0,0,0.3)",zIndex:9999},onClick:()=>m(!1),onKeyDown:e=>{"Enter"!==e.key&&" "!==e.key||m(!1)}})]})}function p(){const e=document.getElementById("prpl-todo-root");e&&(0,t.createRoot)(e).render((0,o.jsx)(d,{}))}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",p):p()})();
\ No newline at end of file
diff --git a/classes/admin/class-dashboard-widget-todo.php b/classes/admin/class-dashboard-widget-todo.php
index 7f8c08c76c..7f9916c0b6 100644
--- a/classes/admin/class-dashboard-widget-todo.php
+++ b/classes/admin/class-dashboard-widget-todo.php
@@ -45,8 +45,6 @@ public function render_widget() {
}
\progress_planner()->the_view( "dashboard-widgets/{$this->id}.php" );
-
- \progress_planner()->the_view( 'js-templates/suggested-task.html' );
}
}
// phpcs:enable Generic.Commenting.Todo
diff --git a/classes/admin/class-enqueue.php b/classes/admin/class-enqueue.php
index 9b48aeff19..b83d3b29f7 100644
--- a/classes/admin/class-enqueue.php
+++ b/classes/admin/class-enqueue.php
@@ -206,74 +206,6 @@ public function localize_script( $handle, $localize_data = [] ) {
];
break;
- case 'progress-planner/suggested-task':
- // Celebrate only on the Progress Planner Dashboard page.
- $delay_celebration = true;
- if ( \progress_planner()->is_on_progress_planner_dashboard_page() ) {
- // should_show_upgrade_popover() also checks if we're on the Progress Planner Dashboard page - but let's be explicit since that method might change in the future.
- $delay_celebration = \progress_planner()->get_plugin_upgrade_tasks()->should_show_upgrade_popover();
- }
-
- // Get the providers available for the user.
- $include_providers = [];
- $providers_available_for_user = \progress_planner()->get_suggested_tasks()->get_tasks_manager()->get_task_providers_available_for_user();
- foreach ( $providers_available_for_user as $provider ) {
- // Skip user provider.
- if ( 'user' === $provider->get_provider_id() ) {
- continue;
- }
- $include_providers[] = $provider->get_provider_id();
- }
-
- // Check if user wants to see all recommendations.
- $show_all_recommendations = isset( $_GET['prpl_show_all_recommendations'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
- $tasks_per_page = $show_all_recommendations ? -1 : \Progress_Planner\Admin\Widgets\Suggested_Tasks::PER_PAGE_DEFAULT;
-
- // Get tasks from task providers (limited to 5 by default, or unlimited if showing all).
- $tasks = \progress_planner()->get_suggested_tasks()->get_tasks_in_rest_format(
- [
- 'post_status' => 'publish',
- 'posts_per_page' => $tasks_per_page,
- 'include_provider' => $include_providers, // User provider is already excluded.
- ]
- );
- // Get pending celebration tasks.
- $pending_celebration_tasks = \progress_planner()->get_suggested_tasks()->get_tasks_in_rest_format(
- [
- 'post_status' => 'pending',
- 'posts_per_page' => 100,
- 'include_provider' => $include_providers, // User provider is already excluded.
- ]
- );
-
- // Get user tasks.
- $user_tasks = \progress_planner()->get_suggested_tasks()->get_tasks_in_rest_format(
- [
- 'post_status' => [ 'publish', 'trash' ],
- 'include_provider' => [ 'user' ],
- ]
- );
-
- $localize_data = [
- 'name' => 'prplSuggestedTask',
- 'data' => [
- 'nonce' => \wp_create_nonce( 'progress_planner' ),
- 'assets' => [
- 'infoIcon' => \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_info.svg',
- 'snoozeIcon' => \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_snooze.svg',
- ],
- 'tasks' => [
- 'pendingTasks' => $tasks,
- 'pendingCelebrationTasks' => $pending_celebration_tasks,
- 'userTasks' => $user_tasks,
- ],
- 'delayCelebration' => $delay_celebration,
- 'tasksPerPage' => $tasks_per_page,
- 'perPageDefault' => \Progress_Planner\Admin\Widgets\Suggested_Tasks::PER_PAGE_DEFAULT,
- ],
- ];
- break;
-
case 'progress-planner/celebrate':
// Check if current date is between Feb 12-16 to use hearts confetti.
$confetti_options = [];
diff --git a/classes/admin/class-page.php b/classes/admin/class-page.php
index e2b662e7e2..0ebe2ac9ce 100644
--- a/classes/admin/class-page.php
+++ b/classes/admin/class-page.php
@@ -196,7 +196,6 @@ public function enqueue_scripts() {
\progress_planner()->get_admin__enqueue()->enqueue_script( 'web-components/prpl-tooltip' );
\progress_planner()->get_admin__enqueue()->enqueue_script( 'header-filters', $default_localization_data );
\progress_planner()->get_admin__enqueue()->enqueue_script( 'settings', $default_localization_data );
- \progress_planner()->get_admin__enqueue()->enqueue_script( 'grid-masonry' );
\progress_planner()->get_admin__enqueue()->enqueue_script( 'upgrade-tasks' );
} else {
\progress_planner()->get_admin__enqueue()->enqueue_script( 'onboard', $default_localization_data );
diff --git a/classes/admin/widgets/class-todo.php b/classes/admin/widgets/class-todo.php
index 1f84a18ab8..d7925989ca 100644
--- a/classes/admin/widgets/class-todo.php
+++ b/classes/admin/widgets/class-todo.php
@@ -73,18 +73,6 @@ public function print_content() {
echo '
{ __( 'No badge data available.', 'progress-planner' ) }
; } + // Inline styles. + const progressWrapperStyle = { + display: 'grid', + gridTemplateColumns: '1fr 1fr 1fr', + gap: 'calc(var(--prpl-gap) / 4)', + padding: 'calc(var(--prpl-padding) / 2)', + borderRadius: 'var(--prpl-border-radius-big)', + background: 'var(--prpl-background-content-badge)', + }; + + const badgeStyle = { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'flex-start', + flexWrap: 'wrap', + minWidth: 0, + }; + + const badgeLabelStyle = { + margin: 0, + fontSize: 'var(--prpl-font-size-small)', + textAlign: 'center', + lineHeight: 1.2, + }; + return ( <>@@ -138,11 +164,15 @@ export default function ContentBadges() {
{ badge.name }
+{ badge.name }
) ) }{ badge.name }
++ { badge.name } +
) ) }