@oxyshop/admin 1.3.49 → 1.3.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.js CHANGED
@@ -24,14 +24,16 @@ import 'semantic-ui-css/components/video';
24
24
  import 'semantic-ui-css/components/visibility';
25
25
  import 'semantic-ui-css/components/visit';
26
26
  import 'jquery.dirtyforms/jquery.dirtyforms';
27
- import 'chart.js/dist/Chart.min';
27
+ import 'chart.js/dist/chart.min';
28
+ import { Chart as Chart$1 } from 'chart.js';
29
+ import { axios } from '@oxyshop/shop/lib/plugins/Axios';
30
+ import axios$1 from 'axios';
28
31
  import 'semantic-ui-css/semantic.css';
29
32
  import '@oxyshop/admin/scss/main.scss';
30
33
  import '@oxyshop/admin/lib/style.css';
31
34
  import '@oxyshop/admin/images/logo.png';
32
35
  import '@oxyshop/admin/images/admin-logo.svg';
33
36
  import '@oxyshop/admin/images/50x50.png';
34
- import { axios } from '@oxyshop/shop/lib/plugins/Axios';
35
37
 
36
38
  window.$ = $$1;
37
39
  window.jQuery = $$1;
@@ -396,6 +398,7 @@ $$1.fn.extend({
396
398
  search: 250,
397
399
  },
398
400
  forceSelection: false,
401
+ saveRemoteData: false,
399
402
  apiSettings: {
400
403
  dataType: 'JSON',
401
404
  cache: false,
@@ -406,12 +409,21 @@ $$1.fn.extend({
406
409
  return settings;
407
410
  },
408
411
  onResponse(response) {
412
+ let results = response.map(item => ({
413
+ name: item[choiceName],
414
+ value: item[choiceValue],
415
+ }));
416
+
417
+ if (!element.hasClass('multiple')) {
418
+ results.unshift({
419
+ name: ' ',
420
+ value: '',
421
+ });
422
+ }
423
+
409
424
  return {
410
425
  success: true,
411
- results: response.map(item => ({
412
- name: item[choiceName],
413
- value: item[choiceValue],
414
- })),
426
+ results: results,
415
427
  };
416
428
  },
417
429
  },
@@ -436,13 +448,12 @@ $$1.fn.extend({
436
448
  $$1(`<div class="item" data-value="${item[choiceValue]}">${item[choiceName]}</div>`)
437
449
  ));
438
450
  });
451
+
452
+ element.dropdown('refresh');
453
+ element.dropdown('set selected', element.find('input.autocomplete').val().split(',').filter(String));
439
454
  },
440
455
  });
441
456
  }
442
-
443
- window.setTimeout(() => {
444
- element.dropdown('set selected', element.find('input.autocomplete').val().split(',').filter(String));
445
- }, 5000);
446
457
  });
447
458
  },
448
459
  });
@@ -473,12 +484,12 @@ const controlAttributesList = function controlAttributesList() {
473
484
  };
474
485
 
475
486
  const modifyAttributesListOnSelectorElementDelete = function modifyAttributesListOnSelectorElementDelete(removedValue) {
476
- $$1(`#attributesContainer .attribute[data-id="${removedValue}"]`).remove();
487
+ $$1(`#attributesContainer .attributes-group[data-attribute-code="${removedValue}"]`).remove();
477
488
  };
478
489
 
479
490
  const modifySelectorOnAttributesListElementDelete = function modifySelectorOnAttributesListElementDelete() {
480
- $$1('.attribute button').off('click').on('click', (event) => {
481
- const attributeId = $$1(event.currentTarget).parents('.attribute').attr('data-id');
491
+ $$1('.attributes-group button[data-attribute="delete"]').off('click').on('click', (event) => {
492
+ const attributeId = $$1(event.currentTarget).parents('.attributes-group').attr('data-attribute-code');
482
493
 
483
494
  $$1('div#attributeChoice > .ui.dropdown.search').dropdown('remove selected', attributeId);
484
495
  modifyAttributesListOnSelectorElementDelete(attributeId);
@@ -507,6 +518,35 @@ const isInTheAttributesContainer = function isInTheAttributesContainer(attribute
507
518
  return result;
508
519
  };
509
520
 
521
+ const copyValueToAllLanguages = function copyValueToAllLanguages() {
522
+ $$1('#attributesContainer').on('click', '.attribute [data-attribute="copy"]', (event) => {
523
+ event.preventDefault();
524
+
525
+ const $attributesContainer = $$1('#attributesContainer');
526
+ const $masterAttribute = $$1(event.currentTarget).closest('.attribute');
527
+ const attributeID = $masterAttribute.attr('data-id');
528
+ const $attributeCollection = $attributesContainer.find(`[data-id="${attributeID}"]`);
529
+
530
+ const $masterAttributeInputs = $masterAttribute.find('input:visible, select, textarea');
531
+
532
+ $attributeCollection.each((index, attr) => {
533
+ const $inputs = $$1(attr).find('input:visible, select, textarea');
534
+
535
+ $inputs.each((i, input) => {
536
+ if (input.getAttribute('type') === 'checkbox') {
537
+ input.checked = $masterAttributeInputs[i].checked;
538
+ } else if (input.nodeName === 'SELECT') {
539
+ for (let x = 0; x < $inputs[i].length; x++) {
540
+ input[x].selected = $masterAttributeInputs[i][x].selected;
541
+ }
542
+ } else {
543
+ input.value = $masterAttributeInputs[i].value;
544
+ }
545
+ });
546
+ });
547
+ });
548
+ };
549
+
510
550
  const setAttributeChoiceListener = function setAttributeChoiceListener() {
511
551
  const $attributeChoice = $$1('#attributeChoice');
512
552
  $attributeChoice.find('button').on('click', (event) => {
@@ -539,8 +579,8 @@ const setAttributeChoiceListener = function setAttributeChoiceListener() {
539
579
  const attributeFormElements = modifyAttributeFormElements($$1(response));
540
580
 
541
581
  attributeFormElements.each((index, element) => {
542
- const localeCode = $$1(element).find('input[type="hidden"]').last().val();
543
- $$1(`#attributesContainer > div[data-tab="${localeCode}"]`).append(element);
582
+ $$1(element).find('input[type="hidden"]').last().val();
583
+ $$1(`#attributesContainer > div`).append(element);
544
584
  });
545
585
 
546
586
  $$1('#sylius_product_attribute_choice').val('');
@@ -567,6 +607,7 @@ $$1.fn.extend({
567
607
 
568
608
  controlAttributesList();
569
609
  modifySelectorOnAttributesListElementDelete();
610
+ copyValueToAllLanguages();
570
611
  },
571
612
  });
572
613
 
@@ -701,6 +742,83 @@ $$1.fn.handlePrototypes = function handlePrototypes(method, ...args) {
701
742
  return undefined;
702
743
  };
703
744
 
745
+ /*
746
+ * This file is part of the Sylius package.
747
+ *
748
+ * (c) Paweł Jędrzejewski
749
+ *
750
+ * For the full copyright and license information, please view the LICENSE
751
+ * file that was distributed with this source code.
752
+ */
753
+
754
+ $$1.fn.extend({
755
+ loadCatalogPromotionActionConfiguration(target) {
756
+ if (target == null || target.querySelector('#sylius_catalog_promotion_actions select[name*="type"]') == null) {
757
+ return;
758
+ }
759
+
760
+ target.querySelector('#sylius_catalog_promotion_actions select[name*="type"]').onchange = function () {
761
+ const parent = this.parentElement;
762
+ const newConfig = document.createElement('div');
763
+ newConfig.innerHTML = this.selectedOptions[0].getAttribute('data-configuration');
764
+ const oldConfig = parent.nextElementSibling;
765
+
766
+ parent.parentElement.replaceChild(newConfig, oldConfig);
767
+
768
+ const oldConfigInputName = oldConfig.querySelector('input').getAttribute('name');
769
+ let newConfigInputs = newConfig.querySelectorAll('input');
770
+
771
+ newConfigInputs.forEach(element => {
772
+ let newConfigInputName = element.getAttribute('name');
773
+
774
+ newConfigInputName = oldConfigInputName.replace(
775
+ oldConfigInputName.substring(oldConfigInputName.indexOf('[configuration]') + 15),
776
+ newConfigInputName.substring(newConfigInputName.indexOf('configuration') + 13),
777
+ );
778
+
779
+ $$1(element).attr('name', newConfigInputName);
780
+ });
781
+ };
782
+ },
783
+ });
784
+
785
+ /*
786
+ * This file is part of the Sylius package.
787
+ *
788
+ * (c) Paweł Jędrzejewski
789
+ *
790
+ * For the full copyright and license information, please view the LICENSE
791
+ * file that was distributed with this source code.
792
+ */
793
+
794
+ $$1.fn.extend({
795
+ loadCatalogPromotionScopeConfiguration(target) {
796
+ if (target == null || target.querySelector('#sylius_catalog_promotion_scopes select[name*="type"]') == null) {
797
+ return;
798
+ }
799
+
800
+ target.querySelector('#sylius_catalog_promotion_scopes select[name*="type"]').onchange = function () {
801
+ const parent = this.parentElement;
802
+ const newConfig = document.createElement('div');
803
+ newConfig.innerHTML = this.selectedOptions[0].getAttribute('data-configuration');
804
+ const oldConfig = parent.nextElementSibling;
805
+
806
+ parent.parentElement.replaceChild(newConfig, oldConfig);
807
+
808
+ const oldConfigInputName = oldConfig.querySelector('input').getAttribute('name');
809
+ let newConfigInputName = newConfig.querySelector('input').getAttribute('name');
810
+
811
+ newConfigInputName = oldConfigInputName.replace(
812
+ oldConfigInputName.substring(oldConfigInputName.lastIndexOf('[') + 1, oldConfigInputName.lastIndexOf(']')),
813
+ newConfigInputName.substring(newConfigInputName.indexOf('[') + 1, newConfigInputName.lastIndexOf(']')),
814
+ );
815
+
816
+ $$1(newConfig).find('input').attr('name', newConfigInputName);
817
+ $$1(newConfig).find('.sylius-autocomplete').autoComplete();
818
+ };
819
+ },
820
+ });
821
+
704
822
  /*
705
823
  * This file is part of the Sylius package.
706
824
  *
@@ -1390,8 +1508,10 @@ const drawChart = function drawChart(canvas, labels = [], values = [], currency)
1390
1508
  },
1391
1509
  responsive: true,
1392
1510
  maintainAspectRatio: false,
1393
- legend: {
1394
- display: false,
1511
+ plugins: {
1512
+ legend: {
1513
+ display: false,
1514
+ },
1395
1515
  },
1396
1516
  },
1397
1517
  });
@@ -1770,18 +1890,26 @@ $$1(document).ready(() => {
1770
1890
  $$1('#sylius_shipping_method_calculator').handlePrototypes({
1771
1891
  prototypePrefix: 'sylius_shipping_method_calculator_calculators',
1772
1892
  containerSelector: '.configuration',
1893
+ }).change(() => {
1894
+ $$1('.ui.tabular.menu .item').tab();
1773
1895
  });
1896
+ setTimeout(() => {
1897
+ $$1('.ui.tabular.menu .item').tab();
1898
+ }, 50);
1774
1899
 
1775
- $$1('#actions a[data-form-collection="add"]').on('click', () => {
1900
+ $$1('#sylius_promotion_actions > a[data-form-collection="add"]').on('click', () => {
1776
1901
  setTimeout(() => {
1777
1902
  $$1('select[name^="sylius_promotion[actions]"][name$="[type]"]').last().change();
1778
1903
  }, 50);
1779
1904
  });
1780
- $$1('#rules a[data-form-collection="add"]').on('click', (event) => {
1781
- const name = $$1(event.target).closest('form').attr('name');
1782
-
1905
+ $$1('#sylius_promotion_rules > a[data-form-collection="add"]').on('click', () => {
1906
+ setTimeout(() => {
1907
+ $$1('select[name^="sylius_promotion[rules]"][name$="[type]"]').last().change();
1908
+ }, 50);
1909
+ });
1910
+ $$1('#sylius_shipping_method_rules > a[data-form-collection="add"]').on('click', () => {
1783
1911
  setTimeout(() => {
1784
- $$1(`select[name^="${name}[rules]"][name$="[type]"]`).last().change();
1912
+ $$1('select[name^="sylius_shipping_method[rules]"][name$="[type]"]').last().change();
1785
1913
  }, 50);
1786
1914
  });
1787
1915
 
@@ -1791,6 +1919,18 @@ $$1(document).ready(() => {
1791
1919
  $$1(element).autoComplete();
1792
1920
  }
1793
1921
  });
1922
+
1923
+ if ($$1('#sylius_catalog_promotion_scopes').length > 0) {
1924
+ $$1(document).loadCatalogPromotionScopeConfiguration(
1925
+ document.querySelector('#sylius_catalog_promotion_scopes [data-form-collection="item"]:last-child')
1926
+ );
1927
+ }
1928
+
1929
+ if ($$1('#sylius_catalog_promotion_actions').length > 0) {
1930
+ $$1(document).loadCatalogPromotionActionConfiguration(
1931
+ document.querySelector('#sylius_catalog_promotion_actions [data-form-collection="item"]:last-child')
1932
+ );
1933
+ }
1794
1934
  });
1795
1935
  $$1(document).on('collection-form-update', () => {
1796
1936
  $$1('.sylius-autocomplete').each((index, element) => {
@@ -1809,12 +1949,21 @@ $$1(document).ready(() => {
1809
1949
  $$1(document).taxonSlugGenerator();
1810
1950
  $$1(document).previewUploadedImage('#sylius_product_images');
1811
1951
  $$1(document).previewUploadedImage('#sylius_taxon_images');
1952
+ if ($$1('#sylius_catalog_promotion_actions').length > 0) {
1953
+ $$1(document).loadCatalogPromotionActionConfiguration(document.querySelector('#sylius_catalog_promotion_actions'));
1954
+ }
1955
+ if ($$1('#sylius_catalog_promotion_scopes').length > 0) {
1956
+ $$1(document).loadCatalogPromotionScopeConfiguration(document.querySelector('#sylius_catalog_promotion_scopes'));
1957
+ }
1812
1958
 
1813
1959
  $$1(document).previewUploadedImage('#add-avatar');
1814
1960
 
1815
1961
  $$1('body').on('DOMNodeInserted', '[data-form-collection="item"]', (event) => {
1816
- if ($$1(event.target).find('.accordion').length > 0) {
1817
- $$1(event.target).find('.accordion').accordion();
1962
+ if ($$1(event.target).find('.ui.accordion').length > 0) {
1963
+ $$1(event.target).find('.ui.accordion').accordion();
1964
+ }
1965
+ if ($$1(event.target).find('.ui.tabular.menu').length > 0) {
1966
+ $$1(event.target).find('.ui.tabular.menu .item').tab();
1818
1967
  }
1819
1968
  });
1820
1969
 
@@ -1901,9 +2050,154 @@ class FeedCategorySelect {
1901
2050
  cache: false,
1902
2051
  },
1903
2052
  minCharacters: 2,
2053
+ clearable: true,
1904
2054
  });
2055
+
2056
+ // Workaround for dropdown clear button not showing after loading initial value from the API
2057
+ setTimeout(() => {
2058
+ $(selectElement).dropdown('restore defaults');
2059
+ }, 0);
2060
+ });
2061
+ }
2062
+ }
2063
+
2064
+ class ProductVariantPricingGraph {
2065
+ constructor(containerSelector) {
2066
+ this.containerSelector = containerSelector;
2067
+ }
2068
+
2069
+ init() {
2070
+ document.querySelectorAll(this.containerSelector).forEach((componentContainer) => {
2071
+ const graphsData = JSON.parse(componentContainer.getAttribute('data-graph'));
2072
+
2073
+ for (const currencyGraphIndex in graphsData) {
2074
+ const currencyGraph = graphsData[currencyGraphIndex];
2075
+
2076
+ const currencyCodeHeader = document.createElement('h4');
2077
+ currencyCodeHeader.classList.add('ui', 'header');
2078
+ currencyCodeHeader.textContent = currencyGraph.currency;
2079
+
2080
+ const graphCanvas = document.createElement('canvas');
2081
+ graphCanvas.width = 400;
2082
+ graphCanvas.height = 200;
2083
+
2084
+ const graphContainer = document.createElement('div');
2085
+ graphContainer.append(currencyCodeHeader);
2086
+ graphContainer.append(graphCanvas);
2087
+
2088
+ componentContainer.append(graphContainer);
2089
+
2090
+ const graphConfig = this.getGraphConfig(currencyGraph.graph);
2091
+ new Chart$1(graphCanvas.getContext('2d'), graphConfig);
2092
+ }
1905
2093
  });
1906
2094
  }
2095
+
2096
+ /** @private */
2097
+ getGraphConfig(rawGraphData) {
2098
+ return {
2099
+ type: 'line',
2100
+ data: {
2101
+ datasets: rawGraphData.datasets.map((rawDataset) => {
2102
+ return {
2103
+ label: rawDataset.label,
2104
+ data: rawDataset.data,
2105
+ borderColor: this.getRandomRgbColor(),
2106
+ fill: false,
2107
+ steppedLine: true,
2108
+ pointRadius: 8,
2109
+ pointHoverRadius: 10,
2110
+ }
2111
+ }),
2112
+ },
2113
+ options: {
2114
+ maintainAspectRatio: true,
2115
+ responsive: true,
2116
+ scales: {
2117
+ xAxes: [
2118
+ {
2119
+ type: 'linear',
2120
+ position: 'bottom',
2121
+ scaleLabel: {
2122
+ display: true,
2123
+ labelString: rawGraphData.xAxisLabel,
2124
+ },
2125
+ },
2126
+ ],
2127
+ yAxes: [
2128
+ {
2129
+ scaleLabel: {
2130
+ display: true,
2131
+ labelString: rawGraphData.yAxisLabel,
2132
+ },
2133
+ },
2134
+ ],
2135
+ },
2136
+ },
2137
+ }
2138
+ }
2139
+
2140
+ /** @private */
2141
+ getRandomRgbColor() {
2142
+ const r = Math.floor(Math.random() * 255);
2143
+ const g = Math.floor(Math.random() * 255);
2144
+ const b = Math.floor(Math.random() * 255);
2145
+ return `rgb(${r},${g},${b})`
2146
+ }
2147
+ }
2148
+
2149
+ class CustomerGroupClientAssigner {
2150
+ constructor(selector, searchSelector = '.ng-client-search-select', submitSelector = '.ng-client-assign-submit') {
2151
+ this.selector = selector;
2152
+ this.searchSelector = searchSelector;
2153
+ this.submitSelector = submitSelector;
2154
+ }
2155
+
2156
+ init() {
2157
+ document.querySelectorAll(this.selector).forEach((containerElement) => {
2158
+ const submitApiEndpoint = containerElement.getAttribute('data-assign-customers-url');
2159
+ const redirectAfterUrl = containerElement.getAttribute('data-redirect-after-url');
2160
+
2161
+ const searchElement = containerElement.querySelector(this.searchSelector);
2162
+ const searchApiEndpoint = searchElement.getAttribute('data-customer-search-url');
2163
+
2164
+ $(searchElement).dropdown({
2165
+ apiSettings: {
2166
+ url: `${searchApiEndpoint}/{query}`,
2167
+ cache: false,
2168
+ },
2169
+
2170
+ minCharacters: 2,
2171
+ });
2172
+
2173
+ const submitElement = containerElement.querySelector(this.submitSelector);
2174
+ submitElement.addEventListener('click', () => {
2175
+ const selectedCustomersIds = this.getSelectValues(searchElement);
2176
+ this.submitCustomers(submitElement, submitApiEndpoint, selectedCustomersIds, redirectAfterUrl);
2177
+ });
2178
+ });
2179
+ }
2180
+
2181
+ getSelectValues(selectElement) {
2182
+ const selected = selectElement.querySelectorAll('option:checked');
2183
+ return Array.from(selected).map((optionElement) => optionElement.value)
2184
+ }
2185
+
2186
+ submitCustomers(submitElement, apiEndpoint, selectedIds, redirectTo) {
2187
+ submitElement.disabled = true;
2188
+
2189
+ axios
2190
+ .post(apiEndpoint, { customers: selectedIds })
2191
+ .then(() => {
2192
+ window.location = redirectTo;
2193
+ })
2194
+ .catch((error) => {
2195
+ console.error(error);
2196
+ })
2197
+ .finally(() => {
2198
+ submitElement.disabled = false;
2199
+ });
2200
+ }
1907
2201
  }
1908
2202
 
1909
2203
  class TooltipHelpers {
@@ -2039,6 +2333,557 @@ class CustomerGroupingRuleConfiguration {
2039
2333
  }
2040
2334
  }
2041
2335
 
2336
+ class ImageInput {
2337
+ constructor(selector) {
2338
+ this.selector = selector;
2339
+ }
2340
+
2341
+ init() {
2342
+ document.querySelectorAll(this.selector).forEach((element) => {
2343
+ element.querySelectorAll('input[type="file"]').forEach((input) => {
2344
+ input.addEventListener('change', () => {
2345
+ this.onImageInputChange(input);
2346
+ });
2347
+ });
2348
+ });
2349
+ }
2350
+
2351
+ onImageInputChange(input) {
2352
+ if (!input.files || !input.files[0]) {
2353
+ return
2354
+ }
2355
+
2356
+ const reader = new FileReader();
2357
+
2358
+ reader.onload = (event) => {
2359
+ const wrapperDiv = input.parentElement.parentElement;
2360
+ const image = wrapperDiv.querySelector('.image');
2361
+
2362
+ if (null === image) {
2363
+ const img = document.createElement('img');
2364
+ img.setAttribute('class', 'ui small bordered image');
2365
+ img.setAttribute('src', event.target.result);
2366
+
2367
+ const inputLabel = wrapperDiv.querySelector(':scope > label');
2368
+ wrapperDiv.insertBefore(img, inputLabel.nextSibling);
2369
+ } else {
2370
+ image.setAttribute('src', event.target.result);
2371
+ }
2372
+ };
2373
+
2374
+ reader.readAsDataURL(input.files[0]);
2375
+ }
2376
+ }
2377
+
2378
+ class ProductVariantPricingSimulation {
2379
+ constructor(selector) {
2380
+ this.selector = selector;
2381
+ }
2382
+
2383
+ init() {
2384
+ document.querySelectorAll(this.selector).forEach((element) => {
2385
+ const productVariantId = element.getAttribute('data-product-variant-id');
2386
+ const channels = JSON.parse(element.getAttribute('data-channels'));
2387
+ const searchApiEndpoint = element.getAttribute('data-customer-search-url');
2388
+ const pricingApiEndpoint = element.getAttribute('data-pricing-url');
2389
+
2390
+ this.initElement(element, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint);
2391
+ });
2392
+ }
2393
+
2394
+ initElement(parentElement, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint) {
2395
+ parentElement.innerHTML = '';
2396
+
2397
+ const simulationData = {
2398
+ productVariantId: productVariantId,
2399
+ channelId: null,
2400
+ currencyId: null,
2401
+ customerId: null,
2402
+ };
2403
+
2404
+ const submitButton = document.createElement('button');
2405
+ const currencySelect = document.createElement('select');
2406
+
2407
+ const onChannelSelect = (event) => {
2408
+ const selectedChannelId = parseInt(event.target.value);
2409
+ simulationData.channelId = selectedChannelId;
2410
+ simulationData.currencyId = null;
2411
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2412
+
2413
+ const selectedChannel = channels.find((channel) => {
2414
+ return channel.id === selectedChannelId
2415
+ });
2416
+
2417
+ currencySelect.innerHTML = '';
2418
+ this.addSelectOptions(currencySelect, selectedChannel.currencies, 'Select currency');
2419
+ };
2420
+
2421
+ const onCurrencySelect = (event) => {
2422
+ simulationData.currencyId = event.target.value;
2423
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2424
+ };
2425
+
2426
+ const onCustomerSelect = (event) => {
2427
+ simulationData.customerId = event.target.value;
2428
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2429
+ };
2430
+
2431
+ const graphWrapper = this.createGraphWrapper();
2432
+
2433
+ const onSubmit = () => {
2434
+ const pricingUrl = `${pricingApiEndpoint}?${this.serializeObjectToQuery(simulationData)}`;
2435
+
2436
+ axios$1
2437
+ .get(pricingUrl)
2438
+ .then((response) => {
2439
+ const chartData = response.data;
2440
+ graphWrapper.setAttribute('data-debug-chart', JSON.stringify(chartData)); // For e2e tests
2441
+ this.renderGraph(graphWrapper, chartData);
2442
+ })
2443
+ .catch((error) => {
2444
+ console.error(error);
2445
+ graphWrapper.innerHTML = 'Pricing simulation error';
2446
+ });
2447
+ };
2448
+
2449
+ const wrapper = document.createElement('div');
2450
+ wrapper.setAttribute('style', 'border: solid 1px #ddd; background-color: #fafafa;');
2451
+
2452
+ const filters = this.createFilters(
2453
+ channels,
2454
+ currencySelect,
2455
+ onChannelSelect,
2456
+ onCurrencySelect,
2457
+ onCustomerSelect,
2458
+ onSubmit,
2459
+ submitButton,
2460
+ searchApiEndpoint
2461
+ );
2462
+ filters.setAttribute(
2463
+ 'style',
2464
+ 'display: grid; grid-template-columns: 1fr 1fr 1fr 60px; grid-gap: 10px; ' +
2465
+ 'padding: 15px; border-bottom: solid 1px #ddd;'
2466
+ );
2467
+
2468
+ const graphFiller = this.createGraphFiller();
2469
+ graphWrapper.append(graphFiller);
2470
+
2471
+ wrapper.append(filters, graphWrapper);
2472
+ parentElement.append(wrapper);
2473
+ }
2474
+
2475
+ createFilters(
2476
+ channels,
2477
+ currencySelect,
2478
+ onChannelSelect,
2479
+ onCurrencySelect,
2480
+ onCustomerSelect,
2481
+ onSubmit,
2482
+ submitButton,
2483
+ searchApiEndpoint
2484
+ ) {
2485
+ const filtersWrapper = document.createElement('div');
2486
+
2487
+ const channelSelect = document.createElement('select');
2488
+ channelSelect.classList.add('dirtyignore');
2489
+ this.addSelectOptions(channelSelect, channels, 'Select channel');
2490
+ channelSelect.addEventListener('change', onChannelSelect);
2491
+
2492
+ currencySelect.classList.add('dirtyignore');
2493
+ currencySelect.addEventListener('change', onCurrencySelect);
2494
+
2495
+ const customerSelect = document.createElement('select');
2496
+ customerSelect.classList.add('dirtyignore');
2497
+ customerSelect.addEventListener('change', onCustomerSelect);
2498
+
2499
+ // this is delayed to avoid racing conditions
2500
+ setTimeout(() => this.hookClientSearchOnSelect(customerSelect, searchApiEndpoint), 600);
2501
+
2502
+ submitButton.disabled = true;
2503
+ submitButton.setAttribute('class', 'ui icon primary button');
2504
+ submitButton.type = 'button';
2505
+ submitButton.addEventListener('click', onSubmit);
2506
+
2507
+ const playIcon = document.createElement('i');
2508
+ playIcon.setAttribute('class', 'icon play');
2509
+ submitButton.append(playIcon);
2510
+
2511
+ filtersWrapper.append(channelSelect, currencySelect, customerSelect, submitButton);
2512
+
2513
+ return filtersWrapper
2514
+ }
2515
+
2516
+ createGraphWrapper() {
2517
+ const wrapper = document.createElement('div');
2518
+ wrapper.setAttribute('style', 'padding: 15px;');
2519
+
2520
+ return wrapper
2521
+ }
2522
+
2523
+ createGraphFiller() {
2524
+ const filler = document.createElement('div');
2525
+ filler.setAttribute(
2526
+ 'style',
2527
+ 'border-radius: 7px; background-color: #eee; height: 350px; display: flex; ' +
2528
+ 'justify-content: space-around; align-items: center; font-size: 4em;'
2529
+ );
2530
+
2531
+ const chartIcon = document.createElement('i');
2532
+ chartIcon.setAttribute('class', 'icon chart line');
2533
+ filler.append(chartIcon);
2534
+
2535
+ return filler
2536
+ }
2537
+
2538
+ addSelectOptions(select, choices, placeholder = null) {
2539
+ if (placeholder !== null) {
2540
+ const placeholderOption = document.createElement('option');
2541
+ placeholderOption.innerHTML = placeholder;
2542
+ placeholderOption.disabled = true;
2543
+ placeholderOption.selected = true;
2544
+ select.append(placeholderOption);
2545
+ }
2546
+
2547
+ for (const property in choices) {
2548
+ const choice = choices[property];
2549
+
2550
+ const channelOption = document.createElement('option');
2551
+ channelOption.innerHTML = choice.name;
2552
+ channelOption.setAttribute('value', choice.id);
2553
+ select.append(channelOption);
2554
+ }
2555
+
2556
+ return select
2557
+ }
2558
+
2559
+ hookClientSearchOnSelect(selectElement, searchApiEndpoint) {
2560
+ selectElement.setAttribute('class', `${selectElement.getAttribute('class')} search dropdown selection`);
2561
+
2562
+ $(selectElement).dropdown({
2563
+ apiSettings: {
2564
+ url: `${searchApiEndpoint}/{query}`,
2565
+ cache: false,
2566
+ },
2567
+
2568
+ minCharacters: 2,
2569
+ });
2570
+ }
2571
+
2572
+ isSubmitButtonDisabled(simulationData) {
2573
+ return (
2574
+ null === simulationData.productVariantId ||
2575
+ null === simulationData.channelId ||
2576
+ null === simulationData.currencyId ||
2577
+ null === simulationData.customerId
2578
+ )
2579
+ }
2580
+
2581
+ serializeObjectToQuery(object) {
2582
+ const query = [];
2583
+
2584
+ for (const part in object) {
2585
+ if (Object.prototype.hasOwnProperty.call(object, part)) {
2586
+ query.push(`${encodeURIComponent(part)}=${encodeURIComponent(object[part])}`);
2587
+ }
2588
+ }
2589
+
2590
+ return query.join('&')
2591
+ }
2592
+
2593
+ renderGraph(graphWrapper, graphData) {
2594
+ graphWrapper.innerHTML = '';
2595
+
2596
+ const graphCanvas = document.createElement('canvas');
2597
+ graphCanvas.width = 600;
2598
+ graphCanvas.height = 200;
2599
+
2600
+ graphWrapper.append(graphCanvas);
2601
+
2602
+ const graphConfig = this.getGraphConfig(graphData);
2603
+ console.log('graphConfig', graphConfig);
2604
+ new Chart$1(graphCanvas.getContext('2d'), graphConfig);
2605
+ }
2606
+
2607
+ /** @private */
2608
+ getGraphConfig(rawGraphData) {
2609
+ return {
2610
+ type: 'line',
2611
+ data: {
2612
+ datasets: rawGraphData.datasets.map((rawDataset) => {
2613
+ return {
2614
+ label: rawDataset.label,
2615
+ data: rawDataset.data,
2616
+ borderColor: '#0000ff',
2617
+ fill: false,
2618
+ steppedLine: true,
2619
+ pointRadius: 8,
2620
+ pointHoverRadius: 10,
2621
+ }
2622
+ }),
2623
+ },
2624
+ options: {
2625
+ maintainAspectRatio: true,
2626
+ responsive: true,
2627
+ scales: {
2628
+ xAxes: [
2629
+ {
2630
+ type: 'linear',
2631
+ position: 'bottom',
2632
+ scaleLabel: {
2633
+ display: true,
2634
+ labelString: rawGraphData.xAxisLabel,
2635
+ },
2636
+ },
2637
+ ],
2638
+ yAxes: [
2639
+ {
2640
+ scaleLabel: {
2641
+ display: true,
2642
+ labelString: rawGraphData.yAxisLabel,
2643
+ },
2644
+ },
2645
+ ],
2646
+ },
2647
+ },
2648
+ }
2649
+ }
2650
+ }
2651
+
2652
+ class DocumentUploadInput {
2653
+ /** @param {string} documentUploadInputSelector */
2654
+ constructor(documentUploadInputSelector) {
2655
+ this.selector = documentUploadInputSelector;
2656
+ }
2657
+
2658
+ init() {
2659
+ const fileInputs = document.querySelectorAll(this.selector);
2660
+
2661
+ Array.prototype.forEach.call(fileInputs, (inputElement) => {
2662
+ const fileUrl = inputElement.getAttribute('data-file-url');
2663
+ const fileTitle = inputElement.getAttribute('data-file-name');
2664
+ const downloadText = inputElement.getAttribute('data-download-translation');
2665
+
2666
+ const iconElement = document.createElement('i');
2667
+ iconElement.classList.add('download', 'icon');
2668
+
2669
+ const buttonElement = document.createElement('button');
2670
+ buttonElement.classList.add('ui', 'compact', 'labeled', 'icon', 'button');
2671
+ buttonElement.setAttribute('type', 'button');
2672
+ buttonElement.append(iconElement);
2673
+ buttonElement.innerHTML += `${downloadText}: ${fileTitle}`;
2674
+
2675
+ const downloadLink = document.createElement('a');
2676
+ downloadLink.setAttribute('style', 'display: inline-block; margin: -5px 0 25px 0;');
2677
+ downloadLink.setAttribute('target', '_blank');
2678
+ downloadLink.setAttribute('href', fileUrl);
2679
+ downloadLink.append(buttonElement);
2680
+
2681
+ inputElement.parentNode.parentNode.append(downloadLink);
2682
+ });
2683
+ }
2684
+ }
2685
+
2686
+ (function ($) {
2687
+
2688
+ $.fn.extend({
2689
+ blogSlugGenerator: function () {
2690
+ let timeout;
2691
+
2692
+ $('[name*="nextgen_cms_bundle_blog[translations]"][name*="[name]"]').on('input', function () {
2693
+ clearTimeout(timeout);
2694
+ let element = $(this);
2695
+
2696
+ timeout = setTimeout(function () {
2697
+ updateSlug(element);
2698
+ }, 1000);
2699
+ });
2700
+
2701
+ $('.toggle-blog-slug-modification').on('click', function (e) {
2702
+ e.preventDefault();
2703
+ toggleSlugModification($(this), $(this).siblings('input'));
2704
+ });
2705
+
2706
+ function updateSlug(element) {
2707
+ let slugInput = element.parents('.content').find('[name*="[slug]"]');
2708
+ let loadableParent = slugInput.parents('.field.loadable');
2709
+
2710
+ if ('readonly' === slugInput.attr('readonly')) {
2711
+ return
2712
+ }
2713
+
2714
+ loadableParent.addClass('loading');
2715
+
2716
+ $.ajax({
2717
+ type: 'GET',
2718
+ url: slugInput.attr('data-url'),
2719
+ data: { name: element.val() },
2720
+ dataType: 'json',
2721
+ accept: 'application/json',
2722
+ success: function (data) {
2723
+ slugInput.val(data.slug);
2724
+ if (slugInput.parents('.field').hasClass('error')) {
2725
+ slugInput.parents('.field').removeClass('error');
2726
+ slugInput.parents('.field').find('.sylius-validation-error').remove();
2727
+ }
2728
+ loadableParent.removeClass('loading');
2729
+ },
2730
+ });
2731
+ }
2732
+
2733
+ function toggleSlugModification(button, slugInput) {
2734
+ if (slugInput.attr('readonly')) {
2735
+ slugInput.removeAttr('readonly');
2736
+ button.html('<i class="unlock icon"></i>');
2737
+ } else {
2738
+ slugInput.attr('readonly', 'readonly');
2739
+ button.html('<i class="lock icon"></i>');
2740
+ }
2741
+ }
2742
+ },
2743
+ });
2744
+ })(jQuery)
2745
+ ;(function ($) {
2746
+ $(document).ready(function () {
2747
+ $(this).blogSlugGenerator();
2748
+ });
2749
+ })(jQuery);
2750
+
2751
+ (function ($) {
2752
+
2753
+ $.fn.extend({
2754
+ articleSlugGenerator: function () {
2755
+ let timeout;
2756
+
2757
+ $('[name*="nextgen_cms_bundle_article[translations]"][name*="[name]"]').on('input', function () {
2758
+ clearTimeout(timeout);
2759
+ let element = $(this);
2760
+
2761
+ timeout = setTimeout(function () {
2762
+ updateSlug(element);
2763
+ }, 1000);
2764
+ });
2765
+
2766
+ $('.toggle-article-slug-modification').on('click', function (e) {
2767
+ e.preventDefault();
2768
+ toggleSlugModification($(this), $(this).siblings('input'));
2769
+ });
2770
+
2771
+ function updateSlug(element) {
2772
+ let slugInput = element.parents('.content').find('[name*="[slug]"]');
2773
+ let loadableParent = slugInput.parents('.field.loadable');
2774
+
2775
+ if ('readonly' === slugInput.attr('readonly')) {
2776
+ return
2777
+ }
2778
+
2779
+ loadableParent.addClass('loading');
2780
+
2781
+ $.ajax({
2782
+ type: 'GET',
2783
+ url: slugInput.attr('data-url'),
2784
+ data: { name: element.val() },
2785
+ dataType: 'json',
2786
+ accept: 'application/json',
2787
+ success: function (data) {
2788
+ slugInput.val(data.slug);
2789
+ if (slugInput.parents('.field').hasClass('error')) {
2790
+ slugInput.parents('.field').removeClass('error');
2791
+ slugInput.parents('.field').find('.sylius-validation-error').remove();
2792
+ }
2793
+ loadableParent.removeClass('loading');
2794
+ },
2795
+ });
2796
+ }
2797
+
2798
+ function toggleSlugModification(button, slugInput) {
2799
+ if (slugInput.attr('readonly')) {
2800
+ slugInput.removeAttr('readonly');
2801
+ button.html('<i class="unlock icon"></i>');
2802
+ } else {
2803
+ slugInput.attr('readonly', 'readonly');
2804
+ button.html('<i class="lock icon"></i>');
2805
+ }
2806
+ }
2807
+ },
2808
+ });
2809
+ })(jQuery)
2810
+ ;(function ($) {
2811
+ $(document).ready(function () {
2812
+ $(this).articleSlugGenerator();
2813
+ });
2814
+ })(jQuery);
2815
+
2816
+ (function ($) {
2817
+
2818
+ $.fn.extend({
2819
+ blogCategorySlugGenerator: function () {
2820
+ let timeout;
2821
+
2822
+ $('[name*="nextgen_cms_bundle_blog_category[translations]"][name*="[name]"]').on('input', function () {
2823
+ clearTimeout(timeout);
2824
+ let element = $(this);
2825
+
2826
+ timeout = setTimeout(function () {
2827
+ updateSlug(element);
2828
+ }, 1000);
2829
+ });
2830
+
2831
+ $('.toggle-blog-category-slug-modification').on('click', function (e) {
2832
+ e.preventDefault();
2833
+ toggleSlugModification($(this), $(this).siblings('input'));
2834
+ });
2835
+
2836
+ function updateSlug(element) {
2837
+ let slugInput = element.parents('.content').find('[name*="[slug]"]');
2838
+ let loadableParent = slugInput.parents('.field.loadable');
2839
+
2840
+ if ('readonly' === slugInput.attr('readonly')) {
2841
+ return
2842
+ }
2843
+
2844
+ loadableParent.addClass('loading');
2845
+
2846
+ let data;
2847
+ data = {
2848
+ name: element.val(),
2849
+ locale: element.closest('[data-locale]').data('locale'),
2850
+ };
2851
+
2852
+ $.ajax({
2853
+ type: 'GET',
2854
+ url: slugInput.attr('data-url'),
2855
+ data: data,
2856
+ dataType: 'json',
2857
+ accept: 'application/json',
2858
+ success: function (data) {
2859
+ slugInput.val(data.slug);
2860
+ if (slugInput.parents('.field').hasClass('error')) {
2861
+ slugInput.parents('.field').removeClass('error');
2862
+ slugInput.parents('.field').find('.sylius-validation-error').remove();
2863
+ }
2864
+ loadableParent.removeClass('loading');
2865
+ },
2866
+ });
2867
+ }
2868
+
2869
+ function toggleSlugModification(button, slugInput) {
2870
+ if (slugInput.attr('readonly')) {
2871
+ slugInput.removeAttr('readonly');
2872
+ button.html('<i class="unlock icon"></i>');
2873
+ } else {
2874
+ slugInput.attr('readonly', 'readonly');
2875
+ button.html('<i class="lock icon"></i>');
2876
+ }
2877
+ }
2878
+ },
2879
+ });
2880
+ })(jQuery)
2881
+ ;(function ($) {
2882
+ $(document).ready(function () {
2883
+ $(this).blogCategorySlugGenerator();
2884
+ });
2885
+ })(jQuery);
2886
+
2042
2887
  /**
2043
2888
  * CKEditor config
2044
2889
  */
@@ -2132,6 +2977,35 @@ vendorDescriptionElements.forEach((element) => {
2132
2977
  CKEDITOR.replace(element.name, ckEditorOptions);
2133
2978
  });
2134
2979
 
2980
+ /**
2981
+ * Blog article
2982
+ */
2983
+ const articleAnnotationElements = document.querySelectorAll(
2984
+ 'form[name="nextgen_cms_bundle_article"] textarea[name$="[annotation]"]'
2985
+ );
2986
+ articleAnnotationElements.forEach((element) => {
2987
+ // eslint-disable-next-line no-undef
2988
+ CKEDITOR.replace(element.name, ckEditorOptions);
2989
+ });
2990
+ const articleContentElements = document.querySelectorAll(
2991
+ 'form[name="nextgen_cms_bundle_article"] textarea[name$="[content]"]'
2992
+ );
2993
+ articleContentElements.forEach((element) => {
2994
+ // eslint-disable-next-line no-undef
2995
+ CKEDITOR.replace(element.name, ckEditorOptions);
2996
+ });
2997
+
2998
+ /**
2999
+ * Blog category
3000
+ */
3001
+ const categoryDescriptionElements = document.querySelectorAll(
3002
+ 'form[name="nextgen_cms_bundle_blog_category"] textarea[name$="[description]"]'
3003
+ );
3004
+ categoryDescriptionElements.forEach((element) => {
3005
+ // eslint-disable-next-line no-undef
3006
+ CKEDITOR.replace(element.name, ckEditorOptions);
3007
+ });
3008
+
2135
3009
  (function ($) {
2136
3010
  $(document).ready(function () {
2137
3011
  $('#sylius_payment_method_calculator').handlePrototypes({
@@ -2141,6 +3015,12 @@ vendorDescriptionElements.forEach((element) => {
2141
3015
  });
2142
3016
  })(jQuery);
2143
3017
 
3018
+ /*
3019
+ This file was created by developers working at BitBag
3020
+ Do you need more information about us and what we do? Visit our https://bitbag.io website!
3021
+ We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
3022
+ */
3023
+
2144
3024
  (function ( $ ) {
2145
3025
 
2146
3026
  $.fn.extend({
@@ -2190,6 +3070,12 @@ vendorDescriptionElements.forEach((element) => {
2190
3070
  });
2191
3071
  })(jQuery);
2192
3072
 
3073
+ /*
3074
+ This file was created by developers working at BitBag
3075
+ Do you need more information about us and what we do? Visit our https://bitbag.io website!
3076
+ We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
3077
+ */
3078
+
2193
3079
  (function ( $ ) {
2194
3080
 
2195
3081
  $.fn.extend({
@@ -2201,7 +3087,7 @@ vendorDescriptionElements.forEach((element) => {
2201
3087
  if (callback !== undefined) {
2202
3088
  callback();
2203
3089
  }
2204
-
3090
+
2205
3091
  var actionButton = $(this);
2206
3092
  var form = actionButton.closest('form');
2207
3093
  var url = actionButton.data('url');
@@ -2253,6 +3139,12 @@ vendorDescriptionElements.forEach((element) => {
2253
3139
  });
2254
3140
  })(jQuery);
2255
3141
 
3142
+ /*
3143
+ This file was created by developers working at BitBag
3144
+ Do you need more information about us and what we do? Visit our https://bitbag.io website!
3145
+ We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
3146
+ */
3147
+
2256
3148
  (function ($) {
2257
3149
 
2258
3150
  $.fn.extend({
@@ -2319,6 +3211,12 @@ vendorDescriptionElements.forEach((element) => {
2319
3211
  });
2320
3212
  })(jQuery);
2321
3213
 
3214
+ /*
3215
+ This file was created by developers working at BitBag
3216
+ Do you need more information about us and what we do? Visit our https://bitbag.io website!
3217
+ We are hiring developers from all over the world. Join us and start your new, exciting adventure and become part of us: https://bitbag.io/career
3218
+ */
3219
+
2322
3220
  (function($) {
2323
3221
  $('.bitbag-cms-import input[type="text"]').click(function() {
2324
3222
  $(this).parent().find('input[type="file"]').click();
@@ -2332,60 +3230,6 @@ vendorDescriptionElements.forEach((element) => {
2332
3230
 
2333
3231
  })( jQuery );
2334
3232
 
2335
- class CustomerGroupClientAssigner {
2336
- constructor(selector, searchSelector = '.ng-client-search-select', submitSelector = '.ng-client-assign-submit') {
2337
- this.selector = selector;
2338
- this.searchSelector = searchSelector;
2339
- this.submitSelector = submitSelector;
2340
- }
2341
-
2342
- init() {
2343
- document.querySelectorAll(this.selector).forEach((containerElement) => {
2344
- const submitApiEndpoint = containerElement.getAttribute('data-assign-customers-url');
2345
- const redirectAfterUrl = containerElement.getAttribute('data-redirect-after-url');
2346
-
2347
- const searchElement = containerElement.querySelector(this.searchSelector);
2348
- const searchApiEndpoint = searchElement.getAttribute('data-customer-search-url');
2349
-
2350
- $(searchElement).dropdown({
2351
- apiSettings: {
2352
- url: `${searchApiEndpoint}/{query}`,
2353
- cache: false,
2354
- },
2355
-
2356
- minCharacters: 2,
2357
- });
2358
-
2359
- const submitElement = containerElement.querySelector(this.submitSelector);
2360
- submitElement.addEventListener('click', () => {
2361
- const selectedCustomersIds = this.getSelectValues(searchElement);
2362
- this.submitCustomers(submitElement, submitApiEndpoint, selectedCustomersIds, redirectAfterUrl);
2363
- });
2364
- });
2365
- }
2366
-
2367
- getSelectValues(selectElement) {
2368
- const selected = selectElement.querySelectorAll('option:checked');
2369
- return Array.from(selected).map((optionElement) => optionElement.value)
2370
- }
2371
-
2372
- submitCustomers(submitElement, apiEndpoint, selectedIds, redirectTo) {
2373
- submitElement.disabled = true;
2374
-
2375
- axios
2376
- .post(apiEndpoint, { customers: selectedIds })
2377
- .then(() => {
2378
- window.location = redirectTo;
2379
- })
2380
- .catch((error) => {
2381
- console.error(error);
2382
- })
2383
- .finally(() => {
2384
- submitElement.disabled = false;
2385
- });
2386
- }
2387
- }
2388
-
2389
3233
  // Scripts
2390
3234
 
2391
3235
  // Components initializations
@@ -2415,7 +3259,21 @@ $(document).ready(() => {
2415
3259
  const adminSidebarScroller = new AdminSidebarScroller(adminSidebarElement, 'a.item');
2416
3260
  adminSidebarScroller.scrollToActiveLink();
2417
3261
 
3262
+ const productVariantPricingGraph = new ProductVariantPricingGraph('.ng-product-variant-pricing-graph');
3263
+ productVariantPricingGraph.init();
3264
+
3265
+ const productVariantPricingSimulation = new ProductVariantPricingSimulation(
3266
+ '.ng-product-variant-pricing-simulation'
3267
+ );
3268
+ productVariantPricingSimulation.init();
3269
+
2418
3270
  // Client search select
2419
3271
  const clientSearchSelect = new CustomerGroupClientAssigner('.ng-customer-group-client-assigner');
2420
3272
  clientSearchSelect.init();
3273
+
3274
+ const imageInput = new ImageInput('.ng-image-input');
3275
+ imageInput.init();
3276
+
3277
+ const documentUploadInput = new DocumentUploadInput('input.ng-document-upload-input');
3278
+ documentUploadInput.init();
2421
3279
  });