feat: implement multilingual SEO support and enhance map UI with data synchronization updates
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { FiTrendingUp, FiTrendingDown, FiStar } from 'react-icons/fi';
|
||||
import { type Language, t } from '../translations';
|
||||
import { AreaChart, Area, ResponsiveContainer, YAxis } from 'recharts';
|
||||
@@ -45,7 +46,7 @@ const LakeCard = ({ lake, language, isFav, onToggleFav }: { lake: Lake, language
|
||||
{/* Star / Favorite button */}
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onToggleFav(lake.id); }}
|
||||
title={isFav ? 'Odepnout' : 'Připnout jako oblíbené'}
|
||||
title={isFav ? (language === 'cs' ? 'Odepnout' : 'Unpin') : (language === 'cs' ? 'Připnout jako oblíbené' : 'Pin to favorites')}
|
||||
style={{
|
||||
position: 'absolute', top: '1rem', right: '1rem',
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
@@ -131,7 +132,7 @@ const SmallLakeCard = ({ lake, isFav, onToggleFav }: { lake: Lake, isFav: boolea
|
||||
{/* Star button */}
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onToggleFav(lake.id); }}
|
||||
title={isFav ? 'Odepnout' : 'Připnout jako oblíbené'}
|
||||
title={isFav ? (language === 'cs' ? 'Odepnout' : 'Unpin') : (language === 'cs' ? 'Připnout jako oblíbené' : 'Pin to favorites')}
|
||||
style={{
|
||||
position: 'absolute', top: '0.6rem', right: '0.6rem',
|
||||
background: 'none', border: 'none', cursor: 'pointer',
|
||||
@@ -166,7 +167,6 @@ const SmallLakeCard = ({ lake, isFav, onToggleFav }: { lake: Lake, isFav: boolea
|
||||
|
||||
const LakesOverview = ({ language }: Props) => {
|
||||
const [lakes, setLakes] = useState<Lake[]>([]);
|
||||
const [sortBy, setSortBy] = useState<'name' | 'level' | 'capacity' | 'inflow'>('name');
|
||||
const { isFavorite, toggleFavorite, favorites } = useFavorites();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -186,41 +186,29 @@ const LakesOverview = ({ language }: Props) => {
|
||||
const priorityLakes = lakes.filter(l => l.priority && !isFavorite(l.id));
|
||||
const otherLakes = lakes.filter(l => !l.priority && !isFavorite(l.id));
|
||||
|
||||
otherLakes.sort((a, b) => {
|
||||
if (sortBy === 'name') return a.name.localeCompare(b.name);
|
||||
if (sortBy === 'level') return b.level - a.level;
|
||||
if (sortBy === 'capacity') return b.capacity - a.capacity;
|
||||
if (sortBy === 'inflow') return b.inflow - a.inflow;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const sortButtonStyle = (type: string) => ({
|
||||
background: 'none', border: 'none',
|
||||
color: sortBy === type ? 'var(--text-main)' : 'var(--text-muted)',
|
||||
cursor: 'pointer', fontSize: '0.85rem'
|
||||
});
|
||||
otherLakes.sort((a, b) => a.name.localeCompare(b.name));
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end' }}>
|
||||
<div>
|
||||
<h1 style={{ fontSize: '1.75rem', fontWeight: 'bold', margin: '0 0 0.5rem 0' }}>Overview: Lakes ({lakes.length})</h1>
|
||||
<p style={{ margin: 0, color: 'var(--text-muted)', fontSize: '0.9rem' }}>Monitoring {lakes.length} reservoirs across the Czech Republic</p>
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', color: 'var(--text-muted)', fontSize: '0.85rem' }}>
|
||||
<span>Sort by:</span>
|
||||
<button style={sortButtonStyle('name')} onClick={() => setSortBy('name')}>Name (A-Z)</button> |
|
||||
<button style={sortButtonStyle('level')} onClick={() => setSortBy('level')}>Level</button> |
|
||||
<button style={sortButtonStyle('capacity')} onClick={() => setSortBy('capacity')}>Capacity</button> |
|
||||
<button style={sortButtonStyle('inflow')} onClick={() => setSortBy('inflow')}>Flow In</button>
|
||||
</div>
|
||||
<Helmet>
|
||||
<title>{t[language].seo.homeTitle}</title>
|
||||
<meta name="description" content={t[language].seo.homeDesc} />
|
||||
<meta property="og:title" content={t[language].seo.homeTitle} />
|
||||
<meta property="og:description" content={t[language].seo.homeDesc} />
|
||||
</Helmet>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
||||
<h1 style={{ fontSize: '1.75rem', fontWeight: 'bold', margin: '0', color: 'var(--text-main)' }}>{t[language].sidebar.lakes} ({lakes.length})</h1>
|
||||
<p style={{ margin: 0, color: 'var(--text-muted)', fontSize: '0.95rem', lineHeight: '1.5' }}>
|
||||
{t[language].seo.homeDesc}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Favorites section */}
|
||||
{favoriteLakes.length > 0 && (
|
||||
<section>
|
||||
<h2 style={{ fontSize: '1.1rem', fontWeight: 'bold', marginBottom: '1rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
|
||||
<FiStar size={16} fill="#f59e0b" color="#f59e0b" /> Oblíbená ({favoriteLakes.length})
|
||||
<FiStar size={16} fill="#f59e0b" color="#f59e0b" /> {language === 'cs' ? 'Oblíbená' : 'Favorites'} ({favoriteLakes.length})
|
||||
</h2>
|
||||
<div style={{
|
||||
display: 'grid',
|
||||
|
||||
Reference in New Issue
Block a user