feat: stránka doplnit údaje předělána na samostatnou stránku jídel + přidáno přejmenování
92 lines
3.8 KiB
TypeScript
92 lines
3.8 KiB
TypeScript
import { StatsRange } from "../enums";
|
|
import { formatDateString, getHumanMonth, shiftDate } from "../Utils";
|
|
|
|
/** Vzhled bubliny s hodnotou — recharts ji kreslí inline stylem, ne přes CSS třídu. */
|
|
export const TOOLTIP_STYLE = {
|
|
background: 'var(--ft-bg-card)',
|
|
border: '1px solid var(--ft-border)',
|
|
borderRadius: 'var(--ft-radius-sm)',
|
|
color: 'var(--ft-text)',
|
|
};
|
|
|
|
/** Hranice zvoleného období statistik, YYYY-MM-DD včetně. */
|
|
export type StatsBounds = { from: string, to: string };
|
|
|
|
/**
|
|
* Popisek období na ose grafu — den u měsíce, měsíc u roku, rok u celého přehledu.
|
|
*
|
|
* @param period označení období ze serveru (YYYY-MM-DD, YYYY-MM nebo YYYY)
|
|
* @param mode zvolený rozsah statistik
|
|
*/
|
|
export function formatPeriodLabel(period: string, mode: StatsRange): string {
|
|
if (mode === StatsRange.MESIC) return formatDateString(period).slice(0, 5);
|
|
if (mode === StatsRange.ROK) return getHumanMonth(period).split(' ')[0];
|
|
return period;
|
|
}
|
|
|
|
/** Vybere řadu součtů podle rozsahu — dny u měsíce, měsíce u roku, roky u celého přehledu. */
|
|
export function pickPeriods<T>(stats: { byDay: T[], byMonth: T[], byYear: T[] }, mode: StatsRange): T[] {
|
|
if (mode === StatsRange.MESIC) return stats.byDay;
|
|
if (mode === StatsRange.ROK) return stats.byMonth;
|
|
return stats.byYear;
|
|
}
|
|
|
|
/**
|
|
* Vrátí označení všech období rozsahu, i těch bez dat — prázdný den v grafu
|
|
* je informace sama o sobě.
|
|
*
|
|
* U "Vše" se začíná rokem prvního dne s daty, jinak by graf otevíralo deset let nul.
|
|
*
|
|
* @param range zobrazené období
|
|
* @param mode rozsah statistik
|
|
* @param firstDate první den s daty, YYYY-MM-DD; bez něj je u "Vše" výsledek prázdný
|
|
*/
|
|
export const listPeriods = (range: StatsBounds, mode: StatsRange, firstDate?: string): string[] => {
|
|
if (mode === StatsRange.MESIC) {
|
|
const periods: string[] = [];
|
|
for (let date = range.from; date <= range.to; date = shiftDate(date, 1)) periods.push(date);
|
|
return periods;
|
|
}
|
|
if (mode === StatsRange.ROK) {
|
|
const year = range.from.substring(0, 4);
|
|
return Array.from({ length: 12 }, (_, index) => `${year}-${String(index + 1).padStart(2, '0')}`);
|
|
}
|
|
if (!firstDate) return [];
|
|
const first = Number(firstDate.substring(0, 4));
|
|
const last = Number(range.to.substring(0, 4));
|
|
return Array.from({ length: last - first + 1 }, (_, index) => String(first + index));
|
|
};
|
|
|
|
/** Zkrátí datum dne na označení období grafu. */
|
|
export const periodOf = (date: string, mode: StatsRange): string => {
|
|
if (mode === StatsRange.MESIC) return date;
|
|
if (mode === StatsRange.ROK) return date.substring(0, 7);
|
|
return date.substring(0, 4);
|
|
};
|
|
|
|
/** Kolik zdrojů má v koláči vlastní výseč; zbytek se slije do "Ostatní". */
|
|
const SOURCE_SLICE_COUNT = 7;
|
|
|
|
/** Výseč koláče zdrojů. */
|
|
export type SourceSlice = { name: string, value: number, color: string };
|
|
|
|
/**
|
|
* Sestaví výseče koláče dle zdroje — příjmu i útraty. Barva jde podle pořadí ve
|
|
* výběru, zdrojů bývá hodně a pevná barva pro každý by paletu nestačila.
|
|
*
|
|
* @param bySource součty dle zdroje, sestupně dle zobrazované hodnoty
|
|
* @param getValue zobrazovaná hodnota součtu (kcal, Kč)
|
|
*/
|
|
export const buildSourceSlices = <T extends { key: string }>(
|
|
bySource: T[],
|
|
getValue: (total: T) => number,
|
|
): SourceSlice[] => {
|
|
const slices = bySource
|
|
.filter(total => getValue(total) > 0)
|
|
.slice(0, SOURCE_SLICE_COUNT)
|
|
.map((total, index) => ({ name: total.key, value: getValue(total), color: `var(--ft-series-${index + 1})` }));
|
|
const rest = bySource.slice(SOURCE_SLICE_COUNT).reduce((sum, total) => sum + getValue(total), 0);
|
|
if (rest > 0) slices.push({ name: 'Ostatní', value: rest, color: 'var(--ft-series-other)' });
|
|
return slices;
|
|
};
|