Files
Filamenteka/app/page.tsx
DaX 7349d1b60d Add Kupujem Prodajem purchase link to homepage
- Added prominent call-to-action button between logo and inventory table
- Styled with green gradient and hover effects
- Includes purchase and external link icons
- Opens in new tab with proper security attributes
- Tracks clicks with Matomo analytics
- Responsive design with smooth transitions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-30 23:03:18 +02:00

174 lines
6.6 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react';
import { FilamentTableV2 } from '../src/components/FilamentTableV2';
import { Filament } from '../src/types/filament';
import { filamentService } from '../src/services/api';
import { trackEvent } from '../src/components/MatomoAnalytics';
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);
// Removed V1/V2 toggle - now only using V2
// 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');
}
} 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' });
};
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-4">
<div className="flex items-center justify-between">
<div className="flex-1 flex justify-center gap-3 text-lg">
<span className="text-blue-700 dark:text-blue-300 font-medium animate-pulse whitespace-nowrap">
Kupovina po gramu dostupna
</span>
<span className="text-gray-400 dark:text-gray-600"></span>
<span className="text-orange-700 dark:text-orange-300 font-medium animate-pulse whitespace-nowrap">
Popust za 5+ komada
</span>
</div>
<div className="flex-shrink-0 ml-4">
{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>
</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"
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';
}}
/>
</button>
</div>
{/* Kupujem Prodajem Link */}
<div className="flex justify-center 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>
</div>
<FilamentTableV2
key={resetKey}
filaments={filaments}
/>
</main>
</div>
);
}