From dbd65415d518ca9f2abe1fd3f84996fadb379f17 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Sat, 8 Mar 2025 05:30:23 +0000 Subject: [PATCH] weehoo --- app/dashboard/chats/page.tsx | 4 +- components/dashboard/ChatList.tsx | 75 +++----- components/dashboard/ChatTable.tsx | 271 +++++++++++++++++++++++++++++ 3 files changed, 296 insertions(+), 54 deletions(-) create mode 100644 components/dashboard/ChatTable.tsx diff --git a/app/dashboard/chats/page.tsx b/app/dashboard/chats/page.tsx index b42434e..ed72af9 100644 --- a/app/dashboard/chats/page.tsx +++ b/app/dashboard/chats/page.tsx @@ -2,7 +2,7 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; -import ChatList from "@/components/dashboard/ChatList"; +import ChatTable from "@/components/dashboard/ChatTable"; import Dashboard from "@/components/dashboard/dashboard"; import { MessageCircle } from "lucide-react"; @@ -30,7 +30,7 @@ export default function ChatsPage() { - + ); diff --git a/components/dashboard/ChatList.tsx b/components/dashboard/ChatList.tsx index 8deb307..572e2ec 100644 --- a/components/dashboard/ChatList.tsx +++ b/components/dashboard/ChatList.tsx @@ -25,9 +25,7 @@ import { Plus, MessageCircle, Loader2, - RefreshCw, - ChevronLeft, - ChevronRight + RefreshCw } from "lucide-react"; import axios from "axios"; import { toast } from "sonner"; @@ -177,61 +175,34 @@ export default function ChatList() { // Fetch chats and unread counts when store is selected const fetchChats = async () => { + if (!selectedStore && vendorStores.length > 0) { + setSelectedStore("all"); // Set default "all" if we have stores but none selected + } + + setLoading(true); + 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, + const endpoint = selectedStore && selectedStore !== "all" + ? `/chats?storeId=${selectedStore}` + : "/chats"; + + const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/api${endpoint}`, { headers: { - Authorization: `Bearer ${authToken}` - } + Authorization: getCookie("Authorization") || "", + "Content-Type": "application/json", + }, }); - // Get vendor ID from JWT token - const tokenParts = authToken.split('.'); - const payload = JSON.parse(atob(tokenParts[1])); - const vendorId = payload.id; + setChats(response.data || []); - 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"); + // Fetch unread counts after loading chats + await fetchUnreadCounts(); } catch (error) { - console.error("Error fetching chats:", error); - toast.error("Failed to load chats"); + console.error("Failed to fetch chats:", error); + toast.error("Failed to load chat conversations"); + setChats([]); + } finally { + setLoading(false); } }; diff --git a/components/dashboard/ChatTable.tsx b/components/dashboard/ChatTable.tsx new file mode 100644 index 0000000..9ae05a4 --- /dev/null +++ b/components/dashboard/ChatTable.tsx @@ -0,0 +1,271 @@ +"use client" + +import { useState, useEffect, useRef } from "react"; +import { useRouter } from "next/navigation"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +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 { + Plus, + MessageCircle, + Loader2, + RefreshCw +} from "lucide-react"; +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 ChatTable() { + const router = useRouter(); + const [chats, setChats] = useState([]); + const [loading, setLoading] = useState(true); + const [unreadCounts, setUnreadCounts] = useState({ totalUnread: 0, chatCounts: {} }); + const audioRef = useRef(null); + + // Initialize audio element for notifications + useEffect(() => { + audioRef.current = new Audio('/notification.mp3'); + return () => { + if (audioRef.current) { + audioRef.current = null; + } + }; + }, []); + + // Play notification sound + const playNotificationSound = () => { + if (audioRef.current) { + audioRef.current.play().catch(e => { + console.log("Failed to play notification sound:", e); + }); + } + }; + + // Get vendor ID from JWT token + const getVendorIdFromToken = () => { + const authToken = getCookie("Authorization") || ""; + + if (!authToken) { + throw new Error("No auth token found"); + } + + const tokenParts = authToken.split("."); + if (tokenParts.length !== 3) { + throw new Error("Invalid token format"); + } + + const payload = JSON.parse(atob(tokenParts[1])); + const vendorId = payload.id; + + if (!vendorId) { + throw new Error("Vendor ID not found in token"); + } + + return { vendorId, authToken }; + }; + + // Fetch chats when component mounts + useEffect(() => { + fetchChats(); + + // Set up polling for unread messages + const interval = setInterval(() => { + fetchUnreadCounts(); + }, 30000); // Check every 30 seconds + + return () => clearInterval(interval); + }, []); + + // Fetch unread counts + const fetchUnreadCounts = async () => { + try { + // Get the vendor ID from the auth token + const { vendorId, authToken } = getVendorIdFromToken(); + + // Fetch unread counts for this vendor + const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/chats/vendor/${vendorId}/unread`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + + const newUnreadCounts = response.data; + + // Play sound if there are new messages + if (newUnreadCounts.totalUnread > unreadCounts.totalUnread) { + playNotificationSound(); + } + + setUnreadCounts(newUnreadCounts); + } catch (error) { + console.error("Failed to fetch unread counts:", error); + } + }; + + // Fetch chats + const fetchChats = async () => { + setLoading(true); + + try { + // Get the vendor ID from the auth token + const { vendorId, authToken } = getVendorIdFromToken(); + + // Now fetch chats for this vendor + const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/chats/vendor/${vendorId}`, { + headers: { + Authorization: `Bearer ${authToken}`, + "Content-Type": "application/json", + }, + }); + + setChats(Array.isArray(response.data) ? response.data : []); + await fetchUnreadCounts(); + } catch (error) { + console.error("Failed to fetch chats:", error); + toast.error("Failed to load chat conversations"); + setChats([]); + } finally { + setLoading(false); + } + }; + + // Navigate to chat detail page + const handleChatClick = (chatId: string) => { + router.push(`/dashboard/chats/${chatId}`); + }; + + // Create new chat + const handleCreateChat = () => { + router.push("/dashboard/chats/new"); + }; + + return ( + + + + {loading ? ( + + ) : ( + + )} + Refresh + + + + + New Chat + + + + + + + + Customer + Last Activity + Status + Actions + + + + {loading ? ( + + + + + + ) : chats.length === 0 ? ( + + + + + No chats found + + + + ) : ( + chats.map((chat) => ( + handleChatClick(chat._id)} + > + + + + + {chat.buyerId?.slice(0, 2).toUpperCase() || 'CU'} + + + + Customer {chat.buyerId.slice(0, 4)} + {chat.orderId && ( + + Order #{chat.orderId} + + )} + + + + + {formatDistanceToNow(new Date(chat.lastUpdated), { addSuffix: true })} + + + {unreadCounts.chatCounts[chat._id] > 0 ? ( + + {unreadCounts.chatCounts[chat._id]} new + + ) : ( + Read + )} + + + { + e.stopPropagation(); + handleChatClick(chat._id); + }} + > + View + + + + )) + )} + + + + + ); +} \ No newline at end of file
No chats found