emile 10 months ago
parent 8a31343fcf
commit 7eb89e5418

Binary file not shown.

@ -0,0 +1,93 @@
from channels.generic.websocket import WebsocketConsumer
from .models import *
import json
from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
import threading
def get_last_seen(user):
connection = Connection.objects.filter(user=user).last()
if not connection.exists():
return "Not seen yet"
if connection.online:
return "Online"
last_seen_time = connection.last_seen
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 OnlineUserConsumer(WebsocketConsumer):
def connect(self):
self.user = self.scope['user']
existing_connection = Connection.objects.filter(user=self.user).last()
if existing_connection:
self.connection = existing_connection
self.connection.online = True
self.connection.disconnected = False
self.connection.save()
else:
self.connection = Connection.objects.create(user=self.user, online=True)
async_to_sync(self.channel_layer.group_add)(
'online_users', self.channel_name
)
self.accept()
self.modify_online_user()
def disconnect(self, close_code):
self.last_seen = datetime.now()
self.connection.disconnected = True
self.connection.save()
timer_thread = threading.Timer(10, self.check_disconnect_status)
timer_thread.start()
def check_disconnect_status(self):
connection = Connection.objects.filter(user=self.user).last()
if connection.disconnected:
self.connection.last_seen = self.last_seen
self.connection.online = False
self.connection.save()
self.modify_online_user()
def modify_online_user(self):
connections = Connection.objects.filter(online=True)
online_users_ids = [connection.user.id for connection in connections]
customer_connections = []
staff_connections = []
for connection in connections:
if hasattr(connection.user, 'customerprofile'):
customer_connections.append(connection)
elif hasattr(connection.user, 'staffprofile'):
staff_connections.append(connection)
print(staff_connections)
event = {
'type': 'online_user_connection_handler',
'staff_connections': staff_connections,
'customer_connections': customer_connections,
'online_users_ids': online_users_ids
}
async_to_sync(self.channel_layer.group_send)(
'online_users', event
)
def online_user_connection_handler(self, event):
context = {
'staff_connections': event['staff_connections'],
'customer_connections': event['customer_connections'],
}
html = render_to_string("details_templates/partials/recently-online.html", context=context)
self.send(text_data=json.dumps({
'event_type': 'online_user_status',
'html': html,
'online_users_ids': event.get('online_users_ids', [])
}))

@ -23,15 +23,6 @@ def calculate_time_ago(status):
def utilities(request): def utilities(request):
latest_connections = Connection.objects.filter(user__staffprofile__isnull=False).values('user').annotate(latest_connection=Max('date'))
online_staff_profiles = []
for connection in latest_connections:
user_id = connection['user']
latest_connection = connection['latest_connection']
last_connection = Connection.objects.filter(user_id=user_id, date=latest_connection).first()
if last_connection.status == 'Online':
online_staff_profiles.append(last_connection.user.staffprofile)
notes = None notes = None
recent_note = None recent_note = None
@ -45,12 +36,6 @@ def utilities(request):
if request.user.is_authenticated and StaffProfile.objects.filter(user=request.user): if request.user.is_authenticated and StaffProfile.objects.filter(user=request.user):
notes = Note.objects.filter(user=request.user).order_by('-date')[:6] notes = Note.objects.filter(user=request.user).order_by('-date')[:6]
recent_note = Note.objects.filter(user=request.user).last() recent_note = Note.objects.filter(user=request.user).last()
last_user_activity = Connection.objects.filter(user=request.user).last()
if last_user_activity and last_user_activity.status == 'Offline':
user_offline = True
else:
user_offline = False
if request.user.is_superuser: if request.user.is_superuser:
open_task_count = Task.objects.filter(status='Open').count() open_task_count = Task.objects.filter(status='Open').count()
@ -141,7 +126,6 @@ def utilities(request):
'latest_statuses_time_ago': latest_statuses_time_ago, 'latest_statuses_time_ago': latest_statuses_time_ago,
'notes' : notes, 'notes' : notes,
'recent_note' : recent_note, 'recent_note' : recent_note,
'online_staff_profiles' : online_staff_profiles,
'user_offline' : user_offline, 'user_offline' : user_offline,
'recent_logged_in_staffs' : recent_logged_in_staffs, 'recent_logged_in_staffs' : recent_logged_in_staffs,
'recent_logged_in_customers' : recent_logged_in_customers, 'recent_logged_in_customers' : recent_logged_in_customers,

@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2024-07-08 05:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0085_rename_date_staffposition_start_date_and_more'),
]
operations = [
migrations.RemoveField(
model_name='connection',
name='status',
),
migrations.AddField(
model_name='connection',
name='terminated_at',
field=models.DateTimeField(blank=True, null=True),
),
]

@ -0,0 +1,17 @@
# Generated by Django 4.2.5 on 2024-07-08 06:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0086_remove_connection_status_connection_terminated_at'),
]
operations = [
migrations.RemoveField(
model_name='connection',
name='date',
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-07-08 06:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0087_remove_connection_date'),
]
operations = [
migrations.AddField(
model_name='connection',
name='date',
field=models.DateTimeField(null=True),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-07-08 08:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0088_connection_date'),
]
operations = [
migrations.AddField(
model_name='connection',
name='connected',
field=models.BooleanField(default=True),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-07-08 08:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0089_connection_connected'),
]
operations = [
migrations.RenameField(
model_name='connection',
old_name='terminated_at',
new_name='last_seen',
),
]

@ -0,0 +1,21 @@
# Generated by Django 4.2.5 on 2024-07-08 08:55
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('osinacore', '0090_rename_terminated_at_connection_last_seen'),
]
operations = [
migrations.AlterField(
model_name='connection',
name='user',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-07-08 08:57
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0091_alter_connection_user'),
]
operations = [
migrations.RenameField(
model_name='connection',
old_name='connected',
new_name='online',
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-07-08 09:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0092_rename_connected_connection_online'),
]
operations = [
migrations.AddField(
model_name='connection',
name='disconnected',
field=models.BooleanField(default=False),
),
]

@ -388,12 +388,9 @@ class DailyReport(models.Model):
class Connection(models.Model): class Connection(models.Model):
STATUS_CHOICES = (
('Online', 'Online'),
('Offline', 'Offline'),
)
status = models.CharField(max_length=200, choices=STATUS_CHOICES, null=True)
date = models.DateTimeField(null=True) date = models.DateTimeField(null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE) user = models.OneToOneField(User, on_delete=models.CASCADE)
online = models.BooleanField(default=True)
last_seen = models.DateTimeField(null=True, blank=True)
disconnected = models.BooleanField(default=False)

@ -0,0 +1,8 @@
from django.urls import path
from .consumers import *
websocket_urlpatterns = [
path("ws/online-users/", OnlineUserConsumer.as_asgi()),
]

@ -944,6 +944,9 @@
<!---------------------- JS SCRIPTS --------------------> <!---------------------- JS SCRIPTS -------------------->
<!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/online/online-consumer.js" %}'></script>
<!-- SIDE BAR SCRIPT --> <!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/side-bar.js" %}'></script> <script type="text/javascript" src='{% static "js/side-bar.js" %}'></script>

@ -610,6 +610,9 @@
<!---------------------- JS SCRIPTS --------------------> <!---------------------- JS SCRIPTS -------------------->
<!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/online/online-consumer.js" %}'></script>
<!-- SIDE BAR SCRIPT --> <!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/side-bar.js" %}'></script> <script type="text/javascript" src='{% static "js/side-bar.js" %}'></script>

@ -126,7 +126,7 @@
{% if latest_statuses_time_ago %} {% if latest_statuses_time_ago %}
{% for latest in latest_statuses_time_ago %} {% for latest in latest_statuses_time_ago %}
<div class="w-full flex flex-col py-3"> <div class="w-full flex flex-col py-3 users-activities" data-userId="{{ latest.status.staff.user.id }}">
<div class="w-full flex flex-col justify-center items-start gap-3 bg-gray-50 pt-2 px-2 pb-6 rounded-md relative"> <div class="w-full flex flex-col justify-center items-start gap-3 bg-gray-50 pt-2 px-2 pb-6 rounded-md relative">
<div class="w-full flex justify-between items-center gap-2"> <div class="w-full flex justify-between items-center gap-2">
<div class="flex justify-start gap-2 cursor-pointer userRecentActivitiesButton" <div class="flex justify-start gap-2 cursor-pointer userRecentActivitiesButton"
@ -137,15 +137,12 @@
class="w-full h-full object-cover rounded-full"> class="w-full h-full object-cover rounded-full">
</div> </div>
{% if latest.status.staff in online_staff_profiles %} <div id="connected"
<div
class="w-[12px] h-[12px] absolute rounded-full bg-green-600 bottom-0 right-0 border-2 border-white"> class="w-[12px] h-[12px] absolute rounded-full bg-green-600 bottom-0 right-0 border-2 border-white">
</div> </div>
{% else %} <div id="not-connected"
<div
class="w-[12px] h-[12px] absolute rounded-full bg-red-500 bottom-0 right-0 border-2 border-white"> class="w-[12px] h-[12px] absolute rounded-full bg-red-500 bottom-0 right-0 border-2 border-white">
</div> </div>
{% endif %}
</div> </div>
<div class="flex flex-col"> <div class="flex flex-col">

@ -0,0 +1,61 @@
{% load static %}
<div
class="w-full xxlg1:flex flex-col justify-center items-center gap-3 bg-white shadow-md rounded-md p-5 logged-in-container">
<div class="w-full flex justify-center items-center">
<p class="text-[20px] text-secondosiblue font-bold text-center">Recently Online</p>
</div>
<div class="w-full recentltLoggedStaffsContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button
class="w-full rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton">Customers</button>
<button class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 staffButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Staffs</button>
</div>
<div class="w-full flex flex-col gap-3 mt-3">
{% for staff_connection in staff_connections %}
<div class="w-full flex justify-start items-center gap-2">
<div class="relative">
<div class="w-[40px] h-[40px] rounded-full">
<img src="{{staff_connection.user.staffprofile.image.url}}"
class="w-full h-full object-cover rounded-full">
</div>
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{staff_connection.user.first_name}}
{{staff_connection.user.last_name}}</p>
<p class="text-gray-500 text-sm">Online</p>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="w-full hidden recentltLoggedCustomersContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Customers</button>
<button class="w-full text-secondosiblue text-sm cursor-pointer p-2 staffButton">Staffs</button>
</div>
<div class="w-full mt-3 flex flex-col gap-3 recentltLoggedCustomers">
{% for customer_connection in customer_connections %}
<div class="w-full flex justify-start items-center gap-2">
<div
class="w-[40px] h-[40px] bg-secondosiblue flex justify-center items-center rounded-full text-white">
<p>{{customer_connection.user.first_name|slice:":1"}}{{customer_connection.user.last_name|slice:":1"}}
</p>
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{recent_logged_in_customer.first_name}}
{{recent_logged_in_customer.last_name}}</p>
<p class="text-gray-500 text-sm">
{{recent_logged_in_customer.last_login|date:"g:i A"}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>

@ -292,7 +292,9 @@
<script type="text/javascript" src='{% static "js/tickets/share-ticket.js" %}'></script> <script type="text/javascript" src='{% static "js/tickets/share-ticket.js" %}'></script>
<script> <script>
// WebSocket connection for new tickets // WebSocket connection for new tickets
const newTicketsSocket = new WebSocket('wss://' + window.location.host + '/ws/new-tickets/'); const ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
const newTicketsSocketUrl = `${ws_scheme}://${window.location.host}/ws/new-tickets/`;
const newTicketsSocket = new WebSocket(newTicketsSocketUrl);
newTicketsSocket.onopen = () => { newTicketsSocket.onopen = () => {
console.log('WebSocket connection to new tickets established'); console.log('WebSocket connection to new tickets established');
@ -320,7 +322,8 @@
// WebSocket connection for new ticket updates // WebSocket connection for new ticket updates
const newTicketUpdatesSocket = new WebSocket('wss://' + window.location.host + '/ws/new-ticket-updates/'); const newTicketUpdatesSocketUrl = `${ws_scheme}://${window.location.host}/ws/new-tickets/`;
const newTicketUpdatesSocket = new WebSocket(newTicketsSocketUrl);
newTicketUpdatesSocket.onopen = () => { newTicketUpdatesSocket.onopen = () => {
console.log('WebSocket connection to new ticket updates established'); console.log('WebSocket connection to new ticket updates established');

@ -1158,204 +1158,23 @@
<h1 class="text-2xl text-secondosiblue text-center font-semibold">Users Activity</h1> <h1 class="text-2xl text-secondosiblue text-center font-semibold">Users Activity</h1>
<div class="w-full h-fit mt-2" id="activitiesContainer"> <div class="w-full h-fit mt-2" id="activitiesContainer">
{% include 'details_templates/partials/recent-activities.html' %}
{% include 'recent-activities.html' %}
</div> </div>
</div> </div>
<!-- CONNECTED USERS --> <div class="connected-users">
<div class="w-full hidden xxlg1:block bg-white shadow-md rounded-md p-5"> <!-- RECENTLY LOGGED IN USERS -->
<div class="w-full h-full flex flex-col gap-3 items-center"> {% include 'details_templates/partials/recently-online.html' %}
<div class="w-full flex justify-center items-center">
<p class="text-[20px] text-secondosiblue font-bold text-center">
Connected Users
</p>
</div>
<div class="w-full flex flex-wrap gap-4 items-center justify-center">
{% for online in online_staff_profiles %}
<div class="flex flex-col justify-center items-center gap-1">
<div class="w-[30px] h-[30px] rounded-full">
<img src="{{online.image.url}}" alt="User Image"
class="w-full h-full rounded-full object-cover">
</div>
<p class="text-gray-500 text-[10px] font-light">{{online.user.first_name}}</p>
</div>
{% endfor %}
</div>
</div>
</div> </div>
<!-- RECENTLY LOGGED IN USERS -->
<div
class="w-full hidden xxlg1:flex flex-col justify-center items-center gap-3 bg-white shadow-md rounded-md p-5 logged-in-container">
<div class="w-full flex justify-center items-center">
<p class="text-[20px] text-secondosiblue font-bold text-center">Recently Logged In</p>
</div>
<div class="w-full recentltLoggedCustomersContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button
class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Customers</button>
<button
class="w-full text-secondosiblue text-sm cursor-pointer p-2 staffButton">Staffs</button>
</div>
<div class="w-full mt-3 flex flex-col gap-3 recentltLoggedCustomers">
{% for recent_logged_in_customer in recent_logged_in_customers %}
<div class="w-full flex justify-start items-center gap-2">
<div
class="w-[40px] h-[40px] bg-secondosiblue flex justify-center items-center rounded-full text-white">
<p>{{recent_logged_in_customer.first_name|slice:":1"}}{{recent_logged_in_customer.last_name|slice:":1"}}
</p>
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{recent_logged_in_customer.first_name}}
{{recent_logged_in_customer.last_name}}</p>
<p class="text-gray-500 text-sm">
{{recent_logged_in_customer.last_login|date:"g:i A"}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="w-full hidden recentltLoggedStaffsContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button
class="w-full rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton">Customers</button>
<button
class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 staffButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Staffs</button>
</div>
<div class="w-full flex flex-col gap-3 mt-3">
{% for recent_logged_in_staff in recent_logged_in_staffs %}
<div class="w-full flex justify-start items-center gap-2">
<div class="relative">
<div class="w-[40px] h-[40px] rounded-full">
<img src="{{recent_logged_in_staff.staffprofile.image.url}}"
class="w-full h-full object-cover rounded-full">
</div>
{% if recent_logged_in_staff.staffprofile in online_staff_profiles %}
<div
class="w-[12px] h-[12px] absolute rounded-full bg-green-600 bottom-0 right-0 border-2 border-white">
</div>
{% else %}
<div
class="w-[12px] h-[12px] absolute rounded-full bg-red-500 bottom-0 right-0 border-2 border-white">
</div>
{% endif %}
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{recent_logged_in_staff.first_name}}
{{recent_logged_in_staff.last_name}}</p>
<p class="text-gray-500 text-sm">{{recent_logged_in_staff.last_login|date:"g:i A"}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div> </div>
</div> </div>
<!-- CONNECTED USERS / RECENTLY LOGGED IN ON MOBILE --> <!-- RECENTLY LOGGED IN ON MOBILE -->
<div class="grid grid-cols-1 s:grid-cols-2 gap-5 px-5 s:px-9 pb-5 xxlg1:hidden"> <div class="grid grid-cols-1 s:grid-cols-2 gap-5 px-5 s:px-9 pb-5 xxlg1:hidden connected-users">
<!-- CONNECTED USERS ON MOBILE -->
<div class="w-full bg-white shadow-md rounded-md p-5">
<div class="w-full h-full flex flex-col gap-3 items-center">
<div class="w-full flex justify-center items-center">
<p class="text-[20px] text-secondosiblue font-bold text-center">
Connected Users
</p>
</div>
<div class="w-full flex flex-wrap gap-4">
{% for online in online_staff_profiles %}
<div class="flex flex-col justify-center items-center gap-1">
<div class="w-[30px] h-[30px] rounded-full">
<img src="{{online.image.url}}" alt="User Image"
class="w-full h-full rounded-full object-cover">
</div>
<p class="text-gray-500 text-[10px] font-light">{{online.user.first_name}}</p>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- RECENTLY LOGGED IN USERS ON MOBILE --> <!-- RECENTLY LOGGED IN USERS ON MOBILE -->
<div {% include 'details_templates/partials/recently-online.html' %}
class="w-full flex flex-col justify-center items-center gap-3 bg-white shadow-md rounded-md p-5 logged-in-container">
<div class="w-full flex justify-center items-center">
<p class="text-[20px] text-secondosiblue font-bold text-center">Recently Logged In</p>
</div>
<div class="w-full recentltLoggedCustomersContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button
class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Customers</button>
<button
class="w-full text-secondosiblue text-sm cursor-pointer p-2 staffButton">Staffs</button>
</div>
<div class="w-full mt-3 flex flex-col gap-3 recentltLoggedCustomers">
{% for recent_logged_in_customer in recent_logged_in_customers %}
<div class="w-full flex justify-start items-center gap-2">
<div
class="w-[40px] h-[40px] bg-secondosiblue flex justify-center items-center rounded-full text-white">
<p>{{recent_logged_in_customer.first_name|slice:":1"}}{{recent_logged_in_customer.last_name|slice:":1"}}
</p>
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{recent_logged_in_customer.first_name}}
{{recent_logged_in_customer.last_name}}</p>
<p class="text-gray-500 text-sm">
{{recent_logged_in_customer.last_login|date:"g:i A"}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
<div class="w-full hidden recentltLoggedStaffsContainer">
<div class="w-full rounded-md bg-gray-200 grid grid-cols-2 shadow-sm">
<button
class="w-full rounded-md text-secondosiblue text-sm cursor-pointer p-2 customerButton">Customers</button>
<button
class="w-full bg-white rounded-md text-secondosiblue text-sm cursor-pointer p-2 staffButton"
style="box-shadow: 0 0 6px rgba(88, 88, 88, 0.043), 6px 0 6px rgba(88, 88, 88, 0.043), 0 6px 6px rgba(88, 88, 88, 0.043), -6px 0 6px rgba(88, 88, 88, 0.043);">Staffs</button>
</div>
<div class="w-full flex flex-col gap-3 mt-3">
{% for recent_logged_in_staff in recent_logged_in_staffs %}
<div class="w-full flex justify-start items-center gap-2">
<div class="relative">
<div class="w-[40px] h-[40px] rounded-full">
<img src="{{recent_logged_in_staff.staffprofile.image.url}}"
class="w-full h-full object-cover rounded-full">
</div>
{% if recent_logged_in_staff.staffprofile in online_staff_profiles %}
<div
class="w-[12px] h-[12px] absolute rounded-full bg-green-600 bottom-0 right-0 border-2 border-white">
</div>
{% else %}
<div
class="w-[12px] h-[12px] absolute rounded-full bg-red-500 bottom-0 right-0 border-2 border-white">
</div>
{% endif %}
</div>
<div class="flex flex-col">
<p class="text-secondosiblue text-sm">{{recent_logged_in_staff.first_name}}
{{recent_logged_in_staff.last_name}}</p>
<p class="text-gray-500 text-sm">
{{recent_logged_in_staff.last_login|date:"g:i A"}}</p>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div> </div>
<!-- MOBILE FOOTER --> <!-- MOBILE FOOTER -->
@ -1398,9 +1217,15 @@
</div> </div>
</div> </div>
<script type="module" src='{% static "js/pop-modals.js" %}'></script> <script type="module" src='{% static "js/pop-modals.js" %}'></script>
<!---------------------- JS SCRIPTS --------------------> <!---------------------- JS SCRIPTS -------------------->
<!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/online/online-consumer.js" %}'></script>
<!-- SIDE BAR SCRIPT --> <!-- SIDE BAR SCRIPT -->
<script type="text/javascript" src='{% static "js/side-bar.js" %}'></script> <script type="text/javascript" src='{% static "js/side-bar.js" %}'></script>
@ -1431,9 +1256,6 @@
<!-- NOTIFICATIONS SIDE BAR --> <!-- NOTIFICATIONS SIDE BAR -->
<script type="text/javascript" src='{% static "js/notifications-side-bar.js" %}'></script> <script type="text/javascript" src='{% static "js/notifications-side-bar.js" %}'></script>
<!-- TO SWITCH BETWEEN THE CUSTOMERS AND STAFFS TABS IN THE RECENTLY LOGGED IN CONTAINER -->
<script type="text/javascript" src='{% static "js/recently-logged-in-users.js" %}'></script>
<!-- MODULES DROP DOWN ON MOBILE --> <!-- MODULES DROP DOWN ON MOBILE -->
<script type="text/javascript" src='{% static "js/accessibilities-dropdown.js" %}'></script> <script type="text/javascript" src='{% static "js/accessibilities-dropdown.js" %}'></script>
</body> </body>

@ -54,7 +54,6 @@ def signin(request):
if user is not None: if user is not None:
login(request, user) login(request, user)
Connection.objects.create(status='Online', date=datetime.now(), user=user)
if next_page: if next_page:
return redirect(next_page) return redirect(next_page)
else: else:
@ -205,7 +204,7 @@ def home(request, *args, **kwargs):
@staff_login_required @staff_login_required
def status_mobile_modal (request, *args, **kwargs): def status_mobile_modal(request, *args, **kwargs):
context = { context = {
} }
@ -733,7 +732,7 @@ def get_updated_last_status(request):
'hours_minutes_ago': hours_minutes_ago, 'hours_minutes_ago': hours_minutes_ago,
} }
recent_status = render_to_string('recent-status.html', response_data) recent_status = render_to_string('details_templates/partials/recent-status.html', response_data)
return HttpResponse(recent_status) return HttpResponse(recent_status)
@ -741,31 +740,6 @@ def get_updated_last_status(request):
# TO GET USER ACTIVITIES # TO GET USER ACTIVITIES
@staff_login_required @staff_login_required
def get_latest_activities(request): def get_latest_activities(request):
latest_connections = Connection.objects.filter(
user__staffprofile__isnull=False
).values('user').annotate(
latest_connection=Max('date')
)
online_staff_profiles = []
for connection in latest_connections:
user_id = connection['user']
latest_connection = connection['latest_connection']
last_connection = Connection.objects.filter(user_id=user_id, date=latest_connection).first()
if last_connection.status == 'Online':
online_staff_profiles.append(last_connection.user.staffprofile)
if request.user.is_authenticated and request.user.is_superuser:
open_task_count = Task.objects.filter(status='Open').count()
working_on_task_count = Task.objects.filter(status='Working On').count()
elif request.user.is_authenticated:
open_task_count = Task.objects.filter(assigned_to=request.user.staffprofile, status='Open').count()
working_on_task_count = Task.objects.filter(assigned_to=request.user.staffprofile, status='Working On').count()
else:
open_task_count = 0
working_on_task_count = 0
total_tasks = open_task_count + working_on_task_count
today = datetime.now().date() today = datetime.now().date()
@ -776,9 +750,8 @@ def get_latest_activities(request):
latest_statuses_time_ago = [{'status': status, 'time_ago': calculate_time_ago(status)} for status in latest_statuses] latest_statuses_time_ago = [{'status': status, 'time_ago': calculate_time_ago(status)} for status in latest_statuses]
response_data = { response_data = {
'total_tasks': total_tasks,
'latest_statuses_time_ago': latest_statuses_time_ago, 'latest_statuses_time_ago': latest_statuses_time_ago,
'online_staff_profiles' : online_staff_profiles,
} }
recent_activities = render_to_string('recent-activities.html', response_data) recent_activities = render_to_string('recent-activities.html', response_data)

@ -13,7 +13,7 @@ from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack from channels.auth import AuthMiddlewareStack
from support import routing from osinaweb.routing import websocket_urlpatterns
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'osinaweb.settings') os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'osinaweb.settings')
@ -23,7 +23,7 @@ django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({ application = ProtocolTypeRouter({
"http": django_asgi_app, "http": django_asgi_app,
"websocket": AllowedHostsOriginValidator( "websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)) AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
), ),
}) })

@ -0,0 +1,4 @@
from support.routing import websocket_urlpatterns as support_websocket_urlpatterns
from osinacore.routing import websocket_urlpatterns as osinacore_websocket_urlpatterns
websocket_urlpatterns = support_websocket_urlpatterns + osinacore_websocket_urlpatterns

Binary file not shown.

After

Width:  |  Height:  |  Size: 711 KiB

@ -0,0 +1,67 @@
document.addEventListener("DOMContentLoaded", function () {
const ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
const webSocketUrl = `${ws_scheme}://${window.location.host}/ws/online-users/`;
const webSocket = new WebSocket(webSocketUrl);
webSocket.onopen = function (event) {
console.log("WebSocket connection to online established");
};
webSocket.onmessage = function (event) {
const data = JSON.parse(event.data);
if (data.event_type === "online_user_status") {
// Update connected users containers
const connectedUsersContainers = document.querySelectorAll(".connected-users");
connectedUsersContainers.forEach(container => {
container.innerHTML = data.html;
});
// Rebind event listeners to switch between containers
const customerButtons = document.querySelectorAll('.customerButton');
const staffButtons = document.querySelectorAll('.staffButton');
const customerContainers = document.querySelectorAll('.recentltLoggedCustomersContainer');
const staffContainers = document.querySelectorAll('.recentltLoggedStaffsContainer');
customerButtons.forEach(button => {
button.addEventListener('click', function () {
customerContainers.forEach(container => container.classList.remove('hidden'));
staffContainers.forEach(container => container.classList.add('hidden'));
});
});
staffButtons.forEach(button => {
button.addEventListener('click', function () {
staffContainers.forEach(container => container.classList.remove('hidden'));
customerContainers.forEach(container => container.classList.add('hidden'));
});
});
const onlineUsersIds = data.online_users_ids || [];
console.log(onlineUsersIds);
// Update user activity containers based on online status
const userActivityContainers = document.querySelectorAll(".users-activities");
userActivityContainers.forEach(container => {
const userId = container.getAttribute("data-userId");
console.log(userId);
if (onlineUsersIds.map(id => id.toString()).includes(userId)) {
container.querySelector("#connected").classList.remove("hidden");
container.querySelector("#not-connected").classList.add("hidden");
} else {
container.querySelector("#connected").classList.add("hidden");
container.querySelector("#not-connected").classList.remove("hidden");
}
});
}
};
webSocket.onclose = function (event) {
console.log("WebSocket connection to online closed");
};
webSocket.onerror = function (error) {
console.error("WebSocket error:", error);
};
});

@ -1,20 +0,0 @@
document.addEventListener('DOMContentLoaded', function () {
const customerButtons = document.querySelectorAll('.customerButton');
const staffButtons = document.querySelectorAll('.staffButton');
const customerContainers = document.querySelectorAll('.recentltLoggedCustomersContainer');
const staffContainers = document.querySelectorAll('.recentltLoggedStaffsContainer');
customerButtons.forEach(button => {
button.addEventListener('click', function () {
customerContainers.forEach(container => container.classList.remove('hidden'));
staffContainers.forEach(container => container.classList.add('hidden'));
});
});
staffButtons.forEach(button => {
button.addEventListener('click', function () {
staffContainers.forEach(container => container.classList.remove('hidden'));
customerContainers.forEach(container => container.classList.add('hidden'));
});
});
});
Loading…
Cancel
Save