mirror of
https://github.com/itflow-org/itflow
synced 2026-11-11 15:07:24 +00:00
419 lines
28 KiB
PHP
419 lines
28 KiB
PHP
<?php
|
|
|
|
/*
|
|
* Recurring tickets - the schedules that raise tickets by themselves: the
|
|
* monthly maintenance visit, the quarterly backup test. Each row says when
|
|
* it runs next (and whether that was missed), how often, what it raised
|
|
* last and how that one stands, and who gets it. The pills along the top
|
|
* pick out the ones to look at: due this week, missed, paused, and nobody's.
|
|
*
|
|
* Old links still work: ?assigned=<agent> from the "assigned to you"
|
|
* notifications, as well as ?assigned_agent=.
|
|
*/
|
|
|
|
// Default Column Sortby Filter
|
|
$sort = "recurring_ticket_next_run";
|
|
$order = "ASC";
|
|
|
|
// If client_id is in URI then show client Side Bar and client header
|
|
if (isset($_GET['client_id'])) {
|
|
require_once "includes/inc_all_client.php";
|
|
$client_query = "AND recurring_ticket_client_id = $client_id";
|
|
$client_url = "client_id=$client_id&";
|
|
} else {
|
|
require_once "includes/inc_all.php";
|
|
$client_query = '';
|
|
$client_url = '';
|
|
}
|
|
|
|
// Perms
|
|
enforceUserPermission('module_support');
|
|
|
|
$can_edit = ticketCan(2);
|
|
$can_delete = ticketCan(3);
|
|
|
|
if (!isset($_GET['assigned_agent']) && isset($_GET['assigned'])) {
|
|
$_GET['assigned_agent'] = $_GET['assigned'];
|
|
}
|
|
unset($_GET['assigned']);
|
|
|
|
// Client (all clients)
|
|
$client = 0;
|
|
if (!$client_url && !empty($_GET['client'])) {
|
|
$client = intval($_GET['client']);
|
|
$client_query = "AND recurring_ticket_client_id = $client";
|
|
}
|
|
|
|
// Category
|
|
$category_filter = intval($_GET['category'] ?? 0);
|
|
$category_query = $category_filter ? "AND recurring_ticket_category = $category_filter" : '';
|
|
|
|
// Agent
|
|
$agent_filter = intval($_GET['assigned_agent'] ?? 0);
|
|
$agent_query = $agent_filter ? "AND recurring_ticket_assigned_to = $agent_filter" : '';
|
|
|
|
// Billable - 1 or 0, or any
|
|
$billable_filter = isset($_GET['billable']) && in_array((string) $_GET['billable'], ['0', '1'], true) ? intval($_GET['billable']) : null;
|
|
$billable_query = $billable_filter === null ? '' : "AND recurring_ticket_billable = $billable_filter";
|
|
|
|
$filters = "(recurring_ticket_subject LIKE '%$q%' OR category_name LIKE '%$q%' OR client_name LIKE '%$q%' OR contact_name LIKE '%$q%' OR asset_name LIKE '%$q%')
|
|
" . clientScopeSql('recurring_ticket_client_id') . " $client_query $category_query $agent_query $billable_query";
|
|
|
|
// Which ones - the pills along the top
|
|
$running_sql = 'recurring_ticket_paused_at IS NULL';
|
|
$shows = [
|
|
'' => ['label' => 'All', 'where' => ''],
|
|
'week' => ['label' => 'Due this week', 'icon' => 'fa-calendar-week', 'where' => "$running_sql AND recurring_ticket_next_run BETWEEN CURDATE() AND CURDATE() + INTERVAL 7 DAY",
|
|
'title' => 'Raising a ticket in the next seven days'],
|
|
'missed' => ['label' => 'Missed', 'icon' => 'fa-exclamation-triangle', 'where' => "$running_sql AND recurring_ticket_next_run < CURDATE()",
|
|
'title' => 'Due before today and not raised - the nightly cron raises them on its next run', 'alarm' => 'danger'],
|
|
'paused' => ['label' => 'Paused', 'icon' => 'fa-pause', 'where' => 'recurring_ticket_paused_at IS NOT NULL', 'title' => 'Kept, raising nothing until resumed'],
|
|
'unassigned' => ['label' => 'Unassigned', 'icon' => 'fa-user-slash', 'where' => 'recurring_ticket_assigned_to = 0', 'title' => 'Raises tickets nobody has', 'alarm' => 'warning'],
|
|
];
|
|
$show = isset($shows[$_GET['show'] ?? '']) ? ($_GET['show'] ?? '') : '';
|
|
$show_query = $shows[$show]['where'] ? 'AND ' . $shows[$show]['where'] : '';
|
|
|
|
$joins = "LEFT JOIN clients ON recurring_ticket_client_id = client_id
|
|
LEFT JOIN contacts ON recurring_ticket_contact_id = contact_id
|
|
LEFT JOIN assets ON recurring_ticket_asset_id = asset_id
|
|
LEFT JOIN categories ON category_id = recurring_ticket_category
|
|
LEFT JOIN users ON user_id = recurring_ticket_assigned_to
|
|
LEFT JOIN ticket_templates ON ticket_template_id = recurring_ticket_ticket_template_id";
|
|
|
|
// Only these sort - an unknown column would take the page down
|
|
$frequency_days_sql = "CASE recurring_ticket_frequency WHEN 'Three Days' THEN 3 WHEN 'Weekly' THEN 7 WHEN 'Biweekly' THEN 14 WHEN 'Monthly' THEN 30 WHEN 'Quarterly' THEN 91 WHEN 'Biannually' THEN 182 WHEN 'Annually' THEN 365 ELSE 999 END";
|
|
$sortable_columns = [
|
|
'recurring_ticket_next_run' => "recurring_ticket_paused_at IS NOT NULL, recurring_ticket_next_run $order",
|
|
'recurring_ticket_subject' => "recurring_ticket_subject $order",
|
|
'recurring_ticket_frequency' => "$frequency_days_sql $order",
|
|
'recurring_ticket_priority' => "CASE recurring_ticket_priority WHEN 'Urgent' THEN 0 WHEN 'High' THEN 1 WHEN 'Medium' THEN 2 WHEN 'Low' THEN 3 ELSE 4 END $order",
|
|
'category_name' => "category_name $order",
|
|
'user_name' => "recurring_ticket_assigned_to = 0 $order, user_name $order",
|
|
'client_name' => "client_name $order",
|
|
];
|
|
if (!isset($sortable_columns[$sort])) {
|
|
$sort = 'recurring_ticket_next_run';
|
|
}
|
|
|
|
$sql = mysqli_query($mysqli, "SELECT SQL_CALC_FOUND_ROWS recurring_tickets.*, client_name, contact_name, contact_photo, asset_name,
|
|
category_name, category_color, category_icon, user_name, user_avatar, ticket_template_name,
|
|
(SELECT COUNT(*) FROM recurring_ticket_tasks WHERE recurring_ticket_task_recurring_ticket_id = recurring_ticket_id) AS recurring_ticket_task_count
|
|
FROM recurring_tickets $joins
|
|
WHERE $filters $show_query
|
|
ORDER BY {$sortable_columns[$sort]}, recurring_ticket_subject ASC
|
|
LIMIT $record_from, $record_to");
|
|
|
|
$num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
|
|
|
|
$recurring_tickets = [];
|
|
while ($row = mysqli_fetch_assoc($sql)) {
|
|
$recurring_tickets[intval($row['recurring_ticket_id'])] = $row;
|
|
}
|
|
$last_tickets = recurringTicketLastTickets(array_keys($recurring_tickets));
|
|
|
|
// How many of each, for the pills - over what the filters leave
|
|
$count_sql = [];
|
|
foreach ($shows as $key => $s) {
|
|
$count_sql[] = $key === '' ? 'COUNT(*)' : "COALESCE(SUM({$s['where']}), 0)";
|
|
}
|
|
$row = mysqli_fetch_row(mysqli_query($mysqli, "SELECT " . implode(', ', $count_sql) . " FROM recurring_tickets $joins WHERE $filters"));
|
|
$counts = [];
|
|
foreach (array_keys($shows) as $i => $key) {
|
|
$counts[$key] = intval($row[$i]);
|
|
}
|
|
|
|
$filter_url = function ($changes) {
|
|
$params = array_merge($_GET, $changes);
|
|
unset($params['page']);
|
|
return '?' . http_build_query(array_filter($params, function ($v) { return $v !== '' && $v !== null; }));
|
|
};
|
|
|
|
$sort_link = function ($column, $label) use ($url_query_strings_sort, $sort, $disp, $order_icon) {
|
|
return '<a class="text-reset text-decoration-none" href="?' . escapeHtml($url_query_strings_sort) . '&sort=' . $column . '&order=' . $disp . '">'
|
|
. $label . ($sort === $column ? " $order_icon" : '') . '</a>';
|
|
};
|
|
|
|
?>
|
|
|
|
<div class="card">
|
|
<div class="card-header bg-dark py-2">
|
|
<h3 class="card-title mt-2"><i class="fas fa-fw fa-redo-alt me-2"></i>Recurring Tickets</h3>
|
|
<div class="card-tools">
|
|
<div class="btn-group">
|
|
<?php if ($can_edit) { ?>
|
|
<button type="button" class="btn btn-primary ajax-modal" data-modal-url="modals/recurring_ticket/recurring_ticket_add.php?<?= $client_url ?>" data-modal-size="lg">
|
|
<i class="fas fa-plus me-2"></i>New Recurring Ticket
|
|
</button>
|
|
<?php } ?>
|
|
<button type="button" class="btn btn-primary dropdown-toggle <?= $can_edit ? 'dropdown-toggle-split' : '' ?>" data-bs-toggle="dropdown"><?= $can_edit ? '' : '<i class="fas fa-ellipsis-h"></i>' ?></button>
|
|
<div class="dropdown-menu dropdown-menu-end">
|
|
<a class="dropdown-item" href="tickets.php<?= $client_url ? '?' . rtrim($client_url, '&') : '' ?>">
|
|
<i class="fa fa-fw fa-life-ring me-2"></i>Tickets
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-header py-3">
|
|
<form autocomplete="off">
|
|
<?php if ($client_url) { ?>
|
|
<input type="hidden" name="client_id" value="<?= $client_id ?>">
|
|
<?php } ?>
|
|
<input type="hidden" name="show" value="<?= escapeHtml($show) ?>">
|
|
<div class="row g-2 align-items-center">
|
|
<div class="<?= $client_url ? 'col-lg-4' : 'col-lg-3' ?> col-md-6">
|
|
<div class="input-group">
|
|
<input type="search" class="form-control" name="q" value="<?php if (isset($q)) { echo stripslashes(escapeHtml($q)); } ?>" placeholder="Search recurring tickets...">
|
|
<button class="btn btn-dark" title="Search"><i class="fa fa-search"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="col-lg-2 col-md-6">
|
|
<select class="form-select select2" name="assigned_agent" onchange="this.form.submit()">
|
|
<option value="">- Any agent -</option>
|
|
<?php
|
|
$sql_agents_filter = mysqli_query($mysqli, "SELECT user_id, user_name, user_avatar FROM users WHERE user_type = 1
|
|
AND (EXISTS (SELECT 1 FROM recurring_tickets WHERE recurring_ticket_assigned_to = user_id) OR user_id = $agent_filter) ORDER BY user_name ASC");
|
|
while ($row = mysqli_fetch_assoc($sql_agents_filter)) {
|
|
$user_id = intval($row['user_id']); ?>
|
|
<option <?= $agent_filter === $user_id ? 'selected' : '' ?> value="<?= $user_id ?>"<?= selectOptionUser($row['user_id'], $row['user_name'], $row['user_avatar'] ?? '') ?>><?= escapeHtml($row['user_name']) ?><?= $user_id === intval($session_user_id) ? ' (me)' : '' ?></option>
|
|
<?php } ?>
|
|
</select>
|
|
</div>
|
|
<div class="col-lg-2 col-md-6">
|
|
<select class="form-select select2" name="category" onchange="this.form.submit()">
|
|
<option value="">- Any category -</option>
|
|
<?= categoryFilterOptionsHtml('Ticket', $category_filter, "SELECT DISTINCT recurring_ticket_category FROM recurring_tickets WHERE recurring_ticket_category <> ''") ?>
|
|
</select>
|
|
</div>
|
|
<?php if (!$client_url) { ?>
|
|
<div class="col-lg-3 col-md-6">
|
|
<select class="form-select select2" name="client" onchange="this.form.submit()">
|
|
<option value="">- All Clients -</option>
|
|
<?php
|
|
$sql_clients_filter = mysqli_query($mysqli, "SELECT DISTINCT client_id, client_name, client_abbreviation FROM clients
|
|
JOIN recurring_tickets ON recurring_ticket_client_id = client_id
|
|
WHERE 1 = 1 " . clientScopeSql('clients.client_id') . " ORDER BY client_name ASC");
|
|
while ($row = mysqli_fetch_assoc($sql_clients_filter)) {
|
|
$client_id_filter = intval($row['client_id']); ?>
|
|
<option <?= $client === $client_id_filter ? 'selected' : '' ?> value="<?= $client_id_filter ?>"<?= selectOptionClient($row['client_id'], $row['client_name'], $row['client_abbreviation'] ?? '') ?>><?= escapeHtml($row['client_name']) ?></option>
|
|
<?php } ?>
|
|
</select>
|
|
</div>
|
|
<?php } ?>
|
|
<div class="<?= $client_url ? 'col-lg-4' : 'col-lg-2' ?> col-md-6">
|
|
<div class="d-flex justify-content-end gap-2">
|
|
<?php if ($can_edit) { ?>
|
|
<div class="dropdown" id="bulkActionButton" hidden>
|
|
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
|
<i class="fas fa-fw fa-layer-group me-2"></i>Bulk Action (<span id="selectedCount">0</span>)
|
|
</button>
|
|
<div class="dropdown-menu dropdown-menu-end">
|
|
<button class="dropdown-item confirm-link" type="submit" form="bulkActions" name="bulk_force_recurring_tickets"
|
|
data-confirm-text="A ticket is raised now from each one, and its next run moves on.">
|
|
<i class="fas fa-fw fa-paper-plane me-2"></i>Raise now
|
|
</button>
|
|
<button class="dropdown-item" type="submit" form="bulkActions" name="bulk_pause_recurring_tickets">
|
|
<i class="fas fa-fw fa-pause me-2"></i>Pause
|
|
</button>
|
|
<button class="dropdown-item" type="submit" form="bulkActions" name="bulk_resume_recurring_tickets">
|
|
<i class="fas fa-fw fa-play me-2"></i>Resume
|
|
</button>
|
|
<div class="dropdown-divider"></div>
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/recurring_ticket/recurring_ticket_bulk_agent_edit.php" data-bulk="true">
|
|
<i class="fas fa-fw fa-user-check me-2"></i>Assign
|
|
</a>
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/recurring_ticket/recurring_ticket_bulk_priority_edit.php" data-bulk="true">
|
|
<i class="fas fa-fw fa-thermometer-half me-2"></i>Set priority
|
|
</a>
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/recurring_ticket/recurring_ticket_bulk_category_edit.php" data-bulk="true">
|
|
<i class="fas fa-fw fa-tag me-2"></i>Set category
|
|
</a>
|
|
<?php if ($config_module_enable_accounting && lookupUserPermission('module_sales') >= 2) { ?>
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/recurring_ticket/recurring_ticket_bulk_billable_edit.php" data-bulk="true">
|
|
<i class="fas fa-fw fa-dollar-sign me-2"></i>Set billing
|
|
</a>
|
|
<?php } ?>
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-url="modals/recurring_ticket/recurring_ticket_bulk_next_run_edit.php" data-bulk="true">
|
|
<i class="fas fa-fw fa-calendar-day me-2"></i>Set next run
|
|
</a>
|
|
<?php if ($can_delete) { ?>
|
|
<div class="dropdown-divider"></div>
|
|
<button class="dropdown-item text-danger text-bold confirm-link" type="submit" form="bulkActions" name="bulk_delete_recurring_tickets"
|
|
data-confirm-text="The schedules go for good. Tickets they already raised stay.">
|
|
<i class="fas fa-fw fa-trash me-2"></i>Delete
|
|
</button>
|
|
<?php } ?>
|
|
</div>
|
|
</div>
|
|
<?php } ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
<!-- Which ones - counted over what the filters above leave -->
|
|
<nav class="nav nav-pills itflow-asset-tabs mt-2 flex-nowrap overflow-auto">
|
|
<?php foreach ($shows as $key => $s) {
|
|
$alarm = $counts[$key] && !empty($s['alarm']) && $show !== $key ? $s['alarm'] : ''; ?>
|
|
<a class="nav-link text-nowrap <?= $show === $key ? 'active' : '' ?>" href="<?= escapeHtml($filter_url(['show' => $key])) ?>"<?= !empty($s['title']) ? ' title="' . escapeHtml($s['title']) . '"' : '' ?>>
|
|
<?php if (!empty($s['icon'])) { ?><i class="fas fa-fw <?= $s['icon'] ?> me-1"></i><?php } ?><?= $s['label'] ?>
|
|
<span class="badge rounded-pill ms-1 <?= $alarm ? "text-bg-$alarm" : '' ?>"><?= $counts[$key] ?></span>
|
|
</a>
|
|
<?php } ?>
|
|
</nav>
|
|
</div>
|
|
|
|
<form id="bulkActions" action="post.php" method="post">
|
|
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0 itflow-asset-table itflow-ticket-list">
|
|
<thead class="<?php if (!$recurring_tickets) { echo "d-none"; } ?> text-nowrap table-light">
|
|
<tr>
|
|
<?php if ($can_edit) { ?>
|
|
<th class="checkbox-column border-end">
|
|
<div class="form-check">
|
|
<input class="form-check-input" id="selectAllCheckbox" type="checkbox" onclick="checkAll(this)" title="Select all on this page">
|
|
</div>
|
|
</th>
|
|
<?php } ?>
|
|
<th><?= $sort_link('recurring_ticket_subject', 'Recurring ticket') ?></th>
|
|
<th><?= $client_url ? 'For' : $sort_link('client_name', 'Client') ?></th>
|
|
<th><?= $sort_link('recurring_ticket_frequency', 'How often') ?></th>
|
|
<th><?= $sort_link('recurring_ticket_next_run', 'Next run') ?></th>
|
|
<th>Last ticket</th>
|
|
<th><?= $sort_link('user_name', 'Agent') ?></th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($recurring_tickets as $recurring_ticket_id => $row) {
|
|
$paused = recurringTicketPaused($row);
|
|
$last = $last_tickets[$recurring_ticket_id] ?? null;
|
|
$edit_url = "modals/recurring_ticket/recurring_ticket_edit.php?id=$recurring_ticket_id";
|
|
$task_count = intval($row['recurring_ticket_task_count']);
|
|
$next = recurringTicketNextRunText($row);
|
|
$assignee = formatAssignee($row['recurring_ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !$paused);
|
|
?>
|
|
<tr class="itflow-ticket-row <?= $next['tone'] === 'danger' ? 'is-danger' : '' ?> <?= $paused ? 'text-body-secondary' : '' ?>">
|
|
<?php if ($can_edit) { ?>
|
|
<td class="checkbox-column bg-light border-end">
|
|
<div class="form-check">
|
|
<input class="form-check-input bulk-select" type="checkbox" name="recurring_ticket_ids[]" value="<?= $recurring_ticket_id ?>" aria-label="Select <?= escapeHtml($row['recurring_ticket_subject']) ?>">
|
|
</div>
|
|
</td>
|
|
<?php } ?>
|
|
<td class="itflow-ticket-main">
|
|
<?php if ($can_edit) { ?>
|
|
<a class="fw-semibold <?= $paused ? 'text-body-secondary' : 'text-body' ?> ajax-modal itflow-ticket-subject" href="#" data-modal-size="lg" data-modal-url="<?= $edit_url ?>"><?= escapeHtml($row['recurring_ticket_subject']) ?></a>
|
|
<?php } else { ?>
|
|
<span class="fw-semibold itflow-ticket-subject"><?= escapeHtml($row['recurring_ticket_subject']) ?></span>
|
|
<?php } ?>
|
|
<div class="itflow-ticket-meta small text-body-secondary">
|
|
<?= ticketPriorityHtml($row['recurring_ticket_priority'], 'detail') ?>
|
|
<?php if ($row['category_name'] !== null && $row['category_name'] !== '') { ?>
|
|
<span class="text-nowrap"><?= categoryMarkHtml($row['category_color'], $row['category_icon'] ?? '', '<i class="fas fa-circle itflow-ticket-dot"%s aria-hidden="true"></i>') ?><?= escapeHtml($row['category_name']) ?></span>
|
|
<?php } ?>
|
|
<?php if (!empty($row['asset_name'])) { ?>
|
|
<span class="text-nowrap text-truncate itflow-ticket-asset"><i class="fas fa-desktop me-1"></i><?= escapeHtml($row['asset_name']) ?></span>
|
|
<?php } ?>
|
|
<?php if ($task_count) { ?>
|
|
<span class="text-nowrap" title="Adds <?= $task_count ?> task<?= $task_count === 1 ? '' : 's' ?>"><i class="fas fa-tasks me-1"></i><?= $task_count ?></span>
|
|
<?php } ?>
|
|
<?php if ($row['ticket_template_name']) { ?>
|
|
<span class="text-nowrap" title="Template"><i class="fas fa-cube me-1"></i><?= escapeHtml($row['ticket_template_name']) ?></span>
|
|
<?php } ?>
|
|
<?php if (intval($row['recurring_ticket_billable'])) { ?>
|
|
<span class="text-nowrap" title="Raises billable tickets"><i class="fas fa-dollar-sign"></i></span>
|
|
<?php } ?>
|
|
</div>
|
|
</td>
|
|
<td class="itflow-ticket-who-cell">
|
|
<?php if (!$client_url) { ?>
|
|
<a class="d-block text-truncate text-body fw-semibold small" href="recurring_tickets.php?client_id=<?= intval($row['recurring_ticket_client_id']) ?>"><?= escapeHtml($row['client_name']) ?></a>
|
|
<?php } ?>
|
|
<?php if (intval($row['recurring_ticket_contact_id']) && $row['contact_name'] !== null) { ?>
|
|
<div class="small text-truncate"><?= contactLink($row['recurring_ticket_client_id'], $row['recurring_ticket_contact_id'], $row['contact_name'], $row['contact_photo'] ?? '') ?></div>
|
|
<?php } elseif ($client_url) { ?>
|
|
<span class="small text-body-secondary">-</span>
|
|
<?php } ?>
|
|
</td>
|
|
<td class="text-nowrap">
|
|
<div><i class="fas fa-redo-alt text-body-secondary small me-1"></i><?= escapeHtml($row['recurring_ticket_frequency']) ?></div>
|
|
<div class="small text-body-secondary"><?= escapeHtml(recurringTicketCadenceText($row)) ?></div>
|
|
</td>
|
|
<td class="text-nowrap">
|
|
<?= recurringTicketNextRunPill($row) ?>
|
|
<?php if (!$paused) { ?>
|
|
<div class="small text-body-secondary mt-1"><?= escapeHtml(date('D M j, Y', strtotime($row['recurring_ticket_next_run']))) ?></div>
|
|
<?php } ?>
|
|
</td>
|
|
<td class="small">
|
|
<?php if ($last) { ?>
|
|
<div class="text-nowrap"><a class="font-monospace" href="<?= escapeHtml(ticketUrl($last['ticket_id'], $last['ticket_client_id'])) ?>"><?= escapeHtml(ticketReference($last)) ?></a>
|
|
<?= ticketStatusHtml($last['ticket_status_name'], $last['ticket_status_color'], 'detail') ?></div>
|
|
<div class="text-body-secondary" title="<?= escapeHtml($last['ticket_created_at']) ?>"><?= timeAgo($last['ticket_created_at']) ?></div>
|
|
<?php } else { ?>
|
|
<span class="text-body-secondary">None yet</span>
|
|
<?php } ?>
|
|
</td>
|
|
<td class="text-nowrap">
|
|
<?= $can_edit ? quickPick('recurring_assignee', $recurring_ticket_id, (string) intval($row['recurring_ticket_assigned_to']), $assignee) : $assignee ?>
|
|
</td>
|
|
<td class="text-end">
|
|
<?php if ($can_edit) { ?>
|
|
<div class="dropdown dropstart">
|
|
<button class="btn btn-sm btn-light" title="More actions" type="button" data-bs-toggle="dropdown"><i class="fas fa-fw fa-ellipsis-v"></i></button>
|
|
<div class="dropdown-menu">
|
|
<a class="dropdown-item ajax-modal" href="#" data-modal-size="lg" data-modal-url="<?= $edit_url ?>">
|
|
<i class="fas fa-fw fa-edit text-secondary me-2"></i>Edit
|
|
</a>
|
|
<a class="dropdown-item confirm-link" href="post.php?force_recurring_ticket=<?= $recurring_ticket_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>"
|
|
data-confirm-text="A ticket is raised now, and the next run moves on a step - this takes its place.">
|
|
<i class="fas fa-fw fa-paper-plane text-secondary me-2"></i>Raise now
|
|
</a>
|
|
<?php if ($paused) { ?>
|
|
<a class="dropdown-item" href="post.php?resume_recurring_ticket=<?= $recurring_ticket_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
|
<i class="fas fa-fw fa-play text-secondary me-2"></i>Resume
|
|
</a>
|
|
<?php } else { ?>
|
|
<a class="dropdown-item" href="post.php?pause_recurring_ticket=<?= $recurring_ticket_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>">
|
|
<i class="fas fa-fw fa-pause text-secondary me-2"></i>Pause
|
|
</a>
|
|
<?php } ?>
|
|
<?php if ($can_delete) { ?>
|
|
<div class="dropdown-divider"></div>
|
|
<a class="dropdown-item text-danger text-bold confirm-link" href="post.php?delete_recurring_ticket=<?= $recurring_ticket_id ?>&csrf_token=<?= $_SESSION['csrf_token'] ?>"
|
|
data-confirm-text="The schedule goes for good. Tickets it already raised stay.">
|
|
<i class="fas fa-fw fa-trash me-2"></i>Delete
|
|
</a>
|
|
<?php } ?>
|
|
</div>
|
|
</div>
|
|
<?php } ?>
|
|
</td>
|
|
</tr>
|
|
<?php } ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</form>
|
|
<?php
|
|
$empty_state = [
|
|
'thing' => 'recurring tickets',
|
|
'icon' => 'fa-redo-alt',
|
|
'filtered' => $q !== '' || $category_filter || $agent_filter || $client || $billable_filter !== null,
|
|
'views' => ['show'],
|
|
'view' => emptyStatePill($shows, $show, '', ['missed', 'unassigned']),
|
|
'text' => 'A monthly maintenance visit or a quarterly backup test is a good first one.',
|
|
'actions' => $can_edit ? [['label' => 'New Recurring Ticket', 'modal' => "modals/recurring_ticket/recurring_ticket_add.php?$client_url", 'size' => 'lg']] : [],
|
|
];
|
|
require_once '../includes/filter_footer.php';
|
|
?>
|
|
</div>
|
|
|
|
<script src="<?= staticFileUrl('/js/bulk_actions.js') ?>"></script>
|
|
|
|
<?php
|
|
require_once "../includes/footer.php";
|