From fcc1218dd962fd27a488cbac8f64c5a326d074e0 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Fri, 28 Feb 2025 00:56:29 +0000 Subject: [PATCH] woohoo?! --- components/dashboard/content.tsx | 128 +++++++++++++++++++++++++++++++ package-lock.json | 10 +++ package.json | 1 + 3 files changed, 139 insertions(+) diff --git a/components/dashboard/content.tsx b/components/dashboard/content.tsx index ce8db72..41988e5 100644 --- a/components/dashboard/content.tsx +++ b/components/dashboard/content.tsx @@ -5,17 +5,85 @@ import OrderStats from "./order-stats" import { getGreeting } from "@/lib/utils" import { statsConfig } from "@/config/dashboard" import type { OrderStatsData } from "@/lib/types" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { ShoppingCart } from "lucide-react" interface ContentProps { username: string orderStats: OrderStatsData } +interface TopProduct { + id: string; + name: string; + price: number; + image: string; + count: number; + revenue: number; +} + export default function Content({ username, orderStats }: ContentProps) { const [greeting, setGreeting] = useState("") + const [topProducts, setTopProducts] = useState([]) + const [isLoading, setIsLoading] = useState(true) useEffect(() => { setGreeting(getGreeting()) + + // Fetch top products for the vendor + const fetchTopProducts = async () => { + try { + // Check if we're in development or production + const isDev = process.env.NODE_ENV === 'development'; + // Use the internal API URL seen in the console + const apiBaseUrl = 'https://internal-api.inboxi.ng/api'; + + console.log('Using API URL:', apiBaseUrl); + + // Get the auth token from cookies + const cookies = document.cookie.split(';'); + console.log('Cookies:', cookies); + const tokenCookie = cookies.find(cookie => cookie.trim().startsWith('Authorization=')); + let token = ''; + + if (tokenCookie) { + // Extract just the token value after "Authorization=" + token = tokenCookie.trim().substring(14); // 'Authorization='.length is 14 + + // Fix any potential malformed token (seen in your screenshot) + if (token.startsWith('ization=')) { + token = token.substring(9); // Remove the 'ization=' prefix if present + } + } + + console.log('Using token:', token); + + const headers = { + 'Authorization': `Bearer ${token}` + }; + console.log('Request headers:', headers); + + const response = await fetch(`${apiBaseUrl}/orders/top-products`, { + credentials: 'include', + headers + }); + + if (response.ok) { + const data = await response.json(); + setTopProducts(data); + } else { + const errorText = await response.text(); + console.error(`Failed to fetch top products: ${response.status} ${response.statusText}`); + console.error('Error details:', errorText); + } + } catch (error) { + console.error("Error fetching top products:", error); + } finally { + setIsLoading(false); + } + }; + + fetchTopProducts(); }, []) return ( @@ -34,6 +102,66 @@ export default function Content({ username, orderStats }: ContentProps) { /> ))} + + {/* Best Selling Products Section */} +
+ + + Your Best Selling Products + Products with the highest sales from your store + + + {isLoading ? ( +
+ {[...Array(5)].map((_, i) => ( +
+
+
+
+
+
+
+
+ ))} +
+ ) : topProducts.length > 0 ? ( +
+ {topProducts.map(product => ( +
+
+
+ {product.image ? ( + {product.name} + ) : ( + + )} +
+
+

{product.name}

+

+ ${product.price.toLocaleString('en-US', { minimumFractionDigits: 2 })} +

+
+
+
+

{product.count} sold

+

+ ${product.revenue?.toLocaleString('en-US', { minimumFractionDigits: 2 }) || '-'} +

+
+
+ ))} +
+ ) : ( +
+ +

No sales data available yet

+

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

+
+ )} +
+
+
) } diff --git a/package-lock.json b/package-lock.json index 6554696..eb24bfd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "embla-carousel-react": "8.5.1", "form-data": "^4.0.2", "input-otp": "1.4.1", + "jwt-decode": "^4.0.0", "lucide-react": "^0.454.0", "next": "14.2.16", "next-themes": "latest", @@ -3960,6 +3961,15 @@ "dev": true, "license": "MIT" }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", diff --git a/package.json b/package.json index c0bb925..806df64 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "embla-carousel-react": "8.5.1", "form-data": "^4.0.2", "input-otp": "1.4.1", + "jwt-decode": "^4.0.0", "lucide-react": "^0.454.0", "next": "14.2.16", "next-themes": "latest",