218 lines
6.3 KiB
PHP
218 lines
6.3 KiB
PHP
@extends('admin.layouts.app')
|
|
|
|
@section('page-title', 'Chat With ' . ($ticket->user->customer_name ?? $ticket->user->name))
|
|
|
|
@section('content')
|
|
|
|
<style>
|
|
.chat-box {
|
|
height: 70vh;
|
|
overflow-y: auto;
|
|
background: #f5f6fa;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
}
|
|
.message {
|
|
max-width: 65%;
|
|
padding: 10px 14px;
|
|
border-radius: 15px;
|
|
margin-bottom: 10px;
|
|
font-size: 0.9rem;
|
|
line-height: 1.4;
|
|
}
|
|
.message.admin {
|
|
background: #007bff;
|
|
color: white;
|
|
margin-left: auto;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
.message.user {
|
|
background: #ffffff;
|
|
border: 1px solid #ddd;
|
|
margin-right: auto;
|
|
border-bottom-left-radius: 0;
|
|
}
|
|
.chat-input {
|
|
position: fixed;
|
|
bottom: 15px;
|
|
left: 250px;
|
|
right: 20px;
|
|
}
|
|
</style>
|
|
|
|
<div class="container py-4">
|
|
|
|
<div class="d-flex align-items-center mb-3">
|
|
<h4 class="fw-bold mb-0">
|
|
Chat With: {{ $ticket->user->customer_name ?? $ticket->user->name }}
|
|
</h4>
|
|
<span class="badge ms-3 {{ $ticket->status === 'open' ? 'bg-success' : 'bg-danger' }}">
|
|
{{ ucfirst($ticket->status) }}
|
|
</span>
|
|
</div>
|
|
|
|
<div id="chatBox" class="chat-box border shadow-sm">
|
|
|
|
@foreach($messages as $msg)
|
|
<div class="message {{ $msg->sender_type === 'App\\Models\\Admin' ? 'admin' : 'user' }}">
|
|
|
|
{{-- TEXT --}}
|
|
@if($msg->message)
|
|
<div>{{ $msg->message }}</div>
|
|
@endif
|
|
|
|
{{-- FILE --}}
|
|
@if($msg->file_path)
|
|
<div class="mt-2">
|
|
@php $isImage = Str::startsWith($msg->file_type, 'image'); @endphp
|
|
|
|
@if($isImage)
|
|
<img src="{{ asset('storage/'.$msg->file_path) }}" style="max-width:150px;" class="rounded">
|
|
@else
|
|
<a href="{{ asset('storage/'.$msg->file_path) }}" target="_blank">📎 View Attachment</a>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
|
|
<small class="text-muted d-block mt-1">
|
|
{{ $msg->created_at->format('d M h:i A') }}
|
|
</small>
|
|
|
|
</div>
|
|
@endforeach
|
|
|
|
</div>
|
|
|
|
<div class="chat-input">
|
|
<div class="card shadow-sm">
|
|
<div class="card-body d-flex align-items-center gap-2">
|
|
<input type="text" id="messageInput" class="form-control" placeholder="Type your message...">
|
|
<input type="file" id="fileInput" class="form-control" style="max-width:200px;">
|
|
<button class="btn btn-primary" id="sendBtn">Send</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
@endsection
|
|
|
|
|
|
@section('scripts')
|
|
|
|
|
|
|
|
<script>
|
|
// ✅ Make current admin ID available to JS
|
|
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();
|
|
|
|
// -------------------------------
|
|
// SEND MESSAGE (WORKING PART FROM SCRIPT #1)
|
|
// -------------------------------
|
|
document.getElementById("sendBtn").addEventListener("click", function () {
|
|
console.log("[SEND] Attempting to send message...");
|
|
|
|
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((response) => {
|
|
console.log("[SEND] Message sent:", response);
|
|
document.getElementById("messageInput").value = "";
|
|
document.getElementById("fileInput").value = "";
|
|
})
|
|
.catch(err => console.error("[SEND] Error:", err));
|
|
});
|
|
|
|
// -------------------------------
|
|
// LISTEN FOR REALTIME MESSAGE (WORKING PART FROM SCRIPT #2)
|
|
// ----------------------------
|
|
|
|
waitForEcho(() => {
|
|
const ticketId = "{{ $ticket->id }}";
|
|
|
|
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; // ✅ flat payload
|
|
|
|
// ✅ CHECK IF THIS MESSAGE IS SENT BY CURRENT ADMIN
|
|
const isMine =
|
|
msg.sender_type === 'App\\Models\\Admin' &&
|
|
msg.sender_id === CURRENT_ADMIN_ID;
|
|
|
|
let html = `
|
|
<div class="message ${isMine ? 'admin' : 'user'}">
|
|
${msg.message ?? ''}
|
|
`;
|
|
|
|
if (msg.file_url) {
|
|
if (msg.file_type?.startsWith("image")) {
|
|
html += `<img src="${msg.file_url}" class="rounded mt-2" style="max-width:150px;">`;
|
|
} else {
|
|
html += `<a href="${msg.file_url}" target="_blank" class="mt-2 d-block">📎 View File</a>`;
|
|
}
|
|
}
|
|
|
|
html += `
|
|
<small class="text-muted d-block mt-1">Just now</small>
|
|
</div>
|
|
`;
|
|
|
|
document
|
|
.getElementById("chatBox")
|
|
.insertAdjacentHTML("beforeend", html);
|
|
|
|
scrollToBottom();
|
|
});
|
|
});
|
|
|
|
</script>
|
|
|
|
@endsection
|