# sequra/3.2.2/assets/js/src/core/TransactionsController.js

seQura, version 3.2.2. 326 lines.

- Page: https://pluginprobe.com/plugins/sequra/3.2.2/code/assets/js/src/core/TransactionsController.js
- Raw: https://pluginprobe.com/plugins/sequra/3.2.2/raw/assets/js/src/core/TransactionsController.js
- Modified: 2024-10-29T15:19:22+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/sequra/3.2.2/code/assets/js/src/core/TransactionsController.js#L10-L20`.

```javascript
if (!window.SequraFE) {
    window.SequraFE = {};
}

(function () {
    /**
     * @typedef TransactionLog
     * @property {string} merchantReference
     * @property {number} executionId
     * @property {string} paymentMethod
     * @property {number} timestamp
     * @property {string} eventCode
     * @property {boolean} isSuccessful
     * @property {string} queueStatus
     * @property {string} reason
     * @property {string | null} failureDescription
     * @property {string} sequraLink
     * @property {string} shopLink
     */

    /**
     * @typedef TransactionData
     * @property {TransactionLog[]} transactionLogs
     * @property {boolean} hasNextPage
     */

    /**
     * Handles payment methods page logic.
     *
     * @param {{
     * getTransactionLogsUrl: string,
     * }} configuration
     * @constructor
     */
    function TransactionsController(configuration) {
        const {templateService, elementGenerator: generator, components, utilities} = SequraFE;
        /** @type AjaxServiceType */
        const api = SequraFE.ajaxService;
        /** @type string */
        let currentStoreId = '';
        /** @type Version */
        let version;
        /** @type Store[] */
        let stores;
        /** @type ConnectionSettings */
        let connectionSettings;
        /** @type TransactionLog[] */
        let transactionLogs;

        let hasNextPage = true;
        let isLoading = false;
        let isListenerSet = false;
        let page = 1;
        const limit = 10;

        /**
         * Displays page content.
         *
         * @param {{ state?: string, storeId: string }} config
         */
        this.display = ({storeId}) => {
            currentStoreId = storeId;
            templateService.clearMainPage();

            stores = SequraFE.state.getData('stores');
            version = SequraFE.state.getData('version');
            connectionSettings = SequraFE.state.getData('connectionSettings');
            transactionLogs = SequraFE.state.getData('transactionLogs');
            isListenerSet = transactionLogs !== null
            page = transactionLogs ? Math.ceil(transactionLogs.length / limit) : 1;

            if (transactionLogs) {
                initializePage();
                utilities.hideLoader();

                return;
            }

            Promise.all([
                transactionLogs ? [] : api.get(configuration.getTransactionLogsUrl + `&page=${page}&limit=${limit}`, null, SequraFE.customHeader),
            ]).then(([transactionLogsRes]) => {
                if (transactionLogsRes.length !== 0) {
                    transactionLogs = transactionLogsRes.transactionLogs;
                    hasNextPage = transactionLogsRes.hasNextPage;
                    SequraFE.state.setData('transactionLogs', transactionLogsRes.transactionLogs)
                }

                initializePage();
            }).finally(() => utilities.hideLoader());
        };

        /**
         * Renders the page contents.
         */
        const initializePage = () => {
            const pageWrapper = document.getElementById('sq-page-wrapper');

            pageWrapper.append(
                generator.createElement('div', 'sq-page-content-wrapper sqv--transactions', '', null, [
                    SequraFE.components.PageHeader.create(
                        {
                            currentVersion: version?.current,
                            newVersion: {
                                versionLabel: version?.new,
                                versionUrl: version?.downloadNewVersionUrl
                            },
                            mode: connectionSettings.environment === 'live' ? connectionSettings.environment : 'test',
                            activeStore: currentStoreId,
                            stores: stores.map((store) => ({label: store.storeName, value: store.storeId})),
                            onChange: (storeId) => {
                                if (storeId !== SequraFE.state.getStoreId()) {
                                    SequraFE.state.setStoreId(storeId);
                                    window.location.hash = '';
                                    SequraFE.state.display();
                                }
                            },
                            menuItems: SequraFE.utilities.getMenuItems(SequraFE.appStates.TRANSACTION)
                        }
                    ),
                    generator.createElement('div', 'sq-page-content', '', null, [
                        generator.createElement('div', 'sq-content-row', '', null, [
                            generator.createElement('main', 'sq-content', '', null, [
                                generator.createElement('div', 'sq-content-inner', '', null, [
                                    generator.createElement('div', 'sq-table-heading', '', null, [
                                        generator.createPageHeading({
                                            title: 'transaction.title',
                                            text: 'transaction.description'
                                        }),
                                    ]),
                                    transactionLogs?.length > 0 ?
                                        components.DataTable.create(getTableHeaders(), getTableRows(transactionLogs)) :
                                        utilities.createFlashMessage('transaction.noLogs', 'success')
                                ]),
                            ])
                        ])
                    ]),
                    generator.createSupportLink()
                ]));

            initializeInfiniteScroll();
        }

        /**
         * Returns table headers.
         *
         * @returns {TableCell[]}
         */
        const getTableHeaders = () => {
            return [
                {label: 'transaction.orderId', className: 'sqm--text-left'},
                {label: 'transaction.paymentMethod', className: 'sqm--text-left'},
                {label: 'transaction.dateTime', className: 'sqm--text-left'},
                {label: 'transaction.eventType', className: 'sqm--text-left'},
                {label: 'transaction.successful', className: 'sqm--text-left'},
                {label: 'transaction.status.title', className: 'sqm--text-left'},
                {label: 'transaction.details.title', className: 'sqm--text-left'}
            ];
        }

        /**
         * Returns table rows.
         *
         * @param {TransactionLog[]} logs
         *
         * @returns {TableCell[][]}
         */
        const getTableRows = (logs) => {
            return logs.map((transactionLog) => {
                return [
                    {label: transactionLog.merchantReference, className: 'sqm--text-left'},
                    {label: transactionLog.paymentMethod, className: 'sqm--text-left'},
                    {label: formatDate(transactionLog.timestamp), className: 'sqm--text-left'},
                    {label: transactionLog.eventCode.toUpperCase(), className: 'sqm--text-left'},
                    {
                        label: transactionLog.isSuccessful ? 'transaction.yes' : 'transaction.no',
                        className: 'sqm--text-left'
                    },
                    {
                        className: 'sqm--text-left',
                        renderer: (cell) =>
                            cell.append(
                                generator.createElement(
                                    'span',
                                    `sqp-status sqt--${transactionLog.queueStatus}`,
                                    `transaction.status.${transactionLog.queueStatus}`
                                )
                            )
                    },
                    transactionLog.failureDescription ? {
                        className: 'sqm--text-left',
                        renderer: (cell) =>
                            cell.append(
                                generator.createButton({
                                    type: 'text',
                                    label: 'transaction.details.label',
                                    onClick: () => renderDetails(transactionLog)
                                })
                            )
                    } : {},
                    {label: transactionLog.executionId.toString(), className: 'sq-row-id sqs--hidden'}
                ];
            });
        }

        /**
         * Renders the Transaction Log details.
         *
         * @param {TransactionLog} transactionLog
         */
        const renderDetails = (transactionLog) => {
            const existingDetails = document.getElementsByClassName('sqp-details')
            Array.from(existingDetails).map((e) => e.remove());

            const idCells = Array.from(document.getElementsByClassName('sq-row-id sqs--hidden'));
            const row = idCells.find((cell) => cell.textContent.trim() === transactionLog.executionId.toString()).parentElement;
            const detailsCell = generator.createElement('tr', 'sqp-details', '', null, [
                generator.createElement('td', '', '', {colSpan: 7}, [getDetails(transactionLog)])
            ])

            row.insertAdjacentElement('afterend', detailsCell);
        }

        /**
         * Returns the Transaction Log details element.
         *
         * @param {TransactionLog} transactionLog
         *
         * @return {HTMLElement}
         */
        const getDetails = (transactionLog) => {
            return generator.createElement('div', 'sqp-details-wrapper', '', null, [
                generator.createElement('div', 'sqp-details-row', '', null, [
                    generator.createElement('div', 'sqp-details-info', '', null, [
                        generator.createElement('span', 'sqp-details-label', 'transaction.details.reason'),
                        generator.createElement('span', 'sqp-details-message', transactionLog.reason),
                    ]),
                    generator.createButtonLink({
                        className: 'sq-link-button',
                        text: 'transaction.details.sequraLink',
                        href: transactionLog.sequraLink,
                        openInNewTab: true
                    })
                ]),
                generator.createElement('div', 'sqp-details-row', '', null, [
                    generator.createElement('div', 'sqp-details-info', '', null, [
                        generator.createElement('span', 'sqp-details-label', 'transaction.details.failureDescription'),
                        generator.createElement('span', 'sqp-details-message', transactionLog.failureDescription),
                    ]),
                    generator.createButtonLink({
                        className: 'sq-link-button',
                        text: 'transaction.details.shopLink',
                        href: transactionLog.shopLink,
                        openInNewTab: true
                    })
                ]),
            ]);
        }

        const initializeInfiniteScroll = () => {
            if(isListenerSet) {
                return;
            }

            window?.addEventListener('scroll', fetchNewPage)
        }

        const fetchNewPage = () => {
            const tableWrapper = document.querySelector(`.sqv--transactions .sq-table-container`);
            if (hasNextPage && !isLoading && hasScrolledToEnd()) {
                isLoading = true;
                let spinnerWrapper = generator.createElement('div', 'sq-loader sqt--large', '', '', [
                    generator.createElement('div', 'sqp-spinner')
                ]);

                tableWrapper?.append(spinnerWrapper);

                api.get(configuration.getTransactionLogsUrl + `&page=${page + 1}&limit=${limit}`, null, SequraFE.customHeader)
                    .then((res) => {
                        page++;
                        hasNextPage = res?.hasNextPage;
                        if(res?.transactionLogs?.length) {
                            components.DataTable.createTableRows(tableWrapper, getTableRows(res.transactionLogs));
                            transactionLogs = transactionLogs.concat(res.transactionLogs);
                            SequraFE.state.setData('transactionLogs', transactionLogs);
                        }
                    })
                    .finally(() => {
                        spinnerWrapper.remove();
                        isLoading = false;
                    });
            }
        }


        /**
         * Returns true if scrolled to end of page.
         */
        const hasScrolledToEnd = () => {
            const scrollTop = window.scrollY || window.pageYOffset;

            return scrollTop + window.innerHeight >= document.documentElement.scrollHeight;
        }

        /**
         * Formats the given date to the required table view.
         *
         * @param {number} timestamp
         *
         * @returns {string}
         */
        const formatDate = (timestamp) => {
            const dateTime = new Date(timestamp * 1000);
            const day = String(dateTime.getDate()).padStart(2, '0');
            const month = String(dateTime.getMonth() + 1).padStart(2, '0');
            const year = dateTime.getFullYear();

            const hours = String(dateTime.getHours()).padStart(2, '0');
            const minutes = String(dateTime.getMinutes()).padStart(2, '0');

            return `${day}-${month}-${year} ${hours}:${minutes}`;
        }
    }

    SequraFE.TransactionsController = TransactionsController;
})();

```
