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

+155 -2
View File
@@ -7,6 +7,68 @@
* @since 1.0.0
*/
/* Hide Divi theme header/logo on plugin tool pages */
body.hssm-tool-page #main-header,
body.hssm-tool-page #top-header,
body.hssm-tool-page .et-l--header,
body.hssm-tool-page header.et-l,
body.hssm-tool-page .logo_container,
body.hssm-tool-page #et-top-navigation,
body.page-template-page-slide-preview #main-header,
body.page-template-page-slide-preview #top-header,
body.page-template-page-slide-preview .et-l--header,
body.page-template-page-slide-preview header.et-l,
body.page-template-page-slide-dashboard #main-header,
body.page-template-page-slide-dashboard #top-header,
body.page-template-page-slide-dashboard .et-l--header,
body.page-template-page-slide-dashboard header.et-l {
display: none !important;
}
body.hssm-tool-page {
padding-top: 0 !important;
}
body.hssm-tool-page #page-container,
body.hssm-tool-page #et-boc,
body.hssm-tool-page .et-l--body {
padding-top: 0 !important;
margin-top: 0 !important;
}
.hssm-preview-approve-bar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
max-width: 1400px;
margin: 20px auto 0;
padding: 16px 24px;
background: #fff8e5;
border: 1px solid #f0c36d;
border-radius: 8px;
}
.hssm-preview-approve-copy {
display: flex;
flex-direction: column;
gap: 4px;
color: #1d2327;
}
.hssm-preview-approve-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
@media (max-width: 782px) {
.hssm-preview-approve-bar {
flex-direction: column;
align-items: flex-start;
}
}
/* ==========================================
Full-Width Layout Override
========================================== */
@@ -1117,6 +1179,11 @@
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.hssm-slide-item-focus {
border-color: #2271b1;
box-shadow: 0 0 0 2px rgba(34, 113, 177, 0.15);
}
.hssm-slide-info {
flex: 1;
}
@@ -1399,7 +1466,7 @@
.hssm-table-header {
display: grid;
grid-template-columns: 80px 1fr 120px 180px 150px 140px;
grid-template-columns: 80px 1fr 150px 180px 150px 180px;
gap: 15px;
padding: 20px 25px;
background: #f8f9fa;
@@ -1416,7 +1483,7 @@
.hssm-slide-row {
display: grid;
grid-template-columns: 80px 1fr 120px 180px 150px 140px;
grid-template-columns: 80px 1fr 150px 180px 150px 180px;
gap: 15px;
padding: 20px 25px;
border-bottom: 1px solid #f1f3f4;
@@ -1428,6 +1495,11 @@
background-color: #f8f9fa;
}
.hssm-slide-row-focus {
background-color: #e8f5e9;
outline: 2px solid #46b450;
}
.hssm-slide-row:last-child {
border-bottom: none;
}
@@ -1532,6 +1604,22 @@
border-color: #1e7e34;
}
.hssm-slide-actions .hssm-approved-slide,
.hssm-slide-actions .hssm-approved-slide:disabled,
.hssm-slide-actions .hssm-approved-slide:hover {
background: #275191;
background-color: #275191;
border-color: #275191;
color: #fff;
}
.hssm-slide-actions .hssm-approved-slide:disabled {
opacity: 1;
cursor: default;
transform: none;
box-shadow: none;
}
/* Empty State */
.hssm-empty-state {
text-align: center;
@@ -1872,4 +1960,69 @@
/* Overlap handling override */
.hssm-datepicker-start.hssm-datepicker-end a {
background: linear-gradient(135deg, #d4edda 50%, #f8d7da 50%) !important;
}
/*
* Theme CSS targets input[type="text"] (higher specificity than .hssm-input)
* and paints those fields black/pill-shaped. Keep plugin forms white.
*/
.hssm-dashboard-main input[type="text"],
.hssm-dashboard-main input[type="url"],
.hssm-dashboard-main input[type="email"],
.hssm-dashboard-main input[type="search"],
.hssm-dashboard-main input[type="number"],
.hssm-dashboard-main input[type="date"],
.hssm-dashboard-main input[type="tel"],
.hssm-dashboard-main textarea,
.hssm-dashboard-main select,
.hssm-dashboard-main .hssm-input,
.hssm-dashboard-main .hssm-textarea,
.hssm-dashboard-main .hssm-select,
input.hssm-input,
input.hssm-input-small,
textarea.hssm-textarea,
select.hssm-select {
background-color: #ffffff !important;
color: #1d2327 !important;
border: 1px solid #ced4da !important;
border-radius: 4px !important;
color-scheme: light;
}
.hssm-dashboard-main input[type="text"]:focus,
.hssm-dashboard-main input[type="url"]:focus,
.hssm-dashboard-main input[type="email"]:focus,
.hssm-dashboard-main input[type="search"]:focus,
.hssm-dashboard-main input[type="number"]:focus,
.hssm-dashboard-main input[type="date"]:focus,
.hssm-dashboard-main input[type="tel"]:focus,
.hssm-dashboard-main textarea:focus,
.hssm-dashboard-main select:focus,
.hssm-dashboard-main .hssm-input:focus,
.hssm-dashboard-main .hssm-textarea:focus,
.hssm-dashboard-main .hssm-select:focus,
input.hssm-input:focus,
input.hssm-input-small:focus,
textarea.hssm-textarea:focus,
select.hssm-select:focus {
background-color: #ffffff !important;
color: #1d2327 !important;
border-color: #007cba !important;
}
.hssm-dashboard-main input::placeholder,
input.hssm-input::placeholder,
input.hssm-input-small::placeholder,
textarea.hssm-textarea::placeholder {
color: #6c757d;
opacity: 1;
}
.hssm-dashboard-main input:-webkit-autofill,
.hssm-dashboard-main input:-webkit-autofill:hover,
.hssm-dashboard-main input:-webkit-autofill:focus,
input.hssm-input:-webkit-autofill,
input.hssm-input-small:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px #ffffff inset !important;
-webkit-text-fill-color: #1d2327 !important;
}
-61
View File
@@ -1,61 +0,0 @@
/**
* Hi-School Slide Manager - Frontend Styles
*/
.hssm-slides-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
margin: 20px 0;
}
.hssm-slide {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
flex: 1;
min-width: 300px;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.hssm-slide:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.hssm-slide h3 {
margin: 0 0 15px 0;
color: #333;
font-size: 1.4em;
border-bottom: 2px solid #0073aa;
padding-bottom: 10px;
}
.hssm-slide-content {
margin-bottom: 15px;
line-height: 1.6;
color: #555;
}
.hssm-slide-image {
text-align: center;
}
.hssm-slide-image img {
max-width: 100%;
height: auto;
border-radius: 4px;
}
/* Responsive design */
@media (max-width: 768px) {
.hssm-slides-container {
flex-direction: column;
}
.hssm-slide {
min-width: auto;
}
}
+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({
-79
View File
@@ -1,79 +0,0 @@
/**
* Hi-School Slide Manager - Frontend JavaScript
*/
jQuery(document).ready(function($) {
'use strict';
// Initialize slide functionality
initializeSlides();
/**
* Initialize slide functionality
*/
function initializeSlides() {
$('.hssm-slides-container').each(function() {
var $container = $(this);
var $slides = $container.find('.hssm-slide');
// Add fade-in animation
$slides.each(function(index) {
var $slide = $(this);
setTimeout(function() {
$slide.addClass('hssm-fade-in');
}, index * 100);
});
// Add click handlers for interactive slides
$slides.on('click', function() {
var $slide = $(this);
$slide.toggleClass('hssm-slide-expanded');
});
});
}
/**
* Handle slide interactions
*/
$(document).on('click', '.hssm-slide-toggle', function(e) {
e.preventDefault();
var $button = $(this);
var $slide = $button.closest('.hssm-slide');
var $content = $slide.find('.hssm-slide-content');
$content.slideToggle(300);
$button.text($content.is(':visible') ? 'Hide' : 'Show');
});
/**
* Handle keyboard navigation
*/
$(document).on('keydown', '.hssm-slide', function(e) {
if (e.which === 13 || e.which === 32) { // Enter or Space
e.preventDefault();
$(this).click();
}
});
});
// Add CSS classes via JavaScript for animations
document.addEventListener('DOMContentLoaded', function() {
var style = document.createElement('style');
style.textContent = `
.hssm-slide {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.hssm-slide.hssm-fade-in {
opacity: 1;
transform: translateY(0);
}
.hssm-slide.hssm-slide-expanded {
transform: scale(1.02);
}
`;
document.head.appendChild(style);
});
+1 -1
View File
@@ -110,7 +110,7 @@
action: 'hssm_approve_slide',
nonce: hssmPreview.nonce,
slide_id: slideId,
approve_action: 'schedule' // Smart approve: schedules if future date exists, otherwise publishes
approve_action: $btn.data('approve-action') || 'schedule'
},
success: (response) => {
if (response.success) {