From 92a27192ee51013f7fa5367d50adcf279153ba00 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Mon, 24 Mar 2025 01:08:18 +0000 Subject: [PATCH] weewoo --- components/dashboard/NewChatForm.tsx | 219 +++++++++++---------------- components/dashboard/content.tsx | 182 ++++++++-------------- config/quotes.ts | 87 +++++++++++ 3 files changed, 242 insertions(+), 246 deletions(-) create mode 100644 config/quotes.ts diff --git a/components/dashboard/NewChatForm.tsx b/components/dashboard/NewChatForm.tsx index d8fdb83..f2f4fbf 100644 --- a/components/dashboard/NewChatForm.tsx +++ b/components/dashboard/NewChatForm.tsx @@ -8,9 +8,8 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { ArrowLeft, Send, RefreshCw, Search, User } from "lucide-react"; -import axios from "axios"; import { toast } from "sonner"; -import { getCookie } from "@/lib/client-utils"; +import { clientFetch } from "@/lib/client-utils"; import debounce from "lodash/debounce"; interface User { @@ -18,60 +17,63 @@ interface User { telegramUsername: string | null; } +interface Store { + _id: string; + name: string; +} + export default function NewChatForm() { const router = useRouter(); const searchParams = useSearchParams(); + + // State management const [buyerId, setBuyerId] = useState(""); const [searchQuery, setSearchQuery] = useState(""); const [searchResults, setSearchResults] = useState([]); - const [searching, setSearching] = useState(false); - const [open, setOpen] = useState(false); const [initialMessage, setInitialMessage] = useState(""); - const [loading, setLoading] = useState(false); - const [loadingUser, setLoadingUser] = useState(false); - const [vendorStores, setVendorStores] = useState<{ _id: string, name: string }[]>([]); + const [vendorStores, setVendorStores] = useState([]); const [selectedStore, setSelectedStore] = useState(""); const [selectedUser, setSelectedUser] = useState(null); - // Create an axios instance with auth - const getAuthAxios = () => { - const authToken = getCookie("Authorization"); - if (!authToken) return null; - - return axios.create({ - baseURL: process.env.NEXT_PUBLIC_API_URL, - headers: { Authorization: `Bearer ${authToken}` } - }); - }; + // Loading states + const [searching, setSearching] = useState(false); + const [loading, setLoading] = useState(false); + const [loadingUser, setLoadingUser] = useState(false); + const [loadingStores, setLoadingStores] = useState(false); + + // UI state + const [open, setOpen] = useState(false); // Parse URL parameters for buyerId and fetch user details if present useEffect(() => { const buyerIdParam = searchParams.get('buyerId'); if (buyerIdParam) { setBuyerId(buyerIdParam); - // We'll fetch user details after stores are loaded } }, [searchParams]); + // Fetch vendor stores on component mount + useEffect(() => { + fetchVendorStores(); + }, []); + + // Fetch user information if buyer ID changes + useEffect(() => { + if (buyerId && vendorStores.length > 0) { + fetchUserById(buyerId); + } + }, [buyerId, vendorStores]); + // Fetch user information by ID const fetchUserById = async (userId: string) => { if (!userId || !vendorStores[0]?._id) return; - const authAxios = getAuthAxios(); - if (!authAxios) { - toast.error("You need to be logged in"); - router.push("/auth/login"); - return; - } - setLoadingUser(true); try { - const response = await authAxios.get(`/chats/user/${userId}`); - if (response.data) { - setSelectedUser(response.data); - setSearchQuery(response.data.telegramUsername || `User ${userId}`); - } else { - // Just leave the buyerId as is without username display + const userData = await clientFetch(`/chats/user/${userId}`); + if (userData) { + setSelectedUser(userData); + setSearchQuery(userData.telegramUsername || `User ${userId}`); } } catch (error) { console.error("Error fetching user:", error); @@ -81,17 +83,47 @@ export default function NewChatForm() { } }; + // Fetch vendor stores + const fetchVendorStores = async () => { + setLoadingStores(true); + try { + // Get stores + const stores = await clientFetch('/storefront'); + + // Handle both array and single object responses + if (Array.isArray(stores)) { + setVendorStores(stores); + if (stores.length > 0) { + setSelectedStore(stores[0]._id); + } + } else if (stores && typeof stores === 'object' && stores._id) { + const singleStore = [stores]; + setVendorStores(singleStore); + setSelectedStore(stores._id); + } + } catch (error) { + console.error("Error fetching stores:", error); + toast.error("Failed to load your stores"); + + // Redirect if there's a login issue + if (error instanceof Error && error.message.includes('logged in')) { + router.push("/auth/login"); + } + } finally { + setLoadingStores(false); + } + }; + // Debounced search function const searchUsers = debounce(async (query: string) => { - if (!query.trim() || !vendorStores[0]?._id) return; - - const authAxios = getAuthAxios(); - if (!authAxios) return; + if (!query.trim() || !selectedStore) return; + setSearching(true); try { - setSearching(true); - const response = await authAxios.get(`/chats/search/users?query=${encodeURIComponent(query)}&storeId=${vendorStores[0]._id}`); - setSearchResults(response.data); + const results = await clientFetch( + `/chats/search/users?query=${encodeURIComponent(query)}&storeId=${selectedStore}` + ); + setSearchResults(results); } catch (error) { console.error("Error searching users:", error); toast.error("Failed to search users"); @@ -115,113 +147,40 @@ export default function NewChatForm() { setOpen(false); }; - // Fetch vendor stores - useEffect(() => { - const fetchVendorStores = async () => { - const authAxios = getAuthAxios(); - if (!authAxios) { - toast.error("You must be logged in to start a chat"); - router.push("/auth/login"); - return; - } - - try { - // Get vendor profile first - const vendorResponse = await authAxios.get('/auth/me'); - - // Extract vendor ID properly - 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 store - const storeResponse = await authAxios.get(`/storefront`); - - // Handle both array and single object responses - if (Array.isArray(storeResponse.data)) { - setVendorStores(storeResponse.data); - if (storeResponse.data.length > 0) { - setSelectedStore(storeResponse.data[0]._id); - } - } else if (storeResponse.data && typeof storeResponse.data === 'object' && storeResponse.data._id) { - 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"); - } - - // Now that we have the store, fetch user details if buyerId was set - const buyerIdParam = searchParams.get('buyerId'); - if (buyerIdParam) { - fetchUserById(buyerIdParam); - } - } catch (error) { - console.error("Error fetching store:", error); - toast.error("Failed to load store"); - setVendorStores([]); - } - }; - - fetchVendorStores(); - }, []); - + // Navigation handlers const handleBackClick = () => { router.push("/dashboard/chats"); }; + // Start new chat const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (!buyerId) { - toast.error("Please select a customer"); + if (!buyerId || !initialMessage.trim() || !selectedStore) { + toast.error("Please fill in all required fields"); return; } - if (vendorStores.length === 0) { - toast.error("No store available. Please create a store first."); - return; - } - - const storeId = vendorStores[0]._id; - setLoading(true); try { - const authAxios = getAuthAxios(); - if (!authAxios) { - toast.error("You need to be logged in"); - router.push("/auth/login"); - return; - } - - const response = await authAxios.post("/chats/create", { - buyerId, - storeId: storeId, - initialMessage: initialMessage.trim() || undefined + const response = await clientFetch('/chats', { + method: 'POST', + body: JSON.stringify({ + buyerId, + storeId: selectedStore, + initialMessage: initialMessage.trim() + }), + headers: { + 'Content-Type': 'application/json' + } }); - 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) { + // Navigate to the new chat + toast.success("Chat created successfully"); + router.push(`/dashboard/chats/${response._id}`); + } catch (error) { 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"); - } + toast.error("Failed to create chat. Please try again."); } finally { setLoading(false); } diff --git a/components/dashboard/content.tsx b/components/dashboard/content.tsx index 448a99e..4c2bac8 100644 --- a/components/dashboard/content.tsx +++ b/components/dashboard/content.tsx @@ -4,6 +4,7 @@ import { useState, useEffect } from "react" import OrderStats from "./order-stats" import { getGreeting } from "@/lib/utils" import { statsConfig } from "@/config/dashboard" +import { getRandomQuote } from "@/config/quotes" import type { OrderStatsData } from "@/lib/types" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" import { ShoppingCart, RefreshCcw } from "lucide-react" @@ -26,96 +27,46 @@ interface TopProduct { revenue: number; } -// Business quotes array -const businessQuotes = [ - { text: "Your most unhappy customers are your greatest source of learning.", author: "Bill Gates" }, - { text: "The secret of getting ahead is getting started.", author: "Mark Twain" }, - { text: "Success is not final; failure is not fatal: It is the courage to continue that counts.", author: "Winston Churchill" }, - { text: "The way to get started is to quit talking and begin doing.", author: "Walt Disney" }, - { text: "Opportunities don't happen. You create them.", author: "Chris Grosser" }, - { text: "The best way to predict the future is to create it.", author: "Peter Drucker" }, - { text: "Don't watch the clock; do what it does. Keep going.", author: "Sam Levenson" }, - { text: "The future belongs to those who believe in the beauty of their dreams.", author: "Eleanor Roosevelt" }, - { text: "Entrepreneurship is living a few years of your life like most people won't so you can spend the rest of your life like most people can't.", author: "Anonymous" }, - { text: "Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work.", author: "Steve Jobs" }, - - // Additional quotes - { text: "If you are not willing to risk the usual, you will have to settle for the ordinary.", author: "Jim Rohn" }, - { text: "The only limit to our realization of tomorrow will be our doubts of today.", author: "Franklin D. Roosevelt" }, - { text: "What would you do if you weren't afraid?", author: "Sheryl Sandberg" }, - { text: "The most valuable businesses of coming decades will be built by entrepreneurs who seek to empower people rather than try to make them obsolete.", author: "Peter Thiel" }, - { text: "If you really look closely, most overnight successes took a long time.", author: "Steve Jobs" }, - { text: "Twenty years from now, you will be more disappointed by the things that you didn't do than by the ones you did do.", author: "Mark Twain" }, - { text: "The biggest risk is not taking any risk. In a world that's changing quickly, the only strategy that is guaranteed to fail is not taking risks.", author: "Mark Zuckerberg" }, - { text: "I have not failed. I've just found 10,000 ways that won't work.", author: "Thomas Edison" }, - { text: "Chase the vision, not the money; the money will end up following you.", author: "Tony Hsieh" }, - { text: "It's not about ideas. It's about making ideas happen.", author: "Scott Belsky" }, - { text: "If you can't fly, then run. If you can't run, then walk. If you can't walk, then crawl. But whatever you do, you have to keep moving forward.", author: "Martin Luther King Jr." }, - { text: "The only place where success comes before work is in the dictionary.", author: "Vidal Sassoon" }, - { text: "Make every detail perfect and limit the number of details to perfect.", author: "Jack Dorsey" }, - { text: "If you're not embarrassed by the first version of your product, you've launched too late.", author: "Reid Hoffman" }, - { text: "Your reputation is more important than your paycheck, and your integrity is worth more than your career.", author: "Ryan Freitas" }, - { text: "Every time you state what you want or believe, you're the first to hear it. It's a message to both you and others about what you think is possible.", author: "Oprah Winfrey" }, - { text: "The question isn't who is going to let me; it's who is going to stop me.", author: "Ayn Rand" }, - { text: "Innovation distinguishes between a leader and a follower.", author: "Steve Jobs" }, - { text: "There's no shortage of remarkable ideas, what's missing is the will to execute them.", author: "Seth Godin" }, - { text: "You don't need to have a 100-person company to develop that idea.", author: "Larry Page" }, - { text: "When everything seems to be going against you, remember that the airplane takes off against the wind, not with it.", author: "Henry Ford" }, - { text: "Don't be afraid to give up the good to go for the great.", author: "John D. Rockefeller" }, - { text: "Always deliver more than expected.", author: "Larry Page" }, - { text: "Risk more than others think is safe. Dream more than others think is practical.", author: "Howard Schultz" }, - { text: "The battles that count aren't the ones for gold medals. The struggles within yourself—the invisible, inevitable battles inside all of us—that's where it's at.", author: "Jesse Owens" }, - { text: "If you do build a great experience, customers tell each other about that. Word of mouth is very powerful.", author: "Jeff Bezos" }, - { text: "No matter how brilliant your mind or strategy, if you're playing a solo game, you'll always lose out to a team.", author: "Reid Hoffman" }, - { text: "If you want to achieve greatness stop asking for permission.", author: "Anonymous" }, - { text: "Things work out best for those who make the best of how things work out.", author: "John Wooden" }, - { text: "If you are not embarrassed by the first version of your product, you've launched too late.", author: "Reid Hoffman" } -]; - -// Function to get a random quote that's not the Mark Twain one -function getRandomQuote() { - // Filter out the Mark Twain quote for now to ensure variety - const filteredQuotes = businessQuotes.filter(quote => quote.author !== "Mark Twain"); - const randomIndex = Math.floor(Math.random() * filteredQuotes.length); - return filteredQuotes[randomIndex]; -} - export default function Content({ username, orderStats }: ContentProps) { - const [greeting, setGreeting] = useState("") - const [topProducts, setTopProducts] = useState([]) - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState(null) - const { toast } = useToast() + const [greeting, setGreeting] = useState(""); + const [topProducts, setTopProducts] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const { toast } = useToast(); - // Initialize with a random quote that's not Mark Twain - const [randomQuote, setRandomQuote] = useState(getRandomQuote()) + // Initialize with a random quote from the quotes config + const [randomQuote, setRandomQuote] = useState(getRandomQuote()); + // Fetch top-selling products data const fetchTopProducts = async () => { try { - setIsLoading(true) + setIsLoading(true); - // 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); - setError(err instanceof Error ? err.message : "Failed to fetch top products") + setError(err instanceof Error ? err.message : "Failed to fetch top products"); toast({ title: "Error loading top products", description: "Please try refreshing the page", variant: "destructive" - }) + }); } finally { - setIsLoading(false) + setIsLoading(false); } }; + // Initialize greeting and fetch data on component mount useEffect(() => { - setGreeting(getGreeting()) - - // Fetch top products + setGreeting(getGreeting()); fetchTopProducts(); - }, []) + }, []); + + // Retry fetching top products data + const handleRetry = () => { + fetchTopProducts(); + }; return (
@@ -128,6 +79,7 @@ export default function Content({ username, orderStats }: ContentProps) {

+ {/* Order Statistics */}
{statsConfig.map((stat) => ( - Retry + Retry )} + {isLoading ? ( + // Loading skeleton
{[...Array(5)].map((_, i) => (
-
-
-
-
+ +
+ + +
+
+ +
-
))}
) : error ? ( -
-

Error loading products

-

{error}

+ // Error state +
+
Failed to load products
- ) : topProducts.length > 0 ? ( + ) : topProducts.length === 0 ? ( + // Empty state +
+ +

No products sold yet

+

+ Your best-selling products will appear here after you make some sales. +

+
+ ) : ( + // Data view
- {topProducts.map(product => ( -
-
-
- {product.image ? ( - {product.name} { - const target = e.currentTarget; - target.src = ""; - if (target.parentElement) { - target.parentElement.classList.add("bg-muted"); - const iconSpan = document.createElement("span"); - iconSpan.className = "h-5 w-5 text-muted-foreground"; - target.parentElement.appendChild(iconSpan); - } - }} - /> - ) : ( - - )} -
-
-

{product.name}

-
+ {topProducts.map((product) => ( +
+
+ {!product.image && ( + + )} +
+
+

{product.name}

-

{product.count} sold

+
{product.count} sold
))}
- ) : ( -
- -

No sales data available yet

-

Your best-selling products will appear here once you have orders

-
)}
- ) + ); } diff --git a/config/quotes.ts b/config/quotes.ts new file mode 100644 index 0000000..04c2411 --- /dev/null +++ b/config/quotes.ts @@ -0,0 +1,87 @@ +/** + * Business motivation quotes for the dashboard + * Collection of quotes from successful entrepreneurs and business leaders + */ + +export interface Quote { + text: string; + author: string; +} + +export const businessQuotes: Quote[] = [ + // Steve Jobs quotes + { text: "Your work is going to fill a large part of your life, and the only way to be truly satisfied is to do what you believe is great work.", author: "Steve Jobs" }, + { text: "Innovation distinguishes between a leader and a follower.", author: "Steve Jobs" }, + { text: "If you really look closely, most overnight successes took a long time.", author: "Steve Jobs" }, + + // Entrepreneurs and CEOs + { text: "Your most unhappy customers are your greatest source of learning.", author: "Bill Gates" }, + { text: "The way to get started is to quit talking and begin doing.", author: "Walt Disney" }, + { text: "Opportunities don't happen. You create them.", author: "Chris Grosser" }, + { text: "The best way to predict the future is to create it.", author: "Peter Drucker" }, + { text: "If you are not willing to risk the usual, you will have to settle for the ordinary.", author: "Jim Rohn" }, + { text: "Chase the vision, not the money; the money will end up following you.", author: "Tony Hsieh" }, + { text: "It's not about ideas. It's about making ideas happen.", author: "Scott Belsky" }, + { text: "If you do build a great experience, customers tell each other about that. Word of mouth is very powerful.", author: "Jeff Bezos" }, + + // Persistence and growth + { text: "The secret of getting ahead is getting started.", author: "Mark Twain" }, + { text: "Success is not final; failure is not fatal: It is the courage to continue that counts.", author: "Winston Churchill" }, + { text: "Don't watch the clock; do what it does. Keep going.", author: "Sam Levenson" }, + { text: "The future belongs to those who believe in the beauty of their dreams.", author: "Eleanor Roosevelt" }, + { text: "If you can't fly, then run. If you can't run, then walk. If you can't walk, then crawl. But whatever you do, you have to keep moving forward.", author: "Martin Luther King Jr." }, + + // Risk and innovation + { text: "The biggest risk is not taking any risk. In a world that's changing quickly, the only strategy that is guaranteed to fail is not taking risks.", author: "Mark Zuckerberg" }, + { text: "I have not failed. I've just found 10,000 ways that won't work.", author: "Thomas Edison" }, + { text: "What would you do if you weren't afraid?", author: "Sheryl Sandberg" }, + { text: "When everything seems to be going against you, remember that the airplane takes off against the wind, not with it.", author: "Henry Ford" }, + { text: "If you're not embarrassed by the first version of your product, you've launched too late.", author: "Reid Hoffman" }, + + // Quality and execution + { text: "The only place where success comes before work is in the dictionary.", author: "Vidal Sassoon" }, + { text: "Make every detail perfect and limit the number of details to perfect.", author: "Jack Dorsey" }, + { text: "There's no shortage of remarkable ideas, what's missing is the will to execute them.", author: "Seth Godin" }, + { text: "Always deliver more than expected.", author: "Larry Page" }, + { text: "Your reputation is more important than your paycheck, and your integrity is worth more than your career.", author: "Ryan Freitas" }, + + // Teamwork and determination + { text: "No matter how brilliant your mind or strategy, if you're playing a solo game, you'll always lose out to a team.", author: "Reid Hoffman" }, + { text: "If you want to achieve greatness stop asking for permission.", author: "Anonymous" }, + { text: "Things work out best for those who make the best of how things work out.", author: "John Wooden" }, + { text: "The most valuable businesses of coming decades will be built by entrepreneurs who seek to empower people rather than try to make them obsolete.", author: "Peter Thiel" }, +]; + +/** + * Returns a random business quote from the collection + */ +export function getRandomQuote(): Quote { + const randomIndex = Math.floor(Math.random() * businessQuotes.length); + return businessQuotes[randomIndex]; +} + +/** + * Returns a random quote by a specific author if available, + * otherwise returns a random quote from any author + */ +export function getRandomQuoteByAuthor(author: string): Quote { + const authorQuotes = businessQuotes.filter(quote => + quote.author.toLowerCase() === author.toLowerCase() + ); + + if (authorQuotes.length === 0) { + return getRandomQuote(); + } + + const randomIndex = Math.floor(Math.random() * authorQuotes.length); + return authorQuotes[randomIndex]; +} + +/** + * Returns quotes filtered by a theme or keyword in the text + */ +export function getQuotesByTheme(keyword: string): Quote[] { + return businessQuotes.filter(quote => + quote.text.toLowerCase().includes(keyword.toLowerCase()) + ); +} \ No newline at end of file