diff --git a/assets/css/settings-page.css b/assets/css/settings-page.css
index 70d431ed6..44ed96d48 100644
--- a/assets/css/settings-page.css
+++ b/assets/css/settings-page.css
@@ -348,3 +348,69 @@
}
}
}
+
+.prpl-api-status-wrapper {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ .prpl-api-status-controls {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ }
+
+ .prpl-api-status-response-wrapper {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+
+ svg {
+ width: 1rem;
+ height: 1rem;
+ }
+
+ .prpl-api-status-icon-ok,
+ .prpl-api-status-icon-error,
+ .prpl-api-status-icon-spinner,
+ .prpl-api-status-text {
+ display: none;
+ }
+
+ &.prpl-api-status-checking {
+
+ .prpl-api-status-icon-spinner {
+ display: inline-block;
+ }
+ }
+
+ &.prpl-api-status-ok {
+
+ .prpl-api-status-icon-spinner {
+ display: none;
+ }
+
+ .prpl-api-status-icon-ok,
+ .prpl-api-status-text {
+ display: inline-block;
+ }
+ }
+
+ &.prpl-api-status-error {
+
+ .prpl-api-status-icon-spinner {
+ display: none;
+ }
+
+ .prpl-api-status-icon-error,
+ .prpl-api-status-text {
+ display: inline-block;
+ }
+ }
+ }
+
+ input {
+ width: 20rem;
+ max-width: calc(100% - 2rem);
+ }
+}
diff --git a/assets/images/icon_server.svg b/assets/images/icon_server.svg
new file mode 100644
index 000000000..332eb1c9c
--- /dev/null
+++ b/assets/images/icon_server.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/js/ajax-request.js b/assets/js/ajax-request.js
index c7c510949..56e3a9a94 100644
--- a/assets/js/ajax-request.js
+++ b/assets/js/ajax-request.js
@@ -14,12 +14,14 @@ const progressPlannerAjaxRequest = ( { url, data } ) => {
http.open( 'POST', url, true );
http.onreadystatechange = () => {
let response;
+
try {
response = JSON.parse( http.response );
} catch ( e ) {
if ( http.readyState === 4 && http.status !== 200 ) {
console.warn( http, e );
- return http.response;
+ // Reject the promise with the response.
+ reject( response );
}
}
diff --git a/assets/js/settings-page.js b/assets/js/settings-page.js
index d7a813486..69edc3eb3 100644
--- a/assets/js/settings-page.js
+++ b/assets/js/settings-page.js
@@ -1,10 +1,10 @@
-/* global alert, prplDocumentReady */
+/* global alert, prplDocumentReady, progressPlannerSettingsPage, progressPlannerAjaxRequest, prplL10n */
/*
* Settings Page
*
* A script to handle the settings page.
*
- * Dependencies: progress-planner/document-ready, wp-util
+ * Dependencies: progress-planner/document-ready, wp-util, progress-planner/ajax-request, progress-planner/l10n
*/
const prplTogglePageSelectorSettingVisibility = function ( page, value ) {
const itemRadiosWrapperEl = document.querySelector(
@@ -92,3 +92,108 @@ prplDocumentReady( function () {
.getElementById( 'prpl-settings' )
.addEventListener( 'submit', prplFormSubmit );
} );
+
+/**
+ * API Status Manager
+ * Handles the display and state of the API status check
+ */
+class APIStatusManager {
+ /**
+ * @param {string} wrapperSelector - The selector for the status wrapper element
+ */
+ constructor( wrapperSelector ) {
+ this.wrapper = document.querySelector( wrapperSelector );
+ }
+
+ /**
+ * Update the status text content
+ * @param {string} text - The text to display
+ */
+ updateStatusText( text ) {
+ const textElement = this.wrapper?.querySelector(
+ '.prpl-api-status-text'
+ );
+ if ( textElement ) {
+ textElement.textContent = text;
+ }
+ }
+
+ /**
+ * Update the status classes
+ * @param {string} status - The status class to add ('ok', 'error', 'checking')
+ */
+ updateStatusClasses( status ) {
+ if ( ! this.wrapper ) {
+ return;
+ }
+
+ this.wrapper.classList.remove(
+ 'prpl-api-status-ok',
+ 'prpl-api-status-error',
+ 'prpl-api-status-checking'
+ );
+ this.wrapper.classList.add( `prpl-api-status-${ status }` );
+ }
+
+ /**
+ * Handle API response
+ * @param {Object} response - The API response
+ */
+ handleResponse( response ) {
+ if ( response.status === 'ok' && response.nonce ) {
+ this.updateStatusText( prplL10n( 'remoteAPIStatusOk' ) );
+ this.updateStatusClasses( 'ok' );
+ } else {
+ this.updateStatusText(
+ response?.message || prplL10n( 'remoteAPIStatusError' )
+ );
+ this.updateStatusClasses( 'error' );
+ }
+ }
+
+ /**
+ * Handle API error
+ * @param {Error} error - The error object
+ */
+ handleError( error ) {
+ this.updateStatusText(
+ error?.message || prplL10n( 'remoteAPIStatusError' )
+ );
+ this.updateStatusClasses( 'error' );
+ }
+
+ /**
+ * Check the API status
+ */
+ checkStatus() {
+ if ( ! this.wrapper ) {
+ return;
+ }
+
+ this.updateStatusClasses( 'checking' );
+
+ progressPlannerAjaxRequest( {
+ url: progressPlannerSettingsPage.onboardNonceURL,
+ data: {
+ site: progressPlannerSettingsPage.siteUrl,
+ },
+ } )
+ .then( ( response ) => this.handleResponse( response ) )
+ .catch( ( error ) => this.handleError( error ) );
+ }
+}
+
+// Add click handler for API status check button
+prplDocumentReady( () => {
+ // Initialize the API status manager
+ const apiStatusManager = new APIStatusManager(
+ '.prpl-api-status-response-wrapper'
+ );
+
+ const statusButton = document.getElementById( 'prpl-setting-api-status' );
+ if ( statusButton ) {
+ statusButton.addEventListener( 'click', () =>
+ apiStatusManager.checkStatus()
+ );
+ }
+} );
diff --git a/classes/admin/class-enqueue.php b/classes/admin/class-enqueue.php
index 9b48aeff1..c27912bfa 100644
--- a/classes/admin/class-enqueue.php
+++ b/classes/admin/class-enqueue.php
@@ -388,6 +388,8 @@ public function get_localized_strings() {
'video' => \esc_html__( 'Video', 'progress-planner' ),
'watchVideo' => \esc_html__( 'Watch video', 'progress-planner' ),
'disabledRRCheckboxTooltip' => \esc_html__( 'Don\'t worry! This task will be checked off automatically when you\'ve completed it.', 'progress-planner' ),
+ 'remoteAPIStatusOk' => \esc_html__( 'API is accessible', 'progress-planner' ),
+ 'remoteAPIStatusError' => \esc_html__( 'API is not accessible', 'progress-planner' ),
'opensInNewWindow' => \esc_html__( 'Opens in new window', 'progress-planner' ),
'whyIsThisImportant' => \esc_html__( 'Why is this important?', 'progress-planner' ),
/* translators: %s: The plugin name. */
diff --git a/classes/admin/class-page.php b/classes/admin/class-page.php
index e2b662e7e..c9f8960d6 100644
--- a/classes/admin/class-page.php
+++ b/classes/admin/class-page.php
@@ -211,7 +211,10 @@ public function enqueue_scripts() {
[
'name' => 'progressPlannerSettingsPage',
'data' => [
- 'siteUrl' => \get_site_url(),
+ 'siteUrl' => \get_site_url(),
+ 'onboardNonceURL' => \progress_planner()->get_utils__onboard()->get_remote_nonce_url(),
+ 'onboardAPIUrl' => \progress_planner()->get_utils__onboard()->get_remote_url(),
+ 'ajaxUrl' => \admin_url( 'admin-ajax.php' ),
],
]
);
diff --git a/classes/suggested-tasks/providers/class-settings-saved.php b/classes/suggested-tasks/providers/class-settings-saved.php
index 5fe62f14c..85d5ecd52 100644
--- a/classes/suggested-tasks/providers/class-settings-saved.php
+++ b/classes/suggested-tasks/providers/class-settings-saved.php
@@ -85,4 +85,14 @@ public function add_task_actions( $data = [], $actions = [] ) {
return $actions;
}
+
+ /**
+ * Check if the task is completed.
+ *
+ * @param string $task_id Optional task ID to check completion for.
+ * @return bool
+ */
+ public function is_task_completed( $task_id = '' ) {
+ return false !== \get_option( 'progress_planner_pro_license_key', false );
+ }
}
diff --git a/views/admin-page-settings.php b/views/admin-page-settings.php
index 1f7fe5bed..d8ed52066 100644
--- a/views/admin-page-settings.php
+++ b/views/admin-page-settings.php
@@ -33,6 +33,8 @@
the_view( 'page-settings/settings.php' ); ?>
+ the_view( 'page-settings/api-status.php' ); ?>
+