@osimatic/helpers-js 1.5.2 → 1.5.4
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/count_down.js +46 -48
- package/date_time.js +0 -1
- package/details_sub_array.js +65 -50
- package/flash_message.js +10 -6
- package/form_date.js +144 -153
- package/form_helper.js +283 -232
- package/google_charts.js +154 -144
- package/google_maps.js +1 -1
- package/import_from_csv.js +198 -160
- package/multi_files_input.js +44 -35
- package/multiple_action_in_table.js +123 -109
- package/package.json +1 -1
- package/paging.js +103 -84
- package/select_all.js +65 -70
- package/sortable_list.js +12 -13
- package/tests/count_down.test.js +131 -352
- package/tests/details_sub_array.test.js +213 -258
- package/tests/flash_message.test.js +21 -153
- package/tests/form_date.test.js +287 -961
- package/tests/form_helper.test.js +553 -673
- package/tests/google_charts.test.js +338 -339
- package/tests/google_maps.test.js +3 -15
- package/tests/import_from_csv.test.js +421 -640
- package/tests/multi_files_input.test.js +305 -737
- package/tests/multiple_action_in_table.test.js +442 -429
- package/tests/open_street_map.test.js +15 -23
- package/tests/paging.test.js +344 -475
- package/tests/select_all.test.js +232 -318
- package/tests/sortable_list.test.js +176 -500
- package/tests/user.test.js +163 -54
- package/user.js +35 -38
package/import_from_csv.js
CHANGED
|
@@ -1,239 +1,268 @@
|
|
|
1
|
+
require('./string');
|
|
1
2
|
|
|
2
3
|
class ImportFromCsv {
|
|
3
4
|
|
|
4
|
-
static
|
|
5
|
-
|
|
5
|
+
static _defaults = {
|
|
6
|
+
errorMessageFileNotValid: 'Le fichier sélectionné n\'est pas un fichier CSV valide.',
|
|
7
|
+
errorMessageFileEmpty: 'Veuillez indiquer le fichier CSV à importer.',
|
|
8
|
+
errorMessageImportSelectColumns: 'Veuillez sélectionner les colonnes à importer.',
|
|
9
|
+
selectDefaultOptionLabel: 'Sélectionnez la colonne\u2026',
|
|
10
|
+
lineLabel: 'Ligne {0} :',
|
|
11
|
+
errorMessageImportFailed: 'L\'importation a échouée :',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
static setDefault(options) {
|
|
15
|
+
ImportFromCsv._defaults = { ...ImportFromCsv._defaults, ...options };
|
|
16
|
+
}
|
|
6
17
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
static initForm(div, options = {}) {
|
|
19
|
+
const {
|
|
20
|
+
importColumns,
|
|
21
|
+
requestImportData,
|
|
22
|
+
specificDescDiv,
|
|
23
|
+
additionalFormField,
|
|
24
|
+
errorMessageFileNotValid,
|
|
25
|
+
errorMessageFileEmpty,
|
|
26
|
+
errorMessageImportSelectColumns,
|
|
27
|
+
selectDefaultOptionLabel,
|
|
28
|
+
lineLabel,
|
|
29
|
+
errorMessageImportFailed,
|
|
30
|
+
} = { ...ImportFromCsv._defaults, ...options };
|
|
31
|
+
|
|
32
|
+
const template = document.querySelector('.import_form_base');
|
|
33
|
+
if (!template) return;
|
|
34
|
+
const clone = template.cloneNode(true);
|
|
35
|
+
clone.classList.remove('import_form_base', 'hide');
|
|
36
|
+
div.innerHTML = '';
|
|
37
|
+
div.appendChild(clone);
|
|
38
|
+
|
|
39
|
+
const formUpload = div.querySelector('.form_upload');
|
|
40
|
+
const formMatching = div.querySelector('.form_matching');
|
|
41
|
+
const divResult = div.querySelector('.csv_result');
|
|
10
42
|
|
|
11
43
|
function resetUi() {
|
|
12
|
-
formMatching.
|
|
13
|
-
formUpload.
|
|
14
|
-
formUpload.
|
|
44
|
+
formMatching.classList.add('hide');
|
|
45
|
+
formUpload.classList.remove('hide');
|
|
46
|
+
formUpload.querySelector('div.errors')?.classList.add('hide');
|
|
15
47
|
}
|
|
16
48
|
|
|
17
|
-
if (
|
|
18
|
-
div.
|
|
49
|
+
if (specificDescDiv != null) {
|
|
50
|
+
div.querySelector('.specific_desc')?.append(specificDescDiv);
|
|
19
51
|
}
|
|
20
52
|
|
|
21
|
-
if (
|
|
22
|
-
div.
|
|
53
|
+
if (additionalFormField != null) {
|
|
54
|
+
div.querySelector('.import_matching_select_content')?.insertAdjacentHTML('afterend', additionalFormField);
|
|
23
55
|
}
|
|
24
56
|
|
|
25
|
-
formUpload.
|
|
57
|
+
const submitUploadBtn = formUpload.querySelector('button[type="submit"]');
|
|
58
|
+
submitUploadBtn.addEventListener('click', function(event) {
|
|
26
59
|
event.preventDefault();
|
|
27
|
-
FormHelper.buttonLoader(
|
|
28
|
-
formUpload.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
$('#form_import_upload div.errors').html(errorMessageFileNotValid).removeClass('hide');
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
let parsedImportList = results.data;
|
|
53
|
-
let header = hasHeader?results.meta.fields:results.data[0];
|
|
54
|
-
|
|
55
|
-
ImportFromCsv.displayData(divResult, parsedImportList, (hasHeader?header:null), formMatching);
|
|
56
|
-
ImportFromCsv.displayFormMatching(formMatching, importColumns, header, hasHeader);
|
|
57
|
-
|
|
58
|
-
formUpload.addClass('hide');
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
before: function(file, inputElem) {
|
|
62
|
-
},
|
|
63
|
-
error: function(err, file, inputElem, reason) {
|
|
64
|
-
isFileParsed = true;
|
|
65
|
-
formUpload.find('div.errors').html(errorMessageFileNotValid).removeClass('hide');
|
|
66
|
-
console.error(err, file, reason);
|
|
60
|
+
FormHelper.buttonLoader(this, 'loading');
|
|
61
|
+
formUpload.querySelector('div.errors')?.classList.add('hide');
|
|
62
|
+
|
|
63
|
+
const fileInput = formUpload.querySelector('input[type="file"]');
|
|
64
|
+
if (!fileInput.files || !fileInput.files.length) {
|
|
65
|
+
const errDiv = formUpload.querySelector('div.errors');
|
|
66
|
+
if (errDiv) { errDiv.innerHTML = errorMessageFileEmpty; errDiv.classList.remove('hide'); }
|
|
67
|
+
FormHelper.buttonLoader(submitUploadBtn, 'reset');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const hasHeader = formUpload.querySelectorAll('input[name="header"][value="1"]:checked').length;
|
|
72
|
+
const encoding = formUpload.querySelector('select[name="encoding"]').value;
|
|
73
|
+
|
|
74
|
+
Papa.parse(fileInput.files[0], {
|
|
75
|
+
header: hasHeader,
|
|
76
|
+
encoding: encoding,
|
|
77
|
+
dynamicTyping: false,
|
|
78
|
+
skipEmptyLines: true,
|
|
79
|
+
beforeFirstChunk: function(chunk) {
|
|
80
|
+
return chunk.trim();
|
|
67
81
|
},
|
|
68
|
-
complete: function() {
|
|
69
|
-
if (
|
|
70
|
-
|
|
82
|
+
complete: function(results, file) {
|
|
83
|
+
if (false === CSV.checkFile(file.name, file.type)) {
|
|
84
|
+
const errDiv = document.querySelector('#form_import_upload div.errors');
|
|
85
|
+
if (errDiv) { errDiv.innerHTML = errorMessageFileNotValid; errDiv.classList.remove('hide'); }
|
|
86
|
+
FormHelper.buttonLoader(submitUploadBtn, 'reset');
|
|
87
|
+
return;
|
|
71
88
|
}
|
|
72
|
-
|
|
89
|
+
|
|
90
|
+
const parsedImportList = results.data;
|
|
91
|
+
const header = hasHeader ? results.meta.fields : results.data[0];
|
|
92
|
+
|
|
93
|
+
ImportFromCsv.displayData(divResult, parsedImportList, (hasHeader ? header : null), formMatching);
|
|
94
|
+
ImportFromCsv.displayFormMatching(formMatching, importColumns, header, hasHeader, selectDefaultOptionLabel);
|
|
95
|
+
|
|
96
|
+
formUpload.classList.add('hide');
|
|
97
|
+
FormHelper.buttonLoader(submitUploadBtn, 'reset');
|
|
98
|
+
},
|
|
99
|
+
error: function(err, file) {
|
|
100
|
+
const errDiv = formUpload.querySelector('div.errors');
|
|
101
|
+
if (errDiv) { errDiv.innerHTML = errorMessageFileNotValid; errDiv.classList.remove('hide'); }
|
|
102
|
+
console.error(err, file);
|
|
103
|
+
FormHelper.buttonLoader(submitUploadBtn, 'reset');
|
|
73
104
|
}
|
|
74
105
|
});
|
|
75
|
-
event.preventDefault();
|
|
76
106
|
});
|
|
77
107
|
|
|
78
|
-
formMatching.
|
|
108
|
+
const submitMatchingBtn = formMatching.querySelector('button[type="submit"]');
|
|
109
|
+
submitMatchingBtn.addEventListener('click', function(event) {
|
|
79
110
|
event.preventDefault();
|
|
80
|
-
FormHelper.buttonLoader(
|
|
81
|
-
formMatching.
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
FormHelper.buttonLoader(
|
|
90
|
-
return
|
|
111
|
+
FormHelper.buttonLoader(this, 'loading');
|
|
112
|
+
const errDiv = formMatching.querySelector('div.errors');
|
|
113
|
+
if (errDiv) { errDiv.classList.add('hide'); errDiv.innerHTML = ''; }
|
|
114
|
+
divResult.querySelectorAll('table tr').forEach(tr => tr.classList.remove('danger'));
|
|
115
|
+
|
|
116
|
+
const tabLink = ImportFromCsv.getTabLink(formMatching);
|
|
117
|
+
|
|
118
|
+
if (Object.keys(tabLink).length === 0) {
|
|
119
|
+
if (errDiv) { errDiv.innerHTML = errorMessageImportSelectColumns; errDiv.classList.remove('hide'); }
|
|
120
|
+
FormHelper.buttonLoader(this, 'reset');
|
|
121
|
+
return;
|
|
91
122
|
}
|
|
92
123
|
|
|
93
|
-
|
|
94
|
-
//console.log('dataToImport', dataToImport);
|
|
124
|
+
const dataToImport = ImportFromCsv.getDataToImport(divResult, tabLink);
|
|
95
125
|
|
|
96
126
|
requestImportData(dataToImport,
|
|
97
|
-
// fonction callback en cas d'erreur de formulaire
|
|
98
127
|
(json) => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
128
|
+
if (errDiv) {
|
|
129
|
+
errDiv.innerHTML = typeof json['import_list'] !== 'undefined'
|
|
130
|
+
? json['import_list']
|
|
131
|
+
: ImportFromCsv.getErrorsHtmlOfImportData(json, divResult, errorMessageImportFailed, lineLabel);
|
|
132
|
+
errDiv.classList.remove('hide');
|
|
102
133
|
}
|
|
103
|
-
|
|
104
|
-
formMatching.find('div.errors').html(ImportFromCsv.getErrorsHtmlOfImportData(json, divResult)).removeClass('hide');
|
|
105
|
-
}
|
|
106
|
-
FormHelper.buttonLoader(formMatching.find('button[type="submit"]'), 'reset');
|
|
134
|
+
FormHelper.buttonLoader(formMatching.querySelector('button[type="submit"]'), 'reset');
|
|
107
135
|
}
|
|
108
136
|
);
|
|
109
137
|
});
|
|
110
138
|
|
|
111
|
-
formMatching.
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
const cancelLink = formMatching.querySelector('a.cancel_link');
|
|
140
|
+
if (cancelLink) {
|
|
141
|
+
cancelLink.addEventListener('click', (event) => {
|
|
142
|
+
event.preventDefault();
|
|
143
|
+
resetUi();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
114
146
|
|
|
115
147
|
resetUi();
|
|
116
148
|
}
|
|
117
149
|
|
|
118
150
|
static getDataToImport(divResult, tabLink) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (!$(line).find('input.import_line_checkbox:checked').length) {
|
|
123
|
-
// if (!divResult.find('table tr[data-line="'+(index+1)+'"] input.import_line_checkbox:checked').length) {
|
|
151
|
+
const importListWithFieldNames = [];
|
|
152
|
+
divResult.querySelectorAll('table tbody tr').forEach((line, index) => {
|
|
153
|
+
if (!line.querySelectorAll('input.import_line_checkbox:checked').length) {
|
|
124
154
|
return;
|
|
125
155
|
}
|
|
126
156
|
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
const lineData = { line: (index + 1) };
|
|
158
|
+
Object.entries(tabLink).forEach(([key, listeImportIndex]) => {
|
|
129
159
|
if (listeImportIndex != -1) {
|
|
130
|
-
|
|
131
|
-
if (td
|
|
132
|
-
lineData[key] = td.
|
|
160
|
+
const td = line.querySelector('td[data-key="' + listeImportIndex + '"]');
|
|
161
|
+
if (td) {
|
|
162
|
+
lineData[key] = td.textContent;
|
|
133
163
|
}
|
|
134
164
|
}
|
|
135
165
|
});
|
|
136
|
-
//console.log('lineData', lineData);
|
|
137
166
|
importListWithFieldNames.push(lineData);
|
|
138
167
|
});
|
|
139
168
|
return importListWithFieldNames;
|
|
140
169
|
}
|
|
141
170
|
|
|
142
171
|
static displayData(divResult, data, header, formMatching) {
|
|
143
|
-
let table = divResult.
|
|
144
|
-
if (table
|
|
145
|
-
divResult.
|
|
146
|
-
table = divResult.
|
|
172
|
+
let table = divResult.querySelector('table');
|
|
173
|
+
if (!table) {
|
|
174
|
+
divResult.insertAdjacentHTML('beforeend', '<table class="table table-sm table-bordered"></table>');
|
|
175
|
+
table = divResult.querySelector('table');
|
|
147
176
|
}
|
|
148
|
-
table.
|
|
177
|
+
table.innerHTML = '';
|
|
149
178
|
|
|
150
179
|
let tableContent = '';
|
|
151
180
|
if (null !== header) {
|
|
152
181
|
tableContent += '<thead><tr>';
|
|
153
|
-
//tableContent += '<th><input type="checkbox" class="import_line_select_all" /></th>';
|
|
154
182
|
tableContent += '<th></th>';
|
|
155
|
-
|
|
156
|
-
tableContent += '<th>'+value+'</th>';
|
|
183
|
+
header.forEach((value) => {
|
|
184
|
+
tableContent += '<th>' + value + '</th>';
|
|
157
185
|
});
|
|
158
186
|
tableContent += '<th></th>';
|
|
159
187
|
tableContent += '</tr></thead>';
|
|
160
188
|
}
|
|
161
189
|
|
|
162
190
|
tableContent += '<tbody>';
|
|
163
|
-
|
|
164
|
-
tableContent += '<tr data-line="'+(index+1)+'">';
|
|
165
|
-
tableContent += '<td class="text-bold text-end select_line_checkbox"><input type="checkbox" class="import_line_checkbox pull-left" checked="checked" /> '+(index+1)+'.</td>';
|
|
166
|
-
|
|
167
|
-
|
|
191
|
+
data.forEach((line, index) => {
|
|
192
|
+
tableContent += '<tr data-line="' + (index + 1) + '">';
|
|
193
|
+
tableContent += '<td class="text-bold text-end select_line_checkbox"><input type="checkbox" class="import_line_checkbox pull-left" checked="checked" /> ' + (index + 1) + '.</td>';
|
|
194
|
+
const entries = Array.isArray(line) ? line.map((v, i) => [i, v]) : Object.entries(line);
|
|
195
|
+
entries.forEach(([key, value]) => {
|
|
196
|
+
tableContent += '<td data-key="' + key + '">' + (value !== null ? value : '') + '</td>';
|
|
168
197
|
});
|
|
169
198
|
tableContent += '<td class="text-center edit_line_button"></td>';
|
|
170
|
-
tableContent +='</tr>';
|
|
199
|
+
tableContent += '</tr>';
|
|
171
200
|
});
|
|
172
201
|
tableContent += '</tbody>';
|
|
173
202
|
|
|
174
|
-
table.
|
|
203
|
+
table.innerHTML = tableContent;
|
|
175
204
|
|
|
176
|
-
table.
|
|
177
|
-
ImportFromCsv.initEditLink(formMatching,
|
|
205
|
+
table.querySelectorAll('td.edit_line_button').forEach(el => {
|
|
206
|
+
ImportFromCsv.initEditLink(formMatching, el);
|
|
178
207
|
});
|
|
179
208
|
|
|
180
|
-
divResult.
|
|
209
|
+
divResult.classList.remove('hide');
|
|
181
210
|
}
|
|
182
211
|
|
|
183
212
|
static initValidateLine(formMatching, td) {
|
|
184
|
-
td.
|
|
185
|
-
td.
|
|
186
|
-
|
|
187
|
-
tr.
|
|
188
|
-
|
|
189
|
-
if (
|
|
213
|
+
td.innerHTML = '<a href="#" class="import_validate_line text-success"><i class="fas fa-check"></i></a>';
|
|
214
|
+
td.querySelector('a.import_validate_line').addEventListener('click', function(e) {
|
|
215
|
+
e.preventDefault();
|
|
216
|
+
const tr = this.closest('tr');
|
|
217
|
+
tr.querySelectorAll('td').forEach((cell) => {
|
|
218
|
+
if (cell.classList.contains('select_line_checkbox') || cell.classList.contains('edit_line_button')) {
|
|
190
219
|
return;
|
|
191
220
|
}
|
|
192
|
-
|
|
221
|
+
const input = cell.querySelector('input');
|
|
222
|
+
cell.innerHTML = input ? input.value : '';
|
|
193
223
|
});
|
|
194
224
|
|
|
195
|
-
if (!td.closest('table').
|
|
196
|
-
formMatching.
|
|
225
|
+
if (!td.closest('table').querySelectorAll('td input[type="text"]').length) {
|
|
226
|
+
formMatching.querySelector('button[type="submit"]').disabled = false;
|
|
197
227
|
}
|
|
198
228
|
ImportFromCsv.initEditLink(formMatching, td);
|
|
199
|
-
return false;
|
|
200
229
|
});
|
|
201
230
|
}
|
|
202
231
|
|
|
203
232
|
static initEditLink(formMatching, td) {
|
|
204
|
-
td.
|
|
205
|
-
td.
|
|
206
|
-
|
|
207
|
-
tr.
|
|
208
|
-
|
|
209
|
-
if (
|
|
233
|
+
td.innerHTML = '<a href="#" class="import_edit_line text-danger"><i class="fas fa-pencil-alt"></i></a>';
|
|
234
|
+
td.querySelector('a.import_edit_line').addEventListener('click', function(e) {
|
|
235
|
+
e.preventDefault();
|
|
236
|
+
const tr = this.closest('tr');
|
|
237
|
+
tr.querySelectorAll('td').forEach((cell) => {
|
|
238
|
+
if (cell.classList.contains('select_line_checkbox') || cell.classList.contains('edit_line_button')) {
|
|
210
239
|
return;
|
|
211
240
|
}
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
cell.dataset.original_value = cell.innerHTML;
|
|
242
|
+
cell.innerHTML = '<input type="text" class="form-control" value="' + cell.innerHTML.replace(/"/g, '"') + '" />';
|
|
214
243
|
});
|
|
215
|
-
formMatching.
|
|
244
|
+
formMatching.querySelector('button[type="submit"]').disabled = true;
|
|
216
245
|
ImportFromCsv.initValidateLine(formMatching, td);
|
|
217
|
-
return false;
|
|
218
246
|
});
|
|
219
247
|
}
|
|
220
248
|
|
|
221
|
-
static getErrorsHtmlOfImportData(json, divResult=null) {
|
|
249
|
+
static getErrorsHtmlOfImportData(json, divResult = null, errorMessageImportFailed = null, lineLabel = null) {
|
|
250
|
+
errorMessageImportFailed = errorMessageImportFailed ?? ImportFromCsv._defaults.errorMessageImportFailed;
|
|
251
|
+
lineLabel = lineLabel ?? ImportFromCsv._defaults.lineLabel;
|
|
222
252
|
let resultError = errorMessageImportFailed;
|
|
223
253
|
resultError += '<ul>';
|
|
224
|
-
|
|
254
|
+
json.forEach((errorData) => {
|
|
225
255
|
console.error(errorData);
|
|
226
256
|
if (null != divResult) {
|
|
227
|
-
divResult.
|
|
257
|
+
divResult.querySelector('table tr[data-line="' + errorData.line + '"]')?.classList.add('danger');
|
|
228
258
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
resultError += '<li>'+error+'</li>';
|
|
259
|
+
resultError += '<li>' + lineLabel.format(errorData.line) + '<ul>';
|
|
260
|
+
errorData.errors.forEach((error) => {
|
|
261
|
+
resultError += '<li>' + error + '</li>';
|
|
233
262
|
});
|
|
234
263
|
resultError += '</ul></li>';
|
|
235
264
|
});
|
|
236
|
-
resultError +='</ul>';
|
|
265
|
+
resultError += '</ul>';
|
|
237
266
|
return resultError;
|
|
238
267
|
}
|
|
239
268
|
|
|
@@ -253,37 +282,46 @@ class ImportFromCsv {
|
|
|
253
282
|
}
|
|
254
283
|
|
|
255
284
|
static getTabLink(formMatching) {
|
|
256
|
-
|
|
257
|
-
formMatching.
|
|
258
|
-
|
|
285
|
+
const tabLink = {};
|
|
286
|
+
formMatching.querySelectorAll('select').forEach((select) => {
|
|
287
|
+
const listeImportIndex = select.value;
|
|
259
288
|
if (listeImportIndex != -1) {
|
|
260
|
-
|
|
261
|
-
tabLink[key] = listeImportIndex;
|
|
289
|
+
tabLink[select.name] = listeImportIndex;
|
|
262
290
|
}
|
|
263
291
|
});
|
|
264
292
|
return tabLink;
|
|
265
293
|
}
|
|
266
294
|
|
|
267
|
-
static displayFormMatching(formMatching, importColumns, header, hasHeader) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
295
|
+
static displayFormMatching(formMatching, importColumns, header, hasHeader, selectDefaultOptionLabel = null) {
|
|
296
|
+
selectDefaultOptionLabel = selectDefaultOptionLabel ?? ImportFromCsv._defaults.selectDefaultOptionLabel;
|
|
297
|
+
let options = '<option value="-1">' + selectDefaultOptionLabel + '</option>';
|
|
298
|
+
header.forEach((value, index) => {
|
|
299
|
+
options += '<option value="' + (hasHeader ? value : index) + '">' + value + '</option>';
|
|
271
300
|
});
|
|
272
301
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
302
|
+
const selectContent = formMatching.querySelector('.import_matching_select_content');
|
|
303
|
+
selectContent.classList.add('row');
|
|
304
|
+
selectContent.innerHTML = '';
|
|
305
|
+
|
|
306
|
+
Object.entries(importColumns).forEach(([key, label]) => {
|
|
307
|
+
const tempDiv = document.createElement('div');
|
|
308
|
+
tempDiv.innerHTML =
|
|
276
309
|
'<div class="form-group col-md-3">' +
|
|
277
|
-
'<label for="form_import_'+key+'">'+label+'</label>' +
|
|
278
|
-
'<select class="form-control" name="'+key+'" id="form_import_'+key+'">'+options+'</select>' +
|
|
279
|
-
'</div>'
|
|
280
|
-
|
|
281
|
-
selectFormGroup.
|
|
282
|
-
|
|
310
|
+
'<label for="form_import_' + key + '">' + label + '</label>' +
|
|
311
|
+
'<select class="form-control" name="' + key + '" id="form_import_' + key + '">' + options + '</select>' +
|
|
312
|
+
'</div>';
|
|
313
|
+
const selectFormGroup = tempDiv.firstElementChild;
|
|
314
|
+
for (const option of selectFormGroup.querySelector('select').options) {
|
|
315
|
+
if (option.textContent.trim() === label) {
|
|
316
|
+
option.selected = true;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
selectContent.appendChild(selectFormGroup);
|
|
283
321
|
});
|
|
284
322
|
|
|
285
|
-
formMatching.
|
|
286
|
-
formMatching.
|
|
323
|
+
formMatching.querySelector('div.errors')?.classList.add('hide');
|
|
324
|
+
formMatching.classList.remove('hide');
|
|
287
325
|
}
|
|
288
326
|
|
|
289
327
|
}
|
package/multi_files_input.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
require('./string');
|
|
2
|
+
const { FlashMessage } = require('./flash_message');
|
|
2
3
|
|
|
3
4
|
class MultiFilesInput {
|
|
4
5
|
static init(fileInput, setFilesList, nbMaxFiles, maxFileSize) {
|
|
5
6
|
let filesList = [];
|
|
6
7
|
const formGroup = fileInput.closest('.form-group');
|
|
7
8
|
|
|
8
|
-
if (formGroup.
|
|
9
|
-
fileInput.
|
|
9
|
+
if (!formGroup.querySelector('.multi_files_input_dropzone')) {
|
|
10
|
+
fileInput.insertAdjacentHTML('afterend', `
|
|
10
11
|
<div class="multi_files_input_dropzone border rounded p-3 text-center" style="background:#fafafa; cursor: pointer;">
|
|
11
12
|
<i class="fas fa-cloud-upload-alt fa-2x text-muted mb-1"></i>
|
|
12
13
|
<div class="small text-muted">Glissez-déposez vos fichiers ici ou cliquez pour sélectionner</div>
|
|
@@ -14,43 +15,51 @@ class MultiFilesInput {
|
|
|
14
15
|
`);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
if (formGroup.
|
|
18
|
-
formGroup.
|
|
18
|
+
if (!formGroup.querySelector('.multi_files_input_files_preview')) {
|
|
19
|
+
formGroup.insertAdjacentHTML('beforeend', `
|
|
19
20
|
<div class="multi_files_input_files_preview mt-2 d-flex flex-wrap gap-2 hide"></div>
|
|
20
21
|
`);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
const dropzone = fileInput.
|
|
24
|
-
const filesPreview = fileInput.
|
|
24
|
+
const dropzone = fileInput.parentElement.querySelector('.multi_files_input_dropzone');
|
|
25
|
+
const filesPreview = fileInput.parentElement.querySelector('.multi_files_input_files_preview');
|
|
26
|
+
filesPreview.innerHTML = '';
|
|
25
27
|
|
|
26
|
-
fileInput.
|
|
28
|
+
fileInput.classList.add('hide');
|
|
27
29
|
|
|
28
30
|
// Dropzone interactions
|
|
29
|
-
dropzone.
|
|
31
|
+
const dropzoneClone = dropzone.cloneNode(true);
|
|
32
|
+
dropzone.parentElement.replaceChild(dropzoneClone, dropzone);
|
|
33
|
+
const activeDropzone = dropzoneClone;
|
|
34
|
+
|
|
35
|
+
activeDropzone.addEventListener('click', (e) => {
|
|
30
36
|
e.preventDefault();
|
|
31
37
|
e.stopPropagation();
|
|
32
|
-
fileInput.
|
|
38
|
+
fileInput.click();
|
|
33
39
|
});
|
|
34
|
-
|
|
40
|
+
activeDropzone.addEventListener('dragover', (e) => {
|
|
35
41
|
e.preventDefault();
|
|
36
42
|
e.stopPropagation();
|
|
37
|
-
|
|
43
|
+
activeDropzone.classList.add('border-primary');
|
|
38
44
|
});
|
|
39
|
-
|
|
45
|
+
activeDropzone.addEventListener('dragleave', (e) => {
|
|
40
46
|
e.preventDefault();
|
|
41
47
|
e.stopPropagation();
|
|
42
|
-
|
|
48
|
+
activeDropzone.classList.remove('border-primary');
|
|
43
49
|
});
|
|
44
|
-
|
|
50
|
+
activeDropzone.addEventListener('drop', (e) => {
|
|
45
51
|
e.preventDefault();
|
|
46
52
|
e.stopPropagation();
|
|
47
|
-
|
|
48
|
-
const dtFiles = (e.
|
|
53
|
+
activeDropzone.classList.remove('border-primary');
|
|
54
|
+
const dtFiles = (e.dataTransfer || {}).files || [];
|
|
49
55
|
handleFiles(Array.from(dtFiles));
|
|
50
56
|
});
|
|
51
|
-
|
|
57
|
+
|
|
58
|
+
const fileInputClone = fileInput.cloneNode(true);
|
|
59
|
+
fileInput.parentElement.replaceChild(fileInputClone, fileInput);
|
|
60
|
+
fileInputClone.addEventListener('change', (e) => {
|
|
52
61
|
handleFiles(Array.from(e.target.files));
|
|
53
|
-
|
|
62
|
+
fileInputClone.value = '';
|
|
54
63
|
});
|
|
55
64
|
|
|
56
65
|
function handleFiles(selected) {
|
|
@@ -71,37 +80,37 @@ class MultiFilesInput {
|
|
|
71
80
|
|
|
72
81
|
function renderPreview(file) {
|
|
73
82
|
const id = 'f_' + Math.random().toString(36).slice(2, 9);
|
|
74
|
-
const wrap =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
const wrap = document.createElement('div');
|
|
84
|
+
wrap.className = 'border rounded p-2 d-inline-flex align-items-center';
|
|
85
|
+
wrap.dataset.fileId = id;
|
|
86
|
+
wrap.style.background = 'white';
|
|
87
|
+
wrap.innerHTML = `
|
|
88
|
+
<div class="me-2 preview-thumb" style="width:64px; height:48px; display:flex; align-items:center; justify-content:center; overflow:hidden;"></div>
|
|
89
|
+
<div class="small text-truncate" style="max-width:160px;">${(file.name || '').escapeHtml()}</div>
|
|
90
|
+
<button type="button" class="btn-close btn-close-small ms-2" aria-label="Supprimer" style="margin-left:8px;"></button>
|
|
91
|
+
`;
|
|
92
|
+
filesPreview.appendChild(wrap);
|
|
93
|
+
filesPreview.classList.remove('hide');
|
|
83
94
|
|
|
84
95
|
// thumbnail for images
|
|
85
96
|
if (file.type.startsWith('image/')) {
|
|
86
97
|
const reader = new FileReader();
|
|
87
98
|
reader.onload = function (e) {
|
|
88
|
-
wrap.
|
|
99
|
+
wrap.querySelector('.preview-thumb').innerHTML = `<img src="${e.target.result}" alt="" style="max-width:100%; max-height:100%;" />`;
|
|
89
100
|
};
|
|
90
101
|
reader.readAsDataURL(file);
|
|
91
102
|
} else {
|
|
92
|
-
wrap.
|
|
103
|
+
wrap.querySelector('.preview-thumb').innerHTML = `<i class="fas fa-file fa-2x text-muted"></i>`;
|
|
93
104
|
}
|
|
94
105
|
|
|
95
|
-
wrap.
|
|
96
|
-
const idx = $(this).closest('[data-file-id]').index();
|
|
97
|
-
// remove by reference: find corresponding file by name+size (best-effort)
|
|
106
|
+
wrap.querySelector('.btn-close').addEventListener('click', () => {
|
|
98
107
|
const name = file.name, size = file.size;
|
|
99
108
|
filesList = filesList.filter(f => !(f.name === name && f.size === size));
|
|
100
109
|
setFilesList(filesList);
|
|
101
|
-
|
|
110
|
+
wrap.remove();
|
|
102
111
|
|
|
103
112
|
if (filesList.length === 0) {
|
|
104
|
-
filesPreview.
|
|
113
|
+
filesPreview.classList.add('hide');
|
|
105
114
|
}
|
|
106
115
|
});
|
|
107
116
|
}
|