Ticket Item Changes & Ticket.string_datetime
Added string datetime and indicators for priority and departments. Also added a toggle for the indicators/departments to be shown.
This commit is contained in:
parent
4344dfb271
commit
90abffac05
@ -152,5 +152,5 @@ class TicketSerializer(DynamicModelSerializer):
|
||||
fields = (
|
||||
"uuid", "title", "description", "author", "create_timestamp",
|
||||
"edit_timestamp", "is_edited", "was_yesterday", "is_older_than_day",
|
||||
"timestamp", "priority", "tags", "short_description"
|
||||
"timestamp", "priority", "tags", "short_description", "string_datetime"
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
import logging
|
||||
import bleach
|
||||
from uuid import uuid4
|
||||
from datetime import timedelta, datetime
|
||||
@ -9,6 +10,8 @@ from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TicketPriority(models.Model):
|
||||
uuid = models.UUIDField(primary_key=True, default=uuid4, editable=False)
|
||||
@ -152,7 +155,7 @@ class Ticket(models.Model):
|
||||
|
||||
@property
|
||||
def was_yesterday(self) -> bool:
|
||||
"""_summary_
|
||||
"""Returns a boolean dependent on if `self.timestamp` is from before midnight yesterday.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@ -165,6 +168,45 @@ class Ticket(models.Model):
|
||||
|
||||
return self.timestamp < midnight_today
|
||||
|
||||
@property
|
||||
def string_datetime(self) -> str:
|
||||
"""Provides a human readable string representation of `self.timestamp` that should be displayed
|
||||
to represent the ticket's age.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The string representation of `self.timestamp`.
|
||||
"""
|
||||
|
||||
difference = timezone.now() - self.timestamp
|
||||
|
||||
days = difference.days
|
||||
hours = difference.total_seconds() // 3600
|
||||
minutes = difference.total_seconds() // 60
|
||||
seconds = difference.total_seconds()
|
||||
|
||||
hours, minutes, seconds = map(int, (hours, minutes, seconds))
|
||||
|
||||
if seconds < 60:
|
||||
value, unit = seconds, "second"
|
||||
|
||||
elif minutes < 60:
|
||||
value, unit = minutes, "minute"
|
||||
|
||||
elif hours < 24:
|
||||
value, unit = hours, "hour"
|
||||
|
||||
elif days < 7:
|
||||
value, unit = days, "day"
|
||||
|
||||
else:
|
||||
return self.timestamp.strftime("%Y-%m-%d")
|
||||
|
||||
if value > 1:
|
||||
unit += "s"
|
||||
|
||||
return f"{value} {unit} ago"
|
||||
|
||||
@property
|
||||
def timestamp(self) -> datetime:
|
||||
|
@ -113,10 +113,21 @@ body {
|
||||
.ticket-item:hover {
|
||||
background-color: var(--bs-tertiary-bg);
|
||||
}
|
||||
|
||||
/*
|
||||
.ticket-item .ticket-item-indicator {
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
writing-mode: vertical-rl;
|
||||
text-orientation: upright;
|
||||
} */
|
||||
|
||||
.ticket-item .ticket-item-complex {
|
||||
transition: all 0.3s ease;
|
||||
overflow: hidden;
|
||||
width: 2rem;
|
||||
}
|
||||
|
||||
#ticketsContainer:not(.complex-items) .ticket-item .ticket-item-complex {
|
||||
margin: 0 !important;
|
||||
width: 0 !important;
|
||||
}
|
||||
|
||||
.ticket-item .ticket-item-icon {
|
||||
|
@ -1,7 +1,8 @@
|
||||
var filters = {"order": "-edit_timestamp"};
|
||||
var filters = {"ordering": "-edit_timestamp"};
|
||||
global_loadingTickets = false;
|
||||
searchTimeout = null;
|
||||
pagination = {};
|
||||
complexItems = false;
|
||||
|
||||
$(document).ready(function() {
|
||||
initSearchBar();
|
||||
@ -220,7 +221,7 @@ function loadTicketItems(page=1) {
|
||||
filters["page"] = page;
|
||||
|
||||
var fetchFilters = { ...filters };
|
||||
fetchFilters["only_fields"] = "uuid,title,short_description,author,priority,tags,timestamp,was_yesterday,is_edited,author__forename,author__surname,author__department,author__icon";
|
||||
fetchFilters["only_fields"] = "uuid,title,short_description,author,priority,tags,timestamp,is_edited,author__forename,author__surname,author__department,author__icon,string_datetime";
|
||||
|
||||
fetchTicketsPromise(fetchFilters).then((response) => {
|
||||
// Update the counts to show how many tickets were found
|
||||
@ -256,17 +257,10 @@ function loadTicketItems(page=1) {
|
||||
// Iterate over and handle each ticket
|
||||
response.results.forEach(function(ticket) {
|
||||
|
||||
// Create a formatted version of the ticket's timestamp
|
||||
const timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime = timestampToHumanDate(timestamp, ticket.was_yesterday);
|
||||
|
||||
// Mark the ticket if it is edited
|
||||
if (ticket.is_edited) formattedTime += " • edited";
|
||||
|
||||
// Create a copy of the template using the ticket data
|
||||
var template = $($("#ticketItemTemplate").html());
|
||||
template.find(".ticket-item-author").text(`${ticket.author.forename} ${ticket.author.surname}`);
|
||||
template.find(".ticket-item-datetime").text(formattedTime);
|
||||
template.find(".ticket-item-datetime").text(ticket.string_datetime);
|
||||
template.find(".ticket-item-title").text(ticket.title);
|
||||
template.find(".ticket-item-desc").html(ticket.short_description);
|
||||
template.find(".ticket-item-icon").attr("src", ticket.author.icon);
|
||||
@ -280,16 +274,20 @@ function loadTicketItems(page=1) {
|
||||
template.find(".ticket-item-tags").append(tagTemplate);
|
||||
});
|
||||
|
||||
var priorityElem = template.find(".ticket-item-indicator");
|
||||
var priorityElem = template.find(".ticket-item-priority");
|
||||
// priorityElem.text(ticket.priority.title);
|
||||
priorityElem.css("color", ticket.priority.colour);
|
||||
priorityElem.css("background-color", ticket.priority.backgroundcolour);
|
||||
priorityElem.attr("data-title", ticket.priority.title + " Priority");
|
||||
|
||||
// Add the priority using the badge template
|
||||
var priorityTemplate = $($("#ticketContentBadgeTemplate").html());
|
||||
priorityTemplate.find(".ticket-content-badge-text").text(ticket.priority.title + " Priority");
|
||||
priorityTemplate.css({ "color": ticket.priority.colour, "background-color": ticket.priority.backgroundcolour });
|
||||
priorityTemplate.removeClass("rounded-pill").addClass("rounded-1");
|
||||
template.find(".ticket-item-priority").append(priorityTemplate);
|
||||
var departmentElem = template.find(".ticket-item-department");
|
||||
departmentElem.addClass("bgc-orange-100 c-orange-700")
|
||||
|
||||
// // Add the priority using the badge template
|
||||
// var priorityTemplate = $($("#ticketContentBadgeTemplate").html());
|
||||
// priorityTemplate.find(".ticket-content-badge-text").text(ticket.priority.title + " Priority");
|
||||
// priorityTemplate.css({ "color": ticket.priority.colour, "background-color": ticket.priority.backgroundcolour });
|
||||
// priorityTemplate.removeClass("rounded-pill").addClass("rounded-1");
|
||||
// template.find(".ticket-item-priority").append(priorityTemplate);
|
||||
|
||||
// Add the content to the interface
|
||||
$("#ticketsContainer .content").append(template);
|
||||
@ -315,17 +313,10 @@ function loadTicketContent(uuid) {
|
||||
fetchTicketsPromise({uuid: uuid}).then((response) => {
|
||||
ticket = response.results[0];
|
||||
|
||||
// Create a formatted version of the ticket's timestamp
|
||||
const timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime = timestampToHumanDate(timestamp, ticket.was_yesterday);
|
||||
|
||||
// Mark the ticket if it is edited
|
||||
if (ticket.is_edited) formattedTime += " • edited";
|
||||
|
||||
// Create a copy of the template using the ticket data
|
||||
var template = $($("#ticketContentTemplate").html());
|
||||
template.find(".ticket-content-author").text(`${ticket.author.forename} ${ticket.author.surname}`);
|
||||
template.find(".ticket-content-datetime").text(formattedTime);
|
||||
template.find(".ticket-content-datetime").text(ticket.string_datetime);
|
||||
template.find(".ticket-content-title").text(ticket.title);
|
||||
template.find(".ticket-content-desc").html(ticket.description);
|
||||
template.find(".ticket-content-icon").attr("src", ticket.author.icon);
|
||||
@ -377,4 +368,15 @@ function fetchTicketsPromise(queryFilters) {
|
||||
// Prevent certain dropdowns from closing when the user clicks.
|
||||
$(".dropdown-menu.prevent-click-close").on("click", function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
|
||||
function toggleComplexItems() {
|
||||
complexItems = !complexItems;
|
||||
|
||||
if (complexItems) {
|
||||
$("#ticketsContainer").addClass("complex-items");
|
||||
}
|
||||
else {
|
||||
$("#ticketsContainer").removeClass("complex-items");
|
||||
}
|
||||
}
|
@ -77,25 +77,6 @@
|
||||
<input type="checkbox" name="strictTags" id="strictTags" class="form-check-input" checked="checked">
|
||||
</div>
|
||||
</li>
|
||||
<!-- <li>
|
||||
<span class="dropdown-item-text">Tag Filtering</span>
|
||||
</li>
|
||||
<li>
|
||||
<label for="ticketTags-AND" class="dropdown-item">
|
||||
<div class="form-check form-check-switch">
|
||||
<span class="form-check-label">Strict</span>
|
||||
<input type="radio" name="ticketTags" id="ticketTags-AND" class="form-check-input" checked="checked" value="AND">
|
||||
</div>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="ticketTags-OR" class="dropdown-item">
|
||||
<div class="form-check form-check-switch">
|
||||
<span class="form-check-label">Loose</span>
|
||||
<input type="radio" name="ticketTags" id="ticketTags-OR" class="form-check-input" value="OR">
|
||||
</div>
|
||||
</label>
|
||||
</li> -->
|
||||
</div>
|
||||
</div>
|
||||
</h6>
|
||||
@ -167,7 +148,7 @@
|
||||
<div class="email-list h-100 layers">
|
||||
<div class="layer w-100">
|
||||
<div class="bg-body-tertiary peers ai-c p-20 fxw-nw">
|
||||
<div class="peer me-auto">
|
||||
<div class="peer me-2">
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="email-side-toggle d-n@md+ btn bg-body bdrs-2 mR-3">
|
||||
<i class="ti-menu"></i>
|
||||
@ -178,9 +159,17 @@
|
||||
<button type="button" class="btn bg-body bdrs-2 mR-3" onclick="javascript:loadTicketItems();">
|
||||
<i class="ti-reload"></i>
|
||||
</button>
|
||||
<button type="button" class="btn bg-body bdrs-2 mR-3" onclick="javascript: toggleComplexItems();">
|
||||
<i class="ti-layout-media-left"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="peer">
|
||||
<!-- <div class="peer me-2">
|
||||
<div class="btn-group" role="group">
|
||||
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="peer ms-auto">
|
||||
<div class="btn-group bg-body mX-3" role="group">
|
||||
<button type="button" id="ticketItemsPrevPage" class="btn bg-body bdrs-2" onclick="javascript: changeItemsPage(false);" disabled>
|
||||
<i class="ti-angle-left"></i>
|
||||
@ -417,30 +406,30 @@
|
||||
{% block javascripts %}
|
||||
<!-- Ticket Item Template -->
|
||||
<script id="ticketItemTemplate" type="text/template">
|
||||
<div class="ticket-item peers fxw-nw bdB" data-uuid="-1">
|
||||
<div class="peer align-self-stretch">
|
||||
<div class="ticket-item-indicator"></div>
|
||||
</div>
|
||||
<div class="peer peer-greed peers fxw-nw p-20 w-100">
|
||||
<div class="peer mR-10">
|
||||
<img src="" alt="" class="ticket-item-icon me-2">
|
||||
<div class="ticket-item fxw-nw bdB peers fxw-nw p-20 w-100" data-uuid="-1">
|
||||
<div class="ticket-item-complex peer mR-20 d-flex flex-column align-self-stretch">
|
||||
<img src="" alt="" class="ticket-item-icon">
|
||||
<div class="ticket-item-department badge rounded mt-auto mx-auto">
|
||||
<i class="fa fa-users"></i>
|
||||
</div>
|
||||
<div class="peer peer-greed ov-h">
|
||||
<div class="peers ai-c mb-2">
|
||||
<div class="peer peer-greed">
|
||||
<h6 class="ticket-item-author"></h6>
|
||||
</div>
|
||||
<div class="peer">
|
||||
<small class="ticket-item-datetime"></small>
|
||||
</div>
|
||||
<div class="ticket-item-priority badge rounded mt-2 mx-auto">
|
||||
<i class="fa fa-folder"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="peer peer-greed ov-h">
|
||||
<div class="peers ai-c mb-2">
|
||||
<div class="peer peer-greed">
|
||||
<h6 class="ticket-item-author"></h6>
|
||||
</div>
|
||||
<h5 class="ticket-item-title mb-0"></h5>
|
||||
<div class="ticket-item-desc mt-2"></div>
|
||||
<div class="peers">
|
||||
<!-- <div class="ticket-item-priority pe-5 peer peer-greed"></div> -->
|
||||
<div class="ticket-item-tags peer d-flex flex-wrap mw-100" data-bs-toggle="tooltip"></div>
|
||||
<div class="peer">
|
||||
<small class="ticket-item-datetime"></small>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="ticket-item-title mb-0"></h5>
|
||||
<div class="ticket-item-desc mt-2"></div>
|
||||
<div class="peers">
|
||||
<div class="ticket-item-tags peer d-flex flex-wrap mw-100" data-bs-toggle="tooltip"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
@ -455,8 +444,13 @@
|
||||
<img class="ticket-content-icon" src="" alt="">
|
||||
</div>
|
||||
<div class="peer">
|
||||
<small class="ticket-content-datetime"></small>
|
||||
<!-- <small class="ticket-content-datetime"></small> -->
|
||||
<h5 class="ticket-content-author mb-0"></h5>
|
||||
<div class="peers mt-2">
|
||||
<div class="peer badge bgc-orange-100 c-orange-700">
|
||||
<i class="fa fa-users"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ticket-content-badges"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user