emile 9 months ago
parent 909b7bdb39
commit 5b27f5284f

Binary file not shown.

@ -60,13 +60,19 @@ class OsitcomVisitor(WebsocketConsumer):
'visitors': visitors,
}
if self.client_type == 'mobile_admin':
visitor_data = []
visitors_data = []
for visitor in visitors:
visitor_data = model_to_dict(visitor)
visitor_dict = model_to_dict(visitor)
visitor_logs = VisitorLog.objects.filter(visitor=visitor)
visitor_logs_data = [model_to_dict(log) for log in visitor_logs]
visitor_dict['visitor_logs'] = visitor_logs_data
visitors_data.append(visitor_dict)
self.send(text_data=json.dumps({
'event_type': 'get_visitors',
'visitor_data': visitor_data,
'visitors_data': visitors_data,
}, cls=DjangoJSONEncoder))
@ -147,6 +153,7 @@ class OsitcomChatRooms(WebsocketConsumer):
chat_room = get_object_or_404(ChatRoom, id=event['chatroom_id'])
user = get_object_or_404(User, id=self.user_id)
number_of_unread = ChatMessage.objects.filter(room=chat_room).exclude(member=user).exclude(chatmessageseen__member=user).count()
last_message = ChatMessage.objects.filter(room=chat_room).last()
context = {
'chat_room': chat_room,
@ -163,6 +170,7 @@ class OsitcomChatRooms(WebsocketConsumer):
self.send(text_data=json.dumps({
'event_type': 'new_update',
'chatroom_id': chat_room.id,
'user': last_message.member.id if last_message and last_message.member else None,
'html': html,
}))
@ -237,10 +245,6 @@ class OsitcomChatRoom(WebsocketConsumer):
)
self.chat_room = chat_room
self.group = f"{self.session_id}_{self.chat_room.id}"
event = {
'type': 'start_conversation_handler',
'chat_room_id': chat_room.id
}
async_to_sync(self.channel_layer.group_discard)(
self.group, self.channel_name
)
@ -251,6 +255,11 @@ class OsitcomChatRoom(WebsocketConsumer):
self.group, event
)
event = {
'type': 'start_conversation_handler',
'chat_room_id': chat_room.id
}
if event_type == 'typing':
event = {
'type': 'typing_handler',

@ -1,5 +1,19 @@
{% load static %}
<div class="w-full h-full flex flex-col justify-end">
<!-- RECONNECTING LOADER -->
<div id="roomLoader" class="w-full h-full absolute flex justify-center items-center bg-black bg-opacity-60 z-20 inset-0 rounded-r-md hidden">
<div class="flex flex-col justify-center items-center gap-2">
<p class="text-white">Reconneting</p>
<div class="w-fit flex gap-1 justify-start items-center">
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md"></div>
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md" style="animation-delay: 0.2s;"></div>
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md" style="animation-delay: 0.4s;">
</div>
</div>
</div>
</div>
<div class="w-full h-full flex flex-col justify-between">
<!-- HEADER -->
<div class="w-full flex items-center gap-1 py-3 px-3 border-b border-gray-100 text-[17px] text-secondosiblue">
<div class="w-[30px] h-[30px] rounded-full shadow-md text-white flex justify-center items-center bg-osiblue uppercase text-xs">
@ -9,8 +23,9 @@
<p>{{chat_room.chatroomguest.visitor.name}}</p>
</div>
<div class="overflow-y-auto flex flex-col justify-end">
<!-- MESSAGES -->
<div class="flex-1 overflow-y-auto p-3 flex flex-col gap-4" id="messages_container">
<div class="overflow-y-auto p-3 flex flex-col gap-4" id="messages_container">
{% for message in chat_room_messages %}
{% if message.member %}
{% if not message.chatmessageattachment %}
@ -87,8 +102,6 @@
{% endif %}
{% endfor %}
<audio id="notification-sound" src="{{domain}}/static/notifications/osichat-notification.mp3" preload="auto"></audio>
</div>
@ -118,4 +131,5 @@
</div>
</div>
</form>
</div>
</div>

@ -4,11 +4,11 @@
<div
class="w-[950px] min-h-[500px] h-[80%] bg-white rounded-md border border-gray-200 shadow-xl z-20 right-9 bottom-24 grid grid-cols-3 fixed">
<!-- LEFT SIDE - CHATS -->
<div id="widgetLeftSide" class="h-full overflow-y-auto border-r border-gray-200 bg-gray-50">
<div id="widgetLeftSide" class="h-full overflow-y-auto border-r border-gray-200 bg-gray-50 relative rounded-l-md">
</div>
<!-- RIGHT SIDE -->
<div class="col-span-2 overflow-hidden" id="widgetRightSide">
<div class="col-span-2 overflow-hidden relative" id="widgetRightSide">
</div>
</div>

@ -2,8 +2,8 @@
<div class="w-full flex justify-between items-center">
<p class="text-secondosiblue font-poppinsBold">{{chat_room.chatroomguest.visitor.ip_address}}</p>
<div
class="w-[20px] h-[20px] bg-osiblue rounded-full shadow-md flex justify-center items-center text-white text-[10px]">
<div data-roomid={{chat_room.id}}
class="unread w-[20px] h-[20px] bg-osiblue rounded-full shadow-md flex justify-center items-center text-white text-[10px] {% if chat_room.number_of_unread == 0 %} hidden {% endif %}">
<p>{{number_of_unread}}</p>
</div>
</div>

@ -1,9 +1,22 @@
<!-- HEADER -->
<div class="grid grid-cols-2 sticky top-0 z-10">
<!-- RECONNECTING LOADER -->
<div id="roomsLoader" class="w-full h-full absolute flex justify-center items-center bg-black bg-opacity-60 z-20 inset-0 rounded-l-md hidden">
<div class="flex flex-col justify-center items-center gap-2">
<p class="text-white">Reconneting</p>
<div class="w-fit flex gap-1 justify-start items-center">
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md"></div>
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md" style="animation-delay: 0.2s;"></div>
<div class="h-2 w-2 bg-white rounded-full animate-typing shadow-md" style="animation-delay: 0.4s;">
</div>
</div>
</div>
</div>
<!-- HEADER -->
<div class="grid grid-cols-2 sticky top-0 z-10 rounded-l-md">
<div
class="w-full flex items-center justify-center gap-1 py-3 px-3 border-b border-gray-100 bg-white rounded-md shadow-md cursor-pointer text-secondosiblue">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-5">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"
class="w-5">
<path stroke-linecap="round" stroke-linejoin="round"
d="M7.5 8.25h9m-9 3H12m-9.75 1.51c0 1.6 1.123 2.994 2.707 3.227 1.129.166 2.27.293 3.423.379.35.026.67.21.865.501L12 21l2.755-4.133a1.14 1.14 0 0 1 .865-.501 48.172 48.172 0 0 0 3.423-.379c1.584-.233 2.707-1.626 2.707-3.228V6.741c0-1.602-1.123-2.995-2.707-3.228A48.394 48.394 0 0 0 12 3c-2.392 0-4.744.175-7.043.513C3.373 3.746 2.25 5.14 2.25 6.741v6.018Z" />
</svg>
@ -26,17 +39,18 @@
</svg>
<p>Visitors</p>
</div>
</div>
</div>
<div class="h-full flex flex-col text-secondosiblue" id="chatrooms">
<div class="h-full flex flex-col text-secondosiblue" id="chatrooms">
<!-- CHATS -->
{% for chat_room in chat_rooms %}
<div data-roomid = {{chat_room.id}} data-session="{{chat_room.chatroomguest.visitor.session_id}}" class="chat-room w-full flex flex-col gap-1 py-3 px-3 border-b border-gray-100 text-sm cursor-pointer">
<div data-roomid={{chat_room.id}} data-session="{{chat_room.chatroomguest.visitor.session_id}}"
class="chat-room w-full flex flex-col gap-1 py-3 px-3 border-b border-gray-100 text-sm cursor-pointer">
<div class="w-full flex justify-between items-center">
<p class="text-secondosiblue font-poppinsBold">{{chat_room.chatroomguest.visitor.ip_address}}</p>
<div
class="w-[20px] h-[20px] bg-osiblue rounded-full shadow-md flex justify-center items-center text-white text-[10px]">
<div data-roomid={{chat_room.id}}
class="unread w-[20px] h-[20px] bg-osiblue rounded-full shadow-md flex justify-center items-center text-white text-[10px] {% if chat_room.number_of_unread == 0 %} hidden {% endif %}">
<p>{{chat_room.number_of_unread}}</p>
</div>
</div>
@ -46,4 +60,6 @@
<p class="text-gray-500 opacity-70 text-xs">{{chat_room.last_updated}}</p>
</div>
{% endfor %}
</div>
</div>
<audio id="notification-sound" src="{{domain}}/static/notifications/osichat-notification.mp3" preload="auto"></audio>

@ -714,6 +714,10 @@ video {
bottom: 4rem;
}
.bottom-2 {
bottom: 0.5rem;
}
.bottom-20 {
bottom: 5rem;
}
@ -722,10 +726,18 @@ video {
bottom: 6rem;
}
.bottom-28 {
bottom: 7rem;
}
.bottom-3 {
bottom: 0.75rem;
}
.bottom-32 {
bottom: 8rem;
}
.bottom-5 {
bottom: 1.25rem;
}
@ -842,18 +854,6 @@ video {
top: 14px;
}
.bottom-2 {
bottom: 0.5rem;
}
.bottom-28 {
bottom: 7rem;
}
.bottom-32 {
bottom: 8rem;
}
.z-10 {
z-index: 10;
}
@ -1103,6 +1103,10 @@ video {
height: 210px;
}
.h-\[22px\] {
height: 22px;
}
.h-\[250px\] {
height: 250px;
}
@ -1200,10 +1204,6 @@ video {
height: 100vh;
}
.h-\[22px\] {
height: 22px;
}
.max-h-\[200px\] {
max-height: 200px;
}
@ -1332,6 +1332,10 @@ video {
width: 220px;
}
.w-\[22px\] {
width: 22px;
}
.w-\[240px\] {
width: 240px;
}
@ -1449,10 +1453,6 @@ video {
width: 100%;
}
.w-\[22px\] {
width: 22px;
}
.min-w-full {
min-width: 100%;
}
@ -1843,6 +1843,11 @@ video {
border-top-right-radius: 0px;
}
.rounded-l {
border-top-left-radius: 0.25rem;
border-bottom-left-radius: 0.25rem;
}
.rounded-bl-md {
border-bottom-left-radius: 0.375rem;
}
@ -2196,6 +2201,10 @@ video {
--tw-bg-opacity: 0.8;
}
.bg-opacity-60 {
--tw-bg-opacity: 0.6;
}
.bg-gradient-to-b {
background-image: linear-gradient(to bottom, var(--tw-gradient-stops));
}
@ -3190,6 +3199,10 @@ video {
}
} */
.hover\:pointer-events-none:hover {
pointer-events: none;
}
.hover\:scale-105:hover {
--tw-scale-x: 1.05;
--tw-scale-y: 1.05;
@ -3202,6 +3215,10 @@ video {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:overflow-hidden:hover {
overflow: hidden;
}
.hover\:bg-gray-100:hover {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity));

@ -2,42 +2,85 @@
function handleChatRoomClick(event) {
const sessionId = event.currentTarget.getAttribute('data-session');
const chatId = event.currentTarget.getAttribute('data-roomid');
if (sessionId && chatId) {
if (sessionId && chatId && chatId !== currentChatId) {
showLoader();
openConversation(sessionId, chatId);
} else {
console.error('Session ID not found for this chat room.');
currentChatId = chatId;
}
}
document.querySelectorAll('.chat-room').forEach(div => {
div.addEventListener('click', handleChatRoomClick);
});
function appendTextAreaScript(domain, conversationContainer) {
if (!document.querySelector(`script[src="${protocol}://${admin_chat_domain}/static/js/osichat-admin/textarea.js"]`)) {
function markCurrentChatRead(chatid) {
const unreadElement = document.querySelector(`.unread[data-roomid='${chatid}']`);
if (unreadElement) {
unreadElement.classList.add('hidden');
}
}
// FUNCTIONS TO SHOW & HIDE THE LOADER
function showLoader() {
const roomLoader = document.getElementById('roomLoader');
if (roomLoader) {
roomLoader.classList.remove('hidden');
}
const widgetLeftSide = document.getElementById('widgetLeftSide');
widgetLeftSide.classList.remove('overflow-y-auto');
widgetLeftSide.classList.add('overflow-hidden');
}
function hideLoader() {
const roomLoader = document.getElementById('roomLoader');
if (roomLoader) {
roomLoader.classList.add('hidden');
}
const widgetLeftSide = document.getElementById('widgetLeftSide');
widgetLeftSide.classList.remove('overflow-hidden');
widgetLeftSide.classList.add('overflow-y-auto');
}
function appendTextAreaScript(conversationContainer) {
const textareaScript = document.createElement('script');
textareaScript.type = 'text/javascript';
textareaScript.src = `${protocol}://${admin_chat_domain}/static/js/osichat-admin/textarea.js`;
conversationContainer.appendChild(textareaScript);
}
}
function scrollBottom() {
const conversationContainer = document.getElementById('messages_container');
if (conversationContainer) {
conversationContainer.scrollTo({
top: conversationContainer.scrollHeight,
behavior: 'smooth'
});
}
}
function openConversation(sessionid, chatid) {
if (osichatadminroomSocket && osichatadminroomSocket.readyState !== WebSocket.CLOSED) { //Close previous sockets
function openConversation(sessionId, chatId) {
if (osichatadminroomSocket) {
osichatadminroomSocket.close();
}
osichatadminroomSocket = new WebSocket(`${admin_chat_ws_scheme}://${admin_chat_domain}/ws/osichat-admin/${sessionid}/${chatid}/`);
osichatadminroomSocket = new WebSocket(`${admin_chat_ws_scheme}://${admin_chat_domain}/ws/osichat-admin/${sessionId}/${chatId}/`);
osichatadminroomSocket.onopen = function () {
scrollBottom();
hideLoader();
markCurrentChatRead(chatId)
console.log('WebSocket connection to osichat established');
osichatadminroomSocket.send(JSON.stringify({ 'event_type': 'load_chat', 'client_type': 'website_admin' }));
osichatadminroomSocket.send(JSON.stringify({ 'event_type': 'update_read_messages', 'user_id': userId, 'chat_state': 'open' }));
};
function handleLoadChatEvent(data, osichatadminroomSocket) {
function handleLoadChatEvent(data) {
let chatDiv = document.getElementById('widgetRightSide');
chatDiv.innerHTML = data.html;
appendTextAreaScript(admin_chat_domain, chatDiv);
appendTextAreaScript(chatDiv);
const sendMessageForm = document.querySelector('#sendMessage');
sendMessageForm.addEventListener('submit', function (event) {
@ -60,10 +103,10 @@
const messagesDiv = document.getElementById('messages_container');
switch (data.event_type) {
case 'load_chat':
handleLoadChatEvent(data, osichatadminroomSocket);
handleLoadChatEvent(data);
break;
case 'typing':
if(!typingDiv && data.user != userId){
if (!typingDiv && data.user != userId) {
messagesDiv.insertAdjacentHTML('beforeend', data.html);
}
break;
@ -75,13 +118,10 @@
case 'send_message':
osichatadminroomSocket.send(JSON.stringify({ 'event_type': 'update_read_messages', 'user_id': userId, 'chat_state': 'open' }));
messagesDiv.insertAdjacentHTML('beforeend', data.html);
if (!data.user) { // If it is sent by a guest play a notification sound for the guest
const notificationSound = document.getElementById('notification-sound');
notificationSound.play();
if (!data.user){
if (typingDiv) {
typingDiv.remove();
}
break;
}
break;
default:
@ -90,11 +130,26 @@
};
osichatadminroomSocket.onclose = function () {
console.log('WebSocket connection to osichat closed');
console.log('WebSocket connection closed');
if (currentChatId === chatId) {
setTimeout(() => {
console.log('Attempting to reconnect to WebSocket...');
openConversation(sessionId, chatId);
}, 2000);
}
};
osichatadminroomSocket.onerror = function (error) {
console.error('WebSocket error:', error);
showLoader();
};
}
window.addEventListener('offline', () => {
showLoader();
if (osichatadminroomSocket) {
osichatadminroomSocket.close();
}
});
})();

@ -2,8 +2,24 @@ let admin_chat_ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";
let protocol = window.location.protocol === "https:" ? "https" : "http";
let admin_chat_domain = "osina.ositcom.com";
let userId = document.getElementById('userId').textContent.trim();
let osichatroomsSocket;
let osichatadminroomSocket;
let osichatadminroomSocket = null;
let currentChatId = null;
// FUNCTIONS TO SHOW & HIDE THE LOADER
function showLoader() {
const roomsLoader = document.getElementById('roomsLoader');
if (roomsLoader) {
roomsLoader.classList.remove('hidden');
}
}
function hideLoader() {
const roomsLoader = document.getElementById('roomsLoader');
if (roomsLoader) {
roomsLoader.classList.add('hidden');
}
}
function appendInnerConversationScript(div) {
const innerConversationScript = document.createElement('script');
@ -12,11 +28,16 @@ function appendInnerConversationScript(div) {
div.appendChild(innerConversationScript);
}
function getRooms(){
osichatroomsSocket = new WebSocket(`${admin_chat_ws_scheme}://${admin_chat_domain}/ws/osichat/rooms/`);
osichatroomsSocket.onopen = function () {
console.log('WebSocket connection to rooms established');
hideLoader();
osichatroomsSocket.send(JSON.stringify({
'event_type': 'set_client_type',
'client_type': 'website_admin',
@ -39,11 +60,34 @@ function getRooms(){
if (chatRoomDiv) {
chatRoomDiv.remove(); // Remove the existing chat room div
}
// Insert the new chat room HTML at the top of the container
const newChatRoomDiv = document.createElement('div');
newChatRoomDiv.innerHTML = data.html;
roomsContainer.insertAdjacentElement('afterbegin', newChatRoomDiv.firstElementChild);
if (parseInt(currentChatId) === parseInt(data.chatroom_id)) {
const unreadIndicator = roomsContainer.querySelector(`.unread[data-roomid='${data.chatroom_id}']`);
if (unreadIndicator) {
unreadIndicator.classList.add('hidden');
}
if (!data.user) { // If it is sent by a guest play a notification sound for the guest
const notificationSound = document.getElementById('notification-sound');
notificationSound.play();
}
} else {
const unreadIndicator = roomsContainer.querySelector(`.unread[data-roomid='${data.chatroom_id}']`);
if (unreadIndicator) {
unreadIndicator.classList.remove('hidden');
}
if (!data.user) { // If it is sent by a guest play a notification sound for the guest
const notificationSound = document.getElementById('notification-sound');
notificationSound.play();
}
}
appendInnerConversationScript(leftDiv);
break;
@ -53,12 +97,25 @@ function getRooms(){
};
osichatroomsSocket.onclose = function () {
console.log('WebSocket connection to rooms closed');
showLoader();
setTimeout(() => {
console.log('Attempting to reconnect to WebSocket...');
getRooms();
}, 2000);
};
osichatroomsSocket.onerror = function (error) {
showLoader();
console.error('WebSocket error:', error);
};
window.addEventListener('offline', () => {
showLoader();
if (osichatroomsSocket) {
osichatroomsSocket.close();
}
});
}
getRooms();

@ -1,12 +1,14 @@
(function() {
// FUNCTION TO ADJUST TEXTAREA HEIGHT AND SUBMIT BUTTON VISIBILITY
function adjustTextAreaAndButton(textarea, submitButton) {
// Adjust the height of the textarea
if (textarea.value.trim() === '') {
textarea.style.height = '50px';
} else {
textarea.style.height = textarea.scrollHeight + 'px';
}
// Display and hide the submit button
if (textarea.value.trim() === '') {
submitButton.classList.add('hidden');
} else {
@ -14,10 +16,21 @@
}
}
function scrollBottom() {
const conversationContainer = document.getElementById('messages_container');
if (conversationContainer) {
conversationContainer.scrollTo({
top: conversationContainer.scrollHeight,
behavior: 'smooth'
});
}
}
// INITIALIZE ELEMENTS
const form = document.querySelector('#sendMessage');
const textarea = document.querySelector('#dynamicTextarea');
const conversationContainer = document.getElementById('conversation');
const submitButton = document.getElementById('submitMessageButton');
const typingUserId = document.getElementById('userId').textContent.trim();
let typingTimeout;
@ -54,8 +67,18 @@
submitButton.classList.add('hidden');
setTimeout(() => {
conversationContainer.scrollTop = conversationContainer.scrollHeight;
scrollBottom();
}, 100);
});
textarea.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
if (!submitButton.classList.contains('hidden')) {
submitButton.click();
}
scrollBottom();
}
});
})();

@ -18,15 +18,19 @@
function scrollBottom() {
const conversationContainer = document.getElementById('conversation');
conversationContainer.scrollTop = conversationContainer.scrollHeight;
}
if (conversationContainer) {
conversationContainer.scrollTo({
top: conversationContainer.scrollHeight,
behavior: 'smooth'
});
}
}
// INITIALIZE ELEMENTS
const form = document.querySelector('#sendMessage');
const textarea = document.querySelector('#dynamicTextarea');
const conversationContainer = document.getElementById('conversation');
const submitButton = document.getElementById('submitMessageButton');
let typingTimeout;
let isTyping = false;

Loading…
Cancel
Save