PluginProbe
seQura / 3.1.0
seQura v3.1.0
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 / TransactionsController.js

TransactionsController.js in seQura 3.1.0, at assets/js/src/core/TransactionsController.js

326 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 if (!window.SequraFE) {
2 window.SequraFE = {};
3 }
4
5 (function () {
6 /**
7 * @typedef TransactionLog
8 * @property {string} merchantReference
9 * @property {number} executionId
10 * @property {string} paymentMethod
11 * @property {number} timestamp
12 * @property {string} eventCode
13 * @property {boolean} isSuccessful
14 * @property {string} queueStatus
15 * @property {string} reason
16 * @property {string | null} failureDescription
17 * @property {string} sequraLink
18 * @property {string} shopLink
19 */
20
21 /**
22 * @typedef TransactionData
23 * @property {TransactionLog[]} transactionLogs
24 * @property {boolean} hasNextPage
25 */
26
27 /**
28 * Handles payment methods page logic.
29 *
30 * @param {{
31 * getTransactionLogsUrl: string,
32 * }} configuration
33 * @constructor
34 */
35 function TransactionsController(configuration) {
36 const {templateService, elementGenerator: generator, components, utilities} = SequraFE;
37 /** @type AjaxServiceType */
38 const api = SequraFE.ajaxService;
39 /** @type string */
40 let currentStoreId = '';
41 /** @type Version */
42 let version;
43 /** @type Store[] */
44 let stores;
45 /** @type ConnectionSettings */
46 let connectionSettings;
47 /** @type TransactionLog[] */
48 let transactionLogs;
49
50 let hasNextPage = true;
51 let isLoading = false;
52 let isListenerSet = false;
53 let page = 1;
54 const limit = 10;
55
56 /**
57 * Displays page content.
58 *
59 * @param {{ state?: string, storeId: string }} config
60 */
61 this.display = ({storeId}) => {
62 currentStoreId = storeId;
63 templateService.clearMainPage();
64
65 stores = SequraFE.state.getData('stores');
66 version = SequraFE.state.getData('version');
67 connectionSettings = SequraFE.state.getData('connectionSettings');
68 transactionLogs = SequraFE.state.getData('transactionLogs');
69 isListenerSet = transactionLogs !== null
70 page = transactionLogs ? Math.ceil(transactionLogs.length / limit) : 1;
71
72 if (transactionLogs) {
73 initializePage();
74 utilities.hideLoader();
75
76 return;
77 }
78
79 Promise.all([
80 transactionLogs ? [] : api.get(configuration.getTransactionLogsUrl + `&page=${page}&limit=${limit}`, null, SequraFE.customHeader),
81 ]).then(([transactionLogsRes]) => {
82 if (transactionLogsRes.length !== 0) {
83 transactionLogs = transactionLogsRes.transactionLogs;
84 hasNextPage = transactionLogsRes.hasNextPage;
85 SequraFE.state.setData('transactionLogs', transactionLogsRes.transactionLogs)
86 }
87
88 initializePage();
89 }).finally(() => utilities.hideLoader());
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.TRANSACTION)
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', 'sq-table-heading', '', null, [
125 generator.createPageHeading({
126 title: 'transaction.title',
127 text: 'transaction.description'
128 }),
129 ]),
130 transactionLogs?.length > 0 ?
131 components.DataTable.create(getTableHeaders(), getTableRows(transactionLogs)) :
132 utilities.createFlashMessage('transaction.noLogs', 'success')
133 ]),
134 ])
135 ])
136 ]),
137 generator.createSupportLink()
138 ]));
139
140 initializeInfiniteScroll();
141 }
142
143 /**
144 * Returns table headers.
145 *
146 * @returns {TableCell[]}
147 */
148 const getTableHeaders = () => {
149 return [
150 {label: 'transaction.orderId', className: 'sqm--text-left'},
151 {label: 'transaction.paymentMethod', className: 'sqm--text-left'},
152 {label: 'transaction.dateTime', className: 'sqm--text-left'},
153 {label: 'transaction.eventType', className: 'sqm--text-left'},
154 {label: 'transaction.successful', className: 'sqm--text-left'},
155 {label: 'transaction.status.title', className: 'sqm--text-left'},
156 {label: 'transaction.details.title', className: 'sqm--text-left'}
157 ];
158 }
159
160 /**
161 * Returns table rows.
162 *
163 * @param {TransactionLog[]} logs
164 *
165 * @returns {TableCell[][]}
166 */
167 const getTableRows = (logs) => {
168 return logs.map((transactionLog) => {
169 return [
170 {label: transactionLog.merchantReference, className: 'sqm--text-left'},
171 {label: transactionLog.paymentMethod, className: 'sqm--text-left'},
172 {label: formatDate(transactionLog.timestamp), className: 'sqm--text-left'},
173 {label: transactionLog.eventCode.toUpperCase(), className: 'sqm--text-left'},
174 {
175 label: transactionLog.isSuccessful ? 'transaction.yes' : 'transaction.no',
176 className: 'sqm--text-left'
177 },
178 {
179 className: 'sqm--text-left',
180 renderer: (cell) =>
181 cell.append(
182 generator.createElement(
183 'span',
184 `sqp-status sqt--${transactionLog.queueStatus}`,
185 `transaction.status.${transactionLog.queueStatus}`
186 )
187 )
188 },
189 transactionLog.failureDescription ? {
190 className: 'sqm--text-left',
191 renderer: (cell) =>
192 cell.append(
193 generator.createButton({
194 type: 'text',
195 label: 'transaction.details.label',
196 onClick: () => renderDetails(transactionLog)
197 })
198 )
199 } : {},
200 {label: transactionLog.executionId.toString(), className: 'sq-row-id sqs--hidden'}
201 ];
202 });
203 }
204
205 /**
206 * Renders the Transaction Log details.
207 *
208 * @param {TransactionLog} transactionLog
209 */
210 const renderDetails = (transactionLog) => {
211 const existingDetails = document.getElementsByClassName('sqp-details')
212 Array.from(existingDetails).map((e) => e.remove());
213
214 const idCells = Array.from(document.getElementsByClassName('sq-row-id sqs--hidden'));
215 const row = idCells.find((cell) => cell.textContent.trim() === transactionLog.executionId.toString()).parentElement;
216 const detailsCell = generator.createElement('tr', 'sqp-details', '', null, [
217 generator.createElement('td', '', '', {colSpan: 7}, [getDetails(transactionLog)])
218 ])
219
220 row.insertAdjacentElement('afterend', detailsCell);
221 }
222
223 /**
224 * Returns the Transaction Log details element.
225 *
226 * @param {TransactionLog} transactionLog
227 *
228 * @return {HTMLElement}
229 */
230 const getDetails = (transactionLog) => {
231 return generator.createElement('div', 'sqp-details-wrapper', '', null, [
232 generator.createElement('div', 'sqp-details-row', '', null, [
233 generator.createElement('div', 'sqp-details-info', '', null, [
234 generator.createElement('span', 'sqp-details-label', 'transaction.details.reason'),
235 generator.createElement('span', 'sqp-details-message', transactionLog.reason),
236 ]),
237 generator.createButtonLink({
238 className: 'sq-link-button',
239 text: 'transaction.details.sequraLink',
240 href: transactionLog.sequraLink,
241 openInNewTab: true
242 })
243 ]),
244 generator.createElement('div', 'sqp-details-row', '', null, [
245 generator.createElement('div', 'sqp-details-info', '', null, [
246 generator.createElement('span', 'sqp-details-label', 'transaction.details.failureDescription'),
247 generator.createElement('span', 'sqp-details-message', transactionLog.failureDescription),
248 ]),
249 generator.createButtonLink({
250 className: 'sq-link-button',
251 text: 'transaction.details.shopLink',
252 href: transactionLog.shopLink,
253 openInNewTab: true
254 })
255 ]),
256 ]);
257 }
258
259 const initializeInfiniteScroll = () => {
260 if(isListenerSet) {
261 return;
262 }
263
264 window?.addEventListener('scroll', fetchNewPage)
265 }
266
267 const fetchNewPage = () => {
268 const tableWrapper = document.querySelector(`.sqv--transactions .sq-table-container`);
269 if (hasNextPage && !isLoading && hasScrolledToEnd()) {
270 isLoading = true;
271 let spinnerWrapper = generator.createElement('div', 'sq-loader sqt--large', '', '', [
272 generator.createElement('div', 'sqp-spinner')
273 ]);
274
275 tableWrapper?.append(spinnerWrapper);
276
277 api.get(configuration.getTransactionLogsUrl + `&page=${page + 1}&limit=${limit}`, null, SequraFE.customHeader)
278 .then((res) => {
279 page++;
280 hasNextPage = res?.hasNextPage;
281 if(res?.transactionLogs?.length) {
282 components.DataTable.createTableRows(tableWrapper, getTableRows(res.transactionLogs));
283 transactionLogs = transactionLogs.concat(res.transactionLogs);
284 SequraFE.state.setData('transactionLogs', transactionLogs);
285 }
286 })
287 .finally(() => {
288 spinnerWrapper.remove();
289 isLoading = false;
290 });
291 }
292 }
293
294
295 /**
296 * Returns true if scrolled to end of page.
297 */
298 const hasScrolledToEnd = () => {
299 const scrollTop = window.scrollY || window.pageYOffset;
300
301 return scrollTop + window.innerHeight >= document.documentElement.scrollHeight;
302 }
303
304 /**
305 * Formats the given date to the required table view.
306 *
307 * @param {number} timestamp
308 *
309 * @returns {string}
310 */
311 const formatDate = (timestamp) => {
312 const dateTime = new Date(timestamp * 1000);
313 const day = String(dateTime.getDate()).padStart(2, '0');
314 const month = String(dateTime.getMonth() + 1).padStart(2, '0');
315 const year = dateTime.getFullYear();
316
317 const hours = String(dateTime.getHours()).padStart(2, '0');
318 const minutes = String(dateTime.getMinutes()).padStart(2, '0');
319
320 return `${day}-${month}-${year} ${hours}:${minutes}`;
321 }
322 }
323
324 SequraFE.TransactionsController = TransactionsController;
325 })();
326