Files
davisfe.cz/src/App.tsx
T

116 lines
4.2 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Routes, Route, useParams, useLocation, useNavigate, 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 { 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">
{/* 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;