Add schedule preview dropdown styles and various improvements

- Add .schedule-preview-dropdown and .schedule-preview-item CSS classes
- Add .btn-schedule-preview badge styling
- Add preview functionality for entity list views
- Improve modal and dropdown styling
- Various JS and SCSS enhancements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 11:49:01 +00:00
parent 4eeb8d85ae
commit 451a47cdcd
22 changed files with 11860 additions and 41 deletions

View File

@@ -55,7 +55,29 @@
priceMin: null,
priceMax: null,
attributes: [],
features: []
features: [],
// Entity-specific filters
productCountMin: null,
productCountMax: null,
salesMin: null,
salesMax: null,
turnoverMin: null,
turnoverMax: null,
depth: null,
hasProducts: false,
hasDescription: false,
hasImage: false,
activeOnly: true, // Default to active only
attributeGroup: null,
featureGroup: null,
dateAddFrom: null,
dateAddTo: null,
lastProductFrom: null,
lastProductTo: null,
// Country-specific filters
hasHolidays: false,
containsStates: false,
zone: null
},
filterableData: null,
// Search history
@@ -102,10 +124,20 @@
this.$wrapper.find('.group-modifiers').hide();
}
// Add fullwidth class to parent form-group
var $formGroup = this.$wrapper.closest('.form-group');
$formGroup.addClass('condition-trait-fullwidth');
$formGroup.find('.col-lg-offset-3').removeClass('col-lg-offset-3');
// Add fullwidth class to parent form-group (skip for form-group layout)
var hasLayoutFormGroup = this.$wrapper.hasClass('layout-form-group');
var $entitySelectorFormGroup = this.$wrapper.closest('.entity-selector-form-group');
console.log('[EntitySelector] hasLayoutFormGroup:', hasLayoutFormGroup);
console.log('[EntitySelector] closest .entity-selector-form-group:', $entitySelectorFormGroup.length);
if (!hasLayoutFormGroup && !$entitySelectorFormGroup.length) {
console.log('[EntitySelector] Adding condition-trait-fullwidth to form-group');
var $formGroup = this.$wrapper.closest('.form-group');
$formGroup.addClass('condition-trait-fullwidth');
$formGroup.find('.col-lg-offset-3').removeClass('col-lg-offset-3');
} else {
console.log('[EntitySelector] SKIPPING fullwidth - form-group layout detected');
}
this.createDropdown();
this.bindEvents();

View File

@@ -327,6 +327,20 @@
html += '<button type="button" class="btn-clear-filters"><i class="icon-times"></i></button>';
html += '</div>';
// Entity-specific filters: Countries
html += '<div class="filter-row filter-row-entity-countries" data-entity="countries" style="display:none;">';
html += '<label class="filter-label"><input type="checkbox" class="filter-active-only" checked> ' + (trans.active_only || 'Active only') + '</label>';
html += '<label class="filter-label"><input type="checkbox" class="filter-has-holidays"> ' + (trans.has_holidays || 'Has holidays') + '</label>';
html += '<label class="filter-label"><input type="checkbox" class="filter-contains-states"> ' + (trans.contains_states || 'Has states') + '</label>';
html += '<div class="filter-select-group">';
html += '<span class="filter-select-label"><i class="icon-globe"></i> ' + (trans.zone || 'Zone') + ':</span>';
html += '<select class="filter-zone-select">';
html += '<option value="">' + (trans.all_zones || 'All zones') + '</option>';
html += '</select>';
html += '</div>';
html += '<button type="button" class="btn-clear-filters"><i class="icon-times"></i></button>';
html += '</div>';
html += '</div>'; // End filter-panel
// Results header for list view (product columns)

View File

@@ -101,14 +101,18 @@
// Close popover when clicking outside
$(document).on('click', function(e) {
if (!$(e.target).closest('.target-preview-popover').length &&
!$(e.target).closest('.holiday-preview-popover').length &&
!$(e.target).closest('.tab-badge').length &&
!$(e.target).closest('.condition-match-count').length &&
!$(e.target).closest('.group-count-badge').length &&
!$(e.target).closest('.group-modifiers').length &&
!$(e.target).closest('.group-preview-badge').length &&
!$(e.target).closest('.toggle-count.clickable').length &&
!$(e.target).closest('.trait-total-count').length) {
!$(e.target).closest('.trait-total-count').length &&
!$(e.target).closest('.chip-preview-holidays').length) {
self.hidePreviewPopover();
// Also close holiday popover
$('.holiday-preview-popover').remove();
}
});
@@ -131,6 +135,23 @@
}
});
// Toggle blocks content (form-content layout)
this.$wrapper.on('click', '.btn-toggle-blocks', function(e) {
e.preventDefault();
var $blocksContent = self.$wrapper.find('.entity-selector-blocks-content');
var $icon = $(this).find('.material-icons');
$blocksContent.stop(true, true);
if ($blocksContent.is(':visible')) {
$blocksContent.slideUp(200);
self.$wrapper.addClass('blocks-collapsed');
$icon.text('expand_more');
} else {
$blocksContent.slideDown(200);
self.$wrapper.removeClass('blocks-collapsed');
$icon.text('expand_less');
}
});
// Group-level collapse toggle (click on group header or toggle icon)
this.$wrapper.on('click', '.group-header', function(e) {
if ($(e.target).closest('.btn-remove-group, .group-name-input').length) {
@@ -1057,6 +1078,8 @@
var $row = $(this).closest('.group-include, .exclude-row');
var id = $chip.data('id');
console.log('[EntitySelector] Chip remove clicked, id:', id);
// Also remove from pending selections if dropdown is open
if (self.pendingSelections) {
self.pendingSelections = self.pendingSelections.filter(function(s) {
@@ -1065,6 +1088,7 @@
}
self.removeSelection($picker, id);
console.log('[EntitySelector] Calling serializeAllBlocks after chip remove');
self.serializeAllBlocks($row);
if (self.$dropdown && self.$dropdown.hasClass('show')) {
@@ -1072,6 +1096,20 @@
}
});
// Country chip holiday preview
this.$wrapper.on('click', '.chip-preview-holidays', function(e) {
e.stopPropagation();
e.preventDefault();
var $btn = $(this);
var $chip = $btn.closest('.entity-chip');
var countryId = $chip.data('id');
var countryName = $chip.find('.chip-name').text();
var countryIso = $chip.data('iso') || '';
self.showHolidayPreview(countryId, countryName, countryIso, $btn);
});
// Chips show more/less toggle
this.$wrapper.on('click', '.chips-show-more-toggle', function(e) {
e.stopPropagation();
@@ -1730,8 +1768,26 @@
self.refreshSearch();
});
// Entity-specific filters: Countries - Has holidays
this.$dropdown.on('change', '.filter-has-holidays', function() {
self.filters.hasHolidays = $(this).is(':checked');
self.refreshSearch();
});
// Entity-specific filters: Countries - Contains states
this.$dropdown.on('change', '.filter-contains-states', function() {
self.filters.containsStates = $(this).is(':checked');
self.refreshSearch();
});
// Entity-specific filters: Countries - Zone select
this.$dropdown.on('change', '.filter-zone-select', function() {
self.filters.zone = $(this).val() || null;
self.refreshSearch();
});
// Clear entity-specific filters
this.$dropdown.on('click', '.filter-row-entity-categories .btn-clear-filters, .filter-row-entity-manufacturers .btn-clear-filters, .filter-row-entity-suppliers .btn-clear-filters, .filter-row-entity-attributes .btn-clear-filters, .filter-row-entity-features .btn-clear-filters, .filter-row-entity-cms .btn-clear-filters, .filter-row-entity-cms-categories .btn-clear-filters', function(e) {
this.$dropdown.on('click', '.filter-row-entity-categories .btn-clear-filters, .filter-row-entity-manufacturers .btn-clear-filters, .filter-row-entity-suppliers .btn-clear-filters, .filter-row-entity-attributes .btn-clear-filters, .filter-row-entity-features .btn-clear-filters, .filter-row-entity-cms .btn-clear-filters, .filter-row-entity-cms-categories .btn-clear-filters, .filter-row-entity-countries .btn-clear-filters', function(e) {
e.preventDefault();
var $row = $(this).closest('.filter-row');
$row.find('input[type="number"]').val('');
@@ -1760,6 +1816,10 @@
self.filters.dateAddTo = null;
self.filters.lastProductFrom = null;
self.filters.lastProductTo = null;
// Country filters
self.filters.hasHolidays = false;
self.filters.containsStates = false;
self.filters.zone = null;
self.refreshSearch();
});

View File

@@ -50,7 +50,11 @@
dateAddFrom: null,
dateAddTo: null,
lastProductFrom: null,
lastProductTo: null
lastProductTo: null,
// Country-specific filters
hasHolidays: false,
containsStates: false,
zone: null
};
if (this.$dropdown) {
@@ -66,6 +70,10 @@
this.$dropdown.find('.filter-depth-select').val('');
this.$dropdown.find('.filter-has-products').prop('checked', false);
this.$dropdown.find('.filter-active-only').prop('checked', true);
// Country filters
this.$dropdown.find('.filter-has-holidays').prop('checked', false);
this.$dropdown.find('.filter-contains-states').prop('checked', false);
this.$dropdown.find('.filter-zone-select').val('');
}
this.refreshSearch();
@@ -99,7 +107,11 @@
dateAddFrom: null,
dateAddTo: null,
lastProductFrom: null,
lastProductTo: null
lastProductTo: null,
// Country-specific filters
hasHolidays: false,
containsStates: false,
zone: null
};
},
@@ -133,6 +145,11 @@
this.$dropdown.removeClass('view-tree view-cols-2 view-cols-3 view-cols-4 view-cols-5 view-cols-6 view-cols-7 view-cols-8').addClass('view-list');
}
// Load zones for countries filter
if (entityType === 'countries') {
this.loadZonesForCountryFilter();
}
// Update sort options for entity type
this.updateSortOptionsForEntity(entityType);
},
@@ -334,6 +351,46 @@
$toggle.toggleClass('has-selection', hasActiveInGroup);
});
}
},
/**
* Load zones for country filter dropdown
*/
loadZonesForCountryFilter: function() {
var self = this;
if (this.zonesLoaded || !this.$dropdown) {
return;
}
var $select = this.$dropdown.find('.filter-zone-select');
if (!$select.length) {
return;
}
$.ajax({
url: this.config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
ajax: 1,
action: 'getZonesForFilter',
trait: 'EntitySelector'
},
success: function(response) {
if (response.success && response.zones && response.zones.length > 0) {
var trans = self.config.trans || {};
$select.empty();
$select.append('<option value="">' + (trans.all_zones || 'All zones') + '</option>');
response.zones.forEach(function(zone) {
$select.append('<option value="' + zone.id + '">' + self.escapeHtml(zone.name) + '</option>');
});
self.zonesLoaded = true;
}
}
});
}
};

View File

@@ -552,6 +552,30 @@
this.previewBlockType = blockType;
// If items not loaded yet, fetch them first
if (items.length === 0 && previewData.count > 0) {
$badge.addClass('loading');
this.fetchTabPreviewItems($tab, function(fetchedItems, hasMore) {
$badge.removeClass('loading');
self.createPreviewPopover({
$badge: $badge,
items: fetchedItems,
totalCount: previewData.count,
hasMore: hasMore,
entityLabel: entityLabelPlural,
previewType: 'tab',
context: { $tab: $tab, blockType: blockType },
onLoadMore: function($btn) {
self.loadMoreTabPreviewItems($tab, $btn);
},
onFilter: function(query) {
self.filterTabPreviewItems($tab, query);
}
});
});
return;
}
this.createPreviewPopover({
$badge: $badge,
items: items,
@@ -569,6 +593,57 @@
});
},
/**
* Fetch preview items for a tab via AJAX
*/
fetchTabPreviewItems: function($tab, callback) {
var self = this;
var blockType = $tab.data('blockType');
var $hiddenInput = this.$wrapper.find('input[name="' + this.config.name + '"]');
var savedData = {};
try {
savedData = JSON.parse($hiddenInput.val() || '{}');
} catch (e) {
callback([], false);
return;
}
var groups = (savedData[blockType] && savedData[blockType].groups) ? savedData[blockType].groups : [];
if (groups.length === 0) {
callback([], false);
return;
}
var data = {};
data[blockType] = { groups: groups };
$.ajax({
url: this.config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
ajax: 1,
action: 'previewEntitySelector',
trait: 'EntitySelector',
conditions: JSON.stringify(data),
block_type: blockType,
limit: 20,
offset: 0
},
success: function(response) {
var items = response.items || response.products || [];
var hasMore = response.hasMore || (response.count > items.length);
// Update stored preview data with items
$tab.data('previewData', response);
callback(items, hasMore);
},
error: function() {
callback([], false);
}
});
},
/**
* AJAX filter handler for tab preview
*/
@@ -686,6 +761,12 @@
return;
}
// Check if this is a country holidays badge
if (conditionData.isCountryHolidays && conditionData.countryIds) {
this.showCountriesHolidayPreview($badge, conditionData.countryIds);
return;
}
this.hidePreviewPopover();
$badge.addClass('popover-open loading');
@@ -1460,6 +1541,392 @@
}
$popover.hide().fadeIn(150);
},
// =========================================================================
// HOLIDAY PREVIEW (Country chip eye button click)
// =========================================================================
/**
* Show holiday preview popover for a country
* @param {number} countryId - Country ID
* @param {string} countryName - Country name
* @param {string} countryIso - Country ISO code
* @param {jQuery} $trigger - The button element that triggered this
*/
showHolidayPreview: function(countryId, countryName, countryIso, $trigger) {
var self = this;
var trans = this.config.trans || {};
// Close any existing holiday popover
$('.holiday-preview-popover').remove();
// Create popover HTML
var popoverHtml = '<div class="holiday-preview-popover target-preview-popover show">';
popoverHtml += '<div class="popover-header">';
popoverHtml += '<span class="popover-title">';
if (countryIso) {
popoverHtml += '<img src="https://flagcdn.com/20x15/' + this.escapeAttr(countryIso.toLowerCase()) + '.png" alt="" class="popover-flag"> ';
}
popoverHtml += this.escapeHtml(countryName) + ' - ' + (trans.holidays || 'Holidays');
popoverHtml += '</span>';
popoverHtml += '<button type="button" class="popover-close"><i class="material-icons">close</i></button>';
popoverHtml += '</div>';
popoverHtml += '<div class="popover-body">';
popoverHtml += '<div class="holiday-preview-loading"><i class="material-icons icon-spin">sync</i> ' + (trans.loading || 'Loading...') + '</div>';
popoverHtml += '</div>';
popoverHtml += '</div>';
var $popover = $(popoverHtml);
$('body').append($popover);
// Position popover near the trigger button
var triggerRect = $trigger[0].getBoundingClientRect();
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
var popoverWidth = $popover.outerWidth();
var popoverHeight = $popover.outerHeight();
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Default: position below and to the right of the trigger
var top = triggerRect.bottom + scrollTop + 8;
var left = triggerRect.left + scrollLeft;
// Adjust horizontal position if it goes off-screen
if (left + popoverWidth > windowWidth - 10) {
left = windowWidth - popoverWidth - 10;
}
if (left < 10) {
left = 10;
}
// Adjust vertical position if it goes below viewport
if (triggerRect.bottom + popoverHeight > windowHeight - 10) {
// Position above the trigger instead
top = triggerRect.top + scrollTop - popoverHeight - 8;
}
$popover.css({
position: 'absolute',
top: top,
left: left,
zIndex: 10001
});
// Close button handler
$popover.find('.popover-close').on('click', function() {
$popover.remove();
});
// Fetch holidays via AJAX
$.ajax({
url: this.config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
ajax: 1,
action: 'getHolidaysPreview',
trait: 'EntitySelector',
id_country: countryId
},
success: function(response) {
if (response.success && response.holidays && response.holidays.length > 0) {
var listHtml = '<div class="holiday-list">';
for (var i = 0; i < response.holidays.length; i++) {
var h = response.holidays[i];
var typeClass = h.type ? 'holiday-type-' + h.type.toLowerCase().replace(/\s+/g, '-') : '';
listHtml += '<div class="holiday-item ' + typeClass + '">';
listHtml += '<div class="holiday-date">';
listHtml += '<span class="holiday-day">' + self.escapeHtml(h.date_formatted || h.date) + '</span>';
if (h.day_of_week) {
listHtml += '<span class="holiday-weekday">' + self.escapeHtml(h.day_of_week) + '</span>';
}
listHtml += '</div>';
listHtml += '<div class="holiday-info">';
listHtml += '<span class="holiday-name">' + self.escapeHtml(h.name) + '</span>';
if (h.type) {
listHtml += '<span class="holiday-type-badge">' + self.escapeHtml(h.type) + '</span>';
}
listHtml += '</div>';
listHtml += '</div>';
}
listHtml += '</div>';
if (response.total_count) {
listHtml += '<div class="holiday-preview-note">' + response.total_count + ' ' + (trans.upcoming_holidays || 'upcoming holidays') + '</div>';
}
$popover.find('.popover-body').html(listHtml);
} else {
var noDataHtml = '<div class="holiday-preview-empty">';
noDataHtml += '<i class="material-icons">event_busy</i>';
noDataHtml += '<p>' + (trans.no_holidays || 'No holidays found') + '</p>';
noDataHtml += '</div>';
$popover.find('.popover-body').html(noDataHtml);
}
// Re-adjust position after content loaded
var newPopoverHeight = $popover.outerHeight();
if (triggerRect.bottom + newPopoverHeight > windowHeight - 10) {
var newTop = triggerRect.top + scrollTop - newPopoverHeight - 8;
if (newTop > 10) {
$popover.css('top', newTop);
}
}
},
error: function() {
var errorHtml = '<div class="holiday-preview-empty">';
errorHtml += '<i class="material-icons">error_outline</i>';
errorHtml += '<p>' + (trans.error_loading || 'Error loading holidays') + '</p>';
errorHtml += '</div>';
$popover.find('.popover-body').html(errorHtml);
}
});
},
// =========================================================================
// COUNTRIES HOLIDAY PREVIEW (Condition badge for multiple countries)
// =========================================================================
/**
* Show holiday preview popover for multiple selected countries
* @param {jQuery} $badge - The condition-match-count badge element
* @param {Array} countryIds - Array of country IDs
*/
showCountriesHolidayPreview: function($badge, countryIds) {
var self = this;
var trans = this.config.trans || {};
// Close any existing popovers
this.hidePreviewPopover();
$('.holiday-preview-popover').remove();
// Create popover HTML with placeholder title (will update after AJAX)
var popoverHtml = '<div class="holiday-preview-popover target-preview-popover show">';
popoverHtml += '<div class="popover-header">';
popoverHtml += '<span class="popover-title"><i class="material-icons icon-spin">sync</i> ' + (trans.loading || 'Loading...') + '</span>';
popoverHtml += '<button type="button" class="popover-close"><i class="material-icons">close</i></button>';
popoverHtml += '</div>';
popoverHtml += '<div class="popover-filter">';
popoverHtml += '<i class="material-icons">search</i>';
popoverHtml += '<input type="text" class="holiday-filter-input" placeholder="' + (trans.filter_holidays || 'Filter by country, date, name...') + '">';
popoverHtml += '</div>';
popoverHtml += '<div class="popover-body">';
popoverHtml += '<div class="holiday-preview-loading"><i class="material-icons icon-spin">sync</i> ' + (trans.loading || 'Loading...') + '</div>';
popoverHtml += '</div>';
popoverHtml += '</div>';
var $popover = $(popoverHtml);
$('body').append($popover);
// Position popover near the badge
var badgeRect = $badge[0].getBoundingClientRect();
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
var popoverWidth = $popover.outerWidth();
var popoverHeight = $popover.outerHeight();
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// Default: position below the badge
var top = badgeRect.bottom + scrollTop + 8;
var left = badgeRect.left + scrollLeft;
// Adjust horizontal position
if (left + popoverWidth > windowWidth - 10) {
left = windowWidth - popoverWidth - 10;
}
if (left < 10) {
left = 10;
}
// Adjust vertical position if it goes below viewport
if (badgeRect.bottom + popoverHeight > windowHeight - 10) {
top = badgeRect.top + scrollTop - popoverHeight - 8;
}
$popover.css({
position: 'absolute',
top: top,
left: left,
zIndex: 10001
});
// Mark badge as open
$badge.addClass('popover-open');
this.$activeBadge = $badge;
// Close button handler
$popover.find('.popover-close').on('click', function() {
$popover.remove();
$badge.removeClass('popover-open');
self.$activeBadge = null;
});
// Fetch holidays via AJAX
$.ajax({
url: this.config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: {
ajax: 1,
action: 'getHolidaysForCountries',
trait: 'EntitySelector',
country_ids: countryIds.join(','),
count_only: 0
},
success: function(response) {
if (response.success && response.holidays && response.holidays.length > 0) {
// Count holidays per country to determine which flags to show
var countryHolidayCounts = {};
var countriesMap = {};
// Build map of countries from response
if (response.countries) {
for (var ci = 0; ci < response.countries.length; ci++) {
var cInfo = response.countries[ci];
countriesMap[cInfo.id] = cInfo;
countryHolidayCounts[cInfo.id] = 0;
}
}
// Count holidays per country
for (var hi = 0; hi < response.holidays.length; hi++) {
var hol = response.holidays[hi];
if (hol.country_id && typeof countryHolidayCounts[hol.country_id] !== 'undefined') {
countryHolidayCounts[hol.country_id]++;
}
}
// Sort countries by holiday count (descending)
var sortedCountries = Object.keys(countriesMap).sort(function(a, b) {
return (countryHolidayCounts[b] || 0) - (countryHolidayCounts[a] || 0);
}).map(function(id) {
return countriesMap[id];
});
// Build header title with flags for countries with most holidays
var titleHtml = '';
var numCountries = sortedCountries.length;
if (numCountries <= 3) {
for (var fi = 0; fi < numCountries; fi++) {
var fc = sortedCountries[fi];
if (fc && fc.iso_code) {
titleHtml += '<img src="https://flagcdn.com/20x15/' + self.escapeAttr(fc.iso_code.toLowerCase()) + '.png" alt="' + self.escapeAttr(fc.iso_code) + '" class="popover-flag" title="' + self.escapeAttr(fc.name) + '"> ';
}
}
} else {
// Show top 2 flags (countries with most holidays) + count
for (var fj = 0; fj < 2; fj++) {
var fcc = sortedCountries[fj];
if (fcc && fcc.iso_code) {
titleHtml += '<img src="https://flagcdn.com/20x15/' + self.escapeAttr(fcc.iso_code.toLowerCase()) + '.png" alt="" class="popover-flag" title="' + self.escapeAttr(fcc.name) + '"> ';
}
}
titleHtml += '+' + (numCountries - 2) + ' ';
}
titleHtml += response.total_count + ' ' + (trans.holidays || 'Holidays');
// Update header title
$popover.find('.popover-title').html(titleHtml);
var listHtml = '<div class="holiday-list">';
for (var i = 0; i < response.holidays.length; i++) {
var h = response.holidays[i];
var typeClass = h.type ? 'holiday-type-' + h.type.toLowerCase().replace(/\s+/g, '-') : '';
// Build search text for filtering
var searchText = [
h.name || '',
h.date_formatted || h.date || '',
h.day_of_week || '',
h.country_name || '',
h.type || ''
].join(' ').toLowerCase();
listHtml += '<div class="holiday-item ' + typeClass + '" data-search="' + self.escapeAttr(searchText) + '">';
listHtml += '<div class="holiday-date">';
listHtml += '<span class="holiday-day">' + self.escapeHtml(h.date_formatted || h.date) + '</span>';
if (h.day_of_week) {
listHtml += '<span class="holiday-weekday">' + self.escapeHtml(h.day_of_week) + '</span>';
}
listHtml += '</div>';
listHtml += '<div class="holiday-info">';
// Show country flag before holiday name when multiple countries
if (h.country_iso && countryIds.length > 1) {
listHtml += '<img src="https://flagcdn.com/16x12/' + self.escapeAttr(h.country_iso.toLowerCase()) + '.png" alt="" class="holiday-country-flag" title="' + self.escapeAttr(h.country_name || '') + '"> ';
}
listHtml += '<span class="holiday-name">' + self.escapeHtml(h.name) + '</span>';
if (h.type) {
listHtml += '<span class="holiday-type-badge">' + self.escapeHtml(h.type) + '</span>';
}
listHtml += '</div>';
listHtml += '</div>';
}
listHtml += '</div>';
if (response.total_count && countryIds.length > 1) {
var noteText = (trans.across_countries || 'across') + ' ' + countryIds.length + ' ' + (trans.countries || 'countries');
listHtml += '<div class="holiday-preview-note">' + noteText + '</div>';
}
$popover.find('.popover-body').html(listHtml);
// Setup filter input handler
$popover.find('.holiday-filter-input').on('input', function() {
var query = $(this).val().toLowerCase().trim();
var $items = $popover.find('.holiday-item');
var visibleCount = 0;
$items.each(function() {
var searchData = $(this).attr('data-search') || '';
if (!query || searchData.indexOf(query) !== -1) {
$(this).show();
visibleCount++;
} else {
$(this).hide();
}
});
// Update note with filtered count
var $note = $popover.find('.holiday-preview-note');
if (query && $note.length) {
$note.text(visibleCount + ' ' + (trans.matches || 'matches'));
} else if ($note.length && countryIds.length > 1) {
var noteText = (trans.across_countries || 'across') + ' ' + countryIds.length + ' ' + (trans.countries || 'countries');
$note.text(noteText);
}
});
} else {
// Update header for empty state
$popover.find('.popover-title').html('0 ' + (trans.holidays || 'Holidays'));
var noDataHtml = '<div class="holiday-preview-empty">';
noDataHtml += '<i class="material-icons">event_busy</i>';
noDataHtml += '<p>' + (trans.no_holidays || 'No holidays found') + '</p>';
noDataHtml += '</div>';
$popover.find('.popover-body').html(noDataHtml);
}
// Re-adjust position after content loaded
var newPopoverHeight = $popover.outerHeight();
if (badgeRect.bottom + newPopoverHeight > windowHeight - 10) {
var newTop = badgeRect.top + scrollTop - newPopoverHeight - 8;
if (newTop > 10) {
$popover.css('top', newTop);
}
}
},
error: function() {
// Update header for error state
$popover.find('.popover-title').html('<i class="material-icons">error_outline</i> ' + (trans.error || 'Error'));
var errorHtml = '<div class="holiday-preview-empty">';
errorHtml += '<i class="material-icons">error_outline</i>';
errorHtml += '<p>' + (trans.error_loading || 'Error loading holidays') + '</p>';
errorHtml += '</div>';
$popover.find('.popover-body').html(errorHtml);
}
});
}
};

View File

@@ -3,6 +3,7 @@
* Reusable patterns - prefer Bootstrap utilities in HTML where possible
*/
@use "sass:color";
@use 'variables' as *;
// =============================================================================
@@ -231,7 +232,7 @@
// Popover open state
&.popover-open {
background: darken($bg, 10%);
background: color.adjust($bg, $lightness: -10%);
box-shadow: 0 2px 8px rgba($bg, 0.4);
}

View File

@@ -3,6 +3,7 @@
* Product attribute combination selection styles
*/
@use "sass:color";
@use '../variables' as *;
@use '../mixins' as *;
@@ -298,7 +299,7 @@
&:hover {
background: $es-primary-hover;
border-color: darken($es-primary-hover, 5%);
border-color: color.adjust($es-primary-hover, $lightness: -5%);
}
}
}

View File

@@ -88,6 +88,29 @@
text-overflow: ellipsis;
}
// Schedule summary (shows current config at a glance)
.trait-summary {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.25rem 0.625rem;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
color: $es-primary;
background: rgba($es-primary, 0.08);
border-radius: $es-radius-full;
white-space: nowrap;
margin-left: $es-spacing-md;
flex-shrink: 0;
max-width: 320px;
overflow: hidden;
text-overflow: ellipsis;
&:empty {
display: none;
}
}
.trait-header-right {
display: flex;
align-items: center;
@@ -277,6 +300,48 @@
gap: $es-spacing-md;
}
// =============================================================================
// Collapse Header (form-content layout)
// =============================================================================
.entity-selector-collapse-header {
padding: 0;
margin-bottom: $es-spacing-sm;
.btn-collapse-toggle {
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.25rem 0;
background: none;
border: none;
color: $es-primary;
font-size: $es-font-size-sm;
cursor: pointer;
transition: color $es-transition-fast;
&:hover {
color: $es-primary-hover;
}
.collapse-icon {
font-size: 1.25rem;
transition: transform 0.2s;
}
.collapse-label {
font-weight: $es-font-weight-medium;
}
}
}
// When collapsed, rotate icon
.condition-trait.collapsed .entity-selector-collapse-header {
.collapse-icon {
// Icon already shows expand_more when collapsed
}
}
// =============================================================================
// Animations
// =============================================================================

View File

@@ -229,6 +229,55 @@
}
}
// Tabs row with actions (form-content layout)
.entity-selector-tabs-row {
display: flex;
align-items: stretch;
background: $es-slate-100;
border-bottom: 1px solid $es-border-color;
border-radius: $es-radius-lg $es-radius-lg 0 0;
.target-block-tabs {
flex: 1;
border-bottom: 0;
border-radius: $es-radius-lg 0 0 0;
}
}
// Expand/collapse toggle area (entire section is clickable)
.entity-selector-actions.btn-toggle-blocks {
display: flex;
align-items: center;
justify-content: center;
padding: 0 $es-spacing-md;
background: $es-slate-100;
border-left: 1px solid $es-border-color;
color: $es-slate-400;
cursor: pointer;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
color: $es-primary;
}
.material-icons {
font-size: 20px !important;
}
}
// When expanded - highlight the toggle area
.entity-selector-trait:not(.blocks-collapsed) .entity-selector-actions.btn-toggle-blocks {
background: $es-primary-light;
border-left-color: $es-primary;
color: $es-primary;
}
// Blocks content wrapper (for form-content layout collapse)
.entity-selector-blocks-content {
// Inherits styles from condition-trait-body context
}
// Block container
.target-block-container {
display: none;

View File

@@ -954,6 +954,20 @@
color: $es-primary;
}
// Country holidays variant - use calendar icon style
&.country-holidays {
background: rgba(139, 92, 246, 0.1);
color: #8b5cf6;
&:hover {
background: rgba(139, 92, 246, 0.2);
}
&.clickable {
background: rgba(139, 92, 246, 0.15);
}
}
i {
font-size: 10px;
}

View File

@@ -517,3 +517,73 @@
opacity: 0.9;
}
}
// =============================================================================
// Schedule Preview Dropdown (for admin list columns)
// =============================================================================
.schedule-preview-dropdown {
display: none;
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
margin-top: 4px;
z-index: 1000;
min-width: 180px;
padding: $es-spacing-sm;
background: $es-white;
border: 1px solid $es-border-color;
border-radius: $es-radius-md;
box-shadow: $es-shadow-lg;
font-size: $es-font-size-sm;
text-align: left;
white-space: nowrap;
}
.schedule-preview-item {
display: flex;
align-items: flex-start;
gap: $es-spacing-sm;
padding: $es-spacing-xs 0;
color: $es-text-primary;
.material-icons {
flex-shrink: 0;
font-size: 16px;
color: $es-text-muted;
}
strong {
font-weight: $es-font-weight-semibold;
}
.schedule-time {
color: $es-text-muted;
}
}
// Badge trigger for schedule preview
.btn-schedule-preview {
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.25rem;
min-width: 20px;
height: 20px;
padding: 0 0.5rem;
background: $es-primary;
color: $es-white;
font-size: $es-font-size-xs;
font-weight: $es-font-weight-semibold;
border-radius: 50rem;
cursor: pointer;
text-transform: none;
.icon-eye {
font-size: 10px;
line-height: 1;
opacity: 0.8;
}
}

View File

@@ -3,6 +3,7 @@
* Preview modals, confirmation dialogs
*/
@use "sass:color";
@use '../variables' as *;
@use '../mixins' as *;
@@ -162,7 +163,7 @@
background: $es-danger;
&:hover {
background: darken($es-danger, 10%);
background: color.adjust($es-danger, $lightness: -10%);
}
}
@@ -273,3 +274,215 @@
border-top: none;
}
}
// ==========================================================================
// Holiday Preview Modal
// ==========================================================================
#mpr-holiday-preview-modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: $es-z-modal;
&.show {
display: flex;
align-items: center;
justify-content: center;
}
.mpr-modal-backdrop {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
}
.mpr-modal-dialog {
position: relative;
width: 90%;
max-width: 480px;
max-height: 80vh;
background: $es-white;
border-radius: $es-radius-lg;
box-shadow: $es-shadow-xl;
display: flex;
flex-direction: column;
overflow: hidden;
}
.mpr-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: $es-spacing-md;
padding: $es-spacing-md $es-spacing-lg;
background: $es-bg-header;
border-bottom: 1px solid $es-border-color;
flex-shrink: 0;
}
.mpr-modal-title {
display: flex;
align-items: center;
gap: $es-spacing-sm;
font-size: $es-font-size-base;
font-weight: $es-font-weight-semibold;
color: $es-text-primary;
margin: 0;
i.material-icons {
font-size: 20px;
color: $es-primary;
}
}
.mpr-modal-close {
@include button-reset;
display: flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
color: $es-text-muted;
border-radius: $es-radius-md;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
color: $es-text-secondary;
}
i {
font-size: 18px;
}
}
.mpr-modal-body {
flex: 1;
overflow-y: auto;
padding: $es-spacing-lg;
@include custom-scrollbar;
}
// Loading state
.holiday-preview-loading {
display: flex;
align-items: center;
justify-content: center;
gap: $es-spacing-sm;
padding: $es-spacing-xl 0;
color: $es-text-muted;
font-size: $es-font-size-sm;
i {
font-size: $es-font-size-lg;
}
}
// Empty state
.holiday-preview-empty {
text-align: center;
padding: $es-spacing-xl 0;
color: $es-text-muted;
i.material-icons {
font-size: 48px;
opacity: 0.5;
margin-bottom: $es-spacing-md;
}
p {
margin: 0 0 $es-spacing-xs;
}
.hint {
font-size: $es-font-size-xs;
color: $es-text-muted;
}
}
// Holiday list
.holiday-list {
display: flex;
flex-direction: column;
gap: $es-spacing-sm;
}
.holiday-item {
display: flex;
align-items: flex-start;
gap: $es-spacing-md;
padding: $es-spacing-sm $es-spacing-md;
background: $es-slate-50;
border-radius: $es-radius-md;
border-left: 3px solid $es-success;
&.holiday-type-bank {
border-left-color: $es-info;
}
&.holiday-type-observance {
border-left-color: $es-warning;
}
&.holiday-type-regional {
border-left-color: #8b5cf6;
}
}
.holiday-date {
flex-shrink: 0;
min-width: 100px;
.holiday-day {
display: block;
font-size: $es-font-size-sm;
font-weight: $es-font-weight-semibold;
color: $es-text-primary;
}
.holiday-weekday {
display: block;
font-size: $es-font-size-xs;
color: $es-text-muted;
}
}
.holiday-info {
flex: 1;
min-width: 0;
}
.holiday-name {
display: block;
font-size: $es-font-size-sm;
color: $es-text-primary;
word-wrap: break-word;
}
.holiday-type-badge {
display: inline-block;
margin-top: $es-spacing-xs;
padding: 0.125rem 0.375rem;
font-size: 10px;
font-weight: $es-font-weight-medium;
text-transform: capitalize;
background: $es-slate-200;
color: $es-text-secondary;
border-radius: $es-radius-sm;
}
.holiday-preview-note {
margin-top: $es-spacing-md;
font-size: $es-font-size-xs;
color: $es-text-muted;
text-align: center;
}
}

View File

@@ -3,6 +3,7 @@
* Hierarchical tree view for category selection inside dropdown
*/
@use "sass:color";
@use '../variables' as *;
@use '../mixins' as *;
@@ -231,7 +232,7 @@
}
&.popover-open {
background: darken($es-primary, 10%);
background: color.adjust($es-primary, $lightness: -10%);
}
}
}

View File

@@ -201,28 +201,36 @@
display: inline-flex;
align-items: center;
gap: 0.25rem;
padding: 0.375rem 0.625rem;
color: $es-text-secondary;
background: $es-slate-100;
border: 1px solid transparent;
border-radius: $es-radius-sm;
padding: 0.375rem 0.75rem;
color: $es-text-muted;
background: transparent;
border: 1px dashed $es-border-color;
border-radius: 100px; // Pill shape
font-size: $es-font-size-xs;
font-weight: $es-font-weight-medium;
font-weight: $es-font-weight-normal;
cursor: pointer;
transition: all $es-transition-fast;
&:hover {
background: $es-slate-200;
color: $es-text-secondary;
border-color: $es-slate-400;
border-style: solid;
}
&.selected {
color: $es-primary;
background: $es-primary-light;
border-color: $es-primary;
border: 1px solid $es-primary;
font-weight: $es-font-weight-medium;
}
i {
font-size: 12px;
font-size: 11px;
opacity: 0.6;
}
&.selected i {
opacity: 1;
}
}