@oxyshop/admin 1.3.38 → 1.3.39

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
@@ -25,12 +25,14 @@ import 'semantic-ui-css/components/visibility';
25
25
  import 'semantic-ui-css/components/visit';
26
26
  import 'jquery.dirtyforms/jquery.dirtyforms';
27
27
  import 'chart.js/dist/Chart.min';
28
+ import 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
- import { axios } from '@oxyshop/shop/lib/plugins/Axios';
34
36
 
35
37
  window.$ = $$1;
36
38
  window.jQuery = $$1;
@@ -1905,6 +1907,152 @@ class FeedCategorySelect {
1905
1907
  }
1906
1908
  }
1907
1909
 
1910
+ class ProductVariantPricingGraph {
1911
+ constructor(containerSelector) {
1912
+ this.containerSelector = containerSelector;
1913
+ }
1914
+
1915
+ init() {
1916
+ document.querySelectorAll(this.containerSelector).forEach((componentContainer) => {
1917
+ const graphsData = JSON.parse(componentContainer.getAttribute('data-graph'));
1918
+
1919
+ for (const currencyGraphIndex in graphsData) {
1920
+ const currencyGraph = graphsData[currencyGraphIndex];
1921
+
1922
+ const currencyCodeHeader = document.createElement('h4');
1923
+ currencyCodeHeader.classList.add('ui', 'header');
1924
+ currencyCodeHeader.textContent = currencyGraph.currency;
1925
+
1926
+ const graphCanvas = document.createElement('canvas');
1927
+ graphCanvas.width = 400;
1928
+ graphCanvas.height = 200;
1929
+
1930
+ const graphContainer = document.createElement('div');
1931
+ graphContainer.append(currencyCodeHeader);
1932
+ graphContainer.append(graphCanvas);
1933
+
1934
+ componentContainer.append(graphContainer);
1935
+
1936
+ const graphConfig = this.getGraphConfig(currencyGraph.graph);
1937
+ new Chart$1(graphCanvas.getContext('2d'), graphConfig);
1938
+ }
1939
+ });
1940
+ }
1941
+
1942
+ /** @private */
1943
+ getGraphConfig(rawGraphData) {
1944
+ return {
1945
+ type: 'line',
1946
+ data: {
1947
+ datasets: rawGraphData.datasets.map((rawDataset) => {
1948
+ return {
1949
+ label: rawDataset.label,
1950
+ data: rawDataset.data,
1951
+ borderColor: this.getRandomRgbColor(),
1952
+ fill: false,
1953
+ steppedLine: true,
1954
+ pointRadius: 8,
1955
+ pointHoverRadius: 10,
1956
+ }
1957
+ }),
1958
+ },
1959
+ options: {
1960
+ maintainAspectRatio: true,
1961
+ responsive: true,
1962
+ scales: {
1963
+ xAxes: [
1964
+ {
1965
+ type: 'linear',
1966
+ position: 'bottom',
1967
+ scaleLabel: {
1968
+ display: true,
1969
+ labelString: rawGraphData.xAxisLabel,
1970
+ },
1971
+ },
1972
+ ],
1973
+ yAxes: [
1974
+ {
1975
+ scaleLabel: {
1976
+ display: true,
1977
+ labelString: rawGraphData.yAxisLabel,
1978
+ },
1979
+ },
1980
+ ],
1981
+ },
1982
+ },
1983
+ }
1984
+ }
1985
+
1986
+ /** @private */
1987
+ getRandomRgbColor() {
1988
+ const r = Math.floor(Math.random() * 255);
1989
+ const g = Math.floor(Math.random() * 255);
1990
+ const b = Math.floor(Math.random() * 255);
1991
+ return `rgb(${r},${g},${b})`
1992
+ }
1993
+ }
1994
+
1995
+ class CustomerGroupClientAssigner {
1996
+ constructor(selector, searchSelector = '.ng-client-search-select', submitSelector = '.ng-client-assign-submit') {
1997
+ this.selector = selector;
1998
+ this.searchSelector = searchSelector;
1999
+ this.submitSelector = submitSelector;
2000
+
2001
+ this.containerElement = null;
2002
+ this.searchElement = null;
2003
+ this.submitElement = null;
2004
+ }
2005
+
2006
+ init() {
2007
+ this.containerElement = document.querySelector(this.selector);
2008
+ if (null === this.containerElement) {
2009
+ return
2010
+ }
2011
+
2012
+ const submitApiEndpoint = this.containerElement.getAttribute('data-assign-customers-url');
2013
+ const redirectAfterUrl = this.containerElement.getAttribute('data-redirect-after-url');
2014
+
2015
+ this.searchElement = this.containerElement.querySelector(this.searchSelector);
2016
+ const searchApiEndpoint = this.searchElement.getAttribute('data-customer-search-url');
2017
+
2018
+ $(this.searchElement).dropdown({
2019
+ apiSettings: {
2020
+ url: `${searchApiEndpoint}/{query}`,
2021
+ cache: false,
2022
+ },
2023
+
2024
+ minCharacters: 2,
2025
+ });
2026
+
2027
+ this.submitElement = this.containerElement.querySelector(this.submitSelector);
2028
+ this.submitElement.addEventListener('click', () => {
2029
+ const selectedCustomersIds = this.getSelectValues(this.searchElement);
2030
+ this.submitCustomers(submitApiEndpoint, selectedCustomersIds, redirectAfterUrl);
2031
+ });
2032
+ }
2033
+
2034
+ getSelectValues(selectElement) {
2035
+ const selected = selectElement.querySelectorAll('option:checked');
2036
+ return Array.from(selected).map((optionElement) => optionElement.value)
2037
+ }
2038
+
2039
+ submitCustomers(apiEndpoint, selectedIds, redirectTo) {
2040
+ this.submitElement.disabled = true;
2041
+
2042
+ axios
2043
+ .post(apiEndpoint, { customers: selectedIds })
2044
+ .then(() => {
2045
+ window.location = redirectTo;
2046
+ })
2047
+ .catch((error) => {
2048
+ console.error(error);
2049
+ })
2050
+ .finally(() => {
2051
+ this.submitElement.disabled = false;
2052
+ });
2053
+ }
2054
+ }
2055
+
1908
2056
  class TooltipHelpers {
1909
2057
  static init() {
1910
2058
  TooltipHelpers.initSyliusShippingMethodTooltip();
@@ -2038,6 +2186,277 @@ class CustomerGroupingRuleConfiguration {
2038
2186
  }
2039
2187
  }
2040
2188
 
2189
+ class ProductVariantPricingSimulation {
2190
+ constructor(selector) {
2191
+ this.selector = selector;
2192
+ }
2193
+
2194
+ init() {
2195
+ document.querySelectorAll(this.selector).forEach((element) => {
2196
+ const productVariantId = element.getAttribute('data-product-variant-id');
2197
+ const channels = JSON.parse(element.getAttribute('data-channels'));
2198
+ const searchApiEndpoint = element.getAttribute('data-customer-search-url');
2199
+ const pricingApiEndpoint = element.getAttribute('data-pricing-url');
2200
+
2201
+ this.initElement(element, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint);
2202
+ });
2203
+ }
2204
+
2205
+ initElement(parentElement, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint) {
2206
+ parentElement.innerHTML = '';
2207
+
2208
+ const simulationData = {
2209
+ productVariantId: productVariantId,
2210
+ channelId: null,
2211
+ currencyId: null,
2212
+ customerId: null,
2213
+ };
2214
+
2215
+ const submitButton = document.createElement('button');
2216
+ const currencySelect = document.createElement('select');
2217
+
2218
+ const onChannelSelect = (event) => {
2219
+ const selectedChannelId = parseInt(event.target.value);
2220
+ simulationData.channelId = selectedChannelId;
2221
+ simulationData.currencyId = null;
2222
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2223
+
2224
+ const selectedChannel = channels.find((channel) => {
2225
+ return channel.id === selectedChannelId
2226
+ });
2227
+
2228
+ currencySelect.innerHTML = '';
2229
+ this.addSelectOptions(currencySelect, selectedChannel.currencies, 'Select currency');
2230
+ };
2231
+
2232
+ const onCurrencySelect = (event) => {
2233
+ simulationData.currencyId = event.target.value;
2234
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2235
+ };
2236
+
2237
+ const onCustomerSelect = (event) => {
2238
+ simulationData.customerId = event.target.value;
2239
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData);
2240
+ };
2241
+
2242
+ const graphWrapper = this.createGraphWrapper();
2243
+
2244
+ const onSubmit = () => {
2245
+ const pricingUrl = `${pricingApiEndpoint}?${this.serializeObjectToQuery(simulationData)}`;
2246
+
2247
+ axios$1
2248
+ .get(pricingUrl)
2249
+ .then((response) => {
2250
+ const chartData = response.data;
2251
+ graphWrapper.setAttribute('data-debug-chart', JSON.stringify(chartData)); // For e2e tests
2252
+ this.renderGraph(graphWrapper, chartData);
2253
+ })
2254
+ .catch((error) => {
2255
+ console.error(error);
2256
+ graphWrapper.innerHTML = 'Pricing simulation error';
2257
+ });
2258
+ };
2259
+
2260
+ const wrapper = document.createElement('div');
2261
+ wrapper.setAttribute('style', 'border: solid 1px #ddd; background-color: #fafafa;');
2262
+
2263
+ const filters = this.createFilters(
2264
+ channels,
2265
+ currencySelect,
2266
+ onChannelSelect,
2267
+ onCurrencySelect,
2268
+ onCustomerSelect,
2269
+ onSubmit,
2270
+ submitButton,
2271
+ searchApiEndpoint
2272
+ );
2273
+ filters.setAttribute(
2274
+ 'style',
2275
+ 'display: grid; grid-template-columns: 1fr 1fr 1fr 60px; grid-gap: 10px; ' +
2276
+ 'padding: 15px; border-bottom: solid 1px #ddd;'
2277
+ );
2278
+
2279
+ const graphFiller = this.createGraphFiller();
2280
+ graphWrapper.append(graphFiller);
2281
+
2282
+ wrapper.append(filters, graphWrapper);
2283
+ parentElement.append(wrapper);
2284
+ }
2285
+
2286
+ createFilters(
2287
+ channels,
2288
+ currencySelect,
2289
+ onChannelSelect,
2290
+ onCurrencySelect,
2291
+ onCustomerSelect,
2292
+ onSubmit,
2293
+ submitButton,
2294
+ searchApiEndpoint
2295
+ ) {
2296
+ const filtersWrapper = document.createElement('div');
2297
+
2298
+ const channelSelect = document.createElement('select');
2299
+ this.addSelectOptions(channelSelect, channels, 'Select channel');
2300
+ channelSelect.addEventListener('change', onChannelSelect);
2301
+
2302
+ currencySelect.addEventListener('change', onCurrencySelect);
2303
+
2304
+ const customerSelect = document.createElement('select');
2305
+ customerSelect.addEventListener('change', onCustomerSelect);
2306
+
2307
+ // this is delayed to avoid racing conditions
2308
+ setTimeout(() => this.hookClientSearchOnSelect(customerSelect, searchApiEndpoint), 600);
2309
+
2310
+ submitButton.disabled = true;
2311
+ submitButton.setAttribute('class', 'ui icon primary button');
2312
+ submitButton.type = 'button';
2313
+ submitButton.addEventListener('click', onSubmit);
2314
+
2315
+ const playIcon = document.createElement('i');
2316
+ playIcon.setAttribute('class', 'icon play');
2317
+ submitButton.append(playIcon);
2318
+
2319
+ filtersWrapper.append(channelSelect, currencySelect, customerSelect, submitButton);
2320
+
2321
+ return filtersWrapper
2322
+ }
2323
+
2324
+ createGraphWrapper() {
2325
+ const wrapper = document.createElement('div');
2326
+ wrapper.setAttribute('style', 'padding: 15px;');
2327
+
2328
+ return wrapper
2329
+ }
2330
+
2331
+ createGraphFiller() {
2332
+ const filler = document.createElement('div');
2333
+ filler.setAttribute(
2334
+ 'style',
2335
+ 'border-radius: 7px; background-color: #eee; height: 350px; display: flex; ' +
2336
+ 'justify-content: space-around; align-items: center; font-size: 4em;'
2337
+ );
2338
+
2339
+ const chartIcon = document.createElement('i');
2340
+ chartIcon.setAttribute('class', 'icon chart line');
2341
+ filler.append(chartIcon);
2342
+
2343
+ return filler
2344
+ }
2345
+
2346
+ addSelectOptions(select, choices, placeholder = null) {
2347
+ if (placeholder !== null) {
2348
+ const placeholderOption = document.createElement('option');
2349
+ placeholderOption.innerHTML = placeholder;
2350
+ placeholderOption.disabled = true;
2351
+ placeholderOption.selected = true;
2352
+ select.append(placeholderOption);
2353
+ }
2354
+
2355
+ for (const property in choices) {
2356
+ const choice = choices[property];
2357
+
2358
+ const channelOption = document.createElement('option');
2359
+ channelOption.innerHTML = choice.name;
2360
+ channelOption.setAttribute('value', choice.id);
2361
+ select.append(channelOption);
2362
+ }
2363
+
2364
+ return select
2365
+ }
2366
+
2367
+ hookClientSearchOnSelect(selectElement, searchApiEndpoint) {
2368
+ selectElement.setAttribute('class', `${selectElement.getAttribute('class')} search dropdown selection`);
2369
+
2370
+ $(selectElement).dropdown({
2371
+ apiSettings: {
2372
+ url: `${searchApiEndpoint}/{query}`,
2373
+ cache: false,
2374
+ },
2375
+
2376
+ minCharacters: 2,
2377
+ });
2378
+ }
2379
+
2380
+ isSubmitButtonDisabled(simulationData) {
2381
+ return (
2382
+ null === simulationData.productVariantId ||
2383
+ null === simulationData.channelId ||
2384
+ null === simulationData.currencyId ||
2385
+ null === simulationData.customerId
2386
+ )
2387
+ }
2388
+
2389
+ serializeObjectToQuery(object) {
2390
+ const query = [];
2391
+
2392
+ for (const part in object) {
2393
+ if (Object.prototype.hasOwnProperty.call(object, part)) {
2394
+ query.push(`${encodeURIComponent(part)}=${encodeURIComponent(object[part])}`);
2395
+ }
2396
+ }
2397
+
2398
+ return query.join('&')
2399
+ }
2400
+
2401
+ renderGraph(graphWrapper, graphData) {
2402
+ graphWrapper.innerHTML = '';
2403
+
2404
+ const graphCanvas = document.createElement('canvas');
2405
+ graphCanvas.width = 600;
2406
+ graphCanvas.height = 200;
2407
+
2408
+ graphWrapper.append(graphCanvas);
2409
+
2410
+ const graphConfig = this.getGraphConfig(graphData);
2411
+ console.log('graphConfig', graphConfig);
2412
+ new Chart$1(graphCanvas.getContext('2d'), graphConfig);
2413
+ }
2414
+
2415
+ /** @private */
2416
+ getGraphConfig(rawGraphData) {
2417
+ return {
2418
+ type: 'line',
2419
+ data: {
2420
+ datasets: rawGraphData.datasets.map((rawDataset) => {
2421
+ return {
2422
+ label: rawDataset.label,
2423
+ data: rawDataset.data,
2424
+ borderColor: '#0000ff',
2425
+ fill: false,
2426
+ steppedLine: true,
2427
+ pointRadius: 8,
2428
+ pointHoverRadius: 10,
2429
+ }
2430
+ }),
2431
+ },
2432
+ options: {
2433
+ maintainAspectRatio: true,
2434
+ responsive: true,
2435
+ scales: {
2436
+ xAxes: [
2437
+ {
2438
+ type: 'linear',
2439
+ position: 'bottom',
2440
+ scaleLabel: {
2441
+ display: true,
2442
+ labelString: rawGraphData.xAxisLabel,
2443
+ },
2444
+ },
2445
+ ],
2446
+ yAxes: [
2447
+ {
2448
+ scaleLabel: {
2449
+ display: true,
2450
+ labelString: rawGraphData.yAxisLabel,
2451
+ },
2452
+ },
2453
+ ],
2454
+ },
2455
+ },
2456
+ }
2457
+ }
2458
+ }
2459
+
2041
2460
  /**
2042
2461
  * CKEditor config
2043
2462
  */
@@ -2331,63 +2750,6 @@ vendorDescriptionElements.forEach((element) => {
2331
2750
 
2332
2751
  })( jQuery );
2333
2752
 
2334
- class CustomerGroupClientAssigner {
2335
- constructor(selector, searchSelector = '.ng-client-search-select', submitSelector = '.ng-client-assign-submit') {
2336
- this.selector = selector;
2337
- this.searchSelector = searchSelector;
2338
- this.submitSelector = submitSelector;
2339
-
2340
- this.containerElement = null;
2341
- this.searchElement = null;
2342
- this.submitElement = null;
2343
- }
2344
-
2345
- init() {
2346
- this.containerElement = document.querySelector(this.selector);
2347
- const submitApiEndpoint = this.containerElement.getAttribute('data-assign-customers-url');
2348
- const redirectAfterUrl = this.containerElement.getAttribute('data-redirect-after-url');
2349
-
2350
- this.searchElement = this.containerElement.querySelector(this.searchSelector);
2351
- const searchApiEndpoint = this.searchElement.getAttribute('data-customer-search-url');
2352
-
2353
- $(this.searchElement).dropdown({
2354
- apiSettings: {
2355
- url: `${searchApiEndpoint}/{query}`,
2356
- cache: false,
2357
- },
2358
-
2359
- minCharacters: 2,
2360
- });
2361
-
2362
- this.submitElement = this.containerElement.querySelector(this.submitSelector);
2363
- this.submitElement.addEventListener('click', () => {
2364
- const selectedCustomersIds = this.getSelectValues(this.searchElement);
2365
- this.submitCustomers(submitApiEndpoint, selectedCustomersIds, redirectAfterUrl);
2366
- });
2367
- }
2368
-
2369
- getSelectValues(selectElement) {
2370
- const selected = selectElement.querySelectorAll('option:checked');
2371
- return Array.from(selected).map((optionElement) => optionElement.value)
2372
- }
2373
-
2374
- submitCustomers(apiEndpoint, selectedIds, redirectTo) {
2375
- this.submitElement.disabled = true;
2376
-
2377
- axios
2378
- .post(apiEndpoint, { customers: selectedIds })
2379
- .then(() => {
2380
- window.location = redirectTo;
2381
- })
2382
- .catch((error) => {
2383
- console.error(error);
2384
- })
2385
- .finally(() => {
2386
- this.submitElement.disabled = false;
2387
- });
2388
- }
2389
- }
2390
-
2391
2753
  // Scripts
2392
2754
 
2393
2755
  // Components initializations
@@ -2403,20 +2765,28 @@ $(document).ready(() => {
2403
2765
  */
2404
2766
  TooltipHelpers.init();
2405
2767
 
2406
- const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select');
2407
- feedCategorySelect.init();
2408
-
2409
2768
  const customerGroupingRuleConfiguration = new CustomerGroupingRuleConfiguration(
2410
2769
  'select.ng-grouping-rule-select',
2411
2770
  '.ng-grouping-rule-configuration'
2412
2771
  );
2413
2772
  customerGroupingRuleConfiguration.init();
2414
2773
 
2774
+ const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select');
2775
+ feedCategorySelect.init();
2776
+
2415
2777
  // Admin sidebar scroller
2416
2778
  const adminSidebarElement = document.getElementById('sidebar');
2417
2779
  const adminSidebarScroller = new AdminSidebarScroller(adminSidebarElement, 'a.item');
2418
2780
  adminSidebarScroller.scrollToActiveLink();
2419
2781
 
2782
+ const productVariantPricingGraph = new ProductVariantPricingGraph('.ng-product-variant-pricing-graph');
2783
+ productVariantPricingGraph.init();
2784
+
2785
+ const productVariantPricingSimulation = new ProductVariantPricingSimulation(
2786
+ '.ng-product-variant-pricing-simulation'
2787
+ );
2788
+ productVariantPricingSimulation.init();
2789
+
2420
2790
  // Client search select
2421
2791
  const clientSearchSelect = new CustomerGroupClientAssigner('.ng-customer-group-client-assigner');
2422
2792
  clientSearchSelect.init();
package/lib/style.css CHANGED
@@ -1,3 +1,217 @@
1
+ .sylius-filters {
2
+ display: flex;
3
+ flex-wrap: wrap;
4
+ margin-left: -10px;
5
+ margin-right: -10px; }
6
+ .sylius-filters__field {
7
+ flex-grow: 1;
8
+ min-width: 360px;
9
+ margin-left: 10px;
10
+ margin-right: 10px; }
11
+ .sylius-filters .sylius-filters__group {
12
+ display: flex;
13
+ flex-wrap: wrap;
14
+ align-items: flex-start; }
15
+ .sylius-filters .sylius-filters__group > .field {
16
+ flex-grow: 1; }
17
+ .sylius-filters .sylius-filters__group > input {
18
+ flex-grow: 1;
19
+ width: auto !important; }
20
+ .sylius-filters .sylius-filters__group > .disabled.field {
21
+ flex-grow: 0; }
22
+ .sylius-filters .sylius-filters__group > .disabled.field input {
23
+ width: 40px !important;
24
+ background: #eeeeee;
25
+ text-align: center; }
26
+ .sylius-filters .field {
27
+ margin-bottom: 22px !important; }
28
+ .sylius-filters .field label {
29
+ font-weight: 700 !important; }
30
+
31
+ .sylius-filters select {
32
+ -webkit-appearance: none;
33
+ -moz-appearance: none;
34
+ appearance: none;
35
+ background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23444444'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat !important;
36
+ background-size: 8px 12px !important;
37
+ background-position: calc(100% - 10px) calc(50% + 3px) !important; }
38
+
39
+ .sylius-filters .sylius-filters__group input, .sylius-filters .sylius-filters__group select {
40
+ border-radius: 0 !important;
41
+ border-right-width: 0 !important; }
42
+
43
+ .sylius-filters .sylius-filters__group .field:last-child input:last-child,
44
+ .sylius-filters .sylius-filters__group .field:last-child select:last-child {
45
+ border-right-width: 1px !important;
46
+ border-radius: 0 .28571429rem .28571429rem 0 !important; }
47
+
48
+ .sylius-filters .sylius-filters__group .field:first-child input:first-of-type,
49
+ .sylius-filters .sylius-filters__group .field:first-child select:first-of-type {
50
+ border-radius: .28571429rem 0 0 .28571429rem !important; }
51
+
52
+ .overflow-x-auto {
53
+ overflow-x: auto; }
54
+
55
+ #wrapper {
56
+ padding: 54px 0 0 0; }
57
+
58
+ #content {
59
+ padding: 1em; }
60
+
61
+ #sidebar {
62
+ font-size: 1.1em;
63
+ padding-bottom: 30px; }
64
+
65
+ #logo {
66
+ margin-bottom: 1em; }
67
+
68
+ body.pushable .pusher {
69
+ background-color: #f9fAfb; }
70
+
71
+ .ui.visible.left.sidebar ~ .fixed.menu,
72
+ .ui.visible.left.sidebar ~ .pusher {
73
+ padding-right: 260px; }
74
+
75
+ body.centered {
76
+ background-color: #f9fAfb; }
77
+ body.centered .grid {
78
+ height: 100%; }
79
+ body.centered .column {
80
+ padding: 1em;
81
+ max-width: 450px; }
82
+
83
+ select.ui.dropdown[multiple="multiple"] {
84
+ height: 100px; }
85
+
86
+ .ui.segments {
87
+ border: 0; }
88
+ .ui.segments .ui.segment {
89
+ border: 1px solid rgba(34, 36, 38, 0.15) !important; }
90
+
91
+ .ui.breadcrumb {
92
+ margin-top: 1em;
93
+ margin-left: 1em;
94
+ margin-bottom: 1em; }
95
+
96
+ th a {
97
+ color: rgba(0, 0, 0, 0.87); }
98
+
99
+ th a:hover {
100
+ color: rgba(0, 0, 0, 0.87); }
101
+
102
+ th a:visited {
103
+ color: rgba(0, 0, 0, 0.87); }
104
+
105
+ .ui.compact.segment .inline.fields {
106
+ margin: 0; }
107
+
108
+ .ui.hidden.element {
109
+ display: none; }
110
+
111
+ .ui.monster.header {
112
+ font-size: 3em; }
113
+
114
+ .ui.styled.header {
115
+ text-transform: uppercase;
116
+ letter-spacing: 1px;
117
+ background-color: #f9fAfb;
118
+ font-size: 0.9em;
119
+ padding-bottom: 7px; }
120
+
121
+ .ui.input input[readonly] {
122
+ color: #aaa; }
123
+
124
+ .field.loading.transition {
125
+ top: auto;
126
+ left: auto; }
127
+
128
+ .ui.floated.dividing.empty {
129
+ top: 100%;
130
+ bottom: auto;
131
+ padding: 0;
132
+ margin: 0; }
133
+
134
+ .ui.left.floated.dividing.empty ~ .ui.dropdown > .menu {
135
+ right: auto;
136
+ left: 0; }
137
+
138
+ .ui.right.floated.dividing.empty ~ .ui.dropdown > .menu {
139
+ right: 0;
140
+ left: auto; }
141
+
142
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk,
143
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination,
144
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__perpage {
145
+ padding-bottom: 1rem; }
146
+
147
+ .sylius-grid-wrapper .sylius-grid-nav__bulk .ui.red.labeled.icon.button:disabled {
148
+ background: #b9babb !important; }
149
+
150
+ .sylius-grid-wrapper .sylius-grid-table-wrapper {
151
+ overflow-x: auto;
152
+ margin-bottom: 1rem; }
153
+ .sylius-grid-wrapper .sylius-grid-table-wrapper .ui.buttons, .sylius-grid-wrapper .sylius-grid-table-wrapper .ui.label {
154
+ white-space: nowrap; }
155
+
156
+ @media only screen and (min-width: 768px) {
157
+ .sylius-grid-wrapper .sylius-grid-nav {
158
+ display: flex;
159
+ flex-wrap: wrap;
160
+ margin-left: -1rem;
161
+ margin-right: -1rem; }
162
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk,
163
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination,
164
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__perpage {
165
+ padding-left: 1rem;
166
+ padding-right: 1rem; }
167
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk {
168
+ display: flex; }
169
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk .button {
170
+ padding-top: 0.99em !important;
171
+ padding-bottom: 0.99em !important; }
172
+ .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination {
173
+ flex-grow: 1; }
174
+ .sylius-grid-wrapper .pagination {
175
+ flex-wrap: wrap; } }
176
+
177
+ #wrapper.full.height {
178
+ position: relative;
179
+ padding-bottom: 80px !important;
180
+ min-height: 100vh; }
181
+ #wrapper.full.height::after {
182
+ content: '';
183
+ display: block;
184
+ position: absolute;
185
+ bottom: 60px;
186
+ right: 30px;
187
+ width: calc(100% - 60px);
188
+ height: 1px;
189
+ background: #ebebeb; }
190
+ #wrapper.full.height .sylius-footer {
191
+ position: absolute;
192
+ bottom: 20px;
193
+ right: 40px;
194
+ font-size: 13px;
195
+ color: #9a9a9a; }
196
+
197
+ input[type="color"] {
198
+ -webkit-appearance: none;
199
+ border: 1px solid rgba(34, 36, 38, 0.15);
200
+ width: 38px;
201
+ height: 38px;
202
+ padding: 3px;
203
+ border-radius: 99px; }
204
+
205
+ input[type="color"]::-webkit-color-swatch,
206
+ input[type="color"]::-webkit-color-swatch-wrapper {
207
+ border-radius: 99px;
208
+ border: 0;
209
+ padding: 3px; }
210
+
211
+ input[type="color"]::-moz-color-swatch {
212
+ border-radius: 99px;
213
+ border: 0; }
214
+
1
215
  .sylius-tree.hidden {
2
216
  display: none; }
3
217
 
@@ -301,217 +515,3 @@ a {
301
515
 
302
516
  .promotion-disabled {
303
517
  color: #a0a0a0; }
304
-
305
- .sylius-filters {
306
- display: flex;
307
- flex-wrap: wrap;
308
- margin-left: -10px;
309
- margin-right: -10px; }
310
- .sylius-filters__field {
311
- flex-grow: 1;
312
- min-width: 360px;
313
- margin-left: 10px;
314
- margin-right: 10px; }
315
- .sylius-filters .sylius-filters__group {
316
- display: flex;
317
- flex-wrap: wrap;
318
- align-items: flex-start; }
319
- .sylius-filters .sylius-filters__group > .field {
320
- flex-grow: 1; }
321
- .sylius-filters .sylius-filters__group > input {
322
- flex-grow: 1;
323
- width: auto !important; }
324
- .sylius-filters .sylius-filters__group > .disabled.field {
325
- flex-grow: 0; }
326
- .sylius-filters .sylius-filters__group > .disabled.field input {
327
- width: 40px !important;
328
- background: #eeeeee;
329
- text-align: center; }
330
- .sylius-filters .field {
331
- margin-bottom: 22px !important; }
332
- .sylius-filters .field label {
333
- font-weight: 700 !important; }
334
-
335
- .sylius-filters select {
336
- -webkit-appearance: none;
337
- -moz-appearance: none;
338
- appearance: none;
339
- background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23444444'><polygon points='0,0 100,0 50,50'/></svg>") no-repeat !important;
340
- background-size: 8px 12px !important;
341
- background-position: calc(100% - 10px) calc(50% + 3px) !important; }
342
-
343
- .sylius-filters .sylius-filters__group input, .sylius-filters .sylius-filters__group select {
344
- border-radius: 0 !important;
345
- border-right-width: 0 !important; }
346
-
347
- .sylius-filters .sylius-filters__group .field:last-child input:last-child,
348
- .sylius-filters .sylius-filters__group .field:last-child select:last-child {
349
- border-right-width: 1px !important;
350
- border-radius: 0 .28571429rem .28571429rem 0 !important; }
351
-
352
- .sylius-filters .sylius-filters__group .field:first-child input:first-of-type,
353
- .sylius-filters .sylius-filters__group .field:first-child select:first-of-type {
354
- border-radius: .28571429rem 0 0 .28571429rem !important; }
355
-
356
- .overflow-x-auto {
357
- overflow-x: auto; }
358
-
359
- #wrapper {
360
- padding: 54px 0 0 0; }
361
-
362
- #content {
363
- padding: 1em; }
364
-
365
- #sidebar {
366
- font-size: 1.1em;
367
- padding-bottom: 30px; }
368
-
369
- #logo {
370
- margin-bottom: 1em; }
371
-
372
- body.pushable .pusher {
373
- background-color: #f9fAfb; }
374
-
375
- .ui.visible.left.sidebar ~ .fixed.menu,
376
- .ui.visible.left.sidebar ~ .pusher {
377
- padding-right: 260px; }
378
-
379
- body.centered {
380
- background-color: #f9fAfb; }
381
- body.centered .grid {
382
- height: 100%; }
383
- body.centered .column {
384
- padding: 1em;
385
- max-width: 450px; }
386
-
387
- select.ui.dropdown[multiple="multiple"] {
388
- height: 100px; }
389
-
390
- .ui.segments {
391
- border: 0; }
392
- .ui.segments .ui.segment {
393
- border: 1px solid rgba(34, 36, 38, 0.15) !important; }
394
-
395
- .ui.breadcrumb {
396
- margin-top: 1em;
397
- margin-left: 1em;
398
- margin-bottom: 1em; }
399
-
400
- th a {
401
- color: rgba(0, 0, 0, 0.87); }
402
-
403
- th a:hover {
404
- color: rgba(0, 0, 0, 0.87); }
405
-
406
- th a:visited {
407
- color: rgba(0, 0, 0, 0.87); }
408
-
409
- .ui.compact.segment .inline.fields {
410
- margin: 0; }
411
-
412
- .ui.hidden.element {
413
- display: none; }
414
-
415
- .ui.monster.header {
416
- font-size: 3em; }
417
-
418
- .ui.styled.header {
419
- text-transform: uppercase;
420
- letter-spacing: 1px;
421
- background-color: #f9fAfb;
422
- font-size: 0.9em;
423
- padding-bottom: 7px; }
424
-
425
- .ui.input input[readonly] {
426
- color: #aaa; }
427
-
428
- .field.loading.transition {
429
- top: auto;
430
- left: auto; }
431
-
432
- .ui.floated.dividing.empty {
433
- top: 100%;
434
- bottom: auto;
435
- padding: 0;
436
- margin: 0; }
437
-
438
- .ui.left.floated.dividing.empty ~ .ui.dropdown > .menu {
439
- right: auto;
440
- left: 0; }
441
-
442
- .ui.right.floated.dividing.empty ~ .ui.dropdown > .menu {
443
- right: 0;
444
- left: auto; }
445
-
446
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk,
447
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination,
448
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__perpage {
449
- padding-bottom: 1rem; }
450
-
451
- .sylius-grid-wrapper .sylius-grid-nav__bulk .ui.red.labeled.icon.button:disabled {
452
- background: #b9babb !important; }
453
-
454
- .sylius-grid-wrapper .sylius-grid-table-wrapper {
455
- overflow-x: auto;
456
- margin-bottom: 1rem; }
457
- .sylius-grid-wrapper .sylius-grid-table-wrapper .ui.buttons, .sylius-grid-wrapper .sylius-grid-table-wrapper .ui.label {
458
- white-space: nowrap; }
459
-
460
- @media only screen and (min-width: 768px) {
461
- .sylius-grid-wrapper .sylius-grid-nav {
462
- display: flex;
463
- flex-wrap: wrap;
464
- margin-left: -1rem;
465
- margin-right: -1rem; }
466
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk,
467
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination,
468
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__perpage {
469
- padding-left: 1rem;
470
- padding-right: 1rem; }
471
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk {
472
- display: flex; }
473
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__bulk .button {
474
- padding-top: 0.99em !important;
475
- padding-bottom: 0.99em !important; }
476
- .sylius-grid-wrapper .sylius-grid-nav .sylius-grid-nav__pagination {
477
- flex-grow: 1; }
478
- .sylius-grid-wrapper .pagination {
479
- flex-wrap: wrap; } }
480
-
481
- #wrapper.full.height {
482
- position: relative;
483
- padding-bottom: 80px !important;
484
- min-height: 100vh; }
485
- #wrapper.full.height::after {
486
- content: '';
487
- display: block;
488
- position: absolute;
489
- bottom: 60px;
490
- right: 30px;
491
- width: calc(100% - 60px);
492
- height: 1px;
493
- background: #ebebeb; }
494
- #wrapper.full.height .sylius-footer {
495
- position: absolute;
496
- bottom: 20px;
497
- right: 40px;
498
- font-size: 13px;
499
- color: #9a9a9a; }
500
-
501
- input[type="color"] {
502
- -webkit-appearance: none;
503
- border: 1px solid rgba(34, 36, 38, 0.15);
504
- width: 38px;
505
- height: 38px;
506
- padding: 3px;
507
- border-radius: 99px; }
508
-
509
- input[type="color"]::-webkit-color-swatch,
510
- input[type="color"]::-webkit-color-swatch-wrapper {
511
- border-radius: 99px;
512
- border: 0;
513
- padding: 3px; }
514
-
515
- input[type="color"]::-moz-color-swatch {
516
- border-radius: 99px;
517
- border: 0; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyshop/admin",
3
- "version": "1.3.38",
3
+ "version": "1.3.39",
4
4
  "author": "oXy Online s.r.o. <info@oxyshop.cz>",
5
5
  "main": "lib/index.js",
6
6
  "sass": "scss/main.scss",
@@ -13,6 +13,10 @@ export default class CustomerGroupClientAssigner {
13
13
 
14
14
  init() {
15
15
  this.containerElement = document.querySelector(this.selector)
16
+ if (null === this.containerElement) {
17
+ return
18
+ }
19
+
16
20
  const submitApiEndpoint = this.containerElement.getAttribute('data-assign-customers-url')
17
21
  const redirectAfterUrl = this.containerElement.getAttribute('data-redirect-after-url')
18
22
 
@@ -0,0 +1,86 @@
1
+ import Chart from 'chart.js'
2
+
3
+ export default class ProductVariantPricingGraph {
4
+ constructor(containerSelector) {
5
+ this.containerSelector = containerSelector
6
+ }
7
+
8
+ init() {
9
+ document.querySelectorAll(this.containerSelector).forEach((componentContainer) => {
10
+ const graphsData = JSON.parse(componentContainer.getAttribute('data-graph'))
11
+
12
+ for (const currencyGraphIndex in graphsData) {
13
+ const currencyGraph = graphsData[currencyGraphIndex]
14
+
15
+ const currencyCodeHeader = document.createElement('h4')
16
+ currencyCodeHeader.classList.add('ui', 'header')
17
+ currencyCodeHeader.textContent = currencyGraph.currency
18
+
19
+ const graphCanvas = document.createElement('canvas')
20
+ graphCanvas.width = 400
21
+ graphCanvas.height = 200
22
+
23
+ const graphContainer = document.createElement('div')
24
+ graphContainer.append(currencyCodeHeader)
25
+ graphContainer.append(graphCanvas)
26
+
27
+ componentContainer.append(graphContainer)
28
+
29
+ const graphConfig = this.getGraphConfig(currencyGraph.graph)
30
+ new Chart(graphCanvas.getContext('2d'), graphConfig)
31
+ }
32
+ })
33
+ }
34
+
35
+ /** @private */
36
+ getGraphConfig(rawGraphData) {
37
+ return {
38
+ type: 'line',
39
+ data: {
40
+ datasets: rawGraphData.datasets.map((rawDataset) => {
41
+ return {
42
+ label: rawDataset.label,
43
+ data: rawDataset.data,
44
+ borderColor: this.getRandomRgbColor(),
45
+ fill: false,
46
+ steppedLine: true,
47
+ pointRadius: 8,
48
+ pointHoverRadius: 10,
49
+ }
50
+ }),
51
+ },
52
+ options: {
53
+ maintainAspectRatio: true,
54
+ responsive: true,
55
+ scales: {
56
+ xAxes: [
57
+ {
58
+ type: 'linear',
59
+ position: 'bottom',
60
+ scaleLabel: {
61
+ display: true,
62
+ labelString: rawGraphData.xAxisLabel,
63
+ },
64
+ },
65
+ ],
66
+ yAxes: [
67
+ {
68
+ scaleLabel: {
69
+ display: true,
70
+ labelString: rawGraphData.yAxisLabel,
71
+ },
72
+ },
73
+ ],
74
+ },
75
+ },
76
+ }
77
+ }
78
+
79
+ /** @private */
80
+ getRandomRgbColor() {
81
+ const r = Math.floor(Math.random() * 255)
82
+ const g = Math.floor(Math.random() * 255)
83
+ const b = Math.floor(Math.random() * 255)
84
+ return `rgb(${r},${g},${b})`
85
+ }
86
+ }
@@ -0,0 +1,273 @@
1
+ import axios from 'axios'
2
+ import Chart from 'chart.js'
3
+
4
+ export default class ProductVariantPricingSimulation {
5
+ constructor(selector) {
6
+ this.selector = selector
7
+ }
8
+
9
+ init() {
10
+ document.querySelectorAll(this.selector).forEach((element) => {
11
+ const productVariantId = element.getAttribute('data-product-variant-id')
12
+ const channels = JSON.parse(element.getAttribute('data-channels'))
13
+ const searchApiEndpoint = element.getAttribute('data-customer-search-url')
14
+ const pricingApiEndpoint = element.getAttribute('data-pricing-url')
15
+
16
+ this.initElement(element, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint)
17
+ })
18
+ }
19
+
20
+ initElement(parentElement, productVariantId, channels, searchApiEndpoint, pricingApiEndpoint) {
21
+ parentElement.innerHTML = ''
22
+
23
+ const simulationData = {
24
+ productVariantId: productVariantId,
25
+ channelId: null,
26
+ currencyId: null,
27
+ customerId: null,
28
+ }
29
+
30
+ const submitButton = document.createElement('button')
31
+ const currencySelect = document.createElement('select')
32
+
33
+ const onChannelSelect = (event) => {
34
+ const selectedChannelId = parseInt(event.target.value)
35
+ simulationData.channelId = selectedChannelId
36
+ simulationData.currencyId = null
37
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData)
38
+
39
+ const selectedChannel = channels.find((channel) => {
40
+ return channel.id === selectedChannelId
41
+ })
42
+
43
+ currencySelect.innerHTML = ''
44
+ this.addSelectOptions(currencySelect, selectedChannel.currencies, 'Select currency')
45
+ }
46
+
47
+ const onCurrencySelect = (event) => {
48
+ simulationData.currencyId = event.target.value
49
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData)
50
+ }
51
+
52
+ const onCustomerSelect = (event) => {
53
+ simulationData.customerId = event.target.value
54
+ submitButton.disabled = this.isSubmitButtonDisabled(simulationData)
55
+ }
56
+
57
+ const graphWrapper = this.createGraphWrapper()
58
+
59
+ const onSubmit = () => {
60
+ const pricingUrl = `${pricingApiEndpoint}?${this.serializeObjectToQuery(simulationData)}`
61
+
62
+ axios
63
+ .get(pricingUrl)
64
+ .then((response) => {
65
+ const chartData = response.data
66
+ graphWrapper.setAttribute('data-debug-chart', JSON.stringify(chartData)) // For e2e tests
67
+ this.renderGraph(graphWrapper, chartData)
68
+ })
69
+ .catch((error) => {
70
+ console.error(error)
71
+ graphWrapper.innerHTML = 'Pricing simulation error'
72
+ })
73
+ }
74
+
75
+ const wrapper = document.createElement('div')
76
+ wrapper.setAttribute('style', 'border: solid 1px #ddd; background-color: #fafafa;')
77
+
78
+ const filters = this.createFilters(
79
+ channels,
80
+ currencySelect,
81
+ onChannelSelect,
82
+ onCurrencySelect,
83
+ onCustomerSelect,
84
+ onSubmit,
85
+ submitButton,
86
+ searchApiEndpoint
87
+ )
88
+ filters.setAttribute(
89
+ 'style',
90
+ 'display: grid; grid-template-columns: 1fr 1fr 1fr 60px; grid-gap: 10px; ' +
91
+ 'padding: 15px; border-bottom: solid 1px #ddd;'
92
+ )
93
+
94
+ const graphFiller = this.createGraphFiller()
95
+ graphWrapper.append(graphFiller)
96
+
97
+ wrapper.append(filters, graphWrapper)
98
+ parentElement.append(wrapper)
99
+ }
100
+
101
+ createFilters(
102
+ channels,
103
+ currencySelect,
104
+ onChannelSelect,
105
+ onCurrencySelect,
106
+ onCustomerSelect,
107
+ onSubmit,
108
+ submitButton,
109
+ searchApiEndpoint
110
+ ) {
111
+ const filtersWrapper = document.createElement('div')
112
+
113
+ const channelSelect = document.createElement('select')
114
+ this.addSelectOptions(channelSelect, channels, 'Select channel')
115
+ channelSelect.addEventListener('change', onChannelSelect)
116
+
117
+ currencySelect.addEventListener('change', onCurrencySelect)
118
+
119
+ const customerSelect = document.createElement('select')
120
+ customerSelect.addEventListener('change', onCustomerSelect)
121
+
122
+ // this is delayed to avoid racing conditions
123
+ setTimeout(() => this.hookClientSearchOnSelect(customerSelect, searchApiEndpoint), 600)
124
+
125
+ submitButton.disabled = true
126
+ submitButton.setAttribute('class', 'ui icon primary button')
127
+ submitButton.type = 'button'
128
+ submitButton.addEventListener('click', onSubmit)
129
+
130
+ const playIcon = document.createElement('i')
131
+ playIcon.setAttribute('class', 'icon play')
132
+ submitButton.append(playIcon)
133
+
134
+ filtersWrapper.append(channelSelect, currencySelect, customerSelect, submitButton)
135
+
136
+ return filtersWrapper
137
+ }
138
+
139
+ createGraphWrapper() {
140
+ const wrapper = document.createElement('div')
141
+ wrapper.setAttribute('style', 'padding: 15px;')
142
+
143
+ return wrapper
144
+ }
145
+
146
+ createGraphFiller() {
147
+ const filler = document.createElement('div')
148
+ filler.setAttribute(
149
+ 'style',
150
+ 'border-radius: 7px; background-color: #eee; height: 350px; display: flex; ' +
151
+ 'justify-content: space-around; align-items: center; font-size: 4em;'
152
+ )
153
+
154
+ const chartIcon = document.createElement('i')
155
+ chartIcon.setAttribute('class', 'icon chart line')
156
+ filler.append(chartIcon)
157
+
158
+ return filler
159
+ }
160
+
161
+ addSelectOptions(select, choices, placeholder = null) {
162
+ if (placeholder !== null) {
163
+ const placeholderOption = document.createElement('option')
164
+ placeholderOption.innerHTML = placeholder
165
+ placeholderOption.disabled = true
166
+ placeholderOption.selected = true
167
+ select.append(placeholderOption)
168
+ }
169
+
170
+ for (const property in choices) {
171
+ const choice = choices[property]
172
+
173
+ const channelOption = document.createElement('option')
174
+ channelOption.innerHTML = choice.name
175
+ channelOption.setAttribute('value', choice.id)
176
+ select.append(channelOption)
177
+ }
178
+
179
+ return select
180
+ }
181
+
182
+ hookClientSearchOnSelect(selectElement, searchApiEndpoint) {
183
+ selectElement.setAttribute('class', `${selectElement.getAttribute('class')} search dropdown selection`)
184
+
185
+ $(selectElement).dropdown({
186
+ apiSettings: {
187
+ url: `${searchApiEndpoint}/{query}`,
188
+ cache: false,
189
+ },
190
+
191
+ minCharacters: 2,
192
+ })
193
+ }
194
+
195
+ isSubmitButtonDisabled(simulationData) {
196
+ return (
197
+ null === simulationData.productVariantId ||
198
+ null === simulationData.channelId ||
199
+ null === simulationData.currencyId ||
200
+ null === simulationData.customerId
201
+ )
202
+ }
203
+
204
+ serializeObjectToQuery(object) {
205
+ const query = []
206
+
207
+ for (const part in object) {
208
+ if (Object.prototype.hasOwnProperty.call(object, part)) {
209
+ query.push(`${encodeURIComponent(part)}=${encodeURIComponent(object[part])}`)
210
+ }
211
+ }
212
+
213
+ return query.join('&')
214
+ }
215
+
216
+ renderGraph(graphWrapper, graphData) {
217
+ graphWrapper.innerHTML = ''
218
+
219
+ const graphCanvas = document.createElement('canvas')
220
+ graphCanvas.width = 600
221
+ graphCanvas.height = 200
222
+
223
+ graphWrapper.append(graphCanvas)
224
+
225
+ const graphConfig = this.getGraphConfig(graphData)
226
+ console.log('graphConfig', graphConfig)
227
+ new Chart(graphCanvas.getContext('2d'), graphConfig)
228
+ }
229
+
230
+ /** @private */
231
+ getGraphConfig(rawGraphData) {
232
+ return {
233
+ type: 'line',
234
+ data: {
235
+ datasets: rawGraphData.datasets.map((rawDataset) => {
236
+ return {
237
+ label: rawDataset.label,
238
+ data: rawDataset.data,
239
+ borderColor: '#0000ff',
240
+ fill: false,
241
+ steppedLine: true,
242
+ pointRadius: 8,
243
+ pointHoverRadius: 10,
244
+ }
245
+ }),
246
+ },
247
+ options: {
248
+ maintainAspectRatio: true,
249
+ responsive: true,
250
+ scales: {
251
+ xAxes: [
252
+ {
253
+ type: 'linear',
254
+ position: 'bottom',
255
+ scaleLabel: {
256
+ display: true,
257
+ labelString: rawGraphData.xAxisLabel,
258
+ },
259
+ },
260
+ ],
261
+ yAxes: [
262
+ {
263
+ scaleLabel: {
264
+ display: true,
265
+ labelString: rawGraphData.yAxisLabel,
266
+ },
267
+ },
268
+ ],
269
+ },
270
+ },
271
+ }
272
+ }
273
+ }
package/src/index.js CHANGED
@@ -7,9 +7,12 @@ import 'sylius-bundle/AdminBundle/Resources/private/js/app'
7
7
  // Scripts - components
8
8
  import './components/taxonAttributes'
9
9
  import FeedCategorySelect from './components/feedCategorySelect'
10
+ import ProductVariantPricingGraph from './components/productVariantPricingGraph'
11
+ import CustomerGroupClientAssigner from './components/customerGroupClientAssigner'
10
12
  import TooltipHelpers from './components/tooltipHelpers'
11
13
  import AdminSidebarScroller from './components/adminSidebarScroller'
12
14
  import CustomerGroupingRuleConfiguration from './components/customerGroupingRuleConfiguration'
15
+ import ProductVariantPricingSimulation from './components/productVariantPricingSimulation'
13
16
 
14
17
  // Scripts - plugin
15
18
  import './plugins/ckeditor/index'
@@ -29,7 +32,6 @@ import '@oxyshop/admin/lib/style.css'
29
32
  // Images
30
33
  import '@oxyshop/admin/images/logo.png'
31
34
  import '@oxyshop/admin/images/admin-logo.svg'
32
- import CustomerGroupClientAssigner from './components/customerGroupClientAssigner'
33
35
 
34
36
  // Components initializations
35
37
  $(document).ready(() => {
@@ -44,20 +46,28 @@ $(document).ready(() => {
44
46
  */
45
47
  TooltipHelpers.init()
46
48
 
47
- const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select')
48
- feedCategorySelect.init()
49
-
50
49
  const customerGroupingRuleConfiguration = new CustomerGroupingRuleConfiguration(
51
50
  'select.ng-grouping-rule-select',
52
51
  '.ng-grouping-rule-configuration'
53
52
  )
54
53
  customerGroupingRuleConfiguration.init()
55
54
 
55
+ const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select')
56
+ feedCategorySelect.init()
57
+
56
58
  // Admin sidebar scroller
57
59
  const adminSidebarElement = document.getElementById('sidebar')
58
60
  const adminSidebarScroller = new AdminSidebarScroller(adminSidebarElement, 'a.item')
59
61
  adminSidebarScroller.scrollToActiveLink()
60
62
 
63
+ const productVariantPricingGraph = new ProductVariantPricingGraph('.ng-product-variant-pricing-graph')
64
+ productVariantPricingGraph.init()
65
+
66
+ const productVariantPricingSimulation = new ProductVariantPricingSimulation(
67
+ '.ng-product-variant-pricing-simulation'
68
+ )
69
+ productVariantPricingSimulation.init()
70
+
61
71
  // Client search select
62
72
  const clientSearchSelect = new CustomerGroupClientAssigner('.ng-customer-group-client-assigner')
63
73
  clientSearchSelect.init()