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 %} |