import { json } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { useState, useRef, useEffect } from "react"; import { Card, CardContent, CardHeader } from "~/components/ui/card"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Badge } from "~/components/ui/badge"; import { ScrollArea } from "~/components/ui/scroll-area"; import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"; import { Separator } from "~/components/ui/separator"; import { Textarea } from "~/components/ui/textarea"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "~/components/ui/dropdown-menu"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from "~/components/ui/dialog"; import { Search, Send, Phone, Video, MoreVertical, Paperclip, Image, MapPin, Clock, Check, CheckCheck, User, MessageCircle, Truck, Package, AlertCircle } from "lucide-react"; // Interfaces interface Message { id: string; senderId: string; senderName: string; content: string; timestamp: string; type: "text" | "image" | "location" | "document"; status: "sent" | "delivered" | "read"; attachmentUrl?: string; } interface Contact { id: string; name: string; role: string; avatar?: string; isOnline: boolean; lastSeen: string; lastMessage: string; unreadCount: number; location?: string; phone: string; } interface ChatData { contacts: Contact[]; messages: { [contactId: string]: Message[] }; currentUser: { id: string; name: string; }; } export const loader = async (): Promise => { // Mock data - dalam implementasi nyata, ambil dari database const chatData: ChatData = { currentUser: { id: "pengelola-001", name: "Ahmad Pengelola" }, contacts: [ { id: "pengepul-001", name: "Budi Santoso", role: "Driver Truk B-001", avatar: "", isOnline: true, lastSeen: "Online", lastMessage: "Sampah sudah diangkut semua pak", unreadCount: 2, location: "Kelurahan Merdeka", phone: "081234567890" }, { id: "pengepul-002", name: "Sari Dewi", role: "Driver Truk B-003", avatar: "", isOnline: false, lastSeen: "15 menit yang lalu", lastMessage: "Baik pak, segera ke lokasi", unreadCount: 0, location: "Komplek Permata", phone: "081234567891" }, { id: "pengepul-003", name: "Dedi Kurniawan", role: "Driver Truk B-004", avatar: "", isOnline: true, lastSeen: "Online", lastMessage: "Truk sedang dalam perjalanan", unreadCount: 1, location: "Jl. Sudirman", phone: "081234567892" }, { id: "pengepul-004", name: "Andi Wijaya", role: "Driver Truk B-002", avatar: "", isOnline: false, lastSeen: "2 jam yang lalu", lastMessage: "Maintenance selesai besok pagi", unreadCount: 0, location: "Workshop", phone: "081234567893" }, { id: "pengepul-005", name: "Rini Astuti", role: "Supervisor Lapangan", avatar: "", isOnline: true, lastSeen: "Online", lastMessage: "Laporan harian sudah dikirim", unreadCount: 0, location: "Pool Kendaraan", phone: "081234567894" } ], messages: { "pengepul-001": [ { id: "msg-001", senderId: "pengepul-001", senderName: "Budi Santoso", content: "Selamat pagi pak, saya sudah sampai di lokasi Kelurahan Merdeka", timestamp: "08:00", type: "text", status: "read" }, { id: "msg-002", senderId: "pengelola-001", senderName: "Ahmad Pengelola", content: "Pagi Bud, bagus. Berapa estimasi waktu pengangkutan?", timestamp: "08:02", type: "text", status: "read" }, { id: "msg-003", senderId: "pengepul-001", senderName: "Budi Santoso", content: "Sekitar 2 jam pak, volume sampah cukup banyak hari ini", timestamp: "08:05", type: "text", status: "read" }, { id: "msg-004", senderId: "pengepul-001", senderName: "Budi Santoso", content: "Sampah sudah diangkut semua pak", timestamp: "10:30", type: "text", status: "delivered" }, { id: "msg-005", senderId: "pengepul-001", senderName: "Budi Santoso", content: "Total 245 kg, lanjut ke lokasi berikutnya?", timestamp: "10:31", type: "text", status: "sent" } ], "pengepul-002": [ { id: "msg-006", senderId: "pengelola-001", senderName: "Ahmad Pengelola", content: "Sari, bisa ke Komplek Permata sekarang?", timestamp: "09:15", type: "text", status: "read" }, { id: "msg-007", senderId: "pengepul-002", senderName: "Sari Dewi", content: "Baik pak, segera ke lokasi", timestamp: "09:17", type: "text", status: "read" } ], "pengepul-003": [ { id: "msg-008", senderId: "pengepul-003", senderName: "Dedi Kurniawan", content: "Pak, ada kemacetan di Jl. Sudirman, mungkin terlambat 30 menit", timestamp: "11:00", type: "text", status: "read" }, { id: "msg-009", senderId: "pengelola-001", senderName: "Ahmad Pengelola", content: "OK Ded, hati-hati di jalan. Update terus ya", timestamp: "11:05", type: "text", status: "read" }, { id: "msg-010", senderId: "pengepul-003", senderName: "Dedi Kurniawan", content: "Truk sedang dalam perjalanan", timestamp: "11:45", type: "text", status: "delivered" } ] } }; return json(chatData); }; export default function ChatPengepul() { const data = useLoaderData(); const [selectedContact, setSelectedContact] = useState( data.contacts[0] ); const [messageInput, setMessageInput] = useState(""); const [searchQuery, setSearchQuery] = useState(""); const scrollAreaRef = useRef(null); // Filter contacts berdasarkan search const filteredContacts = data.contacts.filter( (contact) => contact.name.toLowerCase().includes(searchQuery.toLowerCase()) || contact.role.toLowerCase().includes(searchQuery.toLowerCase()) ); // Get messages untuk contact yang dipilih const currentMessages = selectedContact ? data.messages[selectedContact.id] || [] : []; // Auto scroll ke pesan terbaru useEffect(() => { if (scrollAreaRef.current) { scrollAreaRef.current.scrollTop = scrollAreaRef.current.scrollHeight; } }, [currentMessages]); const handleSendMessage = () => { if (!messageInput.trim() || !selectedContact) return; // Implementasi kirim pesan (dalam real app, hit API) console.log("Sending message:", messageInput, "to:", selectedContact.name); setMessageInput(""); }; const getStatusIcon = (status: string) => { switch (status) { case "sent": return ; case "delivered": return ; case "read": return ; default: return ; } }; const getContactStatusBadge = (contact: Contact) => { if (contact.isOnline) { return ( Online ); } return ( {contact.lastSeen} ); }; return (
{/* Header */}

Chat Pengepul

{/* Chat Container */}
{/* Sidebar Contact List */}
setSearchQuery(e.target.value)} className="border-none shadow-none focus-visible:ring-0" />
{filteredContacts.map((contact) => (
setSelectedContact(contact)} className={`p-3 rounded-lg cursor-pointer transition-colors ${ selectedContact?.id === contact.id ? "bg-primary/10 border border-primary/20" : "hover:bg-gray-50" }`} >
{contact.name .split(" ") .map((n) => n[0]) .join("")} {contact.isOnline && (
)}

{contact.name}

{contact.unreadCount > 0 && ( {contact.unreadCount} )}

{contact.role}

{contact.lastMessage}

{contact.location}
{getContactStatusBadge(contact)}
))}
{/* Chat Area */} {selectedContact ? ( <> {/* Chat Header */}
{selectedContact.name .split(" ") .map((n) => n[0]) .join("")}

{selectedContact.name}

{selectedContact.role}

{getContactStatusBadge(selectedContact)}
Lihat Profil Lacak Lokasi Riwayat Tugas
{/* Messages Area */}
{currentMessages.map((message) => { const isOwnMessage = message.senderId === data.currentUser.id; return (

{message.content}

{message.timestamp} {isOwnMessage && (
{getStatusIcon(message.status)}
)}
); })}
{/* Message Input */}
Kirim Foto Bagikan Lokasi Kirim Dokumen