PluginProbe
seQura / 3.0.6
seQura v3.0.6
4.3.4 4.3.3 4.3.2 4.3.1 trunk 2.0.0 2.0.10 2.0.11 2.0.12 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0.0 3.0.2 3.0.5 3.0.6 3.0.7 3.1.0 3.1.1 3.2.0 3.2.1 3.2.2 4.0.0 All 30 releases
sequra / assets / js / src / core / AdvancedController.js

AdvancedController.js in seQura 3.0.6, at assets/js/src/core/AdvancedController.js

433 lines 16.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { DataTable } from 'simple-datatables';
2 import JSONFormatter from 'json-formatter-js'
3
4 if (!window.SequraFE) {
5 window.SequraFE = {};
6 }
7
8 (function () {
9
10 /**
11 * @typedef Log
12 * @property {string} type
13 * @property {string} datetime
14 * @property {string} message
15 * @property {string} context
16 */
17
18 /**
19 * @typedef LogSettings
20 * @property {boolean} isEnabled
21 * @property {int} level
22 */
23
24 /**
25 * Handles payment methods page logic.
26 *
27 * @param {{
28 * getLogsUrl: string,
29 * }} configuration
30 * @constructor
31 */
32 function AdvancedController(configuration) {
33 const { templateService, elementGenerator: generator, components, utilities } = SequraFE;
34 /** @type AjaxServiceType */
35 const api = SequraFE.ajaxService;
36 /** @type string */
37 let currentStoreId = '';
38 /** @type Version */
39 let version;
40 /** @type Store[] */
41 let stores;
42 /** @type ConnectionSettings */
43 let connectionSettings;
44 /** @type LogSettings */
45 let logsSettings;
46 /** @type string[] */
47 let logs;
48 /** @type string */
49 let logList;
50
51 /** @type DataTable */
52 let dataTable;
53
54 const wrapperClass = 'sqp-datatable-wrapper';
55
56 /**
57 * Displays page content.
58 *
59 * @param {{ state?: string, storeId: string }} config
60 */
61 this.display = ({ storeId }) => {
62
63 currentStoreId = storeId;
64 templateService.clearMainPage();
65
66 stores = SequraFE.state.getData('stores');
67 version = SequraFE.state.getData('version');
68 connectionSettings = SequraFE.state.getData('connectionSettings');
69 logsSettings = SequraFE.state.getData('logsSettings');
70
71 logs = SequraFE.state.getData('logs');
72
73 if (!logs) {
74 api.get(configuration.getLogsUrl, null, SequraFE.customHeader)
75 .then(logsRes => {
76 SequraFE.state.setData('logs', logsRes)
77 logs = logsRes;
78
79 }).catch(error => {
80 console.error(error);
81 })
82 .finally(() => {
83 initializePage();
84 utilities.hideLoader()
85 });
86 } else {
87 initializePage();
88 utilities.hideLoader();
89 }
90 };
91
92 /**
93 * Renders the page contents.
94 */
95 const initializePage = () => {
96 const pageWrapper = document.getElementById('sq-page-wrapper');
97
98 pageWrapper.append(
99 generator.createElement('div', 'sq-page-content-wrapper sqv--transactions', '', null, [
100 SequraFE.components.PageHeader.create(
101 {
102 currentVersion: version?.current,
103 newVersion: {
104 versionLabel: version?.new,
105 versionUrl: version?.downloadNewVersionUrl
106 },
107 mode: connectionSettings.environment === 'live' ? connectionSettings.environment : 'test',
108 activeStore: currentStoreId,
109 stores: stores.map((store) => ({ label: store.storeName, value: store.storeId })),
110 onChange: (storeId) => {
111 if (storeId !== SequraFE.state.getStoreId()) {
112 SequraFE.state.setStoreId(storeId);
113 window.location.hash = '';
114 SequraFE.state.display();
115 }
116 },
117 menuItems: SequraFE.utilities.getMenuItems(SequraFE.appStates.ADVANCED)
118 }
119 ),
120 generator.createElement('div', 'sq-page-content', '', null, [
121 generator.createElement('div', 'sq-content-row', '', null, [
122 generator.createElement('main', 'sq-content', '', null, [
123 generator.createElement('div', 'sq-content-inner', '', null, [
124 generator.createElement('div', 'sqp-flash-message-wrapper'),
125 generator.createElement('div', 'sq-table-heading', '', null, [
126 generator.createPageHeading({
127 title: 'general.advanced'
128 }),
129 ]),
130 generator.createToggleField({
131 className: 'sq-log-settings-toggle',
132 value: logsSettings.isEnabled,
133 label: 'debug.title',
134 description: 'debug.description',
135 onChange: handleEnableDebugChange
136 }),
137 generator.createDropdownField({
138 label: 'debug.log.minLevel',
139 description: 'debug.log.minLevelDescription',
140 value: logsSettings.level,
141 options: getLogLevelOptions(),
142 variation: "label-left",
143 onChange: handleLogLevelChange
144 }),
145 generator.createElement('div', wrapperClass),
146 ]),
147 ])
148 ])
149 ]),
150 generator.createSupportLink()
151 ]));
152
153 initializeDataTable();
154 }
155
156 /**
157 * Returns log level options.
158 *
159 * @returns {[{label: string, value: string}]}
160 */
161 const getLogLevelOptions = () => [
162 { label: 'DEBUG', value: 3 },
163 { label: 'INFO', value: 2 },
164 { label: 'WARNING', value: 1 },
165 { label: 'ERROR', value: 0 },
166 ]
167
168 /**
169 * Returns table headers.
170 *
171 * @returns {TableCell[]}
172 */
173 const getTableHeaders = () => {
174 return [
175 { label: 'debug.log.severity', className: 'sqm--text-left' },
176 { label: 'debug.log.datetime', className: 'sqm--text-left' },
177 { label: 'debug.log.message', className: 'sqm--text-left' },
178 ];
179 }
180
181 /**
182 * Parse raw log data and return an object.
183 *
184 * @returns {Log}
185 */
186 const parseLog = (raw) => {
187 const data = raw.split('\t');
188 return {
189 type: data[0].trim(),
190 datetime: data[1].trim(),
191 message: data[2].trim(),
192 context: data[3].trim()
193 };
194 }
195
196
197 /** @param {EventTarget} btn */
198 const renderDetails = (btn) => {
199 btn.classList.toggle('sqm--log-details-open');
200 const container = btn.parentElement.querySelector('.sqm--log-context');
201 container.style.display = btn.classList.contains('sqm--log-details-open') ? 'block' : 'none';
202
203 if (!container.firstChild) {
204 // debugger
205 const json = JSON.parse(logList[btn.getAttribute('data-index')].context);
206 const formatter = new JSONFormatter(json, 0);
207 container.appendChild(formatter.render());
208 }
209 }
210
211 /**
212 * Returns table rows.
213 *
214 * @param {string[]} logs
215 *
216 * @returns {TableCell[][]}
217 */
218 const getTableRows = (logs) => {
219 logList = logs.map(rawLog => parseLog(rawLog));
220
221 return logList.map((log, index) => {
222 const className = `sqm--text-left sqm--log sqm--log-${log.type.toLowerCase()}`;
223
224 return [
225 { label: log.type, className },
226 { label: log.datetime, className },
227 {
228 label: log.message,
229 className: `${className} sqm--log-index-${index}`,
230 renderer: (cell) => {
231 if ('' != log.context) {
232 const btn = generator.createButton({
233 type: 'text',
234 className: 'sqm--log-details',
235 });
236 btn.setAttribute('data-index', index);
237 cell.append(btn);
238 cell.append(generator.createElement('div', 'sqm--log-context'));
239 }
240 return cell;
241 }
242 }
243 ];
244 });
245 }
246
247 const initializeDataTable = () => {
248 if (dataTable) {
249 dataTable.destroy();
250 dataTable = null;
251 }
252
253 const wrapper = document.querySelector(`.${wrapperClass}`);
254 if (wrapper) {
255 SequraFE.templateService.clearComponent(wrapper);
256 }
257
258 wrapper.append(components.DataTable.create(getTableHeaders(), logs?.length ? getTableRows(logs) : []));
259
260 dataTable = new DataTable(`.${wrapperClass} table`, {
261 classes: {
262 active: "sq-datatable__active",
263 bottom: "sq-datatable__bottom",
264 container: "sq-datatable__container",
265 cursor: "sq-datatable__cursor",
266 dropdown: "sq-datatable__dropdown",
267 ellipsis: "sq-datatable__ellipsis",
268 empty: "sq-datatable__empty",
269 headercontainer: "sq-datatable__headercontainer",
270 info: "sq-datatable__info",
271 input: "sq-datatable__input",
272 loading: "sq-datatable__loading",
273 pagination: "sq-datatable__pagination",
274 paginationList: "sq-datatable__pagination-list",
275 search: "sq-datatable__search",
276 selector: "sq-datatable__selector",
277 sorter: "sq-datatable__sorter",
278 table: "sq-datatable__table",
279 top: "sq-datatable__top",
280 wrapper: "sq-datatable__wrapper"
281 }
282 });
283
284 const searchWrapper = document.querySelector('.sq-datatable__search');
285 if (!searchWrapper) {
286 return;
287 }
288 searchWrapper.append(
289 generator.createButtonField({
290 className: 'sq-controls sqm--block sqm--block-inline',
291 buttonLabel: 'debug.reload',
292 onClick: handleReloadLogs
293 }),
294 generator.createButtonField({
295 className: 'sq-controls sqm--block sqm--block-inline',
296 buttonType: 'danger',
297 buttonLabel: 'debug.remove',
298 onClick: handleRemoveLogs
299 })
300 );
301
302 const resetDetails = () => {
303 document.querySelectorAll('.sqm--log-context').forEach(elem => {
304 elem.style.display = 'none'
305 elem.innerHTML = '';
306 });
307 document.querySelectorAll('.sqm--log-details').forEach(elem => {
308 elem.classList.remove('sqm--log-details-open');
309 });
310 }
311
312 dataTable.on('datatable.init', resetDetails);
313 dataTable.on('datatable.page', resetDetails);
314 dataTable.on('datatable.selectrow', (rowIndex, event) => {
315 if (event.target.classList.contains('sqm--log-details')) {
316 renderDetails(event.target);
317 }
318 });
319 }
320
321 /**
322 * Save logs settings.
323 * @param {LogSettings} settings
324 */
325 const saveLogsSettings = (settings) => {
326 utilities.showLoader();
327 api.post(configuration.saveLogsSettingsUrl, settings, SequraFE.customHeader)
328 .then(response => {
329 logsSettings = settings;
330 SequraFE.state.setData('logsSettings', logsSettings);
331 }).catch(error => {
332 console.error(error);
333 document.querySelector('.sq-log-settings-toggle input').checked = !value;
334 showFlashMessage('general.errors.unknown', 'error');
335 })
336 .finally(() => utilities.hideLoader());
337 }
338
339 const handleEnableDebugChange = (value) => saveLogsSettings({
340 ...logsSettings,
341 isEnabled: value
342 });
343
344 const handleLogLevelChange = (value) => saveLogsSettings({
345 ...logsSettings,
346 level: parseInt(value)
347 });
348
349 const showFlashMessage = (message, status) => {
350 const container = document.querySelector('.sqp-flash-message-wrapper');
351 if (!container) {
352 return;
353 }
354
355 SequraFE.templateService.clearComponent(container);
356
357 container.prepend(SequraFE.utilities.createFlashMessage(message, status));
358 }
359
360 /**
361 * Shows the confirm remove modal.
362 *
363 * @returns {Promise}
364 */
365 const showConfirmRemoveModal = () => {
366 return new Promise((resolve) => {
367 const modal = components.Modal.create({
368 title: "debug.log.removeModal.title",
369 className: "sq-modal",
370 content: [generator.createElement('p', '', "debug.log.removeModal.message")],
371 footer: true,
372 buttons: [
373 {
374 type: 'default',
375 label: 'general.cancel',
376 onClick: () => {
377 modal.close();
378 resolve(false);
379 }
380 },
381 {
382 type: 'danger',
383 label: 'debug.remove',
384 onClick: () => {
385 modal.close();
386 resolve(true);
387 }
388 }
389 ]
390 });
391
392 modal.open();
393 });
394 }
395
396 const handleRemoveLogs = () => {
397 showConfirmRemoveModal().then((confirmed) => {
398 if (!confirmed) {
399 return;
400 }
401
402 utilities.showLoader();
403 api.delete(configuration.removeLogsUrl, null, SequraFE.customHeader)
404 .then(_ => {
405 logs = [];
406 SequraFE.state.setData('logs', logs)
407 initializeDataTable();
408 }).catch(error => {
409 console.error(error);
410 showFlashMessage('general.errors.failedToRemoveLog', 'error');
411 })
412 .finally(() => utilities.hideLoader());
413 });
414 }
415
416 const handleReloadLogs = () => {
417 utilities.showLoader();
418 api.get(configuration.getLogsUrl, null, SequraFE.customHeader)
419 .then(logsRes => {
420 logs = logsRes;
421 SequraFE.state.setData('logs', logs)
422 initializeDataTable();
423 }).catch(error => {
424 console.error(error);
425 showFlashMessage('general.errors.failedToRetrieveLog', 'error');
426 })
427 .finally(() => utilities.hideLoader());
428 }
429 }
430
431 SequraFE.AdvancedController = AdvancedController;
432 })();
433