mirror of
https://gitlab.com/davidels1986/ore-cart-checkout.git
synced 2026-10-30 02:59:13 +00:00
Visual edit in Lovable
Edited UI in Lovable
This commit is contained in:
parent
516f18a742
commit
34fe6ad5be
1 changed files with 33 additions and 62 deletions
|
|
@ -10,31 +10,25 @@ import Fuse from 'fuse.js';
|
|||
// Animated counter hook
|
||||
function useAnimatedCounter(end: number, duration: number = 2000) {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
let startTime: number;
|
||||
let animationFrame: number;
|
||||
|
||||
const animate = (timestamp: number) => {
|
||||
if (!startTime) startTime = timestamp;
|
||||
const progress = Math.min((timestamp - startTime) / duration, 1);
|
||||
|
||||
|
||||
// Easing function for smooth animation
|
||||
const easeOutQuart = 1 - Math.pow(1 - progress, 4);
|
||||
setCount(Math.floor(easeOutQuart * end));
|
||||
|
||||
if (progress < 1) {
|
||||
animationFrame = requestAnimationFrame(animate);
|
||||
}
|
||||
};
|
||||
|
||||
animationFrame = requestAnimationFrame(animate);
|
||||
return () => cancelAnimationFrame(animationFrame);
|
||||
}, [end, duration]);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
const Index = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
|
@ -43,18 +37,23 @@ const Index = () => {
|
|||
const productsCount = useAnimatedCounter(1000, 2000);
|
||||
const clientsCount = useAnimatedCounter(500, 2000);
|
||||
const hoursCount = useAnimatedCounter(24, 1500);
|
||||
|
||||
const fuse = useMemo(() => new Fuse(products, {
|
||||
keys: [
|
||||
{ name: 'name', weight: 0.4 },
|
||||
{ name: 'tags', weight: 0.3 },
|
||||
{ name: 'description', weight: 0.2 },
|
||||
{ name: 'sku', weight: 0.1 },
|
||||
],
|
||||
keys: [{
|
||||
name: 'name',
|
||||
weight: 0.4
|
||||
}, {
|
||||
name: 'tags',
|
||||
weight: 0.3
|
||||
}, {
|
||||
name: 'description',
|
||||
weight: 0.2
|
||||
}, {
|
||||
name: 'sku',
|
||||
weight: 0.1
|
||||
}],
|
||||
threshold: 0.4,
|
||||
includeScore: true,
|
||||
includeScore: true
|
||||
}), []);
|
||||
|
||||
const displayProducts = useMemo(() => {
|
||||
let filtered = products;
|
||||
|
||||
|
|
@ -68,29 +67,24 @@ const Index = () => {
|
|||
if (selectedCategory) {
|
||||
filtered = filtered.filter(p => p.category === selectedCategory);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}, [searchQuery, selectedCategory, fuse]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
return <div className="min-h-screen bg-background">
|
||||
{/* Hero Section */}
|
||||
<section
|
||||
className="relative min-h-[320px] md:min-h-[400px] flex items-center justify-center bg-cover bg-center"
|
||||
style={{ backgroundImage: `url(${heroBg})` }}
|
||||
>
|
||||
<section className="relative min-h-[320px] md:min-h-[400px] flex items-center justify-center bg-cover bg-center" style={{
|
||||
backgroundImage: `url(${heroBg})`
|
||||
}}>
|
||||
<div className="absolute inset-0 gradient-industrial opacity-90" />
|
||||
<div className="relative z-10 container mx-auto px-4 py-12 text-center">
|
||||
<h1 className="font-display text-3xl md:text-4xl lg:text-5xl font-bold text-primary-foreground mb-3 animate-fade-in">
|
||||
Industrial Supplies for
|
||||
<span className="block text-brand-yellow">Mining & Construction</span>
|
||||
</h1>
|
||||
|
||||
<p className="text-base md:text-lg text-primary-foreground/80 mb-6 max-w-2xl mx-auto animate-fade-in">
|
||||
Quality fasteners, tools, and equipment. Request a quote today.
|
||||
</p>
|
||||
|
||||
{/* Quick Stats with Animated Numbers */}
|
||||
<div className="flex flex-wrap justify-center gap-6 md:gap-8 animate-fade-in" style={{ animationDelay: '0.2s' }}>
|
||||
<div className="flex flex-wrap justify-center gap-6 md:gap-8 animate-fade-in" style={{
|
||||
animationDelay: '0.2s'
|
||||
}}>
|
||||
<div className="text-center">
|
||||
<p className="text-2xl md:text-3xl font-bold text-brand-yellow">{productsCount.toLocaleString()}+</p>
|
||||
<p className="text-primary-foreground/70 text-xs md:text-sm">Products</p>
|
||||
|
|
@ -115,32 +109,21 @@ const Index = () => {
|
|||
<div className="flex flex-col lg:flex-row gap-6 lg:gap-8">
|
||||
{/* Category Sidebar - Sticky */}
|
||||
<div className="lg:sticky lg:top-4 lg:self-start">
|
||||
<CategorySidebar
|
||||
selectedCategory={selectedCategory}
|
||||
onSelectCategory={setSelectedCategory}
|
||||
/>
|
||||
<CategorySidebar selectedCategory={selectedCategory} onSelectCategory={setSelectedCategory} />
|
||||
</div>
|
||||
|
||||
{/* Products Section */}
|
||||
<div className="flex-1">
|
||||
{/* Search Bar - Above Products */}
|
||||
<div className="mb-6">
|
||||
<SearchBar
|
||||
large
|
||||
value={searchQuery}
|
||||
onChange={setSearchQuery}
|
||||
/>
|
||||
<SearchBar large value={searchQuery} onChange={setSearchQuery} />
|
||||
</div>
|
||||
|
||||
{/* Products Header */}
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div>
|
||||
<h2 className="font-display text-xl md:text-2xl font-bold text-foreground">
|
||||
{selectedCategory
|
||||
? `${selectedCategory.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}`
|
||||
: searchQuery.length >= 2
|
||||
? `Results for "${searchQuery}"`
|
||||
: 'Featured Products'}
|
||||
{selectedCategory ? `${selectedCategory.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}` : searchQuery.length >= 2 ? `Results for "${searchQuery}"` : 'Featured Products'}
|
||||
</h2>
|
||||
<p className="text-muted-foreground text-sm mt-1">
|
||||
{displayProducts.length} products available
|
||||
|
|
@ -151,19 +134,15 @@ const Index = () => {
|
|||
{/* Scrollable Products Grid */}
|
||||
<div className="max-h-[70vh] overflow-y-auto pr-2 scrollbar-thin">
|
||||
<div className="grid grid-cols-2 lg:grid-cols-3 gap-3 md:gap-4">
|
||||
{displayProducts.map((product) => (
|
||||
<ProductCard key={product.id} product={product} />
|
||||
))}
|
||||
{displayProducts.map(product => <ProductCard key={product.id} product={product} />)}
|
||||
</div>
|
||||
|
||||
{displayProducts.length === 0 && (
|
||||
<div className="text-center py-12">
|
||||
{displayProducts.length === 0 && <div className="text-center py-12">
|
||||
<p className="text-muted-foreground">No products found.</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Try different keywords or browse categories.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -180,23 +159,15 @@ const Index = () => {
|
|||
Add items to your cart and request a personalized quote today.
|
||||
</p>
|
||||
<div className="flex flex-wrap justify-center gap-4">
|
||||
<a
|
||||
href="tel:+27123456789"
|
||||
className="inline-flex items-center gap-2 bg-brand-yellow text-brand-yellow-foreground font-semibold px-8 py-3 rounded-lg hover:opacity-90 transition-opacity"
|
||||
>
|
||||
<a href="tel:+27123456789" className="inline-flex items-center gap-2 bg-brand-yellow text-brand-yellow-foreground font-semibold px-8 py-3 rounded-lg hover:opacity-90 transition-opacity">
|
||||
Call Us Now
|
||||
</a>
|
||||
<a
|
||||
href="mailto:sales@westscottfasteners.co.za"
|
||||
className="inline-flex items-center gap-2 bg-primary-foreground text-primary font-semibold px-8 py-3 rounded-lg hover:opacity-90 transition-opacity"
|
||||
>
|
||||
<a href="mailto:sales@westscottfasteners.co.za" className="inline-flex items-center gap-2 bg-primary-foreground text-primary font-semibold px-8 py-3 rounded-lg hover:opacity-90 transition-opacity">
|
||||
Email Us
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default Index;
|
||||
export default Index;
|
||||
Loading…
Reference in a new issue