← back to Watches
src/App-optimized.jsx
413 lines
import React, { useState, useEffect, lazy, Suspense } from 'react';
import { AnimatePresence, motion } from 'framer-motion';
import EnhancedHeader from './components/EnhancedHeader';
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';
// Lazy load heavy components for code splitting
const EnhancedDashboard = lazy(() => import('./components/EnhancedDashboard'));
const EnhancedWatchList = lazy(() => import('./components/EnhancedWatchList'));
const PriceChart = lazy(() => import('./components/PriceChart'));
const WatchComparison = lazy(() => import('./components/WatchComparison'));
const InstallPrompt = lazy(() => import('./components/InstallPrompt'));
const OfflineIndicator = lazy(() => import('./components/OfflineIndicator'));
// Loading fallback component
const LoadingFallback = ({ message = 'Loading...' }) => (
<div className="min-h-screen flex items-center justify-center bg-gray-100 dark:bg-gray-900">
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
className="text-center"
>
<div className="relative">
<div className="animate-spin rounded-full h-20 w-20 border-b-4 border-omega-red mx-auto mb-4"></div>
<div className="absolute inset-0 flex items-center justify-center">
<span className="text-3xl">⌚</span>
</div>
</div>
<h1 className="text-gray-600 dark:text-gray-300 text-lg font-semibold">
{message}
</h1>
</motion.div>
</div>
);
/**
* PERFORMANCE OPTIMIZED Main App Component
*
* Optimizations:
* - Lazy loading of all heavy components
* - Code splitting by route
* - Memoized callbacks
* - Optimized re-renders
* - Aggressive caching
*/
function App() {
const [watches, setWatches] = useState([]);
const [statistics, setStatistics] = useState(null);
const [selectedWatch, setSelectedWatch] = useState(null);
const [view, setView] = useState('dashboard');
const [loading, setLoading] = useState(true);
const [compareWatches, setCompareWatches] = useState([]);
const [showComparison, setShowComparison] = useState(false);
const [darkMode] = useDarkMode();
// Preload critical resources on mount
useEffect(() => {
preloadCriticalResources();
loadData();
handleDeepLinking();
// Prefetch API data for instant navigation
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
fetch('/api/statistics').then(r => r.json());
});
}
}, []);
// Update SEO when view changes
useEffect(() => {
cleanupStructuredData();
switch (view) {
case 'dashboard':
updateDashboardSEO();
break;
case 'list':
updateListPageSEO();
break;
case 'chart':
if (selectedWatch) {
updateWatchPageSEO(selectedWatch);
}
break;
case 'compare':
updateComparisonPageSEO(compareWatches);
break;
default:
updateDashboardSEO();
}
}, [view, selectedWatch, compareWatches]);
const loadData = async () => {
try {
setLoading(true);
if (getOnlineStatus()) {
// Use Promise.all for parallel requests
const [watchesRes, statsRes] = await Promise.all([
fetch('/api/watches'),
fetch('/api/statistics')
]);
const watchesData = await watchesRes.json();
const statsData = await statsRes.json();
const watchesList = watchesData.watches || watchesData;
setWatches(watchesList);
setStatistics(statsData);
// Save to offline storage asynchronously
saveWatchesOffline(watchesList);
} else {
console.log('Loading from offline storage...');
const offlineWatches = await getWatchesOffline();
setWatches(offlineWatches);
setStatistics(null);
}
} catch (error) {
console.error('Error loading data:', error);
// Fallback to offline storage
const offlineWatches = await getWatchesOffline();
if (offlineWatches.length > 0) {
console.log('Loaded from offline cache');
setWatches(offlineWatches);
}
} finally {
setLoading(false);
}
};
const handleDeepLinking = () => {
const params = new URLSearchParams(window.location.search);
const watchId = params.get('watch');
const viewParam = params.get('view');
if (viewParam && ['dashboard', 'list', '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 = React.useCallback((watch) => {
setSelectedWatch(watch);
setView('chart');
const newUrl = `${window.location.pathname}?watch=${watch.id}`;
window.history.pushState({ watch: watch.id }, '', newUrl);
updateWatchPageSEO(watch);
}, []);
const handleBackToList = React.useCallback(() => {
setView('list');
const newUrl = `${window.location.pathname}?view=list`;
window.history.pushState({ view: 'list' }, '', newUrl);
updateListPageSEO();
}, []);
const handleCompareToggle = React.useCallback(() => {
setShowComparison(prev => !prev);
}, []);
const handleAddToCompare = React.useCallback((watch) => {
setCompareWatches(prev => {
if (prev.length < 3 && !prev.find(w => w.id === watch.id)) {
return [...prev, watch];
}
return prev;
});
}, []);
const handleRemoveFromCompare = React.useCallback((watchId) => {
setCompareWatches(prev => prev.filter(w => w.id !== watchId));
}, []);
if (loading) {
return <LoadingFallback message="Loading Omega Watch Collection..." />;
}
return (
<div className={`min-h-screen ${darkMode ? 'dark' : ''}`}>
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
{/* Lazy load PWA components */}
<Suspense fallback={null}>
<OfflineIndicator />
<InstallPrompt variant="banner" />
</Suspense>
{/* Header */}
<header role="banner">
<EnhancedHeader view={view} setView={setView} />
</header>
{/* Main content with lazy loading */}
<main id="main-content" role="main" className="container mx-auto px-4 py-6 md:py-8">
<AnimatePresence mode="wait">
{view === 'dashboard' && (
<Suspense fallback={<LoadingFallback message="Loading Dashboard..." />}>
<motion.article
key="dashboard"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
<EnhancedDashboard
statistics={statistics}
watches={watches}
onWatchSelect={handleWatchSelect}
loading={loading}
/>
</motion.article>
</Suspense>
)}
{view === 'list' && (
<Suspense fallback={<LoadingFallback message="Loading Watch List..." />}>
<motion.section
key="list"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
<EnhancedWatchList
watches={watches}
onWatchSelect={handleWatchSelect}
loading={loading}
onCompare={handleAddToCompare}
/>
</motion.section>
</Suspense>
)}
{view === 'chart' && selectedWatch && (
<Suspense fallback={<LoadingFallback message="Loading Price Chart..." />}>
<motion.article
key="chart"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
>
<PriceChart watch={selectedWatch} onBack={handleBackToList} />
</motion.article>
</Suspense>
)}
{view === 'compare' && (
<Suspense fallback={<LoadingFallback message="Loading Comparison..." />}>
<motion.section
key="compare"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.3 }}
>
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h1 className="text-2xl font-bold text-gray-800 dark:text-white mb-4">
Watch Comparison Tool
</h1>
<p className="text-gray-600 dark:text-gray-300 mb-6">
Select watches from the list to compare their specifications and price history side-by-side.
</p>
<button
onClick={() => setShowComparison(true)}
className="bg-omega-red hover:bg-red-700 text-white font-semibold px-6 py-3 rounded-lg transition-colors"
aria-label="Compare selected watches"
>
{compareWatches.length > 0
? `Compare ${compareWatches.length} ${compareWatches.length === 1 ? 'Watch' : 'Watches'}`
: 'Select Watches to Compare'}
</button>
{statistics && statistics.topPerformers && (
<section className="mt-8" aria-labelledby="top-performers-heading">
<h2 id="top-performers-heading" className="text-lg font-bold text-gray-800 dark:text-white mb-4">
Quick Select: Top Performers
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{statistics.topPerformers.slice(0, 6).map(topWatch => {
const fullWatch = watches.find(w => w.id === topWatch.id);
const isSelected = compareWatches.find(w => w.id === topWatch.id);
return fullWatch ? (
<button
key={fullWatch.id}
onClick={() => {
if (isSelected) {
handleRemoveFromCompare(fullWatch.id);
} else {
handleAddToCompare(fullWatch);
}
}}
className={`p-4 rounded-lg border-2 transition-all text-left ${
isSelected
? 'border-omega-red bg-red-50 dark:bg-red-900/20'
: 'border-gray-200 dark:border-gray-700 hover:border-omega-red'
}`}
aria-pressed={!!isSelected}
>
<h3 className="font-semibold text-gray-800 dark:text-white">
{fullWatch.model}
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">
{fullWatch.series}
</p>
<p className="text-lg font-bold text-green-600 mt-2">
+{topWatch.appreciation}%
</p>
</button>
) : null;
})}
</div>
</section>
)}
</div>
</motion.section>
</Suspense>
)}
</AnimatePresence>
</main>
{/* Comparison Modal */}
<AnimatePresence>
{showComparison && compareWatches.length > 0 && (
<Suspense fallback={null}>
<WatchComparison
watches={watches}
selectedWatches={compareWatches}
onClose={() => setShowComparison(false)}
onAddWatch={handleAddToCompare}
onRemoveWatch={handleRemoveFromCompare}
/>
</Suspense>
)}
</AnimatePresence>
{/* Footer */}
<footer role="contentinfo" className="bg-omega-navy dark:bg-black text-white py-6 mt-12">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 text-center md:text-left">
<section aria-labelledby="footer-about">
<h2 id="footer-about" className="font-bold text-lg mb-2">Omega Price History</h2>
<p className="text-sm text-gray-400">
Historical price tracking and analysis system for Omega timepieces.
Track 32 iconic models from 1957 to 2024.
</p>
</section>
<section aria-labelledby="footer-features">
<h2 id="footer-features" className="font-bold text-lg mb-2">Features</h2>
<ul className="text-sm text-gray-400 space-y-1">
<li>32 Iconic Omega Watches</li>
<li>Historical Price Data (1957-2024)</li>
<li>Advanced Analytics & Predictions</li>
<li>Dark Mode Support</li>
<li>Watch Comparison Tools</li>
</ul>
</section>
<section aria-labelledby="footer-info">
<h2 id="footer-info" className="font-bold text-lg mb-2">System Info</h2>
<p className="text-sm text-gray-400">
Server: 45.61.58.125:7600
</p>
<p className="text-sm text-gray-400">
Status: <span className="text-green-400">Online</span>
</p>
<nav aria-label="Footer navigation">
<ul className="text-sm text-gray-400 space-y-1 mt-2">
<li><a href="/sitemap.xml" className="hover:text-white">Sitemap</a></li>
<li><a href="/api/docs" className="hover:text-white">API Documentation</a></li>
</ul>
</nav>
</section>
</div>
<div className="mt-6 pt-6 border-t border-gray-700 text-center text-sm text-gray-400">
<p>© 2024 Omega Watch Price History System | Educational purposes only</p>
<p className="mt-1">Historical pricing data for luxury watch collectors and investors</p>
</div>
</div>
</footer>
</div>
</div>
);
}
export default App;