Seed: ecommerce template (24 files)

This commit is contained in:
2026-07-23 22:09:23 +00:00
parent 81aee5f8c0
commit 5ee91ec105
+252
View File
@@ -0,0 +1,252 @@
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 { toast } from "sonner";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
import { useAuth } from "@/hooks/use-tygarun";
import { Label } from "@/components/ui/label";
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 },
];
function AccountSignIn() {
const { signin, loading, error } = useAuth();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const handleSignin = async (e: React.FormEvent) => {
e.preventDefault();
try { await signin(email, password); } catch { toast.error("Sign in failed."); }
};
return (
<div className="min-h-screen bg-stone-50 text-stone-900 antialiased">
<Header />
<div className="mx-auto max-w-sm px-6 pt-32 pb-20">
<h1 className="text-2xl font-bold mb-6">Sign in to your account</h1>
<form onSubmit={handleSignin} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="account-email">Email</Label>
<Input id="account-email" type="email" required value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />
</div>
<div className="space-y-2">
<Label htmlFor="account-password">Password</Label>
<Input id="account-password" type="password" required value={password} onChange={(e) => setPassword(e.target.value)} placeholder="••••••••" />
</div>
{error && <p className="text-sm text-rose-600">{error}</p>}
<Button type="submit" disabled={loading} className="w-full rounded-full bg-rose-600 hover:bg-rose-700">
{loading ? "Signing in…" : "Sign In"}
</Button>
</form>
</div>
<Footer />
</div>
);
}
export default function AccountPage() {
const { user, isSignedIn, connected, signout } = useAuth();
const [profile, setProfile] = useState({
firstName: "Alexandra",
lastName: "Chen",
email: "alex.chen@email.com",
phone: "+1 (555) 234-5678",
});
// When tyga.run is connected, require sign-in. In demo mode the account
// preview stays walkable with seed data.
if (connected && !isSignedIn) return <AccountSignIn />;
const displayFirst = user?.firstName || profile.firstName;
const displayEmail = user?.email || profile.email;
const renderStars = (count: number) =>
Array.from({ length: 5 }, (_, i) => (
<Star key={i} className={`h-3 w-3 \${i < count ? "fill-amber-400 text-amber-400" : "text-stone-300"}`} />
));
return (
<div className="min-h-screen bg-stone-50 text-stone-900 antialiased">
<Header />
<div className="mx-auto max-w-5xl px-6 pt-28 pb-20">
{/* Profile Header */}
<div className="flex items-center gap-4 mb-10">
<Avatar className="h-16 w-16">
<AvatarFallback className="bg-rose-100 text-rose-700 text-xl font-bold">AC</AvatarFallback>
</Avatar>
<div className="flex-1">
<h1 className="text-2xl font-bold animate-fade-up">Welcome back, {displayFirst}</h1>
<p className="text-stone-500 text-sm">{displayEmail}</p>
</div>
{isSignedIn && (
<Button variant="outline" className="rounded-full" onClick={() => signout()}>
Sign Out
</Button>
)}
</div>
<Tabs defaultValue="orders" className="space-y-8">
<TabsList className="bg-white border shadow-sm rounded-full p-1">
<TabsTrigger value="orders" className="rounded-full gap-1.5 text-sm"><Package className="h-4 w-4" /> Orders</TabsTrigger>
<TabsTrigger value="profile" className="rounded-full gap-1.5 text-sm"><User className="h-4 w-4" /> Profile</TabsTrigger>
<TabsTrigger value="addresses" className="rounded-full gap-1.5 text-sm"><MapPin className="h-4 w-4" /> Addresses</TabsTrigger>
<TabsTrigger value="wishlist" className="rounded-full gap-1.5 text-sm"><Heart className="h-4 w-4" /> Wishlist</TabsTrigger>
</TabsList>
{/* Orders Tab */}
<TabsContent value="orders">
<Card>
<CardContent className="p-0">
<Table>
<TableHeader>
<TableRow className="bg-stone-50">
<TableHead className="font-semibold">Order</TableHead>
<TableHead className="font-semibold">Date</TableHead>
<TableHead className="font-semibold">Status</TableHead>
<TableHead className="font-semibold text-right">Total</TableHead>
<TableHead className="font-semibold text-right">Action</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{orders.map((order) => (
<TableRow key={order.id}>
<TableCell className="font-medium">{order.id}</TableCell>
<TableCell className="text-stone-500">{order.date}</TableCell>
<TableCell>
<Badge className={`\${order.statusColor} border-0 font-medium`}>{order.status}</Badge>
</TableCell>
<TableCell className="text-right font-medium">\${order.total.toFixed(2)}</TableCell>
<TableCell className="text-right">
<Button variant="ghost" size="sm" className="text-rose-600 hover:text-rose-700">
<Eye className="h-4 w-4 mr-1" /> View
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
{/* Profile Tab */}
<TabsContent value="profile">
<Card>
<CardContent className="p-6 sm:p-8">
<h2 className="text-xl font-bold mb-6">Personal Information</h2>
<div className="grid sm:grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-stone-700 mb-1.5 block">First Name</label>
<Input value={profile.firstName} onChange={(e) => setProfile({ ...profile, firstName: e.target.value })} />
</div>
<div>
<label className="text-sm font-medium text-stone-700 mb-1.5 block">Last Name</label>
<Input value={profile.lastName} onChange={(e) => setProfile({ ...profile, lastName: e.target.value })} />
</div>
<div>
<label className="text-sm font-medium text-stone-700 mb-1.5 block">Email</label>
<Input type="email" value={profile.email} onChange={(e) => setProfile({ ...profile, email: e.target.value })} />
</div>
<div>
<label className="text-sm font-medium text-stone-700 mb-1.5 block">Phone</label>
<Input value={profile.phone} onChange={(e) => setProfile({ ...profile, phone: e.target.value })} />
</div>
</div>
<Button className="mt-6 bg-stone-900 hover:bg-stone-800 rounded-full active:scale-[0.98] transition-all">Save Changes</Button>
</CardContent>
</Card>
</TabsContent>
{/* Addresses Tab */}
<TabsContent value="addresses">
<div className="grid sm:grid-cols-2 gap-6">
<Card className="relative">
<CardContent className="p-6">
<div className="flex items-center justify-between mb-4">
<Badge className="bg-stone-900 text-white border-0">Default</Badge>
<Button variant="ghost" size="sm"><Edit className="h-4 w-4" /></Button>
</div>
<h3 className="font-semibold mb-1">Home</h3>
<p className="text-sm text-stone-600 leading-relaxed">
Alexandra Chen<br />
456 Park Avenue, Apt 12B<br />
New York, NY 10022
</p>
</CardContent>
</Card>
<Card>
<CardContent className="p-6">
<div className="flex items-center justify-between mb-4">
<Badge variant="secondary">Office</Badge>
<Button variant="ghost" size="sm"><Edit className="h-4 w-4" /></Button>
</div>
<h3 className="font-semibold mb-1">Office</h3>
<p className="text-sm text-stone-600 leading-relaxed">
Alexandra Chen<br />
789 Madison Ave, Floor 15<br />
New York, NY 10065
</p>
</CardContent>
</Card>
</div>
<Button variant="outline" className="mt-6 rounded-full">+ Add New Address</Button>
</TabsContent>
{/* Wishlist Tab */}
<TabsContent value="wishlist">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 stagger">
{wishlistItems.map((item) => (
<Card key={item.id} className="overflow-hidden border-0 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300">
<div className={`aspect-[3/4] bg-gradient-to-br \${item.gradient} flex items-center justify-center text-stone-400 text-xs relative overflow-hidden`}>
<div className="absolute inset-0" style={{ backgroundImage: "radial-gradient(circle, rgba(0,0,0,0.06) 1px, transparent 1px)", backgroundSize: "16px 16px" }} />
<div className="absolute top-1/4 -left-8 h-24 w-24 rounded-full bg-white/30 blur-2xl" />
<span className="relative">Image</span>
</div>
<CardContent className="p-4 space-y-2">
<h3 className="font-medium text-sm">{item.name}</h3>
<div className="flex items-center gap-0.5">{renderStars(item.rating)}</div>
<p className="font-semibold">\${item.price}</p>
<div className="flex gap-2 pt-1">
<Button size="sm" className="flex-1 bg-stone-900 hover:bg-stone-800 text-xs rounded-full">
<ShoppingBag className="h-3 w-3 mr-1" /> Add to Cart
</Button>
<Button variant="outline" size="icon" className="h-8 w-8 rounded-full text-stone-400 hover:text-rose-500">
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
</CardContent>
</Card>
))}
</div>
</TabsContent>
</Tabs>
</div>
<Footer />
</div>
);
}