Refactor to multi-category catalog with polished light mode

- Restructure from single filament table to multi-category product catalog
  (filamenti, stampaci, ploce, mlaznice, delovi, oprema)
- Add shared layout components (SiteHeader, SiteFooter, CategoryNav, Breadcrumb)
- Add reusable UI primitives (Badge, Button, Card, Modal, PriceDisplay, EmptyState)
- Add catalog components (CatalogPage, ProductTable, ProductGrid, FilamentCard, ProductCard)
- Add admin dashboard with sidebar navigation and category management
- Add product API endpoints and database migrations
- Add SEO pages (politika-privatnosti, uslovi-koriscenja, robots.txt, sitemap.xml)
- Fix light mode: gradient text contrast, category nav accessibility,
  surface tokens, card shadows, CTA section theming
This commit is contained in:
DaX
2026-02-21 21:56:17 +01:00
parent a854fd5524
commit 1d3d11afec
62 changed files with 8618 additions and 358 deletions

View File

@@ -1,277 +1,384 @@
'use client'
import { useState, useEffect } from 'react';
import { FilamentTableV2 } from '../src/components/FilamentTableV2';
import { SaleCountdown } from '../src/components/SaleCountdown';
import ColorRequestModal from '../src/components/ColorRequestModal';
import { Filament } from '../src/types/filament';
import { filamentService } from '../src/services/api';
import { trackEvent } from '../src/components/MatomoAnalytics';
import Link from 'next/link';
import { SiteHeader } from '@/src/components/layout/SiteHeader';
import { SiteFooter } from '@/src/components/layout/SiteFooter';
import { SaleCountdown } from '@/src/components/SaleCountdown';
import ColorRequestModal from '@/src/components/ColorRequestModal';
import { CategoryIcon } from '@/src/components/ui/CategoryIcon';
import { getEnabledCategories } from '@/src/config/categories';
import { filamentService } from '@/src/services/api';
import { Filament } from '@/src/types/filament';
import { trackEvent } from '@/src/components/MatomoAnalytics';
const KP_URL = 'https://www.kupujemprodajem.com/kompjuteri-desktop/3d-stampaci-i-oprema/originalni-bambu-lab-filamenti-na-stanju/oglas/182256246';
export default function Home() {
const [filaments, setFilaments] = useState<Filament[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [darkMode, setDarkMode] = useState(false);
const [mounted, setMounted] = useState(false);
const [resetKey, setResetKey] = useState(0);
const [showColorRequestModal, setShowColorRequestModal] = useState(false);
// Removed V1/V2 toggle - now only using V2
const [mounted, setMounted] = useState(false);
const categories = getEnabledCategories();
useEffect(() => { setMounted(true); }, []);
// Initialize dark mode from localStorage after mounting
useEffect(() => {
setMounted(true);
const saved = localStorage.getItem('darkMode');
if (saved) {
setDarkMode(JSON.parse(saved));
}
}, []);
// Update dark mode
useEffect(() => {
if (!mounted) return;
localStorage.setItem('darkMode', JSON.stringify(darkMode));
if (darkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
}, [darkMode, mounted]);
const fetchFilaments = async () => {
try {
setLoading(true);
setError(null);
const filamentsData = await filamentService.getAll();
setFilaments(filamentsData);
} catch (err: any) {
console.error('API Error:', err);
// More descriptive error messages
if (err.code === 'ERR_NETWORK') {
setError('Network Error - Unable to connect to API');
} else if (err.response) {
setError(`Server error: ${err.response.status} - ${err.response.statusText}`);
} else if (err.request) {
setError('No response from server - check if API is running');
} else {
setError(err.message || 'Greška pri učitavanju filamenata');
const fetchFilaments = async () => {
try {
const data = await filamentService.getAll();
setFilaments(data);
} catch (err) {
console.error('Failed to fetch filaments for sale check:', err);
}
} finally {
setLoading(false);
}
};
useEffect(() => {
};
fetchFilaments();
// Auto-refresh data every 30 seconds to stay in sync
const interval = setInterval(() => {
fetchFilaments();
}, 30000);
// Also refresh when window regains focus
const handleFocus = () => {
fetchFilaments();
};
window.addEventListener('focus', handleFocus);
return () => {
clearInterval(interval);
window.removeEventListener('focus', handleFocus);
};
}, []);
const handleLogoClick = () => {
setResetKey(prev => prev + 1);
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const activeSaleFilaments = filaments.filter(f => f.sale_active === true);
const hasActiveSale = activeSaleFilaments.length > 0;
const maxSalePercentage = hasActiveSale
? Math.max(...activeSaleFilaments.map(f => f.sale_percentage || 0), 0)
: 0;
const saleEndDate = (() => {
const withEndDate = activeSaleFilaments.filter(f => f.sale_end_date);
if (withEndDate.length === 0) return null;
return withEndDate.reduce((latest, current) => {
if (!latest.sale_end_date) return current;
if (!current.sale_end_date) return latest;
return new Date(current.sale_end_date) > new Date(latest.sale_end_date) ? current : latest;
}).sale_end_date ?? null;
})();
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
<header className="bg-gradient-to-r from-blue-50 to-orange-50 dark:from-gray-800 dark:to-gray-900 shadow-lg transition-all duration-300">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-3 sm:py-4">
<div className="flex flex-col sm:flex-row items-center justify-between gap-3 sm:gap-0">
<div className="flex-1 flex flex-col sm:flex-row justify-center items-center gap-1 sm:gap-3 text-sm sm:text-lg">
<span className="text-blue-700 dark:text-blue-300 font-medium animate-pulse text-center">
Kupovina po gramu dostupna
</span>
<span className="hidden sm:inline text-gray-400 dark:text-gray-600"></span>
<span className="text-orange-700 dark:text-orange-300 font-medium animate-pulse text-center">
Popust za 5+ komada
</span>
</div>
<div className="flex-shrink-0">
{mounted ? (
<button
onClick={() => {
setDarkMode(!darkMode);
trackEvent('UI', 'Dark Mode Toggle', darkMode ? 'Light' : 'Dark');
}}
className="p-2 bg-white/50 dark:bg-gray-700/50 backdrop-blur text-gray-800 dark:text-gray-200 rounded-full hover:bg-white/80 dark:hover:bg-gray-600/80 transition-all duration-200 shadow-md"
title={darkMode ? 'Svetla tema' : 'Tamna tema'}
>
{darkMode ? '☀️' : '🌙'}
</button>
) : (
<div className="w-10 h-10" />
)}
</div>
</div>
<div className="min-h-screen" style={{ background: 'var(--surface-primary)' }}>
<SiteHeader />
{/* ═══ HERO ═══ */}
<section className="relative overflow-hidden noise-overlay">
{/* Rich multi-layer gradient background */}
<div className="absolute inset-0 bg-gradient-to-br from-blue-950 via-purple-950 to-indigo-950" />
{/* Floating color orbs — big, bright, energetic */}
<div className="absolute inset-0 overflow-hidden" aria-hidden="true">
<div className="absolute w-[600px] h-[600px] -top-48 -left-48 rounded-full opacity-40 blur-[100px] animate-float"
style={{ background: 'radial-gradient(circle, #3b82f6, transparent)' }} />
<div className="absolute w-[500px] h-[500px] top-10 right-0 rounded-full opacity-40 blur-[80px] animate-float-slow"
style={{ background: 'radial-gradient(circle, #a855f7, transparent)' }} />
<div className="absolute w-[450px] h-[450px] -bottom-24 left-1/3 rounded-full opacity-40 blur-[90px] animate-float-delayed"
style={{ background: 'radial-gradient(circle, #f59e0b, transparent)' }} />
<div className="absolute w-[400px] h-[400px] bottom-10 right-1/4 rounded-full opacity-40 blur-[80px] animate-float"
style={{ background: 'radial-gradient(circle, #22c55e, transparent)' }} />
<div className="absolute w-[350px] h-[350px] top-1/2 left-10 rounded-full opacity-35 blur-[70px] animate-float-slow"
style={{ background: 'radial-gradient(circle, #ef4444, transparent)' }} />
<div className="absolute w-[300px] h-[300px] top-1/4 right-1/3 rounded-full opacity-35 blur-[75px] animate-float-delayed"
style={{ background: 'radial-gradient(circle, #06b6d4, transparent)' }} />
</div>
</header>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Logo centered above content */}
<div className="flex justify-center mb-8">
<button
onClick={handleLogoClick}
className="group transition-transform duration-200"
title="Klikni za reset"
>
{/* Using next/image would cause preload, so we use regular img with loading="lazy" */}
<img
src="/logo.png"
alt="Filamenteka"
loading="lazy"
{/* Grid pattern overlay */}
<div className="absolute inset-0 opacity-[0.03]"
style={{
backgroundImage: 'linear-gradient(rgba(255,255,255,.1) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.1) 1px, transparent 1px)',
backgroundSize: '60px 60px'
}} />
<div className="relative z-10 max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 sm:py-28 lg:py-32">
<div className="flex flex-col items-center text-center reveal-up">
{/* Logo */}
<img
src="/logo.png"
alt="Filamenteka"
loading="eager"
decoding="async"
className="h-36 sm:h-44 md:h-52 w-auto drop-shadow-lg group-hover:drop-shadow-2xl transition-all duration-200"
onError={(e) => {
const target = e.currentTarget as HTMLImageElement;
target.style.display = 'none';
}}
className="h-24 sm:h-32 md:h-40 w-auto drop-shadow-2xl mb-8"
onError={(e) => { (e.currentTarget as HTMLImageElement).style.display = 'none'; }}
/>
</button>
</div>
{/* Action Buttons */}
<div className="flex flex-col sm:flex-row justify-center items-center gap-4 mb-8">
<a
href="https://www.kupujemprodajem.com/kompjuteri-desktop/3d-stampaci-i-oprema/originalni-bambu-lab-filamenti-na-stanju/oglas/182256246"
target="_blank"
rel="noopener noreferrer"
onClick={() => trackEvent('External Link', 'Kupujem Prodajem', 'Homepage')}
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200 transform hover:scale-105"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Kupi na Kupujem Prodajem
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a>
<a
href="tel:+381631031048"
onClick={() => trackEvent('Contact', 'Phone Call', 'Homepage')}
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200 transform hover:scale-105"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
</svg>
Pozovi +381 63 103 1048
</a>
<button
onClick={() => {
setShowColorRequestModal(true);
trackEvent('Navigation', 'Request Color', 'Homepage');
}}
className="inline-flex items-center gap-2 px-6 py-3 bg-gradient-to-r from-purple-500 to-purple-600 hover:from-purple-600 hover:to-purple-700 text-white font-semibold rounded-lg shadow-lg hover:shadow-xl transition-all duration-200 transform hover:scale-105"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h.01" />
</svg>
Zatraži Novu Boju
</button>
</div>
<SaleCountdown
hasActiveSale={filaments.some(f => f.sale_active === true)}
maxSalePercentage={Math.max(...filaments.filter(f => f.sale_active === true).map(f => f.sale_percentage || 0), 0)}
saleEndDate={(() => {
const activeSales = filaments.filter(f => f.sale_active === true && f.sale_end_date);
if (activeSales.length === 0) return null;
const latestSale = activeSales.reduce((latest, current) => {
if (!latest.sale_end_date) return current;
if (!current.sale_end_date) return latest;
return new Date(current.sale_end_date) > new Date(latest.sale_end_date) ? current : latest;
}).sale_end_date;
return latestSale;
})()}
/>
{/* Pricing Information */}
<div className="mb-6 space-y-4">
{/* Reusable Spool Price Notice */}
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
<div className="flex items-center justify-center gap-2">
<svg className="w-5 h-5 text-blue-600 dark:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span className="text-blue-800 dark:text-blue-200 font-medium">
Cena višekratne špulne: 499 RSD
{/* Tagline */}
<h1
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-black text-white mb-5 tracking-tight leading-[1.1]"
style={{ fontFamily: 'var(--font-display)' }}
>
Bambu Lab oprema
<br />
<span className="hero-gradient-text">
u Srbiji
</span>
</div>
</div>
</h1>
<p className="text-lg sm:text-xl text-gray-300 max-w-xl mb-10 leading-relaxed reveal-up reveal-delay-1">
Filamenti, stampaci, podloge, mlaznice, delovi i oprema.
<br className="hidden sm:block" />
Originalni proizvodi, privatna prodaja.
</p>
{/* Selling by Grams Notice */}
<div className="p-4 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg">
<div className="flex flex-col items-center gap-3 text-center">
<div className="flex items-center justify-center gap-2">
<svg className="w-5 h-5 text-green-600 dark:text-green-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" />
{/* CTA */}
<div className="flex flex-col sm:flex-row gap-3 sm:gap-4 reveal-up reveal-delay-2">
<a
href={KP_URL}
target="_blank"
rel="noopener noreferrer"
onClick={() => trackEvent('External Link', 'Kupujem Prodajem', 'Hero CTA')}
className="group inline-flex items-center justify-center gap-2.5 px-8 py-4
bg-blue-500 hover:bg-blue-600 text-white font-bold text-base rounded-2xl
shadow-lg shadow-blue-500/25
hover:shadow-2xl hover:shadow-blue-500/35
transition-all duration-300 active:scale-[0.97]"
style={{ fontFamily: 'var(--font-display)' }}
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
</svg>
<span className="text-green-800 dark:text-green-200 font-semibold">
Prodaja filamenta na grame - idealno za testiranje materijala ili manje projekte
</span>
</div>
<div className="flex flex-wrap justify-center gap-4 text-sm">
<span className="text-green-700 dark:text-green-300 font-medium">50gr - 299 RSD</span>
<span className="text-gray-400 dark:text-gray-600"></span>
<span className="text-green-700 dark:text-green-300 font-medium">100gr - 499 RSD</span>
<span className="text-gray-400 dark:text-gray-600"></span>
<span className="text-green-700 dark:text-green-300 font-medium">200gr - 949 RSD</span>
</div>
</div>
</div>
</div>
Kupi na KupujemProdajem
<svg className="w-4 h-4 opacity-60 group-hover:opacity-100 group-hover:translate-x-0.5 transition-all" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
</svg>
</a>
<FilamentTableV2
key={resetKey}
filaments={filaments}
/>
</main>
<footer className="bg-gray-100 dark:bg-gray-800 border-t border-gray-200 dark:border-gray-700 mt-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="flex flex-col justify-center items-center gap-6">
<div className="text-center">
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">Kontakt</h3>
<a
href="tel:+381631031048"
className="inline-flex items-center gap-2 text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300 transition-colors"
onClick={() => trackEvent('Contact', 'Phone Call', 'Footer')}
onClick={() => trackEvent('Contact', 'Phone Call', 'Hero CTA')}
className="inline-flex items-center justify-center gap-2.5 px-8 py-4
text-white/90 hover:text-white font-semibold text-base rounded-2xl
border border-white/20 hover:border-white/40
hover:bg-white/[0.08]
transition-all duration-300 active:scale-[0.97]"
style={{ fontFamily: 'var(--font-display)' }}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z" />
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z" />
</svg>
+381 63 103 1048
</a>
</div>
</div>
</div>
</footer>
{/* Color Request Modal */}
<ColorRequestModal
{/* Bottom fade */}
<div className="absolute bottom-0 left-0 right-0 h-24 bg-gradient-to-t from-[var(--surface-primary)] to-transparent z-10" />
</section>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 -mt-8 relative z-20">
{/* Sale Banner */}
<SaleCountdown
hasActiveSale={hasActiveSale}
maxSalePercentage={maxSalePercentage}
saleEndDate={saleEndDate}
/>
{/* ═══ CATEGORIES ═══ */}
<section className="py-12 sm:py-16">
<div className="text-center mb-10 sm:mb-14">
<h2
className="text-4xl sm:text-5xl font-extrabold tracking-tight mb-3"
style={{ fontFamily: 'var(--font-display)', color: 'var(--text-primary)' }}
>
Sta nudimo
</h2>
<p style={{ color: 'var(--text-secondary)' }} className="text-base sm:text-lg max-w-md mx-auto">
Kompletna ponuda originalne Bambu Lab opreme
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 auto-rows-auto">
{categories.map((cat, i) => {
// First card (filamenti) is featured — spans 2 cols on lg
const isFeatured = i === 0;
// Last card spans 2 cols on sm/lg for variety
const isWide = i === categories.length - 1;
return (
<Link
key={cat.slug}
href={`/${cat.slug}`}
onClick={() => trackEvent('Navigation', 'Category Click', cat.label)}
className={`category-card group block rounded-2xl reveal-up reveal-delay-${i + 1} ${
isFeatured ? 'lg:col-span-2 lg:row-span-2' : ''
} ${isWide ? 'sm:col-span-2 lg:col-span-1' : ''}`}
style={{
background: `linear-gradient(135deg, ${cat.colorHex}, ${cat.colorHex}cc)`,
boxShadow: `0 8px 32px -8px ${cat.colorHex}35`,
}}
>
<div className={`relative z-10 ${isFeatured ? 'p-8 sm:p-10' : 'p-6 sm:p-7'}`}>
{/* SVG Icon */}
<div className={`${isFeatured ? 'w-14 h-14' : 'w-11 h-11'} bg-white/20 backdrop-blur-sm rounded-xl flex items-center justify-center mb-4`}>
<CategoryIcon slug={cat.slug} className={isFeatured ? 'w-7 h-7 text-white' : 'w-5 h-5 text-white'} />
</div>
<h3 className={`${isFeatured ? 'text-2xl lg:text-3xl' : 'text-lg'} font-bold text-white tracking-tight mb-2`}
style={{ fontFamily: 'var(--font-display)' }}>
{cat.label}
</h3>
<p className={`text-white/75 ${isFeatured ? 'text-base' : 'text-sm'} leading-relaxed mb-5`}>
{cat.description}
</p>
<div className="flex items-center text-sm font-bold text-white/90 group-hover:text-white transition-colors">
Pogledaj ponudu
<svg className="w-4 h-4 ml-1.5 group-hover:translate-x-1.5 transition-transform duration-300" fill="none" viewBox="0 0 24 24" strokeWidth={2.5} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" />
</svg>
</div>
</div>
</Link>
);
})}
</div>
</section>
{/* ═══ INFO CARDS ═══ */}
<section className="pb-12 sm:pb-16">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-5">
{/* Reusable Spool Price */}
<div
className="rounded-2xl border border-l-4 p-6 sm:p-7 flex items-start gap-4 shadow-sm"
style={{ borderColor: 'var(--border-subtle)', borderLeftColor: '#3b82f6', background: 'var(--surface-elevated)' }}
>
<div className="flex-shrink-0 w-11 h-11 bg-blue-500/10 dark:bg-blue-500/15 rounded-xl flex items-center justify-center">
<svg className="w-5 h-5 text-blue-500" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z" />
</svg>
</div>
<div>
<h3 className="font-extrabold text-base mb-1" style={{ fontFamily: 'var(--font-display)', color: 'var(--text-primary)' }}>
Visekratna spulna
</h3>
<p className="text-sm font-medium" style={{ color: 'var(--text-secondary)' }}>
Cena visekratne spulne: <span className="font-extrabold text-blue-500">499 RSD</span>
</p>
</div>
</div>
{/* Selling by Grams */}
<div
className="rounded-2xl border border-l-4 p-6 sm:p-7 flex items-start gap-4 shadow-sm"
style={{ borderColor: 'var(--border-subtle)', borderLeftColor: '#10b981', background: 'var(--surface-elevated)' }}
>
<div className="flex-shrink-0 w-11 h-11 bg-emerald-500/10 dark:bg-emerald-500/15 rounded-xl flex items-center justify-center">
<svg className="w-5 h-5 text-emerald-500" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 3v17.25m0 0c-1.472 0-2.882.265-4.185.75M12 20.25c1.472 0 2.882.265 4.185.75M18.75 4.97A48.416 48.416 0 0 0 12 4.5c-2.291 0-4.545.16-6.75.47m13.5 0c1.01.143 2.01.317 3 .52m-3-.52 2.62 10.726c.122.499-.106 1.028-.589 1.202a5.988 5.988 0 0 1-2.031.352 5.988 5.988 0 0 1-2.031-.352c-.483-.174-.711-.703-.59-1.202L18.75 4.971Zm-16.5.52c.99-.203 1.99-.377 3-.52m0 0 2.62 10.726c.122.499-.106 1.028-.589 1.202a5.989 5.989 0 0 1-2.031.352 5.989 5.989 0 0 1-2.031-.352c-.483-.174-.711-.703-.59-1.202L5.25 4.971Z" />
</svg>
</div>
<div>
<h3 className="font-extrabold text-base mb-1.5" style={{ fontFamily: 'var(--font-display)', color: 'var(--text-primary)' }}>
Prodaja na grame
</h3>
<p className="text-sm font-medium mb-2.5" style={{ color: 'var(--text-secondary)' }}>
Idealno za testiranje materijala ili manje projekte.
</p>
<div className="flex flex-wrap gap-2">
{[
{ g: '50g', price: '299' },
{ g: '100g', price: '499' },
{ g: '200g', price: '949' },
].map(({ g, price }) => (
<span key={g} className="inline-flex items-center gap-1 px-2.5 py-1 rounded-lg text-xs font-bold bg-emerald-500/10 dark:bg-emerald-500/15 text-emerald-600 dark:text-emerald-400">
{g} {price} RSD
</span>
))}
</div>
</div>
</div>
</div>
</section>
{/* ═══ DISCLAIMER ═══ */}
<section className="pb-12 sm:pb-16">
<div className="rounded-2xl border border-amber-200/60 dark:border-amber-500/20 bg-amber-50/50 dark:bg-amber-500/[0.06] p-5 sm:p-6">
<div className="flex items-start gap-3">
<div className="flex-shrink-0 w-9 h-9 bg-amber-400/20 dark:bg-amber-500/15 rounded-lg flex items-center justify-center mt-0.5">
<svg className="w-5 h-5 text-amber-600 dark:text-amber-400" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z" />
</svg>
</div>
<p className="text-sm leading-relaxed text-amber-900 dark:text-amber-200/80">
Ovo je privatna prodaja fizickog lica. Filamenteka nije registrovana prodavnica.
Sva kupovina se odvija preko KupujemProdajem platforme.
</p>
</div>
</div>
</section>
{/* ═══ CONTACT CTA ═══ */}
<section className="pb-16 sm:pb-20">
<div className="relative overflow-hidden rounded-3xl bg-gradient-to-br from-slate-100 via-slate-50 to-blue-50 dark:bg-none dark:from-transparent dark:via-transparent dark:to-transparent dark:bg-[#0f172a] dark:noise-overlay border border-slate-200/80 dark:border-transparent">
<div className="relative z-10 p-8 sm:p-12 lg:p-14 text-center">
<h2
className="text-2xl sm:text-3xl lg:text-4xl font-extrabold mb-3 tracking-tight"
style={{ fontFamily: 'var(--font-display)', color: 'var(--text-primary)' }}
>
Zainteresovani?
</h2>
<p className="mb-8 max-w-md mx-auto text-base" style={{ color: 'var(--text-muted)' }}>
Pogledajte ponudu na KupujemProdajem ili nas kontaktirajte direktno.
</p>
<div className="flex flex-col sm:flex-row justify-center items-center gap-3 sm:gap-4">
<a
href={KP_URL}
target="_blank"
rel="noopener noreferrer"
onClick={() => trackEvent('External Link', 'Kupujem Prodajem', 'Contact CTA')}
className="group inline-flex items-center gap-2.5 px-7 py-3.5
bg-blue-500 hover:bg-blue-600 text-white
font-bold rounded-xl
shadow-lg shadow-blue-500/25
hover:shadow-xl hover:shadow-blue-500/35
transition-all duration-300 active:scale-[0.97]"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 3h1.386c.51 0 .955.343 1.087.835l.383 1.437M7.5 14.25a3 3 0 0 0-3 3h15.75m-12.75-3h11.218c1.121-2.3 2.1-4.684 2.924-7.138a60.114 60.114 0 0 0-16.536-1.84M7.5 14.25 5.106 5.272M6 20.25a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Zm12.75 0a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" />
</svg>
Kupi na KupujemProdajem
<svg className="w-4 h-4 opacity-50 group-hover:opacity-100 group-hover:translate-x-0.5 transition-all" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" />
</svg>
</a>
<a
href="tel:+381631031048"
onClick={() => trackEvent('Contact', 'Phone Call', 'Contact CTA')}
className="inline-flex items-center gap-2.5 px-7 py-3.5
font-semibold rounded-xl
text-slate-700 dark:text-white/90 hover:text-slate-900 dark:hover:text-white
border border-slate-300 dark:border-white/25 hover:border-slate-400 dark:hover:border-white/50
hover:bg-slate-50 dark:hover:bg-white/[0.1]
transition-all duration-300 active:scale-[0.97]"
>
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M2.25 6.75c0 8.284 6.716 15 15 15h2.25a2.25 2.25 0 0 0 2.25-2.25v-1.372c0-.516-.351-.966-.852-1.091l-4.423-1.106c-.44-.11-.902.055-1.173.417l-.97 1.293c-.282.376-.769.542-1.21.38a12.035 12.035 0 0 1-7.143-7.143c-.162-.441.004-.928.38-1.21l1.293-.97c.363-.271.527-.734.417-1.173L6.963 3.102a1.125 1.125 0 0 0-1.091-.852H4.5A2.25 2.25 0 0 0 2.25 4.5v2.25Z" />
</svg>
+381 63 103 1048
</a>
</div>
{/* Color Request */}
<div className="mt-6">
<button
onClick={() => {
setShowColorRequestModal(true);
trackEvent('Navigation', 'Request Color', 'Contact CTA');
}}
className="inline-flex items-center gap-2 text-sm font-medium transition-colors duration-200 text-slate-500 hover:text-slate-800 dark:text-white/70 dark:hover:text-white"
>
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M4.098 19.902a3.75 3.75 0 0 0 5.304 0l6.401-6.402M6.75 21A3.75 3.75 0 0 1 3 17.25V4.125C3 3.504 3.504 3 4.125 3h5.25c.621 0 1.125.504 1.125 1.125v4.072M6.75 21a3.75 3.75 0 0 0 3.75-3.75V8.197M6.75 21h13.125c.621 0 1.125-.504 1.125-1.125v-5.25c0-.621-.504-1.125-1.125-1.125h-4.072M10.5 8.197l2.88-2.88c.438-.439 1.15-.439 1.59 0l3.712 3.713c.44.44.44 1.152 0 1.59l-2.879 2.88M6.75 17.25h.008v.008H6.75v-.008Z" />
</svg>
Ne nalazite boju? Zatrazite novu
</button>
</div>
</div>
</div>
</section>
</main>
<SiteFooter />
<ColorRequestModal
isOpen={showColorRequestModal}
onClose={() => setShowColorRequestModal(false)}
/>
</div>
);
}
}