Fix JavaScript error in dashboard modal

- Changed $steps variable from const to let to allow reassignment
- Added new modal styling and AJAX form handlers
- Fixed campaign form JavaScript

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ruben Ramirez 2025-03-07 07:34:04 -06:00
parent c11bc80f75
commit e251e7fe24
4 changed files with 1078 additions and 27 deletions

344
css/modal.css Normal file
View file

@ -0,0 +1,344 @@
/* Modal Styles */
.mf-floating-action-button {
position: fixed;
bottom: 30px;
right: 30px;
width: 60px;
height: 60px;
background-color: #0073aa;
border-radius: 50%;
text-align: center;
line-height: 60px;
font-size: 24px;
color: white;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
z-index: 1000;
transition: transform 0.3s, background-color 0.3s;
}
.mf-floating-action-button:hover {
transform: scale(1.1);
background-color: #005d87;
}
.mf-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(8px);
z-index: 1001;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
}
.mf-modal-overlay.active {
opacity: 1;
visibility: visible;
}
.mf-modal-container {
background: white;
width: 90%;
max-width: 700px;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
position: relative;
overflow: hidden;
max-height: 90vh;
display: flex;
flex-direction: column;
}
.mf-modal-header {
padding: 20px;
background-color: #f8f8f8;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.mf-modal-header h2 {
margin: 0;
font-size: 1.4rem;
color: #333;
}
.mf-modal-close {
background: none;
border: none;
font-size: 24px;
color: #888;
cursor: pointer;
transition: color 0.3s;
}
.mf-modal-close:hover {
color: #333;
}
.mf-modal-body {
padding: 20px;
overflow-y: auto;
flex: 1;
}
.mf-modal-footer {
padding: 15px 20px;
background-color: #f8f8f8;
border-top: 1px solid #eee;
display: flex;
justify-content: space-between;
}
.mf-modal-footer button {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
border: none;
font-weight: 500;
}
.mf-back-btn {
background-color: #f0f0f0;
color: #555;
}
.mf-next-btn {
background-color: #0073aa;
color: white;
}
.mf-back-btn:hover {
background-color: #e0e0e0;
}
.mf-next-btn:hover {
background-color: #005d87;
}
.mf-next-btn.disabled {
background-color: #ccc;
cursor: not-allowed;
}
/* Step indicator styles */
.mf-steps-indicator {
display: flex;
justify-content: center;
margin: 0 0 20px;
}
.mf-step-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #ddd;
margin: 0 5px;
transition: background-color 0.3s;
}
.mf-step-dot.active {
background-color: #0073aa;
}
.mf-step-dot.completed {
background-color: #4CAF50;
}
/* Multi-step form styles */
.mf-step {
display: none;
}
.mf-step.active {
display: block;
}
.mf-step h3 {
margin-top: 0;
color: #333;
}
.mf-form-field {
margin-bottom: 20px;
}
.mf-form-field label {
display: block;
margin-bottom: 8px;
font-weight: 500;
}
.mf-form-field input,
.mf-form-field select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 16px;
}
.mf-form-field input:focus,
.mf-form-field select:focus {
border-color: #0073aa;
outline: none;
box-shadow: 0 0 0 1px #0073aa;
}
.mf-selection-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
.mf-selection-button {
flex: 1;
padding: 15px;
border: 2px solid #ddd;
border-radius: 6px;
background-color: #f8f8f8;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.mf-selection-button:hover {
border-color: #0073aa;
background-color: #f0f7fb;
}
.mf-selection-button.selected {
border-color: #0073aa;
background-color: #e6f3fa;
}
.mf-selection-button i {
display: block;
font-size: 24px;
margin-bottom: 10px;
color: #555;
}
.mf-selection-button.selected i {
color: #0073aa;
}
.mf-validation-error {
color: #d32f2f;
font-size: 14px;
margin-top: 5px;
display: none;
}
.mf-loading {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.mf-loading-spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #0073aa;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin-right: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.mf-success-message,
.mf-error-message {
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.mf-success-message {
background-color: #e8f5e9;
border: 1px solid #c8e6c9;
color: #2e7d32;
}
.mf-error-message {
background-color: #ffebee;
border: 1px solid #ffcdd2;
color: #c62828;
}
.mf-mx-option {
border: 1px solid #ddd;
border-radius: 4px;
padding: 15px;
margin-bottom: 10px;
cursor: pointer;
transition: all 0.3s;
}
.mf-mx-option:hover {
border-color: #0073aa;
background-color: #f0f7fb;
}
.mf-mx-option.selected {
border-color: #0073aa;
background-color: #e6f3fa;
}
.mf-email-list {
margin-top: 20px;
}
.mf-email-row {
display: flex;
margin-bottom: 10px;
gap: 10px;
}
.mf-email-row input {
flex: 1;
}
.mf-add-email-btn {
background-color: #0073aa;
color: white;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
margin-top: 10px;
}
.mf-add-email-btn:hover {
background-color: #005d87;
}
.mf-remove-email-btn {
background-color: #f44336;
color: white;
border: none;
border-radius: 4px;
padding: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.mf-remove-email-btn:hover {
background-color: #d32f2f;
}

View file

@ -356,32 +356,6 @@ add_action('wp_ajax_rl_mailwarmer_create_dns_backup', function () {
}
});
add_action('wp_ajax_rl_mailwarmer_modify_email_account', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'modify_email_account_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Prepare arguments for modify_email_account
$args = [
'action' => sanitize_text_field($_POST['action_type']),
'email' => sanitize_email($_POST['email_address']),
'metadata' => [
'mail_password' => sanitize_text_field($_POST['mail_password']),
'email_provider' => intval($_POST['email_provider']),
'imap_server' => sanitize_text_field($_POST['imap_server']),
],
];
// Call modify_email_account
try {
$result = RL_MailWarmer_Email_Helper::modify_email_account($args);
wp_send_json_success('Email account successfully modified. Post ID: ' . $result);
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});
// CAMPAIGN MANAGEMENT AJAX ENDPOINTS
/**
@ -655,6 +629,32 @@ add_action('wp_ajax_rl_mailwarmer_delete_email_account', function () {
wp_send_json_success(['message' => __('Email account deleted successfully', 'rl-mailwarmer')]);
});
add_action('wp_ajax_rl_mailwarmer_modify_email_account', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'modify_email_account_nonce')) {
wp_send_json_error(__('Invalid nonce', 'rl-mailwarmer'));
}
// Prepare arguments for modify_email_account
$args = [
'action' => sanitize_text_field($_POST['action_type']),
'email' => sanitize_email($_POST['email_address']),
'metadata' => [
'mail_password' => sanitize_text_field($_POST['mail_password']),
'email_provider' => intval($_POST['email_provider']),
'imap_server' => sanitize_text_field($_POST['imap_server']),
],
];
// Call modify_email_account
try {
$result = RL_MailWarmer_Email_Helper::modify_email_account($args);
wp_send_json_success('Email account successfully modified. Post ID: ' . $result);
} catch (Exception $e) {
wp_send_json_error($e->getMessage());
}
});
add_action('wp_ajax_rl_mailwarmer_check_mail_login', function () {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'check_mail_login_nonce')) {
@ -677,3 +677,226 @@ add_action('wp_ajax_rl_mailwarmer_check_mail_login', function () {
wp_send_json_error($e->getMessage());
}
});
/**
* Dashboard Modal AJAX handlers
*/
/**
* Validate domain for adding to Mailferno
*/
add_action('wp_ajax_validate_domain', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
// Get and validate domain
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
if (empty($domain)) {
wp_send_json_error(['message' => __('Domain name is required', 'rl-mailwarmer')]);
}
// Check if domain already exists
$existing_domain = get_page_by_title($domain, OBJECT, 'domain');
if ($existing_domain) {
wp_send_json_error(['message' => __('Domain already exists in your account', 'rl-mailwarmer')]);
return;
}
// Check if domain is valid (DNS resolves)
if (!checkdnsrr($domain, 'A') && !checkdnsrr($domain, 'AAAA')) {
wp_send_json_error(['message' => __('Domain does not resolve to a valid IP address', 'rl-mailwarmer')]);
return;
}
// Check if domain uses CloudFlare
$uses_cloudflare = false;
// Get the NS records for the domain
$ns_records = dns_get_record($domain, DNS_NS);
// If no NS records found directly, try with www
if (empty($ns_records)) {
$ns_records = dns_get_record('www.' . $domain, DNS_NS);
}
// Check if any NS record contains cloudflare
if (!empty($ns_records)) {
foreach ($ns_records as $record) {
if (isset($record['target']) && stripos($record['target'], 'cloudflare') !== false) {
$uses_cloudflare = true;
break;
}
}
}
wp_send_json_success([
'isValid' => true,
'usesCloudFlare' => $uses_cloudflare
]);
});
/**
* Verify CloudFlare credentials and check MX records
*/
add_action('wp_ajax_verify_cloudflare', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
$domain = isset($_POST['domain']) ? sanitize_text_field($_POST['domain']) : '';
$email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
$key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
if (empty($domain) || empty($email) || empty($key)) {
wp_send_json_error(['message' => __('Missing required fields', 'rl-mailwarmer')]);
return;
}
// Use existing cloudflare helper to verify credentials and get zone ID
try {
$client = new \GuzzleHttp\Client([
'base_uri' => 'https://api.cloudflare.com/client/v4/',
'headers' => [
'Content-Type' => 'application/json',
],
]);
$response = $client->get('zones', [
'headers' => [
'Authorization' => "Bearer {$key}",
'Content-Type' => 'application/json',
],
'query' => ['name' => $domain],
]);
$data = json_decode($response->getBody()->getContents(), true);
if (!isset($data['result'][0]['id'])) {
wp_send_json_error(['message' => __('Domain not found in your CloudFlare account', 'rl-mailwarmer')]);
return;
}
$zone_id = $data['result'][0]['id'];
// Check if MX records exist
$has_mx = false;
// Query CloudFlare for MX records
$mx_response = $client->get("zones/{$zone_id}/dns_records", [
'headers' => [
'Authorization' => "Bearer {$key}",
'Content-Type' => 'application/json',
],
'query' => ['type' => 'MX'],
]);
$mx_data = json_decode($mx_response->getBody()->getContents(), true);
if (isset($mx_data['result']) && !empty($mx_data['result'])) {
$has_mx = true;
}
wp_send_json_success([
'zoneId' => $zone_id,
'hasMX' => $has_mx
]);
} catch (Exception $e) {
wp_send_json_error(['message' => __('Failed to verify CloudFlare credentials: ', 'rl-mailwarmer') . $e->getMessage()]);
}
});
/**
* Create domain with all configurations
*/
add_action('wp_ajax_create_domain', function() {
// Verify nonce
if (!isset($_POST['security']) || !wp_verify_nonce($_POST['security'], 'dashboard_modal_nonce')) {
wp_send_json_error(['message' => __('Security check failed', 'rl-mailwarmer')]);
}
// Get domain data
$domain_data_json = isset($_POST['domainData']) ? $_POST['domainData'] : '';
if (empty($domain_data_json)) {
wp_send_json_error(['message' => __('No domain data provided', 'rl-mailwarmer')]);
return;
}
$domain_data = json_decode(stripslashes($domain_data_json), true);
// Create the domain post
$current_user_id = get_current_user_id();
$domain_name = $domain_data['domain'];
$post_data = array(
'post_title' => $domain_name,
'post_type' => 'domain',
'post_status' => 'publish'
);
$post_id = wp_insert_post($post_data);
if (is_wp_error($post_id)) {
wp_send_json_error(['message' => $post_id->get_error_message()]);
return;
}
// Save domain meta data
update_post_meta($post_id, 'cloudflare_api_email', $domain_data['cloudflareEmail']);
update_post_meta($post_id, 'cloudflare_api_key', $domain_data['cloudflareKey']);
update_post_meta($post_id, 'cloudflare_zone_id', $domain_data['cloudflareZoneId']);
update_post_meta($post_id, 'domain_in_use', !$domain_data['configureMailferno']);
update_post_meta($post_id, 'owner_id', $current_user_id);
// Run a domain health check
$domain_report_id = RL_MailWarmer_Domain_Helper::save_domain_health_report($post_id);
// Handle DNS configuration if requested
if ($domain_data['configureMailferno']) {
$server = get_field('defaut_mailferno_mx', 'option');
RL_MailWarmer_Domain_Helper::update_mx_record($post_id, $server->post_title, 0, 3600);
}
// Fix DNS records
$results = RL_MailWarmer_Domain_Helper::fix_deliverability_dns_issues($post_id);
// Create email accounts if provided
$email_accounts = [];
if (!empty($domain_data['emailAccounts'])) {
foreach ($domain_data['emailAccounts'] as $email) {
// Extract username from email
$username = explode('@', $email)[0];
// Create email account
$email_post_data = array(
'post_title' => $email,
'post_type' => 'email-account',
'post_status' => 'publish'
);
$email_post_id = wp_insert_post($email_post_data);
if (!is_wp_error($email_post_id)) {
// Set default password and metadata
update_post_meta($email_post_id, 'domain_id', $post_id);
update_post_meta($email_post_id, 'mail_password', wp_generate_password(12, false));
update_post_meta($email_post_id, 'full_name', ucwords(str_replace(['.', '-', '_'], ' ', $username)));
update_post_meta($email_post_id, 'owner_id', $current_user_id);
update_post_meta($email_post_id, 'include_in_warmup_pool', true);
$email_accounts[] = $email_post_id;
}
}
}
wp_send_json_success([
'message' => __('Domain added successfully. ' . (!empty($email_accounts) ? count($email_accounts) . ' email accounts created.' : ''), 'rl-mailwarmer'),
'domain_id' => $post_id,
'email_accounts' => $email_accounts
]);
});

View file

@ -41,7 +41,7 @@ jQuery(document).ready(function($) {
// Reset form or redirect if not staying on page
if (!$('#stay_on_page').is(':checked') && response.data.permalink) {
window.location.href = response.data.permalink;
// window.location.href = response.data.permalink;
return;
} else if (!$('#stay_on_page').is(':checked')) {
window.location.href = campaign_form_vars.campaigns_page;

484
js/dashboard-modal.js Normal file
View file

@ -0,0 +1,484 @@
/**
* Dashboard Modal & Multi-step Form Handlers
*/
jQuery(document).ready(function($) {
// Modal elements
const $fabButton = $('.mf-floating-action-button');
const $modalOverlay = $('.mf-modal-overlay');
const $modalContainer = $('.mf-modal-container');
const $modalClose = $('.mf-modal-close');
const $backBtn = $('.mf-back-btn');
const $nextBtn = $('.mf-next-btn');
// Step indicators
const $stepDots = $('.mf-step-dot');
let $steps = $('.mf-step');
// Selection buttons
const $selectionButtons = $('.mf-selection-button');
// Current step and selected type
let currentStep = 0;
let selectedType = '';
let domainData = {
domain: '',
isValid: false,
usesCloudFlare: false,
cloudflareZoneId: '',
cloudflareEmail: '',
cloudflareKey: '',
hasMX: false,
configureMailferno: false,
emailAccounts: []
};
// Open Modal
$fabButton.on('click', function() {
resetForm();
$modalOverlay.addClass('active');
});
// Close Modal
$modalClose.on('click', closeModal);
$modalOverlay.on('click', function(e) {
if (e.target === this) {
closeModal();
}
});
function closeModal() {
$modalOverlay.removeClass('active');
resetForm();
}
// Reset Form
function resetForm() {
currentStep = 0;
selectedType = '';
domainData = {
domain: '',
isValid: false,
usesCloudFlare: false,
cloudflareZoneId: '',
cloudflareEmail: '',
cloudflareKey: '',
hasMX: false,
configureMailferno: false,
emailAccounts: []
};
$selectionButtons.removeClass('selected');
updateStepIndicators();
showCurrentStep();
$('.mf-validation-error').hide();
$('.mf-success-message, .mf-error-message').remove();
$('.mf-loading').hide();
$('#mf-domain-input').val('');
$('#mf-cloudflare-email').val('');
$('#mf-cloudflare-key').val('');
$('.mf-mx-option').removeClass('selected');
$('.mf-email-row:not(:first)').remove();
$('.mf-email-row input').val('');
}
// Selection buttons
$selectionButtons.on('click', function() {
$selectionButtons.removeClass('selected');
$(this).addClass('selected');
selectedType = $(this).data('type');
$nextBtn.removeClass('disabled');
});
// Navigation buttons
$backBtn.on('click', function() {
if (currentStep > 0) {
currentStep--;
updateStepIndicators();
showCurrentStep();
}
});
$nextBtn.on('click', function() {
if ($(this).hasClass('disabled')) return;
// Handle first step (selection)
if (currentStep === 0) {
if (!selectedType) {
alert('Please select an option to continue');
return;
}
// Set up next steps based on selection
setupStepsByType(selectedType);
currentStep++;
updateStepIndicators();
showCurrentStep();
return;
}
// Domain specific step handling
if (selectedType === 'domain') {
const result = handleDomainStep(currentStep);
if (!result) return; // If step validation fails, don't proceed
}
// If we reach here, move to next step
currentStep++;
updateStepIndicators();
showCurrentStep();
});
// Update step indicators
function updateStepIndicators() {
$stepDots.removeClass('active completed');
// Update for current setup
$stepDots.each(function(index) {
if (index < currentStep) {
$(this).addClass('completed');
} else if (index === currentStep) {
$(this).addClass('active');
}
});
// Update back button visibility
if (currentStep === 0) {
$backBtn.hide();
} else {
$backBtn.show();
}
// Update next button text
const isLastStep = (currentStep === $steps.length - 1);
$nextBtn.text(isLastStep ? 'Next' : 'Next');
// When on selection step, disable next until selection made
if (currentStep === 0) {
$nextBtn.toggleClass('disabled', !selectedType);
} else {
$nextBtn.removeClass('disabled');
}
}
// Show current step
function showCurrentStep() {
$steps.removeClass('active');
$steps.eq(currentStep).addClass('active');
}
// Setup steps by selected type
function setupStepsByType(type) {
// Hide all steps except the first one
$steps.not(':first').remove();
// Clear step indicators
$('.mf-steps-indicator').empty();
let stepCount = 1; // Selection step
if (type === 'domain') {
// Step 1: Input domain
$('<div class="mf-step">')
.append('<h3>Enter Domain Name</h3>')
.append('<div class="mf-form-field"><label for="mf-domain-input">Domain Name</label><input type="text" id="mf-domain-input" placeholder="example.com"><div class="mf-validation-error" id="domain-validation-error">Please enter a valid domain name</div></div>')
.append('<div class="mf-loading" style="display:none;"><div class="mf-loading-spinner"></div><p>Validating domain...</p></div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 2: CloudFlare verification
$('<div class="mf-step">')
.append('<h3>Verify CloudFlare Connection</h3>')
.append('<div class="mf-form-field"><label for="mf-cloudflare-email">CloudFlare Email</label><input type="email" id="mf-cloudflare-email" placeholder="your@email.com"></div>')
.append('<div class="mf-form-field"><label for="mf-cloudflare-key">CloudFlare API Key</label><input type="password" id="mf-cloudflare-key" placeholder="Your API key"></div>')
.append('<div class="mf-validation-error" id="cloudflare-validation-error">Please enter valid CloudFlare credentials</div>')
.append('<div class="mf-loading" style="display:none;"><div class="mf-loading-spinner"></div><p>Verifying CloudFlare credentials...</p></div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 3: MX Configuration
$('<div class="mf-step">')
.append('<h3>Email Server Configuration</h3>')
.append('<p>How would you like to configure email for this domain?</p>')
.append('<div class="mf-mx-options"><div class="mf-mx-option" data-option="mailferno"><h4>Use Mailferno MX Server</h4><p>Configure this domain to use Mailferno\'s email server. Best if this is a new domain not currently used for email.</p></div><div class="mf-mx-option" data-option="existing"><h4>Keep Existing Configuration</h4><p>Keep your current email server configuration. Best if this domain is already being used for email.</p></div></div>')
.append('<div class="mf-validation-error" id="mx-validation-error">Please select an MX configuration option</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Step 4: Email accounts
$('<div class="mf-step">')
.append('<h3>Add Email Accounts</h3>')
.append('<p>Would you like to create email accounts for this domain now?</p>')
.append('<div class="mf-email-list"><div class="mf-email-row"><input type="text" class="mf-email-local" placeholder="username"><span>@'+domainData.domain+'</span><button type="button" class="mf-remove-email-btn">×</button></div></div>')
.append('<button type="button" class="mf-add-email-btn">+ Add Another Email</button>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Domain Setup Complete</h3>')
.append('<div class="mf-success-message">Your domain has been successfully added to Mailferno!</div>')
.append('<p>You can now continue to manage your domain, email accounts, and campaigns from the dashboard.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
} else if (type === 'campaign') {
// Add campaign specific steps
$('<div class="mf-step">')
.append('<h3>Create Campaign</h3>')
.append('<p>Campaign creation functionality will be implemented here.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Campaign Created</h3>')
.append('<div class="mf-success-message">Your campaign has been successfully created!</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
} else if (type === 'email') {
// Add email account specific steps
$('<div class="mf-step">')
.append('<h3>Create Email Account</h3>')
.append('<p>Email account creation functionality will be implemented here.</p>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
// Final step
$('<div class="mf-step">')
.append('<h3>Email Account Created</h3>')
.append('<div class="mf-success-message">Your email account has been successfully created!</div>')
.appendTo($modalContainer.find('.mf-modal-body'));
stepCount++;
}
// Create step indicators
for (let i = 0; i < stepCount; i++) {
$('<div class="mf-step-dot">')
.addClass(i === 0 ? 'completed' : (i === 1 ? 'active' : ''))
.appendTo($('.mf-steps-indicator'));
}
// Make steps accessible
$steps = $('.mf-step');
// Setup event handlers for the domain steps
if (type === 'domain') {
setupDomainStepHandlers();
}
}
// Domain step specific handlers
function setupDomainStepHandlers() {
// Step 1: Domain validation
$('#mf-domain-input').on('input', function() {
// Basic validation
const domain = $(this).val().trim();
const isValid = /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
if (isValid) {
$('#domain-validation-error').hide();
domainData.domain = domain;
} else {
$('#domain-validation-error').show();
}
});
// Step 3: MX Configuration
$('.mf-mx-option').on('click', function() {
$('.mf-mx-option').removeClass('selected');
$(this).addClass('selected');
domainData.configureMailferno = ($(this).data('option') === 'mailferno');
});
// Step 4: Email accounts
$('.mf-add-email-btn').on('click', function() {
const $newRow = $('.mf-email-row').first().clone();
$newRow.find('input').val('');
$newRow.insertBefore($(this));
});
// Remove email button
$(document).on('click', '.mf-remove-email-btn', function() {
if ($('.mf-email-row').length > 1) {
$(this).closest('.mf-email-row').remove();
} else {
$('.mf-email-row').find('input').val('');
}
});
}
// Domain step validation and AJAX handling
function handleDomainStep(step) {
// Adjust step to match domain-specific numbering (after selection)
const domainStep = step - 1;
switch(domainStep) {
case 0: // Domain input validation
const domain = $('#mf-domain-input').val().trim();
if (!domain || !/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain)) {
$('#domain-validation-error').show();
return false;
}
domainData.domain = domain;
// Show loading indicator
$steps.eq(currentStep).find('.mf-loading').show();
$nextBtn.addClass('disabled');
// AJAX validation call to check if domain is valid and uses CloudFlare
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'validate_domain',
security: dashboard_modal_vars.nonce,
domain: domain
},
success: function(response) {
$steps.eq(currentStep).find('.mf-loading').hide();
if (response.success) {
domainData.isValid = true;
domainData.usesCloudFlare = response.data.usesCloudFlare;
if (!domainData.usesCloudFlare) {
$steps.eq(currentStep).append('<div class="mf-error-message">This domain is not using CloudFlare nameservers. Please configure CloudFlare for this domain before continuing.</div>');
return;
}
// Update domain text in email step
$('.mf-email-row span').text('@' + domain);
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).find('.mf-loading').hide();
$steps.eq(currentStep).append('<div class="mf-error-message">Error validating domain. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false; // Prevent automatic next step - we'll handle it in AJAX callback
case 1: // CloudFlare verification
const email = $('#mf-cloudflare-email').val().trim();
const key = $('#mf-cloudflare-key').val().trim();
if (!email || !key) {
$('#cloudflare-validation-error').show();
return false;
}
domainData.cloudflareEmail = email;
domainData.cloudflareKey = key;
// Show loading indicator
$steps.eq(currentStep).find('.mf-loading').show();
$nextBtn.addClass('disabled');
// AJAX call to verify CloudFlare credentials
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'verify_cloudflare',
security: dashboard_modal_vars.nonce,
domain: domainData.domain,
email: email,
key: key
},
success: function(response) {
$steps.eq(currentStep).find('.mf-loading').hide();
if (response.success) {
domainData.cloudflareZoneId = response.data.zoneId;
domainData.hasMX = response.data.hasMX;
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).find('.mf-loading').hide();
$steps.eq(currentStep).append('<div class="mf-error-message">Error verifying CloudFlare credentials. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false; // Prevent automatic next step - we'll handle it in AJAX callback
case 2: // MX Configuration
if (!$('.mf-mx-option.selected').length) {
$('#mx-validation-error').show();
return false;
}
domainData.configureMailferno = ($('.mf-mx-option.selected').data('option') === 'mailferno');
return true;
case 3: // Email accounts
domainData.emailAccounts = [];
let allValid = true;
$('.mf-email-row').each(function() {
const username = $(this).find('.mf-email-local').val().trim();
if (username) {
domainData.emailAccounts.push(username + '@' + domainData.domain);
}
});
if (allValid) {
// Create domain via AJAX
$nextBtn.addClass('disabled');
$.ajax({
url: dashboard_modal_vars.ajax_url,
type: 'POST',
data: {
action: 'create_domain',
security: dashboard_modal_vars.nonce,
domainData: JSON.stringify(domainData)
},
success: function(response) {
if (response.success) {
// Update final step
$steps.eq(currentStep + 1).find('.mf-success-message').text(response.data.message);
// Continue to next step
$nextBtn.removeClass('disabled');
$nextBtn.trigger('click');
// Reload page after a short delay to show updated dashboard
setTimeout(function() {
window.location.reload();
}, 5000);
} else {
$steps.eq(currentStep).append('<div class="mf-error-message">' + response.data.message + '</div>');
$nextBtn.removeClass('disabled');
}
},
error: function() {
$steps.eq(currentStep).append('<div class="mf-error-message">Error creating domain. Please try again.</div>');
$nextBtn.removeClass('disabled');
}
});
return false;
}
return allValid;
case 4: // Final step - just close modal
closeModal();
return true;
}
return true;
}
});