CI / Generate TypeScript types (push) Successful in 10s
CI / Server unit tests (push) Successful in 21s
CI / Build server (push) Successful in 24s
CI / Build client (push) Successful in 38s
CI / Playwright E2E tests (push) Successful in 1m18s
CI / Build and push Docker image (push) Successful in 40s
CI / Notify (push) Successful in 2s
136 lines
4.3 KiB
TypeScript
136 lines
4.3 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
import Footer from "../components/Footer";
|
|
import Header from "../components/Header";
|
|
import { useAuth } from "../context/auth";
|
|
import Login from "../Login";
|
|
import { formatDate, getFirstWorkDayOfWeek, getHumanDate, getLastWorkDayOfWeek } from "../Utils";
|
|
import { WeeklyStats, LunchChoice, getStats } from "../../../types";
|
|
import Loader from "../components/Loader";
|
|
import { faChevronLeft, faChevronRight, faGear } from "@fortawesome/free-solid-svg-icons";
|
|
import { Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import { getLunchChoiceName } from "../enums";
|
|
import './StatsPage.scss';
|
|
|
|
const CHART_WIDTH = 1400;
|
|
const CHART_HEIGHT = 700;
|
|
const STROKE_WIDTH = 2.5;
|
|
|
|
const COLORS = [
|
|
'#ff1493',
|
|
'#1e90ff',
|
|
'#c5a700',
|
|
'#006400',
|
|
'#b300ff',
|
|
'#ff4500',
|
|
'#bc8f8f',
|
|
'#00ff00',
|
|
'#7c7c7c',
|
|
]
|
|
|
|
export default function StatsPage() {
|
|
const auth = useAuth();
|
|
const [dateRange, setDateRange] = useState<Date[]>();
|
|
const [data, setData] = useState<WeeklyStats>();
|
|
|
|
// Prvotní nastavení aktuálního týdne
|
|
useEffect(() => {
|
|
const today = new Date();
|
|
setDateRange([getFirstWorkDayOfWeek(today), getLastWorkDayOfWeek(today)]);
|
|
}, []);
|
|
|
|
// Přenačtení pro zvolený týden
|
|
useEffect(() => {
|
|
if (dateRange) {
|
|
getStats({ query: { startDate: formatDate(dateRange[0]), endDate: formatDate(dateRange[1]) } }).then(response => {
|
|
setData(response.data);
|
|
});
|
|
}
|
|
}, [dateRange]);
|
|
|
|
const renderLine = (location: LunchChoice) => {
|
|
const index = Object.values(LunchChoice).indexOf(location);
|
|
return <Line key={location} name={getLunchChoiceName(location)} type="monotone" dataKey={data => data.locations[location] ?? 0} stroke={COLORS[index]} strokeWidth={STROKE_WIDTH} />
|
|
}
|
|
|
|
const handlePreviousWeek = () => {
|
|
if (dateRange) {
|
|
const previousStartDate = new Date(dateRange[0]);
|
|
previousStartDate.setDate(previousStartDate.getDate() - 7);
|
|
const previousEndDate = new Date(previousStartDate);
|
|
previousEndDate.setDate(previousEndDate.getDate() + 4);
|
|
setDateRange([previousStartDate, previousEndDate]);
|
|
}
|
|
}
|
|
|
|
const handleNextWeek = () => {
|
|
if (dateRange) {
|
|
const nextStartDate = new Date(dateRange[0]);
|
|
nextStartDate.setDate(nextStartDate.getDate() + 7);
|
|
const nextEndDate = new Date(nextStartDate);
|
|
nextEndDate.setDate(nextEndDate.getDate() + 4);
|
|
setDateRange([nextStartDate, nextEndDate]);
|
|
}
|
|
}
|
|
|
|
const isCurrentOrFutureWeek = useMemo(() => {
|
|
if (!dateRange) return true;
|
|
const currentWeekEnd = getLastWorkDayOfWeek(new Date());
|
|
currentWeekEnd.setHours(23, 59, 59, 999);
|
|
return dateRange[1] >= currentWeekEnd;
|
|
}, [dateRange]);
|
|
|
|
const handleKeyDown = useCallback((e: any) => {
|
|
if (e.keyCode === 37) {
|
|
handlePreviousWeek();
|
|
} else if (e.keyCode === 39 && !isCurrentOrFutureWeek) {
|
|
handleNextWeek()
|
|
}
|
|
}, [dateRange, isCurrentOrFutureWeek]);
|
|
|
|
useEffect(() => {
|
|
document.addEventListener('keydown', handleKeyDown);
|
|
return () => {
|
|
document.removeEventListener('keydown', handleKeyDown);
|
|
}
|
|
}, [handleKeyDown]);
|
|
|
|
if (!auth?.login) {
|
|
return <Login />;
|
|
}
|
|
|
|
if (!dateRange) {
|
|
return <Loader
|
|
icon={faGear}
|
|
description={'Načítám data...'}
|
|
animation={'fa-bounce'}
|
|
/>
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<Header />
|
|
<div className="stats-page">
|
|
<h1>Statistiky</h1>
|
|
<div className="week-navigator">
|
|
<span title="Předchozí týden">
|
|
<FontAwesomeIcon icon={faChevronLeft} style={{ cursor: "pointer" }} onClick={handlePreviousWeek} />
|
|
</span>
|
|
<h2 className="date-range">{getHumanDate(dateRange[0])} - {getHumanDate(dateRange[1])}</h2>
|
|
<span title="Následující týden">
|
|
<FontAwesomeIcon icon={faChevronRight} style={{ cursor: "pointer", visibility: isCurrentOrFutureWeek ? "hidden" : "visible" }} onClick={handleNextWeek} />
|
|
</span>
|
|
</div>
|
|
<LineChart width={CHART_WIDTH} height={CHART_HEIGHT} data={data}>
|
|
{Object.values(LunchChoice).map(location => renderLine(location))}
|
|
<XAxis dataKey="date" />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Legend />
|
|
</LineChart>
|
|
</div>
|
|
<Footer />
|
|
</>
|
|
);
|
|
}
|