diff --git a/.gitignore b/.gitignore index 31f6175..d39aba3 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ env.local public/git-info.json */git-info.json public/git-info.json +public/git-info.json diff --git a/components/KeepOnline.ts b/components/KeepOnline.ts index 3770e20..457277e 100644 --- a/components/KeepOnline.ts +++ b/components/KeepOnline.ts @@ -11,7 +11,7 @@ const KeepOnline = () => { clientFetch('/auth/me'); } - updateOnlineStatus(); + // Start interval without immediate call const interval = setInterval(updateOnlineStatus, 1000*60*1); return () => clearInterval(interval); diff --git a/components/dashboard/ChatNotifications.tsx b/components/dashboard/ChatNotifications.tsx deleted file mode 100644 index fd672ef..0000000 --- a/components/dashboard/ChatNotifications.tsx +++ /dev/null @@ -1,212 +0,0 @@ -"use client" - -import React, { useState, useEffect, useRef } from "react"; -import { useRouter } from "next/navigation"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Bell } from "lucide-react"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { clientFetch } from "@/lib/api"; - -interface UnreadCounts { - totalUnread: number; - chatCounts: Record; -} - -export default function ChatNotifications() { - const router = useRouter(); - const [unreadCounts, setUnreadCounts] = useState({ totalUnread: 0, chatCounts: {} }); - const [previousUnreadTotal, setPreviousUnreadTotal] = useState(0); - const [loading, setLoading] = useState(true); - const [chatMetadata, setChatMetadata] = useState>({}); - const audioRef = useRef(null); - - useEffect(() => { - 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 unread counts - useEffect(() => { - const fetchUnreadCounts = async () => { - try { - // Use clientFetch instead of direct API calls to leverage the Next.js API rewrite rules - - // Get vendor info from profile endpoint - const vendorData = await clientFetch('/auth/me'); - - // Access correct property - the vendor ID is in vendor._id - const vendorId = vendorData.vendor?._id; - - if (!vendorId) { - console.error("Vendor ID not found in profile response:", vendorData); - return; - } - - const response = await clientFetch(`/chats/vendor/${vendorId}/unread`); - - // Check if there are new notifications and play sound if needed - if (!loading && response.totalUnread > previousUnreadTotal) { - playNotificationSound(); - } - - // Update state - clientFetch already parses the JSON - setUnreadCounts(response); - setPreviousUnreadTotal(response.totalUnread); - - if (response.totalUnread > 0) { - const chatIds = Object.keys(response.chatCounts); - - if (chatIds.length > 0) { - // Create a simplified metadata object with just needed info - const metadata: Record = {}; - - // Fetch each chat to get buyer IDs - await Promise.all( - chatIds.map(async (chatId) => { - try { - // Use markAsRead=false to ensure we don't mark messages as read - const chatResponse = await clientFetch(`/chats/${chatId}?markAsRead=false`); - metadata[chatId] = { - buyerId: chatResponse.buyerId, - }; - } catch (error) { - console.error(`Error fetching chat ${chatId}:`, error); - } - }) - ); - - setChatMetadata(metadata); - } - } - } catch (error) { - console.error("Error fetching unread counts:", error); - } finally { - setLoading(false); - } - }; - - fetchUnreadCounts(); - - // Set polling interval (every 10 seconds for more responsive notifications) - const intervalId = setInterval(fetchUnreadCounts, 10000); - - return () => clearInterval(intervalId); - }, [loading, previousUnreadTotal]); - - const handleChatClick = (chatId: string) => { - router.push(`/dashboard/chats/${chatId}`); - }; - - return ( - - - - - -
-

Messages

-
- - {unreadCounts.totalUnread === 0 ? ( -
-

No new notifications

-
- ) : ( - <> -
- {Object.entries(unreadCounts.chatCounts).map(([chatId, count]) => ( - handleChatClick(chatId)} - > -
-
-

- Customer {chatMetadata[chatId]?.buyerId.slice(-4) || 'Unknown'} -

-

- {count} new {count === 1 ? 'message' : 'messages'} -

-
- {count} -
-
- ))} -
-
- -
- - )} -
-
- ); -} \ No newline at end of file diff --git a/components/notifications/UnifiedNotifications.tsx b/components/notifications/UnifiedNotifications.tsx index 421b960..7de9ea9 100644 --- a/components/notifications/UnifiedNotifications.tsx +++ b/components/notifications/UnifiedNotifications.tsx @@ -54,6 +54,7 @@ export default function UnifiedNotifications() { const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState("all"); const audioRef = useRef(null); + const vendorIdRef = useRef(null); // Total notifications count const totalNotifications = unreadCounts.totalUnread + newOrders.length; @@ -72,6 +73,34 @@ export default function UnifiedNotifications() { } }; }, []); + + // Get vendor ID from JWT token + const getVendorIdFromToken = () => { + if (vendorIdRef.current) { + return vendorIdRef.current; + } + + 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"); + } + + vendorIdRef.current = vendorId; + return vendorId; + }; // Function to play notification sound const playNotificationSound = () => { @@ -107,8 +136,6 @@ export default function UnifiedNotifications() { yesterday.setDate(yesterday.getDate() - 1); const timestamp = yesterday.toISOString(); - // Use orderDate parameter instead of 'after' to avoid backend casting errors - // The error logs show that the 'after' parameter is being interpreted as 'orderId' incorrectly const orderData = await clientFetch(`/orders?status=paid&limit=10&orderDate[gte]=${timestamp}`); const orders: Order[] = orderData.orders || []; @@ -175,19 +202,8 @@ export default function UnifiedNotifications() { const fetchUnreadCounts = async () => { try { - // Use clientFetch instead of direct API calls to leverage the API rewrite rules - // This will route through Next.js rewrites instead of calling the API directly - - // Get vendor info from profile endpoint - const vendorData = await clientFetch('/auth/me'); - - // Access correct property - the vendor ID is in vendor._id - const vendorId = vendorData.vendor?._id; - - if (!vendorId) { - console.error("Vendor ID not found in profile response:", vendorData); - return; - } + // Get vendor ID from token + const vendorId = getVendorIdFromToken(); // Use clientFetch which will properly route through Next.js API rewrites const response = await clientFetch(`/chats/vendor/${vendorId}/unread`); diff --git a/next.config.mjs b/next.config.mjs index a7304ed..4b093c6 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,7 @@ /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', + reactStrictMode: false, images: { remotePatterns: [ { diff --git a/public/git-info.json b/public/git-info.json index 79b3a27..a245579 100644 --- a/public/git-info.json +++ b/public/git-info.json @@ -1,4 +1,4 @@ { - "commitHash": "f8e3173", - "buildTime": "2025-05-17T21:10:37.604Z" + "commitHash": "02e0900", + "buildTime": "2025-05-19T00:35:36.962Z" } \ No newline at end of file