Initial commit: prestashop-entity-selector
Forked from prestashop-target-conditions Renamed all references from target-conditions to entity-selector
This commit is contained in:
574
README.md
Executable file
574
README.md
Executable file
@@ -0,0 +1,574 @@
|
||||
# PrestaShop Condition Traits
|
||||
|
||||
A collection of reusable PHP traits for PrestaShop admin controllers that provide powerful UI widgets for targeting and scheduling.
|
||||
|
||||
## Available Traits
|
||||
|
||||
| Trait | Purpose |
|
||||
|-------|---------|
|
||||
| **TargetConditions** | Multi-criteria targeting (countries, currencies, carriers, groups, languages) |
|
||||
| **ScheduleConditions** | Time-based scheduling (datetime range, weekly schedule, holiday exclusions) |
|
||||
|
||||
## Installation
|
||||
|
||||
### Via Composer
|
||||
|
||||
```bash
|
||||
composer require myprestarocks/prestashop-target-conditions
|
||||
```
|
||||
|
||||
### Manual Installation
|
||||
|
||||
Copy the package to your module's vendor directory:
|
||||
```
|
||||
modules/your_module/vendor/myprestarocks/prestashop-target-conditions/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# TargetConditions Trait
|
||||
|
||||
A powerful multi-criteria targeting widget. Allows users to define conditions based on countries, currencies, carriers, customer groups, and languages.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### 1. Include the Trait in Your Admin Controller
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use MyPrestaRocks\TargetConditions\TargetConditions;
|
||||
|
||||
class AdminYourModuleController extends ModuleAdminController
|
||||
{
|
||||
use TargetConditions;
|
||||
|
||||
// Your controller code...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Register Assets in setMedia()
|
||||
|
||||
```php
|
||||
public function setMedia($isNewTheme = false)
|
||||
{
|
||||
parent::setMedia($isNewTheme);
|
||||
|
||||
// Register the target conditions assets
|
||||
$this->registerTargetConditionsAssets();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Add the Widget to Your Form
|
||||
|
||||
In your form rendering method (e.g., `renderForm()`), add the widget:
|
||||
|
||||
```php
|
||||
public function renderForm()
|
||||
{
|
||||
// Get existing restrictions from database (JSON string or array)
|
||||
$restrictions = $this->object->restrictions ?? '[]';
|
||||
|
||||
$this->fields_form = [
|
||||
'legend' => [
|
||||
'title' => $this->l('Configuration'),
|
||||
],
|
||||
'input' => [
|
||||
[
|
||||
'type' => 'free',
|
||||
'label' => $this->l('Target Conditions'),
|
||||
'name' => 'restrictions_widget',
|
||||
'desc' => $this->l('Define where this item should be available'),
|
||||
],
|
||||
],
|
||||
// ... other fields
|
||||
];
|
||||
|
||||
// Render the widget HTML
|
||||
$this->fields_value['restrictions_widget'] = $this->renderTargetConditionsHtml(
|
||||
'restrictions', // Field name for form submission
|
||||
$restrictions, // Current value (JSON or array)
|
||||
[
|
||||
// Configuration options (see below)
|
||||
]
|
||||
);
|
||||
|
||||
return parent::renderForm();
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Process the Submitted Data
|
||||
|
||||
```php
|
||||
public function postProcess()
|
||||
{
|
||||
if (Tools::isSubmit('submitYourForm')) {
|
||||
$restrictions = Tools::getValue('restrictions');
|
||||
|
||||
// The value is already JSON-encoded from the widget
|
||||
// Store it in your database
|
||||
$this->object->restrictions = $restrictions;
|
||||
$this->object->save();
|
||||
}
|
||||
|
||||
return parent::postProcess();
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
The `renderTargetConditionsHtml()` method accepts a configuration array as the third parameter:
|
||||
|
||||
```php
|
||||
$this->renderTargetConditionsHtml('field_name', $value, [
|
||||
// Available condition groups to show
|
||||
// Default: all groups enabled
|
||||
'groups' => [
|
||||
'countries' => true, // Country selection
|
||||
'currencies' => true, // Currency selection
|
||||
'carriers' => true, // Carrier/shipping method selection
|
||||
'groups' => true, // Customer group selection
|
||||
'languages' => true, // Language selection
|
||||
],
|
||||
|
||||
// Empty selection behavior
|
||||
// true = empty means "all included" (available everywhere)
|
||||
// false = empty means "none selected" (not available anywhere)
|
||||
'empty_means_all' => true,
|
||||
|
||||
// Show "All X" toggle badges
|
||||
// When set, displays toggle buttons showing selection state
|
||||
'show_all_toggle' => [
|
||||
'empty' => $this->l('Available Everywhere'), // Label when nothing selected
|
||||
'selected' => $this->l('Restricted'), // Label when items selected
|
||||
],
|
||||
|
||||
// Show group modifiers (Limit/Sort controls)
|
||||
// true = show limit dropdown and sort controls per group
|
||||
// false = hide modifiers (cleaner UI for targeting contexts)
|
||||
'show_modifiers' => true,
|
||||
|
||||
// Pre-filter available options based on external restrictions
|
||||
// Useful when parent entity has its own restrictions
|
||||
'restrictions' => [
|
||||
'countries' => ['FR', 'DE', 'PL'], // Only show these countries
|
||||
'currencies' => ['EUR', 'PLN'], // Only show these currencies
|
||||
// ... etc
|
||||
],
|
||||
|
||||
// Custom labels for condition groups
|
||||
'labels' => [
|
||||
'countries' => $this->l('Countries'),
|
||||
'currencies' => $this->l('Currencies'),
|
||||
'carriers' => $this->l('Carriers'),
|
||||
'groups' => $this->l('Customer Groups'),
|
||||
'languages' => $this->l('Languages'),
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
## Use Case Examples
|
||||
|
||||
### Payment Method Targeting
|
||||
|
||||
Restrict payment methods to specific countries/currencies:
|
||||
|
||||
```php
|
||||
$this->renderTargetConditionsHtml('restrictions', $paymentOption->restrictions, [
|
||||
'groups' => [
|
||||
'countries' => true,
|
||||
'currencies' => true,
|
||||
'carriers' => false, // Not relevant for payments
|
||||
'groups' => true,
|
||||
'languages' => false,
|
||||
],
|
||||
'empty_means_all' => true,
|
||||
'show_modifiers' => false, // No need for limit/sort on targeting
|
||||
'show_all_toggle' => [
|
||||
'empty' => $this->l('Available Everywhere'),
|
||||
'selected' => $this->l('Restricted'),
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
### Product Selection Widget
|
||||
|
||||
Select products with quantity limits:
|
||||
|
||||
```php
|
||||
$this->renderTargetConditionsHtml('selected_products', $rule->products, [
|
||||
'groups' => [
|
||||
'products' => true,
|
||||
],
|
||||
'empty_means_all' => false, // Must explicitly select products
|
||||
'show_modifiers' => true, // Allow limiting quantity per selection
|
||||
]);
|
||||
```
|
||||
|
||||
### Shipping Rules
|
||||
|
||||
Define carrier availability by zone:
|
||||
|
||||
```php
|
||||
$this->renderTargetConditionsHtml('availability', $carrier->zones, [
|
||||
'groups' => [
|
||||
'countries' => true,
|
||||
'groups' => true, // Different rates for customer groups
|
||||
],
|
||||
'empty_means_all' => true,
|
||||
'show_all_toggle' => [
|
||||
'empty' => $this->l('Ships Everywhere'),
|
||||
'selected' => $this->l('Limited Zones'),
|
||||
],
|
||||
]);
|
||||
```
|
||||
|
||||
## Data Format
|
||||
|
||||
The widget stores data as a JSON object with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"countries": {
|
||||
"items": ["FR", "DE", "PL"],
|
||||
"limit": 0,
|
||||
"sort": "default"
|
||||
},
|
||||
"currencies": {
|
||||
"items": ["EUR", "PLN"],
|
||||
"limit": 0,
|
||||
"sort": "default"
|
||||
},
|
||||
"carriers": {
|
||||
"items": [],
|
||||
"limit": 0,
|
||||
"sort": "default"
|
||||
},
|
||||
"groups": {
|
||||
"items": [1, 2, 3],
|
||||
"limit": 0,
|
||||
"sort": "default"
|
||||
},
|
||||
"languages": {
|
||||
"items": [],
|
||||
"limit": 0,
|
||||
"sort": "default"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
- **items**: Array of selected IDs or ISO codes
|
||||
- **limit**: Maximum items to use (0 = no limit)
|
||||
- **sort**: Sort order for items ("default", "asc", "desc", "random")
|
||||
|
||||
## Selection Logic
|
||||
|
||||
### When `empty_means_all` is `true` (default):
|
||||
- Empty selection = item available for ALL values in that group
|
||||
- Selection made = item restricted to ONLY selected values
|
||||
|
||||
### When `empty_means_all` is `false`:
|
||||
- Empty selection = item NOT available (nothing selected)
|
||||
- Selection made = item available for selected values only
|
||||
|
||||
### Restriction Inheritance
|
||||
|
||||
When using the `restrictions` config option, the widget will only display options that match the parent restrictions. This is useful for hierarchical configurations where child items cannot exceed parent permissions.
|
||||
|
||||
Example: A payment method restricted to France and Germany cannot have a sub-option available in Poland.
|
||||
|
||||
---
|
||||
|
||||
# ScheduleConditions Trait
|
||||
|
||||
A time-based scheduling widget with visual timeline controls. Allows users to define when something should be active using datetime ranges, weekly schedules, and public holiday exclusions.
|
||||
|
||||
## Features
|
||||
|
||||
- **Datetime Range**: Specific start/end period with datetime pickers
|
||||
- **Weekly Schedule**: Per-day time ranges with visual timeline sliders
|
||||
- **Public Holiday Exclusions**: Optional integration with holiday providers
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### 1. Include the Trait
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use MyPrestaRocks\TargetConditions\ScheduleConditions;
|
||||
|
||||
class AdminYourModuleController extends ModuleAdminController
|
||||
{
|
||||
use ScheduleConditions;
|
||||
|
||||
// Your controller code...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Initialize Assets in setMedia()
|
||||
|
||||
```php
|
||||
public function setMedia($isNewTheme = false)
|
||||
{
|
||||
parent::setMedia($isNewTheme);
|
||||
|
||||
// Initialize schedule conditions (loads CSS + JS)
|
||||
$this->initScheduleConditions();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Render the Widget
|
||||
|
||||
```php
|
||||
public function renderForm()
|
||||
{
|
||||
// Get saved schedule data (JSON string from database)
|
||||
$scheduleJson = $this->object->schedule ?? '{}';
|
||||
$scheduleData = json_decode($scheduleJson, true) ?: [];
|
||||
|
||||
$this->fields_form = [
|
||||
'legend' => ['title' => $this->l('Configuration')],
|
||||
'input' => [
|
||||
[
|
||||
'type' => 'free',
|
||||
'label' => $this->l('Schedule'),
|
||||
'name' => 'schedule_widget',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// Render schedule widget
|
||||
$this->fields_value['schedule_widget'] = $this->renderScheduleConditionsHtml(
|
||||
[
|
||||
'id' => 'my-schedule',
|
||||
'name_prefix' => 'schedule_',
|
||||
'title' => $this->l('Availability Schedule'),
|
||||
'subtitle' => $this->l('Define when this should be active'),
|
||||
'show_datetime_range' => true,
|
||||
'show_weekly_schedule' => true,
|
||||
'show_holiday_exclusions' => true,
|
||||
],
|
||||
$scheduleData
|
||||
);
|
||||
|
||||
return parent::renderForm();
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Process Form Submission
|
||||
|
||||
```php
|
||||
public function postProcess()
|
||||
{
|
||||
if (Tools::isSubmit('submitYourForm')) {
|
||||
// Parse schedule data from form
|
||||
$scheduleData = $this->parseScheduleConditionsFromPost('schedule_');
|
||||
|
||||
// Serialize to JSON for storage
|
||||
$this->object->schedule = $this->serializeScheduleConditions($scheduleData);
|
||||
$this->object->save();
|
||||
}
|
||||
|
||||
return parent::postProcess();
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Evaluate Schedule at Runtime
|
||||
|
||||
```php
|
||||
// Check if current time matches the schedule
|
||||
$scheduleData = $this->parseScheduleConditions($object->schedule);
|
||||
|
||||
if ($this->evaluateScheduleConditions($scheduleData)) {
|
||||
// Schedule allows - item is active
|
||||
} else {
|
||||
// Schedule blocks - item is inactive
|
||||
}
|
||||
|
||||
// Or check against a specific timestamp
|
||||
$futureTimestamp = strtotime('+1 day');
|
||||
$willBeActive = $this->evaluateScheduleConditions($scheduleData, $futureTimestamp);
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
```php
|
||||
$this->renderScheduleConditionsHtml([
|
||||
// Unique ID for the widget
|
||||
'id' => 'schedule-conditions',
|
||||
|
||||
// Form field name prefix
|
||||
'name_prefix' => 'schedule_',
|
||||
|
||||
// Header text
|
||||
'title' => 'Schedule',
|
||||
'subtitle' => 'Define when this should be active',
|
||||
|
||||
// Which sections to show
|
||||
'show_datetime_range' => true, // Start/end datetime pickers
|
||||
'show_weekly_schedule' => true, // Per-day time range sliders
|
||||
'show_holiday_exclusions' => true, // Holiday exclusion controls
|
||||
], $savedData);
|
||||
```
|
||||
|
||||
## Data Format
|
||||
|
||||
The schedule data is stored as JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"enabled": true,
|
||||
"datetime_start": "2024-12-24 08:00:00",
|
||||
"datetime_end": "2024-12-24 18:00:00",
|
||||
"weekly_schedule": {
|
||||
"0": {"enabled": false, "start": 0, "end": 1440},
|
||||
"1": {"enabled": true, "start": 540, "end": 1020},
|
||||
"2": {"enabled": true, "start": 540, "end": 1020},
|
||||
"3": {"enabled": true, "start": 540, "end": 1020},
|
||||
"4": {"enabled": true, "start": 540, "end": 1020},
|
||||
"5": {"enabled": true, "start": 540, "end": 1020},
|
||||
"6": {"enabled": false, "start": 0, "end": 1440}
|
||||
},
|
||||
"exclude_holidays": true,
|
||||
"holiday_countries": [1, 14, 17]
|
||||
}
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
- **enabled**: Whether scheduling is active (false = always active)
|
||||
- **datetime_start/end**: ISO datetime strings for absolute date range
|
||||
- **weekly_schedule**: Per-day configuration (0=Sunday, 1=Monday, etc.)
|
||||
- **enabled**: Whether this day is active
|
||||
- **start/end**: Minutes from midnight (0-1440, e.g., 540 = 09:00)
|
||||
- **exclude_holidays**: Whether to exclude public holidays
|
||||
- **holiday_countries**: Country IDs to check for holidays
|
||||
|
||||
## Public Holiday Integration
|
||||
|
||||
To enable holiday exclusions, implement the `HolidayProviderInterface`:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
use MyPrestaRocks\TargetConditions\HolidayProviderInterface;
|
||||
|
||||
class MyHolidayProvider implements HolidayProviderInterface
|
||||
{
|
||||
/**
|
||||
* Check if a date is a holiday for any of the given countries
|
||||
*/
|
||||
public static function isHoliday($date, array $countryIds)
|
||||
{
|
||||
// Your implementation
|
||||
return Db::getInstance()->getValue('
|
||||
SELECT 1 FROM ps_holidays
|
||||
WHERE holiday_date = "' . pSQL($date) . '"
|
||||
AND id_country IN (' . implode(',', array_map('intval', $countryIds)) . ')
|
||||
') ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of country IDs that have holidays configured
|
||||
*/
|
||||
public static function getCountriesWithHolidays()
|
||||
{
|
||||
return array_column(
|
||||
Db::getInstance()->executeS('SELECT DISTINCT id_country FROM ps_holidays'),
|
||||
'id_country'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get holidays within a date range
|
||||
*/
|
||||
public static function getHolidaysInRange(array $countryIds, $startDate, $endDate)
|
||||
{
|
||||
return Db::getInstance()->executeS('
|
||||
SELECT holiday_date, display_name, country_iso
|
||||
FROM ps_holidays h
|
||||
JOIN ps_country c ON c.id_country = h.id_country
|
||||
WHERE h.id_country IN (' . implode(',', array_map('intval', $countryIds)) . ')
|
||||
AND holiday_date BETWEEN "' . pSQL($startDate) . '" AND "' . pSQL($endDate) . '"
|
||||
');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Then override the provider method in your controller:
|
||||
|
||||
```php
|
||||
protected function getScheduleHolidayProvider()
|
||||
{
|
||||
return 'MyHolidayProvider';
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Methods
|
||||
|
||||
```php
|
||||
// Get human-readable description of schedule
|
||||
$description = $this->getScheduleDescription($scheduleData);
|
||||
// Returns: "Dec 24 08:00 to Dec 24 18:00 • Weekdays only • Excl. holidays (3 countries)"
|
||||
|
||||
// Format minutes to time string
|
||||
$time = $this->formatMinutesToTime(540); // Returns "09:00"
|
||||
|
||||
// Parse time string to minutes
|
||||
$minutes = $this->parseTimeToMinutes("09:00"); // Returns 540
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Assets
|
||||
|
||||
Both traits share a common CSS file with trait-specific styling:
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `assets/css/admin/condition-traits.css` | Shared styling for both traits |
|
||||
| `assets/js/admin/target-conditions.js` | TargetConditions widget functionality |
|
||||
| `assets/js/admin/schedule-conditions.js` | ScheduleConditions widget functionality |
|
||||
|
||||
## CSS Classes Reference
|
||||
|
||||
### TargetConditions
|
||||
|
||||
```css
|
||||
.condition-trait { } /* Main wrapper */
|
||||
.condition-group { } /* Individual group container */
|
||||
.condition-group-header { } /* Group header with label */
|
||||
.condition-group-items { } /* Items container */
|
||||
.condition-item { } /* Individual selectable item */
|
||||
.condition-item.selected { } /* Selected state */
|
||||
.group-modifiers { } /* Limit/sort controls container */
|
||||
.condition-trait-toggle { } /* All/restricted toggle badge */
|
||||
```
|
||||
|
||||
### ScheduleConditions
|
||||
|
||||
```css
|
||||
.schedule-conditions { } /* Main wrapper */
|
||||
.schedule-section { } /* Section container */
|
||||
.schedule-datetime-range { } /* Datetime range section */
|
||||
.schedule-weekly { } /* Weekly schedule section */
|
||||
.schedule-holidays { } /* Holiday exclusions section */
|
||||
.day-timeline { } /* Individual day row */
|
||||
.timeline-track { } /* Time range slider track */
|
||||
.timeline-range { } /* Active time range bar */
|
||||
.timeline-handle { } /* Draggable handles */
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Requirements
|
||||
|
||||
- PrestaShop 1.7.x or 8.x
|
||||
- PHP 7.1+
|
||||
|
||||
# License
|
||||
|
||||
MIT License
|
||||
1
assets/css/admin/condition-traits.css
Normal file
1
assets/css/admin/condition-traits.css
Normal file
File diff suppressed because one or more lines are too long
588
assets/css/admin/entity-list-preview.css
Executable file
588
assets/css/admin/entity-list-preview.css
Executable file
@@ -0,0 +1,588 @@
|
||||
/**
|
||||
* Target Conditions List Preview Styles
|
||||
*/
|
||||
|
||||
/* Trigger in list */
|
||||
.target-preview-trigger {
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.target-preview-trigger:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.target-preview-trigger .badge {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Backdrop */
|
||||
.target-list-preview-backdrop {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 10500;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Popover container */
|
||||
.target-list-preview-popover {
|
||||
position: fixed;
|
||||
z-index: 10501;
|
||||
width: 380px;
|
||||
height: 500px;
|
||||
max-height: calc(100vh - 100px);
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.15), 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.target-list-preview-popover .preview-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-title {
|
||||
font-weight: 600;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-close {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
border-radius: 4px;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-close:hover {
|
||||
background: #e9ecef;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.target-list-preview-popover .preview-tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
flex-shrink: 0;
|
||||
max-height: 120px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-tab {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 4px 8px;
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-tab:hover {
|
||||
background: #f8f9fa;
|
||||
border-color: #adb5bd;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-tab.active {
|
||||
background: #337ab7;
|
||||
border-color: #337ab7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-tab i {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Filter bar */
|
||||
.target-list-preview-popover .preview-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 12px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-filter i {
|
||||
color: #adb5bd;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-filter-input {
|
||||
flex: 1;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-filter-input::placeholder {
|
||||
color: #adb5bd;
|
||||
}
|
||||
|
||||
/* Content containers */
|
||||
.target-list-preview-popover .preview-contents {
|
||||
flex: 1 1 0;
|
||||
overflow: hidden;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-content {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
flex: 1 1 0;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-content.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Loading state */
|
||||
.target-list-preview-popover .preview-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 40px 20px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-loading i {
|
||||
font-size: 16px;
|
||||
color: #337ab7;
|
||||
}
|
||||
|
||||
/* Empty state */
|
||||
.target-list-preview-popover .preview-empty,
|
||||
.target-list-preview-popover .preview-empty-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 40px 20px;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-empty i,
|
||||
.target-list-preview-popover .preview-empty-inline i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Error state */
|
||||
.target-list-preview-popover .preview-error,
|
||||
.target-list-preview-popover .preview-error-inline {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
padding: 40px 20px;
|
||||
color: #dc3545;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-error i,
|
||||
.target-list-preview-popover .preview-error-inline i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* Items list */
|
||||
.target-list-preview-popover .preview-items {
|
||||
padding: 8px;
|
||||
flex: 1 1 0;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
background: #f8f9fa;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-no-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f1f3f5;
|
||||
border-radius: 4px;
|
||||
color: #adb5bd;
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-name {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-ref {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-item-price {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
/* Footer with load more */
|
||||
.target-list-preview-popover .preview-footer {
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border-top: 1px solid #e9ecef;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-label {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-select {
|
||||
padding: 4px 6px;
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
min-width: 70px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-select:hover {
|
||||
border-color: #337ab7;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-select:focus {
|
||||
outline: none;
|
||||
border-color: #337ab7;
|
||||
box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.2);
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-of {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .load-more-of .remaining-count {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .btn-load-more {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
padding: 0;
|
||||
background: #337ab7;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .btn-load-more:hover {
|
||||
background: #286090;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .btn-load-more i {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .loading-text,
|
||||
.target-list-preview-popover .error-text {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .error-text {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .loading-text i {
|
||||
color: #337ab7;
|
||||
}
|
||||
|
||||
/* Rules summary */
|
||||
.target-list-preview-popover .preview-rules {
|
||||
padding: 8px 12px;
|
||||
background: #f8f9fa;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
flex-shrink: 0;
|
||||
max-height: 140px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule-group {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 0;
|
||||
border-bottom: 1px dashed #dee2e6;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule-group:last-child {
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule-group:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-group-label {
|
||||
font-size: 10px;
|
||||
color: #6c757d;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
margin-right: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
padding: 2px 6px;
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
cursor: help;
|
||||
transition: transform 0.1s, box-shadow 0.1s;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule.include {
|
||||
background: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule.exclude {
|
||||
background: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule .rule-icon {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule .rule-icon i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-rule .rule-text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Rule detail popup - appended to body */
|
||||
.rule-detail-popup {
|
||||
position: fixed;
|
||||
min-width: 200px;
|
||||
max-width: 300px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
z-index: 10600;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.rule-detail-loading,
|
||||
.rule-detail-error,
|
||||
.rule-detail-info {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
text-align: center;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.rule-detail-info strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.rule-detail-error {
|
||||
color: #dc3545;
|
||||
}
|
||||
|
||||
.rule-detail-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.rule-detail-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.rule-detail-item:hover {
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.rule-detail-img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
object-fit: cover;
|
||||
border-radius: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.rule-detail-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f1f3f5;
|
||||
border-radius: 3px;
|
||||
color: #6c757d;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Drag and Resize */
|
||||
.target-list-preview-popover .preview-header {
|
||||
cursor: move;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .preview-header .preview-close {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .popover-resize-handle {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
cursor: se-resize;
|
||||
background: linear-gradient(135deg, transparent 50%, #adb5bd 50%, #adb5bd 60%, transparent 60%, transparent 70%, #adb5bd 70%, #adb5bd 80%, transparent 80%);
|
||||
border-radius: 0 0 8px 0;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.target-list-preview-popover .popover-resize-handle:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.target-list-preview-popover.dragging {
|
||||
cursor: move;
|
||||
opacity: 0.95;
|
||||
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.2), 0 5px 15px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.target-list-preview-popover.resizing {
|
||||
cursor: se-resize;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.target-list-preview-popover.dragging *,
|
||||
.target-list-preview-popover.resizing * {
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.target-list-preview-popover.dragging .preview-header,
|
||||
.target-list-preview-popover.resizing .popover-resize-handle {
|
||||
pointer-events: auto;
|
||||
}
|
||||
6282
assets/css/admin/entity-selector.css
Normal file
6282
assets/css/admin/entity-selector.css
Normal file
File diff suppressed because it is too large
Load Diff
184
assets/css/admin/mpr-modal.css
Executable file
184
assets/css/admin/mpr-modal.css
Executable file
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* MPR Search Revolution - Universal Modal Styles
|
||||
* All admin modals use .mpr-sr-admin-modal class
|
||||
* Works on both Symfony pages (.show) and Legacy pages (.in)
|
||||
*/
|
||||
|
||||
.mpr-sr-admin-modal.show,
|
||||
.mpr-sr-admin-modal.in {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal.show .modal-dialog,
|
||||
.mpr-sr-admin-modal.in .modal-dialog {
|
||||
transform: none !important;
|
||||
top: auto !important;
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-dialog {
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-content {
|
||||
padding: 1.25rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header,
|
||||
.mpr-sr-admin-modal .modal-body,
|
||||
.mpr-sr-admin-modal .modal-footer {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header .modal-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header .modal-title i {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header .close,
|
||||
.mpr-sr-admin-modal .modal-header .mpr-close-modal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
margin-left: auto;
|
||||
padding: 5px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.2s;
|
||||
line-height: 1;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-header .close:hover,
|
||||
.mpr-sr-admin-modal .modal-header .mpr-close-modal:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body .body-title {
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body .modal-amount {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1.25rem;
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body .tables-table {
|
||||
margin-bottom: 1rem;
|
||||
font-size: 0.875rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body .tables-table thead th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.025em;
|
||||
color: #64748b;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-body .tables-table tbody td {
|
||||
padding: 0.5rem 0.75rem;
|
||||
vertical-align: middle;
|
||||
border-color: #f1f5f9;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer {
|
||||
display: flex;
|
||||
padding-top: 1rem;
|
||||
gap: 0.75rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.625rem 1rem;
|
||||
font-weight: 500;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .btn i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .cancel-btn,
|
||||
.mpr-sr-admin-modal .modal-footer .btn-secondary {
|
||||
background: #f1f5f9;
|
||||
border: 1px solid #e2e8f0;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .cancel-btn:hover,
|
||||
.mpr-sr-admin-modal .modal-footer .btn-secondary:hover {
|
||||
background: #e2e8f0;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .confirm-btn,
|
||||
.mpr-sr-admin-modal .modal-footer .btn-primary {
|
||||
border: none;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #337ab7 0%, #286090 100%);
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .confirm-btn:hover:not(:disabled),
|
||||
.mpr-sr-admin-modal .modal-footer .btn-primary:hover:not(:disabled) {
|
||||
transform: translateY(-1px);
|
||||
background: linear-gradient(135deg, #286090 0%, #204d74 100%);
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .confirm-btn:disabled,
|
||||
.mpr-sr-admin-modal .modal-footer .btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .btn-danger {
|
||||
background: linear-gradient(135deg, #dc2626 0%, #b91c1c 100%);
|
||||
border: none;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mpr-sr-admin-modal .modal-footer .btn-danger:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, #b91c1c 0%, #991b1b 100%);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
5726
assets/css/admin/tailwind-input.css
Normal file
5726
assets/css/admin/tailwind-input.css
Normal file
File diff suppressed because it is too large
Load Diff
1
assets/css/admin/tailwind-output.css
Executable file
1
assets/css/admin/tailwind-output.css
Executable file
File diff suppressed because one or more lines are too long
1149
assets/js/admin/entity-list-preview.js
Executable file
1149
assets/js/admin/entity-list-preview.js
Executable file
File diff suppressed because it is too large
Load Diff
7951
assets/js/admin/entity-selector.js
Normal file
7951
assets/js/admin/entity-selector.js
Normal file
File diff suppressed because it is too large
Load Diff
515
assets/js/admin/modal.js
Executable file
515
assets/js/admin/modal.js
Executable file
@@ -0,0 +1,515 @@
|
||||
/**
|
||||
* MPR Express Checkout - Standardized Modal Helper
|
||||
*
|
||||
* Provides a consistent API for managing modals across the module.
|
||||
*
|
||||
* Usage:
|
||||
* const modal = new MPRModal('my-modal-id');
|
||||
* modal.show();
|
||||
* modal.setHeader('success', 'icon-check', 'Operation Complete');
|
||||
* modal.setBody('<p>Content here</p>');
|
||||
* modal.setFooter([
|
||||
* { type: 'cancel', label: 'Close' },
|
||||
* { type: 'primary', label: 'Continue', icon: 'arrow-right', onClick: () => {} }
|
||||
* ]);
|
||||
*/
|
||||
|
||||
class MPRModal {
|
||||
/**
|
||||
* @param {string} modalId - The modal element ID (without #)
|
||||
* @param {Object} options - Configuration options
|
||||
* @param {Function} options.onShow - Callback when modal is shown
|
||||
* @param {Function} options.onHide - Callback when modal is hidden
|
||||
* @param {Function} options.onCancel - Callback when cancel/close is clicked
|
||||
*/
|
||||
constructor(modalId, options = {}) {
|
||||
this.modalId = modalId;
|
||||
this.$modal = $(`#${modalId}`);
|
||||
this.$header = this.$modal.find('.mpr-modal-header');
|
||||
this.$title = this.$modal.find('.mpr-modal-title');
|
||||
this.$titleText = this.$modal.find('.mpr-modal-title-text');
|
||||
this.$titleIcon = this.$modal.find('.mpr-modal-icon');
|
||||
this.$body = this.$modal.find('.mpr-modal-body');
|
||||
this.$footer = this.$modal.find('.mpr-modal-footer');
|
||||
this.options = options;
|
||||
this.currentView = null;
|
||||
|
||||
this._bindEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind modal events
|
||||
*/
|
||||
_bindEvents() {
|
||||
this.$modal.on('shown.bs.modal', () => {
|
||||
if (typeof this.options.onShow === 'function') {
|
||||
this.options.onShow();
|
||||
}
|
||||
});
|
||||
|
||||
this.$modal.on('hidden.bs.modal', () => {
|
||||
if (typeof this.options.onHide === 'function') {
|
||||
this.options.onHide();
|
||||
}
|
||||
});
|
||||
|
||||
this.$modal.on('click', '[data-dismiss="modal"]', () => {
|
||||
if (typeof this.options.onCancel === 'function') {
|
||||
this.options.onCancel();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the modal
|
||||
* @param {Object} options - Bootstrap modal options
|
||||
*/
|
||||
show(options = {}) {
|
||||
if (this.$modal.length === 0) {
|
||||
console.error('[MPRModal] Modal element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
this.$modal.modal({
|
||||
backdrop: options.static ? 'static' : true,
|
||||
keyboard: !options.static,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the modal
|
||||
*/
|
||||
hide() {
|
||||
this.$modal.modal('hide');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header appearance
|
||||
* @param {string} type - Header type: default, primary, success, warning, danger, dark
|
||||
* @param {string} icon - Icon class without prefix (e.g., 'shield' for icon-shield)
|
||||
* @param {string} title - Title text
|
||||
*/
|
||||
setHeader(type, icon, title) {
|
||||
// Remove all header type classes
|
||||
this.$header.removeClass(
|
||||
'mpr-modal-header-default mpr-modal-header-primary mpr-modal-header-success ' +
|
||||
'mpr-modal-header-warning mpr-modal-header-danger mpr-modal-header-dark'
|
||||
);
|
||||
this.$header.addClass(`mpr-modal-header-${type}`);
|
||||
|
||||
if (icon) {
|
||||
if (this.$titleIcon.length) {
|
||||
this.$titleIcon.attr('class', `mpr-icon icon-${icon} mpr-modal-icon`);
|
||||
} else {
|
||||
this.$title.prepend(`<i class="mpr-icon icon-${icon} mpr-modal-icon"></i>`);
|
||||
this.$titleIcon = this.$modal.find('.mpr-modal-icon');
|
||||
}
|
||||
}
|
||||
|
||||
if (title !== undefined) {
|
||||
this.$titleText.text(title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set only the header type/color
|
||||
* @param {string} type - Header type: default, primary, success, warning, danger, dark
|
||||
*/
|
||||
setHeaderType(type) {
|
||||
this.$header.removeClass(
|
||||
'mpr-modal-header-default mpr-modal-header-primary mpr-modal-header-success ' +
|
||||
'mpr-modal-header-warning mpr-modal-header-danger mpr-modal-header-dark'
|
||||
);
|
||||
this.$header.addClass(`mpr-modal-header-${type}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header title
|
||||
* @param {string} title - Title text
|
||||
*/
|
||||
setTitle(title) {
|
||||
this.$titleText.text(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header icon
|
||||
* @param {string} icon - Icon class without prefix
|
||||
*/
|
||||
setIcon(icon) {
|
||||
if (this.$titleIcon.length) {
|
||||
this.$titleIcon.attr('class', `icon-${icon} mpr-modal-icon`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set modal size
|
||||
* @param {string} size - Size: sm, md, lg, xl, fullwidth
|
||||
*/
|
||||
setSize(size) {
|
||||
const $dialog = this.$modal.find('.modal-dialog');
|
||||
$dialog.removeClass('modal-sm modal-lg modal-xl modal-fullwidth');
|
||||
if (size === 'sm') {
|
||||
$dialog.addClass('modal-sm');
|
||||
} else if (size === 'lg') {
|
||||
$dialog.addClass('modal-lg');
|
||||
} else if (size === 'xl') {
|
||||
$dialog.addClass('modal-xl');
|
||||
} else if (size === 'fullwidth') {
|
||||
$dialog.addClass('modal-fullwidth');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set body content
|
||||
* @param {string} html - HTML content for the body
|
||||
*/
|
||||
setBody(html) {
|
||||
this.$body.html(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append content to body
|
||||
* @param {string} html - HTML content to append
|
||||
*/
|
||||
appendBody(html) {
|
||||
this.$body.append(html);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set footer buttons
|
||||
* @param {Array} buttons - Array of button configurations
|
||||
* Each button: { type, label, icon, id, onClick, disabled, className, size }
|
||||
* type: 'cancel', 'primary', 'success', 'warning', 'danger', 'default', 'dark',
|
||||
* 'outline-primary', 'outline-danger', 'ghost'
|
||||
* size: 'sm', 'lg' (optional)
|
||||
*/
|
||||
setFooter(buttons) {
|
||||
this.$footer.empty();
|
||||
|
||||
buttons.forEach(btn => {
|
||||
const btnType = btn.type === 'cancel' ? 'default' : btn.type;
|
||||
let btnClass = `mpr-btn mpr-btn-${btnType}`;
|
||||
|
||||
if (btn.size) {
|
||||
btnClass += ` mpr-btn-${btn.size}`;
|
||||
}
|
||||
if (btn.className) {
|
||||
btnClass += ` ${btn.className}`;
|
||||
}
|
||||
|
||||
const $btn = $('<button>', {
|
||||
type: 'button',
|
||||
class: btnClass,
|
||||
id: btn.id || undefined,
|
||||
disabled: btn.disabled || false
|
||||
});
|
||||
|
||||
if (btn.type === 'cancel' || btn.dismiss) {
|
||||
$btn.attr('data-dismiss', 'modal');
|
||||
}
|
||||
|
||||
// Add icon - cancel buttons get 'close' icon by default if no icon specified
|
||||
const iconName = btn.icon || (btn.type === 'cancel' ? 'close' : null);
|
||||
if (iconName) {
|
||||
$btn.append(`<i class="mpr-icon icon-${iconName}"></i> `);
|
||||
}
|
||||
|
||||
// Support HTML in labels (for inline icons) or plain text
|
||||
if (btn.html) {
|
||||
$btn.append(btn.label);
|
||||
} else {
|
||||
$btn.append(document.createTextNode(btn.label));
|
||||
}
|
||||
|
||||
if (typeof btn.onClick === 'function') {
|
||||
$btn.on('click', btn.onClick);
|
||||
}
|
||||
|
||||
this.$footer.append($btn);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set button loading state
|
||||
* @param {string} buttonId - Button ID
|
||||
* @param {boolean} loading - Loading state
|
||||
*/
|
||||
setButtonLoading(buttonId, loading) {
|
||||
const $btn = $(`#${buttonId}`);
|
||||
if (loading) {
|
||||
$btn.addClass('mpr-btn-loading').prop('disabled', true);
|
||||
} else {
|
||||
$btn.removeClass('mpr-btn-loading').prop('disabled', false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show footer
|
||||
*/
|
||||
showFooter() {
|
||||
this.$footer.removeClass('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide footer
|
||||
*/
|
||||
hideFooter() {
|
||||
this.$footer.addClass('hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable a footer button by ID
|
||||
* @param {string} buttonId - Button ID
|
||||
* @param {boolean} enabled - Enable or disable
|
||||
*/
|
||||
setButtonEnabled(buttonId, enabled) {
|
||||
$(`#${buttonId}`).prop('disabled', !enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update button label
|
||||
* @param {string} buttonId - Button ID
|
||||
* @param {string} label - New label
|
||||
* @param {string} icon - Optional new icon
|
||||
*/
|
||||
setButtonLabel(buttonId, label, icon = null) {
|
||||
const $btn = $(`#${buttonId}`);
|
||||
$btn.empty();
|
||||
if (icon) {
|
||||
$btn.append(`<i class="mpr-icon icon-${icon}"></i> `);
|
||||
}
|
||||
$btn.append(document.createTextNode(label));
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch between views (for multi-step modals)
|
||||
* Views should have class 'mpr-modal-view' and a data-view attribute
|
||||
* @param {string} viewName - The view to show
|
||||
*/
|
||||
showView(viewName) {
|
||||
this.$body.find('.mpr-modal-view').removeClass('active');
|
||||
this.$body.find(`[data-view="${viewName}"]`).addClass('active');
|
||||
this.currentView = viewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current view name
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getCurrentView() {
|
||||
return this.currentView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and show a simple confirmation modal
|
||||
* @param {Object} config - Configuration
|
||||
* @param {string} config.type - Header type
|
||||
* @param {string} config.icon - Header icon
|
||||
* @param {string} config.title - Title
|
||||
* @param {string} config.message - Body message (can be HTML)
|
||||
* @param {string} config.confirmLabel - Confirm button label
|
||||
* @param {string} config.confirmType - Confirm button type (primary, danger, etc.)
|
||||
* @param {string} config.cancelLabel - Cancel button label
|
||||
* @param {Function} config.onConfirm - Confirm callback
|
||||
* @param {Function} config.onCancel - Cancel callback
|
||||
*/
|
||||
confirm(config) {
|
||||
this.setHeader(
|
||||
config.type || 'primary',
|
||||
config.icon || 'question',
|
||||
config.title || 'Confirm'
|
||||
);
|
||||
|
||||
this.setBody(`
|
||||
<div class="mpr-modal-center">
|
||||
<p>${config.message}</p>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.setFooter([
|
||||
{
|
||||
type: 'cancel',
|
||||
label: config.cancelLabel || 'Cancel',
|
||||
onClick: config.onCancel
|
||||
},
|
||||
{
|
||||
type: config.confirmType || 'primary',
|
||||
label: config.confirmLabel || 'Confirm',
|
||||
icon: config.confirmIcon,
|
||||
onClick: () => {
|
||||
if (typeof config.onConfirm === 'function') {
|
||||
config.onConfirm();
|
||||
}
|
||||
if (config.autoClose !== false) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
this.show({ static: config.static || false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a progress state
|
||||
* @param {Object} config - Configuration
|
||||
* @param {string} config.title - Progress title
|
||||
* @param {string} config.subtitle - Progress subtitle
|
||||
* @param {number} config.percent - Initial percentage (0-100)
|
||||
*/
|
||||
showProgress(config = {}) {
|
||||
const title = config.title || 'Processing...';
|
||||
const subtitle = config.subtitle || 'Please wait';
|
||||
const percent = config.percent || 0;
|
||||
|
||||
this.setBody(`
|
||||
<div class="mpr-modal-progress">
|
||||
<i class="icon-refresh mpr-modal-progress-icon"></i>
|
||||
<div class="mpr-modal-progress-title">${title}</div>
|
||||
<div class="mpr-modal-progress-subtitle">${subtitle}</div>
|
||||
<div class="mpr-modal-progress-bar-container">
|
||||
<div class="mpr-modal-progress-bar" style="width: ${percent}%"></div>
|
||||
</div>
|
||||
<div class="mpr-modal-progress-percent">${percent}%</div>
|
||||
<div class="mpr-modal-progress-current"></div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.hideFooter();
|
||||
this.setHeaderType('primary');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update progress bar
|
||||
* @param {number} percent - Percentage (0-100)
|
||||
* @param {string} currentItem - Current item being processed
|
||||
*/
|
||||
updateProgress(percent, currentItem = '') {
|
||||
this.$body.find('.mpr-modal-progress-bar').css('width', `${percent}%`);
|
||||
this.$body.find('.mpr-modal-progress-percent').text(`${Math.round(percent)}%`);
|
||||
if (currentItem) {
|
||||
this.$body.find('.mpr-modal-progress-current').text(currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a result state
|
||||
* @param {Object} config - Configuration
|
||||
* @param {string} config.type - Result type: success, warning, danger, info
|
||||
* @param {string} config.icon - Icon (defaults based on type)
|
||||
* @param {string} config.title - Result title
|
||||
* @param {string} config.message - Result message
|
||||
* @param {string} config.closeLabel - Close button label
|
||||
* @param {Function} config.onClose - Close callback
|
||||
*/
|
||||
showResult(config) {
|
||||
const iconMap = {
|
||||
success: 'check-circle',
|
||||
warning: 'warning',
|
||||
danger: 'times-circle',
|
||||
info: 'info-circle'
|
||||
};
|
||||
|
||||
const icon = config.icon || iconMap[config.type] || 'info-circle';
|
||||
|
||||
this.setHeaderType(config.type === 'info' ? 'primary' : config.type);
|
||||
|
||||
this.setBody(`
|
||||
<div class="mpr-modal-result">
|
||||
<i class="icon-${icon} mpr-modal-result-icon result-${config.type}"></i>
|
||||
<div class="mpr-modal-result-title">${config.title}</div>
|
||||
<div class="mpr-modal-result-message">${config.message}</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
this.setFooter([
|
||||
{
|
||||
type: 'primary',
|
||||
label: config.closeLabel || 'Close',
|
||||
onClick: () => {
|
||||
if (typeof config.onClose === 'function') {
|
||||
config.onClose();
|
||||
}
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
this.showFooter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock modal (prevent closing)
|
||||
*/
|
||||
lock() {
|
||||
this.$modal.data('bs.modal').options.backdrop = 'static';
|
||||
this.$modal.data('bs.modal').options.keyboard = false;
|
||||
this.$modal.find('.mpr-modal-close').hide();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlock modal (allow closing)
|
||||
*/
|
||||
unlock() {
|
||||
this.$modal.data('bs.modal').options.backdrop = true;
|
||||
this.$modal.data('bs.modal').options.keyboard = true;
|
||||
this.$modal.find('.mpr-modal-close').show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the modal instance
|
||||
*/
|
||||
destroy() {
|
||||
this.$modal.modal('dispose');
|
||||
this.$modal.off();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory function to create modals dynamically
|
||||
* Creates the modal HTML and appends it to the body
|
||||
*
|
||||
* @param {Object} config - Modal configuration
|
||||
* @param {string} config.id - Modal ID
|
||||
* @param {string} config.size - Modal size: sm, md, lg, xl
|
||||
* @param {boolean} config.static - Static backdrop
|
||||
* @returns {MPRModal}
|
||||
*/
|
||||
MPRModal.create = function(config) {
|
||||
const id = config.id || 'mpr-modal-' + Date.now();
|
||||
const sizeClass = config.size === 'sm' ? 'modal-sm' :
|
||||
config.size === 'lg' ? 'modal-lg' :
|
||||
config.size === 'xl' ? 'modal-xl' :
|
||||
config.size === 'fullwidth' ? 'modal-fullwidth' : '';
|
||||
|
||||
const html = `
|
||||
<div class="modal fade mpr-modal" id="${id}" tabindex="-1" role="dialog"
|
||||
${config.static ? 'data-backdrop="static" data-keyboard="false"' : ''}>
|
||||
<div class="modal-dialog ${sizeClass}" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header mpr-modal-header mpr-modal-header-primary">
|
||||
<h5 class="modal-title mpr-modal-title">
|
||||
<i class="mpr-modal-icon"></i>
|
||||
<span class="mpr-modal-title-text"></span>
|
||||
</h5>
|
||||
<button type="button" class="close mpr-modal-close" data-dismiss="modal">
|
||||
<span>×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body mpr-modal-body"></div>
|
||||
<div class="modal-footer mpr-modal-footer"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('body').append(html);
|
||||
|
||||
return new MPRModal(id, config);
|
||||
};
|
||||
|
||||
// Export for module systems if available
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = MPRModal;
|
||||
}
|
||||
1100
assets/js/admin/schedule-conditions.js
Executable file
1100
assets/js/admin/schedule-conditions.js
Executable file
File diff suppressed because it is too large
Load Diff
27
composer.json
Executable file
27
composer.json
Executable file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "myprestarocks/prestashop-entity-selector",
|
||||
"description": "Universal entity selection widget for PrestaShop admin controllers - select products, categories, manufacturers with include/exclude logic",
|
||||
"keywords": ["prestashop", "admin", "controller", "traits", "entity", "selector", "products", "categories"],
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "mypresta.rocks",
|
||||
"email": "info@mypresta.rocks"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyPrestaRocks\\EntitySelector\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
}
|
||||
944
package-lock.json
generated
Executable file
944
package-lock.json
generated
Executable file
@@ -0,0 +1,944 @@
|
||||
{
|
||||
"name": "prestashop-target-conditions",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "prestashop-target-conditions",
|
||||
"version": "1.0.0",
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.13",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
|
||||
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0",
|
||||
"@jridgewell/trace-mapping": "^0.3.24"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
|
||||
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.31",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
|
||||
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.1.0",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.scandir": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "2.0.5",
|
||||
"run-parallel": "^1.1.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.stat": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
|
||||
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@nodelib/fs.walk": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
|
||||
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.scandir": "2.1.5",
|
||||
"fastq": "^1.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/any-promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/binary-extensions": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/braces": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
|
||||
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fill-range": "^7.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
"integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.10.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/didyoumean": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/dlv": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
|
||||
"integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nodelib/fs.stat": "^2.0.2",
|
||||
"@nodelib/fs.walk": "^1.2.3",
|
||||
"glob-parent": "^5.1.2",
|
||||
"merge2": "^1.3.0",
|
||||
"micromatch": "^4.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-glob/node_modules/glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/fastq": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
|
||||
"integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"reusify": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/fill-range": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/glob-parent": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-glob": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-core-module": {
|
||||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-extglob": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jiti": {
|
||||
"version": "1.21.7",
|
||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
|
||||
"integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/lilconfig": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
|
||||
"integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/antonk52"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
|
||||
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"braces": "^3.0.3",
|
||||
"picomatch": "^2.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mz": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"any-promise": "^1.0.0",
|
||||
"object-assign": "^4.0.1",
|
||||
"thenify-all": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.11",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||
"integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-hash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/path-parse": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pirates": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
|
||||
"integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.6",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
|
||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-import": {
|
||||
"version": "15.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"postcss-value-parser": "^4.0.0",
|
||||
"read-cache": "^1.0.0",
|
||||
"resolve": "^1.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-js": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
|
||||
"integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"camelcase-css": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12 || ^14 || >= 16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.4.21"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-load-config": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz",
|
||||
"integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"lilconfig": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"jiti": ">=1.21.0",
|
||||
"postcss": ">=8.0.9",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"postcss": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-nested": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
|
||||
"integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"postcss-selector-parser": "^6.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.2.14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
|
||||
"integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
"integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"pify": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"picomatch": "^2.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve": {
|
||||
"version": "1.22.11",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
|
||||
"integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.1",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
|
||||
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"iojs": ">=1.0.0",
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.1",
|
||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
|
||||
"integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.2",
|
||||
"commander": "^4.0.0",
|
||||
"lines-and-columns": "^1.1.6",
|
||||
"mz": "^2.7.0",
|
||||
"pirates": "^4.0.1",
|
||||
"tinyglobby": "^0.2.11",
|
||||
"ts-interface-checker": "^0.1.9"
|
||||
},
|
||||
"bin": {
|
||||
"sucrase": "bin/sucrase",
|
||||
"sucrase-node": "bin/sucrase-node"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/supports-preserve-symlinks-flag": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.19",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz",
|
||||
"integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"arg": "^5.0.2",
|
||||
"chokidar": "^3.6.0",
|
||||
"didyoumean": "^1.2.2",
|
||||
"dlv": "^1.1.3",
|
||||
"fast-glob": "^3.3.2",
|
||||
"glob-parent": "^6.0.2",
|
||||
"is-glob": "^4.0.3",
|
||||
"jiti": "^1.21.7",
|
||||
"lilconfig": "^3.1.3",
|
||||
"micromatch": "^4.0.8",
|
||||
"normalize-path": "^3.0.0",
|
||||
"object-hash": "^3.0.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss": "^8.4.47",
|
||||
"postcss-import": "^15.1.0",
|
||||
"postcss-js": "^4.0.1",
|
||||
"postcss-load-config": "^4.0.2 || ^5.0 || ^6.0",
|
||||
"postcss-nested": "^6.2.0",
|
||||
"postcss-selector-parser": "^6.1.2",
|
||||
"resolve": "^1.22.8",
|
||||
"sucrase": "^3.35.0"
|
||||
},
|
||||
"bin": {
|
||||
"tailwind": "lib/cli.js",
|
||||
"tailwindcss": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/thenify": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"any-promise": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/thenify-all": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"thenify": ">= 3.1.0 < 4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-number": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-interface-checker": {
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
12
package.json
Executable file
12
package.json
Executable file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "prestashop-target-conditions",
|
||||
"version": "1.0.0",
|
||||
"description": "Target conditions widget for PrestaShop modules",
|
||||
"scripts": {
|
||||
"build:css": "npx tailwindcss -i ./assets/css/admin/tailwind-input.css -o ./assets/css/admin/tailwind-output.css --minify",
|
||||
"watch:css": "npx tailwindcss -i ./assets/css/admin/tailwind-input.css -o ./assets/css/admin/tailwind-output.css --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
}
|
||||
13490
src/EntitySelector.php
Normal file
13490
src/EntitySelector.php
Normal file
File diff suppressed because it is too large
Load Diff
47
src/HolidayProviderInterface.php
Executable file
47
src/HolidayProviderInterface.php
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* HolidayProviderInterface
|
||||
*
|
||||
* Interface for holiday functionality in ScheduleConditions.
|
||||
* Implement this interface in your module to enable public holiday exclusions.
|
||||
*
|
||||
* @author mypresta.rocks <info@mypresta.rocks>
|
||||
* @copyright Copyright (c) mypresta.rocks
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
namespace MyPrestaRocks\EntitySelector;
|
||||
|
||||
if (!defined('_PS_VERSION_')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
interface HolidayProviderInterface
|
||||
{
|
||||
/**
|
||||
* Check if a specific date is a public holiday for any of the given countries
|
||||
*
|
||||
* @param string $date Date in Y-m-d format
|
||||
* @param array $countryIds Array of country IDs to check
|
||||
* @return bool True if date is a holiday for any of the countries
|
||||
*/
|
||||
public static function isHoliday($date, array $countryIds);
|
||||
|
||||
/**
|
||||
* Get list of country IDs that have holidays configured
|
||||
*
|
||||
* @return array Array of country IDs
|
||||
*/
|
||||
public static function getCountriesWithHolidays();
|
||||
|
||||
/**
|
||||
* Get holidays within a date range for specific countries
|
||||
*
|
||||
* @param array $countryIds Array of country IDs
|
||||
* @param string $startDate Start date in Y-m-d format
|
||||
* @param string $endDate End date in Y-m-d format
|
||||
* @return array Array of holiday data with keys: holiday_date, display_name, country_iso
|
||||
*/
|
||||
public static function getHolidaysInRange(array $countryIds, $startDate, $endDate);
|
||||
}
|
||||
1193
src/ScheduleConditions.php
Executable file
1193
src/ScheduleConditions.php
Executable file
File diff suppressed because it is too large
Load Diff
18
tailwind.config.js
Executable file
18
tailwind.config.js
Executable file
@@ -0,0 +1,18 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
prefix: 'tw-',
|
||||
content: [
|
||||
'./src/**/*.php',
|
||||
'./assets/js/admin/**/*.js',
|
||||
// Also scan consuming modules' templates
|
||||
'../../../views/templates/admin/**/*.tpl',
|
||||
'../../../controllers/admin/**/*.php',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
corePlugins: {
|
||||
preflight: false,
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user