From 2b40ee668f369b475e5cd2a0bb4aa011314827d6 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Sun, 23 Mar 2025 23:27:49 +0000 Subject: [PATCH] fix --- components/dashboard/BuyerOrderInfo.tsx | 28 +++-- components/dashboard/NewChatForm.tsx | 132 ++++++++---------------- components/dashboard/content.tsx | 31 +----- lib/client-utils.ts | 7 +- 4 files changed, 71 insertions(+), 127 deletions(-) diff --git a/components/dashboard/BuyerOrderInfo.tsx b/components/dashboard/BuyerOrderInfo.tsx index 6bdfeb4..af1c231 100644 --- a/components/dashboard/BuyerOrderInfo.tsx +++ b/components/dashboard/BuyerOrderInfo.tsx @@ -11,7 +11,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { getCookie } from "@/lib/client-utils"; -import { clientFetch } from "@/lib/client-utils"; +import axios from "axios"; import { useRouter } from "next/navigation"; interface Order { @@ -62,13 +62,27 @@ export default function BuyerOrderInfo({ buyerId, chatId }: BuyerOrderInfoProps) setLoading(true); try { - // Use clientFetch instead of direct axios calls - // This ensures the request goes through Next.js API rewrites - const response = await clientFetch(`/chats/${chatId}/orders?limit=10`); + const authToken = getCookie("Authorization"); - if (response && response.orders) { - setOrders(response.orders); - setHasOrders(response.orders.length > 0); + if (!authToken) { + isFetchingRef.current = false; + setLoading(false); + return; + } + + const authAxios = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + headers: { + Authorization: `Bearer ${authToken}` + } + }); + + // Use the new endpoint that works with sub-users + const response = await authAxios.get(`/chats/${chatId}/orders?limit=10`); // Limit to fewer orders for faster response + + if (response.data && response.data.orders) { + setOrders(response.data.orders); + setHasOrders(response.data.orders.length > 0); } else { setHasOrders(false); } diff --git a/components/dashboard/NewChatForm.tsx b/components/dashboard/NewChatForm.tsx index ec9ad03..d8fdb83 100644 --- a/components/dashboard/NewChatForm.tsx +++ b/components/dashboard/NewChatForm.tsx @@ -12,7 +12,6 @@ import axios from "axios"; import { toast } from "sonner"; import { getCookie } from "@/lib/client-utils"; import debounce from "lodash/debounce"; -import { clientFetch } from "@/lib/client-utils"; interface User { telegramUserId: string; @@ -33,7 +32,6 @@ export default function NewChatForm() { const [vendorStores, setVendorStores] = useState<{ _id: string, name: string }[]>([]); const [selectedStore, setSelectedStore] = useState(""); const [selectedUser, setSelectedUser] = useState(null); - const [isSubmitting, setIsSubmitting] = useState(false); // Create an axios instance with auth const getAuthAxios = () => { @@ -83,47 +81,24 @@ export default function NewChatForm() { } }; - // Check if a chat already exists with this user - const checkExistingChat = async (userId: string) => { - try { - // Use clientFetch instead of direct axios calls - const response = await clientFetch(`/chats/user/${userId}`); - - // If a chat is found, redirect to it - if (response && Array.isArray(response) && response.length > 0) { - toast.info("Chat already exists, redirecting..."); - router.push(`/dashboard/chats/${response[0]._id}`); - return true; - } - return false; - } catch (error) { - console.error("Error checking existing chat:", error); - return false; - } - }; + // Debounced search function + const searchUsers = debounce(async (query: string) => { + if (!query.trim() || !vendorStores[0]?._id) return; + + const authAxios = getAuthAxios(); + if (!authAxios) return; - // Search for users by Telegram username or ID - const searchUsers = async (query: string) => { - setSearching(true); - try { - if (!query || query.length < 2) { - setSearchResults([]); - return; - } - - // Use clientFetch instead of direct axios calls - const response = await clientFetch(`/chats/search/users?query=${encodeURIComponent(query)}&storeId=${vendorStores[0]._id}`); - - setSearchResults(response || []); + setSearching(true); + const response = await authAxios.get(`/chats/search/users?query=${encodeURIComponent(query)}&storeId=${vendorStores[0]._id}`); + setSearchResults(response.data); } catch (error) { console.error("Error searching users:", error); - toast.error("Failed to search for users"); - setSearchResults([]); + toast.error("Failed to search users"); } finally { setSearching(false); } - }; + }, 300); // Handle search input change const handleSearchChange = (value: string) => { @@ -201,71 +176,54 @@ export default function NewChatForm() { router.push("/dashboard/chats"); }; - // Create a new chat with the selected user - const createChat = async (e?: React.FormEvent) => { - if (e) e.preventDefault(); + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); - if (!selectedUser) { - toast.error("Please select a user first"); + if (!buyerId) { + toast.error("Please select a customer"); return; } - setIsSubmitting(true); + if (vendorStores.length === 0) { + toast.error("No store available. Please create a store first."); + return; + } + const storeId = vendorStores[0]._id; + + setLoading(true); try { - // Check for existing chat first - const chatExists = await checkExistingChat(selectedUser.telegramUserId); - if (chatExists) { - setIsSubmitting(false); + const authAxios = getAuthAxios(); + if (!authAxios) { + toast.error("You need to be logged in"); + router.push("/auth/login"); return; } - // Get current vendor info - // Use clientFetch instead of direct axios calls - const vendorResponse = await clientFetch('/auth/me'); - const vendorId = vendorResponse.vendor?._id; - - if (!vendorId) { - toast.error("Vendor ID not found"); - return; - } - - // Get store ID from the current vendor - // Use clientFetch instead of direct axios calls - const storeResponse = await clientFetch(`/storefront`); - const storeId = storeResponse?.store?._id; - - if (!storeId) { - toast.error("Store ID not found"); - return; - } - - // Create chat data object - const chatData = { - buyerId: selectedUser.telegramUserId, - vendorId, - storeId, - initialMessage: initialMessage.trim() || "Hello! How can I help you today?", - }; - - // Create the chat - // Use clientFetch instead of direct axios calls - const response = await clientFetch('/chats', { - method: 'POST', - body: JSON.stringify(chatData), + const response = await authAxios.post("/chats/create", { + buyerId, + storeId: storeId, + initialMessage: initialMessage.trim() || undefined }); - if (response && response._id) { - toast.success("Chat created successfully"); - router.push(`/dashboard/chats/${response._id}`); + if (response.data.chatId) { + toast.success("Chat created successfully!"); + router.push(`/dashboard/chats/${response.data.chatId}`); + } else if (response.data.error === "Chat already exists") { + toast.info("Chat already exists, redirecting..."); + router.push(`/dashboard/chats/${response.data.chatId}`); + } + } catch (error: any) { + console.error("Error creating chat:", error); + + if (error.response?.status === 409) { + toast.info("Chat already exists, redirecting..."); + router.push(`/dashboard/chats/${error.response.data.chatId}`); } else { toast.error("Failed to create chat"); } - } catch (error) { - console.error("Error creating chat:", error); - toast.error("Failed to create chat"); } finally { - setIsSubmitting(false); + setLoading(false); } }; @@ -284,7 +242,7 @@ export default function NewChatForm() { -
createChat(e)} className="space-y-6"> +
diff --git a/components/dashboard/content.tsx b/components/dashboard/content.tsx index acbb6cc..7c347bb 100644 --- a/components/dashboard/content.tsx +++ b/components/dashboard/content.tsx @@ -10,6 +10,7 @@ import { ShoppingCart, RefreshCcw } from "lucide-react" import { Button } from "@/components/ui/button" import { useToast } from "@/components/ui/use-toast" import { Skeleton } from "@/components/ui/skeleton" +import { clientFetch } from "@/lib/client-utils" interface ContentProps { username: string @@ -90,35 +91,11 @@ export default function Content({ username, orderStats }: ContentProps) { const [randomQuote, setRandomQuote] = useState(getRandomQuote()) const fetchTopProducts = async () => { - setIsLoading(true) - setError(null) - try { - // Get the auth token from cookies - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; + setIsLoading(true) - if (!authToken) { - throw new Error("Authentication token not found") - } - - // Use the API URL from environment variables - const apiUrl = `${process.env.NEXT_PUBLIC_API_URL}/orders/top-products`; - - const response = await fetch(apiUrl, { - headers: { - Authorization: `Bearer ${authToken}`, - 'Content-Type': 'application/json' - } - }); - - if (!response.ok) { - throw new Error(`API request failed: ${response.status} ${response.statusText}`) - } - - const data = await response.json(); + // Use clientFetch to handle URL routing and authentication properly + const data = await clientFetch('/orders/top-products'); setTopProducts(data); } catch (err) { console.error("Error fetching top products:", err); diff --git a/lib/client-utils.ts b/lib/client-utils.ts index 95d3326..dec31df 100644 --- a/lib/client-utils.ts +++ b/lib/client-utils.ts @@ -16,18 +16,13 @@ export async function clientFetch(url: string, options: RequestInit = {}): Promi // Ensure the url doesn't start with a slash if it's going to be appended to a URL that ends with one const cleanUrl = url.startsWith('/') ? url.substring(1) : url; - - // IMPORTANT: Always use /api as the base URL for client-side requests - // This ensures all requests go through Next.js API rewrite rules - const baseUrl = '/api'; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || '/api'; // Ensure there's only one slash between the base URL and endpoint const fullUrl = baseUrl.endsWith('/') ? `${baseUrl}${cleanUrl}` : `${baseUrl}/${cleanUrl}`; - console.log(`[clientFetch] Requesting: ${fullUrl}`); - const res = await fetch(fullUrl, { ...options, headers,