chat support
This commit is contained in:
217
resources/views/admin/chat_window.blade.php
Normal file
217
resources/views/admin/chat_window.blade.php
Normal file
@@ -0,0 +1,217 @@
|
||||
@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
|
||||
Reference in New Issue
Block a user