118 lines
4.3 KiB
TypeScript
118 lines
4.3 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Routes, Route, useParams, Navigate } from 'react-router-dom';
|
|
import LakeDetail from './components/LakeDetail';
|
|
import LakesOverview from './components/LakesOverview';
|
|
import LakeMap from './components/LakeMap';
|
|
import FavoritesOverview from './components/FavoritesOverview';
|
|
import Sidebar from './components/Sidebar';
|
|
import Topbar from './components/Topbar';
|
|
import SettingsModal from './components/SettingsModal';
|
|
import { DisclaimerModal } from './components/DisclaimerModal';
|
|
import { type Language, t } from './translations';
|
|
import { lakesConfig } from '../scripts/lakesConfig';
|
|
import { slugify } from './utils/slugify';
|
|
import './App.css';
|
|
|
|
const LakeDetailWrapper = ({ language, windUnit }: { language: Language, windUnit: 'kmh' | 'ms' }) => {
|
|
const { slug } = useParams();
|
|
const lake = lakesConfig.find(l => slugify(l.text) === slug);
|
|
|
|
if (!lake) return <Navigate to="/" replace />;
|
|
|
|
return <LakeDetail language={language} lakeId={lake.id} windUnit={windUnit} />;
|
|
};
|
|
|
|
function App() {
|
|
const [language, setLanguage] = useState<Language>(() => {
|
|
return (localStorage.getItem('hladinator_lang') as Language) || 'en';
|
|
});
|
|
const [theme, setTheme] = useState<'dark' | 'light'>(() => {
|
|
return (localStorage.getItem('hladinator_theme') as 'dark' | 'light') || 'dark';
|
|
});
|
|
const [windUnit, setWindUnit] = useState<'kmh' | 'ms'>(() => {
|
|
return (localStorage.getItem('hladinator_windUnit') as 'kmh' | 'ms') || 'kmh';
|
|
});
|
|
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
if (theme === 'light') {
|
|
document.body.classList.add('light-mode');
|
|
} else {
|
|
document.body.classList.remove('light-mode');
|
|
}
|
|
localStorage.setItem('hladinator_theme', theme);
|
|
|
|
// Clean up empty hash from URL (e.g. if the user clicked an empty anchor)
|
|
if (window.location.href.endsWith('#')) {
|
|
window.history.replaceState(null, '', window.location.href.slice(0, -1));
|
|
}
|
|
}, [theme]);
|
|
|
|
useEffect(() => {
|
|
localStorage.setItem('hladinator_lang', language);
|
|
}, [language]);
|
|
|
|
useEffect(() => {
|
|
localStorage.setItem('hladinator_windUnit', windUnit);
|
|
}, [windUnit]);
|
|
|
|
return (
|
|
<div className="dashboard-container">
|
|
<DisclaimerModal language={language} setLanguage={setLanguage} />
|
|
{/* Mobile overlay */}
|
|
{isMobileMenuOpen && (
|
|
<div
|
|
style={{ position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', backgroundColor: 'rgba(0,0,0,0.5)', zIndex: 999 }}
|
|
onClick={() => setIsMobileMenuOpen(false)}
|
|
></div>
|
|
)}
|
|
|
|
<Sidebar
|
|
language={language}
|
|
onOpenSettings={() => setIsSettingsOpen(true)}
|
|
isMobileMenuOpen={isMobileMenuOpen}
|
|
onCloseMobileMenu={() => setIsMobileMenuOpen(false)}
|
|
/>
|
|
|
|
<div className="main-content" style={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
|
<Topbar language={language} onToggleMobileMenu={() => setIsMobileMenuOpen(!isMobileMenuOpen)} />
|
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
<Routes>
|
|
<Route path="/" element={<LakesOverview language={language} />} />
|
|
<Route path="/favorites" element={<FavoritesOverview language={language} />} />
|
|
<Route path="/map" element={<LakeMap language={language} />} />
|
|
<Route path="/:slug" element={<LakeDetailWrapper language={language} windUnit={windUnit} />} />
|
|
</Routes>
|
|
</div>
|
|
<footer style={{
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
padding: '1.5rem',
|
|
fontSize: '0.8rem',
|
|
color: 'var(--text-muted)',
|
|
marginTop: 'auto'
|
|
}}>
|
|
<span>{t[language].chart.dataSources} pvl.cz, open-meteo.com</span>
|
|
<span>{t[language].chart.createdIn}</span>
|
|
</footer>
|
|
</div>
|
|
|
|
{isSettingsOpen && (
|
|
<SettingsModal
|
|
language={language}
|
|
setLanguage={setLanguage}
|
|
theme={theme}
|
|
setTheme={setTheme}
|
|
windUnit={windUnit}
|
|
setWindUnit={setWindUnit}
|
|
onClose={() => setIsSettingsOpen(false)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default App;
|