tickets API changes & search box function
This commit is contained in:
parent
df82417790
commit
73eeedbb36
@ -1,10 +1,13 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
|
||||
from . import views
|
||||
|
||||
from .views import TicketListApiView, FilterCountApiView
|
||||
|
||||
urlpatterns = [
|
||||
path("ticket/", TicketListApiView.as_view(), name="ticket"),
|
||||
path("filter-counts/", FilterCountApiView.as_view(), name="filter-counts"),
|
||||
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
|
||||
|
||||
path("tickets/", views.TicketListApiView.as_view(), name="tickets"),
|
||||
path("filter-counts/", views.FilterCountApiView.as_view(), name="filter-counts"),
|
||||
]
|
@ -41,7 +41,11 @@ class TicketListApiView(APIView):
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
pagination_class = TicketPaginiation
|
||||
|
||||
ALLOWED_FILTERS = ("uuid__in", "priority__in", "tags__in", "author__department__in")
|
||||
ALLOWED_FILTERS = ("uuid__in", "priority__in", "tags__in", "author__department__in", "title__contains")
|
||||
MATCHER_MAP = {
|
||||
"contains": lambda k, v: {k: v[0]},
|
||||
"in": lambda k, v: {k: v}
|
||||
}
|
||||
|
||||
# @method_decorator(cache_page(60 * 5))
|
||||
def get(self, request: HttpRequest) -> Response:
|
||||
@ -68,7 +72,6 @@ class TicketListApiView(APIView):
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
def _get_tickets(self, request: HttpRequest) -> Response:
|
||||
"""Returns a response containing tickets matching the given filters.
|
||||
|
||||
@ -92,16 +95,20 @@ class TicketListApiView(APIView):
|
||||
|
||||
queryset = Ticket.objects.all()
|
||||
|
||||
for key in request.GET.keys():
|
||||
values = request.GET.getlist(key) if key.endswith("[]") else [request.GET.get(key)]
|
||||
for key, values in request.GET.lists():
|
||||
key = key.removesuffix("[]")
|
||||
|
||||
if key not in self.ALLOWED_FILTERS:
|
||||
raise KeyError(key)
|
||||
|
||||
for value in values:
|
||||
if value == "all": continue
|
||||
queryset = queryset.filter(Q(**{key: [value]}))
|
||||
if "all" in values:
|
||||
continue
|
||||
|
||||
if not key.endswith("__in"):
|
||||
values = values[0]
|
||||
|
||||
filter_kwargs = {key: values}
|
||||
queryset = queryset.filter(Q(**filter_kwargs))
|
||||
|
||||
tickets = queryset.order_by("-create_timestamp")
|
||||
serializer = TicketSerializer(tickets, many=True)
|
||||
|
@ -22,7 +22,7 @@
|
||||
"fields": {
|
||||
"title": "Security Patch Installation",
|
||||
"description": "There's a critical security patch that needs to be installed on all workstations to address recent vulnerabilities. Attempted to deploy the patch, but facing challenges on certain machines. Need high-priority assistance to ensure all systems are promptly updated for enhanced security measures.",
|
||||
"author": "291cc4c1-3bb3-4417-aaa6-748606fede77",
|
||||
"author": "4965745e-b82a-4496-80e1-055217a780b0",
|
||||
"priority": "a680328f-0680-456c-8e26-f594e05989ad",
|
||||
"create_timestamp": "2024-01-09T00:09:20Z",
|
||||
"edit_timestamp": "2024-01-09T00:09:20Z",
|
||||
@ -92,7 +92,7 @@
|
||||
"fields": {
|
||||
"title": "Hardware Malfunction",
|
||||
"description": "The printer on the third floor is not responding. Checked cables and power source, but issue persists. Need assistance to fix the hardware problem.",
|
||||
"author": "291cc4c1-3bb3-4417-aaa6-748606fede77",
|
||||
"author": "4965745e-b82a-4496-80e1-055217a780b0",
|
||||
"priority": "e79687c6-9054-4706-b9a2-34afccfaa7c8",
|
||||
"create_timestamp": "2024-01-09T00:06:58Z",
|
||||
"edit_timestamp": "2024-01-09T00:44:49Z",
|
||||
@ -126,7 +126,7 @@
|
||||
"fields": {
|
||||
"title": "VPN Connection Issue",
|
||||
"description": "Remote team members are encountering difficulties establishing a VPN connection. This is hindering their ability to access essential resources. Providing detailed information on the error messages received and troubleshooting steps taken so far. Requesting immediate attention to restore seamless VPN functionality.",
|
||||
"author": "291cc4c1-3bb3-4417-aaa6-748606fede77",
|
||||
"author": "4965745e-b82a-4496-80e1-055217a780b0",
|
||||
"priority": "e79687c6-9054-4706-b9a2-34afccfaa7c8",
|
||||
"create_timestamp": "2024-01-09T00:09:39Z",
|
||||
"edit_timestamp": "2024-01-09T00:13:27Z",
|
||||
@ -143,7 +143,7 @@
|
||||
"fields": {
|
||||
"title": "IT Training Request",
|
||||
"description": "Requesting IT training sessions for the marketing team to enhance their proficiency in utilizing specific software tools. Providing a detailed outline of the desired training topics and the anticipated benefits for the team. Seeking assistance in scheduling and conducting the training sessions.",
|
||||
"author": "291cc4c1-3bb3-4417-aaa6-748606fede77",
|
||||
"author": "4965745e-b82a-4496-80e1-055217a780b0",
|
||||
"priority": "0ebc194c-b856-4e4f-9def-cd190d1e8d43",
|
||||
"create_timestamp": "2024-01-09T00:12:17Z",
|
||||
"edit_timestamp": "2024-01-09T00:12:17Z",
|
||||
@ -190,7 +190,7 @@
|
||||
"fields": {
|
||||
"title": "Software Update Problem",
|
||||
"description": "Unable to install the latest software update on my workstation. Getting error code XYZ. Detailed steps attempted are listed in the description.",
|
||||
"author": "291cc4c1-3bb3-4417-aaa6-748606fede77",
|
||||
"author": "4965745e-b82a-4496-80e1-055217a780b0",
|
||||
"priority": "a680328f-0680-456c-8e26-f594e05989ad",
|
||||
"create_timestamp": "2024-01-09T00:06:32Z",
|
||||
"edit_timestamp": "2024-01-09T00:06:32Z",
|
||||
|
343
apps/static/assets/js/tickets.js
Normal file
343
apps/static/assets/js/tickets.js
Normal file
@ -0,0 +1,343 @@
|
||||
var displayedTicketID = -1;
|
||||
filters = {};
|
||||
editor = null;
|
||||
searchTimeout = null;
|
||||
loadingTickets = false;
|
||||
|
||||
const formControls = [
|
||||
{
|
||||
id: "newTitle",
|
||||
validation: function(element) {
|
||||
const value = element.val();
|
||||
return (!element.attr("required") || value.trim() !== "")
|
||||
},
|
||||
errorMessage: function(element) {
|
||||
return "This field is required."
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$(document).ready(function() {
|
||||
// $(".email-list-item").on("click", function() {
|
||||
// displayTicket(this);
|
||||
// });
|
||||
|
||||
ClassicEditor
|
||||
.create( document.getElementById("newDesc"), {})
|
||||
.then( newEditor => {
|
||||
editor = newEditor;
|
||||
})
|
||||
.catch( error => {
|
||||
console.error(error)
|
||||
});
|
||||
|
||||
$("#searchTickets").keyup(() => {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => {
|
||||
value = $("#searchTickets").val();
|
||||
if (value === "") {
|
||||
delete filters["title__contains"];
|
||||
return;
|
||||
}
|
||||
|
||||
filters["title__contains"] = value;
|
||||
loadAllTickets();
|
||||
}, 500);
|
||||
})
|
||||
|
||||
setupFilter("#filterSidebar .filter-department", "author__department__in");
|
||||
setupFilter("#filterSidebar .filter-tag", "tags__in")
|
||||
setupFilter("#filterSidebar .filter-priority", "priority__in")
|
||||
|
||||
loadFilterCounts();
|
||||
loadAllTickets();
|
||||
});
|
||||
|
||||
function setupFilter(selector, key) {
|
||||
$(selector).each(function () {
|
||||
var input = $(this).find("input[type=checkbox], input[type=radio]");
|
||||
var uuid = input.val();
|
||||
|
||||
input.on("change", function () {
|
||||
if (input.is(":checkbox")) {
|
||||
if ($(this).is(":checked")) {
|
||||
filters[key] = filters[key] || [];
|
||||
filters[key].push(uuid);
|
||||
} else {
|
||||
// filters[key].splice(filters[key].indexOf(uuid), 1);
|
||||
filters[key] = filters[key].filter(id => id !== uuid);
|
||||
if (filters[key].length === 0) {
|
||||
delete filters[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input.is(":radio") && input.is(":checked")) {
|
||||
filters[key] = [uuid];
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(filters, null, 4));
|
||||
loadAllTickets();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
|
||||
$("#ticketModal form").find(".form-control,.form-select").removeClass("is-valid is-invalid");
|
||||
$("#ticketModal form .invalid-feedback").text("");
|
||||
|
||||
var valid = true;
|
||||
|
||||
formControls.forEach(function(control) {
|
||||
var element = $("#" + control.id);
|
||||
if (!control.validation(element)) {
|
||||
element.addClass("is-invalid");
|
||||
element.siblings(".invalid-feedback").text(control.errorMessage(element));
|
||||
valid = false;
|
||||
}
|
||||
else {
|
||||
element.addClass("is-valid");
|
||||
}
|
||||
});
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
$("#ticketModal form").on("submit", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: URL_NewTicket,
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
csrfmiddlewaretoken: CSRFMiddlewareToken,
|
||||
title: $("#newTitle").val(),
|
||||
description: editor.getData(),
|
||||
author_id: CurrentUserID,
|
||||
priority_id: $("#newPriority").val(),
|
||||
tag_ids: $("#newTags").val()
|
||||
},
|
||||
success: function(data) {
|
||||
loadAllTickets();
|
||||
loadFilterCounts();
|
||||
},
|
||||
error: function(data) {
|
||||
alert(JSON.stringify(data, null, 4))
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getOrdinalSuffix(day) {
|
||||
if (day >= 11 && day <= 13) {
|
||||
return day + 'th';
|
||||
} else {
|
||||
switch (day % 10) {
|
||||
case 1: return day + 'st';
|
||||
case 2: return day + 'nd';
|
||||
case 3: return day + 'rd';
|
||||
default: return day + 'th';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilterCounts(filterType, data) {
|
||||
$("#filterSidebar .filter-" + filterType).each(function() {
|
||||
var uuid = $(this).find("input[type=checkbox],input[type=radio]").val();
|
||||
var count = data[filterType + '_counts'][uuid];
|
||||
$(this).find(".badge").text(count);
|
||||
});
|
||||
}
|
||||
|
||||
function loadFilterCounts() {
|
||||
$.ajax({
|
||||
url: URL_FilterCounts,
|
||||
type: "GET",
|
||||
success: function(data) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
|
||||
updateFilterCounts('priority', data);
|
||||
updateFilterCounts('tag', data);
|
||||
updateFilterCounts('department', data);
|
||||
$("#filterPriorityAll .badge").text(data.ticket_count);
|
||||
$("#ticketCount").text(data.ticket_count)
|
||||
|
||||
},
|
||||
error: function(data) {
|
||||
alert(JSON.stringify(data, null, 4))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadAllTickets() {
|
||||
if (loadingTickets === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$("#ticketsContainer").empty();
|
||||
loadingTickets = true;
|
||||
// alert(JSON.stringify(filters, null, 4));
|
||||
|
||||
$.ajax({
|
||||
url: URL_Tickets,
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
data: filters,
|
||||
success: function(data) {
|
||||
loadingTickets = false;
|
||||
console.log(JSON.stringify(data, null, 4))
|
||||
|
||||
data.forEach(function(ticket) {
|
||||
var timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime;
|
||||
|
||||
if (ticket.was_yesterday) {
|
||||
var day = getOrdinalSuffix(timestamp.getDate());
|
||||
var month = timestamp.toLocaleString('en-GB', { month: 'short' });
|
||||
var year = timestamp.toLocaleString('en-GB', { year: 'numeric' });
|
||||
var time = timestamp.toLocaleString('en-GB', { hour: 'numeric', minute: 'numeric' });
|
||||
|
||||
// Formatting the final result
|
||||
var formattedTime = time + ', ' + day + ' ' + month + ' ' + year;
|
||||
}
|
||||
else {
|
||||
var hours = timestamp.getUTCHours();
|
||||
var minutes = timestamp.getUTCMinutes();
|
||||
formattedTime = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
if (ticket.is_edited) {
|
||||
formattedTime += " • edited";
|
||||
}
|
||||
|
||||
var item = $(`
|
||||
<div class="email-list-item peers fxw-nw p-20 bdB bgcH-grey-100 cur-p" data-ticket-id="${ticket.uuid}" data-author-icon="${ticket.author.icon}">
|
||||
<div class="peer mR-10">
|
||||
<img src="${ticket.author.icon}" alt="" class="w-2r h-2r bdrs-50p me-2" style="object-fit: cover;">
|
||||
</div>
|
||||
<div class="peer peer-greed ov-h">
|
||||
<div class="peers ai-c">
|
||||
<div class="peer peer-greed">
|
||||
<h6 class="ticket-author">${ticket.author.forename} ${ticket.author.surname}</h6>
|
||||
</div>
|
||||
<div class="peer">
|
||||
<small class="ticket-timestamp">${formattedTime}</small>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="fsz-def tt-c c-grey-900 ticket-title">${ticket.title}</h5>
|
||||
<span class="whs-nw w-100 ov-h tov-e d-b ticket-desc">${ticket.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
|
||||
$("#ticketsContainer").append(item);
|
||||
});
|
||||
|
||||
$(".email-list-item").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
displayTicket(this);
|
||||
$('.email-content').toggleClass('open');
|
||||
});
|
||||
},
|
||||
error: function(data) {
|
||||
loadingTickets = false;
|
||||
alert(JSON.stringify(data, null, 4));
|
||||
console.error(`${data.responseJSON.error}\n${data.responseJSON.detail}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayTicket(ticketElement) {
|
||||
ticket = $(ticketElement);
|
||||
ticketID = ticket.data("ticket-id");
|
||||
|
||||
// $(".back-to-mailbox").off("click").on("click", function(event) {
|
||||
// event.preventDefault();
|
||||
// $('.email-content').toggleClass('open');
|
||||
// displayTicket(ticketElement);
|
||||
// });
|
||||
|
||||
|
||||
if (displayedTicketID === ticketID) {
|
||||
// displayedTicketID = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
ticket.siblings().removeClass("bgc-grey-100");
|
||||
ticket.addClass("bgc-grey-100");
|
||||
|
||||
$("#ticketTitle").text("")
|
||||
$("#ticketDesc").empty();
|
||||
$("#ticketAuthor").text("");
|
||||
$("#ticketAuthorImg").hide();
|
||||
$("#ticketAuthorImg").prop("src", "");
|
||||
$("#ticketTimestamp").text("");
|
||||
$("#btnGroupDrop2").hide();
|
||||
$("#ticketBadges").empty().hide();
|
||||
|
||||
|
||||
displayedTicketID = ticketID;
|
||||
|
||||
$.ajax({
|
||||
url: URL_Tickets,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
uuid__in: [ticketID]
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
|
||||
var ticket = data[0];
|
||||
var author = ticket.author;
|
||||
var department = author.department;
|
||||
var priority = ticket.priority;
|
||||
|
||||
$("#ticketTitle").text(ticket.title);
|
||||
$("#ticketDesc").append($(`<div class="w-100">${ticket.description}</div>`));
|
||||
$("#ticketAuthor").text(`${author.forename} ${author.surname}`);
|
||||
$("#ticketAuthorImg").show();
|
||||
$("#ticketAuthorImg").prop("src", author.icon);
|
||||
$("#btnGroupDrop2").show();
|
||||
$("#ticketBadges").show();
|
||||
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="color: ${priority.colour}; background-color: ${priority.backgroundcolour};">${priority.title} Priority <i class="ti-control-record "></i></div>`));
|
||||
|
||||
if (department != null) {
|
||||
$("#ticketBadges").append($(`<div class="badge bgc-deep-purple-500 me-1">${department.title}</div>`));
|
||||
}
|
||||
|
||||
ticket.tags.forEach(function(tag) {
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="color: ${tag.colour}; background-color: ${tag.backgroundcolour};">${tag.title} <i class="ti-tag"></i></div>`));
|
||||
});
|
||||
|
||||
// timestamp
|
||||
var timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime;
|
||||
|
||||
if (ticket.was_yesterday) {
|
||||
var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
|
||||
formattedTime = timestamp.toLocaleDateString('en-GB', options);
|
||||
}
|
||||
else {
|
||||
var hours = timestamp.getUTCHours();
|
||||
var minutes = timestamp.getUTCMinutes();
|
||||
formattedTime = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
if (ticket.is_edited) {
|
||||
formattedTime += " • edited";
|
||||
}
|
||||
|
||||
$("#ticketTimestamp").text(formattedTime);
|
||||
},
|
||||
error: function(message) {
|
||||
alert(JSON.stringify(message, null, 4));
|
||||
}
|
||||
});
|
||||
}
|
@ -151,7 +151,7 @@
|
||||
</div>
|
||||
<div class="layer w-100">
|
||||
<div class="bdT bdB">
|
||||
<input type="text" class="form-control m-0 bdw-0 pY-15 pX-20 bdrs-0" placeholder="Search...">
|
||||
<input type="text" id="searchTickets" class="form-control m-0 bdw-0 pY-15 pX-20 bdrs-0" placeholder="Search...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -296,333 +296,14 @@
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
<!-- Specific Page JS goes HERE -->
|
||||
{% block javascripts %}
|
||||
<!-- Define Variables -->
|
||||
<script>
|
||||
var displayedTicketID = -1;
|
||||
filters = {};
|
||||
editor = null;
|
||||
|
||||
const formControls = [
|
||||
{
|
||||
id: "newTitle",
|
||||
validation: function(element) {
|
||||
const value = element.val();
|
||||
return (!element.attr("required") || value.trim() !== "")
|
||||
},
|
||||
errorMessage: function(element) {
|
||||
return "This field is required."
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$(document).ready(function() {
|
||||
// $(".email-list-item").on("click", function() {
|
||||
// displayTicket(this);
|
||||
// });
|
||||
|
||||
ClassicEditor
|
||||
.create( document.getElementById("newDesc"), {})
|
||||
.then( newEditor => {
|
||||
editor = newEditor;
|
||||
})
|
||||
.catch( error => {
|
||||
console.error(error)
|
||||
});
|
||||
|
||||
// $("#filterPriorityAll").click(function() {
|
||||
// delete filters[".filter-priority"];
|
||||
// loadAllTickets();
|
||||
// });
|
||||
|
||||
setupFilter("#filterSidebar .filter-department", "author__department__in");
|
||||
setupFilter("#filterSidebar .filter-tag", "tags__in")
|
||||
setupFilter("#filterSidebar .filter-priority", "priority__in")
|
||||
|
||||
loadFilterCounts();
|
||||
loadAllTickets();
|
||||
});
|
||||
|
||||
function setupFilter(selector, key) {
|
||||
$(selector).each(function () {
|
||||
var input = $(this).find("input[type=checkbox], input[type=radio]");
|
||||
var uuid = input.val();
|
||||
|
||||
input.on("change", function () {
|
||||
if (input.is(":checkbox")) {
|
||||
if ($(this).is(":checked")) {
|
||||
filters[key] = filters[key] || [];
|
||||
filters[key].push(uuid);
|
||||
} else {
|
||||
// filters[key].splice(filters[key].indexOf(uuid), 1);
|
||||
filters[key] = filters[key].filter(id => id !== uuid);
|
||||
if (filters[key].length === 0) {
|
||||
delete filters[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (input.is(":radio") && input.is(":checked")) {
|
||||
filters[key] = [uuid];
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(filters, null, 4));
|
||||
loadAllTickets();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function validateForm() {
|
||||
|
||||
$("#ticketModal form").find(".form-control,.form-select").removeClass("is-valid is-invalid");
|
||||
$("#ticketModal form .invalid-feedback").text("");
|
||||
|
||||
var valid = true;
|
||||
|
||||
formControls.forEach(function(control) {
|
||||
var element = $("#" + control.id);
|
||||
if (!control.validation(element)) {
|
||||
element.addClass("is-invalid");
|
||||
element.siblings(".invalid-feedback").text(control.errorMessage(element));
|
||||
valid = false;
|
||||
}
|
||||
else {
|
||||
element.addClass("is-valid");
|
||||
}
|
||||
});
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
$("#ticketModal form").on("submit", function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "{% url 'ticket-new' %}",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
csrfmiddlewaretoken: "{{ csrf_token }}",
|
||||
title: $("#newTitle").val(),
|
||||
description: editor.getData(),
|
||||
author_id: "{{ request.user.uuid }}",
|
||||
priority_id: $("#newPriority").val(),
|
||||
tag_ids: $("#newTags").val()
|
||||
},
|
||||
success: function(data) {
|
||||
loadAllTickets();
|
||||
loadFilterCounts();
|
||||
},
|
||||
error: function(data) {
|
||||
alert(JSON.stringify(data, null, 4))
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getOrdinalSuffix(day) {
|
||||
if (day >= 11 && day <= 13) {
|
||||
return day + 'th';
|
||||
} else {
|
||||
switch (day % 10) {
|
||||
case 1: return day + 'st';
|
||||
case 2: return day + 'nd';
|
||||
case 3: return day + 'rd';
|
||||
default: return day + 'th';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateFilterCounts(filterType, data) {
|
||||
$("#filterSidebar .filter-" + filterType).each(function() {
|
||||
var uuid = $(this).find("input[type=checkbox],input[type=radio]").val();
|
||||
var count = data[filterType + '_counts'][uuid];
|
||||
$(this).find(".badge").text(count);
|
||||
});
|
||||
}
|
||||
|
||||
function loadFilterCounts() {
|
||||
$.ajax({
|
||||
url: "{% url 'api:filter-counts' %}",
|
||||
type: "GET",
|
||||
success: function(data) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
|
||||
updateFilterCounts('priority', data);
|
||||
updateFilterCounts('tag', data);
|
||||
updateFilterCounts('department', data);
|
||||
$("#filterPriorityAll .badge").text(data.ticket_count);
|
||||
$("#ticketCount").text(data.ticket_count)
|
||||
|
||||
},
|
||||
error: function(data) {
|
||||
alert(JSON.stringify(data, null, 4))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadAllTickets() {
|
||||
$("#ticketsContainer").empty();
|
||||
// alert(JSON.stringify(filters, null, 4));
|
||||
|
||||
$.ajax({
|
||||
url: "{% url 'api:ticket' %}",
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
data: filters,
|
||||
success: function(data) {
|
||||
console.log(JSON.stringify(data, null, 4))
|
||||
|
||||
data.forEach(function(ticket) {
|
||||
var timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime;
|
||||
|
||||
if (ticket.was_yesterday) {
|
||||
var day = getOrdinalSuffix(timestamp.getDate());
|
||||
var month = timestamp.toLocaleString('en-GB', { month: 'short' });
|
||||
var year = timestamp.toLocaleString('en-GB', { year: 'numeric' });
|
||||
var time = timestamp.toLocaleString('en-GB', { hour: 'numeric', minute: 'numeric' });
|
||||
|
||||
// Formatting the final result
|
||||
var formattedTime = time + ', ' + day + ' ' + month + ' ' + year;
|
||||
}
|
||||
else {
|
||||
var hours = timestamp.getUTCHours();
|
||||
var minutes = timestamp.getUTCMinutes();
|
||||
formattedTime = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
if (ticket.is_edited) {
|
||||
formattedTime += " • edited";
|
||||
}
|
||||
|
||||
var item = $(`
|
||||
<div class="email-list-item peers fxw-nw p-20 bdB bgcH-grey-100 cur-p" data-ticket-id="${ticket.uuid}" data-author-icon="${ticket.author.icon}">
|
||||
<div class="peer mR-10">
|
||||
<img src="${ticket.author.icon}" alt="" class="w-2r h-2r bdrs-50p me-2" style="object-fit: cover;">
|
||||
</div>
|
||||
<div class="peer peer-greed ov-h">
|
||||
<div class="peers ai-c">
|
||||
<div class="peer peer-greed">
|
||||
<h6 class="ticket-author">${ticket.author.forename} ${ticket.author.surname}</h6>
|
||||
</div>
|
||||
<div class="peer">
|
||||
<small class="ticket-timestamp">${formattedTime}</small>
|
||||
</div>
|
||||
</div>
|
||||
<h5 class="fsz-def tt-c c-grey-900 ticket-title">${ticket.title}</h5>
|
||||
<span class="whs-nw w-100 ov-h tov-e d-b ticket-desc">${ticket.description}</span>
|
||||
</div>
|
||||
</div>
|
||||
`);
|
||||
|
||||
|
||||
$("#ticketsContainer").append(item);
|
||||
});
|
||||
|
||||
$(".email-list-item").on("click", function(e) {
|
||||
e.preventDefault();
|
||||
displayTicket(this);
|
||||
$('.email-content').toggleClass('open');
|
||||
});
|
||||
},
|
||||
error: function(data) {
|
||||
alert(JSON.stringify(data, null, 4));
|
||||
console.error(`${data.responseJSON.error}\n${data.responseJSON.detail}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function displayTicket(ticketElement) {
|
||||
ticket = $(ticketElement);
|
||||
ticketID = ticket.data("ticket-id");
|
||||
|
||||
// $(".back-to-mailbox").off("click").on("click", function(event) {
|
||||
// event.preventDefault();
|
||||
// $('.email-content').toggleClass('open');
|
||||
// displayTicket(ticketElement);
|
||||
// });
|
||||
|
||||
|
||||
if (displayedTicketID === ticketID) {
|
||||
// displayedTicketID = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
ticket.siblings().removeClass("bgc-grey-100");
|
||||
ticket.addClass("bgc-grey-100");
|
||||
|
||||
$("#ticketTitle").text("")
|
||||
$("#ticketDesc").empty();
|
||||
$("#ticketAuthor").text("");
|
||||
$("#ticketAuthorImg").hide();
|
||||
$("#ticketAuthorImg").prop("src", "");
|
||||
$("#ticketTimestamp").text("");
|
||||
$("#btnGroupDrop2").hide();
|
||||
$("#ticketBadges").empty().hide();
|
||||
|
||||
|
||||
displayedTicketID = ticketID;
|
||||
|
||||
$.ajax({
|
||||
url: `{% url 'api:ticket' %}`,
|
||||
type: 'get',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
uuid__in: [ticketID]
|
||||
},
|
||||
success: function (data) {
|
||||
console.log(JSON.stringify(data, null, 4));
|
||||
|
||||
var ticket = data[0];
|
||||
var author = ticket.author;
|
||||
var department = author.department;
|
||||
var priority = ticket.priority;
|
||||
|
||||
$("#ticketTitle").text(ticket.title);
|
||||
$("#ticketDesc").append($(`<div class="w-100">${ticket.description}</div>`));
|
||||
$("#ticketAuthor").text(`${author.forename} ${author.surname}`);
|
||||
$("#ticketAuthorImg").show();
|
||||
$("#ticketAuthorImg").prop("src", author.icon);
|
||||
$("#btnGroupDrop2").show();
|
||||
$("#ticketBadges").show();
|
||||
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="color: ${priority.colour}; background-color: ${priority.backgroundcolour};">${priority.title} Priority <i class="ti-control-record "></i></div>`));
|
||||
|
||||
if (department != null) {
|
||||
$("#ticketBadges").append($(`<div class="badge bgc-deep-purple-500 me-1">${department.title}</div>`));
|
||||
}
|
||||
|
||||
ticket.tags.forEach(function(tag) {
|
||||
$("#ticketBadges").append($(`<div class="badge me-1" style="color: ${tag.colour}; background-color: ${tag.backgroundcolour};">${tag.title} <i class="ti-tag"></i></div>`));
|
||||
});
|
||||
|
||||
// timestamp
|
||||
var timestamp = new Date(ticket.timestamp);
|
||||
var formattedTime;
|
||||
|
||||
if (ticket.was_yesterday) {
|
||||
var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
|
||||
formattedTime = timestamp.toLocaleDateString('en-GB', options);
|
||||
}
|
||||
else {
|
||||
var hours = timestamp.getUTCHours();
|
||||
var minutes = timestamp.getUTCMinutes();
|
||||
formattedTime = hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
if (ticket.is_edited) {
|
||||
formattedTime += " • edited";
|
||||
}
|
||||
|
||||
$("#ticketTimestamp").text(formattedTime);
|
||||
},
|
||||
error: function(message) {
|
||||
alert(JSON.stringify(message, null, 4));
|
||||
}
|
||||
});
|
||||
}
|
||||
const URL_Tickets = "{% url 'api:tickets' %}";
|
||||
const URL_NewTicket = "{% url 'ticket-new' %}";
|
||||
const URL_FilterCounts = "{% url 'api:filter-counts' %}";
|
||||
const CSRFMiddlewareToken = "{{ csrf_token }}";
|
||||
const CurrentUserID = "{{ request.user.uuid }}";
|
||||
</script>
|
||||
<script src="{{ ASSETS_ROOT }}/js/tickets.js"></script>
|
||||
{% endblock javascripts %}
|
||||
|
@ -6,8 +6,7 @@ from django.conf.urls.static import static
|
||||
from django.urls import path, include # add this
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls), # Django admin route
|
||||
path("api-auth/", include("rest_framework.urls")),
|
||||
path('admin/', admin.site.urls),
|
||||
path("api/", include(("apps.api.urls", "apps.api"), namespace="api")),
|
||||
|
||||
# ADD NEW Routes HERE
|
||||
|
Loading…
x
Reference in New Issue
Block a user