364 lines
13 KiB
JavaScript
364 lines
13 KiB
JavaScript
var filters = {"order": "-edit_timestamp"};
|
|
global_loadingTickets = false;
|
|
searchTimeout = null;
|
|
pagination = {};
|
|
|
|
$(document).ready(function() {
|
|
initSearchBar();
|
|
|
|
setupFilter("#filterSidebar .filter-department", "author__department");
|
|
setupFilter("#filterSidebar .filter-tags", "tags");
|
|
setupFilter("#filterSidebar .filter-priority", "priority");
|
|
|
|
loadFilterCounts();
|
|
loadTicketItems();
|
|
});
|
|
|
|
function updateItemsState(state) {
|
|
console.debug(`updating items state to '${state}'`);
|
|
switch (state) {
|
|
case "content":
|
|
$("#ticketsContainer .none-found").hide();
|
|
$("#ticketsContainer .loading").hide();
|
|
break;
|
|
case "loading":
|
|
$("#ticketsContainer .content").empty();
|
|
$("#ticketsContainer .none-found").hide();
|
|
$("#ticketsContainer .loading").show();
|
|
break;
|
|
case "no-content":
|
|
$("#ticketsContainer .content").empty();
|
|
$("#ticketsContainer .none-found").show();
|
|
$("#ticketsContainer .loading").hide();
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid Items State '${state}'`);
|
|
}
|
|
}
|
|
|
|
function updateContentState(state) {
|
|
console.debug(`updating content state to '${state}'`);
|
|
switch (state) {
|
|
case "content":
|
|
$("#ticketContent .loading").hide();
|
|
break;
|
|
case "loading":
|
|
$("#ticketContent .content").empty();
|
|
$("#ticketContent .loading").show();
|
|
break;
|
|
default:
|
|
throw new Error(`Invalid Content State '${state}'`);
|
|
}
|
|
}
|
|
|
|
function initSearchBar() {
|
|
$("#searchTickets").keyup(() => {
|
|
updateItemsState("loading");
|
|
clearTimeout(searchTimeout);
|
|
searchTimeout = setTimeout(() => {
|
|
console.debug("searching");
|
|
value = $("#searchTickets").val();
|
|
if (value === "") {
|
|
console.debug("deleted search filters");
|
|
delete filters["search"];
|
|
}
|
|
else {
|
|
console.debug("updated search filters");
|
|
filters["search"] = value;
|
|
}
|
|
|
|
loadTicketItems();
|
|
}, 500);
|
|
})
|
|
}
|
|
|
|
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] = filters[key].filter(id => id !== uuid);
|
|
if (filters[key].length === 0) delete filters[key];
|
|
}
|
|
}
|
|
else if (input.is(":radio") && input.is(":checked")) {
|
|
if (uuid === "all") delete filters[key];
|
|
else filters[key] = [uuid];
|
|
}
|
|
|
|
console.debug(`Filter applied '${key}' as '${uuid}'`)
|
|
loadTicketItems();
|
|
});
|
|
});
|
|
}
|
|
|
|
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 timestampToHumanDate(timestamp, wasYesterday) {
|
|
if (wasYesterday) {
|
|
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' });
|
|
return time + ', ' + day + ' ' + month + ' ' + year;
|
|
}
|
|
|
|
var hours = timestamp.getUTCHours();
|
|
var minutes = timestamp.getUTCMinutes();
|
|
return hours.toString().padStart(2, '0') + ':' + minutes.toString().padStart(2, '0');
|
|
}
|
|
|
|
function updateFilterCounts(filterType, data) {
|
|
$("#filterSidebar .filter-" + filterType).each(function() {
|
|
var uuid = $(this).find("input[type=checkbox],input[type=radio]").val();
|
|
var count = data[filterType][uuid];
|
|
$(this).find(".badge").text(count);
|
|
});
|
|
}
|
|
|
|
function loadFilterCounts() {
|
|
$.ajax({
|
|
url: URL_FilterCounts,
|
|
type: "GET",
|
|
success: function(data) {
|
|
updateFilterCounts('priority', data);
|
|
updateFilterCounts('tags', data);
|
|
updateFilterCounts('department', data);
|
|
|
|
$("#filterPriorityAll .badge").text(data.tickets);
|
|
$("#filterDepartmentAll .badge").text(data.tickets)
|
|
// $("#ticketCounts .total").text(data.tickets)
|
|
},
|
|
error: function(data) {
|
|
console.error(JSON.stringify(data, null, 4))
|
|
}
|
|
});
|
|
}
|
|
|
|
function applyTicketClickFunction() {
|
|
$(".ticket-item").on("click", function(e) {
|
|
e.preventDefault();
|
|
if ($(this).hasClass("active")) {
|
|
return;
|
|
}
|
|
|
|
loadTicketContent($(this).data("uuid"));
|
|
$(".ticket-item").removeClass("active");
|
|
$(this).addClass("active");
|
|
$('.email-app').removeClass('side-active');
|
|
$('.email-content').toggleClass('open');
|
|
});
|
|
}
|
|
|
|
$(".back-to-mailbox").on("click", function(e) {
|
|
e.preventDefault();
|
|
$(".ticket-item.active").removeClass("active");
|
|
});
|
|
|
|
function reloadCurrentTicket() {
|
|
loadTicketContent($(".ticket-item.active").data("uuid"));
|
|
}
|
|
|
|
function changeTicket(next=true) {
|
|
var selectedTicket = $(".ticket-item.active");
|
|
var uuid;
|
|
|
|
if (!selectedTicket.length) selectedTicket = $(".ticket-item").first();
|
|
else if (next) selectedTicket = selectedTicket.next();
|
|
else selectedTicket = selectedTicket.prev();
|
|
|
|
$(".ticket-item").removeClass("active");
|
|
selectedTicket.addClass("active");
|
|
uuid = selectedTicket.data("uuid");
|
|
loadTicketContent(uuid);
|
|
|
|
if (!$('.email-content').hasClass('open')) {
|
|
$('.email-content').addClass('open');
|
|
}
|
|
}
|
|
|
|
function changeItemsPage(next) {
|
|
$("#ticketItemsNextPage").prop("disabled", true);
|
|
$("#ticketItemsPrevPage").prop("disabled", true);
|
|
|
|
var page = pagination.page;
|
|
|
|
if (next && pagination.next) page ++;
|
|
else if (!next && pagination.prev) page --;
|
|
else return;
|
|
|
|
loadTicketItems(page);
|
|
}
|
|
|
|
function loadTicketItems(page=1) {
|
|
updateItemsState("loading");
|
|
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";
|
|
|
|
fetchTicketsPromise(fetchFilters).then((response) => {
|
|
// Update the counts to show how many tickets were found
|
|
$("#ticketCounts .current").text(response.results.length);
|
|
$("#ticketCounts .total").text(response.count);
|
|
|
|
// If there are no tickets
|
|
if (!response.count) {
|
|
updateItemsState("no-content");
|
|
return;
|
|
}
|
|
|
|
const ticketHasNextPage = typeof response.next === "string";
|
|
const ticketHasPrevPage = typeof response.previous === "string";
|
|
$("#ticketItemsNextPage").prop("disabled", !ticketHasNextPage);
|
|
$("#ticketItemsPrevPage").prop("disabled", !ticketHasPrevPage);
|
|
const pageNumber = filters["page"] || 1; // If we haven't tracked the page, it's safe to assume it's 1,
|
|
pagination = { // because we always track it when it changes.
|
|
"page": pageNumber,
|
|
"next": ticketHasNextPage,
|
|
"prev": ticketHasPrevPage
|
|
}
|
|
|
|
// Update the pagination count with the current page
|
|
$("#paginationCounts .current").text(pageNumber);
|
|
|
|
// If we are on page one (which has been normalised to be the case in many intances)
|
|
// We can use the amount of results to calculate the total amount of pages.
|
|
if (pageNumber === 1) {
|
|
$("#paginationCounts .total").text(Math.ceil(response.count / response.results.length));
|
|
}
|
|
|
|
// 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-title").text(ticket.title);
|
|
template.find(".ticket-item-desc").html(ticket.short_description);
|
|
template.find(".ticket-item-icon").attr("src", ticket.author.icon);
|
|
template.attr("data-uuid", ticket.uuid);
|
|
|
|
// Add tickets using the badge template
|
|
ticket.tags.forEach(function(tag) {
|
|
var tagTemplate = $($("#ticketContentBadgeTemplate").html());
|
|
tagTemplate.find(".ticket-content-badge-text").text(tag.title);
|
|
tagTemplate.css({ "color": tag.colour, "background-color": tag.backgroundcolour });
|
|
template.find(".ticket-item-tags").append(tagTemplate);
|
|
});
|
|
|
|
// 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);
|
|
});
|
|
|
|
// Make tickets clickable
|
|
applyTicketClickFunction();
|
|
|
|
updateItemsState("content");
|
|
});
|
|
}
|
|
|
|
function loadTicketContent(uuid) {
|
|
updateContentState("loading");
|
|
|
|
if (global_loadingTickets) {
|
|
console.debug("Spam prevention\nStopped loadTicketContent because already loading.");
|
|
return;
|
|
}
|
|
|
|
$("#ticketContent .content").empty();
|
|
|
|
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-title").text(ticket.title);
|
|
template.find(".ticket-content-desc").html(ticket.description);
|
|
template.find(".ticket-content-icon").attr("src", ticket.author.icon);
|
|
console.debug(ticket.description);
|
|
|
|
ticket.tags.forEach(function(tag) {
|
|
var tagTemplate = $($("#ticketContentBadgeTemplate").html());
|
|
tagTemplate.find(".ticket-content-badge-text").text(tag.title);
|
|
tagTemplate.css({ "color": tag.colour, "background-color": tag.backgroundcolour });
|
|
template.find(".ticket-content-badges").append(tagTemplate);
|
|
});
|
|
|
|
$("#ticketContent .content").append(template);
|
|
|
|
updateContentState("content");
|
|
});
|
|
|
|
// $("#ticketContent").empty();
|
|
|
|
// updateInterfaceState("showing-content");
|
|
}
|
|
|
|
function fetchTicketsPromise(queryFilters) {
|
|
return new Promise(function(resolve, reject) {
|
|
global_loadingTickets = true;
|
|
$.ajax({
|
|
url: URL_Tickets,
|
|
type: "GET",
|
|
dataType: "JSON",
|
|
data: $.param(queryFilters, true),
|
|
success: function(response) {
|
|
global_loadingTickets = false;
|
|
resolve(response);
|
|
},
|
|
error: function(response) {
|
|
global_loadingTickets = false;
|
|
if (response.status === 429) {
|
|
alert(`
|
|
HTTP ${response.status} - ${response.statusText}\n
|
|
${response.responseJSON.detail}
|
|
`);
|
|
}
|
|
reject(response);
|
|
}
|
|
});
|
|
});
|
|
} |