Refactor UI to remove Christmas theme and improve actions
All checks were successful
Build Frontend / build (push) Successful in 1m11s

Removed all Christmas-specific theming and logic from the home page and navbar, standardizing colors to indigo. Updated QuickActions to open a modal for adding products instead of navigating to a new page, including logic for product creation and category fetching. Simplified ChatTable row animations and fixed minor layout issues. Updated button styles and mobile menu links for consistency.
This commit is contained in:
g
2026-01-12 07:43:33 +00:00
parent 244014f33a
commit e9737c8b24
4 changed files with 212 additions and 127 deletions

View File

@@ -1,77 +1,43 @@
import { getPlatformStatsServer } from "@/lib/server-api"; import { getPlatformStatsServer } from "@/lib/server-api";
import { HomeNavbar } from "@/components/home-navbar"; import { HomeNavbar } from "@/components/home-navbar";
import { Suspense } from "react"; import { Shield, LineChart, Zap, ArrowRight, Sparkles } from "lucide-react";
import { Shield, LineChart, Zap, ArrowRight, CheckCircle2, Sparkles } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import Link from "next/link"; import Link from "next/link";
import { AnimatedStatsSection } from "@/components/animated-stats-section"; import { AnimatedStatsSection } from "@/components/animated-stats-section";
import { isDecember } from "@/lib/utils/christmas";
import { MotionWrapper } from "@/components/ui/motion-wrapper"; import { MotionWrapper } from "@/components/ui/motion-wrapper";
export const dynamic = 'force-dynamic'; 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() { export default async function Home() {
try { try {
const stats = await getPlatformStatsServer(); const stats = await getPlatformStatsServer();
const isDec = isDecember();
return ( return (
<div className={`flex flex-col min-h-screen bg-black text-white ${isDec ? 'christmas-theme' : ''}`}> <div className="flex flex-col min-h-screen bg-black text-white">
<div className={`absolute inset-0 bg-gradient-to-br pointer-events-none scale-100 ${isDec <div className="absolute inset-0 bg-gradient-to-br from-indigo-500/10 via-purple-500/5 to-transparent pointer-events-none scale-100" />
? 'from-red-500/10 via-green-500/5 to-transparent'
: 'from-[#D53F8C]/10 via-[#D53F8C]/3 to-transparent'
}`} />
<HomeNavbar /> <HomeNavbar />
{/* Hero Section */} {/* Hero Section */}
<section className="relative overflow-hidden"> <section className="relative overflow-hidden">
<div className="relative flex flex-col items-center px-4 py-20 md:py-32 mx-auto max-w-7xl"> <div className="relative flex flex-col items-center px-4 py-20 md:py-32 mx-auto max-w-7xl">
<div className="flex flex-col items-center text-center space-y-6 max-w-3xl"> <div className="flex flex-col items-center text-center space-y-6 max-w-3xl">
<div className={`inline-flex items-center px-4 py-2 rounded-full border mb-4 ${isDec <div className="inline-flex items-center px-4 py-2 rounded-full border border-indigo-500/20 bg-indigo-500/10 mb-4">
? 'bg-red-500/10 border-red-500/20' <Sparkles className="h-4 w-4 mr-2 text-indigo-400" />
: 'bg-[#D53F8C]/10 border-[#D53F8C]/20' <span className="text-sm font-medium text-indigo-400">
}`}>
<Sparkles className={`h-4 w-4 mr-2 ${isDec ? 'text-red-400' : 'text-[#D53F8C]'}`} />
<span className={`text-sm font-medium ${isDec ? 'text-red-400' : 'text-[#D53F8C]'}`}>
Secure Crypto Payments Secure Crypto Payments
</span> </span>
</div> </div>
<h1 className="text-4xl md:text-6xl font-bold tracking-tight"> <h1 className="text-4xl md:text-6xl font-bold tracking-tight">
The Future of <span className={isDec ? 'text-red-400' : 'text-[#D53F8C]'}>E-commerce</span> Management The Future of <span className="text-indigo-500">E-commerce</span> Management
</h1> </h1>
<p className="text-lg md:text-xl text-zinc-400 max-w-2xl"> <p className="text-lg md:text-xl text-zinc-400 max-w-2xl">
{isDec Streamline your online business with our all-in-one platform. Secure payments, order tracking, and analytics in one place.
? '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.'
}
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 mt-4"> <div className="flex flex-col sm:flex-row gap-4 mt-4">
<Link href="/dashboard"> <Link href="/dashboard">
<Button <Button
size="lg" size="lg"
className={`gap-2 text-white border-0 h-12 px-8 ${isDec className="gap-2 text-white border-0 h-12 px-8 bg-indigo-600 hover:bg-indigo-700"
? 'bg-gradient-to-r from-red-500 to-green-500 hover:from-red-600 hover:to-green-600'
: 'bg-[#D53F8C] hover:bg-[#B83280]'
}`}
> >
Get Started Get Started
<ArrowRight className="h-4 w-4" /> <ArrowRight className="h-4 w-4" />
@@ -103,35 +69,21 @@ export default async function Home() {
title: "Lightning Fast", title: "Lightning Fast",
description: "Optimized for speed with real-time updates and instant notifications." description: "Optimized for speed with real-time updates and instant notifications."
} }
].map((feature, i) => { ].map((feature, i) => (
const christmasColors = ['from-red-500/5', 'from-green-500/5', 'from-yellow-500/5'];
const christmasBorders = ['border-red-500/30', 'border-green-500/30', 'border-yellow-500/30'];
const christmasIcons = ['text-red-400', 'text-green-400', 'text-yellow-400'];
const christmasBgs = ['bg-red-500/10', 'bg-green-500/10', 'bg-yellow-500/10'];
return (
<div <div
key={i} key={i}
className={`group relative overflow-hidden rounded-xl bg-gradient-to-b p-6 border transition-all duration-300 hover:scale-[1.02] hover:shadow-lg ${isDec className="group relative overflow-hidden rounded-xl bg-gradient-to-b from-zinc-800/30 to-transparent p-6 border border-zinc-800 transition-all duration-300 hover:scale-[1.02] hover:shadow-lg"
? `from-zinc-800/30 to-transparent ${christmasBorders[i % 3]}`
: 'from-zinc-800/30 to-transparent border-zinc-800'
}`}
> >
<div <div className="absolute inset-0 bg-gradient-to-b from-indigo-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity" />
className={`absolute inset-0 bg-gradient-to-b to-transparent opacity-0 group-hover:opacity-100 transition-opacity ${isDec ? christmasColors[i % 3] : 'from-[#D53F8C]/5'
}`}
/>
<div className="relative"> <div className="relative">
<div className={`h-12 w-12 flex items-center justify-center rounded-lg mb-4 ${isDec ? christmasBgs[i % 3] : 'bg-[#D53F8C]/10' <div className="h-12 w-12 flex items-center justify-center rounded-lg mb-4 bg-indigo-500/10">
}`}> <feature.icon className="h-6 w-6 text-indigo-500" />
<feature.icon className={`h-6 w-6 ${isDec ? christmasIcons[i % 3] : 'text-[#D53F8C]'}`} />
</div> </div>
<h3 className="text-lg font-semibold text-white mb-2">{feature.title}</h3> <h3 className="text-lg font-semibold text-white mb-2">{feature.title}</h3>
<p className="text-sm text-zinc-400">{feature.description}</p> <p className="text-sm text-zinc-400">{feature.description}</p>
</div> </div>
</div> </div>
); ))}
})}
</div> </div>
</MotionWrapper> </MotionWrapper>
@@ -145,13 +97,6 @@ export default async function Home() {
{/* Footer */} {/* Footer */}
<footer className="relative py-12 px-4 mt-auto"> <footer className="relative py-12 px-4 mt-auto">
<div className="max-w-7xl mx-auto flex flex-col items-center"> <div className="max-w-7xl mx-auto flex flex-col items-center">
{isDec && (
<div className="flex items-center gap-2 mb-4 text-red-400 animate-twinkle">
<span className="text-xl">🎄</span>
<span className="text-sm font-medium">Happy Holidays from da ember team!</span>
<span className="text-xl">🎄</span>
</div>
)}
<div className="text-sm text-zinc-500"> <div className="text-sm text-zinc-500">
© {new Date().getFullYear()} Ember. All rights reserved. © {new Date().getFullYear()} Ember. All rights reserved.
</div> </div>

View File

@@ -304,7 +304,7 @@ export default function ChatTable() {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
<AnimatePresence mode="popLayout"> <AnimatePresence>
{loading ? ( {loading ? (
<TableRow> <TableRow>
<TableCell colSpan={4} className="h-32 text-center"> <TableCell colSpan={4} className="h-32 text-center">
@@ -328,10 +328,10 @@ export default function ChatTable() {
chats.map((chat, index) => ( chats.map((chat, index) => (
<motion.tr <motion.tr
key={chat._id} key={chat._id}
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }} transition={{ duration: 0.2 }}
className="group cursor-pointer hover:bg-muted/30 transition-colors border-b border-border/50 last:border-0" className="group cursor-pointer hover:bg-muted/30 transition-colors border-b border-border/50 last:border-0"
onClick={() => handleChatClick(chat._id)} onClick={() => handleChatClick(chat._id)}
style={{ display: 'table-row' }} // Essential for table layout style={{ display: 'table-row' }} // Essential for table layout

View File

@@ -1,26 +1,33 @@
"use client" "use client"
import { useState, useEffect, ChangeEvent } from "react"
import Link from "next/link" import Link from "next/link"
import { motion } from "framer-motion" import { motion } from "framer-motion"
import { import {
PlusCircle, PlusCircle,
Package,
BarChart3,
Settings,
MessageSquare,
Truck, Truck,
Tag, BarChart3,
Users MessageSquare,
} from "lucide-react" } from "lucide-react"
import { Card, CardContent } from "@/components/ui/card" 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 = [ const actions = [
{ {
title: "Add Product", title: "Add Product",
icon: PlusCircle, icon: PlusCircle,
href: "/dashboard/products/new", href: "/dashboard/products/new", // Fallback text
color: "bg-blue-500/10 text-blue-500", color: "bg-blue-500/10 text-blue-500",
description: "Create a new listing" description: "Create a new listing",
action: "modal"
}, },
{ {
title: "Process Orders", title: "Process Orders",
@@ -46,19 +53,118 @@ const actions = [
] ]
export default function QuickActions() { export default function QuickActions() {
const [modalOpen, setModalOpen] = useState(false);
const [loading, setLoading] = useState(false);
const [categories, setCategories] = useState<Category[]>([]);
const [productData, setProductData] = useState<Product>({
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<HTMLInputElement | HTMLTextAreaElement>) => {
setProductData({ ...productData, [e.target.name]: e.target.value });
};
const handleTieredPricingChange = (e: ChangeEvent<HTMLInputElement>, 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 ( return (
<>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
{actions.map((action, index) => ( {actions.map((action, index) => {
<motion.div const isModalAction = action.action === "modal";
key={action.title}
initial={{ opacity: 0, y: 20 }} const CardContentWrapper = () => (
animate={{ opacity: 1, y: 0 }} <Card className="hover:border-primary/50 transition-colors cursor-pointer group h-full border-border/40 bg-background/50 backdrop-blur-sm">
transition={{ delay: index * 0.1 }}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<Link href={action.href}>
<Card className="hover:border-primary/50 transition-colors cursor-pointer group h-full">
<CardContent className="p-6 flex flex-col items-center text-center"> <CardContent className="p-6 flex flex-col items-center text-center">
<div className={`p-3 rounded-xl ${action.color} mb-4 group-hover:scale-110 transition-transform`}> <div className={`p-3 rounded-xl ${action.color} mb-4 group-hover:scale-110 transition-transform`}>
<action.icon className="h-6 w-6" /> <action.icon className="h-6 w-6" />
@@ -67,9 +173,44 @@ export default function QuickActions() {
<p className="text-sm text-muted-foreground mt-1">{action.description}</p> <p className="text-sm text-muted-foreground mt-1">{action.description}</p>
</CardContent> </CardContent>
</Card> </Card>
</Link> );
</motion.div>
))} return (
<motion.div
key={action.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.1 }}
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
{isModalAction ? (
<div onClick={() => setModalOpen(true)}>
<CardContentWrapper />
</div> </div>
) : (
<Link href={action.href}>
<CardContentWrapper />
</Link>
)}
</motion.div>
);
})}
</div>
<ProductModal
open={modalOpen}
onClose={() => setModalOpen(false)}
onSave={handleSaveProduct}
productData={productData}
categories={categories}
editing={false}
handleChange={handleChange}
handleTieredPricingChange={handleTieredPricingChange}
handleAddTier={handleAddTier}
handleRemoveTier={handleRemoveTier}
setProductData={setProductData}
/>
</>
) )
} }

View File

@@ -3,7 +3,6 @@
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { LogIn } from "lucide-react"; import { LogIn } from "lucide-react";
import { ThemeSwitcher } from "@/components/theme-switcher";
import { useState } from "react"; import { useState } from "react";
export function HomeNavbar() { export function HomeNavbar() {
@@ -27,8 +26,8 @@ export function HomeNavbar() {
Log In Log In
</Button> </Button>
</Link> </Link>
<Link href="/auth/login"> <Link href="/dashboard">
<Button className="bg-[#D53F8C] hover:bg-[#B83280] text-white border-0">Get Started</Button> <Button className="bg-indigo-600 hover:bg-indigo-700 text-white border-0">Get Started</Button>
</Link> </Link>
</nav> </nav>
<div className="md:hidden"> <div className="md:hidden">
@@ -78,11 +77,11 @@ export function HomeNavbar() {
Log In Log In
</Link> </Link>
<Link <Link
href="/auth/register" href="/dashboard"
className="text-sm p-2 hover:bg-gray-900 rounded-md text-gray-300" className="text-sm p-2 hover:bg-gray-900 rounded-md text-gray-300"
onClick={() => setMenuOpen(false)} onClick={() => setMenuOpen(false)}
> >
Create Account Get Started
</Link> </Link>
</div> </div>
</div> </div>