Seed: real-estate template (18 files)

This commit is contained in:
2026-07-23 22:11:44 +00:00
parent e0c9ead955
commit 719dc8064e
+113
View File
@@ -0,0 +1,113 @@
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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Search, Bed, Bath, Maximize, MapPin, Heart, Home } from "lucide-react";
import Header from "@/components/Header";
import Footer from "@/components/Footer";
const listings = [
{ id: 1, name: "Skyline Penthouse", price: "$2,100,000", beds: "3", baths: "3", sqft: "2,800", addr: "200 Central Park W, NYC", status: "For Sale", gradient: "from-emerald-800/40 to-stone-700/40" },
{ id: 2, name: "Harbor View Loft", price: "$3,800/mo", beds: "2", baths: "2", sqft: "1,600", addr: "15 Harbor Ln, Boston", status: "For Rent", gradient: "from-stone-700/40 to-amber-800/40" },
{ id: 3, name: "Sunset Ridge Estate", price: "$3,950,000", beds: "5", baths: "4", sqft: "5,200", addr: "88 Ridge Rd, Malibu", status: "For Sale", gradient: "from-amber-800/40 to-emerald-900/40" },
{ id: 4, name: "Maple Grove House", price: "$780,000", beds: "4", baths: "3", sqft: "3,000", addr: "22 Maple St, Denver", status: "For Sale", gradient: "from-emerald-900/40 to-stone-800/40" },
{ id: 5, name: "Urban Studio Plus", price: "$2,200/mo", beds: "1", baths: "1", sqft: "850", addr: "45 Market St, SF", status: "For Rent", gradient: "from-stone-800/40 to-emerald-800/40" },
{ id: 6, name: "Colonial Revival", price: "$1,250,000", beds: "5", baths: "4", sqft: "4,100", addr: "10 Elm Ave, Greenwich", status: "For Sale", gradient: "from-emerald-700/40 to-amber-900/40" },
];
export default function PropertiesPage() {
const [liked, setLiked] = useState<Set<number>>(new Set());
const toggleLike = (id: number) => {
setLiked((prev) => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
};
return (
<div className="min-h-screen bg-background text-foreground antialiased">
<Header />
{/* ── HERO ── */}
<section className="pt-32 pb-12 bg-gradient-to-b from-stone-950 via-emerald-950/20 to-background">
<div className="mx-auto max-w-7xl px-6 text-center animate-fade-up">
<Badge className="bg-emerald-600/20 text-emerald-400 border-emerald-600/30 mb-4">Our Listings</Badge>
<h1 className="text-4xl sm:text-5xl font-bold text-white mb-4">Browse Properties</h1>
<p className="text-stone-400 max-w-lg mx-auto">Find the perfect property from our extensive portfolio of premium listings.</p>
</div>
</section>
{/* ── FILTERS ── */}
<section className="py-8 bg-background border-b border-stone-200 dark:border-stone-800 sticky top-[72px] z-40 backdrop-blur-xl bg-background/90">
<div className="mx-auto max-w-7xl px-6">
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
<Input placeholder="Location..." className="rounded-xl" />
<Select>
<SelectTrigger className="rounded-xl"><SelectValue placeholder="Property Type" /></SelectTrigger>
<SelectContent>
{["House", "Apartment", "Condo", "Villa"].map((t) => <SelectItem key={t} value={t.toLowerCase()}>{t}</SelectItem>)}
</SelectContent>
</Select>
<Select>
<SelectTrigger className="rounded-xl"><SelectValue placeholder="Price Range" /></SelectTrigger>
<SelectContent>
{["$0-$250K", "$250K-$500K", "$500K-$1M", "$1M+"].map((t) => <SelectItem key={t} value={t}>{t}</SelectItem>)}
</SelectContent>
</Select>
<Select>
<SelectTrigger className="rounded-xl"><SelectValue placeholder="Bedrooms" /></SelectTrigger>
<SelectContent>
{["1+", "2+", "3+", "4+", "5+"].map((t) => <SelectItem key={t} value={t}>{t} Beds</SelectItem>)}
</SelectContent>
</Select>
<Button className="bg-emerald-600 hover:bg-emerald-700 text-white rounded-xl active:scale-[0.97] transition-all">
<Search className="h-4 w-4 mr-2" />Search
</Button>
</div>
</div>
</section>
{/* ── GRID ── */}
<section className="py-16 bg-background">
<div className="mx-auto max-w-7xl px-6">
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{listings.map((p, i) => (
<Card key={p.id} className="overflow-hidden border-stone-200 dark:border-stone-800 bg-white dark:bg-stone-900/50 backdrop-blur rounded-2xl shadow-md hover:shadow-xl hover:-translate-y-1 transition-all duration-300 group animate-fade-up" style={{ animationDelay: `\${i * 80}ms` }}>
<div className={`h-56 bg-gradient-to-br \${p.gradient} flex items-center justify-center relative`}>
<Home className="h-12 w-12 text-white/20" />
<Badge className="absolute top-4 left-4 bg-emerald-600 text-white border-0">{p.status}</Badge>
<button onClick={() => toggleLike(p.id)} className={`absolute top-4 right-4 p-2 rounded-full backdrop-blur-sm transition-all \${liked.has(p.id) ? "bg-red-500/20 text-red-400" : "bg-white/10 text-white/60 hover:text-red-400"}`}>
<Heart className={`h-4 w-4 \${liked.has(p.id) ? "fill-current" : ""}`} />
</button>
</div>
<CardContent className="p-5">
<div className="text-2xl font-bold text-emerald-600">{p.price}</div>
<Link to={`/properties/\${p.id}`} className="text-base font-semibold text-stone-800 dark:text-white mt-1 block hover:text-emerald-600 transition-colors">{p.name}</Link>
<div className="flex items-center gap-1 text-sm text-stone-500 dark:text-stone-400 mt-1"><MapPin className="h-3 w-3" />{p.addr}</div>
<div className="flex items-center gap-4 mt-3 pt-3 border-t border-stone-100 dark:border-stone-800 text-sm text-stone-500 dark:text-stone-400">
<span className="flex items-center gap-1"><Bed className="h-4 w-4" />{p.beds}</span>
<span className="flex items-center gap-1"><Bath className="h-4 w-4" />{p.baths}</span>
<span className="flex items-center gap-1"><Maximize className="h-4 w-4" />{p.sqft} sqft</span>
</div>
</CardContent>
</Card>
))}
</div>
<div className="text-center mt-12">
<Button variant="outline" className="rounded-full px-8 border-emerald-600 text-emerald-600 hover:bg-emerald-600 hover:text-white transition-all">
Load More Properties
</Button>
</div>
</div>
</section>
<Footer />
</div>
);
}