File "default_board.php"

Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/views/taskmanager/tmpl/default_board.php
File size: 15.5 KB
MIME-type: text/x-php
Charset: utf-8

<?php
/** 
 * @package     VikBooking
 * @subpackage  core
 * @author      E4J s.r.l.
 * @copyright   Copyright (C) 2025 E4J s.r.l. All Rights Reserved.
 * @license     http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
 * @link        https://vikwp.com
 */

// No direct access
defined('ABSPATH') or die('No script kiddies please!');

?>

<div class="vbo-tm-board-areas-list">
    <?php
    foreach ($this->visibleAreas as $areaRecord) {
        // wrap the area record into a task area object
        $taskArea = VBOTaskArea::getInstance((array) $areaRecord);
        ?>
        <div class="vbo-tm-board-area-container" data-area-id="<?php echo $taskArea->getID(); ?>">
        <?php
        // build layout data
        $layout_data = [
            'data' => [
                'taskArea' => $taskArea,
                'filters'  => $this->filters,
            ],
        ];

        // render task list area
        echo JLayoutHelper::render('taskmanager.board.area', $layout_data);
        ?>
        </div>
        <?php
    }
    ?>
</div>

<script type="text/javascript">
    /**
     * Register function for rendering a task area.
     */
    const vboTmBoardRenderArea = (areaId) => {
        let areaContainer = jQuery('<div></div>')
            .addClass('vbo-tm-board-area-container')
            .attr('data-area-id', areaId);

        areaContainer.append(
            jQuery('<div></div>')
                .addClass('vbo-tm-board-area-loading')
                .html('<?php VikBookingIcons::e('circle-notch', 'fa-spin fa-fw'); ?>')
        );

        areaContainer.appendTo('.vbo-tm-board-areas-list');

        // make the request to display the requested task area
        VBOCore.doAjax(
            "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
            {
                type: 'board.area',
                data: {
                    area_id: areaId,
                    filters: vboTmFilters || {},
                },
            },
            (resp) => {
                try {
                    // decode the response (if needed)
                    let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;

                    // set HTML response (use jQuery rather than plain JS to avoid issues with injected scripts execution)
                    areaContainer.html(obj_res['html']);

                    // silently update task area status
                    vboTmToggleAreaDisplay(areaId, 1);

                    // load area tasks list
                    vboTmBoardRenderAreaTasks(areaId);
                } catch (err) {
                    console.error('Error decoding the response', err, resp);
                }
            },
            (error) => {
                // display error message
                alert(error.responseText);

                // dispatch the event for updating the active areas
                VBOCore.emitEvent('vbo-tm-area-update-status');
            }
        );
    };

    /**
     * Register function for rendering the given area tasks with filters and start offset.
     */
    const vboTmBoardRenderAreaTasks = (areaId, filters, start) => {
        let tasksList;
        let loadingList;
        let nextPage = false;

        if (start) {
            // existing tasks list for loading a new page
            tasksList = jQuery('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
                .find('.vbo-tm-board-area-tasks-list');

            if (tasksList.length) {
                // set the current loading list
                loadingList = document
                    .querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
                    .querySelector('.vbo-tm-board-area-tasks-list');

                // populate loading skeletons
                vboTmBoardSetSkeletons(areaId, 10);
            } else {
                // start a new list
                tasksList = null;
            }
        }

        if (!tasksList) {
            // load the first page of tasks
            tasksList = jQuery('<div></div>')
                .addClass('vbo-tm-board-area-tasks-list');

            tasksList.append(
                jQuery('<div></div>')
                    .addClass('vbo-tm-board-area-tasks-loading')
                    .html('<?php VikBookingIcons::e('circle-notch', 'fa-spin fa-fw'); ?>')
            );

            tasksList.appendTo('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]');
        } else {
            // turn flag on for loading a next page
            nextPage = true;
        }

        // make the request to display the requested task area
        VBOCore.doAjax(
            "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
            {
                type: 'board.tasks',
                data: {
                    area_id: areaId,
                    filters: filters || vboTmFilters || {},
                    start: start || 0,
                    limit: 10,
                },
            },
            (resp) => {
                try {
                    // decode the response (if needed)
                    let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;

                    // set HTML response (use jQuery rather than plain JS to avoid issues with injected scripts execution)
                    if (!nextPage) {
                        // set the loading result for the first page of tasks
                        tasksList.html(obj_res['html']);
                    } else {
                        if (loadingList) {
                            // turn flag off for loading a new page
                            loadingList.pageLoading = false;

                            // delete loading skeletons
                            vboTmBoardUnsetSkeletons(areaId);
                        }

                        if ((obj_res['html'] + '').indexOf('data-no-results="1"') < 0 || !tasksList.find('[data-no-results="1"]').length) {
                            // append the loading result of the next tasks by preventing multiple "no-results" elements due to infinite scroll loading
                            tasksList.append(obj_res['html']);
                        }
                    }

                    // dispatch event for TM contents loaded
                    VBOCore.emitEvent('vbo-tm-contents-loaded', {
                        element: tasksList,
                    });
                } catch (err) {
                    console.error('Error decoding the response', err, resp);
                }
            },
            (error) => {
                // display error message
                alert(error.responseText);

                if (loadingList) {
                    // turn flag off for loading a new page
                    loadingList.pageLoading = false;
                }
            }
        );
    };

    /**
     * Register function to set some task loading skeletons.
     */
    const vboTmBoardSetSkeletons = (areaId, quantity) => {
        let container = document.querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]');
        let tasksList = container ? container.querySelector('.vbo-tm-board-area-tasks-list') : null;

        if (!tasksList) {
            return;
        }

        // ensure the quantity is a valid number greater than zero
        quantity = !isNaN(quantity) && quantity > 0 ? quantity : 1;

        // build skeleton HTML string
        let skeletonHtml = '';
        skeletonHtml += '<div class="vbo-tm-board-area-task-wrap vbo-tm-board-area-task-wrap-skeleton">';
        skeletonHtml += '    <div class="vbo-tm-board-area-task-head">';
        skeletonHtml += '        <div class="vbo-skeleton-loading vbo-tm-board-area-task-title"></div>';
        skeletonHtml += '        <div class="vbo-tm-board-area-task-notes">';
        skeletonHtml += '            <div class="vbo-skeleton-loading vbo-tm-board-area-task-notes-line"></div>';
        skeletonHtml += '            <div class="vbo-skeleton-loading vbo-tm-board-area-task-notes-line"></div>';
        skeletonHtml += '        </div>';
        skeletonHtml += '    </div>';
        skeletonHtml += '    <div class="vbo-tm-board-area-task-body">';
        skeletonHtml += '        <div class="vbo-tm-board-area-task-status">';
        skeletonHtml += '            <span class="vbo-skeleton-loading vbo-tm-board-area-task-status-badge"></span>';
        skeletonHtml += '        </div>';
        skeletonHtml += '        <div class="vbo-tm-board-area-task-assignees">';
        skeletonHtml += '            <span class="vbo-skeleton-loading vbo-tm-board-area-task-assignee"></span>';
        skeletonHtml += '            <span class="vbo-skeleton-loading vbo-tm-board-area-task-assignee"></span>';
        skeletonHtml += '        </div>';
        skeletonHtml += '    </div>';
        skeletonHtml += '</div>';

        for (let i = 1; i <= quantity; i++) {
            tasksList.insertAdjacentHTML('beforeend', skeletonHtml);
        }
    };

    /**
     * Register function to unset the task loading skeletons.
     */
    const vboTmBoardUnsetSkeletons = (areaId) => {
        let container = document.querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]');
        let tasksList = container ? container.querySelector('.vbo-tm-board-area-tasks-list') : null;

        if (!tasksList) {
            return;
        }

        tasksList
            .querySelectorAll('.vbo-tm-board-area-task-wrap.vbo-tm-board-area-task-wrap-skeleton')
            .forEach((skeleton) => {
                skeleton.remove();
            });
    };

    /**
     * Register function for hiding a task area.
     */
    const vboTmBoardHideArea = (areaId) => {
        // remove element blocks from DOM
        document
            .querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
            .remove();

        // silently update task area status
        vboTmToggleAreaDisplay(areaId, 0);
    };

    /**
     * Register function for hiding the given area tasks.
     */
    const vboTmBoardHideAreaTasks = (areaId) => {
        // remove tasks list element from DOM
        let list = document
            .querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
            .querySelector('.vbo-tm-board-area-tasks-list');

        if (list) {
            list.remove();
        }
    };

    /**
     * Register function for setting up the infinite scroll loading for an area.
     */
    const vboTmBoardSetupInfiniteScroll = (areaId, start, count) => {
        let tasksList = document
            .querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
            .querySelector('.vbo-tm-board-area-tasks-list');

        if (!tasksList) {
            return;
        }

        if (count < 10) {
            // no extra loading needed
            tasksList
                .removeEventListener('scroll', vboTmBoardHandleInfiniteScroll);
            return;
        }

        // get wrapper dimensions
        let listViewHeight = tasksList.offsetHeight;
        let listGlobHeight = tasksList.scrollHeight;

        if (listViewHeight >= listGlobHeight) {
            // no scrolling detected
            return;
        }

        // inject/update custom object properties
        tasksList.areaId = areaId;
        tasksList.offsetStart = start;

        if (!start && !tasksList.scrollListener) {
            // inject custom object property to identify the scroll event listener
            tasksList.scrollListener = true;

            // register infinite scroll event handler
            tasksList
                .addEventListener('scroll', vboTmBoardHandleInfiniteScroll);
        }
    };

    /**
     * Register function for infinite scroll loading handler.
     */
    const vboTmBoardHandleInfiniteScroll = (e) => {
        // access the injected area ID property
        let areaId = e.currentTarget.areaId;

        if (!areaId) {
            return;
        }

        // access the involved tasks list
        let tasksList = document
            .querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')
            .querySelector('.vbo-tm-board-area-tasks-list');

        if (!tasksList) {
            return;
        }

        // make sure the loading of a next page isn't running
        if (tasksList.pageLoading) {
            // abort
            return;
        }

        // register throttling callback
        VBOCore.throttleTimer(() => {
            // get wrapper dimensions
            let listViewHeight = tasksList.offsetHeight;
            let listGlobHeight = tasksList.scrollHeight;
            let listScrollTop  = tasksList.scrollTop;

            if (!listScrollTop || listViewHeight >= listGlobHeight) {
                // no scrolling detected at all
                return;
            }

            // calculate missing distance to the end of the list
            let listEndDistance = listGlobHeight - (listViewHeight + listScrollTop);

            if (listEndDistance < 140) {
                // inject custom property to identify a next page is loading
                tasksList.pageLoading = true;

                // load the next page of notifications
                vboTmBoardRenderAreaTasks(areaId, (vboTmFilters || {}), (tasksList.offsetStart || 0) + 10);
            }
        }, 100);
    };

    jQuery(function() {

        /**
         * Load all area tasks upon page loading.
         */
        document.querySelectorAll('.vbo-tm-board-area-container').forEach((area) => {
            let areaId = area.getAttribute('data-area-id');
            // render area tasks
            vboTmBoardRenderAreaTasks(areaId);
        });

        /**
         * Register to the event for rendering a task area.
         */
        document.addEventListener('vbo-tm-area-render-task', (e) => {
            if (!e || !e?.detail?.areaId) {
                return;
            }

            let areaId = e.detail.areaId;

            vboTmBoardRenderArea(areaId);
        });

        /**
         * Register to the event for hiding a task area.
         */
        document.addEventListener('vbo-tm-area-hide-task', (e) => {
            if (!e || !e?.detail?.areaId) {
                return;
            }

            let areaId = e.detail.areaId;

            vboTmBoardHideArea(areaId);
        });

        /**
         * Register listener for the filters changed event.
         */
        document.addEventListener('vbo-tm-filters-changed', (e) => {
            // obtain the global filters
            let filters = e?.detail?.filters;

            // iterate over all the visible areas
            document.querySelectorAll('.vbo-tm-board-area-container').forEach((area) => {
                // get area ID
                let areaId = area.getAttribute('data-area-id');

                // hide the area tasks
                vboTmBoardHideAreaTasks(areaId);

                // re-render area tasks
                vboTmBoardRenderAreaTasks(areaId, filters);
            });
        });

        /**
         * Register listener for the tasks loaded event to configure the infinite scroll loading.
         */
        document.addEventListener('vbo-tm-board-tasks-loaded', (e) => {
            let areaId = e?.detail?.areaId || 0;
            let start  = e?.detail?.start || 0;
            let count  = e?.detail?.count || 0;

            if (document.querySelector('.vbo-tm-board-area-container[data-area-id="' + areaId + '"]')) {
                // setup infinite scroll loading for the current area
                vboTmBoardSetupInfiniteScroll(areaId, start, count);
            }
        });

    });
</script>