File "month.php"

Full Path: /home/romayxjt/public_html/wp-content/plugins/vikbooking/admin/layouts/taskmanager/calendar/month.php
File size: 22.54 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!');

/**
 * Obtain vars from arguments received in the layout file.
 * 
 * @var array  $data  The data for rendering the tasks of a given month within a calendar.
 */
extract($displayData);

// access the task manager object
$taskManager = VBOFactory::getTaskManager();

// get the task area IDs
$areaIds = [];

if (is_array($data['area_ids'] ?? null)) {
    $areaIds = $data['area_ids'];
}

// get the filters for rendering the calendar tasks
$filters = (array) ($data['filters'] ?? []);

// determine the month to load
$fromYmd = $filters['calendar_month'] ?? date('Y-m-01');
$toYmd = date('Y-m-t', strtotime($fromYmd));
if (($filters['dates'] ?? null) && !($filters['calendar_month'] ?? null)) {
    // when no calendar month filter set, use the current dates filter
    list($fromDt, $toDt) = VBOTaskModelTask::getInstance()->getFilterDatesInterval((string) $filters['dates'], $local = true, $sql = false);
    if ($fromDt && $toDt) {
        $fromYmd = date('Y-m-01', strtotime($fromDt));
        $toYmd = date('Y-m-t', strtotime($toDt));
    }
}

// build the pool of month tasks
$monthTasks = [];

if ($areaIds) {
    // save current filters in the user state
    JFactory::getApplication()->setUserState('vbo.tm.filters', $filters);

    // load tasks according to filters, by always forcing/injecting the area IDs and the dates
    foreach (VBOTaskModelTask::getInstance()->filterItems(array_merge($filters, ['id_areas' => $areaIds, 'dates' => $fromYmd . ':' . $toYmd]), 0, 0) as $taskRecord) {
        // wrap task record into a registry
        $task = VBOTaskTaskregistry::getInstance((array) $taskRecord);

        // get task due date in Y-m-d format
        $due_date_key = $task->getDueDate($local = true, $format = 'Y-m-d');

        if ($due_date_key) {
            // start a container for this date
            $monthTasks[$due_date_key] = $monthTasks[$due_date_key] ?? [];

            // push task registry for this day
            $monthTasks[$due_date_key][] = $task;
        }
    }
}

// obtain the current month information
$monthDate = getdate(strtotime($fromYmd));
$monthIter = $monthDate;

// get navigation dates
$todayYmd   = date('Y-m-d');
$monthBack  = date('Y-m-01', strtotime('-1 month', $monthDate[0]));
$monthNext  = date('Y-m-01', strtotime('+1 month', $monthDate[0]));
$monthToday = date('Y-m-01');

// build week-day indexes
$firstwday    = (int) VikBooking::getFirstWeekDay();
$days_indexes = [];
$wday_labels  = [
    JText::translate('VBSUN'),
    JText::translate('VBMON'),
    JText::translate('VBTUE'),
    JText::translate('VBWED'),
    JText::translate('VBTHU'),
    JText::translate('VBFRI'),
    JText::translate('VBSAT'),
];

for ($d = 0; $d < 7; $d++) {
    $days_indexes[$d] = (6-($firstwday-$d)+1)%7;
}

?>

<div class="vbo-tm-calendar-wrap">
    <div class="vbo-tm-calendar-head">
        <div class="vbo-tm-calendar-info">
            <span class="vbo-tm-calendar-month-name"><?php echo VikBooking::sayMonth($monthDate['mon']); ?></span>
            <span class="vbo-tm-calendar-month-year"><?php echo $monthDate['year']; ?></span>
        </div>

        <div class="vbo-tm-calendar-nav">
            <div class="vbo-tm-calendar-nav-btns">
                <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-back" data-month="<?php echo $monthBack; ?>"><?php VikBookingIcons::e('chevron-left'); ?></span>
                <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-today" data-month="<?php echo $monthToday; ?>"><?php echo JText::translate('VBTODAY'); ?></span>
                <span class="vbo-tm-calendar-nav-btn vbo-tm-calendar-nav-next" data-month="<?php echo $monthNext; ?>"><?php VikBookingIcons::e('chevron-right'); ?></span>
            </div>
        </div>
    </div>

    <div class="vbo-tm-calendar-month-container">

        <div class="vbo-tm-calendar-month-row vbo-tm-calendar-month-weekdays">
        <?php
        // display days of week
        for ($d = 0; $d < 7; $d++) {
            $d_ind = ($d + $firstwday) < 7 ? ($d + $firstwday) : ($d + $firstwday - 7);
            ?>
            <div class="vbo-tm-calendar-month-day vbo-tm-calendar-month-weekday">
                <span><?php echo $wday_labels[$d_ind]; ?></span>
            </div>
            <?php
        }
        ?>
        </div>

        <div class="vbo-tm-calendar-month-row">
        <?php
        // start month-day counter
        $d_count = 0;

        // display the initial empty week-days, if any
        for ($i = 0, $n = $days_indexes[$monthDate['wday']]; $i < $n; $i++, $d_count++) {
            ?>
            <div class="vbo-tm-calendar-month-day vbo-tm-calendar-month-day-empty"></div>
            <?php
        }

        // loop through all dates of the current month
        while ($monthIter['mon'] == $monthDate['mon']) {
            if ($d_count > 6) {
                // week is over, reset counter and start a new row
                $d_count = 0;
                echo '</div>' . "\n" . '<div class="vbo-tm-calendar-month-row">' . "\n";
            }

            // build date key
            $date_key = date('Y-m-d', $monthIter[0]);

            ?>
            <div class="vbo-tm-calendar-month-day<?php echo $date_key == $todayYmd ? ' vbo-tm-calendar-month-today' : ''; ?>" data-date="<?php echo $date_key; ?>">
                <span class="vbo-tm-calendar-mday"><?php echo $monthIter['mday']; ?></span>
            <?php
            if ($monthTasks[$date_key] ?? []) {
                ?>
                <div class="vbo-tm-calendar-month-day-tasks">
                <?php
                foreach ($monthTasks[$date_key] as $taskIndex => $task) {
                    // get task status
                    $statusColor = '';
                    $statusName = '';
                    if ($taskManager->statusTypeExists($task->getStatus())) {
                        $status = $taskManager->getStatusTypeInstance($task->getStatus());
                        $statusColor = $status->getColor();
                        $statusName = $status->getName();
                    }

                    // get task assignee details
                    $assignees = $task->getAssigneeDetails();
                    ?>
                    <div class="vbo-tm-calendar-month-day-task vbo-tm-color <?php echo $statusColor ?: 'gray'; ?> <?php echo $statusName ? 'vbo-tooltip vbo-tooltip-top' : ''; ?>" data-task-id="<?php echo $task->getID(); ?>" data-area-id="<?php echo $task->getAreaID(); ?>" data-tooltiptext="<?php echo JHtml::fetch('esc_attr', $statusName); ?>">
                        <div class="vbo-tm-calendar-month-day-task-wrap">
                            <div class="vbo-tm-calendar-task-title">
                                <?php echo $task->getTitle(); ?>

                                <?php if ($task->get('hasUnreadMessages', false)): ?>
                                    <span class="unread-message-dot">
                                        <?php VikBookingIcons::e('comment'); ?>
                                    </span>
                                <?php endif; ?>
                            </div>
                        <?php
                        if ($assignees) {
                            ?>
                            <span class="vbo-tm-calendar-task-assignees">
                            <?php
                            foreach ($assignees as $operator) {
                                ?>
                                <span class="vbo-tm-calendar-task-assignee vbo-tm-task-assignee">
                                    <span class="vbo-tm-calendar-task-assignee-avatar vbo-tm-task-assignee-avatar" title="<?php echo JHtml::fetch('esc_attr', $operator['name']); ?>">
                                    <?php
                                    if (!empty($operator['img_uri'])) {
                                        ?>
                                        <img src="<?php echo $operator['img_uri']; ?>" alt="<?php echo JHtml::fetch('esc_attr', $operator['initials']); ?>" decoding="async" loading="lazy" />
                                        <?php
                                    } else {
                                        ?>
                                        <span><?php echo $operator['initials']; ?></span>
                                        <?php
                                    }
                                    ?>
                                    </span>
                                </span>
                                <?php
                            }
                            ?>
                            </span>
                            <?php
                        }
                        ?>
                        </div>
                    </div>
                    <?php
                    if ($taskIndex > 1 && $exceeding = (count($monthTasks[$date_key]) - ++$taskIndex)) {
                        // we display at most 3 tasks per day
                        ?>
                    <div class="vbo-tm-calendar-month-day-task vbo-tm-calendar-month-day-more" data-date="<?php echo $date_key; ?>">
                        <span><?php echo sprintf('+%d %s', $exceeding, strtolower($exceeding > 1 ? JText::translate('VBO_TASKS') : JText::translate('VBO_TASK'))); ?></span>
                    </div>
                        <?php

                        // break the execution of the tasks for this day
                        break;
                    }
                }
                ?>
                </div>
                <?php
            }
            ?>
            </div>
            <?php
            // increase month-day counter
            $d_count++;

            // go to next day
            $monthIter = getdate(mktime(0, 0, 0, $monthIter['mon'], $monthIter['mday'] + 1, $monthIter['year']));
        }

        // display the ending empty week-days, if any
        for ($i = $d_count; $i <= 6; $i++) {
            ?>
            <div class="vbo-tm-calendar-month-day vbo-tm-calendar-month-day-empty"></div>
            <?php
        }
        ?>
        </div>

    </div>
</div>

<script type="text/javascript">
    jQuery(function($) {

        let newTaskButtons = [];

        newTaskButtons.push({
            text: <?php echo json_encode(JText::translate('VBO_NEW_TASK')); ?>,
            class: 'btngroup',
            disabled: true,
        });

        vboTmAllAreas.forEach((area) => {
            // push area button
            newTaskButtons.push({
                areaId: area.id,
                class: 'vbo-context-menu-entry-secondary',
                icon: '<?php echo VikBookingIcons::i('plus-circle'); ?>',
                text: area.name,
                action: (root, event) => {
                    VBOCore.emitEvent('vbo-tm-newtask-trigger', {
                        areaId: area.id,
                        filters: Object.assign({}, vboTmFilters, {
                            calendar_type: 'day',
                            calendar_day: $(root).attr('data-date'),
                        }),
                    });
                },
            });
        });

        newTaskButtons[newTaskButtons.length - 1].separator = true;

        newTaskButtons.push({
            text: <?php echo json_encode(JText::translate('VBO_SEE_ALL')); ?>,
            class: 'vbo-context-menu-entry-secondary',
            icon: '<?php echo VikBookingIcons::i('eye'); ?>',
            visible: (root, config) => {
                return $(root).find('.vbo-tm-calendar-month-day-task').length ? true : false;
            },
            action: (root, event) => {
                VBOCore.emitEvent('vbo-tm-apply-filters', {
                    filters: {
                        calendar_type: 'day',
                        calendar_day: $(root).attr('data-date'),
                    },
                });
            }
        });

        /**
         * Register listeners for month navigation buttons.
         */
        document
            .querySelectorAll('.vbo-tm-calendar-nav-btn')
            .forEach((nav) => {
                // get the navigation month
                const month = nav.getAttribute('data-month');

                nav.addEventListener('click', () => {
                    VBOCore.emitEvent('vbo-tm-apply-filters', {
                        filters: {
                            calendar_month: month,
                        },
                    });
                });
            });

        /**
         * Register listeners for day calendar type.
         */
        document
            .querySelectorAll('.vbo-tm-calendar-month-day[data-date]')
            .forEach((day) => {
                // get the navigation month
                const ymd = day.getAttribute('data-date');

                day
                    .querySelector('.vbo-tm-calendar-mday')
                    .addEventListener('click', (event) => {
                        event.preventDefault();
                        event.stopPropagation();

                        VBOCore.emitEvent('vbo-tm-apply-filters', {
                            filters: {
                                calendar_type: 'day',
                                calendar_day: ymd,
                            },
                        });
                    });

                $(day).vboContextMenu({
                    buttons: newTaskButtons,
                    class: 'vbo-dropdown-cxmenu',
                });
            });

        /**
         * Register listeners for editing an existing task.
         */
        document
            .querySelectorAll('.vbo-tm-calendar-month-day-task')
            .forEach((taskElement) => {
                if (taskElement.clickListener) {
                    // listener added already
                    return;
                }

                // get the clicked task and area IDs
                const taskId = taskElement.getAttribute('data-task-id');
                const areaId = taskElement.getAttribute('data-area-id');
                const date = taskElement.getAttribute('data-date');

                if ((!taskId || !areaId) && !date) {
                    // missing task details
                    return;
                }

                if (!taskId && !areaId && date) {
                    // see more day tasks
                    taskElement.addEventListener('click', (event) => {
                        event.preventDefault();
                        event.stopPropagation();

                        VBOCore.emitEvent('vbo-tm-apply-filters', {
                            filters: {
                                calendar_type: 'day',
                                calendar_day: date,
                            },
                        });
                    });

                    // abort
                    return;
                }

                taskElement.addEventListener('click', (event) => {
                    event.preventDefault();
                    event.stopPropagation();

                    // define the modal delete button
                    let delete_btn = $('<button></button>')
                        .attr('type', 'button')
                        .addClass('btn btn-danger')
                        .text(<?php echo json_encode(JText::translate('VBELIMINA')); ?>)
                        .on('click', function() {
                            if (!confirm(<?php echo json_encode(JText::translate('VBDELCONFIRM')); ?>)) {
                                return false;
                            }

                            // disable button to prevent double submissions
                            let submit_btn = $(this);
                            submit_btn.prop('disabled', true);

                            // start loading animation
                            VBOCore.emitEvent('vbo-tm-edittask-loading');

                            // make the request
                            VBOCore.doAjax(
                                "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.deleteTask'); ?>",
                                {
                                    data: {
                                        id: taskId,
                                    },
                                },
                                (resp) => {
                                    // trigger filters-changed event on success
                                    VBOCore.emitEvent('vbo-tm-filters-changed', {
                                        filters: vboTmFilters,
                                    });

                                    // dismiss the modal
                                    VBOCore.emitEvent('vbo-tm-edittask-dismiss');
                                },
                                (error) => {
                                    // display error message
                                    alert(error.responseText);

                                    // re-enable submit button
                                    submit_btn.prop('disabled', false);

                                    // stop loading
                                    VBOCore.emitEvent('vbo-tm-edittask-loading');
                                }
                            );
                        });

                    // define the modal save button
                    let save_btn = $('<button></button>')
                        .attr('type', 'button')
                        .addClass('btn btn-success')
                        .text(<?php echo json_encode(JText::translate('VBSAVE')); ?>)
                        .on('click', function() {
                            // disable button to prevent double submissions
                            let submit_btn = $(this);
                            submit_btn.prop('disabled', true);

                            // start loading animation
                            VBOCore.emitEvent('vbo-tm-edittask-loading');

                            // get form data
                            const taskForm = new FormData(document.querySelector('#vbo-tm-task-manage-form'));

                            // build query parameters for the request
                            let qpRequest = new URLSearchParams(taskForm);

                            // make sure the request always includes the assignees query parameter, even if the list is empty
                            if (!qpRequest.has('data[assignees][]')) {
                                qpRequest.append('data[assignees][]', []);
                            }

                            // make sure the request always includes the tags query parameter, even if the list is empty
                            if (!qpRequest.has('data[tags][]')) {
                                qpRequest.append('data[tags][]', []);
                            }

                            // make the request
                            VBOCore.doAjax(
                                "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.updateTask'); ?>",
                                qpRequest.toString(),
                                (resp) => {
                                    // trigger filters-changed event on success
                                    VBOCore.emitEvent('vbo-tm-filters-changed', {
                                        filters: vboTmFilters,
                                    });

                                    // dismiss the modal
                                    VBOCore.emitEvent('vbo-tm-edittask-dismiss');
                                },
                                (error) => {
                                    // display error message
                                    alert(error.responseText);

                                    // re-enable submit button
                                    submit_btn.prop('disabled', false);

                                    // stop loading
                                    VBOCore.emitEvent('vbo-tm-edittask-loading');
                                }
                            );
                        });

                    // display modal
                    let modalBody = VBOCore.displayModal({
                        suffix:         'tm_edittask_modal',
                        title:          <?php echo json_encode(JText::translate('VBO_TASK')); ?> + ' #' + taskId,
                        extra_class:    'vbo-modal-rounded vbo-modal-taller vbo-modal-large',
                        body_prepend:   true,
                        lock_scroll:    true,
                        escape_dismiss: false,
                        footer_left:    delete_btn,
                        footer_right:   save_btn,
                        loading_event:  'vbo-tm-edittask-loading',
                        dismiss_event:  'vbo-tm-edittask-dismiss',
                    });

                    // start loading animation
                    VBOCore.emitEvent('vbo-tm-edittask-loading');

                    // make the request
                    VBOCore.doAjax(
                        "<?php echo VikBooking::ajaxUrl('index.php?option=com_vikbooking&task=taskmanager.renderLayout'); ?>",
                        {
                            type: 'tasks.managetask',
                            data: {
                                task_id: taskId,
                                area_id: areaId,
                                form_id: 'vbo-tm-task-manage-form',
                            },
                        },
                        (resp) => {
                            // stop loading
                            VBOCore.emitEvent('vbo-tm-edittask-loading');

                            try {
                                // decode the response (if needed), and append the content to the modal body
                                let obj_res = typeof resp === 'string' ? JSON.parse(resp) : resp;
                                modalBody.append(obj_res['html']);
                            } catch (err) {
                                console.error('Error decoding the response', err, resp);
                            }
                        },
                        (error) => {
                            // display error message
                            alert(error.responseText);

                            // stop loading
                            VBOCore.emitEvent('vbo-tm-edittask-loading');
                        }
                    );
                });

                // turn flag on for listener set
                taskElement.clickListener = true;
            });

    });
</script>