← back to Watches
src/App.luxury.jsx
450 lines
import React, { useState, useEffect } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import LuxuryHero from './components/LuxuryHero';
import ParallaxSection from './components/ParallaxSection';
import LuxuryWatchCard from './components/LuxuryWatchCard';
import GestureControls from './components/GestureControls';
import PremiumLoader from './components/PremiumLoader';
import EnhancedHeader from './components/EnhancedHeader';
import PriceChart from './components/PriceChart';
import WatchComparison from './components/WatchComparison';
import InstallPrompt from './components/InstallPrompt';
import OfflineIndicator from './components/OfflineIndicator';
import { useDarkMode } from './hooks/useLocalStorage';
import { saveWatchesOffline, getWatchesOffline } from './utils/offlineStorage';
import { getOnlineStatus } from './utils/pwaUtils';
import {
updateWatchPageSEO,
updateListPageSEO,
updateDashboardSEO,
updateComparisonPageSEO,
preloadCriticalResources,
cleanupStructuredData
} from './utils/seo';
/**
* OMEGA LUXURY APP - Ultra-Premium Edition
* Features:
* - Cinematic hero section with parallax
* - 3D card effects with mouse tracking
* - Gesture-based navigation
* - Haptic feedback simulation
* - Premium loading states
* - Gold accent animations
* - Smooth page transitions
*/
function App() {
const [watches, setWatches] = useState([]);
const [statistics, setStatistics] = useState(null);
const [selectedWatch, setSelectedWatch] = useState(null);
const [view, setView] = useState('hero'); // hero, collection, chart, compare
const [loading, setLoading] = useState(true);
const [loadingProgress, setLoadingProgress] = useState(0);
const [compareWatches, setCompareWatches] = useState([]);
const [showComparison, setShowComparison] = useState(false);
const [currentCollectionIndex, setCurrentCollectionIndex] = useState(0);
const [darkMode] = useDarkMode();
useEffect(() => {
preloadCriticalResources();
loadData();
handleDeepLinking();
}, []);
useEffect(() => {
cleanupStructuredData();
switch (view) {
case 'hero':
case 'collection':
updateDashboardSEO();
break;
case 'chart':
if (selectedWatch) {
updateWatchPageSEO(selectedWatch);
}
break;
case 'compare':
updateComparisonPageSEO(compareWatches);
break;
default:
updateDashboardSEO();
}
}, [view, selectedWatch, compareWatches]);
const loadData = async () => {
try {
setLoading(true);
setLoadingProgress(10);
if (getOnlineStatus()) {
setLoadingProgress(30);
const [watchesRes, statsRes] = await Promise.all([
fetch('/api/watches'),
fetch('/api/statistics')
]);
setLoadingProgress(60);
const watchesData = await watchesRes.json();
const statsData = await statsRes.json();
const watchesList = watchesData.watches || watchesData;
setWatches(watchesList);
setStatistics(statsData);
setLoadingProgress(90);
await saveWatchesOffline(watchesList);
setLoadingProgress(100);
// Smooth transition out of loading
setTimeout(() => setLoading(false), 500);
} else {
const offlineWatches = await getWatchesOffline();
setWatches(offlineWatches);
setStatistics(null);
setLoadingProgress(100);
setTimeout(() => setLoading(false), 500);
}
} catch (error) {
console.error('Error loading data:', error);
const offlineWatches = await getWatchesOffline();
if (offlineWatches.length > 0) {
setWatches(offlineWatches);
}
setLoadingProgress(100);
setTimeout(() => setLoading(false), 500);
}
};
const handleDeepLinking = () => {
const params = new URLSearchParams(window.location.search);
const watchId = params.get('watch');
const viewParam = params.get('view');
if (viewParam && ['hero', 'collection', 'chart', 'compare'].includes(viewParam)) {
setView(viewParam);
}
if (watchId) {
const checkAndSelect = setInterval(() => {
if (watches.length > 0) {
const watch = watches.find(w => w.id === watchId);
if (watch) {
setSelectedWatch(watch);
setView('chart');
}
clearInterval(checkAndSelect);
}
}, 100);
setTimeout(() => clearInterval(checkAndSelect), 5000);
}
};
const handleWatchSelect = (watch) => {
setSelectedWatch(watch);
setView('chart');
const newUrl = `${window.location.pathname}?watch=${watch.id}`;
window.history.pushState({ watch: watch.id }, '', newUrl);
updateWatchPageSEO(watch);
};
const handleBackToCollection = () => {
setView('collection');
const newUrl = `${window.location.pathname}?view=collection`;
window.history.pushState({ view: 'collection' }, '', newUrl);
updateListPageSEO();
};
const handleExploreCollection = () => {
setView('collection');
window.scrollTo({ top: 0, behavior: 'smooth' });
};
const handleAddToCompare = (watch) => {
if (compareWatches.length < 3 && !compareWatches.find(w => w.id === watch.id)) {
setCompareWatches([...compareWatches, watch]);
}
};
const handleRemoveFromCompare = (watchId) => {
setCompareWatches(compareWatches.filter(w => w.id !== watchId));
};
// Group watches by series for collection view
const groupedWatches = watches.reduce((acc, watch) => {
const series = watch.series || 'Other';
if (!acc[series]) {
acc[series] = [];
}
acc[series].push(watch);
return acc;
}, {});
if (loading) {
return <PremiumLoader progress={loadingProgress} />;
}
return (
<div className={`min-h-screen ${darkMode ? 'dark' : ''}`}>
<div className="min-h-screen bg-gradient-luxury-dark transition-colors">
{/* PWA Components */}
<OfflineIndicator />
<InstallPrompt variant="banner" />
{/* Luxury Header */}
<header role="banner" className="no-print">
<EnhancedHeader view={view} setView={setView} />
</header>
{/* Main content with page transitions */}
<main id="main-content" role="main">
<AnimatePresence mode="wait">
{/* Hero Section */}
{view === 'hero' && (
<motion.div
key="hero"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6 }}
>
<LuxuryHero onExplore={handleExploreCollection} />
{/* Statistics section with parallax */}
{statistics && (
<ParallaxSection
title="Market Insights"
subtitle="Data-driven analysis of Omega's enduring value"
>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mt-12">
<motion.div
className="luxury-card p-8 text-center"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.1 }}
>
<div className="text-5xl mb-4 text-gold">32</div>
<h3 className="text-xl font-display text-white">Iconic Models</h3>
<p className="text-gray-400 mt-2">Curated collection</p>
</motion.div>
<motion.div
className="luxury-card p-8 text-center"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.2 }}
>
<div className="text-5xl mb-4 text-gold">67</div>
<h3 className="text-xl font-display text-white">Years Tracked</h3>
<p className="text-gray-400 mt-2">Since 1957</p>
</motion.div>
<motion.div
className="luxury-card p-8 text-center"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: 0.3 }}
>
<div className="text-5xl mb-4 text-green-400">+3,400%</div>
<h3 className="text-xl font-display text-white">Average Growth</h3>
<p className="text-gray-400 mt-2">Value appreciation</p>
</motion.div>
</div>
<motion.button
onClick={handleExploreCollection}
className="btn-luxury mt-12 haptic-click"
initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
transition={{ delay: 0.5 }}
>
View Full Collection
</motion.button>
</ParallaxSection>
)}
</motion.div>
)}
{/* Collection View with Gesture Controls */}
{view === 'collection' && (
<motion.div
key="collection"
className="min-h-screen py-24 px-4"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.6 }}
>
<div className="max-w-7xl mx-auto">
<motion.div
className="text-center mb-16"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
>
<h1 className="text-5xl md:text-7xl font-display font-bold mb-6">
<span className="text-gold">The Collection</span>
</h1>
<p className="text-xl text-gray-300 font-luxury max-w-3xl mx-auto">
Explore 32 masterpieces of Swiss horology, each with its own story of
innovation, precision, and timeless elegance.
</p>
</motion.div>
{/* Watch grid by series */}
{Object.entries(groupedWatches).map(([series, seriesWatches], seriesIndex) => (
<motion.section
key={series}
className="mb-20"
initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: seriesIndex * 0.2 }}
>
<h2 className="text-3xl md:text-4xl font-display font-bold text-gold mb-8 border-b border-gold/30 pb-4">
{series}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{seriesWatches.map((watch, index) => (
<LuxuryWatchCard
key={watch.id}
watch={watch}
onClick={handleWatchSelect}
index={index}
/>
))}
</div>
</motion.section>
))}
</div>
{/* Gesture controls for mobile swipe navigation */}
<GestureControls
items={Object.keys(groupedWatches)}
currentIndex={currentCollectionIndex}
onIndexChange={setCurrentCollectionIndex}
/>
</motion.div>
)}
{/* Chart View */}
{view === 'chart' && selectedWatch && (
<motion.div
key="chart"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.4 }}
className="min-h-screen py-24"
>
<PriceChart watch={selectedWatch} onBack={handleBackToCollection} />
</motion.div>
)}
{/* Comparison View */}
{view === 'compare' && (
<motion.div
key="compare"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.4 }}
className="min-h-screen py-24 px-4"
>
<div className="max-w-6xl mx-auto">
<div className="luxury-card p-8 md:p-12">
<h1 className="text-4xl md:text-5xl font-display font-bold text-gold mb-6">
Watch Comparison
</h1>
<p className="text-lg text-gray-300 mb-8">
Compare up to 3 watches side-by-side
</p>
{compareWatches.length > 0 && (
<button
onClick={() => setShowComparison(true)}
className="btn-luxury haptic-click"
>
Compare {compareWatches.length} {compareWatches.length === 1 ? 'Watch' : 'Watches'}
</button>
)}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</main>
{/* Comparison Modal */}
<AnimatePresence>
{showComparison && compareWatches.length > 0 && (
<WatchComparison
watches={watches}
selectedWatches={compareWatches}
onClose={() => setShowComparison(false)}
onAddWatch={handleAddToCompare}
onRemoveWatch={handleRemoveFromCompare}
/>
)}
</AnimatePresence>
{/* Luxury Footer */}
<footer role="contentinfo" className="relative bg-black/50 backdrop-blur-xl text-white py-12 mt-24 border-t border-gold/20">
<div className="max-w-7xl mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 text-center md:text-left">
<section>
<h2 className="font-display text-2xl text-gold mb-4">OMEGA</h2>
<p className="text-sm text-gray-400 font-luxury">
Historical price tracking and analysis for the world's most prestigious timepieces.
Experience Swiss excellence through data.
</p>
</section>
<section>
<h3 className="font-display text-lg text-gold mb-4">Collection</h3>
<ul className="text-sm text-gray-400 space-y-2">
<li>32 Iconic Models</li>
<li>67 Years of Data (1957-2024)</li>
<li>Comprehensive Price Analytics</li>
<li>Investment Insights</li>
</ul>
</section>
<section>
<h3 className="font-display text-lg text-gold mb-4">Experience</h3>
<ul className="text-sm text-gray-400 space-y-2">
<li>3D Interactive Cards</li>
<li>Gesture Navigation</li>
<li>Cinematic Animations</li>
<li>Luxury UI/UX</li>
</ul>
</section>
</div>
<div className="mt-12 pt-8 border-t border-gold/20 text-center">
<p className="text-sm text-gray-500">
© 2024 Omega Price History | Ultra-Luxury Edition
</p>
<p className="text-xs text-gray-600 mt-2">
Server: 45.61.58.125:7600 | Status: <span className="text-green-400">Online</span>
</p>
</div>
</div>
{/* Decorative bottom accent */}
<div className="absolute bottom-0 left-0 right-0 h-1 bg-gradient-gold" />
</footer>
</div>
</div>
);
}
export default App;