emile 10 months ago
parent 988ac3a2d7
commit e77014d6eb

@ -0,0 +1,106 @@
{% load static %}
<div class="flex gap-3 mb-10">
<div>
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
{% if update.added_by.customerprofile %}
{% if update.added_by.customerprofile.image %}
<img src="{{update.added_by.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">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% elif update.added_by.staffprofile %}
{% if update.added_by.staffprofile.image %}
<img src="{{update.added_by.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">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% endif %}
</div>
</div>
<div class="w-full replyContainer shadow-md">
<div
class="w-full bg-gray-100 flex justify-between items-center gap-3 px-3 py-3 cursor-pointer rounded-t-md toggleReply">
<p class="text-secondosiblue font-light text-sm s:text-base"><span
class="font-semibold">{{update.added_by.first_name}}</span>
replied {{update.date_added}}</p>
<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 arrowUp">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>
<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 arrowDown hidden">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</div>
<div class="w-full bg-white p-5 flex flex-col gap-3 reply default-css">
{{update.description | safe }}
{% if update.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
{% for file in update.ticketattachment_set.all %}
<div
class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer text-sm">
<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">
{{ file.file_path | cut:"static/images/uploaded_ticket_files/" }}{% if not forloop.last %}, {% endif %}
</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% if update.added_by.staffprofile %}
<div class="w-full border-t border-gray-200 pt-5 flex justify-start items-center gap-3 p-5">
<a class="text-secondosiblue font-light cursor-pointer hover:text-gray-500 duration-300">How
did I do?</a>
<div class="flex justify-start items-center gap-2">
<a href="{% url 'customeraddticketupdatereaction' 'Happy' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Happy' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/happy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Indifferent' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Indifferent' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/neutral-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Sad' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Sad' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/unhappy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
</div>
</div>
{% endif %}
</div>
</div>

@ -69,120 +69,19 @@
</div>
<div class="w-full flex flex-col gap-8 mt-5 s:mt-10">
<!-- REPLY 1 -->
<div class="w-full flex flex-col mt-5 s:mt-10">
{% for update in ticket_updates %}
<div class="flex gap-3">
<div>
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
{% if update.added_by.customerprofile %}
{% if update.added_by.customerprofile.image %}
<img src="{{update.added_by.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">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% elif update.added_by.staffprofile %}
{% if update.added_by.staffprofile.image %}
<img src="{{update.added_by.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">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% endif %}
</div>
</div>
<div class="w-full replyContainer shadow-md">
<div
class="w-full bg-gray-100 flex justify-between items-center gap-3 px-3 py-3 cursor-pointer rounded-t-md toggleReply">
<p class="text-secondosiblue font-light text-sm s:text-base"><span
class="font-semibold">{{update.added_by.first_name}}</span>
replied {{update.date_added}}</p>
<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 arrowUp">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>
<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 arrowDown hidden">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</div>
<div class="w-full bg-white p-5 flex flex-col gap-3 reply default-css">
{{update.description | safe }}
{% if update.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
{% for file in update.ticketattachment_set.all %}
<div
class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer text-sm">
<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">
{{ file.file_path | cut:"static/images/uploaded_ticket_files/" }}{% if not forloop.last %}, {% endif %}
</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% if update.added_by.staffprofile %}
<div class="w-full border-t border-gray-200 pt-5 flex justify-start items-center gap-3 p-5">
<a class="text-secondosiblue font-light cursor-pointer hover:text-gray-500 duration-300">How
did I do?</a>
<div class="flex justify-start items-center gap-2">
<a href="{% url 'customeraddticketupdatereaction' 'Happy' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Happy' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/happy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Indifferent' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Indifferent' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/neutral-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Sad' update.id %}">
<div
class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Sad' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/unhappy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
</div>
</div>
{% endif %}
</div>
<div id="messages">
{% for update in ticket_updates %}
{% include 'details_templates/customer-ticket-message.html' %}
{% endfor %}
</div>
{% endfor %}
<div id="typing-notification"></div>
<!-- REPLYING SECTION -->
<form class="flex gap-3" method="POST" action="{% url 'customeraddticketupdate' ticket.id %}"
<form id="ticketForm" class="flex gap-3"
enctype="multipart/form-data">
{% csrf_token %}
<div>
@ -213,103 +112,9 @@
<div class="w-full flex flex-col gap-3">
<div class="w-full rounded-md text-black" x-data="app()" x-init="init($refs.wysiwyg)">
<div class="border border-gray-200 overflow-hidden rounded-md">
<div class="w-full border-b border-gray-200 flex flex-wrap text-xl text-gray-600">
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('bold')">
<i class="mdi mdi-format-bold"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('italic')">
<i class="mdi mdi-format-italic"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('underline')">
<i class="mdi mdi-format-underline"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','P')">
<i class="mdi mdi-format-paragraph"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H1')">
<i class="mdi mdi-format-header-1"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H2')">
<i class="mdi mdi-format-header-2"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H3')">
<i class="mdi mdi-format-header-3"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('insertUnorderedList')">
<i class="mdi mdi-format-list-bulleted"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('insertOrderedList')">
<i class="mdi mdi-format-list-numbered"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
id="rightToLeft" @click="setDirection('rtl')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
class="w-[18px] h-[18px]" fill="none">
<path d="M15 3.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M15 9.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M3 15.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M3 21.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path
d="M3.58579 9.91421C4.17157 10.5 5.11438 10.5 7 10.5C8.88562 10.5 9.82843 10.5 10.4142 9.91421C11 9.32843 11 8.38562 11 6.5C11 4.61438 11 3.67157 10.4142 3.08579C9.82843 2.5 8.88562 2.5 7 2.5C5.11438 2.5 4.17157 2.5 3.58579 3.08579C3 3.67157 3 4.61438 3 6.5C3 8.38562 3 9.32843 3.58579 9.91421Z"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
id="leftToLeft" @click="setDirection('ltr')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
class="w-[18px] h-[18px]" fill="none">
<path d="M3 3.5H9" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M3 9.5H9" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M3 15.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path d="M3 21.5H21" stroke="currentColor" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round" />
<path
d="M13.5858 9.91421C14.1716 10.5 15.1144 10.5 17 10.5C18.8856 10.5 19.8284 10.5 20.4142 9.91421C21 9.32843 21 8.38562 21 6.5C21 4.61438 21 3.67157 20.4142 3.08579C19.8284 2.5 18.8856 2.5 17 2.5C15.1144 2.5 14.1716 2.5 13.5858 3.08579C13 3.67157 13 4.61438 13 6.5C13 8.38562 13 9.32843 13.5858 9.91421Z"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
</div>
<div class="w-full">
<iframe x-ref="wysiwyg" class="w-full h-[300px] overflow-y-auto"></iframe>
</div>
</div>
</div>
<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 hidden"
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>
@ -338,6 +143,85 @@
</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>

@ -204,9 +204,7 @@ def customer_ticket_details(request, ticket_number):
}
return render(request, 'details_templates/inner-customer-ticket.html', context)
# PRODUCTS
@customer_login_required

Binary file not shown.

@ -77,6 +77,8 @@ class TicketRoomConsumer(WebsocketConsumer):
'update': update,
'user': self.user
}
html = render_to_string("details_templates/new-ticket-message.html", context=context)
self.send(text_data=json.dumps({
'event_type': 'update',

@ -79,7 +79,7 @@
<div id="messages">
{% for update in ticket_updates %}
{% include 'details_templates/ticket-message.html' %}
{% include 'details_templates/ticket-message.html' %}
{% endfor %}
</div>
@ -158,7 +158,7 @@
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const ticketId = '{{ticket.id}}'; // Use the appropriate way to pass the ticket ID
const ticketId = '{{ticket.id}}';
const wsUrl = `ws://${window.location.host}/ws/ticketroom/${ticketId}/`;
const socket = new WebSocket(wsUrl);
let typingTimeout = null;

Loading…
Cancel
Save