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:
parent
c11bc80f75
commit
e251e7fe24
4 changed files with 1078 additions and 27 deletions
344
css/modal.css
Normal file
344
css/modal.css
Normal 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;
|
||||
}
|
||||
|
|
@ -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
|
||||
]);
|
||||
});
|
||||
|
|
@ -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
484
js/dashboard-modal.js
Normal 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;
|
||||
}
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue