commit dea01a3c0142a31ff4853c0b31a8146c0cb604b3 Author: Joe Wee Date: Thu May 21 12:19:54 2026 +0100 Initial: ecommerce template via tAI diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..dba91da --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,13 @@ +{ + "permissions": { + "allow": [ + "Read", + "Write", + "Edit", + "Bash", + "Glob", + "Grep" + ] + }, + "model": "claude-sonnet-4-20250514" +} \ No newline at end of file diff --git a/.tai-manifest.tn b/.tai-manifest.tn new file mode 100644 index 0000000..b0b3d86 --- /dev/null +++ b/.tai-manifest.tn @@ -0,0 +1,22 @@ +# .tai-manifest.tn — ecommerce (vite-react) +# Auto-generated by tAI manifest generator +# tN (tAI Notation) — compact template encoding + +σ:template|ecommerce|vite-react|17 files|117KB +σ:docs|CLAUDE.md|1124B|imports:1 +σ:docs|README.md|41B +σ:entry|index.html|278B|title="Ecommerce" +σ:app-root|src/App.tsx|1170B|routes:8|imports:10 +σ:component|src/components/Footer.tsx|3349B|imports:2|uses:[Link,Separator] +σ:component|src/components/Header.tsx|6954B|imports:10 +σ:style|src/index.css|59B +σ:utility|src/lib/cart-context.tsx|2532B|imports:1|uses:[CartContextType,CartItem,CartContext] +σ:bootstrap|src/main.tsx|236B|imports:3|uses:[React,App] +σ:page|src/pages/Account.tsx|10374B|h1="Welcome back, {profile.firstName}"|imports:11 +σ:page|src/pages/Cart.tsx|8631B|h1="Your cart is empty"|imports:12 +σ:page|src/pages/Checkout.tsx|19311B|imports:12 +σ:page|src/pages/Index.tsx|20029B|imports:10 +σ:page|src/pages/OrderConfirmation.tsx|3351B|h1="Order Confirmed!"|imports:7 +σ:page|src/pages/ProductDetail.tsx|16562B|h1="Classic Leather Jacket"|imports:13 +σ:page|src/pages/Products.tsx|18616B|h1="All Products"|imports:10 +σ:page|src/pages/Search.tsx|7498B|imports:10 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e28ad44 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,32 @@ +# StyleHaus +Multi-page e-commerce store with product catalog, cart, checkout, and account pages +Stack: React 18 / TypeScript / Vite / Tailwind CSS / shadcn/ui +Pages: Index, Products, ProductDetail, Cart, Checkout, Account, Search, OrderConfirmation +Palette: rose/stone/amber warm tones + +## Files +- src/pages/Index.tsx (19KB) +- src/pages/Products.tsx (18KB) +- src/pages/ProductDetail.tsx (16KB) +- src/pages/Cart.tsx (8KB) +- src/pages/Checkout.tsx (19KB) +- src/pages/Account.tsx (10KB) +- src/pages/Search.tsx (7KB) +- src/pages/OrderConfirmation.tsx (3KB) +- src/components/Header.tsx (7KB) +- src/components/Footer.tsx (3KB) +- src/App.tsx (1KB) +- src/lib/cart-context.tsx (2KB) + +## Imports +UI: @/components/ui/{button,card,badge,input,textarea,tabs,accordion,dialog,sheet} +Icons: lucide-react +Toast: import { toast } from "sonner" +Theme: ThemeToggle in headers (next-themes) +Router: react-router-dom BrowserRouter + +## Rules +- MODIFY existing files — never rebuild from scratch +- Pages import Header + Footer from @/components/ +- Tailwind only — no inline styles +- Use existing shadcn components — don't create custom ones diff --git a/README.md b/README.md new file mode 100644 index 0000000..b80d4fb --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# template-ecommerce + +Template: ecommerce \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..6cd7288 --- /dev/null +++ b/index.html @@ -0,0 +1,12 @@ + + + + + + Ecommerce + + +
+ + + diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..769a777 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,31 @@ +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { CartProvider } from "@/lib/cart-context"; +import IndexPage from "@/pages/Index"; +import ProductsPage from "@/pages/Products"; +import ProductDetailPage from "@/pages/ProductDetail"; +import CartPage from "@/pages/Cart"; +import CheckoutPage from "@/pages/Checkout"; +import AccountPage from "@/pages/Account"; +import SearchPage from "@/pages/Search"; +import OrderConfirmationPage from "@/pages/OrderConfirmation"; + +function App() { + return ( + + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + ); +} + +export default App; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..1944886 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,93 @@ +import { Link } from "react-router-dom"; +import { Separator } from "@/components/ui/separator"; + +const footerLinks = { + shop: [ + { label: "New Arrivals", to: "/products" }, + { label: "Best Sellers", to: "/products" }, + { label: "Sale", to: "/products" }, + { label: "Collections", to: "/products" }, + ], + help: [ + { label: "Customer Service", to: "/" }, + { label: "Size Guide", to: "/" }, + { label: "Shipping & Returns", to: "/" }, + { label: "FAQ", to: "/" }, + ], + about: [ + { label: "Our Story", to: "/" }, + { label: "Sustainability", to: "/" }, + { label: "Careers", to: "/" }, + { label: "Press", to: "/" }, + ], +}; + +export default function Footer() { + return ( + + ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..77b6b26 --- /dev/null +++ b/src/components/Header.tsx @@ -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 ( +
+ {/* Top Banner */} +
+ Free shipping on orders over $100 | Use code STYLE20 for 20% off +
+ +
+ {/* Logo */} + + StyleHaus + + + {/* Desktop Nav */} + + + {/* Right Icons */} +
+ + + + + + + +

Your Cart

+
+ {cartPreviewItems.map((item, i) => ( +
+
+

{item.name}

+

Qty: {item.qty}

+
+ \${(item.price * item.qty).toFixed(2)} +
+ ))} +
+ +
+ Subtotal + \${cartPreviewItems.reduce((s, i) => s + i.price * i.qty, 0).toFixed(2)} +
+
+ + +
+
+
+ + + + {/* Mobile Menu */} + + + + + + + + +
+
+
+ ); +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/lib/cart-context.tsx b/src/lib/cart-context.tsx new file mode 100644 index 0000000..05f1d2a --- /dev/null +++ b/src/lib/cart-context.tsx @@ -0,0 +1,102 @@ +import { createContext, useContext, useState, ReactNode } from "react"; + +export interface CartItem { + id: string; + name: string; + price: number; + quantity: number; + size?: string; + color?: string; + image?: string; +} + +interface CartContextType { + items: CartItem[]; + addItem: (item: CartItem) => void; + removeItem: (id: string) => void; + updateQuantity: (id: string, quantity: number) => void; + clearCart: () => void; + total: number; + itemCount: number; +} + +const CartContext = createContext(undefined); + +export function CartProvider({ children }: { children: ReactNode }) { + const [items, setItems] = useState([ + { + id: "leather-jacket-1", + name: "Classic Leather Jacket", + price: 189, + quantity: 1, + size: "M", + color: "Black", + image: "", + }, + { + id: "silk-dress-1", + name: "Silk Evening Dress", + price: 245, + quantity: 1, + size: "S", + color: "Rose", + image: "", + }, + { + id: "cashmere-sweater-1", + name: "Cashmere Knit Sweater", + price: 128, + quantity: 2, + size: "M", + color: "Stone", + image: "", + }, + ]); + + const addItem = (newItem: CartItem) => { + setItems((prev) => { + const existing = prev.find( + (i) => i.id === newItem.id && i.size === newItem.size && i.color === newItem.color + ); + if (existing) { + return prev.map((i) => + i.id === existing.id && i.size === existing.size && i.color === existing.color + ? { ...i, quantity: i.quantity + newItem.quantity } + : i + ); + } + return [...prev, newItem]; + }); + }; + + const removeItem = (id: string) => { + setItems((prev) => prev.filter((i) => i.id !== id)); + }; + + const updateQuantity = (id: string, quantity: number) => { + if (quantity <= 0) { + removeItem(id); + return; + } + setItems((prev) => prev.map((i) => (i.id === id ? { ...i, quantity } : i))); + }; + + const clearCart = () => setItems([]); + + const total = items.reduce((sum, i) => sum + i.price * i.quantity, 0); + const itemCount = items.reduce((sum, i) => sum + i.quantity, 0); + + return ( + + {children} + + ); +} + +export function useCart() { + const context = useContext(CartContext); + if (!context) { + throw new Error("useCart must be used within a CartProvider"); + } + return context; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..9b67590 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import App from "./App"; +import "./index.css"; + +ReactDOM.createRoot(document.getElementById("root")!).render( + + + +); diff --git a/src/pages/Account.tsx b/src/pages/Account.tsx new file mode 100644 index 0000000..69b60bc --- /dev/null +++ b/src/pages/Account.tsx @@ -0,0 +1,201 @@ +import { useState } from "react"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Separator } from "@/components/ui/separator"; +import { + Table, TableBody, TableCell, TableHead, TableHeader, TableRow +} from "@/components/ui/table"; +import { + Package, User, MapPin, Heart, Star, Eye, Trash2, ShoppingBag, Edit +} from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +const orders = [ + { id: "ORD-2024-1847", date: "Mar 15, 2024", status: "Delivered", statusColor: "bg-green-100 text-green-700", total: 423.00, items: 3 }, + { id: "ORD-2024-1692", date: "Feb 28, 2024", status: "Shipped", statusColor: "bg-blue-100 text-blue-700", total: 189.00, items: 1 }, + { id: "ORD-2024-1534", date: "Feb 10, 2024", status: "Processing", statusColor: "bg-amber-100 text-amber-700", total: 567.00, items: 4 }, +]; + +const wishlistItems = [ + { id: 1, name: "Silk Evening Dress", price: 245, gradient: "from-amber-100 to-orange-200", rating: 5 }, + { id: 2, name: "Italian Leather Handbag", price: 320, gradient: "from-stone-200 to-rose-100", rating: 5 }, + { id: 3, name: "Tailored Wool Coat", price: 385, gradient: "from-stone-200 to-stone-400", rating: 5 }, + { id: 4, name: "Pearl Drop Earrings", price: 68, gradient: "from-amber-100 to-rose-100", rating: 4 }, +]; + +export default function AccountPage() { + const [profile, setProfile] = useState({ + firstName: "Alexandra", + lastName: "Chen", + email: "alex.chen@email.com", + phone: "+1 (555) 234-5678", + }); + + const renderStars = (count: number) => + Array.from({ length: 5 }, (_, i) => ( + + )); + + return ( +
+
+ +
+ {/* Profile Header */} +
+ + AC + +
+

Welcome back, {profile.firstName}

+

{profile.email}

+
+
+ + + + Orders + Profile + Addresses + Wishlist + + + {/* Orders Tab */} + + + + + + + Order + Date + Status + Total + Action + + + + {orders.map((order) => ( + + {order.id} + {order.date} + + {order.status} + + \${order.total.toFixed(2)} + + + + + ))} + +
+
+
+
+ + {/* Profile Tab */} + + + +

Personal Information

+
+
+ + setProfile({ ...profile, firstName: e.target.value })} /> +
+
+ + setProfile({ ...profile, lastName: e.target.value })} /> +
+
+ + setProfile({ ...profile, email: e.target.value })} /> +
+
+ + setProfile({ ...profile, phone: e.target.value })} /> +
+
+ +
+
+
+ + {/* Addresses Tab */} + +
+ + +
+ Default + +
+

Home

+

+ Alexandra Chen
+ 456 Park Avenue, Apt 12B
+ New York, NY 10022 +

+
+
+ + +
+ Office + +
+

Office

+

+ Alexandra Chen
+ 789 Madison Ave, Floor 15
+ New York, NY 10065 +

+
+
+
+ +
+ + {/* Wishlist Tab */} + +
+ {wishlistItems.map((item) => ( + +
+
+
+ Image +
+ +

{item.name}

+
{renderStars(item.rating)}
+

\${item.price}

+
+ + +
+
+ + ))} +
+ + +
+ +
+
+ ); +} diff --git a/src/pages/Cart.tsx b/src/pages/Cart.tsx new file mode 100644 index 0000000..986ec5e --- /dev/null +++ b/src/pages/Cart.tsx @@ -0,0 +1,177 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { Minus, Plus, X, ShoppingBag, ArrowRight, ArrowLeft, Tag } from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; +import { useCart } from "@/lib/cart-context"; + +export default function CartPage() { + const { items, removeItem, updateQuantity, total, itemCount, clearCart } = useCart(); + const [coupon, setCoupon] = useState(""); + const [couponApplied, setCouponApplied] = useState(false); + + const shipping = total >= 100 ? 0 : 12.99; + const tax = total * 0.08; + const discount = couponApplied ? total * 0.1 : 0; + const grandTotal = total + shipping + tax - discount; + + const applyCoupon = () => { + if (coupon.trim().length > 0) { + setCouponApplied(true); + toast.success("Coupon applied!"); + } + }; + + if (items.length === 0) { + return ( +
+
+
+
+ +
+

Your cart is empty

+

Looks like you haven't added anything to your cart yet.

+ +
+
+
+ ); + } + + return ( +
+
+ +
+

Shopping Cart

+

{itemCount} {itemCount === 1 ? "item" : "items"} in your cart

+ +
+ {/* Cart Items */} +
+ {items.map((item) => ( + + +
+
+
+
+ Image +
+
+
+
+

{item.name}

+
+ {item.color && {item.color}} + {item.size && Size: {item.size}} +
+
+ +
+
+
+ + {item.quantity} + +
+ \${(item.price * item.quantity).toFixed(2)} +
+
+
+ + + ))} +
+ + {/* Order Summary */} +
+ + +

Order Summary

+ +
+
+ Subtotal + \${total.toFixed(2)} +
+
+ Shipping + + {shipping === 0 ? "Free" : \`\$\${shipping.toFixed(2)}\`} + +
+
+ Tax + \${tax.toFixed(2)} +
+ {couponApplied && ( +
+ Discount (10%) + -\${discount.toFixed(2)} +
+ )} +
+ + + +
+ Total + \${grandTotal.toFixed(2)} +
+ + {/* Coupon */} +
+
+ + setCoupon(e.target.value)} + className="pl-9 rounded-full text-sm" + /> +
+ +
+ + {shipping > 0 && ( +

+ Add \${(100 - total).toFixed(2)} more for free shipping +

+ )} + +
+ + +
+
+
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/Checkout.tsx b/src/pages/Checkout.tsx new file mode 100644 index 0000000..62a5a1a --- /dev/null +++ b/src/pages/Checkout.tsx @@ -0,0 +1,334 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link, useNavigate } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { Badge } from "@/components/ui/badge"; +import { Check, CreditCard, MapPin, ClipboardList, ArrowLeft, ArrowRight, Lock } from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; +import { useCart } from "@/lib/cart-context"; + +const steps = [ + { num: 1, label: "Shipping", icon: MapPin }, + { num: 2, label: "Payment", icon: CreditCard }, + { num: 3, label: "Review", icon: ClipboardList }, +]; + +export default function CheckoutPage() { + const [step, setStep] = useState(1); + const [errors, setErrors] = useState>({}); + const { items, total, clearCart } = useCart(); + const navigate = useNavigate(); + + const [shipping, setShipping] = useState({ + firstName: "", lastName: "", email: "", address: "", city: "", state: "", zip: "", + }); + const [payment, setPayment] = useState({ + cardNumber: "", expiry: "", cvv: "", cardholder: "", + }); + + const shippingCost = total >= 100 ? 0 : 12.99; + const tax = total * 0.08; + const grandTotal = total + shippingCost + tax; + + const validateShipping = () => { + const e: Record = {}; + if (!shipping.firstName.trim()) e.firstName = "First name is required"; + if (!shipping.lastName.trim()) e.lastName = "Last name is required"; + if (!shipping.email.includes("@")) e.email = "Valid email is required"; + if (!shipping.address.trim()) e.address = "Address is required"; + if (!shipping.city.trim()) e.city = "City is required"; + if (!shipping.zip.trim()) e.zip = "ZIP code is required"; + setErrors(e); + return Object.keys(e).length === 0; + }; + + const validatePayment = () => { + const e: Record = {}; + if (payment.cardNumber.replace(/\s/g, "").length < 16) e.cardNumber = "Enter a valid 16-digit card number"; + if (!payment.expiry.trim()) e.expiry = "Expiry date is required"; + if (payment.cvv.length < 3) e.cvv = "CVV must be at least 3 digits"; + setErrors(e); + return Object.keys(e).length === 0; + }; + + const handlePlaceOrder = () => { + toast.success("Order placed!"); + clearCart(); + navigate("/order-confirmation"); + }; + + const updateShipping = (field: string, value: string) => { + setShipping((prev) => ({ ...prev, [field]: value })); + if (errors[field]) setErrors((prev) => { const n = { ...prev }; delete n[field]; return n; }); + }; + + const updatePayment = (field: string, value: string) => { + setPayment((prev) => ({ ...prev, [field]: value })); + if (errors[field]) setErrors((prev) => { const n = { ...prev }; delete n[field]; return n; }); + }; + + return ( +
+
+ +
+ {/* Step Indicator */} +
+ {steps.map((s, i) => ( +
+
= s.num ? "text-stone-900" : "text-stone-400"}\`} onClick={() => s.num < step && setStep(s.num)}> +
s.num ? "bg-green-500/80 text-white" : step === s.num ? "bg-stone-900/80 text-white" : "bg-white/80 dark:bg-slate-900/80 text-stone-500" + }\`}> + {step > s.num ? : s.num} +
+ {s.label} +
+ {i < steps.length - 1 && ( +
s.num ? "bg-green-500" : "bg-stone-200"}\`} /> + )} +
+ ))} +
+ +
+ {/* Main Form */} +
+ {/* Step 1: Shipping */} + {step === 1 && ( + + +

+ Shipping Information +

+
{ e.preventDefault(); if (validateShipping()) setStep(2); }}> +
+
+ +
+ updateShipping("firstName", e.target.value)} placeholder="John" className={\`\${errors.firstName ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.firstName && shipping.firstName.trim() && } +
+ {errors.firstName &&

{errors.firstName}

} +
+
+ +
+ updateShipping("lastName", e.target.value)} placeholder="Doe" className={\`\${errors.lastName ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.lastName && shipping.lastName.trim() && } +
+ {errors.lastName &&

{errors.lastName}

} +
+
+ +
+ updateShipping("email", e.target.value)} placeholder="john@example.com" className={\`\${errors.email ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.email && shipping.email?.includes("@") && } +
+ {errors.email &&

{errors.email}

} +
+
+ +
+ updateShipping("address", e.target.value)} placeholder="123 Main Street" className={\`\${errors.address ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.address && shipping.address.trim() && } +
+ {errors.address &&

{errors.address}

} +
+
+ +
+ updateShipping("city", e.target.value)} placeholder="New York" className={\`\${errors.city ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.city && shipping.city.trim() && } +
+ {errors.city &&

{errors.city}

} +
+
+
+ + updateShipping("state", e.target.value)} placeholder="NY" /> +
+
+ +
+ updateShipping("zip", e.target.value)} placeholder="10001" className={\`\${errors.zip ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.zip && shipping.zip.trim() && } +
+ {errors.zip &&

{errors.zip}

} +
+
+
+
+ + +
+
+
+
+ )} + + {/* Step 2: Payment */} + {step === 2 && ( + + +

+ Payment Details +

+
{ e.preventDefault(); if (validatePayment()) setStep(3); }}> +
+
+ + updatePayment("cardholder", e.target.value)} placeholder="John Doe" /> +
+
+ +
+ updatePayment("cardNumber", e.target.value)} placeholder="4242 4242 4242 4242" className={\`\${errors.cardNumber ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.cardNumber && payment.cardNumber.replace(/\s/g, "").length >= 16 && } +
+ {errors.cardNumber &&

{errors.cardNumber}

} +
+
+
+ +
+ updatePayment("expiry", e.target.value)} placeholder="MM/YY" className={\`\${errors.expiry ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.expiry && payment.expiry.trim() && } +
+ {errors.expiry &&

{errors.expiry}

} +
+
+ +
+ updatePayment("cvv", e.target.value)} placeholder="123" className={\`\${errors.cvv ? "border-red-500 focus-visible:ring-red-500" : ""}\`} /> + {!errors.cvv && payment.cvv.length >= 3 && } +
+ {errors.cvv &&

{errors.cvv}

} +
+
+
+
+ Your payment information is encrypted and secure. +
+
+ + +
+
+
+
+ )} + + {/* Step 3: Review */} + {step === 3 && ( + + +

+ Review Your Order +

+ + {/* Items */} +
+ {items.map((item) => ( +
+
+
+

{item.name}

+

Qty: {item.quantity} | {item.size} | {item.color}

+
+ \${(item.price * item.quantity).toFixed(2)} +
+ ))} +
+ + + + {/* Shipping Address */} +
+

Shipping Address

+

+ {shipping.firstName} {shipping.lastName}
+ {shipping.address}
+ {shipping.city}, {shipping.state} {shipping.zip} +

+
+ + {/* Payment */} +
+

Payment Method

+
+ + Card ending in {payment.cardNumber.slice(-4) || "****"} +
+
+ +
+ + +
+ + + )} +
+ + {/* Right Sidebar — Order Summary */} +
+ + +

Order Summary

+
+ {items.map((item) => ( +
+ {item.name} x{item.quantity} + \${(item.price * item.quantity).toFixed(2)} +
+ ))} +
+ +
+
+ Subtotal + \${total.toFixed(2)} +
+
+ Shipping + + {shippingCost === 0 ? "Free" : \`\$\${shippingCost.toFixed(2)}\`} + +
+
+ Tax + \${tax.toFixed(2)} +
+
+ +
+ Total + \${grandTotal.toFixed(2)} +
+
+
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx new file mode 100644 index 0000000..16c6f4b --- /dev/null +++ b/src/pages/Index.tsx @@ -0,0 +1,304 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { + Truck, RotateCcw, ShieldCheck, Star, ArrowRight, Heart, + ChevronRight, Mail, Sparkles, Check +} from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +const featuredProducts = [ + { id: 1, name: "Classic Leather Jacket", price: 189, originalPrice: 249, rating: 5, sale: true, gradient: "from-rose-100 to-stone-200", image: "" }, + { id: 2, name: "Silk Evening Dress", price: 245, originalPrice: null, rating: 5, sale: false, gradient: "from-amber-100 to-orange-200", image: "" }, + { id: 3, name: "Cashmere Knit Sweater", price: 128, originalPrice: null, rating: 4, sale: false, gradient: "from-stone-100 to-stone-300", image: "" }, + { id: 4, name: "Linen Wide-Leg Trousers", price: 95, originalPrice: 135, rating: 4, sale: true, gradient: "from-rose-50 to-amber-100", image: "" }, + { id: 5, name: "Italian Leather Handbag", price: 320, originalPrice: null, rating: 5, sale: false, gradient: "from-stone-200 to-rose-100", image: "" }, + { id: 6, name: "Merino Wool Blazer", price: 165, originalPrice: null, rating: 4, sale: false, gradient: "from-amber-50 to-stone-200", image: "" }, +]; + +const categories = [ + { name: "Women", gradient: "from-stone-700 to-stone-900", count: 124 }, + { name: "Men", gradient: "from-rose-700 to-rose-900", count: 86 }, + { name: "Accessories", gradient: "from-amber-700 to-amber-900", count: 53 }, + { name: "Footwear", gradient: "from-stone-600 to-rose-800", count: 97 }, +]; + +export default function IndexPage() { + const [email, setEmail] = useState(""); + const [subscribed, setSubscribed] = useState(false); + + const renderStars = (count: number) => + Array.from({ length: 5 }, (_, i) => ( + + )); + + return ( +
+
+ + {/* ── HERO ── */} +
+ {/* Floating gradient blurs */} +
+
+
+
+ {/* Dot grid pattern overlay */} +
+
+
+ + Summer Sale — Up to 40% Off + +

+ Elevate Your Everyday Style +

+

+ Discover curated collections of premium fashion, accessories, and lifestyle essentials crafted for the modern individual. +

+
+ + +
+
+ Free Shipping Over $100 + 30-Day Returns + Secure Checkout +
+
+ {/* Featured product card floating on right */} +
+ +
+
+
+
+
Featured
+ Trending +
+ +

{featuredProducts[0].name}

+

\${featuredProducts[0].price}

+
+ +
+
+
+ + {/* ── FEATURED PRODUCTS ── */} +
+
+
+
+

Featured Collection

+

Handpicked pieces for the season

+
+ + View All + +
+
+ {featuredProducts.map((product, i) => ( + + +
+
+
+
+
+ Product Image +
+ {product.sale && ( + Sale + )} + +
+
+ +

{product.name}

+
+
{renderStars(product.rating)}
+
+ \${product.price} + {product.originalPrice && ( + \${product.originalPrice} + )} +
+
{[...Array(5)].map((_, j) => )}
(128)
+ {i < 2 && Only 3 left} +
+ + + ))} +
+
+ +
+
+
+ + {/* ── CATEGORIES ── */} +
+
+

Shop by Category

+
+ {categories.map((cat) => ( + +
+
+
+

{cat.name}

+

{cat.count} items

+ + Shop Now + +
+ + ))} +
+
+
+ + {/* ── TRUST BAR ── */} +
+
+ {[ + { icon: Truck, title: "Free Shipping", desc: "On orders over $100" }, + { icon: RotateCcw, title: "Easy Returns", desc: "30-day return policy" }, + { icon: ShieldCheck, title: "Secure Payment", desc: "SSL encrypted checkout" }, + { icon: Sparkles, title: "Premium Quality", desc: "Handpicked materials" }, + ].map((item) => ( +
+
+ +
+

{item.title}

+

{item.desc}

+
+ ))} +
+
+ + {/* ── THE COLLECTION LOOKBOOK ── */} +
+
+

The Collection

+
+
+
+
+
+ New Season +

Spring Essentials

+
+
+
+
+
+
+
+ Trending +

Accessories

+
+
+
+
+
+
+
+ Popular +

Best Sellers

+
+
+
+
+
+
+
+ Exclusive +

Limited Edition

+
+
+
+
+
+
+ + {/* ── BRAND STORY ── */} +
+
+

Our Story

+

Crafted with Care Since 2020

+

Every piece in our collection is thoughtfully designed and ethically sourced. We believe in quality over quantity, creating timeless pieces that tell a story.

+ +
+
+ + {/* ── NEWSLETTER ── */} +
+
+ +

Stay in the Loop

+

Get early access to new arrivals, exclusive deals, and styling tips.

+ {subscribed ? ( +
+
+ You're subscribed! Check your inbox. +
+
+ ) : ( +
{ e.preventDefault(); if (email) { setSubscribed(true); toast.success("Subscribed!"); } }} className="mt-8 flex flex-col sm:flex-row gap-3 max-w-md mx-auto"> + setEmail(e.target.value)} + className="bg-white/10 border-white/20 text-white placeholder:text-stone-400 rounded-full" + /> + +
+ )} +

Join 15,000+ subscribers. No spam, unsubscribe anytime.

+
+
+ + {/* ── INSTAGRAM GALLERY ── */} +
+
+

Follow @StyleHaus

+

Tag us in your looks for a chance to be featured

+
+ {["from-rose-100 to-rose-200", "from-stone-100 to-stone-200", "from-amber-100 to-amber-200", + "from-rose-200 to-stone-100", "from-stone-200 to-amber-100", "from-amber-200 to-rose-100" + ].map((grad, i) => ( +
+
+ +
+
+ ))} +
+
+
+ +
+
+ ); +} diff --git a/src/pages/OrderConfirmation.tsx b/src/pages/OrderConfirmation.tsx new file mode 100644 index 0000000..39a86d8 --- /dev/null +++ b/src/pages/OrderConfirmation.tsx @@ -0,0 +1,74 @@ +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Separator } from "@/components/ui/separator"; +import { CheckCircle, Package, ArrowRight } from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +export default function OrderConfirmationPage() { + const orderNumber = "ORD-2024-" + Math.floor(1000 + Math.random() * 9000); + + return ( +
+
+ +
+
+ +
+ +

Order Confirmed!

+

Order number: {orderNumber}

+

Thank you for your purchase. You will receive a confirmation email shortly.

+ + + +

Order Summary

+
+
+ Classic Leather Jacket x1 + $189.00 +
+
+ Silk Evening Dress x1 + $245.00 +
+
+ +
+
+ Subtotal + $434.00 +
+
+ Shipping + Free +
+
+ Tax + $34.72 +
+
+ +
+ Total + $468.72 +
+
+
+ +
+ + +
+
+ +
+
+ ); +} diff --git a/src/pages/ProductDetail.tsx b/src/pages/ProductDetail.tsx new file mode 100644 index 0000000..019656c --- /dev/null +++ b/src/pages/ProductDetail.tsx @@ -0,0 +1,307 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; +import { Separator } from "@/components/ui/separator"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { + Star, Heart, Minus, Plus, ChevronRight, Truck, RotateCcw, ShieldCheck, ShoppingBag, Flame, ZoomIn +} from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; +import { useCart } from "@/lib/cart-context"; + +const thumbnails = [ + "from-rose-100 to-stone-200", + "from-stone-100 to-rose-100", + "from-amber-100 to-stone-100", + "from-stone-200 to-amber-100", +]; + +const colors = [ + { name: "Black", class: "bg-stone-900" }, + { name: "Cognac", class: "bg-amber-700" }, + { name: "Rose", class: "bg-rose-400" }, + { name: "Stone", class: "bg-stone-400" }, +]; + +const sizes = ["XS", "S", "M", "L", "XL", "XXL"]; + +const reviews = [ + { name: "Sarah M.", date: "2 weeks ago", rating: 5, text: "Absolutely stunning quality. The leather is soft yet durable and the fit is perfect. Worth every penny!", avatar: "SM" }, + { name: "James K.", date: "1 month ago", rating: 4, text: "Great jacket overall. Runs slightly large so consider sizing down. The color is rich and beautiful.", avatar: "JK" }, + { name: "Emily R.", date: "1 month ago", rating: 5, text: "This is my third purchase from StyleHaus and they never disappoint. The craftsmanship is exceptional.", avatar: "ER" }, +]; + +const relatedProducts = [ + { id: 10, name: "Satin Blouse", price: 89, gradient: "from-rose-50 to-rose-100", rating: 4 }, + { id: 11, name: "Tailored Wool Coat", price: 385, gradient: "from-stone-200 to-stone-400", rating: 5 }, + { id: 7, name: "Organic Cotton Tee", price: 45, gradient: "from-rose-100 to-rose-200", rating: 4 }, + { id: 8, name: "Velvet Midi Skirt", price: 115, gradient: "from-stone-300 to-stone-100", rating: 5 }, +]; + +export default function ProductDetailPage() { + const [selectedImage, setSelectedImage] = useState(0); + const [selectedColor, setSelectedColor] = useState("Black"); + const [selectedSize, setSelectedSize] = useState("M"); + const [quantity, setQuantity] = useState(1); + const [wishlisted, setWishlisted] = useState(false); + const { addItem } = useCart(); + + const renderStars = (count: number) => + Array.from({ length: 5 }, (_, i) => ( + + )); + + const handleAddToCart = () => { + addItem({ + id: "leather-jacket-1", + name: "Classic Leather Jacket", + price: 189, + quantity, + size: selectedSize, + color: selectedColor, + image: "", + }); + toast.success("Added to cart!"); + }; + + return ( +
+
+ +
+ {/* Breadcrumb */} + + + {/* Product Layout */} +
+ {/* Left — Images */} +
+ + +
+
+
+
+ Product Image +
+ +
+
+ + +
+
+
+
+ Product Image +
+ +
+
+ {thumbnails.map((grad, i) => ( +
+
+ + {/* Right — Details */} +
+
+ Best Seller +

Classic Leather Jacket

+
+
{renderStars(5)}
+ (128 reviews) +
+
+ +
+ $189 + $249 + -24% +
+ +

Timeless silhouette crafted from premium full-grain leather. Features a tailored fit with minimal hardware for a clean, modern aesthetic.

+ + + + {/* Color Selector */} +
+

Color: {selectedColor}

+
+ {colors.map((c) => ( +
+
+ + {/* Size Selector */} +
+
+

Size

+ +
+
+ {sizes.map((s) => ( + + ))} +
+
+ + {/* Scarcity Signal */} +
Selling Fast — Only 5 left in stock
+ + {/* Quantity + Add to Cart */} +
+
+ + {quantity} + +
+ + +
+ + {/* Trust */} +
+ {[ + { icon: Truck, text: "Free Shipping" }, + { icon: RotateCcw, text: "Easy Returns" }, + { icon: ShieldCheck, text: "Secure Payment" }, + ].map((item) => ( +
+ + {item.text} +
+ ))} +
+
+
+ + {/* Tabs */} + + + Description + Reviews (128) + Shipping Info + + +

Our Classic Leather Jacket is the cornerstone of any wardrobe. Made from hand-selected full-grain leather that develops a beautiful patina over time, this jacket is designed to be your companion for years to come. The tailored fit flatters every body type while allowing comfortable layering.

+
    +
  • Premium full-grain leather construction
  • +
  • YKK zippers with custom pulls
  • +
  • Interior and exterior pockets
  • +
  • Fully lined with satin finish
  • +
  • Available in 4 colors
  • +
+
+ +
+
4.8
+
+
{[...Array(5)].map((_, i) => )}
+

Based on 128 reviews

+
+
+ {reviews.map((review, i) => ( +
+
{review.avatar}
+
+
+

{review.name}

+ Verified Purchase + {review.date} +
+
{renderStars(review.rating)}
+

{review.text}

+
+
+ ))} + +
+ +
+
+ +
+

Free Standard Shipping

+

On all orders over $100. Delivery in 5-7 business days.

+
+
+
+ +
+

30-Day Returns

+

Not satisfied? Return within 30 days for a full refund.

+
+
+
+
+
+ + {/* You Might Also Like */} +
+

You Might Also Like

+
+ {relatedProducts.map((product) => ( + + +
+
+
+
+ Image +
+ +

{product.name}

+
{renderStars(product.rating)}
+

\${product.price}

+
+ + + ))} +
+
+
+ +
+
+ ); +} diff --git a/src/pages/Products.tsx b/src/pages/Products.tsx new file mode 100644 index 0000000..5fdbdea --- /dev/null +++ b/src/pages/Products.tsx @@ -0,0 +1,303 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { + Dialog, DialogContent, DialogTrigger, +} from "@/components/ui/dialog"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Star, Heart, SlidersHorizontal, ChevronLeft, ChevronRight, Grid3X3, LayoutList, X, ShoppingBag +} from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +const allProducts = [ + { id: 1, name: "Classic Leather Jacket", price: 189, originalPrice: 249, rating: 5, badge: "Sale", gradient: "from-rose-100 to-stone-200", brand: "StyleHaus", size: ["S","M","L","XL"], color: "black" }, + { id: 2, name: "Silk Evening Dress", price: 245, originalPrice: null, rating: 5, badge: "New", gradient: "from-amber-100 to-orange-200", brand: "Luxe", size: ["XS","S","M"], color: "rose" }, + { id: 3, name: "Cashmere Knit Sweater", price: 128, originalPrice: null, rating: 4, badge: null, gradient: "from-stone-100 to-stone-300", brand: "StyleHaus", size: ["S","M","L"], color: "stone" }, + { id: 4, name: "Linen Wide-Leg Trousers", price: 95, originalPrice: 135, rating: 4, badge: "Sale", gradient: "from-rose-50 to-amber-100", brand: "Atelier", size: ["S","M","L","XL"], color: "cream" }, + { id: 5, name: "Italian Leather Handbag", price: 320, originalPrice: null, rating: 5, badge: "New", gradient: "from-stone-200 to-rose-100", brand: "Luxe", size: ["One Size"], color: "brown" }, + { id: 6, name: "Merino Wool Blazer", price: 165, originalPrice: null, rating: 4, badge: null, gradient: "from-amber-50 to-stone-200", brand: "StyleHaus", size: ["S","M","L","XL","XXL"], color: "navy" }, + { id: 7, name: "Organic Cotton Tee", price: 45, originalPrice: null, rating: 4, badge: null, gradient: "from-rose-100 to-rose-200", brand: "Atelier", size: ["XS","S","M","L","XL"], color: "white" }, + { id: 8, name: "Velvet Midi Skirt", price: 115, originalPrice: 155, rating: 5, badge: "Sale", gradient: "from-stone-300 to-stone-100", brand: "Luxe", size: ["XS","S","M","L"], color: "rose" }, + { id: 9, name: "Suede Ankle Boots", price: 198, originalPrice: null, rating: 5, badge: "New", gradient: "from-amber-200 to-stone-200", brand: "StyleHaus", size: ["36","37","38","39","40","41"], color: "brown" }, + { id: 10, name: "Satin Blouse", price: 89, originalPrice: null, rating: 4, badge: null, gradient: "from-rose-50 to-rose-100", brand: "Atelier", size: ["XS","S","M","L"], color: "cream" }, + { id: 11, name: "Tailored Wool Coat", price: 385, originalPrice: null, rating: 5, badge: null, gradient: "from-stone-200 to-stone-400", brand: "Luxe", size: ["S","M","L","XL"], color: "stone" }, + { id: 12, name: "Pearl Drop Earrings", price: 68, originalPrice: null, rating: 4, badge: "New", gradient: "from-amber-100 to-rose-100", brand: "Luxe", size: ["One Size"], color: "gold" }, +]; + +const colorOptions = [ + { name: "Black", value: "black", class: "bg-stone-900" }, + { name: "Rose", value: "rose", class: "bg-rose-400" }, + { name: "Stone", value: "stone", class: "bg-stone-400" }, + { name: "Cream", value: "cream", class: "bg-amber-100 border border-stone-200" }, + { name: "Brown", value: "brown", class: "bg-amber-700" }, + { name: "Navy", value: "navy", class: "bg-blue-900" }, + { name: "White", value: "white", class: "bg-white border border-stone-200" }, + { name: "Gold", value: "gold", class: "bg-amber-400" }, +]; + +export default function ProductsPage() { + const [sort, setSort] = useState("featured"); + const [page, setPage] = useState(1); + const [selectedBrands, setSelectedBrands] = useState([]); + const [selectedColors, setSelectedColors] = useState([]); + const [priceRange, setPriceRange] = useState<[number, number]>([0, 500]); + const [showFilters, setShowFilters] = useState(false); + const [wishlist, setWishlist] = useState([]); + const [quickView, setQuickView] = useState(null); + const [selectedSize, setSelectedSize] = useState(""); + + const toggleBrand = (b: string) => + setSelectedBrands((prev) => prev.includes(b) ? prev.filter((x) => x !== b) : [...prev, b]); + + const toggleColor = (c: string) => + setSelectedColors((prev) => prev.includes(c) ? prev.filter((x) => x !== c) : [...prev, c]); + + const toggleWishlist = (id: number) => { + const isAdding = !wishlist.includes(id); + setWishlist((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]); + if (isAdding) toast.success("Added to wishlist!"); + }; + + const filtered = allProducts.filter((p) => { + if (selectedBrands.length && !selectedBrands.includes(p.brand)) return false; + if (selectedColors.length && !selectedColors.includes(p.color)) return false; + if (p.price < priceRange[0] || p.price > priceRange[1]) return false; + return true; + }); + + const renderStars = (count: number) => + Array.from({ length: 5 }, (_, i) => ( + + )); + + const Sidebar = () => ( +
+
+

Price Range

+
+ setPriceRange([+e.target.value, priceRange[1]])} className="w-20 h-8 text-xs" /> + to + setPriceRange([priceRange[0], +e.target.value])} className="w-20 h-8 text-xs" /> +
+
+ +
+

Brand

+
+ {["StyleHaus", "Luxe", "Atelier"].map((b) => ( + + ))} +
+
+ +
+

Color

+
+ {colorOptions.map((c) => ( +
+
+ + +
+ ); + + return ( +
+
+ +
+ {/* Top Bar */} +
+
+

All Products

+

{filtered.length} products found

+
+
+ + +
+
+ + {/* Active Filters */} + {(selectedBrands.length > 0 || selectedColors.length > 0) && ( +
+ {selectedBrands.map((b) => ( + toggleBrand(b)}> + {b} + + ))} + {selectedColors.map((c) => ( + toggleColor(c)}> + {c} + + ))} +
+ )} + +
+ {/* Sidebar */} + + + {/* Product Grid */} +
+
+ {filtered.map((product, i) => ( + { setQuickView(open ? product.id : null); if (!open) setSelectedSize(""); }}> + +
+
+ +

{product.name}

+
+
{renderStars(product.rating)}
+
+ \${product.price} + {product.originalPrice && ( + \${product.originalPrice} + )} +
+
{[...Array(5)].map((_, j) => )}
(128)
+ {i < 2 && Only 3 left} +
+ + + + +
+ {/* Product Image */} +
+
+
+
+
Product Image
+ {product.badge && ( + + {product.badge} + + )} +
+ {/* Product Details */} +
+

{product.brand}

+

{product.name}

+
{renderStars(product.rating)}
+
+ \${product.price} + {product.originalPrice && ( + \${product.originalPrice} + )} +
+

Premium quality crafted with the finest materials. Designed for comfort and style that lasts.

+ +
+

Size

+
+ {product.size.map((s) => ( + + ))} +
+
+
+ + +
+
+
+ + + ))} +
+ + {/* Pagination */} +
+ + {[1, 2, 3].map((p) => ( + + ))} + +
+
+
+
+ +
+
+ ); +} diff --git a/src/pages/Search.tsx b/src/pages/Search.tsx new file mode 100644 index 0000000..7a7e421 --- /dev/null +++ b/src/pages/Search.tsx @@ -0,0 +1,137 @@ +import { useState } from "react"; +import { toast } from "sonner"; +import { Link } from "react-router-dom"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Input } from "@/components/ui/input"; +import { Search as SearchIcon, Star, Heart, X, Sparkles } from "lucide-react"; +import Header from "@/components/Header"; +import Footer from "@/components/Footer"; + +const allProducts = [ + { id: 1, name: "Classic Leather Jacket", price: 189, gradient: "from-rose-100 to-stone-200", rating: 5, category: "Outerwear" }, + { id: 2, name: "Silk Evening Dress", price: 245, gradient: "from-amber-100 to-orange-200", rating: 5, category: "Dresses" }, + { id: 3, name: "Cashmere Knit Sweater", price: 128, gradient: "from-stone-100 to-stone-300", rating: 4, category: "Knitwear" }, + { id: 4, name: "Linen Wide-Leg Trousers", price: 95, gradient: "from-rose-50 to-amber-100", rating: 4, category: "Bottoms" }, + { id: 5, name: "Italian Leather Handbag", price: 320, gradient: "from-stone-200 to-rose-100", rating: 5, category: "Accessories" }, + { id: 6, name: "Merino Wool Blazer", price: 165, gradient: "from-amber-50 to-stone-200", rating: 4, category: "Outerwear" }, + { id: 7, name: "Organic Cotton Tee", price: 45, gradient: "from-rose-100 to-rose-200", rating: 4, category: "Tops" }, + { id: 8, name: "Velvet Midi Skirt", price: 115, gradient: "from-stone-300 to-stone-100", rating: 5, category: "Bottoms" }, + { id: 9, name: "Suede Ankle Boots", price: 198, gradient: "from-amber-200 to-stone-200", rating: 5, category: "Footwear" }, + { id: 10, name: "Satin Blouse", price: 89, gradient: "from-rose-50 to-rose-100", rating: 4, category: "Tops" }, + { id: 11, name: "Tailored Wool Coat", price: 385, gradient: "from-stone-200 to-stone-400", rating: 5, category: "Outerwear" }, + { id: 12, name: "Pearl Drop Earrings", price: 68, gradient: "from-amber-100 to-rose-100", rating: 4, category: "Accessories" }, +]; + +const filterChips = ["All", "Outerwear", "Dresses", "Tops", "Bottoms", "Accessories", "Footwear", "Knitwear"]; + +export default function SearchPage() { + const [query, setQuery] = useState(""); + const [activeFilter, setActiveFilter] = useState("All"); + + const filtered = allProducts.filter((p) => { + const matchesQuery = query.length === 0 || p.name.toLowerCase().includes(query.toLowerCase()); + const matchesFilter = activeFilter === "All" || p.category === activeFilter; + return matchesQuery && matchesFilter; + }); + + const renderStars = (count: number) => + Array.from({ length: 5 }, (_, i) => ( + + )); + + return ( +
+
+ +
+ {/* Search Input */} +
+
+ + setQuery(e.target.value)} + autoFocus + className="pl-12 pr-10 h-14 text-lg rounded-full border-stone-200 focus-visible:ring-rose-500 focus-visible:ring-2 focus-visible:border-rose-400 transition-shadow" + /> + {query && ( + + )} +
+
+ + {/* Results heading */} + {query && ( +

+ {filtered.length} result{filtered.length !== 1 ? "s" : ""} for "{query}" +

+ )} + + {/* Filter Chips */} +
+ {filterChips.map((chip) => ( + + ))} +
+ + {/* Results */} + {filtered.length > 0 ? ( +
+ {filtered.map((product) => ( + + +
+
+
+
+
Image
+ +
+ +

{product.name}

+
{renderStars(product.rating)}
+

\${product.price}

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

No results found

+

+ We couldn't find anything matching your search. Try different keywords or browse our categories. +

+
+ Summer Dresses + Leather Jackets + Cashmere + Accessories +
+
+ )} +
+ +
+ ); +}