new
parent
ccbec47cab
commit
988ac3a2d7
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,99 @@
|
|||||||
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from customercore.models import *
|
||||||
|
import json
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
|
from channels.generic.websocket import WebsocketConsumer
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from customercore.models import *
|
||||||
|
import json
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from asgiref.sync import async_to_sync
|
||||||
|
|
||||||
|
|
||||||
|
class TicketRoomConsumer(WebsocketConsumer):
|
||||||
|
def connect(self):
|
||||||
|
self.user = self.scope['user']
|
||||||
|
self.ticket_id = self.scope['url_route']['kwargs']['ticket_id']
|
||||||
|
self.ticket = get_object_or_404(Ticket, id=self.ticket_id)
|
||||||
|
self.ticket_number = self.ticket.ticket_number
|
||||||
|
async_to_sync(self.channel_layer.group_add)(
|
||||||
|
self.ticket_number, self.channel_name
|
||||||
|
)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
|
def disconnect(self, close_code):
|
||||||
|
async_to_sync(self.channel_layer.group_discard)(
|
||||||
|
self.ticket_number, self.channel_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def receive(self, text_data):
|
||||||
|
text_data_json = json.loads(text_data)
|
||||||
|
event_type = text_data_json.get('event_type')
|
||||||
|
|
||||||
|
if event_type == 'typing':
|
||||||
|
event = {
|
||||||
|
'type': 'typing_handler',
|
||||||
|
'user': self.scope['user']
|
||||||
|
}
|
||||||
|
async_to_sync(self.channel_layer.group_send)(
|
||||||
|
self.ticket_number, event
|
||||||
|
)
|
||||||
|
elif event_type == 'stop_typing':
|
||||||
|
event = {
|
||||||
|
'type': 'stop_typing_handler',
|
||||||
|
}
|
||||||
|
async_to_sync(self.channel_layer.group_send)(
|
||||||
|
self.ticket_number, event
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
body = text_data_json['description']
|
||||||
|
file_paths = text_data_json['filePath']
|
||||||
|
ticketupdate = TicketUpdate.objects.create(
|
||||||
|
added_by=self.user,
|
||||||
|
description=body,
|
||||||
|
ticket=self.ticket,
|
||||||
|
date_added=datetime.now()
|
||||||
|
)
|
||||||
|
for file_path in file_paths:
|
||||||
|
ticket_attachment = TicketAttachment(
|
||||||
|
ticket_update=ticketupdate,
|
||||||
|
file_path=file_path
|
||||||
|
)
|
||||||
|
ticket_attachment.save()
|
||||||
|
event = {
|
||||||
|
'type': 'update_handler',
|
||||||
|
'update_id': ticketupdate.id
|
||||||
|
}
|
||||||
|
async_to_sync(self.channel_layer.group_send)(
|
||||||
|
self.ticket_number, event
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_handler(self, event):
|
||||||
|
update_id = event['update_id']
|
||||||
|
update = TicketUpdate.objects.get(id=update_id)
|
||||||
|
context = {
|
||||||
|
'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',
|
||||||
|
'html': html
|
||||||
|
}))
|
||||||
|
|
||||||
|
def typing_handler(self, event):
|
||||||
|
context = {
|
||||||
|
'user': event['user']
|
||||||
|
}
|
||||||
|
html = render_to_string("details_templates/typing-message.html", context=context)
|
||||||
|
self.send(text_data=json.dumps({
|
||||||
|
'event_type': 'typing',
|
||||||
|
'html': html
|
||||||
|
}))
|
||||||
|
|
||||||
|
def stop_typing_handler(self, event):
|
||||||
|
self.send(text_data=json.dumps({
|
||||||
|
'event_type': 'stop_typing'
|
||||||
|
}))
|
@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from .consumers import *
|
||||||
|
|
||||||
|
websocket_urlpatterns = [
|
||||||
|
path("ws/ticketroom/<int:ticket_id>/", TicketRoomConsumer.as_asgi()),
|
||||||
|
|
||||||
|
|
||||||
|
]
|
Binary file not shown.
@ -0,0 +1,19 @@
|
|||||||
|
<div id="messages" hx-swap-oob="beforeend">
|
||||||
|
|
||||||
|
<div class="fade-in-up">
|
||||||
|
{% include 'details_templates/ticket-message.html' %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes fadeInAndUp {
|
||||||
|
from { opacity: 0; transform: translateY(12px); }
|
||||||
|
to { opacity: 1; transform: translateY(0px); }
|
||||||
|
}
|
||||||
|
.fade-in-up {
|
||||||
|
animation: fadeInAndUp 0.6s ease;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,69 @@
|
|||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -0,0 +1,36 @@
|
|||||||
|
<div id="typing-notification" 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 user.customerprofile %}
|
||||||
|
{% if user.customerprofile.image %}
|
||||||
|
<img src="{{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">
|
||||||
|
{{ user.first_name.0 }}{{ user.last_name.0 }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% elif user.staffprofile %}
|
||||||
|
{% if user.staffprofile.image %}
|
||||||
|
<img src="{{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">
|
||||||
|
{{ user.first_name.0 }}{{ user.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">{{user.first_name}}</span> is typing... </p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 3.7 MiB |
Binary file not shown.
After Width: | Height: | Size: 854 KiB |
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue