- 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>
484 lines
No EOL
20 KiB
JavaScript
484 lines
No EOL
20 KiB
JavaScript
/**
|
||
* 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;
|
||
}
|
||
}); |