The tickets are now loaded dynamically using ajax, instead of when the page loads using django template syntax. This allows for dynamic reloading.
573 lines
27 KiB
HTML
573 lines
27 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %} Tickets {% endblock title %}
|
|
|
|
<!-- Specific CSS goes HERE -->
|
|
{% block stylesheets %}
|
|
<link rel="stylesheet" href="{{ ASSETS_ROOT }}/css/select2-bootstrap.min.css">
|
|
{% endblock stylesheets %}
|
|
|
|
{% block content %}
|
|
|
|
<!-- ### $App Screen Content ### -->
|
|
<main class='main-content bgc-grey-100'>
|
|
<div id='mainContent'>
|
|
<div class="full-container">
|
|
<div class="email-app">
|
|
<div class="email-side-nav remain-height ov-h">
|
|
<div class="h-100 layers">
|
|
<div class="p-20 bgc-grey-100 layer w-100">
|
|
<button type="button" class="btn btn-danger c-white w-100" data-bs-toggle="modal" data-bs-target="#ticketModal">New Ticket</button>
|
|
</div>
|
|
<div class="scrollable pos-r bdT layer w-100 fxg-1">
|
|
<ul class="p-20 nav flex-column">
|
|
<li class="nav-item">
|
|
<h6>Filters</h6>
|
|
</li>
|
|
|
|
{% if priorities %}
|
|
<li class="nav-item ps-3 mT-15">
|
|
<h6 class="small">Priority</h6>
|
|
{% for priority in priorities %}
|
|
<div class="form-check">
|
|
<input type="checkbox" id="priority-{{ priority.id }}" class="form-check-input">
|
|
<label for="priority-{{ priority.id }}" class="form-check-label">{{ priority.title }}</label>
|
|
</div>
|
|
{% endfor %}
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% if departments %}
|
|
<li class="nav-item ps-3 mT-15">
|
|
<h6 class="small">Department</h6>
|
|
{% for department in departments %}
|
|
<div class="form-check">
|
|
<input type="checkbox" id="department-{{ department.id }}" class="form-check-input">
|
|
<label for="department-{{ department.id }}" class="form-check-label">{{ department.title }}</label>
|
|
</div>
|
|
{% endfor %}
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% if tags %}
|
|
<li class="nav-item ps-3 mT-15">
|
|
<h6 class="small">Tags</h6>
|
|
{% for tag in tags %}
|
|
<div class="form-check">
|
|
<input type="checkbox" id="tag-{{ tag.id }}" class="form-check-input">
|
|
<label for="tag-{{ tag.id }}" class="form-check-label">{{ tag.title }}</label>
|
|
</div>
|
|
{% endfor %}
|
|
</li>
|
|
{% endif %}
|
|
|
|
<!-- <li class="nav-item mt-5">
|
|
<a href="javascript:void(0)" class="nav-link c-grey-800 cH-blue-500 actived">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-email"></i>
|
|
<span>Inbox</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-deep-purple-50 c-deep-purple-700">+99</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="" class="nav-link c-grey-800 cH-blue-500">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-share"></i>
|
|
<span>Sent</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-green-50 c-green-700">12</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="" class="nav-link c-grey-800 cH-blue-500">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-star"></i>
|
|
<span>Important</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-blue-50 c-blue-700">3</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="" class="nav-link c-grey-800 cH-blue-500">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-file"></i>
|
|
<span>Drafts</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-amber-50 c-amber-700">5</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="" class="nav-link c-grey-800 cH-blue-500">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-alert"></i>
|
|
<span>Spam</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-red-50 c-red-700">1</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a href="" class="nav-link c-grey-800 cH-blue-500">
|
|
<div class="peers ai-c jc-sb">
|
|
<div class="peer peer-greed">
|
|
<i class="mR-10 ti-trash"></i>
|
|
<span>Trash</span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="badge rounded-pill bgc-red-50 c-red-700">+99</span>
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</li> -->
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="email-wrapper row remain-height bgc-white ov-h">
|
|
<div class="email-list h-100 layers">
|
|
<div class="layer w-100">
|
|
<div class="bgc-grey-100 peers ai-c p-20 fxw-nw">
|
|
<div class="peer me-auto">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="email-side-toggle d-n@md+ btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-menu"></i>
|
|
</button>
|
|
<button type="button" class="btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-folder"></i>
|
|
</button>
|
|
<button type="button" class="btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-tag"></i>
|
|
</button>
|
|
<div class="btn-group" role="group">
|
|
<button id="btnGroupDrop1" type="button" class="btn cur-p bgc-white no-after dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="ti-more-alt"></i>
|
|
</button>
|
|
<ul class="dropdown-menu fsz-sm" aria-labelledby="btnGroupDrop1">
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-trash mR-10"></i>
|
|
<span>Delete</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-alert mR-10"></i>
|
|
<span>Mark as Spam</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-star mR-10"></i>
|
|
<span>Star</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="peer">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="fsz-xs btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-angle-left"></i>
|
|
</button>
|
|
<button type="button" class="fsz-xs btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-angle-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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...">
|
|
</div>
|
|
</div>
|
|
<div id="ticketsContainer" class="layer w-100 fxg-1 scrollable pos-r">
|
|
<!-- {% for ticket in tickets %}
|
|
<div class="email-list-item peers fxw-nw p-20 bdB bgcH-grey-100 cur-p" data-ticket-id="{{ ticket.id }}" data-author-icon="{{ ticket.author.icon.url }}">
|
|
<div class="peer mR-10">
|
|
<img src="{{ ticket.author.icon.url }}" alt="" class="w-2r bdrs-50p me-2">
|
|
</div>
|
|
<div class="peer peer-greed ov-h">
|
|
<div class="peers ai-c">
|
|
<div class="peer peer-greed">
|
|
<h6 class="ticket-author mb-0">{{ ticket.author.fullname }}</h6>
|
|
</div>
|
|
<div class="peer">
|
|
<small class="ticket-timestamp">
|
|
{% if ticket.is_older_than_day %}
|
|
{{ ticket.timestamp|date:"D, w M Y" }}
|
|
{% else %}
|
|
{{ ticket.timestamp|date:"H:i" }}
|
|
{% endif %}
|
|
</small>
|
|
{% if ticket.is_edited %}
|
|
<small>• edited</small>
|
|
{% endif %}
|
|
</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|safe }}</span>
|
|
</div>
|
|
</div>
|
|
{% endfor %} -->
|
|
</div>
|
|
</div>
|
|
<div class="email-content h-100">
|
|
<div class="h-100 scrollable pos-r">
|
|
<div class="bgc-grey-100 peers ai-c jc-sb p-20 fxw-nw d-n@md+">
|
|
<div class="peer">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="back-to-mailbox btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-angle-left"></i>
|
|
</button>
|
|
<button type="button" class="btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-folder"></i>
|
|
</button>
|
|
<button type="button" class="btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-tag"></i>
|
|
</button>
|
|
<div class="btn-group" role="group">
|
|
<button id="btnGroupDrop1" type="button" class="btn cur-p bgc-white no-after dropdown-toggle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="ti-more-alt"></i>
|
|
</button>
|
|
<ul class="dropdown-menu fsz-sm" aria-labelledby="btnGroupDrop1">
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-trash mR-10"></i>
|
|
<span>Delete</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-alert mR-10"></i>
|
|
<span>Mark as Spam</span>
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a href="" class="d-b td-n pY-5 pX-10 bgcH-grey-100 c-grey-700">
|
|
<i class="ti-star mR-10"></i>
|
|
<span>Star</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="peer">
|
|
<div class="btn-group" role="group">
|
|
<button type="button" class="fsz-xs btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-angle-left"></i>
|
|
</button>
|
|
<button type="button" class="fsz-xs btn bgc-white bdrs-2 mR-3 cur-p">
|
|
<i class="ti-angle-right"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="email-content-wrapper">
|
|
<!-- Header -->
|
|
<div class="peers ai-c jc-sb pX-40 pY-30">
|
|
<div class="peers peer-greed">
|
|
<div class="peer mR-20">
|
|
<img id="ticketAuthorImg" class="bdrs-50p w-3r h-3r" alt="" src="" style="display: none;">
|
|
</div>
|
|
<div class="peer">
|
|
<small id="ticketTimestamp"></small>
|
|
<h5 id="ticketAuthor" class="c-grey-900 mB-5"></h5>
|
|
<div id="ticketBadges" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
<div class="peer">
|
|
<div class="btn-group" role="group">
|
|
<button id="btnGroupDrop2" class="btn btn-danger c-white bdrs-50p p-15 lh-0" style="display: none;" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|
<i class="ti-menu"></i>
|
|
</button>
|
|
<ul class="dropdown-menu fsz-sm" aria-labelledby="btnGroupDrop2">
|
|
test
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content -->
|
|
<div class="bdT pX-40 pY-30">
|
|
|
|
<h4 id="ticketTitle"></h4>
|
|
<p id="ticketDesc"></p>
|
|
<!-- <h4>Title of this email goes here</h4>
|
|
<p>
|
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
|
|
cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
|
|
proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
</p>
|
|
<p>
|
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
|
|
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
|
|
</p>
|
|
<p>
|
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
|
|
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam
|
|
</p> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="ticketModal" class="modal fade" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<form method="post" action="{% url 'ticket-new' %}">
|
|
|
|
{% csrf_token %}
|
|
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h3 class="modal-title fs-5">New Ticket</h3>
|
|
<button type="button" class="btn-close" data-bs-toggle="modal"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label for="newTitle" class="form-label">Subject</label>
|
|
<input type="text" name="newTitle" id="newTitle" class="form-control" placeholder="Subject">
|
|
<small class="text-muted">Briefly describe the subject of the ticket.</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newDesc" class="form-label">Description</label>
|
|
<div id="newDesc" class="form-control"></div>
|
|
<small class="text-muted">Describe your issue in detail here.</small>
|
|
<!-- class="form-control" -->
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newPriority" class="form-label">Priority</label>
|
|
<select name="newPriority" id="newPriority" class="select-2">
|
|
{% for priority in priorities %}
|
|
<option value="{{ priority.id }}">{{ priority.title }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<small class="text-muted">How important is this ticket?</small>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="newTagss" class="form-label">Tags</label>
|
|
<select name="newTags" id="newTags" class="select-2" multiple="multiple">
|
|
{% for tag in tags %}
|
|
<option value="{{ tag.id }}">{{ tag.title }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
<small class="text-muted">Use tags to categorize this ticket.</small>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-danger c-white">Submit</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock content %}
|
|
|
|
<!-- Specific Page JS goes HERE -->
|
|
{% block javascripts %}
|
|
<script>
|
|
var displayedTicketID = -1;
|
|
|
|
$(document).ready(function() {
|
|
$(".email-list-item").on("click", function() {
|
|
displayTicket(this);
|
|
});
|
|
|
|
ClassicEditor
|
|
.create( document.getElementById("newDesc"), {})
|
|
.catch( error => {
|
|
console.error(error)
|
|
});
|
|
|
|
loadAllTickets();
|
|
});
|
|
|
|
function loadAllTickets() {
|
|
$("#ticketsContainer").empty();
|
|
|
|
$.ajax({
|
|
url: "{% url 'ticket-getmany' %}",
|
|
type: "POST",
|
|
dataType: "json",
|
|
data: {
|
|
csrfmiddlewaretoken: "{{ csrf_token }}",
|
|
filters: {}
|
|
},
|
|
success: function(data) {
|
|
console.log(JSON.stringify(data, null, 4))
|
|
|
|
data.tickets.forEach(function(ticket) {
|
|
var timestamp = new Date(ticket.timestamp);
|
|
var formattedTime;
|
|
|
|
if (ticket.is_older_than_day) {
|
|
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";
|
|
}
|
|
|
|
var item = $(`
|
|
<div class="email-list-item peers fxw-nw p-20 bdB bgcH-grey-100 cur-p" data-ticket-id="${ticket.id}" data-author-icon="${ticket.author.icon}">
|
|
<div class="peer mR-10">
|
|
<img src="${ticket.author.icon}" alt="" class="w-2r bdrs-50p me-2">
|
|
</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() {
|
|
displayTicket(this);
|
|
});
|
|
},
|
|
error: function(data) {
|
|
alert(JSON.stringify(data, null, 4))
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
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);
|
|
});
|
|
|
|
$("#ticketTitle").text("")
|
|
$("#ticketDesc").empty();
|
|
$("#ticketAuthor").text("");
|
|
$("#ticketAuthorImg").hide();
|
|
$("#ticketAuthorImg").prop("src", "");
|
|
$("#ticketTimestamp").text("");
|
|
$("#btnGroupDrop2").hide();
|
|
$("#ticketBadges").empty().hide();
|
|
|
|
if (displayedTicketID === ticketID) {
|
|
displayedTicketID = -1;
|
|
return;
|
|
}
|
|
|
|
displayedTicketID = ticketID;
|
|
|
|
$.ajax({
|
|
url: `{% url 'ticket-getone' %}`,
|
|
type: 'POST',
|
|
dataType: 'json',
|
|
data: {
|
|
csrfmiddlewaretoken: '{{ csrf_token }}',
|
|
ticket_id: ticketID
|
|
},
|
|
success: function (data) {
|
|
console.log(JSON.stringify(data, null, 4));
|
|
|
|
var ticket = data.ticket;
|
|
var author = ticket.author;
|
|
var department = author.department;
|
|
var priority = ticket.priority;
|
|
|
|
$("#ticketTitle").text(ticket.title);
|
|
$("#ticketDesc").append($(`<div>${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="background-color: ${priority.colour};">${priority.title} Priority</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="background-color: ${tag.colour};">${tag.title}</div>`));
|
|
});
|
|
|
|
// timestamp
|
|
var timestamp = new Date(ticket.timestamp);
|
|
var formattedTime;
|
|
|
|
if (ticket.is_older_than_day) {
|
|
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));
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock javascripts %}
|