You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
12 KiB
HTML
237 lines
12 KiB
HTML
{% extends "customer_main.html" %}
|
|
{%load static%}
|
|
{% block content %}
|
|
|
|
<!-- TEXT EDITOR -->
|
|
<style>
|
|
@import url(https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/5.3.45/css/materialdesignicons.min.css);
|
|
</style>
|
|
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.js" defer></script>
|
|
|
|
|
|
<div class="w-full px-5 s:px-9 flex flex-col gap-3">
|
|
<div class="w-full bg-white rounded-md h-fit shadow-md p-5">
|
|
<div class="w-full h-fit flex flex-col gap-2 bg-gray-100 shadow-md rounded-md px-3 py-3">
|
|
<div class="w-full flex flex-col s:flex-row justify-between items-start s:items-center gap-3 mb-4 s:mb-0">
|
|
<p class="text-secondosiblue text-[20px]">Ticket <span
|
|
class="font-semibold">#{{ticket.ticket_number}}</span></p>
|
|
|
|
<button
|
|
class="w-full s:w-fit px-3 py-2 bg-red-500 border border-red-500 text-white cursor-pointer duration-300 hover:bg-white hover:text-red-500 rounded-md closeTicketButton" data-modal-url="{% url 'closeticketstatusmodal' %}">
|
|
Close Ticket
|
|
</button>
|
|
</div>
|
|
|
|
|
|
|
|
{% if last_ticket_status.status == 'Open' %}
|
|
<div class="flex justify-start items-center gap-1">
|
|
<div class="w-[16px] h-[16px] rounded-full bg-green-200 shadow-md"></div>
|
|
<p class="text-secondosiblue font-light">Opened by {{last_ticket_status.added_by.first_name}} at
|
|
{{last_ticket_status.date_added}}</p>
|
|
</div>
|
|
{% elif last_ticket_status.status == 'Working On' %}
|
|
<div class="flex justify-start items-center gap-1">
|
|
<div class="w-[16px] h-[16px] rounded-full bg-yellow-200 shadow-md"></div>
|
|
<p class="text-secondosiblue font-light">Updated to 'Working On' by
|
|
{{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}</p>
|
|
</div>
|
|
{% elif last_ticket_status.status == 'Closed' %}
|
|
<div class="flex justify-start items-center gap-1">
|
|
<div class="w-[16px] h-[16px] rounded-full bg-red-200 shadow-md"></div>
|
|
<p class="text-secondosiblue font-light">Closed by {{last_ticket_status.added_by.first_name}} at
|
|
{{last_ticket_status.date_added}}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
|
|
<div class="w-full mt-3">
|
|
<p class="text-gray-500 font-light text-sm leading-7">{{ticket.description}}
|
|
</p>
|
|
</div>
|
|
|
|
{% if ticket.ticketattachment_set.all %}
|
|
<div class="w-full flex flex-wrap justify-end items-center gap-3">
|
|
{% for file in ticket.ticketattachment_set.all %}
|
|
<div class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
|
stroke="currentColor" class="w-4 h-4 text-secondosiblue">
|
|
<path stroke-linecap="round" stroke-linejoin="round"
|
|
d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
|
|
</svg>
|
|
<a href="https://osina.ositcom.com/{{file.file_path}}" target="_blank" class="text-sm">
|
|
{{ file.file_path | cut:"static/images/uploaded_ticket_files/" }}{% if not forloop.last %}, {% endif %}</a>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
</div>
|
|
|
|
|
|
<div class="w-full flex flex-col mt-5 s:mt-10">
|
|
|
|
<div id="messages">
|
|
{% for update in ticket_updates %}
|
|
{% include 'details_templates/customer-ticket-message.html' %}
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div id="typing-notification"></div>
|
|
|
|
|
|
<!-- REPLYING SECTION -->
|
|
<form id="ticketForm" class="flex gap-3"
|
|
enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
<div>
|
|
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
|
|
{% if request.user.customerprofile %}
|
|
{% if request.user.customerprofile.image %}
|
|
<img src="{{request.user.customerprofile.image.url}}"
|
|
class="w-full h-full rounded-full object-cover">
|
|
{% else %}
|
|
<div
|
|
class="w-full h-full border border-secondosiblue bg-secondosiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
|
|
{{ request.user.first_name.0 }}{{ request.user.last_name.0 }}
|
|
</div>
|
|
{% endif %}
|
|
{% elif request.user.staffprofile %}
|
|
{% if request.user.staffprofile.image %}
|
|
<img src="{{request.user.staffprofile.image.url}}"
|
|
class="w-full h-full rounded-full object-cover">
|
|
{% else %}
|
|
<div
|
|
class="w-full h-full border border-osiblue bg-osiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
|
|
{{ request.user.first_name.0 }}{{ request.user.last_name.0 }}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="w-full flex flex-col gap-3">
|
|
|
|
<textarea name="description" rows="8" id="textEditor"
|
|
class="w-full bg-white px-3 py-3 border border-gray-200 rounded-b-md outline-none text-gray-500 resize-none"
|
|
placeholder="Add Comment..." required></textarea>
|
|
|
|
|
|
<div class="w-full flex flex-col items-end gap-3">
|
|
<div class="w-full s:w-fit flex flex-col s:flex-row justify-end items-center gap-3">
|
|
<div
|
|
class="w-full s:w-[50px] h-[50px] rounded-md bg-gray-50 shadow-md border border-gray-100 flex justify-center items-center p-2 cursor-pointer relative hover:scale-105 duration-300 transition-transform">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
|
stroke-width="1.5" stroke="currentColor"
|
|
class="w-5 h-5 text-secondosiblue z-10 absolute pointer-events-none">
|
|
<path stroke-linecap="round" stroke-linejoin="round"
|
|
d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
|
|
</svg>
|
|
<input type="file" id="fileupload" name="" placeholder="Select file" multiple
|
|
class="opacity-0 absolute top-1/2 left-1/2 w-[25px] -translate-x-1/2 -translate-y-1/2 z-10">
|
|
<select id="filePathInput" name="filePath" multiple hidden></select>
|
|
</div>
|
|
|
|
<button
|
|
class="w-full s:w-fit bg-secondosiblue border border-secondosiblue text-white rounded-md cursor-pointer hover:bg-white hover:text-secondosiblue duration-300 px-9 py-3">
|
|
Send
|
|
</button>
|
|
</div>
|
|
|
|
<div id="uploaded_files" class="w-full flex flex-col gap-3"></div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const ticketId = '{{ticket.id}}';
|
|
const wsUrl = `ws://${window.location.host}/ws/ticketroom/${ticketId}/`;
|
|
const socket = new WebSocket(wsUrl);
|
|
let typingTimeout = null;
|
|
|
|
socket.onopen = () => {
|
|
console.log('WebSocket connection established');
|
|
};
|
|
|
|
socket.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
if (data.event_type === 'typing') {
|
|
const typingDiv = document.getElementById('typing-notification');
|
|
typingDiv.innerHTML = data.html;
|
|
} else if (data.event_type === 'stop_typing') {
|
|
const typingDiv = document.getElementById('typing-notification');
|
|
typingDiv.innerHTML = '';
|
|
} else {
|
|
const typingDiv = document.getElementById('typing-notification');
|
|
const messagesDiv = document.getElementById('messages');
|
|
messagesDiv.insertAdjacentHTML('beforeend', data.html);
|
|
typingDiv.innerHTML = '';
|
|
}
|
|
};
|
|
|
|
socket.onclose = () => {
|
|
console.log('WebSocket connection closed');
|
|
};
|
|
|
|
socket.onerror = (error) => {
|
|
console.log('WebSocket error:', error);
|
|
};
|
|
|
|
const form = document.getElementById('ticketForm');
|
|
const textEditor = document.getElementById('textEditor');
|
|
|
|
textEditor.addEventListener('input', () => {
|
|
clearTimeout(typingTimeout);
|
|
const data = {
|
|
event_type: 'typing'
|
|
};
|
|
socket.send(JSON.stringify(data));
|
|
|
|
typingTimeout = setTimeout(() => {
|
|
const stopTypingData = {
|
|
event_type: 'stop_typing'
|
|
};
|
|
socket.send(JSON.stringify(stopTypingData));
|
|
}, 2000);
|
|
});
|
|
|
|
form.addEventListener('submit', (event) => {
|
|
event.preventDefault();
|
|
|
|
const formData = new FormData(form);
|
|
const description = formData.get('description');
|
|
const files = formData.getAll('filePath');
|
|
|
|
const filePaths = [];
|
|
for (const file of files) {
|
|
const filePath = `/path/to/uploaded/files/${file.name}`;
|
|
filePaths.push(filePath);
|
|
}
|
|
|
|
const data = {
|
|
event_type: 'update',
|
|
description: description,
|
|
filePath: filePaths
|
|
};
|
|
|
|
socket.send(JSON.stringify(data));
|
|
form.reset();
|
|
});
|
|
});
|
|
|
|
</script>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!---------------------- JS SCRIPTS -------------------->
|
|
<script type="text/javascript" src="{% static 'js/tickets/ticket-details.js' %}"></script>
|
|
|
|
<script type="text/javascript" src="{% static 'js/inputs/text-editor.js' %}"></script>
|
|
|
|
<script type="text/javascript" src='{% static "js/inputs/file-uploader.js" %}'></script>
|
|
|
|
{% endblock %} |