Files
Kent-logistics-Laravel/resources/views/admin/chat_window.blade.php
Utkarsh Khedkar 3941b06355 changes
2025-12-19 17:27:54 +05:30

1066 lines
30 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends('admin.layouts.app')
@section('page-title', 'Chat With ' . ($ticket->user->customer_name ?? $ticket->user->name))
@section('content')
<style>
/* ... तुझा existing complete CSS जसाच्या तसा ठेव ... */
:root {
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--primary-soft: rgba(102, 126, 234, 0.12);
--message-admin: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--message-user: #ffffff;
--bg-pattern: #0f172a;
--border-radius-lg: 18px;
--border-radius-sm: 12px;
--shadow-soft: 0 18px 45px rgba(15, 23, 42, 0.4);
--shadow-chip: 0 4px 12px rgba(15, 23, 42, 0.35);
}
body {
background: radial-gradient(circle at top, #1f2937 0, #020617 55%);
}
.chat-container {
max-width: 1180px;
margin: 0 auto;
padding: 20px;
}
/* glass card wrapper */
.chat-shell {
border-radius: 26px;
overflow: hidden;
background: radial-gradient(circle at top left, rgba(148, 163, 184, 0.15), rgba(15, 23, 42, 0.95));
box-shadow: var(--shadow-soft);
border: 1px solid rgba(148, 163, 184, 0.35);
backdrop-filter: blur(22px);
}
.chat-header {
background: radial-gradient(circle at top left, #4f46e5 0%, #4338ca 35%, #0f172a 100%);
padding: 18px 26px;
color: #e5e7eb;
position: relative;
overflow: hidden;
}
.chat-header::before {
content: '';
position: absolute;
inset: -40%;
background:
radial-gradient(circle at 0% 0%, rgba(248, 250, 252, 0.15), transparent 55%),
radial-gradient(circle at 80% 0%, rgba(248, 250, 252, 0.12), transparent 55%);
opacity: 0.5;
pointer-events: none;
}
.chat-header-content {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 1;
}
.user-info {
display: flex;
align-items: center;
gap: 14px;
}
.user-avatar {
width: 50px;
height: 50px;
border-radius: 20px;
background: radial-gradient(circle at 0 0, #f97316 0, #ec4899 50%, #6366f1 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
font-weight: 700;
color: #f9fafb;
border: 2px solid rgba(248, 250, 252, 0.3);
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.7);
}
.user-details h4 {
margin: 0;
font-size: 18px;
font-weight: 700;
color: #f9fafb;
display: flex;
align-items: center;
gap: 8px;
}
.user-details h4 span {
font-size: 11px;
padding: 2px 8px;
border-radius: 999px;
background: rgba(15, 23, 42, 0.55);
color: #e5e7eb;
text-transform: uppercase;
letter-spacing: 0.06em;
}
.user-details p {
margin: 4px 0 0;
font-size: 13px;
color: #c7d2fe;
opacity: 0.9;
}
.header-actions {
display: flex;
align-items: center;
gap: 12px;
}
.status-badge {
padding: 6px 14px;
border-radius: 999px;
font-weight: 600;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.08em;
border: 1px solid rgba(15, 23, 42, 0.5);
display: inline-flex;
align-items: center;
gap: 6px;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.7);
}
.status-badge.open {
background: radial-gradient(circle at 0 0, #22c55e 0, #16a34a 60%, #15803d 100%);
color: #ecfdf3;
}
.status-badge.closed {
background: radial-gradient(circle at 0 0, #fb7185 0, #ef4444 60%, #b91c1c 100%);
color: #fef2f2;
}
.back-btn {
background: rgba(15, 23, 42, 0.6);
border: 1px solid rgba(148, 163, 184, 0.6);
color: #e5e7eb;
padding: 7px 18px;
border-radius: 999px;
font-weight: 500;
font-size: 12px;
letter-spacing: 0.06em;
text-transform: uppercase;
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.7);
backdrop-filter: blur(10px);
}
.back-btn svg {
width: 16px;
height: 16px;
}
.back-btn:hover {
background: rgba(15, 23, 42, 0.9);
transform: translateX(-3px);
border-color: #e5e7eb;
}
.chat-box {
height: calc(100vh - 320px);
min-height: 420px;
max-height: 620px;
overflow-y: auto;
background: radial-gradient(circle at top left, rgba(15, 23, 42, 0.9), rgba(15, 23, 42, 1));
padding: 20px 22px;
position: relative;
}
.chat-box::before {
content: '';
position: absolute;
inset: 0;
background:
radial-gradient(circle at 15% 25%, rgba(56, 189, 248, 0.12) 0, transparent 55%),
radial-gradient(circle at 80% 80%, rgba(129, 140, 248, 0.1) 0, transparent 50%);
pointer-events: none;
opacity: 0.9;
}
.chat-box-inner {
position: relative;
z-index: 1;
}
.chat-box::-webkit-scrollbar {
width: 8px;
}
.chat-box::-webkit-scrollbar-track {
background: rgba(15, 23, 42, 0.9);
border-radius: 999px;
}
.chat-box::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, #4f46e5, #22d3ee);
border-radius: 999px;
}
.message {
max-width: 68%;
padding: 10px 14px;
margin-bottom: 14px;
font-size: 14px;
line-height: 1.55;
position: relative;
word-wrap: break-word;
border-radius: 14px;
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.7);
animation: messageSlideIn 0.22s ease-out;
}
@keyframes messageSlideIn {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.message.admin {
margin-left: auto;
background: radial-gradient(circle at 0 0, #4f46e5 0, #6366f1 40%, #22d3ee 100%);
color: #eef2ff;
border-radius: 14px 14px 4px 14px;
}
.message.user {
margin-right: auto;
background: rgba(15, 23, 42, 0.9);
border-radius: 14px 14px 14px 4px;
border: 1px solid rgba(148, 163, 184, 0.8);
color: #e5e7eb;
}
.message img {
max-width: 210px;
border-radius: 10px;
margin-top: 8px;
display: block;
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.8);
cursor: pointer;
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.message img:hover {
transform: translateY(-2px) scale(1.01);
box-shadow: 0 16px 40px rgba(15, 23, 42, 1);
}
/* File preview container */
.file-preview {
margin-top: 8px;
display: flex;
flex-direction: column;
gap: 6px;
}
.file-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
background: rgba(15, 23, 42, 0.55);
border-radius: 10px;
border: 1px solid rgba(148, 163, 184, 0.6);
transition: all 0.2s ease;
text-decoration: none;
color: inherit;
max-width: 250px;
}
.message.admin .file-item {
background: rgba(15, 23, 42, 0.5);
border-color: rgba(226, 232, 240, 0.7);
}
.message.user .file-item {
background: rgba(15, 23, 42, 0.7);
}
.file-item:hover {
transform: translateY(-1px);
background: rgba(15, 23, 42, 0.8);
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.4);
}
.file-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 14px;
}
.image-file .file-icon {
background: linear-gradient(135deg, #8b5cf6, #ec4899);
color: white;
}
.pdf-file .file-icon {
background: linear-gradient(135deg, #ef4444, #dc2626);
color: white;
}
.video-file .file-icon {
background: linear-gradient(135deg, #3b82f6, #1d4ed8);
color: white;
}
.document-file .file-icon {
background: linear-gradient(135deg, #10b981, #059669);
color: white;
}
.other-file .file-icon {
background: linear-gradient(135deg, #6b7280, #4b5563);
color: white;
}
.file-info {
flex: 1;
min-width: 0;
}
.file-name {
font-size: 12px;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom: 2px;
}
.file-size {
font-size: 10px;
opacity: 0.7;
color: #9ca3af;
}
.file-download-btn {
background: rgba(255, 255, 255, 0.1);
border: none;
width: 24px;
height: 24px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
flex-shrink: 0;
}
.file-download-btn svg {
width: 12px;
height: 12px;
color: #cbd5e1;
}
.file-download-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.message small {
font-size: 10px;
opacity: 0.7;
margin-top: 6px;
display: block;
text-align: right;
}
.message.user small {
text-align: left;
color: #9ca3af;
}
.message.admin small {
color: #e0f2fe;
}
.date-divider {
text-align: center;
margin: 18px 0;
position: relative;
}
.date-divider::before,
.date-divider::after {
content: '';
position: absolute;
top: 50%;
width: 40%;
height: 1px;
background: radial-gradient(circle, rgba(148, 163, 184, 0.6), transparent);
}
.date-divider::before {
left: 0;
}
.date-divider::after {
right: 0;
}
.date-divider span {
background: rgba(15, 23, 42, 0.95);
padding: 4px 12px;
border-radius: 999px;
font-size: 11px;
font-weight: 600;
color: #9ca3af;
border: 1px solid rgba(55, 65, 81, 0.8);
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.9);
}
.chat-input-container {
background: radial-gradient(circle at top, rgba(15, 23, 42, 0.95), #020617);
padding: 14px 18px 18px;
border-top: 1px solid rgba(31, 41, 55, 0.9);
}
.chat-input-wrapper {
display: flex;
align-items: center;
gap: 10px;
background: rgba(15, 23, 42, 0.95);
border-radius: 999px;
padding: 4px 4px 4px 18px;
border: 1px solid rgba(55, 65, 81, 0.9);
transition: all 0.18s ease;
}
.chat-input-wrapper:focus-within {
border-color: #6366f1;
box-shadow: 0 0 0 1px rgba(129, 140, 248, 0.9);
}
.chat-input-wrapper input[type="text"] {
flex: 1;
border: none;
background: transparent;
font-size: 14px;
padding: 8px 0;
outline: none;
color: #e5e7eb;
}
.chat-input-wrapper input[type="text"]::placeholder {
color: #6b7280;
}
.file-upload-container {
position: relative;
display: flex;
align-items: center;
gap: 8px;
}
.file-upload-btn {
position: relative;
cursor: pointer;
}
.file-upload-btn input[type="file"] {
position: absolute;
opacity: 0;
inset: 0;
cursor: pointer;
}
.file-upload-btn label {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border-radius: 999px;
background: radial-gradient(circle at 0 0, #22c55e 0, #22c55e 40%, #16a34a 100%);
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 10px 25px rgba(22, 163, 74, 0.7);
}
.file-upload-btn label svg {
width: 18px;
height: 18px;
color: #ecfdf5;
}
.file-upload-btn label:hover {
transform: translateY(-1px) scale(1.03);
filter: brightness(1.05);
}
.file-name-display {
background: rgba(15, 23, 42, 0.7);
border: 1px solid rgba(55, 65, 81, 0.8);
border-radius: 8px;
padding: 6px 10px;
font-size: 12px;
color: #9ca3af;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 150px;
display: none;
}
.clear-file-btn {
background: none;
border: none;
color: #ef4444;
cursor: pointer;
padding: 4px;
border-radius: 4px;
display: none;
transition: all 0.2s ease;
}
.clear-file-btn:hover {
background: rgba(239, 68, 68, 0.1);
}
.clear-file-btn svg {
width: 14px;
height: 14px;
}
.send-btn {
background: radial-gradient(circle at 0 0, #38bdf8 0, #6366f1 50%, #4f46e5 100%);
border: none;
width: 44px;
height: 44px;
border-radius: 999px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 12px 28px rgba(37, 99, 235, 0.85);
}
.send-btn svg {
width: 18px;
height: 18px;
color: #eff6ff;
}
.send-btn:hover {
transform: translateY(-1px) scale(1.04);
box-shadow: 0 18px 40px rgba(37, 99, 235, 1);
}
.send-btn:active {
transform: scale(0.96);
box-shadow: 0 8px 22px rgba(30, 64, 175, 0.9);
}
.typing-indicator {
display: none;
padding: 8px 12px;
background: rgba(15, 23, 42, 0.95);
border-radius: 999px;
max-width: 70px;
margin-bottom: 10px;
box-shadow: 0 10px 25px rgba(15, 23, 42, 0.9);
}
.typing-indicator span {
height: 6px;
width: 6px;
background: #a5b4fc;
border-radius: 999px;
display: inline-block;
margin: 0 2px;
animation: typing 1.2s infinite;
}
.typing-indicator span:nth-child(2) {
animation-delay: 0.15s;
}
.typing-indicator span:nth-child(3) {
animation-delay: 0.3s;
}
@keyframes typing {
0%, 60%, 100% {
transform: translateY(0);
opacity: 0.5;
}
30% {
transform: translateY(-6px);
opacity: 1;
}
}
@media (max-width: 768px) {
.chat-container {
padding: 10px;
}
.chat-shell {
border-radius: 20px;
}
.chat-header-content {
flex-direction: column;
align-items: flex-start;
gap: 10px;
}
.user-info {
width: 100%;
}
.header-actions {
width: 100%;
justify-content: space-between;
}
.back-btn {
padding-inline: 12px;
}
.message {
max-width: 85%;
}
.chat-box {
height: calc(100vh - 360px);
}
.file-name-display {
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">
<div class="chat-shell">
<div class="chat-header">
<div class="chat-header-content">
<div class="user-info">
<div class="user-avatar">
{{ strtoupper(substr(($ticket->user->customer_name ?? $ticket->user->name), 0, 1)) }}
</div>
<div class="user-details">
<h4>
{{ $ticket->user->customer_name ?? $ticket->user->name }}
<span>Online</span>
</h4>
<p>Ticket #{{ $ticket->id }}</p>
</div>
</div>
<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' }}">
{{ ucfirst($ticket->status) }}
</span>
<a href="{{ route('admin.chat_support') }}" class="back-btn">
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
Back
</a>
</div>
</div>
</div>
{{-- Messages --}}
<div id="chatBox" class="chat-box">
<div class="chat-box-inner">
@foreach($messages as $msg)
<div class="message {{ $msg->sender_type === 'App\\Models\\Admin' ? 'admin' : 'user' }}">
@if($msg->message)
<div>{{ $msg->message }}</div>
@endif
@if($msg->file_path)
@php
$isImage = Str::startsWith($msg->file_type, 'image');
$isVideo = Str::startsWith($msg->file_type, 'video');
$isPdf = Str::endsWith($msg->file_path, '.pdf');
$isDocument = in_array(Str::lower(Str::afterLast($msg->file_path, '.')), ['doc', 'docx', 'txt']);
$fileName = basename($msg->file_path);
$fileSize = 'N/A';
try {
$fullPath = storage_path('app/public/' . $msg->file_path);
if (file_exists($fullPath)) {
$size = filesize($fullPath);
if ($size < 1024) {
$fileSize = $size . ' B';
} elseif ($size < 1048576) {
$fileSize = round($size / 1024, 1) . ' KB';
} else {
$fileSize = round($size / 1048576, 1) . ' MB';
}
}
} catch (Exception $e) {
$fileSize = 'Unknown';
}
if ($isImage) {
$fileClass = 'image-file';
$fileIcon = '🖼️';
} elseif ($isVideo) {
$fileClass = 'video-file';
$fileIcon = '🎬';
} elseif ($isPdf) {
$fileClass = 'pdf-file';
$fileIcon = '📄';
} elseif ($isDocument) {
$fileClass = 'document-file';
$fileIcon = '📝';
} else {
$fileClass = 'other-file';
$fileIcon = '📎';
}
@endphp
<div class="file-preview">
@if($isImage)
<img src="{{ asset('storage/'.$msg->file_path) }}"
style="max-width:210px;"
class="rounded mt-2"
alt="{{ $fileName }}">
@elseif($isVideo)
<video src="{{ asset('storage/'.$msg->file_path) }}"
controls
class="mt-2 rounded"
style="max-width:200px;">
Your browser does not support the video tag.
</video>
@endif
<a href="{{ asset('storage/'.$msg->file_path) }}"
target="_blank"
class="file-item {{ $fileClass }}"
title="{{ $fileName }}">
<div class="file-icon">
{{ $fileIcon }}
</div>
<div class="file-info">
<div class="file-name">{{ $fileName }}</div>
<div class="file-size">{{ $fileSize }}</div>
</div>
<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" />
</svg>
</div>
</a>
</div>
@endif
<small class="mt-2">
{{ $msg->created_at->format('d M h:i A') }}
</small>
</div>
@endforeach
<div class="typing-indicator" id="typingIndicator">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
{{-- Input --}}
<div class="chat-input-container">
<div class="chat-input-wrapper">
<input type="text" id="messageInput" placeholder="Type a reply…" autocomplete="off">
<div class="file-upload-container">
<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"/>
</svg>
</button>
<div class="file-upload-btn">
<input type="file" id="fileInput">
<label for="fileInput" title="Attach file">
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
d="M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13" />
</svg>
</label>
</div>
</div>
<button class="send-btn" id="sendBtn">
<svg xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round"
stroke-width="2"
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" />
</svg>
</button>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
<script>
const CURRENT_ADMIN_ID = {{ auth('admin')->id() }};
console.log("CHAT WINDOW: script loaded");
// -------------------------------
// WAIT FOR ECHO READY
// -------------------------------
function waitForEcho(callback, retries = 40) {
if (window.Echo) {
console.log("%c[ECHO] Ready!", "color: green; font-weight: bold;", window.Echo);
return callback();
}
console.warn("[ECHO] Not ready. Retrying...");
if (retries <= 0) {
console.error("[ECHO] FAILED to initialize after retry limit");
return;
}
setTimeout(() => waitForEcho(callback, retries - 1), 200);
}
// Scroll chat down
function scrollToBottom() {
const el = document.getElementById("chatBox");
if (el) el.scrollTop = el.scrollHeight;
}
scrollToBottom();
// -------------------------------
// FILE UPLOAD HANDLING
// -------------------------------
const fileInput = document.getElementById('fileInput');
const fileNameDisplay = document.getElementById('fileNameDisplay');
const clearFileBtn = document.getElementById('clearFileBtn');
fileInput.addEventListener('change', function() {
if (this.files.length > 0) {
const file = this.files[0];
const name = file.name;
const size = formatFileSize(file.size);
fileNameDisplay.textContent = `${name} (${size})`;
fileNameDisplay.style.display = 'block';
clearFileBtn.style.display = 'block';
}
});
clearFileBtn.addEventListener('click', function() {
fileInput.value = '';
fileNameDisplay.style.display = 'none';
clearFileBtn.style.display = 'none';
});
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
function getFileIcon(fileType, fileName) {
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';
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 () {
let msg = document.getElementById("messageInput").value;
let file = document.getElementById("fileInput").files[0];
if (!msg.trim() && !file) {
alert("Please type something or upload a file.");
return;
}
let formData = new FormData();
formData.append("message", msg);
if (file) formData.append("file", file);
fetch("{{ route('admin.chat.send', $ticket->id) }}", {
method: "POST",
headers: { "X-CSRF-TOKEN": "{{ csrf_token() }}" },
body: formData
})
.then(res => res.json())
.then(() => {
document.getElementById("messageInput").value = "";
document.getElementById("fileInput").value = "";
fileNameDisplay.style.display = 'none';
clearFileBtn.style.display = 'none';
scrollToBottom();
})
.catch(err => console.error("[SEND] Error:", err));
});
// Enter key support
document.getElementById("messageInput").addEventListener("keypress", function(e) {
if (e.key === "Enter") {
document.getElementById("sendBtn").click();
}
});
// -------------------------------
// 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;
const isMine =
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 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);
const isImage = (msg.file_type || '').startsWith('image');
const isVideo = (msg.file_type || '').startsWith('video');
html += `<div class="file-preview">`;
if (isImage) {
html += `
<img src="${fileUrl}"
class="rounded mt-2"
style="max-width:210px;"
alt="${fileName}">
`;
} else if (isVideo) {
html += `
<video src="${fileUrl}"
controls
class="mt-2 rounded"
style="max-width:200px;">
</video>
`;
}
html += `
<a href="${fileUrl}"
target="_blank"
class="file-item ${fileClass}"
title="${fileName}">
<div class="file-icon">${fileIcon}</div>
<div class="file-info">
<div class="file-name">${fileName}</div>
<div class="file-size">${fileSize}</div>
</div>
<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"/>
</svg>
</div>
</a>
</div>`;
}
html += `<small class="mt-2">Just now</small></div>`;
document
.querySelector("#chatBox .chat-box-inner")
.insertAdjacentHTML("beforeend", html);
scrollToBottom();
});
});
</script>
@endsection