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 = { 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>; /** * 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)); };