emile 10 months ago
parent c19a9d7bf8
commit 03f40cb8db

Binary file not shown.

@ -20,12 +20,20 @@ class TicketRoomConsumer(WebsocketConsumer):
self.ticket = get_object_or_404(Ticket, id=self.ticket_id)
self.ticket_number = self.ticket.ticket_number
existing_connection = TicketConnection.objects.filter(ticket=self.ticket, user=self.user, terminated=False).delete()
TicketConnection.objects.create(
ticket=self.ticket,
user=self.user,
type='Online',
date=datetime.now()
)
staff_profile = StaffProfile.objects.filter(user=self.user).first()
if staff_profile:
if not TicketStaff.objects.filter(staff=staff_profile, ticket=self.ticket).exists():
TicketStaff.objects.create(
staff=staff_profile,
ticket=self.ticket,
date_added=datetime.now()
)
async_to_sync(self.channel_layer.group_add)(
self.ticket_number, self.channel_name
@ -38,8 +46,7 @@ class TicketRoomConsumer(WebsocketConsumer):
TicketConnection.objects.filter(
ticket=self.ticket,
user=self.user,
type='Online'
).delete()
).update(terminated=True)
async_to_sync(self.channel_layer.group_discard)(
self.ticket_number, self.channel_name
)
@ -164,7 +171,7 @@ class TicketRoomConsumer(WebsocketConsumer):
}))
def modify_online_user(self):
connections = TicketConnection.objects.filter(ticket=self.ticket, type='Online')
connections = TicketConnection.objects.filter(ticket=self.ticket, terminated=False)
event = {
'type': 'user_connection_handler',
'user': self.user,

@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2024-07-03 06:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('support', '0005_alter_ticketconnection_ticket'),
]
operations = [
migrations.RemoveField(
model_name='ticketconnection',
name='type',
),
migrations.AddField(
model_name='ticketconnection',
name='terminated',
field=models.BooleanField(default=False),
),
]

@ -0,0 +1,20 @@
# Generated by Django 4.2.5 on 2024-07-03 07:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0085_rename_date_staffposition_start_date_and_more'),
('support', '0006_remove_ticketconnection_type_and_more'),
]
operations = [
migrations.AlterField(
model_name='ticketstaff',
name='staff',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.staffprofile'),
),
]

@ -0,0 +1,19 @@
# Generated by Django 4.2.5 on 2024-07-03 07:50
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('support', '0007_alter_ticketstaff_staff'),
]
operations = [
migrations.AlterField(
model_name='tickettask',
name='ticket',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='support.ticket'),
),
]

@ -9,6 +9,7 @@ from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from threading import Timer
from datetime import time
def send_ticket(ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
@ -74,10 +75,6 @@ def send_update(update_id):
# Create your models here.
class Ticket(models.Model):
@ -109,6 +106,30 @@ class Ticket(models.Model):
super().save(*args, **kwargs)
def get_customer_last_seen(self):
connections = TicketConnection.objects.filter(user=self.customer.user).order_by('-date')
if not connections.exists():
return "Not seen yet"
last_connection = connections.first()
if not last_connection.terminated:
return "Online"
last_seen_time = last_connection.date
now = timezone.now()
time_diff = now - last_seen_time
if time_diff < timedelta(days=1):
if last_seen_time.date() == now.date():
return f"last seen today at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen yesterday at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen on {last_seen_time.strftime('%b %d at %I:%M %p')}"
@receiver(post_save, sender=Ticket)
def send_signal_on_save(sender, instance, created, **kwargs):
@ -127,9 +148,32 @@ class TicketDepartment(models.Model):
class TicketStaff(models.Model):
staff = models.ForeignKey(StaffPosition, on_delete=models.CASCADE)
staff = models.ForeignKey(StaffProfile, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
date_added = models.DateTimeField()
def get_last_seen(self):
connections = TicketConnection.objects.filter(user=self.staff.user).order_by('-date')
if not connections.exists():
return "Not seen yet"
last_connection = connections.first()
if not last_connection.terminated:
return "Online"
last_seen_time = last_connection.date
now = timezone.now()
time_diff = now - last_seen_time
if time_diff < timedelta(days=1):
if last_seen_time.date() == now.date():
return f"last seen today at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen yesterday at {last_seen_time.strftime('%I:%M %p')}"
else:
return f"last seen on {last_seen_time.strftime('%b %d at %I:%M %p')}"
class TicketStatus(models.Model):
STATUS_CHOICES = (
@ -143,6 +187,20 @@ class TicketStatus(models.Model):
date_added = models.DateTimeField()
class TicketTask(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
class TicketConnection(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateTimeField()
terminated = models.BooleanField(default=False)
class TicketUpdate(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
description = models.TextField(null=True, blank=True)
@ -180,19 +238,3 @@ class TicketUpdateReaction(models.Model):
reaction = models.CharField(max_length=50, choices=REACTION_CHOICES, null=True)
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
class TicketTask(models.Model):
ticket = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
class TicketConnection(models.Model):
CONNECTION_CHOICES = (
('Online', 'Online'),
('Offline', 'Offline'),
)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
type = models.CharField(max_length=50, choices=CONNECTION_CHOICES, null=True)
date = models.DateTimeField()

@ -26,7 +26,9 @@
<div class="w-full s:w-fit flex flex-col s:flex-row justify-end items-center gap-2">
<a href="{% url 'ticketsettings' %}" class="w-full s:w-fit">
{% if request.user.staffprofile %}
<a href="{% url 'ticketsettings' ticket.ticket_number %}" class="w-full s:w-fit">
<button
class="w-full s:w-fit px-3 py-2 bg-transparent border border-osiblue text-osiblue cursor-pointer duration-300 hover:bg-osiblue hover:text-white rounded-md flex justify-center items-center gap-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-5" fill="none">
@ -40,6 +42,7 @@
Settings
</button>
</a>
{% endif %}
<button

@ -25,26 +25,14 @@
<div class="w-full flex flex-col gap-3 mt-3">
{% for department in ticket_departments %}
<div
class="w-full rounded-md py-5 px-3 bg-secondosiblue border border-secondosiblue flex justify-between items-center gap-3 text-gray-50">
<p class="text-sm sm:text-[17px]">Departnet Name</p>
class="w-full rounded-md py-5 px-3 {% if forloop.first %} bg-secondosiblue border border-secondosiblue {% else %} bg-gray-50 border border-gray-100 {% endif %} flex justify-between items-center gap-3 text-gray-50">
<p class="text-sm sm:text-[17px]">{{department.department.name}}</p>
<p class="text-sm sm:text-base">10-2-2024</p>
</div>
<div
class="w-full rounded-md py-5 px-3 bg-gray-50 border border-gray-100 flex justify-between items-center gap-3 text-secondosiblue">
<p class="text-sm sm:text-[17px]">Departnet Name</p>
<p class="text-sm sm:text-base">10-2-2024</p>
</div>
<div
class="w-full rounded-md py-5 px-3 bg-gray-50 border border-gray-100 flex justify-between items-center gap-3 text-secondosiblue">
<p class="text-sm sm:text-[17px]">Departnet Name</p>
<p class="text-sm sm:text-base">10-2-2024</p>
<p class="text-sm sm:text-base">{{department.date_added}}</p>
</div>
{% endfor %}
</div>
</div>
@ -65,15 +53,41 @@
<div class="w-full grid grid-cols-1 l:grid-cols-2 xl:grid-cols-3 gap-3 mt-3">
<!-- PROJECT MANAGER -->
<a>
<a href="{% url 'customerdetails' ticket.customer.customer_id %}">
<div
class="w-full h-full flex flex-col gap-1 px-3 py-3 bg-secondosiblue rounded-md shadow-md cursor-pointer hover:scale-105 duration-500">
<div class="w-full flex justify-between items-center gap-1">
<div class="flex justify-start items-center gap-2">
<div class="w-[45px] h-[45px] rounded-full bg-white">
<div
class="w-full h-full border border-white bg-white text-secondosiblue uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ ticket.customer.user.first_name.0 }}{{ ticket.customer.user.last_name.0 }}
</div>
</div>
<div>
<p class="text-white font-light">{{ticket.customer.user.first_name}} {{ticket.customer.user.last_name}}</p>
<div class="text-xs text-white py-1">
<p>{{customer_last_seen}}</p>
</div>
</div>
</div>
</div>
</div>
</a>
{% for member in ticket_members %}
<a href="{% url 'userdetails' member.staff.staff_id %}">
<div
class="w-full h-full flex flex-col gap-1 px-3 py-3 bg-gray-100 rounded-md shadow-md cursor-pointer hover:scale-105 duration-500">
<div class="w-full flex justify-between items-center gap-1">
<div class="flex justify-start items-center gap-2">
<div class="w-[45px] h-[45px] rounded-full bg-white">
{% if project.manager.image %}
<img src="{{member.image.url}}" class="w-full h-full rounded-full object-cover">
{% if member.staff.image %}
<img src="{{member.staff.image.url}}" class="w-full h-full rounded-full object-cover">
{% else %}
<img src="{% static 'images/default-user.png' %}"
class="w-full h-full rounded-full object-cover">
@ -82,9 +96,9 @@
<div>
<p class="text-secondosiblue font-light">Emile Elliye</p>
<div class="text-xs bg-secondosiblue text-white shadow-md py-1 px-2 rounded-md">
<p>Project Manager</p>
<p class="text-secondosiblue font-light">{{member.staff.user.first_name}} {{member.staff.user.last_name}}</p>
<div class="text-xs text-secondosiblue py-1">
<p>{{member.get_last_seen}}</p>
</div>
</div>
</div>
@ -98,6 +112,7 @@
</div>
</div>
</a>
{% endfor %}
</div>
</div>
</div>

@ -21,7 +21,7 @@ from django.conf import settings
urlpatterns = [
path('tickets/<str:ticket_number>/', views.ticket_room, name='ticketroom'),
path('ticket-settings', views.ticket_settings, name='ticketsettings'),
path('tickets/<str:ticket_number>/settings/', views.ticket_settings, name='ticketsettings'),
path('add-ticket-department', views.add_ticket_department_modal, name='addticketdepartmentmodal'),
path('add-ticket-member', views.add_ticket_member_modal, name='addticketmembermodal'),

@ -1,10 +1,7 @@
from django.shortcuts import render, get_object_or_404
from .models import *
from .decorators import *
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
from osinacore.decorators import *
# Create your views here.
@ticket_member_required
@ -37,10 +34,44 @@ def ticket_room(request, ticket_number):
@ticket_member_required
def ticket_settings(request):
def get_last_seen_order_key(member):
last_seen_text = member.get_last_seen()
if last_seen_text == "Online":
return (0, datetime.max)
elif "last seen today" in last_seen_text:
time_part = last_seen_text.split(" at ")[-1]
last_seen_time = datetime.strptime(time_part, '%I:%M %p')
return (1, last_seen_time)
elif "last seen yesterday" in last_seen_text:
time_part = last_seen_text.split(" at ")[-1]
last_seen_time = datetime.now() - timedelta(days=1)
last_seen_time = last_seen_time.replace(hour=datetime.strptime(time_part, '%I:%M %p').hour,
minute=datetime.strptime(time_part, '%I:%M %p').minute)
return (2, last_seen_time)
elif "last seen on" in last_seen_text:
date_time_part = last_seen_text.split(" on ")[-1]
last_seen_time = datetime.strptime(date_time_part, '%b %d at %I:%M %p')
return (3, last_seen_time)
else:
return (4, datetime.min)
@staff_login_required
def ticket_settings(request, ticket_number):
ticket = get_object_or_404(Ticket, ticket_number=ticket_number)
ticket_departments = TicketDepartment.objects.filter(ticket=ticket).order_by('-id')
ticket_members = TicketStaff.objects.filter(ticket=ticket)
ticket_members = sorted(ticket_members, key=get_last_seen_order_key)
customer_last_seen = ticket.get_customer_last_seen()
context = {
'ticket': ticket,
'ticket_departments': ticket_departments,
'ticket_members': ticket_members,
'customer_last_seen': customer_last_seen,
}

Loading…
Cancel
Save