Initial: ecommerce template via tAI

This commit is contained in:
Joe Wee
2026-05-21 11:30:45 +01:00
commit b5aea5304b
18 changed files with 2284 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet";
import { Menu, Search, ShoppingBag, User, Heart, Separator as SeparatorIcon } from "lucide-react";
import { Separator } from "@/components/ui/separator";
import { ThemeToggle } from "@/components/ThemeToggle";
import { useCart } from "@/lib/cart-context";
const cartPreviewItems = [
{ name: "Classic Leather Jacket", price: 189, qty: 1 },
{ name: "Silk Scarf", price: 65, qty: 1 },
{ name: "Organic Cotton Tee", price: 45, qty: 2 },
];
const navLinks = [
{ label: "Home", to: "/" },
{ label: "Shop", to: "/products" },
{ label: "New Arrivals", to: "/products" },
{ label: "Sale", to: "/products" },
];
export default function Header() {
const [mobileOpen, setMobileOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const { itemCount } = useCart();
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
<header className={\`fixed top-0 inset-x-0 z-50 transition-all duration-300 \${
scrolled ? "backdrop-blur-xl bg-white/80 dark:bg-slate-900/80 border-white/20 border-b border-stone-100 shadow-sm" : "bg-white/80 backdrop-blur-sm"
}\`}>
{/* Top Banner */}
<div className="bg-stone-900 text-white text-center text-xs py-1.5">
Free shipping on orders over $100 | Use code STYLE20 for 20% off
</div>
<div className="mx-auto max-w-7xl flex items-center justify-between px-6 py-3">
{/* Logo */}
<Link to="/" className="text-xl font-bold tracking-tight text-stone-900">
StyleHaus
</Link>
{/* Desktop Nav */}
<nav className="hidden md:flex items-center gap-8">
{navLinks.map((l) => (
<Link key={l.to + l.label} to={l.to} className="text-sm text-stone-600 hover:text-stone-900 transition-colors font-medium">
{l.label}
</Link>
))}
</nav>
{/* Right Icons */}
<div className="flex items-center gap-1">
<Button asChild variant="ghost" size="icon" className="hidden sm:flex h-9 w-9">
<Link to="/search"><Search className="h-4 w-4" /></Link>
</Button>
<Button asChild variant="ghost" size="icon" className="hidden sm:flex h-9 w-9">
<Link to="/account?tab=wishlist"><Heart className="h-4 w-4" /></Link>
</Button>
<Sheet>
<SheetTrigger asChild>
<Button variant={itemCount > 0 ? "default" : "ghost"} size="icon" className={\`relative h-9 w-9 transition-all \${itemCount > 0 ? "bg-stone-900 hover:bg-stone-800 text-white" : ""}\`}>
<ShoppingBag className="h-4 w-4" />
{itemCount > 0 && (
<span className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-rose-500 text-[10px] font-bold text-white flex items-center justify-center ring-2 ring-white animate-fade-in">
{itemCount}
</span>
)}
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-80 bg-white flex flex-col">
<h2 className="text-lg font-bold mb-4">Your Cart</h2>
<div className="flex-1 overflow-y-auto space-y-4">
{cartPreviewItems.map((item, i) => (
<div key={i} className="flex items-center justify-between text-sm">
<div>
<p className="font-medium">{item.name}</p>
<p className="text-stone-500 text-xs">Qty: {item.qty}</p>
</div>
<span className="font-semibold">\${(item.price * item.qty).toFixed(2)}</span>
</div>
))}
</div>
<Separator className="my-4" />
<div className="flex justify-between text-sm font-bold mb-4">
<span>Subtotal</span>
<span>\${cartPreviewItems.reduce((s, i) => s + i.price * i.qty, 0).toFixed(2)}</span>
</div>
<div className="space-y-2">
<Button asChild variant="outline" className="w-full rounded-full">
<Link to="/cart">View Cart</Link>
</Button>
<Button asChild className="w-full rounded-full bg-stone-900 hover:bg-stone-800">
<Link to="/checkout">Checkout</Link>
</Button>
</div>
</SheetContent>
</Sheet>
<Button asChild variant="ghost" size="icon" className="hidden sm:flex h-9 w-9">
<Link to="/account"><User className="h-4 w-4" /></Link>
</Button>
<ThemeToggle />
{/* Mobile Menu */}
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetTrigger asChild className="md:hidden">
<Button variant="ghost" size="icon" className="h-9 w-9"><Menu className="h-5 w-5" /></Button>
</SheetTrigger>
<SheetContent side="right" className="w-72 bg-white">
<nav className="mt-8 flex flex-col gap-1">
{navLinks.map((l) => (
<Link
key={l.to + l.label}
to={l.to}
className="px-4 py-3 text-base font-medium hover:bg-stone-50 rounded-lg transition-colors"
onClick={() => setMobileOpen(false)}
>
{l.label}
</Link>
))}
<div className="border-t border-stone-100 mt-4 pt-4 space-y-1">
<Link to="/search" className="flex items-center gap-3 px-4 py-3 text-sm text-stone-600 hover:bg-stone-50 rounded-lg" onClick={() => setMobileOpen(false)}>
<Search className="h-4 w-4" /> Search
</Link>
<Link to="/account" className="flex items-center gap-3 px-4 py-3 text-sm text-stone-600 hover:bg-stone-50 rounded-lg" onClick={() => setMobileOpen(false)}>
<User className="h-4 w-4" /> Account
</Link>
<Link to="/account?tab=wishlist" className="flex items-center gap-3 px-4 py-3 text-sm text-stone-600 hover:bg-stone-50 rounded-lg" onClick={() => setMobileOpen(false)}>
<Heart className="h-4 w-4" /> Wishlist
</Link>
<div className="px-4 pt-2"><ThemeToggle /></div>
</div>
</nav>
</SheetContent>
</Sheet>
</div>
</div>
</header>
);
}