feat: stránka doplnit údaje předělána na samostatnou stránku jídel + přidáno přejmenování
75 lines
3.1 KiB
TypeScript
75 lines
3.1 KiB
TypeScript
import { IntakeStats, MealType } from "../../../types";
|
|
import { MEAL_TYPES_IN_ORDER, StatsRange } from "../enums";
|
|
import { StatsBounds, formatPeriodLabel, listPeriods, pickPeriods } from "./statsShared";
|
|
|
|
/**
|
|
* Barva chodu v grafech příjmu. Pořadí sleduje průběh dne, takže sousední
|
|
* chody mají od sebe dobře odlišitelné barvy palety.
|
|
*/
|
|
export const MEAL_TYPE_COLORS: Record<MealType, string> = {
|
|
SNIDANE: 'var(--ft-series-4)',
|
|
DOPOLEDNI_SVACINA: 'var(--ft-series-5)',
|
|
OBED: 'var(--ft-series-1)',
|
|
ODPOLEDNI_SVACINA: 'var(--ft-series-3)',
|
|
VECERE: 'var(--ft-series-7)',
|
|
PULNOCNI_SVACINA: 'var(--ft-series-2)',
|
|
JINE: 'var(--ft-series-other)',
|
|
};
|
|
|
|
/** Jeden sloupec grafu příjmu — kcal po chodech pod klíčem chodu (`OBED`, ...). */
|
|
export type IntakeSeriesRow = {
|
|
label: string;
|
|
calories: number;
|
|
/** Výdej dní s kaloriemi; v období bez nich chybí, čára se tam přeruší místo pádu na nulu. */
|
|
burn: number | null;
|
|
/** Výdej posunutý o cílovou bilanci — kolik se mělo sníst; jen s nastaveným cílem. */
|
|
target: number | null;
|
|
/** Cílová bilance za dny s kaloriemi. */
|
|
targetBalance: number | null;
|
|
dayCount: number;
|
|
caloriesDayCount: number;
|
|
} & Partial<Record<MealType, number>>;
|
|
|
|
/**
|
|
* Sestaví sloupce grafu příjmu: každý chod vlastní díl sloupce, nad nimi čára
|
|
* výdeje a případně cílového příjmu.
|
|
*
|
|
* Období bez zapsaného jídla zůstávají v grafu jako prázdná, ale výdej v nich
|
|
* chybí — nezapsaný den (nebo den s jídly bez kalorií) není den, kdy se nejedlo.
|
|
*
|
|
* @param stats statistiky příjmu ze serveru
|
|
* @param range zobrazené období
|
|
* @param mode rozsah statistik
|
|
*/
|
|
export const buildIntakeSeries = (stats: IntakeStats, range: StatsBounds, mode: StatsRange): IntakeSeriesRow[] => {
|
|
const totalMap = new Map(pickPeriods(stats, mode).map(total => [total.period, total]));
|
|
return listPeriods(range, mode, stats.byDay[0]?.period).map(period => {
|
|
const total = totalMap.get(period);
|
|
const row: IntakeSeriesRow = {
|
|
label: formatPeriodLabel(period, mode),
|
|
calories: total?.calories ?? 0,
|
|
burn: null,
|
|
target: null,
|
|
targetBalance: null,
|
|
dayCount: total?.dayCount ?? 0,
|
|
caloriesDayCount: total?.caloriesDayCount ?? 0,
|
|
};
|
|
if (!total?.caloriesDayCount) return row;
|
|
row.burn = total.burnCalories;
|
|
if (stats.targetDailyBalance != null) {
|
|
row.targetBalance = stats.targetDailyBalance * total.caloriesDayCount;
|
|
row.target = total.burnCalories + row.targetBalance;
|
|
}
|
|
for (const group of total.byMealType) {
|
|
row[group.key as MealType] = group.calories;
|
|
}
|
|
return row;
|
|
});
|
|
};
|
|
|
|
/** Chody, které v období něco dodaly, v pořadí průběhu dne — pořadí dílů sloupce i legendy. */
|
|
export const getUsedMealTypes = (stats: IntakeStats): MealType[] => {
|
|
const used = new Set(stats.byMealType.map(group => group.key));
|
|
return MEAL_TYPES_IN_ORDER.filter(mealType => used.has(mealType));
|
|
};
|