email working - remove clients - preview polish

This commit is contained in:
stack_admn committed 2026-11-01 21:41:37 -08:00
1 parent 2b1424d127
commit da83beaec3
19 files changed
+1342 -1060

No files matched your search

+174 -117
View File
@@ -23,20 +23,24 @@
hasMore: false,
filters: {
status: '',
site: '',
client: ''
site: ''
},
// Edit mode state
isEditMode: false,
editingSlideId: null,
currentSlides: null,
focusSlideId: null,
// Initialize the dashboard
init: function() {
const params = new URLSearchParams(window.location.search);
this.focusSlideId = params.get('hssm_focus') || null;
this.bindEvents();
this.initializeTabs();
this.addInitialSlideForm();
this.populateFilterDropdowns();
this.loadExistingSlides();
// Initialize site data
window.hssmSites = window.hssmSites || [];
@@ -64,8 +68,6 @@
$(document).on('click', '.hssm-remove-button', this.removeButtonRow.bind(this));
// Slide actions
$(document).on('click', '.hssm-save-slide', this.saveSlide.bind(this));
$(document).on('click', '#save-all-slides', this.saveAllSlides.bind(this));
$(document).on('click', '.hssm-preview-slide', this.previewSlide.bind(this));
$(document).on('click', '.hssm-schedule-slide', this.scheduleSlide.bind(this));
$(document).on('click', '.hssm-publish-slide', this.publishSlide.bind(this));
@@ -89,6 +91,8 @@
// Filtering and management (slide row edit/delete bound in bindSlideActions after list load)
$(document).on('click', '#apply-filters', this.applyFilters.bind(this));
$(document).on('click', '#clear-filters', this.clearFilters.bind(this));
$(document).on('change', '#filter-site, #filter-status', this.applyFilters.bind(this));
// Edit form actions — register once; do not re-bind in populateEditForm (prevents duplicate toasts)
$(document).on('click.hssmDashboard', '.hssm-cancel-edit, .hssm-cancel-edit-btn', (e) => {
@@ -104,14 +108,18 @@
// Additional functionality
$(document).on('click', '#import-slides', this.importSlides.bind(this));
$(document).on('click', '#export-slides', this.exportSlides.bind(this));
$(document).on('click', '#save-draft-all', this.saveAllAsDrafts.bind(this));
// Notification Settings
$(document).on('submit', '#notification-settings-form', this.saveNotificationSettings.bind(this));
},
// Initialize tab functionality
initializeTabs: function() {
// Confirmation / email links should land on the manage list.
if (this.focusSlideId) {
sessionStorage.setItem('hssm_active_tab', 'manage-slides');
}
// Check for saved tab in sessionStorage
const savedTab = sessionStorage.getItem('hssm_active_tab');
@@ -425,84 +433,6 @@
});
},
// Save individual slide
saveSlide: function(e) {
e.preventDefault();
const formId = $(e.currentTarget).data('form');
const $form = $(`.hssm-slide-form[data-form-id="${formId}"]`);
this.submitSlideForm($form);
},
// Save all slides
saveAllSlides: function(e) {
if (e) e.preventDefault();
const $forms = $('.hssm-slide-form');
let savedCount = 0;
let errorCount = 0;
$forms.each((index, form) => {
this.submitSlideForm($(form), () => {
savedCount++;
this.checkAllSaved(savedCount, errorCount, $forms.length);
}, () => {
errorCount++;
this.checkAllSaved(savedCount, errorCount, $forms.length);
});
});
},
// Check if all forms have been processed
checkAllSaved: function(savedCount, errorCount, totalCount) {
if (savedCount + errorCount === totalCount) {
if (errorCount === 0) {
this.showMessage(`All ${savedCount} slides saved successfully!`, 'success');
} else {
this.showMessage(`${savedCount} slides saved, ${errorCount} failed.`, 'warning');
}
}
},
// Submit individual slide form
submitSlideForm: function($form, successCallback, errorCallback) {
if (!this.validateForm($form)) {
if (errorCallback) errorCallback();
return;
}
const formData = this.collectFormData($form);
$form.addClass('hssm-loading');
formData.append('nonce', hssmDashboard.nonce);
$.ajax({
url: hssmDashboard.ajaxurl,
type: 'POST',
data: formData,
processData: false,
contentType: false,
success: (response) => {
$form.removeClass('hssm-loading');
if (response.success) {
this.showMessage(response.data.message, 'success');
this.clearForm($form);
if (successCallback) successCallback();
} else {
this.showMessage(response.data.message || 'Save failed', 'error');
if (errorCallback) errorCallback();
}
},
error: () => {
$form.removeClass('hssm-loading');
this.showMessage('Save failed. Please try again.', 'error');
if (errorCallback) errorCallback();
}
});
},
// Clear all field errors in a form
clearFieldErrors: function($form) {
$form.find('.hssm-field-error').remove();
@@ -771,7 +701,11 @@
type: 'POST',
data: {
action: 'hssm_get_slides',
nonce: hssmDashboard.nonce
nonce: hssmDashboard.nonce,
page: this.currentPage,
per_page: this.perPage,
status_filter: this.filters.status || '',
site_filter: this.filters.site || ''
},
beforeSend: function(xhr, settings) {
// Optional: Add loading indicators
@@ -779,6 +713,7 @@
success: (response) => {
if (response.success && response.data && response.data.slides) {
this.renderSlidesList(response.data.slides);
this.highlightFocusedSlide();
} else {
console.error('Failed to get slides:', response);
$slidesList.html('<p>Failed to load slides. Response format unexpected.</p>');
@@ -807,7 +742,7 @@
<span class="dashicons dashicons-slides"></span>
</div>
<h3>No slides found</h3>
<p>Create your first slide to get started!</p>
<p>${this.hasActiveFilters() ? 'No slides match the current filters.' : 'Create your first slide to get started!'}</p>
<button class="hssm-btn hssm-btn-primary" onclick="$('.hssm-nav-tabs a[data-tab=create-slides]').click()">
<span class="dashicons dashicons-plus-alt"></span> Create First Slide
</button>
@@ -833,8 +768,11 @@
slides.forEach(slide => {
const imageUrl = slide.featured_image_url || '';
const statusClass = this.getStatusClass(slide.status);
const statusLabel = this.getStatusLabel(slide);
const sitesText = this.formatSitesList(slide.sites || slide.target_sites);
const scheduleText = this.formatScheduleText(slide);
const isPending = slide.status === 'pending';
const isApproved = slide.status === 'publish' || slide.status === 'future';
html += `
<div class="hssm-slide-row" data-slide-id="${slide.id}">
@@ -848,7 +786,7 @@
<div class="hssm-slide-subtitle">${slide.subtitle || ''}</div>
</div>
<div class="hssm-col-status">
<span class="hssm-status-badge hssm-status-${statusClass}">${slide.status}</span>
<span class="hssm-status-badge hssm-status-${statusClass}">${statusLabel}</span>
</div>
<div class="hssm-col-dates">
<div class="hssm-schedule-info">${scheduleText}</div>
@@ -858,15 +796,28 @@
</div>
<div class="hssm-col-actions">
<div class="hssm-slide-actions">
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-preview-slide" data-slide-id="${slide.id}" title="Preview banner">
<span class="dashicons dashicons-visibility"></span>
</button>
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-edit-slide" data-slide-id="${slide.id}" title="Edit">
<span class="dashicons dashicons-edit"></span>
</button>
<button class="hssm-btn hssm-btn-sm hssm-btn-secondary hssm-duplicate-slide" data-slide-id="${slide.id}" title="Duplicate">
<span class="dashicons dashicons-admin-page"></span>
</button>
${isPending ? `
<button class="hssm-btn hssm-btn-sm hssm-btn-success hssm-approve-slide" data-slide-id="${slide.id}" data-approve-action="schedule" title="Approve">
<span class="dashicons dashicons-yes"></span>
</button>
` : isApproved ? `
<button type="button" class="hssm-btn hssm-btn-sm hssm-btn-primary hssm-approved-slide" title="Approved" disabled>
<span class="dashicons dashicons-yes"></span>
</button>
` : `
<button class="hssm-btn hssm-btn-sm hssm-btn-success hssm-toggle-status" data-slide-id="${slide.id}" title="Toggle Status">
<span class="dashicons dashicons-${slide.status === 'publish' ? 'pause' : 'controls-play'}"></span>
</button>
`}
<button class="hssm-btn hssm-btn-sm hssm-btn-danger hssm-delete-slide" data-slide-id="${slide.id}" title="Delete">
<span class="dashicons dashicons-trash"></span>
</button>
@@ -885,17 +836,49 @@
this.bindSlideActions();
},
highlightFocusedSlide: function() {
if (!this.focusSlideId) {
return;
}
const $row = $('.hssm-slide-row[data-slide-id="' + this.focusSlideId + '"]');
if (!$row.length) {
return;
}
$row.addClass('hssm-slide-row-focus');
if ($row[0] && $row[0].scrollIntoView) {
$row[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
}
},
// Helper function to get status CSS class
getStatusClass: function(status) {
const statusMap = {
'publish': 'published',
'draft': 'draft',
'future': 'scheduled',
'pending': 'pending',
'private': 'private'
};
return statusMap[status] || 'unknown';
},
getStatusLabel: function(slide) {
if (slide.status_display) {
return slide.status_display;
}
const labels = {
publish: 'Published',
draft: 'Draft',
future: 'Scheduled',
pending: 'Pending Approval',
private: 'Private'
};
return labels[slide.status] || slide.status;
},
// Helper function to format sites list
formatSitesList: function(sites) {
if (!sites || sites.length === 0) return 'No sites';
@@ -943,6 +926,12 @@
const slideId = $(e.currentTarget).data('slide-id');
this.toggleSlideStatus(slideId);
});
$(document).off('click.hssmSlideActions', '.hssm-approve-slide').on('click.hssmSlideActions', '.hssm-approve-slide', (e) => {
e.preventDefault();
const $btn = $(e.currentTarget);
this.approveSlide($btn.data('slide-id'), $btn.data('approve-action') || 'schedule');
});
},
// Edit slide function
@@ -1097,6 +1086,9 @@
</div>
</div>
<div class="hssm-form-actions">
<button type="button" class="hssm-btn hssm-btn-outline hssm-preview-slide" data-form="${this.formCounter}" data-slide-id="${slideData.id}">
<span class="dashicons dashicons-visibility"></span> Preview Banner
</button>
<button type="button" class="hssm-btn hssm-btn-secondary hssm-schedule-updated-slide" data-form="${this.formCounter}" data-slide-id="${slideData.id}">
<span class="dashicons dashicons-calendar-alt"></span> Schedule for Approval
</button>
@@ -1302,6 +1294,38 @@
});
},
approveSlide: function(slideId, approveAction) {
const confirmText = (hssmDashboard.strings && hssmDashboard.strings.approve_confirm)
? hssmDashboard.strings.approve_confirm
: 'Approve this slide?';
if (!confirm(confirmText)) {
return;
}
$.ajax({
url: hssmDashboard.ajaxurl,
type: 'POST',
data: {
action: 'hssm_approve_slide',
nonce: hssmDashboard.nonce,
slide_id: slideId,
approve_action: approveAction || 'schedule'
},
success: (response) => {
if (response.success) {
this.showMessage((hssmDashboard.strings && hssmDashboard.strings.approve_success) || 'Slide approved.', 'success');
this.loadExistingSlides();
} else {
this.showMessage(response.data?.message || 'Failed to approve slide', 'error');
}
},
error: () => {
this.showMessage('Failed to approve slide. Please try again.', 'error');
}
});
},
// Toggle slide status
toggleSlideStatus: function(slideId) {
$.ajax({
@@ -1344,15 +1368,73 @@
}, dismissMs);
},
// Placeholder methods for future implementation
previewSlide: function(e) {
e.preventDefault();
this.showMessage('Preview functionality coming soon!', 'info');
populateFilterDropdowns: function() {
const $site = $('#filter-site');
if ($site.length && window.hssmSites && $site.children().length <= 1) {
window.hssmSites.forEach((site) => {
$site.append(`<option value="${site.id}">${site.name}</option>`);
});
}
},
hasActiveFilters: function() {
return !!(this.filters.status || this.filters.site);
},
readFiltersFromForm: function() {
this.filters.status = $('#filter-status').val() || '';
this.filters.site = $('#filter-site').val() || '';
},
applyFilters: function(e) {
if (e && e.preventDefault) {
e.preventDefault();
}
this.readFiltersFromForm();
this.currentPage = 1;
this.loadExistingSlides();
},
clearFilters: function(e) {
if (e && e.preventDefault) {
e.preventDefault();
}
this.filters = { status: '', site: '' };
$('#filter-status, #filter-site').val('');
this.currentPage = 1;
this.loadExistingSlides();
},
buildPreviewUrl: function(slide) {
const base = (hssmDashboard.previewUrl || '/slide-preview/').replace(/\/?$/, '/');
const params = new URLSearchParams();
if (slide && slide.id) {
params.set('hssm_preview', slide.id);
}
if (slide && slide.start_date) {
params.set('hssm_date', slide.start_date);
}
if (slide && slide.site_slugs && slide.site_slugs.length) {
params.set('hssm_site', slide.site_slugs[0]);
}
const query = params.toString();
return query ? `${base}?${query}` : base;
},
previewSlide: function(e) {
e.preventDefault();
this.showMessage('Filter functionality coming soon!', 'info');
const slideId = $(e.currentTarget).data('slide-id') || this.editingSlideId;
if (!slideId) {
this.showMessage((hssmDashboard.strings && hssmDashboard.strings.preview_save_first) || 'Save the slide first, then preview it as a banner.', 'info');
return;
}
const slide = this.findSlideById(slideId) || { id: slideId };
window.open(this.buildPreviewUrl(slide), '_blank', 'noopener');
},
// Load analytics data
@@ -1386,31 +1468,6 @@
this.showMessage('Slide export functionality coming soon!', 'info');
},
// Save all slides as drafts
saveAllAsDrafts: function(e) {
e.preventDefault();
const $forms = $('.hssm-slide-form');
let savedCount = 0;
let errorCount = 0;
$forms.each((index, form) => {
const $form = $(form);
// Override status to draft
const formData = this.collectFormData($form);
formData.append('post_status', 'draft');
this.submitSlideForm($form, () => {
savedCount++;
this.checkAllSaved(savedCount, errorCount, $forms.length);
}, () => {
errorCount++;
this.checkAllSaved(savedCount, errorCount, $forms.length);
});
});
},
// Load notification settings
loadNotificationSettings: function() {
$.ajax({