"use client" import React, { useState, useEffect, useRef } from "react"; import { useRouter } from "next/navigation"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { formatDistanceToNow } from "date-fns"; import axios from "axios"; import { toast } from "sonner"; import { getCookie } from "@/lib/client-utils"; interface Chat { _id: string; buyerId: string; vendorId: string; storeId: string; lastUpdated: string; orderId?: string; } interface UnreadCounts { totalUnread: number; chatCounts: Record; } export default function ChatList() { const router = useRouter(); const [chats, setChats] = useState([]); const [loading, setLoading] = useState(true); const [unreadCounts, setUnreadCounts] = useState({ totalUnread: 0, chatCounts: {} }); const [previousTotalUnread, setPreviousTotalUnread] = useState(0); const [selectedStore, setSelectedStore] = useState(""); const [vendorStores, setVendorStores] = useState<{ _id: string, name: string }[]>([]); const audioRef = useRef(null); // Initialize audio element useEffect(() => { // Create audio element for notification sound audioRef.current = new Audio('/notification.mp3'); // Fallback if notification.mp3 doesn't exist - use browser API for a simple beep audioRef.current.addEventListener('error', () => { audioRef.current = null; }); return () => { if (audioRef.current) { audioRef.current = null; } }; }, []); // Function to play notification sound const playNotificationSound = () => { if (audioRef.current) { audioRef.current.currentTime = 0; audioRef.current.play().catch(err => { console.log('Error playing sound:', err); // Fallback to simple beep if audio file fails try { const context = new (window.AudioContext || (window as any).webkitAudioContext)(); const oscillator = context.createOscillator(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(800, context.currentTime); oscillator.connect(context.destination); oscillator.start(); oscillator.stop(context.currentTime + 0.2); } catch (e) { console.error('Could not play fallback audio', e); } }); } else { // Fallback to simple beep if audio element is not available try { const context = new (window.AudioContext || (window as any).webkitAudioContext)(); const oscillator = context.createOscillator(); oscillator.type = 'sine'; oscillator.frequency.setValueAtTime(800, context.currentTime); oscillator.connect(context.destination); oscillator.start(); oscillator.stop(context.currentTime + 0.2); } catch (e) { console.error('Could not play fallback audio', e); } } }; // Fetch vendor ID and stores useEffect(() => { const fetchVendorData = async () => { try { // Get auth token from cookies const authToken = getCookie("Authorization"); if (!authToken) { toast.error("You need to be logged in to view chats"); router.push("/auth/login"); return; } // Set up axios with the auth token const authAxios = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { Authorization: `Bearer ${authToken}` } }); // First, get vendor info using the /auth/me endpoint const vendorResponse = await authAxios.get('/auth/me'); console.log("Vendor auth response:", vendorResponse.data); // Access correct property - the vendor ID is in vendor._id const vendorId = vendorResponse.data.vendor?._id; if (!vendorId) { console.error("Vendor ID not found in profile response:", vendorResponse.data); toast.error("Could not retrieve vendor information"); return; } // Fetch vendor's store using storefront endpoint const storeResponse = await authAxios.get(`/storefront`); console.log("Store response:", storeResponse.data); // Handle both array and single object responses if (Array.isArray(storeResponse.data)) { // If it's an array, use it as is setVendorStores(storeResponse.data); if (storeResponse.data.length > 0) { setSelectedStore(storeResponse.data[0]._id); } } else if (storeResponse.data && typeof storeResponse.data === 'object' && storeResponse.data._id) { // If it's a single store object, convert it to an array with one element const singleStore = [storeResponse.data]; setVendorStores(singleStore); setSelectedStore(storeResponse.data._id); } else { console.error("Expected store data but received:", storeResponse.data); setVendorStores([]); toast.error("Failed to load store data in expected format"); } } catch (error) { console.error("Error fetching vendor data:", error); toast.error("Failed to load vendor data"); setVendorStores([]); } }; fetchVendorData(); }, [router]); // Fetch chats and unread counts when store is selected const fetchChats = async () => { try { if (!selectedStore) return; // Get vendor ID from auth token const authToken = getCookie("Authorization"); if (!authToken) { console.error("No auth token found"); return; } // Set up axios with auth token const authAxios = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, headers: { Authorization: `Bearer ${authToken}` } }); // Get vendor ID from JWT token const tokenParts = authToken.split('.'); const payload = JSON.parse(atob(tokenParts[1])); const vendorId = payload.id; console.log("Fetching chats for vendor:", vendorId, "store:", selectedStore); // Fetch chats for this vendor and filter by selected store const response = await authAxios.get(`/chats/vendor/${vendorId}`); console.log("All chats:", response.data); const filteredChats = response.data.filter((chat: Chat) => chat.storeId === selectedStore ); console.log("Filtered chats:", filteredChats); setChats(filteredChats); // Fetch unread counts const unreadResponse = await authAxios.get(`/chats/vendor/${vendorId}/unread`); console.log("Unread counts:", unreadResponse.data); // Check if there are new unread messages and play sound if (!loading && unreadResponse.data.totalUnread > previousTotalUnread) { playNotificationSound(); } // Update states setUnreadCounts(unreadResponse.data); setPreviousTotalUnread(unreadResponse.data.totalUnread); setLoading(false); console.log("Chat loading complete"); } catch (error) { console.error("Error fetching chats:", error); toast.error("Failed to load chats"); } }; // Add polling effect useEffect(() => { if (selectedStore) { fetchChats(); // Poll for updates every 10 seconds const intervalId = setInterval(fetchChats, 10000); return () => clearInterval(intervalId); } }, [selectedStore]); // Handle chat selection const handleChatClick = (chatId: string) => { router.push(`/dashboard/chats/${chatId}`); }; // Handle store change const handleStoreChange = (e: React.ChangeEvent) => { setSelectedStore(e.target.value); }; // Create a new chat const handleCreateChat = () => { router.push("/dashboard/chats/new"); }; if (loading) { return ( Loading chats...
{[1, 2, 3].map((n) => (
))}
); } return ( Customer Chats {vendorStores.length === 0 ? (
No store available. Please create a store first.
) : vendorStores.length === 1 ? (
{vendorStores[0].name}
) : (
)}
{chats.length === 0 ? (

No chats available

There are no customer conversations for this store yet.

) : (
{chats.map((chat) => (
handleChatClick(chat._id)} >
{chat.buyerId.slice(0, 2).toUpperCase()}

Customer {chat.buyerId.slice(-4)}

{formatDistanceToNow(new Date(chat.lastUpdated), { addSuffix: true })}

{unreadCounts.chatCounts[chat._id] > 0 && ( {unreadCounts.chatCounts[chat._id]} unread )}
))}
)}
); }