feat: import jen některých dnů
feat: násobení jídel/cviků feat: refaktor napojování šablon
This commit is contained in:
1 parent
c3990d80f8
commit
8dac67bdd1
37 files changed
+2314
-481
No files matched your search
@@ -74,7 +74,7 @@ cd client && yarn build # tsc --noEmit + vite build → client/dist
|
||||
`TemplatesPage` (šablony tréninků), `ProfilePage` (tělesné údaje a klidový výdej),
|
||||
`StatsPage` (statistiky + import)
|
||||
- **Components:** `components/` (Header, CalorieLookup) a `components/modals/`
|
||||
(MealModal, ActivityModal, ApplyWorkoutModal, SettingsModal, ImportModal)
|
||||
(MealModal, ActivityModal, ApplyWorkoutModal, ApplyMealModal, SettingsModal, ImportModal)
|
||||
- **Klientské enumy** patří do `enums.ts` jako TypeScript `enum` s mapou popisků,
|
||||
ne jako inline union u komponenty (`DayTab`, `StatsRange`)
|
||||
- **Context:** `context/auth.tsx` (JWT), `context/settings.tsx` (světlý/tmavý motiv)
|
||||
@@ -111,7 +111,23 @@ uživatel vybere zdroj.
|
||||
|
||||
`client/src/Utils.tsx` má stejnojmennou funkci pro živý náhled ve formuláři.
|
||||
**Když se změní jedna, musí se změnit i druhá** — jinak klient ukazuje něco jiného,
|
||||
než server uloží.
|
||||
než server uloží. Totéž platí pro `applyMultiplier` (`server/src/utils.ts`)
|
||||
a `deriveRecipe` (`server/src/mealTemplates.ts`).
|
||||
|
||||
## Násobek položky dne
|
||||
|
||||
Jídlo i aktivita mají volitelný `multiplier` (i necelý, 0,01–100): dvě sušenky
|
||||
jsou ×2, nedojedená polovina ×0,5. Mění se přímo z přehledu dne
|
||||
(`/meals/multiplier`, `/activities/multiplier`), bez otevření formuláře.
|
||||
|
||||
- **Uložené `calories` jsou vždy za jednu porci** a výsledek je
|
||||
`applyMultiplier(calories, multiplier)`. Kdyby se ukládal už vynásobený
|
||||
výsledek, opakovaná změna násobku by kumulovala zaokrouhlení.
|
||||
- Všechny součty (`buildDayRecord`, `buildActivityDay`, `statsService`) proto
|
||||
musí jít přes `applyMultiplier`, ne přes holé `calories`.
|
||||
- **Cena se nenásobí** — za nedojedené jídlo se platí celé. Dvě kusy se stejnou
|
||||
cenou jsou dva záznamy, nebo jedna cena zadaná za oba.
|
||||
- Násobek 1 je výchozí stav a neukládá se (stejně jako jedna série).
|
||||
|
||||
## Zdroje energetických hodnot
|
||||
|
||||
@@ -159,6 +175,11 @@ umí `createMealTemplateFromMeal`, které navíc **přepíše šablonu stejného
|
||||
opakované "uložit jako šablonu" u téhož jídla má dát jednu šablonu, ne několik
|
||||
stejných. Záznamy ve dni zůstanou v obou případech beze změny.
|
||||
|
||||
**Dodatečná zátěž** (`extraWeightGrams` u aktivity i cviku šablony, v gramech)
|
||||
je jen informace o tréninku, kalorie **nemění** — žádný jednoduchý vzorec
|
||||
spotřebu podle zátěže nedává, takže by šlo o vymyšlené číslo. Kdo chce, promítne
|
||||
ji do sazby na 100 jednotek.
|
||||
|
||||
`WorkoutTemplate` (`workouts.ts`) je pojmenovaný seznam položek. Položka je buď
|
||||
cvik (`kind: CVIK`), nebo odkaz na jinou šablonu (`kind: SABLONA`), jejíž cviky se
|
||||
při použití rozbalí. Použitím vzniknou běžné aktivity s vazbou `templateId` — jsou
|
||||
@@ -196,16 +217,27 @@ takže `DayPage` si vystačí s jedním voláním.
|
||||
## Šablony jídel
|
||||
|
||||
`MealTemplate` (`mealTemplates.ts`, klíč `mealTemplates:<login>`) je opakovaně
|
||||
jedené jídlo. Na rozdíl od šablon tréninků se **neskládá z jiných** — je to plochý
|
||||
záznam. Použitím vznikne běžný `MealEntry` přes `addMeal`, takže gramáž, cenu
|
||||
i kalorie dopočítá stejná logika jako u ručně zadaného jídla.
|
||||
jedené jídlo. Na rozdíl od šablon tréninků se **neskládá z jiných**. Použitím
|
||||
vznikne běžný `MealEntry` přes `addMeal`, takže gramáž, cenu i kalorie dopočítá
|
||||
stejná logika jako u ručně zadaného jídla. Při zakládání jídla v přehledu dne se
|
||||
šablony nabízejí k rozepsanému názvu (`TemplateSuggestions`).
|
||||
|
||||
Šablona může nést buď `calories` (celá porce), nebo `caloriesPer100g` + `weight`.
|
||||
Obojí projde `deriveAmounts`, takže stačí vyplnit, co o jídle víte.
|
||||
Šablona má druh (`kind`):
|
||||
|
||||
Pole `note` slouží i na postup přípravy u domácích jídel. Strukturované suroviny
|
||||
(recept jako seznam položek s množstvím) mohou přibýt později jako další volitelné
|
||||
pole, aniž by se muselo měnit cokoli stávajícího.
|
||||
- **HOTOVE** — objednané nebo koupené jídlo. Nese jen výsledné hodnoty porce:
|
||||
buď `calories`, nebo `caloriesPer100g` + `weight`; obojí projde `deriveAmounts`.
|
||||
- **RECEPT** — vařené jídlo se surovinami (`ingredients`), počtem porcí
|
||||
(`servings`), hmotností hotového jídla (`cookedWeight`) a postupem v `note`.
|
||||
`deriveRecipe` z nich při uložení spočítá hodnoty **jedné porce** a uloží je
|
||||
do stejných plochých polí (`weight`, `caloriesPer100g`, `calories`, `price`),
|
||||
takže použití šablony je pro oba druhy stejné. Energie na 100 g se bere
|
||||
z hmotnosti hotového jídla, ne ze součtu surovin — vařením se voda odpaří
|
||||
nebo nasákne. Postup se do záznamu dne nekopíruje.
|
||||
|
||||
Šablony uložené dřív, než druhy přibyly, nemají `kind`; `withKind` jim ho při
|
||||
načtení doplní na `HOTOVE`, v úložišti se nic nepřepisuje.
|
||||
`createMealTemplateFromMeal` recept stejného názvu **nepřepíše** (přišel by
|
||||
o suroviny a postup) a odmítne to chybou.
|
||||
|
||||
Kontrakt má vlastní `MealTemplateInput`, kde je `id` volitelné — `MealTemplate`
|
||||
ho vyžaduje a nešlo by jím posílat novou šablonu.
|
||||
@@ -229,7 +261,13 @@ ve všech třech formátech, které Luncher exportuje (XLSX, CSV, JSON — viz
|
||||
nejsou místa, takže se jako zdroj nepoužijí a záznam zůstane bez zdroje.
|
||||
- Duplicity řeší `importKey` složený z data, pořadí řádku v rámci dne, typu,
|
||||
jídla a částky — opakovaný import stejného měsíce nic nezduplikuje.
|
||||
- Režim (`ImportMode`): **PRIDAT** (výchozí) přeskočí už naimportované řádky;
|
||||
**NAHRADIT** v dnech, do kterých se něco zakládá, odebere dřívější záznamy
|
||||
z importu a založí je znovu. Ručně zadaná jídla zůstávají v obou režimech.
|
||||
- Endpoint podporuje `dryRun` pro náhled před uložením; klient ho vždy použije.
|
||||
Náhled vrací všechny řádky, které založit jde, i jídla, která už v dotčených
|
||||
dnech jsou (`existing`). Uživatel v něm řádky odškrtá a ostrý import dostane
|
||||
jen `selectedKeys`.
|
||||
|
||||
**Když se změní formát exportu v Luncheru, je potřeba upravit i tento parser**
|
||||
a testy v `server/src/tests/luncherImport.test.ts`, které si export sestavují
|
||||
|
||||
Reference in new issue
Block a user