Skip to content

Commit c312465

Browse files
authored
SSP-1450 Attach Model to ProductLists and Model to Assets. (#11763)
SSP-1450 Product List and Asset attachment to Model in BO.
1 parent 43c162b commit c312465

File tree

7 files changed

+179
-17
lines changed

7 files changed

+179
-17
lines changed

assets/Zed/js/modules/libs/table/selectable-table.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export class SelectableTable {
2020
remove: 'js-remove-item',
2121
counter: 'js-counter',
2222
selected: 'has-selected',
23+
checkboxInitialized: 'checkbox-initialized',
2324
};
2425

2526
constructor(data) {
@@ -71,7 +72,13 @@ export class SelectableTable {
7172
this.tableActions();
7273
}
7374

74-
setPredefinedData() {
75+
selectRowsByData(data) {
76+
this.setPredefinedData(data);
77+
this.tableActions();
78+
this.updateCheckboxes();
79+
}
80+
81+
setPredefinedData(_data) {
7582
const cols = this.data.api.columns().header().toArray();
7683

7784
this.colIndexes = {
@@ -81,7 +88,16 @@ export class SelectableTable {
8188

8289
const store = JSON.parse(localStorage.getItem(this.getStorageKey()) || '[]');
8390
const input = document.querySelector(this.data.config.inputSelector);
84-
const data = JSON.parse((input.value || '[]').replace(/"/g, '"').replace(/,/g, ''));
91+
const data = (_data ?? JSON.parse((input.value || '[]').replace(/"/g, '"').replace(/,/g, ''))).map(
92+
(_item) => {
93+
const item = [..._item];
94+
95+
item.splice(Math.min(this.colIndexes.selection, item.length), 0, '');
96+
97+
return item;
98+
},
99+
);
100+
85101
const merged = [...store, ...data];
86102
const unique = merged.filter(
87103
(item, index, self) =>
@@ -119,6 +135,10 @@ export class SelectableTable {
119135
this.addRow(rowData, true);
120136
}
121137

138+
if (checkbox.hasAttribute(this.staticClasses.checkboxInitialized)) continue;
139+
140+
checkbox.setAttribute(this.staticClasses.checkboxInitialized, true);
141+
122142
checkbox.addEventListener('change', () => {
123143
checkbox.checked ? this.addRow(rowData) : this.removeRow(id);
124144
});
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { Table } from './table';
2+
3+
export const EVENT_DATA_TABLE_FILE_LOADED = 'EVENT_DATA_TABLE_FILE_LOADED';
4+
5+
/**
6+
* @typedef {Object} config
7+
* @param {string} url - The URL to upload the file to.
8+
* @param {string} path - (Optional) Key in the response object used to extract nested data.
9+
* @param {string} inputSelector - (Optional) Selector to the input element (default .js-table-file-uploader).
10+
*/
11+
export class TableFileUploader {
12+
uploader = null;
13+
tableIds = null;
14+
15+
staticClasses = {
16+
ajaxInitialized: 'ajax-initialized',
17+
isLoading: 'is-loading',
18+
};
19+
20+
constructor(data) {
21+
this.data = data;
22+
23+
this.init();
24+
}
25+
26+
init() {
27+
this.setDefaults();
28+
29+
const selectors = document.querySelectorAll(this.data.config.inputSelector);
30+
31+
this.uploader = selectors.length === 1 ? selectors[0] : this.findClosestInput();
32+
33+
if (!this.uploader) return;
34+
35+
this.tableIds = Array.from(document.querySelectorAll(`table[id][${Table.FEATURES.uploader.attribute}]`))
36+
.filter(
37+
(table) =>
38+
JSON.parse(table.getAttribute(Table.FEATURES.uploader.attribute)).url === this.data.config.url,
39+
)
40+
.map((table) => table.id);
41+
42+
this.initializeAjax();
43+
this.initializeUploadEvent();
44+
}
45+
46+
setDefaults() {
47+
this.data.config.inputSelector ??= '.js-table-file-uploader';
48+
}
49+
50+
initializeUploadEvent() {
51+
this.uploader.addEventListener(EVENT_DATA_TABLE_FILE_LOADED, (event) => {
52+
if (!event.detail.tableIds.includes(this.data.tableId)) {
53+
return;
54+
}
55+
56+
const table = this.data.tables.get(this.data.tableId);
57+
const data = this.data.config.path ? event.detail.data[this.data.config.path] : event.detail.data;
58+
59+
if (data?.length) {
60+
table.features.selectable.selectRowsByData(data);
61+
}
62+
});
63+
}
64+
65+
initializeAjax() {
66+
if (this.uploader.hasAttribute(this.staticClasses.ajaxInitialized)) return;
67+
68+
this.uploader.setAttribute(this.staticClasses.ajaxInitialized, true);
69+
this.uploader.addEventListener('change', this.fetchData.bind(this));
70+
}
71+
72+
async fetchData(event) {
73+
const target = event.target;
74+
75+
if (!target.value) return;
76+
77+
const file = target.files[0];
78+
target.classList.add(this.staticClasses.isLoading);
79+
80+
try {
81+
const formData = new FormData();
82+
formData.append('file', file, file.name);
83+
84+
const response = await (
85+
await fetch(this.data.config.url, {
86+
method: 'POST',
87+
body: formData,
88+
})
89+
).json();
90+
91+
this.uploader.dispatchEvent(
92+
new CustomEvent(EVENT_DATA_TABLE_FILE_LOADED, {
93+
detail: {
94+
tableIds: this.tableIds,
95+
data: response.data,
96+
},
97+
}),
98+
);
99+
} catch (error) {
100+
// eslint-disable-next-line no-console
101+
console.error('Upload failed:', error);
102+
} finally {
103+
target.classList.remove(this.staticClasses.isLoading);
104+
target.value = '';
105+
}
106+
}
107+
108+
findClosestInput() {
109+
let current = this.data.table;
110+
111+
while (current) {
112+
const fileInput = current.querySelector(this.data.config.inputSelector);
113+
114+
if (fileInput) return fileInput;
115+
116+
current = current.parentElement;
117+
}
118+
119+
return null;
120+
}
121+
}

assets/Zed/js/modules/libs/table/table.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SelectableTable } from './selectable-table';
22
import { FilterableTable } from './filterable-table';
3+
import { TableFileUploader } from './table-file-uploader';
34

45
function getTranslations() {
56
const localeEl = document.documentElement.dataset.applicationLocale;
@@ -25,6 +26,10 @@ export class Table {
2526
class: FilterableTable,
2627
attribute: 'data-filterable',
2728
},
29+
uploader: {
30+
attribute: 'data-uploader',
31+
class: TableFileUploader,
32+
},
2833
};
2934

3035
static #defaultOptions = {
@@ -49,6 +54,8 @@ export class Table {
4954
},
5055
};
5156

57+
tables = new Map();
58+
5259
constructor(options = {}) {
5360
this.options = { ...Table.#defaultOptions, ...options };
5461
this.options.config.debounce ??= 1000;
@@ -80,25 +87,35 @@ export class Table {
8087
}
8188

8289
initFeatures(data) {
83-
for (const feature of Object.values(Table.FEATURES)) {
90+
const features = {};
91+
92+
for (const [name, feature] of Object.entries(Table.FEATURES)) {
8493
const { attribute, class: FeatureClass } = feature;
8594

8695
if (!data.table.hasAttribute(attribute) && attribute) {
8796
continue;
8897
}
8998

99+
const params = {
100+
...data,
101+
features,
102+
config: JSON.parse(data.table.getAttribute(attribute) || '{}'),
103+
tables: this.tables,
104+
};
105+
106+
this.tables.set(data.tableId, params);
107+
90108
/**
91109
* @param {Object} element - Table element.
92110
* @param {Object} api - Table API instance.
93111
* @param {Object} config - Config for feature.
112+
* @param {Object} features - Instances of features on current table.
94113
* @param {string} tableId - Table ID.
95114
* @param {Object} options - Table options.
115+
* @param {Object} tables - Map of initialized tables.
96116
* @param {Object} translations - List of translations for current locale.
97117
*/
98-
new FeatureClass({
99-
...data,
100-
config: JSON.parse(data.table.getAttribute(attribute) || '{}'),
101-
});
118+
features[name] = new FeatureClass(params);
102119
}
103120
}
104121

assets/Zed/sass/_animations.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@keyframes spin-to {
2+
from {
3+
transform: rotate(0deg);
4+
}
5+
6+
to {
7+
transform: rotate(360deg);
8+
}
9+
}

assets/Zed/sass/_custom.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2082,6 +2082,10 @@ form .horizontal-form {
20822082
}
20832083
}
20842084

2085+
.spinner-icon {
2086+
animation: spin-to 1s linear infinite;
2087+
}
2088+
20852089
// [START] Drop with bootstrap 4
20862090
.d-inline {
20872091
display: inline;

assets/Zed/sass/_image-uploader.scss

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,3 @@
117117
margin-bottom: 0;
118118
}
119119
}
120-
121-
@keyframes spin-to {
122-
from {
123-
transform: rotate(0deg);
124-
}
125-
126-
to {
127-
transform: rotate(360deg);
128-
}
129-
}

assets/Zed/sass/main.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ $icon-font-path: '../../fonts/bootstrap/';
2828
// spryker customization
2929
@import 'variables';
3030
@import 'mixins';
31+
@import 'animations';
3132
@import 'inspinia';
3233
@import 'action-buttons';
3334
@import 'custom';

0 commit comments

Comments
 (0)