feat: předělání historie a profilu

This commit is contained in:
Ondřej Anděl committed 2026-10-07 14:48:21 +02:00
1 parent 9e72f431a1
commit 8fa7f601f2
97 files changed
+7502 -1034

No files matched your search

+56 -8
View File
@@ -1,4 +1,5 @@
import { MealTemplate } from "../../types";
import { MealSuggestion, MealTemplate, Nutrients } from "../../types";
import { NUTRIENTS_IN_ORDER } from "./enums";
const TOKEN_KEY = "token";
@@ -341,17 +342,64 @@ export const deriveRecipe = (
export const normalizeSearch = (value: string): string =>
value.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase().trim();
/** Popíše recept jednou řádkou — zdroj a hodnoty jedné porce, co je vyplněné. */
export const describeMealTemplate = (template: MealTemplate): string => [
template.source,
template.weight != null ? `porce ${formatWeight(template.weight)}` : undefined,
template.calories != null ? formatCalories(template.calories) : undefined,
template.price != null ? formatPrice(template.price) : undefined,
].filter(item => !!item).join(' · ');
/**
* Popíše šablonu jídla jednou řádkou — zdroj, gramáž porce, kalorie a cena,
* co je vyplněné. U receptu jsou to hodnoty jedné porce, které dopočítal server.
* Popíše jídlo z historie jednou řádkou — zdroj, gramáž, kalorie a cena, co je
* vyplněné. Gramáž a kalorie se dopočtou stejně, jako je dopočte nový záznam.
*/
export const describeMealTemplate = (template: MealTemplate): string => {
const derived = deriveAmounts(template.price, template.pricePer100g, template.weight, template.caloriesPer100g);
const calories = derived.calories ?? template.calories;
export const describeMealSuggestion = (suggestion: MealSuggestion): string => {
const derived = deriveAmounts(suggestion.price, suggestion.pricePer100g, suggestion.weight, suggestion.caloriesPer100g);
const calories = derived.calories ?? suggestion.calories;
return [
template.source,
suggestion.source,
derived.weight != null ? formatWeight(derived.weight) : undefined,
calories != null ? formatCalories(calories) : undefined,
template.price != null ? formatPrice(template.price) : undefined,
suggestion.price != null ? formatPrice(suggestion.price) : undefined,
].filter(item => !!item).join(' · ');
};
/** Zaokrouhlí na desetiny gramu — přesnost, kterou uvádějí etikety. */
const roundTenth = (value: number): number => Math.round(value * 10) / 10;
/**
* Spočítá živiny porce z hodnot na 100 g, gramáže a násobku.
* Musí dávat stejné výsledky jako `getPortionNutrients` na serveru.
*/
export const getPortionNutrients = (
nutrientsPer100g: Nutrients | undefined,
weight: number | undefined,
multiplier?: number,
): Nutrients | undefined => {
if (!nutrientsPer100g || weight == null) return undefined;
const result: Nutrients = {};
for (const key of NUTRIENTS_IN_ORDER) {
const per100g = nutrientsPer100g[key];
if (per100g != null) {
result[key] = roundTenth(per100g * weight / 100 * (multiplier ?? 1));
}
}
return result;
};
/** Naformátuje množství živiny v gramech, např. 6.3 → "6,3 g". */
export const formatNutrient = (grams?: number): string =>
grams == null ? '—' : `${grams.toLocaleString('cs-CZ', { maximumFractionDigits: 1 })} g`;
/** Převede gramy živiny zadané uživatelem (i s desetinnou čárkou) na číslo; záporný vstup odmítne. */
export const parseNutrientInput = (value: string): number | undefined => {
const trimmed = value.trim().replace(',', '.');
if (!trimmed.length) return undefined;
const parsed = Number(trimmed);
return Number.isFinite(parsed) && parsed >= 0 ? roundTenth(parsed) : undefined;
};
/** Převede gramy živiny na text pro editační pole. */
export const formatNutrientInput = (grams?: number): string =>
grams == null ? '' : String(grams).replace('.', ',');