This commit is contained in:
Utkarsh Khedkar
2025-12-19 17:27:54 +05:30
parent 80c6e42e0c
commit 3941b06355

View File

@@ -4,6 +4,7 @@
@section('content') @section('content')
<style> <style>
/* ... तुझा existing complete CSS जसाच्या तसा ठेव ... */
:root { :root {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%); --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--primary-soft: rgba(102, 126, 234, 0.12); --primary-soft: rgba(102, 126, 234, 0.12);
@@ -643,6 +644,32 @@ body {
max-width: 100px; max-width: 100px;
} }
} }
.header-notify {
position: relative;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: #cbd5f5;
}
.header-notify-badge {
min-width: 18px;
height: 18px;
border-radius: 999px;
background: #ef4444;
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 700;
box-shadow: 0 0 0 2px rgba(15,23,42,0.8);
}
.header-notify.d-none {
display: none;
}
</style> </style>
<div class="chat-container"> <div class="chat-container">
@@ -662,6 +689,12 @@ body {
</div> </div>
</div> </div>
<div class="header-actions"> <div class="header-actions">
{{-- perticket new message notification --}}
<div id="ticketNotifyBox" class="header-notify d-none">
New messages:
<span id="ticketNotifyCount" class="header-notify-badge">0</span>
</div>
<span class="status-badge {{ $ticket->status === 'open' ? 'open' : 'closed' }}"> <span class="status-badge {{ $ticket->status === 'open' ? 'open' : 'closed' }}">
{{ ucfirst($ticket->status) }} {{ ucfirst($ticket->status) }}
</span> </span>
@@ -693,7 +726,6 @@ body {
$isDocument = in_array(Str::lower(Str::afterLast($msg->file_path, '.')), ['doc', 'docx', 'txt']); $isDocument = in_array(Str::lower(Str::afterLast($msg->file_path, '.')), ['doc', 'docx', 'txt']);
$fileName = basename($msg->file_path); $fileName = basename($msg->file_path);
// Get file size from storage if possible
$fileSize = 'N/A'; $fileSize = 'N/A';
try { try {
$fullPath = storage_path('app/public/' . $msg->file_path); $fullPath = storage_path('app/public/' . $msg->file_path);
@@ -711,7 +743,6 @@ body {
$fileSize = 'Unknown'; $fileSize = 'Unknown';
} }
// Determine file class
if ($isImage) { if ($isImage) {
$fileClass = 'image-file'; $fileClass = 'image-file';
$fileIcon = '🖼️'; $fileIcon = '🖼️';
@@ -789,7 +820,8 @@ body {
<div class="file-name-display" id="fileNameDisplay"></div> <div class="file-name-display" id="fileNameDisplay"></div>
<button class="clear-file-btn" id="clearFileBtn" title="Remove file"> <button class="clear-file-btn" id="clearFileBtn" title="Remove file">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M6 18L18 6M6 6l12 12"/>
</svg> </svg>
</button> </button>
@@ -823,8 +855,6 @@ body {
@section('scripts') @section('scripts')
<script> <script>
const CURRENT_ADMIN_ID = {{ auth('admin')->id() }}; const CURRENT_ADMIN_ID = {{ auth('admin')->id() }};
// ✅ Make current admin ID available to JS
console.log("CHAT WINDOW: script loaded"); console.log("CHAT WINDOW: script loaded");
// ------------------------------- // -------------------------------
@@ -859,13 +889,12 @@ const fileInput = document.getElementById('fileInput');
const fileNameDisplay = document.getElementById('fileNameDisplay'); const fileNameDisplay = document.getElementById('fileNameDisplay');
const clearFileBtn = document.getElementById('clearFileBtn'); const clearFileBtn = document.getElementById('clearFileBtn');
fileInput.addEventListener('change', function(e) { fileInput.addEventListener('change', function() {
if (this.files.length > 0) { if (this.files.length > 0) {
const file = this.files[0]; const file = this.files[0];
const fileName = file.name; const name = file.name;
const fileSize = formatFileSize(file.size); const size = formatFileSize(file.size);
fileNameDisplay.textContent = `${name} (${size})`;
fileNameDisplay.textContent = `${fileName} (${fileSize})`;
fileNameDisplay.style.display = 'block'; fileNameDisplay.style.display = 'block';
clearFileBtn.style.display = 'block'; clearFileBtn.style.display = 'block';
} }
@@ -886,40 +915,26 @@ function formatFileSize(bytes) {
} }
function getFileIcon(fileType, fileName) { function getFileIcon(fileType, fileName) {
if (fileType.startsWith('image/')) { if (fileType.startsWith('image/')) return '🖼️';
return '🖼️'; if (fileType.startsWith('video/')) return '🎬';
} else if (fileType.startsWith('video/')) { if (fileName.endsWith('.pdf')) return '📄';
return '🎬'; if (fileName.match(/\.(doc|docx|txt)$/i)) return '📝';
} else if (fileName.endsWith('.pdf')) { return '📎';
return '📄';
} else if (fileName.match(/\.(doc|docx|txt)$/i)) {
return '📝';
} else {
return '📎';
}
} }
function getFileClass(fileType, fileName) { function getFileClass(fileType, fileName) {
if (fileType.startsWith('image/')) { if (fileType.startsWith('image/')) return 'image-file';
return 'image-file'; if (fileType.startsWith('video/')) return 'video-file';
} else if (fileType.startsWith('video/')) { if (fileName.endsWith('.pdf')) return 'pdf-file';
return 'video-file'; if (fileName.match(/\.(doc|docx|txt)$/i)) return 'document-file';
} else if (fileName.endsWith('.pdf')) { return 'other-file';
return 'pdf-file';
} else if (fileName.match(/\.(doc|docx|txt)$/i)) {
return 'document-file';
} else {
return 'other-file';
}
} }
// ------------------------------- // -------------------------------
// SEND MESSAGE // SEND MESSAGE
// ------------------------------- // -------------------------------
document.getElementById("sendBtn").addEventListener("click", function () { document.getElementById("sendBtn").addEventListener("click", function () {
console.log("[SEND] Attempting to send message..."); let msg = document.getElementById("messageInput").value;
let msg = document.getElementById("messageInput").value;
let file = document.getElementById("fileInput").files[0]; let file = document.getElementById("fileInput").files[0];
if (!msg.trim() && !file) { if (!msg.trim() && !file) {
@@ -937,8 +952,7 @@ document.getElementById("sendBtn").addEventListener("click", function () {
body: formData body: formData
}) })
.then(res => res.json()) .then(res => res.json())
.then((response) => { .then(() => {
console.log("[SEND] Message sent:", response);
document.getElementById("messageInput").value = ""; document.getElementById("messageInput").value = "";
document.getElementById("fileInput").value = ""; document.getElementById("fileInput").value = "";
fileNameDisplay.style.display = 'none'; fileNameDisplay.style.display = 'none';
@@ -957,15 +971,18 @@ document.getElementById("messageInput").addEventListener("keypress", function(e)
// ------------------------------- // -------------------------------
// LISTEN FOR REALTIME MESSAGE // LISTEN FOR REALTIME MESSAGE
// ---------------------------- // -------------------------------
waitForEcho(() => { waitForEcho(() => {
const ticketId = "{{ $ticket->id }}"; const ticketId = "{{ $ticket->id }}";
const notifyBox = document.getElementById('ticketNotifyBox');
const notifyCount = document.getElementById('ticketNotifyCount');
let ticketNewCount = 0;
console.log("[ECHO] Subscribing to PRIVATE channel:", `ticket.${ticketId}`); console.log("[ECHO] Subscribing to PRIVATE channel:", `ticket.${ticketId}`);
window.Echo.private(`ticket.${ticketId}`) window.Echo.private(`ticket.${ticketId}`)
.listen(".NewChatMessage", (event) => { .listen(".NewChatMessage", (event) => {
console.log("%c[REALTIME RECEIVED]", "color: blue; font-weight: bold;", event); console.log("%c[REALTIME RECEIVED]", "color: blue; font-weight: bold;", event);
const msg = event; const msg = event;
@@ -973,21 +990,28 @@ waitForEcho(() => {
msg.sender_type === 'App\\Models\\Admin' && msg.sender_type === 'App\\Models\\Admin' &&
msg.sender_id === CURRENT_ADMIN_ID; msg.sender_id === CURRENT_ADMIN_ID;
// जर message admin ने पाठवला असेल तर त्या साठी notification वाढवायची गरज नाही
if (!isMine) {
ticketNewCount++;
if (notifyBox && notifyCount) {
notifyBox.classList.remove('d-none');
notifyCount.textContent = ticketNewCount;
}
}
let html = ` let html = `
<div class="message ${isMine ? 'admin' : 'user'}"> <div class="message ${isMine ? 'admin' : 'user'}">
${msg.message ? `<div>${msg.message}</div>` : ''} ${msg.message ? `<div>${msg.message}</div>` : ''}
`; `;
if (msg.file_path) { if (msg.file_path) {
const fileUrl = `/storage/${msg.file_path}`; const fileUrl = `/storage/${msg.file_path}`;
const fileName = msg.file_path.split('/').pop(); const fileName = msg.file_path.split('/').pop();
const fileSize = msg.file_size ? formatFileSize(msg.file_size) : 'Unknown'; const fileSize = msg.file_size ? formatFileSize(msg.file_size) : 'Unknown';
const fileIcon = getFileIcon(msg.file_type || '', fileName); const fileIcon = getFileIcon(msg.file_type || '', fileName);
const fileClass = getFileClass(msg.file_type || '', fileName); const fileClass= getFileClass(msg.file_type || '', fileName);
const isImage = (msg.file_type || '').startsWith('image');
// Check if it's image or video for preview const isVideo = (msg.file_type || '').startsWith('video');
const isImage = (msg.file_type || '').startsWith('image');
const isVideo = (msg.file_type || '').startsWith('video');
html += `<div class="file-preview">`; html += `<div class="file-preview">`;
@@ -1013,9 +1037,7 @@ waitForEcho(() => {
target="_blank" target="_blank"
class="file-item ${fileClass}" class="file-item ${fileClass}"
title="${fileName}"> title="${fileName}">
<div class="file-icon"> <div class="file-icon">${fileIcon}</div>
${fileIcon}
</div>
<div class="file-info"> <div class="file-info">
<div class="file-name">${fileName}</div> <div class="file-name">${fileName}</div>
<div class="file-size">${fileSize}</div> <div class="file-size">${fileSize}</div>
@@ -1023,17 +1045,14 @@ waitForEcho(() => {
<div class="file-download-btn"> <div class="file-download-btn">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/>
</svg> </svg>
</div> </div>
</a> </a>
</div>`; </div>`;
} }
html += ` html += `<small class="mt-2">Just now</small></div>`;
<small class="mt-2">Just now</small>
</div>
`;
document document
.querySelector("#chatBox .chat-box-inner") .querySelector("#chatBox .chat-box-inner")