import React, { useState, useEffect, useRef } from 'react'; import { Head, Link, router } from '@inertiajs/react'; import EmployerLayout from '../../../Layouts/EmployerLayout'; import { useTranslation } from '../../../lib/LanguageContext'; import { Send, ArrowLeft, CheckCircle2, Globe2, DollarSign, Sparkles, MoreVertical, Phone, Video, Paperclip, Search, Info, Calendar, Briefcase, ShieldCheck, ChevronRight, MapPin, Smile, X, MessageSquare, Eye, UploadCloud, FileText, Mic, Square } from 'lucide-react'; import { toast } from 'sonner'; export default function Show({ conversation, initialMessages, conversations = [] }) { const { t } = useTranslation(); const [messages, setMessages] = useState(initialMessages || []); const [input, setInput] = useState(''); const [isTyping, setIsTyping] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [showHireConfirmModal, setShowHireConfirmModal] = useState(false); const [showAttachmentModal, setShowAttachmentModal] = useState(false); const [uploadedFiles, setUploadedFiles] = useState([]); const [isRecording, setIsRecording] = useState(false); const mediaRecorderRef = useRef(null); const audioChunksRef = useRef([]); const messagesEndRef = useRef(null); const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }; useEffect(() => { scrollToBottom(); }, [messages, isTyping]); useEffect(() => { setMessages(initialMessages || []); }, [initialMessages]); // Simulate typing indicator when conversation opens useEffect(() => { setIsTyping(true); const timer = setTimeout(() => setIsTyping(false), 2500); return () => clearTimeout(timer); }, [conversation.id]); const filteredConversations = (conversations || []).filter(c => c.worker_name.toLowerCase().includes(searchTerm.toLowerCase()) ); const sendFile = (file) => { if (!file) return; const isImage = file.type.startsWith('image/'); const isAudio = file.type.startsWith('audio/'); const type = isImage ? 'image' : (isAudio ? 'voice' : 'document'); // Optimistically update message feed const newMsg = { id: Date.now(), sender: 'employer', text: file.name, time: t('just_now', 'Just Now'), read: false, attachment_url: URL.createObjectURL(file), attachment_type: type }; setMessages(prev => [...prev, newMsg]); router.post(`/employer/messages/${conversation.id}/send`, { text: file.name, file: file }, { preserveScroll: true, onSuccess: () => { toast.success(t('file_sent_success', 'File and notification sent successfully!')); setShowAttachmentModal(false); }, onError: (err) => { toast.error(t('file_sent_error', 'Failed to upload file attachment.')); } }); }; const sendMsg = (text) => { if (!text.trim()) return; // Optimistically update message feed const newMsg = { id: Date.now(), sender: 'employer', text: text, time: t('just_now', 'Just Now'), read: false }; setMessages(prev => [...prev, newMsg]); setInput(''); const isLookingJobQuery = text.toLowerCase().includes('looking') && text.toLowerCase().includes('job'); router.post(`/employer/messages/${conversation.id}/send`, { text: text }, { preserveScroll: true, onSuccess: () => { router.reload({ preserveScroll: true }); } }); }; const handleFormSubmit = (e) => { e.preventDefault(); sendMsg(input); }; const handleFileUpload = (e) => { const file = e.target.files[0]; if (!file) return; sendFile(file); }; const startRecording = async () => { try { // Check browser permission status if query API is supported if (navigator.permissions && navigator.permissions.query) { try { const permissionStatus = await navigator.permissions.query({ name: 'microphone' }); if (permissionStatus.state === 'denied') { toast.error(t('microphone_blocked', 'Microphone access is blocked. Please enable microphone permissions in your browser settings.')); return; } } catch (pe) { // Ignore query check failures if name is not supported in some browsers } } const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); audioChunksRef.current = []; const recorder = new MediaRecorder(stream); recorder.ondataavailable = (e) => { if (e.data.size > 0) { audioChunksRef.current.push(e.data); } }; recorder.onstop = () => { const audioBlob = new Blob(audioChunksRef.current, { type: 'audio/webm' }); const file = new File([audioBlob], `voice_${Date.now()}.webm`, { type: 'audio/webm' }); sendFile(file); // Stop all tracks to release mic stream.getTracks().forEach(track => track.stop()); }; mediaRecorderRef.current = recorder; recorder.start(); setIsRecording(true); toast.info(t('recording_started', 'Recording voice message... Click microphone again to stop.')); } catch (err) { console.error(err); if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') { toast.error(t('microphone_permission_denied', 'Permission to access the microphone was denied. Please allow microphone access in your browser settings.')); } else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') { toast.error(t('microphone_not_found', 'No microphone device was detected on your system.')); } else { toast.error(t('microphone_access_error', 'Could not access the microphone. Please check your system settings.')); } } }; const handleMarkHired = () => { router.post(`/employer/workers/${conversation.worker_id}/mark-hired`, {}, { preserveScroll: true, onSuccess: () => { toast.success(t('marked_hired', 'Worker successfully marked as Hired!')); router.reload(); } }); }; const stopRecording = () => { if (mediaRecorderRef.current && isRecording) { mediaRecorderRef.current.stop(); setIsRecording(false); } }; const chips = [ t('chip_salary', "What is your final expected salary?"), t('chip_visa', "Can you transfer your visa immediately?") ]; return (
{/* Conversation List Sidebar (Desktop) */} {/* Main Chat Interface */}
{/* Header */}
{conversation.worker_name.charAt(0)}

{conversation.worker_name}

{t('active_now', 'Active Now')}
{conversation.worker_status !== 'hired' && conversation.worker_status !== 'hidden' ? ( ) : conversation.worker_status === 'hired' ? ( {t('hired', 'Hired')} ) : null}
{/* Chat Messages Log */}
{t('today_realtime_chat_logs', 'Today • Realtime Chat Logs')}
{messages.map((msg, idx) => { const isEmp = msg.sender === 'employer'; const hasImage = msg.attachment_url && msg.attachment_type === 'image'; const hasVoice = msg.attachment_url && msg.attachment_type === 'voice'; const hasDoc = msg.attachment_url && msg.attachment_type === 'document'; const isImageFilename = msg.text && msg.text.match(/\.(jpg|jpeg|png|gif|webp)$/i); return (
{/* Main Message Bubble (WhatsApp Style) */}
{/* Image Attachment (WhatsApp Full Width style) */} {hasImage && ( )} {/* Voice Message */} {hasVoice && (
)} {/* Document Attachment */} {hasDoc && ( )} {/* Text Content (if not just the filename of the image) */} {msg.text && (!hasImage || !isImageFilename) && (
{msg.text}
)} {/* Time & Read Status Indicator */}
{msg.time} {isEmp && ( )}
); })} {isTyping && (
)}
{/* Quick replies & typing input */}
{chips.map((chip, idx) => ( ))}
setInput(e.target.value)} placeholder={t('type_message_placeholder', 'Type a compliant secure message...')} className="flex-1 px-5 py-3.5 bg-slate-50 rounded-xl text-xs font-semibold outline-none border border-transparent focus:border-slate-200 transition-all" />
{/* Interactive File Attachment Modal Placeholder */} {showAttachmentModal && (

{t('attach_sponsor_documents', 'Attach Verified Sponsor Documents')}

{t('attach_documents_desc', 'Upload sponsor papers, employment agreements, or work descriptions to review securely.')}

{t('select_files_drag', 'Select files or drag here')}
PDF, JPG, PNG (Max 5MB)
)} {/* Hired Confirmation Modal */} {showHireConfirmModal && (

{t('confirm_hire_title', 'Confirm Hiring')}

{t('confirm_hire_desc', 'Verify this worker with your needs and make sure to hire this worker. If hired, you can view this worker in the Hired Workers page.')}

)} ); }