feat: Initial commit - Hladinator (Water Reservoir Dashboard)
continuous-integration/drone/push Build encountered an error
continuous-integration/drone/push Build encountered an error
- Setup React project with Vite and TypeScript - Built dynamic UI supporting Dark/Light mode and CS/EN localization - Added Lakes Overview grid with mock data for 40+ reservoirs - Created interactive Recharts charts for water levels and flow rates - Designed fully responsive premium mobile layout with custom SVG KPIs - Developed TypeScript scraper scripts to fetch reservoir data
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { FiArrowUp, FiArrowDown } from 'react-icons/fi';
|
||||
import { type Language, t } from '../translations';
|
||||
|
||||
interface KpiData {
|
||||
level: number;
|
||||
inflow: number;
|
||||
outflow: number;
|
||||
volume: number;
|
||||
fullness: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: KpiData;
|
||||
language: Language;
|
||||
lakeName?: string;
|
||||
}
|
||||
|
||||
const KpiCards = ({ data, language, lakeName = 'Lipno 1' }: Props) => {
|
||||
const dict = t[language].kpi;
|
||||
const flowDiff = data.inflow - data.outflow;
|
||||
|
||||
return (
|
||||
<div className="kpi-container-mobile">
|
||||
{/* CARD 1: HLADINA */}
|
||||
<div className="kpi-card-full">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<div>
|
||||
<div style={{ fontSize: '1rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
|
||||
{dict.level} {lakeName}
|
||||
</div>
|
||||
<div style={{ fontSize: '2.5rem', fontWeight: 'bold', color: 'var(--color-cyan)', lineHeight: 1 }}>
|
||||
{data.level.toFixed(2)} <span style={{ fontSize: '1.2rem', fontWeight: 'normal', color: 'var(--text-main)' }}>m n. m.</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '0.9rem', color: 'var(--color-green)', marginTop: '0.5rem' }}>
|
||||
(+0.02 m / 24h)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Decorative Circle */}
|
||||
<div style={{ width: '60px', height: '60px', position: 'relative' }}>
|
||||
<svg width="60" height="60" viewBox="0 0 60 60">
|
||||
<circle cx="30" cy="30" r="26" fill="transparent" stroke="rgba(255,255,255,0.05)" strokeWidth="6" />
|
||||
<circle cx="30" cy="30" r="26" fill="transparent" stroke="var(--color-cyan)" strokeWidth="6" strokeDasharray="163" strokeDashoffset="40" strokeLinecap="round" transform="rotate(-90 30 30)" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="kpi-row-half">
|
||||
{/* CARD 2: PRŮTOK */}
|
||||
<div className="kpi-card-half">
|
||||
<div style={{ fontSize: '1rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
|
||||
{dict.flow}
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', height: '100%' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.25rem', fontSize: '0.85rem' }}>
|
||||
<span style={{ color: 'var(--text-main)' }}>{dict.inflow}: <span style={{ fontWeight: 'bold' }}>{data.inflow.toFixed(1)} m³/s</span></span>
|
||||
<span style={{ color: 'var(--text-main)' }}>{dict.outflow}: <span style={{ fontWeight: 'bold' }}>{data.outflow.toFixed(1)} m³/s</span> <FiArrowDown color="var(--color-red)" /></span>
|
||||
</div>
|
||||
|
||||
{/* Flow Circle */}
|
||||
<div style={{ width: '50px', height: '50px', position: 'relative' }}>
|
||||
<svg width="50" height="50" viewBox="0 0 50 50">
|
||||
<circle cx="25" cy="25" r="22" fill="transparent" stroke="rgba(255,255,255,0.05)" strokeWidth="4" />
|
||||
<circle cx="25" cy="25" r="22" fill="transparent" stroke="var(--color-cyan)" strokeWidth="4" strokeDasharray="138" strokeDashoffset="40" strokeLinecap="round" transform="rotate(-90 25 25)" />
|
||||
</svg>
|
||||
<div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<span style={{ fontSize: '0.8rem', fontWeight: 'bold', lineHeight: 1 }}>{Math.max(data.inflow, data.outflow).toFixed(1)}</span>
|
||||
<span style={{ fontSize: '0.5rem', color: 'var(--text-muted)' }}>m³/s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CARD 3: NAPLNĚNOST */}
|
||||
<div className="kpi-card-half">
|
||||
<div style={{ fontSize: '1rem', color: 'var(--text-muted)', marginBottom: '0.5rem' }}>
|
||||
{dict.fullness}
|
||||
</div>
|
||||
<div style={{ fontSize: '2rem', fontWeight: 'bold', lineHeight: 1, marginBottom: '0.5rem' }}>
|
||||
{data.fullness.toFixed(1)}%
|
||||
</div>
|
||||
<div style={{ fontSize: '0.85rem', color: 'var(--text-muted)' }}>
|
||||
{dict.volume}: {data.volume.toFixed(1)} mil. m³
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KpiCards;
|
||||
Reference in New Issue
Block a user