diff --git a/app/page.tsx b/app/page.tsx index 4a9f506..ec2b261 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,77 +1,43 @@ import { getPlatformStatsServer } from "@/lib/server-api"; import { HomeNavbar } from "@/components/home-navbar"; -import { Suspense } from "react"; -import { Shield, LineChart, Zap, ArrowRight, CheckCircle2, Sparkles } from "lucide-react"; +import { Shield, LineChart, Zap, ArrowRight, Sparkles } from "lucide-react"; import { Button } from "@/components/ui/button"; import Link from "next/link"; import { AnimatedStatsSection } from "@/components/animated-stats-section"; -import { isDecember } from "@/lib/utils/christmas"; import { MotionWrapper } from "@/components/ui/motion-wrapper"; export const dynamic = 'force-dynamic'; -const PY_20 = 20; -const PY_32 = 32; -const PX_6 = 6; -const PX_10 = 10; - -function formatNumberValue(num: number): string { - return new Intl.NumberFormat().format(Math.round(num)); -} - -// Format currency -function formatCurrencyValue(amount: number): string { - return new Intl.NumberFormat('en-GB', { - style: 'currency', - currency: 'GBP', - maximumFractionDigits: 0 - }).format(amount); -} - -// This is a server component export default async function Home() { try { const stats = await getPlatformStatsServer(); - const isDec = isDecember(); return ( -
-
+
+
{/* Hero Section */}
-
- - +
+ + Secure Crypto Payments

- The Future of E-commerce Management + The Future of E-commerce Management

- {isDec - ? 'Spread joy this holiday season with our all-in-one platform. Secure payments, order tracking, and analytics wrapped up in one beautiful package. 🎄' - : 'Streamline your online business with our all-in-one platform. Secure payments, order tracking, and analytics in one place.' - } + Streamline your online business with our all-in-one platform. Secure payments, order tracking, and analytics in one place.

-
- )} +
+ )}
); } diff --git a/components/dashboard/quick-actions.tsx b/components/dashboard/quick-actions.tsx index f09dbe4..a4db859 100644 --- a/components/dashboard/quick-actions.tsx +++ b/components/dashboard/quick-actions.tsx @@ -1,26 +1,33 @@ "use client" +import { useState, useEffect, ChangeEvent } from "react" import Link from "next/link" import { motion } from "framer-motion" import { PlusCircle, - Package, - BarChart3, - Settings, - MessageSquare, Truck, - Tag, - Users + BarChart3, + MessageSquare, } from "lucide-react" import { Card, CardContent } from "@/components/ui/card" +import dynamic from "next/dynamic" +import { Product } from "@/models/products" +import { Category } from "@/models/categories" +import { clientFetch } from "@/lib/api" +import { toast } from "sonner" + +const ProductModal = dynamic(() => import("@/components/modals/product-modal").then(mod => ({ default: mod.ProductModal })), { + loading: () => null +}); const actions = [ { title: "Add Product", icon: PlusCircle, - href: "/dashboard/products/new", + href: "/dashboard/products/new", // Fallback text color: "bg-blue-500/10 text-blue-500", - description: "Create a new listing" + description: "Create a new listing", + action: "modal" }, { title: "Process Orders", @@ -46,19 +53,118 @@ const actions = [ ] export default function QuickActions() { + const [modalOpen, setModalOpen] = useState(false); + const [loading, setLoading] = useState(false); + const [categories, setCategories] = useState([]); + const [productData, setProductData] = useState({ + name: "", + description: "", + unitType: "pcs", + category: "", + pricing: [{ minQuantity: 1, pricePerUnit: 0 }], + image: null, + costPerUnit: 0, + }); + + // Fetch categories on mount + useEffect(() => { + const fetchCategories = async () => { + try { + const data = await clientFetch('/categories'); + setCategories(data); + } catch (error) { + console.error("Failed to fetch categories:", error); + } + }; + fetchCategories(); + }, []); + + const handleChange = (e: ChangeEvent) => { + setProductData({ ...productData, [e.target.name]: e.target.value }); + }; + + const handleTieredPricingChange = (e: ChangeEvent, index: number) => { + const updatedPricing = [...productData.pricing]; + const name = e.target.name as "minQuantity" | "pricePerUnit"; + updatedPricing[index][name] = e.target.valueAsNumber || 0; + setProductData({ ...productData, pricing: updatedPricing }); + }; + + const handleAddTier = () => { + setProductData((prev) => ({ + ...prev, + pricing: [...prev.pricing, { minQuantity: 1, pricePerUnit: 0 }], + })); + }; + + const handleRemoveTier = (index: number) => { + setProductData((prev) => ({ + ...prev, + pricing: prev.pricing.filter((_, i) => i !== index), + })); + }; + + const handleSaveProduct = async (data: Product, file?: File | null) => { + try { + setLoading(true); + + // Prepare the product data + const payload = { + ...data, + stockTracking: data.stockTracking ?? true, + currentStock: data.currentStock ?? 0, + lowStockThreshold: data.lowStockThreshold ?? 10, + stockStatus: data.stockStatus ?? 'out_of_stock' + }; + + const productResponse = await clientFetch("/products", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + }); + + if (file) { + const formData = new FormData(); + formData.append("file", file); + await fetch(`${process.env.NEXT_PUBLIC_API_URL}/products/${productResponse._id}/image`, { + method: "PUT", + headers: { + Authorization: `Bearer ${document.cookie.split("; ").find((row) => row.startsWith("Authorization="))?.split("=")[1]}`, + }, + body: formData, + }); + } + + setModalOpen(false); + setProductData({ + name: "", + description: "", + unitType: "pcs", + category: "", + pricing: [{ minQuantity: 1, pricePerUnit: 0 }], + image: null, + costPerUnit: 0, + }); + toast.success("Product added successfully"); + + // Optional: trigger a refresh of products or stats if needed + // currently just closing modal + } catch (error) { + console.error(error); + toast.error("Failed to save product"); + } finally { + setLoading(false); + } + }; + return ( -
- {actions.map((action, index) => ( - - - + <> +
+ {actions.map((action, index) => { + const isModalAction = action.action === "modal"; + + const CardContentWrapper = () => ( +
@@ -67,9 +173,44 @@ export default function QuickActions() {

{action.description}

- - - ))} -
+ ); + + return ( + + {isModalAction ? ( +
setModalOpen(true)}> + +
+ ) : ( + + + + )} +
+ ); + })} +
+ + setModalOpen(false)} + onSave={handleSaveProduct} + productData={productData} + categories={categories} + editing={false} + handleChange={handleChange} + handleTieredPricingChange={handleTieredPricingChange} + handleAddTier={handleAddTier} + handleRemoveTier={handleRemoveTier} + setProductData={setProductData} + /> + ) } diff --git a/components/home-navbar.tsx b/components/home-navbar.tsx index 5eeb0d9..1cd145c 100644 --- a/components/home-navbar.tsx +++ b/components/home-navbar.tsx @@ -3,7 +3,6 @@ import Link from "next/link"; import { Button } from "@/components/ui/button"; import { LogIn } from "lucide-react"; -import { ThemeSwitcher } from "@/components/theme-switcher"; import { useState } from "react"; export function HomeNavbar() { @@ -27,16 +26,16 @@ export function HomeNavbar() { Log In - - + +
- + {/* Mobile menu */} {menuOpen && (
- setMenuOpen(false)} > Features - setMenuOpen(false)} > Benefits - setMenuOpen(false)} > Log In - setMenuOpen(false)} > - Create Account + Get Started
)} ); -} \ No newline at end of file +} \ No newline at end of file