UI: User Avatar pills for Assigned tickets, updated Icons for Ticket Mine and Unusassigned tickets

This commit is contained in:
johnnyq
2026-09-29 18:02:05 -04:00
parent b15ef69bf4
commit 1666305a1c
11 changed files with 44 additions and 77 deletions

View File

@@ -1156,16 +1156,7 @@ if (isset($_GET['asset_id'])) {
} else {
$ticket_priority_display = "-";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status_id == 5) {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !in_array($ticket_status_id, [4, 5]));
?>

View File

@@ -118,7 +118,7 @@ if (isset($_GET['contact_id'])) {
// Related Tickets Query - 1 to 1 relationship
$sql_related_tickets = mysqli_query($mysqli, "SELECT ticket_assigned_to, ticket_closed_at, ticket_created_at, ticket_id, ticket_number,
ticket_prefix, ticket_priority, ticket_status, ticket_status_color, ticket_status_name,
ticket_subject, ticket_updated_at, user_name FROM tickets
ticket_subject, ticket_updated_at, user_avatar, user_name FROM tickets
LEFT JOIN users ON ticket_assigned_to = user_id
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
WHERE ticket_contact_id = $contact_id ORDER BY ticket_id DESC");
@@ -885,16 +885,7 @@ if (isset($_GET['contact_id'])) {
} else {
$ticket_priority_display = "-";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status == "Closed") {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !in_array(intval($row['ticket_status']), [4, 5]));
?>

View File

@@ -687,16 +687,7 @@ ob_start();
} else {
$ticket_priority_display = "-";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status_id == 5) {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !in_array($ticket_status_id, [4, 5]));
?>

View File

@@ -99,7 +99,7 @@ $credential_count = mysqli_num_rows($sql_related_credentials);
// Related Tickets Query - 1 to 1 relationship
$sql_related_tickets = mysqli_query($mysqli, "SELECT ticket_assigned_to, ticket_created_at, ticket_id, ticket_number, ticket_prefix,
ticket_priority, ticket_status, ticket_status_color, ticket_status_name, ticket_subject,
ticket_updated_at, user_name FROM tickets
ticket_updated_at, user_avatar, user_name FROM tickets
LEFT JOIN users ON ticket_assigned_to = user_id
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
WHERE ticket_contact_id = $contact_id
@@ -658,16 +658,7 @@ ob_start();
$ticket_priority_display = "-";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status == "Closed") {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !in_array(intval($row['ticket_status']), [4, 5]));
?>
<tr>
<td>

View File

@@ -87,7 +87,7 @@ if (isset($_GET['project_id'])) {
$sql_tickets = mysqli_query($mysqli, "SELECT client_id, client_name, ticket_assigned_to, ticket_billable, ticket_closed_at,
ticket_created_at, ticket_id, ticket_number, ticket_prefix, ticket_priority,
ticket_project_id, ticket_status, ticket_status_color, ticket_status_name, ticket_subject,
ticket_updated_at, user_name FROM tickets
ticket_updated_at, user_avatar, user_name FROM tickets
LEFT JOIN ticket_statuses ON ticket_status = ticket_status_id
LEFT JOIN clients ON ticket_client_id = client_id
LEFT JOIN users ON ticket_assigned_to = user_id
@@ -423,16 +423,7 @@ if (isset($_GET['project_id'])) {
$ticket_priority_display = "-";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
if ($ticket_status == 5) {
$ticket_assigned_to_display = "<p>Not Assigned</p>";
} else {
$ticket_assigned_to_display = "<p class='text-danger'>Not Assigned</p>";
}
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], !in_array($ticket_status, [4, 5]));
$project_id = intval($row['ticket_project_id']);

View File

@@ -295,7 +295,7 @@ $num_rows = mysqli_fetch_row(mysqli_query($mysqli, "SELECT FOUND_ROWS()"));
}
$recurring_ticket_category = escapeHtml($row['category_name']) ?: '-';
$recurring_ticket_client_name = escapeHtml($row['client_name']);
$assigned_to = escapeHtml($row['user_name']) ?: '-';
$assigned_to = formatAssignee($row['recurring_ticket_assigned_to'], $row['user_name'], $row['user_avatar'], false);
$recurring_ticket_template_name = escapeHtml($row['ticket_template_name']);
$recurring_ticket_task_count = intval($row['recurring_ticket_task_count']);
?>

View File

@@ -130,12 +130,7 @@ if (isset($_GET['ticket_id'])) {
$ticket_is_open = !$ticket_is_resolved && !$ticket_is_closed;
$can_edit_ticket = lookupUserPermission("module_support") >= 2;
$ticket_assigned_to = intval($ticket['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
$ticket_assigned_to_display = "<span class='text-danger'><i class='fas fa-fw fa-user-slash me-1'></i>Unassigned</span>";
} else {
$ticket_assigned_to_display = escapeHtml($ticket['user_name']);
}
$ticket_assigned_to_display = formatAssignee($ticket['ticket_assigned_to'], $ticket['user_name'], $ticket['user_avatar'], $ticket_is_open);
// Tab Title // No Sanitizing needed
$page_title = $ticket['ticket_subject'];
@@ -604,7 +599,7 @@ if (isset($_GET['ticket_id'])) {
<div class="ticket-field-label">Assigned to</div>
<div class="ticket-field-value">
<?php if ($can_edit_ticket && !$ticket_is_closed) { ?>
<a class="ajax-modal" href="#" data-modal-url="modals/ticket/ticket_assign.php?id=<?= $ticket_id ?>" title="Change assignment"><?= $ticket_assigned_to_display ?></a>
<a class="ajax-modal text-decoration-none" href="#" data-modal-url="modals/ticket/ticket_assign.php?id=<?= $ticket_id ?>" title="Change assignment"><?= $ticket_assigned_to_display ?></a>
<?php } else { ?>
<?= $ticket_assigned_to_display ?>
<?php } ?>

View File

@@ -189,13 +189,7 @@ $kanban = array_values($statuses);
<?php } ?>
<div class="kanban-card-footer">
<span title="Assigned to">
<?php if (!empty($item['user_name'])) { ?>
<i class="fas fa-fw fa-user me-1"></i><?= escapeHtml($item['user_name']) ?>
<?php } else { ?>
<i class="fas fa-fw fa-user-slash me-1 text-danger"></i><span class="text-danger">Unassigned</span>
<?php } ?>
</span>
<span title="Assigned to"><?= formatAssignee($item['ticket_assigned_to'], $item['user_name'], $item['user_avatar'], empty($item['ticket_resolved_at']) && empty($item['ticket_closed_at'])) ?></span>
<span title="Created <?= escapeHtml($item['ticket_created_at']) ?>"><?= timeAgo($item['ticket_created_at']) ?></span>
</div>

View File

@@ -256,12 +256,7 @@ if ($tickets) {
$ticket_priority_color = "info";
}
$ticket_assigned_to = intval($row['ticket_assigned_to']);
if (empty($ticket_assigned_to)) {
$ticket_assigned_to_display = $ticket_is_open ? "<span class='text-muted'>Unassigned</span>" : "<span>Unassigned</span>";
} else {
$ticket_assigned_to_display = escapeHtml($row['user_name']);
}
$ticket_assigned_to_display = formatAssignee($row['ticket_assigned_to'], $row['user_name'], $row['user_avatar'], $ticket_is_open);
if (empty($contact_name)) {
$contact_display = "<span class='text-muted'>-</span>";

View File

@@ -267,7 +267,7 @@ $ticket_select_columns =
ticket_response_sla_alert_stage, ticket_resolution_sla_alert_stage,
ticket_status_id, ticket_status_name, ticket_status_color, ticket_status_pauses_sla,
client_name, contact_id, contact_name, contact_email,
user_name, asset_name, category_name";
user_name, user_avatar, asset_name, category_name";
$ticket_joins =
"LEFT JOIN clients ON ticket_client_id = client_id
@@ -381,11 +381,11 @@ if ($date_filter_active) {
<div class="btn-group float-sm-end">
<a href="<?= $ticket_assigned_filter_id === intval($session_user_id) ? ticketsFilterUrl(['assigned' => null]) : ticketsFilterUrl(['assigned' => $session_user_id]) ?>"
class="btn <?= $ticket_assigned_filter_id === intval($session_user_id) ? 'btn-primary' : 'btn-outline-primary' ?>">
<i class="fas fa-fw fa-user"></i><span class="d-none d-xl-inline ms-2">Mine</span> | <strong><?= $user_active_assigned_tickets ?></strong>
<i class="fas fa-fw fa-user-check"></i><span class="d-none d-xl-inline ms-2">Mine</span> | <strong><?= $user_active_assigned_tickets ?></strong>
</a>
<a href="<?= $ticket_assigned_filter_id === 0 ? ticketsFilterUrl(['assigned' => null]) : ticketsFilterUrl(['assigned' => 'unassigned']) ?>"
class="btn <?= $ticket_assigned_filter_id === 0 ? 'btn-danger' : 'btn-outline-danger' ?>">
<i class="fas fa-fw fa-exclamation-triangle"></i><span class="d-none d-xl-inline ms-2">Unassigned</span> | <strong><?= $total_tickets_unassigned ?></strong>
<i class="fas fa-fw fa-user-slash"></i><span class="d-none d-xl-inline ms-2">Unassigned</span> | <strong><?= $total_tickets_unassigned ?></strong>
</a>
<a href="<?= ticketsFilterUrl(['view' => $view == 'kanban' ? 'list' : 'kanban']) ?>" class="btn btn-outline-dark ms-2" title="Switch to the <?= $view == 'kanban' ? 'list' : 'kanban' ?> view">
<i class="fa fa-fw <?= $view == 'kanban' ? 'fa-list' : 'fa-columns' ?>"></i>

View File

@@ -225,6 +225,34 @@ function formatAddress($address, $city, $state, $zip, $country = '', $separator
}
/**
* The agent a ticket is assigned to, as a pill: their avatar (or their initials
* on a grey circle) then their name. Unassigned keeps the same shape with a
* user-slash circle - red while the ticket still needs someone, muted once it
* doesn't. Takes the RAW user columns and escapes them itself.
* Bootstrap 5.3 utilities and Font Awesome only - no custom CSS.
*/
function formatAssignee($user_id, $user_name, $user_avatar, $highlight_unassigned = true) {
$pill = "badge rounded-pill d-inline-flex align-items-center align-middle gap-1 p-1 pe-2";
$user_id = intval($user_id);
if (!$user_id) {
$tone = $highlight_unassigned ? 'danger' : 'secondary';
$colors = $highlight_unassigned ? 'text-danger-emphasis bg-danger-subtle' : 'text-body-secondary bg-body-secondary';
return "<span class='$pill $colors'><span class='position-relative d-inline-flex' aria-hidden='true'><i class='fas fa-circle fs-5 text-$tone'></i><i class='fas fa-user-slash position-absolute top-50 start-50 translate-middle small text-white'></i></span>Unassigned</span>";
}
$user_avatar = escapeHtml($user_avatar);
if ($user_avatar) {
$avatar = "<img src='/uploads/users/$user_id/$user_avatar' alt='' width='20' height='20' class='rounded-circle object-fit-cover'>";
} else {
$avatar = "<span class='position-relative d-inline-flex' aria-hidden='true'><i class='fas fa-circle fs-5 text-secondary'></i><span class='position-absolute top-50 start-50 translate-middle small text-white'><small>" . escapeHtml(initials($user_name)) . "</small></span></span>";
}
return "<span class='$pill text-body-emphasis bg-body-secondary'>$avatar" . escapeHtml($user_name) . "</span>";
}
function timeAgo($datetime) {
if (is_null($datetime)) {
return "-";