diff --git a/assets/css/check-email-dns-records.css b/assets/css/check-email-dns-records.css new file mode 100644 index 000000000..1324e0273 --- /dev/null +++ b/assets/css/check-email-dns-records.css @@ -0,0 +1,190 @@ +/** + * Check Email DNS Records Styles + * + * Styles for email DNS check task execution and display. + */ + +/* Email DNS Check Popover */ +.prpl-popover-email-dns { + max-width: 600px; +} + +.prpl-email-dns-content { + min-height: 100px; +} + +/* Header */ +.prpl-popover-email-dns .prpl-popover-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 15px 20px; + border-bottom: 1px solid #e5e7eb; +} + +.prpl-popover-email-dns .prpl-popover-title { + margin: 0; + font-size: 18px; + font-weight: 600; + color: #1f2937; +} + +.prpl-popover-email-dns .prpl-popover-close { + background: none; + border: none; + cursor: pointer; + padding: 5px; + color: #6b7280; + transition: color 0.2s; +} + +.prpl-popover-email-dns .prpl-popover-close:hover { + color: #1f2937; +} + +/* Content */ +.prpl-popover-email-dns .prpl-popover-content { + padding: 20px; +} + +/* Instructions */ +.prpl-email-dns-instructions p { + margin: 0 0 10px 0; + line-height: 1.6; +} + +.prpl-email-dns-instructions p:last-child { + margin-bottom: 0; +} + +/* Loading State */ +.prpl-email-dns-loading { + text-align: center; + padding: 30px 20px; +} + +.prpl-email-dns-loading .prpl-spinner { + margin: 0 auto 20px; + width: 40px; + height: 40px; + border: 4px solid #f3f4f6; + border-top-color: #534786; + border-radius: 50%; + animation: prpl-email-dns-spin 1s linear infinite; +} + +@keyframes prpl-email-dns-spin { + + to { + transform: rotate(360deg); + } +} + +.prpl-email-dns-loading p { + color: #666; + font-size: 14px; + margin: 0; +} + +/* Result State */ +.prpl-email-dns-result { + padding: 20px; + background: #f9fafb; + border-radius: 6px; + margin-bottom: 20px; +} + +.prpl-email-dns-response { + line-height: 1.6; + color: #1f2937; +} + +/* DNS Results Styling */ +.prpl-dns-results { + font-size: 14px; +} + +.prpl-spam-score { + margin: 0 0 15px 0; + padding: 10px; + border-radius: 4px; +} + +.prpl-spam-score.prpl-score-good { + background-color: #d1fae5; + color: #065f46; +} + +.prpl-spam-score.prpl-score-warning { + background-color: #fef3c7; + color: #92400e; +} + +.prpl-spam-score.prpl-score-bad { + background-color: #fee2e2; + color: #991b1b; +} + +.prpl-dns-records-list { + list-style: none; + margin: 0; + padding: 0; +} + +.prpl-dns-record { + padding: 8px 0; + border-bottom: 1px solid #e5e7eb; + display: flex; + align-items: center; + gap: 8px; +} + +.prpl-dns-record:last-child { + border-bottom: none; +} + +.prpl-status-icon { + font-weight: 700; + font-size: 16px; +} + +.prpl-dns-record.prpl-status-pass .prpl-status-icon { + color: #059669; +} + +.prpl-dns-record.prpl-status-fail .prpl-status-icon { + color: #dc2626; +} + +/* Error State */ +.prpl-email-dns-error { + padding: 15px; + background: #fef2f2; + border: 1px solid #fecaca; + border-radius: 6px; + margin-bottom: 20px; +} + +.prpl-email-dns-error .prpl-error-message { + color: #dc2626; + margin: 0; + font-size: 14px; +} + +/* Buttons */ +.prpl-email-dns-check, +.prpl-email-dns-retry { + min-width: 120px; +} + +/* Responsive */ +@media (max-width: 768px) { + + .prpl-popover-email-dns { + max-width: 90vw; + } + + .prpl-email-dns-result { + padding: 15px; + } +} diff --git a/assets/js/recommendations/check-email-dns-records.js b/assets/js/recommendations/check-email-dns-records.js new file mode 100644 index 000000000..8b44b6795 --- /dev/null +++ b/assets/js/recommendations/check-email-dns-records.js @@ -0,0 +1,294 @@ +/* global progressPlannerAjaxRequest, progressPlanner */ + +/** + * Check Email DNS Records Handler + * + * Handles execution of email DNS check tasks with state management. + * + * Dependencies: progress-planner/suggested-task, progress-planner/ajax-request + */ + +( () => { + /** + * Handle email DNS check execution. + */ + const handleEmailDNSCheck = () => { + const popover = document.getElementById( + 'prpl-popover-check-email-dns-records' + ); + if ( ! popover ) { + return; + } + + const checkButton = popover.querySelector( '.prpl-email-dns-check' ); + const retryButton = popover.querySelector( '.prpl-email-dns-retry' ); + const checkReportAgainButton = popover.querySelector( + '.prpl-email-dns-check-report-again' + ); + const completeButton = popover.querySelector( + '.prpl-email-dns-complete' + ); + const instructionsEl = popover.querySelector( + '.prpl-email-dns-instructions' + ); + const loadingEl = popover.querySelector( '.prpl-email-dns-loading' ); + const resultEl = popover.querySelector( '.prpl-email-dns-result' ); + const responseEl = popover.querySelector( '.prpl-email-dns-response' ); + const errorEl = popover.querySelector( '.prpl-email-dns-error' ); + const errorMessageEl = popover.querySelector( '.prpl-error-message' ); + + // Define all elements in one place for easy maintenance. + const elements = { + checkButton, + retryButton, + checkReportAgainButton, + completeButton, + instructionsEl, + loadingEl, + resultEl, + responseEl, + errorEl, + errorMessageEl, + }; + + // State configuration: define visibility for each element in each state. + const states = { + loading: { + checkButton: 'none', + retryButton: 'none', + checkReportAgainButton: 'none', + completeButton: 'none', + instructionsEl: 'none', + loadingEl: 'block', + resultEl: 'none', + errorEl: 'none', + }, + pending: { + instructionsEl: 'none', + loadingEl: 'none', + checkButton: 'none', + retryButton: 'none', + checkReportAgainButton: 'inline-block', + completeButton: 'none', + resultEl: 'none', + errorEl: 'block', + }, + result: { + instructionsEl: 'none', + loadingEl: 'none', + checkButton: 'none', + retryButton: 'none', + checkReportAgainButton: 'none', + errorEl: 'none', + resultEl: 'block', + completeButton: 'inline-block', + }, + error: { + instructionsEl: 'none', + loadingEl: 'none', + checkButton: 'none', + retryButton: 'inline-block', + checkReportAgainButton: 'none', + completeButton: 'none', + resultEl: 'none', + errorEl: 'block', + }, + }; + + /** + * Set the UI state. + * + * @param {string} stateName - The state name ('loading', 'result', 'error', or 'pending'). + * @param {Object} options - Additional options for the state. + * @param {string} options.responseHtml - HTML content for responseEl (result state only). + * @param {string} options.message - Error message for errorMessageEl (error and pending states). + */ + const setUIState = ( stateName, options = {} ) => { + const state = states[ stateName ]; + if ( ! state ) { + console.warn( `Unknown state: ${ stateName }` ); + return; + } + + // Apply visibility for each element in the state. + Object.entries( state ).forEach( ( [ key, display ] ) => { + const element = elements[ key ]; + if ( element ) { + element.style.display = display; + } + } ); + + // Handle special cases. + if ( + stateName === 'result' && + options.responseHtml && + elements.responseEl + ) { + elements.responseEl.innerHTML = options.responseHtml; + } + + if ( + ( stateName === 'error' || stateName === 'pending' ) && + options.message && + elements.errorMessageEl + ) { + elements.errorMessageEl.textContent = options.message; + } + }; + + /** + * Show loading state. + */ + const showLoading = () => { + setUIState( 'loading' ); + }; + + /** + * Show result state. + * + * @param {string} responseHtml - The formatted HTML response. + */ + const showResult = ( responseHtml ) => { + setUIState( 'result', { responseHtml } ); + }; + + /** + * Show error state. + * + * @param {string} message - The error message. + */ + const showError = ( message ) => { + setUIState( 'error', { message } ); + }; + + /** + * Show pending state (report is still being processed). + * + * @param {string} message - The message to display. + */ + const showPending = ( message ) => { + setUIState( 'pending', { message } ); + }; + + /** + * Execute the email DNS check. + */ + const executeCheck = () => { + showLoading(); + + // Make AJAX request to check email DNS records. + progressPlannerAjaxRequest( { + url: progressPlanner.ajaxUrl, + data: { + action: 'prpl_interactive_task_submit_check-email-dns-records', + nonce: progressPlanner.nonce, + }, + } ) + .then( ( response ) => { + if ( ! response.success ) { + const errorMessage = + response.data?.message || + 'Failed to check email DNS records. Please try again.'; + showError( errorMessage ); + return; + } + + const data = response.data; + + // Check if the report is still being processed. + if ( data.status === 'pending' ) { + showPending( + data.message || + 'Report is still being processed. Please check again in a moment.' + ); + return; + } + + const responseHtml = + data.response_html || + '
Check completed successfully.
'; + + showResult( responseHtml ); + } ) + .catch( ( error ) => { + console.error( 'Email DNS check error:', error ); + showError( + 'An error occurred while checking your email DNS records. Please try again.' + ); + } ); + }; + + /** + * Check if the report is ready (without sending a new email). + */ + const checkReportAgain = () => { + showLoading(); + + // Make AJAX request to check if report is ready. + progressPlannerAjaxRequest( { + url: progressPlanner.ajaxUrl, + data: { + action: 'prpl_check_email_dns_report_again', + nonce: progressPlanner.nonce, + }, + } ) + .then( ( response ) => { + if ( ! response.success ) { + const errorMessage = + response.data?.message || + 'Failed to check email DNS records. Please try again.'; + showError( errorMessage ); + return; + } + + const data = response.data; + + // Check if the report is still being processed. + if ( data.status === 'pending' ) { + showPending( + data.message || + 'Report is still being processed. Please check again in a moment.' + ); + return; + } + + const responseHtml = + data.response_html || + 'Check completed successfully.
'; + + showResult( responseHtml ); + } ) + .catch( ( error ) => { + console.error( 'Email DNS check error:', error ); + showError( + 'An error occurred while checking your email DNS records. Please try again.' + ); + } ); + }; + + // Check button click handler. + if ( checkButton ) { + checkButton.addEventListener( 'click', executeCheck ); + } + + // Retry button click handler. + if ( retryButton ) { + retryButton.addEventListener( 'click', executeCheck ); + } + + // Check report again button click handler. + if ( checkReportAgainButton ) { + checkReportAgainButton.addEventListener( + 'click', + checkReportAgain + ); + } + }; + + // Initialize when DOM is ready. + if ( document.readyState === 'loading' ) { + document.addEventListener( 'DOMContentLoaded', handleEmailDNSCheck ); + } else { + handleEmailDNSCheck(); + } +} )(); diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index d20164df9..5c251e7a9 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -36,6 +36,7 @@ use Progress_Planner\Suggested_Tasks\Providers\Unpublished_Content; use Progress_Planner\Suggested_Tasks\Providers\Collaborator; use Progress_Planner\Suggested_Tasks\Providers\Select_Timezone; +use Progress_Planner\Suggested_Tasks\Providers\Check_Email_DNS_Records; use Progress_Planner\Suggested_Tasks\Providers\Set_Date_Format; use Progress_Planner\Suggested_Tasks\Providers\SEO_Plugin; use Progress_Planner\Suggested_Tasks\Providers\Improve_Pdf_Handling; @@ -84,6 +85,7 @@ public function __construct() { new Unpublished_Content(), new Collaborator(), new Select_Timezone(), + new Check_Email_DNS_Records(), new Set_Date_Format(), new SEO_Plugin(), new Improve_Pdf_Handling(), diff --git a/classes/suggested-tasks/providers/class-check-email-dns-records.php b/classes/suggested-tasks/providers/class-check-email-dns-records.php new file mode 100644 index 000000000..e3375a004 --- /dev/null +++ b/classes/suggested-tasks/providers/class-check-email-dns-records.php @@ -0,0 +1,440 @@ +get_activities__query()->query_activities( + [ + 'category' => 'suggested_task', + 'data_id' => static::PROVIDER_ID, + ] + ); + + return ! $email_dns_records_activity; + } + + /** + * Print popover form contents. + * Required by Tasks_Interactive, but we override add_popover() completely. + * + * @return void + */ + public function print_popover_form_contents() { + // Not used - we use a custom popover view. + } + + /** + * The popover content, WIP until get_file bug is fixed. + * + * @return void + */ + public function the_popover_content() { + \progress_planner()->the_view( + 'popovers/' . static::POPOVER_ID . '.php', + [ + 'prpl_popover_id' => static::POPOVER_ID, + 'prpl_provider_id' => $this->get_provider_id(), + ] + ); + } + + /** + * Add the popover for email DNS check task. + * Overrides parent to use custom popover view with state management. + * + * @return void + */ + public function add_popover() { + // Don't add the popover if the task is not published. + if ( ! $this->is_task_published() ) { + return; + } + ?> +