@oxyshop/admin 1.3.35 → 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 +494 -7
- package/package.json +1 -1
- package/scss/main.scss +4 -0
- package/src/components/customerGroupClientAssigner.js +62 -0
- package/src/components/customerGroupingRuleConfiguration.js +44 -0
- package/src/components/feedCategorySelect.js +8 -6
- package/src/components/productVariantPricingGraph.js +86 -0
- package/src/components/productVariantPricingSimulation.js +273 -0
- package/src/index.js +26 -2
package/lib/index.js
CHANGED
|
@@ -25,6 +25,9 @@ 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';
|
|
@@ -1884,11 +1887,13 @@ $.fn.extend({
|
|
|
1884
1887
|
},
|
|
1885
1888
|
});
|
|
1886
1889
|
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
+
class FeedCategorySelect {
|
|
1891
|
+
constructor(selector) {
|
|
1892
|
+
this.selectSelector = selector;
|
|
1893
|
+
}
|
|
1890
1894
|
|
|
1891
|
-
|
|
1895
|
+
init() {
|
|
1896
|
+
document.querySelectorAll(this.selectSelector).forEach((selectElement) => {
|
|
1892
1897
|
const sourceCode = selectElement.getAttribute('data-source-code');
|
|
1893
1898
|
|
|
1894
1899
|
$(selectElement).dropdown({
|
|
@@ -1899,8 +1904,154 @@ $.fn.extend({
|
|
|
1899
1904
|
minCharacters: 2,
|
|
1900
1905
|
});
|
|
1901
1906
|
});
|
|
1902
|
-
}
|
|
1903
|
-
}
|
|
1907
|
+
}
|
|
1908
|
+
}
|
|
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
|
+
}
|
|
1904
2055
|
|
|
1905
2056
|
class TooltipHelpers {
|
|
1906
2057
|
static init() {
|
|
@@ -1990,6 +2141,322 @@ class AdminSidebarScroller {
|
|
|
1990
2141
|
}
|
|
1991
2142
|
}
|
|
1992
2143
|
|
|
2144
|
+
class CustomerGroupingRuleConfiguration {
|
|
2145
|
+
/**
|
|
2146
|
+
* @param {string} groupingRuleSelectSelector
|
|
2147
|
+
* @param {string} formGroupSelector
|
|
2148
|
+
*/
|
|
2149
|
+
constructor(groupingRuleSelectSelector, formGroupSelector) {
|
|
2150
|
+
this.groupingRuleSelectSelector = groupingRuleSelectSelector;
|
|
2151
|
+
this.formGroupSelector = formGroupSelector;
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2154
|
+
init() {
|
|
2155
|
+
// Event listener on dynamic elements
|
|
2156
|
+
$(document).on('change', this.groupingRuleSelectSelector, (selectEvent) => {
|
|
2157
|
+
this.toggleRuleConfigurationSection(selectEvent.target);
|
|
2158
|
+
});
|
|
2159
|
+
|
|
2160
|
+
// Show all sections with selected value
|
|
2161
|
+
document.querySelectorAll(this.groupingRuleSelectSelector).forEach((selectElement) => {
|
|
2162
|
+
this.toggleRuleConfigurationSection(selectElement);
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
toggleRuleConfigurationSection(selectElement) {
|
|
2167
|
+
const selectedRuleCode = selectElement.value;
|
|
2168
|
+
const configurationGroup = selectElement.parentElement.parentElement;
|
|
2169
|
+
|
|
2170
|
+
configurationGroup.querySelectorAll(this.formGroupSelector).forEach((formGroup) => {
|
|
2171
|
+
const groupRuleCode = formGroup.getAttribute('data-grouping-rule-code');
|
|
2172
|
+
const toggleFunction = groupRuleCode === selectedRuleCode ? this.showElement : this.hideElement;
|
|
2173
|
+
|
|
2174
|
+
toggleFunction(formGroup);
|
|
2175
|
+
});
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
/** @private */
|
|
2179
|
+
hideElement(element) {
|
|
2180
|
+
element.classList.add('d-none');
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/** @private */
|
|
2184
|
+
showElement(element) {
|
|
2185
|
+
element.classList.remove('d-none');
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
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
|
+
|
|
1993
2460
|
/**
|
|
1994
2461
|
* CKEditor config
|
|
1995
2462
|
*/
|
|
@@ -2288,7 +2755,6 @@ vendorDescriptionElements.forEach((element) => {
|
|
|
2288
2755
|
// Components initializations
|
|
2289
2756
|
$(document).ready(() => {
|
|
2290
2757
|
$('.ng-taxon-attr-dropdown').taxonAttributes();
|
|
2291
|
-
$('.ng-feed-category-select').feedCategorySelect();
|
|
2292
2758
|
|
|
2293
2759
|
$(document).previewUploadedImage('#sylius_payment_method_images');
|
|
2294
2760
|
$(document).previewUploadedImage('#sylius_shipping_method_images');
|
|
@@ -2299,8 +2765,29 @@ $(document).ready(() => {
|
|
|
2299
2765
|
*/
|
|
2300
2766
|
TooltipHelpers.init();
|
|
2301
2767
|
|
|
2768
|
+
const customerGroupingRuleConfiguration = new CustomerGroupingRuleConfiguration(
|
|
2769
|
+
'select.ng-grouping-rule-select',
|
|
2770
|
+
'.ng-grouping-rule-configuration'
|
|
2771
|
+
);
|
|
2772
|
+
customerGroupingRuleConfiguration.init();
|
|
2773
|
+
|
|
2774
|
+
const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select');
|
|
2775
|
+
feedCategorySelect.init();
|
|
2776
|
+
|
|
2302
2777
|
// Admin sidebar scroller
|
|
2303
2778
|
const adminSidebarElement = document.getElementById('sidebar');
|
|
2304
2779
|
const adminSidebarScroller = new AdminSidebarScroller(adminSidebarElement, 'a.item');
|
|
2305
2780
|
adminSidebarScroller.scrollToActiveLink();
|
|
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
|
+
|
|
2790
|
+
// Client search select
|
|
2791
|
+
const clientSearchSelect = new CustomerGroupClientAssigner('.ng-customer-group-client-assigner');
|
|
2792
|
+
clientSearchSelect.init();
|
|
2306
2793
|
});
|
package/package.json
CHANGED
package/scss/main.scss
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { axios } from '@oxyshop/shop/lib/plugins/Axios'
|
|
2
|
+
|
|
3
|
+
export default class CustomerGroupClientAssigner {
|
|
4
|
+
constructor(selector, searchSelector = '.ng-client-search-select', submitSelector = '.ng-client-assign-submit') {
|
|
5
|
+
this.selector = selector
|
|
6
|
+
this.searchSelector = searchSelector
|
|
7
|
+
this.submitSelector = submitSelector
|
|
8
|
+
|
|
9
|
+
this.containerElement = null
|
|
10
|
+
this.searchElement = null
|
|
11
|
+
this.submitElement = null
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
init() {
|
|
15
|
+
this.containerElement = document.querySelector(this.selector)
|
|
16
|
+
if (null === this.containerElement) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const submitApiEndpoint = this.containerElement.getAttribute('data-assign-customers-url')
|
|
21
|
+
const redirectAfterUrl = this.containerElement.getAttribute('data-redirect-after-url')
|
|
22
|
+
|
|
23
|
+
this.searchElement = this.containerElement.querySelector(this.searchSelector)
|
|
24
|
+
const searchApiEndpoint = this.searchElement.getAttribute('data-customer-search-url')
|
|
25
|
+
|
|
26
|
+
$(this.searchElement).dropdown({
|
|
27
|
+
apiSettings: {
|
|
28
|
+
url: `${searchApiEndpoint}/{query}`,
|
|
29
|
+
cache: false,
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
minCharacters: 2,
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
this.submitElement = this.containerElement.querySelector(this.submitSelector)
|
|
36
|
+
this.submitElement.addEventListener('click', () => {
|
|
37
|
+
const selectedCustomersIds = this.getSelectValues(this.searchElement)
|
|
38
|
+
this.submitCustomers(submitApiEndpoint, selectedCustomersIds, redirectAfterUrl)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
getSelectValues(selectElement) {
|
|
43
|
+
const selected = selectElement.querySelectorAll('option:checked')
|
|
44
|
+
return Array.from(selected).map((optionElement) => optionElement.value)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
submitCustomers(apiEndpoint, selectedIds, redirectTo) {
|
|
48
|
+
this.submitElement.disabled = true
|
|
49
|
+
|
|
50
|
+
axios
|
|
51
|
+
.post(apiEndpoint, { customers: selectedIds })
|
|
52
|
+
.then(() => {
|
|
53
|
+
window.location = redirectTo
|
|
54
|
+
})
|
|
55
|
+
.catch((error) => {
|
|
56
|
+
console.error(error)
|
|
57
|
+
})
|
|
58
|
+
.finally(() => {
|
|
59
|
+
this.submitElement.disabled = false
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export default class CustomerGroupingRuleConfiguration {
|
|
2
|
+
/**
|
|
3
|
+
* @param {string} groupingRuleSelectSelector
|
|
4
|
+
* @param {string} formGroupSelector
|
|
5
|
+
*/
|
|
6
|
+
constructor(groupingRuleSelectSelector, formGroupSelector) {
|
|
7
|
+
this.groupingRuleSelectSelector = groupingRuleSelectSelector
|
|
8
|
+
this.formGroupSelector = formGroupSelector
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
init() {
|
|
12
|
+
// Event listener on dynamic elements
|
|
13
|
+
$(document).on('change', this.groupingRuleSelectSelector, (selectEvent) => {
|
|
14
|
+
this.toggleRuleConfigurationSection(selectEvent.target)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
// Show all sections with selected value
|
|
18
|
+
document.querySelectorAll(this.groupingRuleSelectSelector).forEach((selectElement) => {
|
|
19
|
+
this.toggleRuleConfigurationSection(selectElement)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
toggleRuleConfigurationSection(selectElement) {
|
|
24
|
+
const selectedRuleCode = selectElement.value
|
|
25
|
+
const configurationGroup = selectElement.parentElement.parentElement
|
|
26
|
+
|
|
27
|
+
configurationGroup.querySelectorAll(this.formGroupSelector).forEach((formGroup) => {
|
|
28
|
+
const groupRuleCode = formGroup.getAttribute('data-grouping-rule-code')
|
|
29
|
+
const toggleFunction = groupRuleCode === selectedRuleCode ? this.showElement : this.hideElement
|
|
30
|
+
|
|
31
|
+
toggleFunction(formGroup)
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @private */
|
|
36
|
+
hideElement(element) {
|
|
37
|
+
element.classList.add('d-none')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** @private */
|
|
41
|
+
showElement(element) {
|
|
42
|
+
element.classList.remove('d-none')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export default class FeedCategorySelect {
|
|
2
|
+
constructor(selector) {
|
|
3
|
+
this.selectSelector = selector
|
|
4
|
+
}
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
init() {
|
|
7
|
+
document.querySelectorAll(this.selectSelector).forEach((selectElement) => {
|
|
6
8
|
const sourceCode = selectElement.getAttribute('data-source-code')
|
|
7
9
|
|
|
8
10
|
$(selectElement).dropdown({
|
|
@@ -13,5 +15,5 @@ $.fn.extend({
|
|
|
13
15
|
minCharacters: 2,
|
|
14
16
|
})
|
|
15
17
|
})
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -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
|
@@ -6,9 +6,13 @@ import 'sylius-bundle/AdminBundle/Resources/private/js/app'
|
|
|
6
6
|
|
|
7
7
|
// Scripts - components
|
|
8
8
|
import './components/taxonAttributes'
|
|
9
|
-
import './components/feedCategorySelect'
|
|
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'
|
|
14
|
+
import CustomerGroupingRuleConfiguration from './components/customerGroupingRuleConfiguration'
|
|
15
|
+
import ProductVariantPricingSimulation from './components/productVariantPricingSimulation'
|
|
12
16
|
|
|
13
17
|
// Scripts - plugin
|
|
14
18
|
import './plugins/ckeditor/index'
|
|
@@ -32,7 +36,6 @@ import '@oxyshop/admin/images/admin-logo.svg'
|
|
|
32
36
|
// Components initializations
|
|
33
37
|
$(document).ready(() => {
|
|
34
38
|
$('.ng-taxon-attr-dropdown').taxonAttributes()
|
|
35
|
-
$('.ng-feed-category-select').feedCategorySelect()
|
|
36
39
|
|
|
37
40
|
$(document).previewUploadedImage('#sylius_payment_method_images')
|
|
38
41
|
$(document).previewUploadedImage('#sylius_shipping_method_images')
|
|
@@ -43,8 +46,29 @@ $(document).ready(() => {
|
|
|
43
46
|
*/
|
|
44
47
|
TooltipHelpers.init()
|
|
45
48
|
|
|
49
|
+
const customerGroupingRuleConfiguration = new CustomerGroupingRuleConfiguration(
|
|
50
|
+
'select.ng-grouping-rule-select',
|
|
51
|
+
'.ng-grouping-rule-configuration'
|
|
52
|
+
)
|
|
53
|
+
customerGroupingRuleConfiguration.init()
|
|
54
|
+
|
|
55
|
+
const feedCategorySelect = new FeedCategorySelect('.ng-feed-category-select')
|
|
56
|
+
feedCategorySelect.init()
|
|
57
|
+
|
|
46
58
|
// Admin sidebar scroller
|
|
47
59
|
const adminSidebarElement = document.getElementById('sidebar')
|
|
48
60
|
const adminSidebarScroller = new AdminSidebarScroller(adminSidebarElement, 'a.item')
|
|
49
61
|
adminSidebarScroller.scrollToActiveLink()
|
|
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
|
+
|
|
71
|
+
// Client search select
|
|
72
|
+
const clientSearchSelect = new CustomerGroupClientAssigner('.ng-customer-group-client-assigner')
|
|
73
|
+
clientSearchSelect.init()
|
|
50
74
|
})
|