changes
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
@section('content')
|
||||
<style>
|
||||
/* ... तुझा existing complete CSS जसाच्या तसा ठेव ... */
|
||||
:root {
|
||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
--primary-soft: rgba(102, 126, 234, 0.12);
|
||||
@@ -643,6 +644,32 @@ body {
|
||||
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>
|
||||
|
||||
<div class="chat-container">
|
||||
@@ -662,6 +689,12 @@ body {
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
{{-- per‑ticket 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' }}">
|
||||
{{ ucfirst($ticket->status) }}
|
||||
</span>
|
||||
@@ -693,7 +726,6 @@ body {
|
||||
$isDocument = in_array(Str::lower(Str::afterLast($msg->file_path, '.')), ['doc', 'docx', 'txt']);
|
||||
$fileName = basename($msg->file_path);
|
||||
|
||||
// Get file size from storage if possible
|
||||
$fileSize = 'N/A';
|
||||
try {
|
||||
$fullPath = storage_path('app/public/' . $msg->file_path);
|
||||
@@ -711,7 +743,6 @@ body {
|
||||
$fileSize = 'Unknown';
|
||||
}
|
||||
|
||||
// Determine file class
|
||||
if ($isImage) {
|
||||
$fileClass = 'image-file';
|
||||
$fileIcon = '🖼️';
|
||||
@@ -789,7 +820,8 @@ body {
|
||||
<div class="file-name-display" id="fileNameDisplay"></div>
|
||||
<button class="clear-file-btn" id="clearFileBtn" title="Remove file">
|
||||
<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>
|
||||
</button>
|
||||
|
||||
@@ -823,8 +855,6 @@ body {
|
||||
@section('scripts')
|
||||
<script>
|
||||
const CURRENT_ADMIN_ID = {{ auth('admin')->id() }};
|
||||
|
||||
// ✅ Make current admin ID available to JS
|
||||
console.log("CHAT WINDOW: script loaded");
|
||||
|
||||
// -------------------------------
|
||||
@@ -859,13 +889,12 @@ const fileInput = document.getElementById('fileInput');
|
||||
const fileNameDisplay = document.getElementById('fileNameDisplay');
|
||||
const clearFileBtn = document.getElementById('clearFileBtn');
|
||||
|
||||
fileInput.addEventListener('change', function(e) {
|
||||
fileInput.addEventListener('change', function() {
|
||||
if (this.files.length > 0) {
|
||||
const file = this.files[0];
|
||||
const fileName = file.name;
|
||||
const fileSize = formatFileSize(file.size);
|
||||
|
||||
fileNameDisplay.textContent = `${fileName} (${fileSize})`;
|
||||
const name = file.name;
|
||||
const size = formatFileSize(file.size);
|
||||
fileNameDisplay.textContent = `${name} (${size})`;
|
||||
fileNameDisplay.style.display = 'block';
|
||||
clearFileBtn.style.display = 'block';
|
||||
}
|
||||
@@ -886,40 +915,26 @@ function formatFileSize(bytes) {
|
||||
}
|
||||
|
||||
function getFileIcon(fileType, fileName) {
|
||||
if (fileType.startsWith('image/')) {
|
||||
return '🖼️';
|
||||
} else if (fileType.startsWith('video/')) {
|
||||
return '🎬';
|
||||
} else if (fileName.endsWith('.pdf')) {
|
||||
return '📄';
|
||||
} else if (fileName.match(/\.(doc|docx|txt)$/i)) {
|
||||
return '📝';
|
||||
} else {
|
||||
return '📎';
|
||||
}
|
||||
if (fileType.startsWith('image/')) return '🖼️';
|
||||
if (fileType.startsWith('video/')) return '🎬';
|
||||
if (fileName.endsWith('.pdf')) return '📄';
|
||||
if (fileName.match(/\.(doc|docx|txt)$/i)) return '📝';
|
||||
return '📎';
|
||||
}
|
||||
|
||||
function getFileClass(fileType, fileName) {
|
||||
if (fileType.startsWith('image/')) {
|
||||
return 'image-file';
|
||||
} else if (fileType.startsWith('video/')) {
|
||||
return 'video-file';
|
||||
} else if (fileName.endsWith('.pdf')) {
|
||||
return 'pdf-file';
|
||||
} else if (fileName.match(/\.(doc|docx|txt)$/i)) {
|
||||
return 'document-file';
|
||||
} else {
|
||||
return 'other-file';
|
||||
}
|
||||
if (fileType.startsWith('image/')) return 'image-file';
|
||||
if (fileType.startsWith('video/')) return 'video-file';
|
||||
if (fileName.endsWith('.pdf')) return 'pdf-file';
|
||||
if (fileName.match(/\.(doc|docx|txt)$/i)) return 'document-file';
|
||||
return 'other-file';
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// SEND MESSAGE
|
||||
// -------------------------------
|
||||
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];
|
||||
|
||||
if (!msg.trim() && !file) {
|
||||
@@ -937,8 +952,7 @@ document.getElementById("sendBtn").addEventListener("click", function () {
|
||||
body: formData
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then((response) => {
|
||||
console.log("[SEND] Message sent:", response);
|
||||
.then(() => {
|
||||
document.getElementById("messageInput").value = "";
|
||||
document.getElementById("fileInput").value = "";
|
||||
fileNameDisplay.style.display = 'none';
|
||||
@@ -957,15 +971,18 @@ document.getElementById("messageInput").addEventListener("keypress", function(e)
|
||||
|
||||
// -------------------------------
|
||||
// LISTEN FOR REALTIME MESSAGE
|
||||
// ----------------------------
|
||||
// -------------------------------
|
||||
waitForEcho(() => {
|
||||
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}`);
|
||||
|
||||
window.Echo.private(`ticket.${ticketId}`)
|
||||
.listen(".NewChatMessage", (event) => {
|
||||
|
||||
console.log("%c[REALTIME RECEIVED]", "color: blue; font-weight: bold;", event);
|
||||
|
||||
const msg = event;
|
||||
@@ -973,21 +990,28 @@ waitForEcho(() => {
|
||||
msg.sender_type === 'App\\Models\\Admin' &&
|
||||
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 = `
|
||||
<div class="message ${isMine ? 'admin' : 'user'}">
|
||||
${msg.message ? `<div>${msg.message}</div>` : ''}
|
||||
`;
|
||||
|
||||
if (msg.file_path) {
|
||||
const fileUrl = `/storage/${msg.file_path}`;
|
||||
const fileUrl = `/storage/${msg.file_path}`;
|
||||
const fileName = msg.file_path.split('/').pop();
|
||||
const fileSize = msg.file_size ? formatFileSize(msg.file_size) : 'Unknown';
|
||||
const fileIcon = getFileIcon(msg.file_type || '', fileName);
|
||||
const fileClass = getFileClass(msg.file_type || '', fileName);
|
||||
|
||||
// Check if it's image or video for preview
|
||||
const isImage = (msg.file_type || '').startsWith('image');
|
||||
const isVideo = (msg.file_type || '').startsWith('video');
|
||||
const fileClass= getFileClass(msg.file_type || '', fileName);
|
||||
const isImage = (msg.file_type || '').startsWith('image');
|
||||
const isVideo = (msg.file_type || '').startsWith('video');
|
||||
|
||||
html += `<div class="file-preview">`;
|
||||
|
||||
@@ -1013,9 +1037,7 @@ waitForEcho(() => {
|
||||
target="_blank"
|
||||
class="file-item ${fileClass}"
|
||||
title="${fileName}">
|
||||
<div class="file-icon">
|
||||
${fileIcon}
|
||||
</div>
|
||||
<div class="file-icon">${fileIcon}</div>
|
||||
<div class="file-info">
|
||||
<div class="file-name">${fileName}</div>
|
||||
<div class="file-size">${fileSize}</div>
|
||||
@@ -1023,17 +1045,14 @@ waitForEcho(() => {
|
||||
<div class="file-download-btn">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<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>
|
||||
</div>
|
||||
</a>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += `
|
||||
<small class="mt-2">Just now</small>
|
||||
</div>
|
||||
`;
|
||||
html += `<small class="mt-2">Just now</small></div>`;
|
||||
|
||||
document
|
||||
.querySelector("#chatBox .chat-box-inner")
|
||||
|
||||
Reference in New Issue
Block a user