feat: založení základní stránky příjem/výdej + import a statistiky

This commit is contained in:
Ondřej Anděl
2026-09-07 14:58:49 +02:00
commit a5711f3e1e
98 changed files with 15505 additions and 0 deletions
+664
View File
@@ -0,0 +1,664 @@
MealType:
description: |
Typ jídla (chod v rámci dne). Importy z Luncheru se zakládají jako OBED,
pokud se při importu neurčí jinak.
type: string
enum:
- SNIDANE
- DOPOLEDNI_SVACINA
- OBED
- ODPOLEDNI_SVACINA
- VECERE
- JINE
MealEntry:
description: Jeden snědený pokrm v konkrétní den
type: object
additionalProperties: false
required:
- id
- date
- mealType
- name
- createdAt
properties:
id:
description: Identifikátor záznamu (UUID)
type: string
date:
description: Datum konzumace ve formátu YYYY-MM-DD
type: string
format: date
mealType:
$ref: "#/MealType"
name:
description: Název jídla
type: string
source:
description: Zdroj jídla (podnik, obchod, "doma", ...)
type: string
price:
description: Cena v haléřích. Celé číslo, aby nevznikaly chyby zaokrouhlením.
type: integer
calories:
description: |
Energetická hodnota v kcal. Pokud je vyplněná gramáž i caloriesPer100g,
dopočte se z nich; jinak platí ručně zadaná hodnota.
type: integer
weight:
description: |
Gramáž porce v gramech. U podniků, které účtují podle váhy (TechTower),
se dopočítá z ceny a pricePer100g, ale dá se přepsat.
type: integer
pricePer100g:
description: Cena za 100 g v haléřích. Slouží k dopočtu gramáže z ceny.
type: integer
caloriesPer100g:
description: Energetická hodnota na 100 g v kcal. Slouží k dopočtu kalorií z gramáže.
type: integer
note:
description: Poznámka uživatele k jídlu
type: string
importSource:
description: Odkud byl záznam naimportován (např. "luncher"). U ručně přidaných chybí.
type: string
importKey:
description: |
Klíč pro rozpoznání duplicit při opakovaném importu stejného období.
Odvozuje se z data, typu, názvu jídla a částky importovaného řádku.
type: string
createdAt:
description: Čas vzniku záznamu
type: string
format: date-time
updatedAt:
description: Čas poslední úpravy záznamu
type: string
format: date-time
MealInput:
description: Data pro založení nebo úpravu záznamu o jídle
type: object
additionalProperties: false
required:
- date
- mealType
- name
properties:
date:
description: Datum konzumace ve formátu YYYY-MM-DD
type: string
format: date
mealType:
$ref: "#/MealType"
name:
description: Název jídla
type: string
source:
description: Zdroj jídla (podnik, obchod, "doma", ...)
type: string
price:
description: Cena v haléřích
type: integer
nullable: true
calories:
description: |
Energetická hodnota v kcal. Ignoruje se, pokud jde dopočíst
z gramáže a caloriesPer100g.
type: integer
nullable: true
weight:
description: Gramáž porce v gramech. Nevyplněná se dopočte z ceny a pricePer100g.
type: integer
nullable: true
pricePer100g:
description: Cena za 100 g v haléřích
type: integer
nullable: true
caloriesPer100g:
description: Energetická hodnota na 100 g v kcal
type: integer
nullable: true
note:
description: Poznámka uživatele k jídlu
type: string
nullable: true
DayRecord:
description: Přehled jídel jednoho dne včetně součtů
type: object
additionalProperties: false
required:
- date
- entries
- totalPrice
- totalCalories
properties:
date:
description: Datum ve formátu YYYY-MM-DD
type: string
format: date
entries:
description: Jídla daného dne seřazená dle typu (snídaně → večeře)
type: array
items:
$ref: "#/MealEntry"
totalPrice:
description: Součet cen všech jídel dne v haléřích
type: integer
totalCalories:
description: Součet kalorií jídel dne v kcal (jídla bez kalorií se nezapočítávají)
type: integer
PeriodTotal:
description: Součty za jedno období (den, měsíc nebo rok)
type: object
additionalProperties: false
required:
- period
- price
- calories
- count
properties:
period:
description: Označení období — YYYY-MM-DD (den), YYYY-MM (měsíc) nebo YYYY (rok)
type: string
price:
description: Utracená částka v haléřích
type: integer
calories:
description: Součet kalorií v kcal
type: integer
count:
description: Počet záznamů o jídle
type: integer
GroupTotal:
description: Součty za jednu skupinu (typ jídla nebo zdroj)
type: object
additionalProperties: false
required:
- key
- price
- calories
- count
properties:
key:
description: Klíč skupiny (hodnota MealType, nebo název zdroje)
type: string
price:
description: Utracená částka v haléřích
type: integer
calories:
description: Součet kalorií v kcal
type: integer
count:
description: Počet záznamů o jídle
type: integer
FoodStats:
description: Statistiky útraty a jídel za zvolené období
type: object
additionalProperties: false
required:
- from
- to
- totalPrice
- totalCalories
- entryCount
- dayCount
- byDay
- byMonth
- byYear
- byMealType
- bySource
properties:
from:
description: Začátek období (YYYY-MM-DD, včetně)
type: string
format: date
to:
description: Konec období (YYYY-MM-DD, včetně)
type: string
format: date
totalPrice:
description: Celková útrata za období v haléřích
type: integer
totalCalories:
description: Celkový součet kalorií za období v kcal
type: integer
entryCount:
description: Počet záznamů o jídle v období
type: integer
dayCount:
description: Počet dní, ve kterých je alespoň jeden záznam
type: integer
byDay:
description: Součty po dnech, vzestupně dle data
type: array
items:
$ref: "#/PeriodTotal"
byMonth:
description: Součty po měsících, vzestupně
type: array
items:
$ref: "#/PeriodTotal"
byYear:
description: Součty po letech, vzestupně
type: array
items:
$ref: "#/PeriodTotal"
byMealType:
description: Součty dle typu jídla, sestupně dle útraty
type: array
items:
$ref: "#/GroupTotal"
bySource:
description: Součty dle zdroje jídla, sestupně dle útraty
type: array
items:
$ref: "#/GroupTotal"
ImportRequest:
description: Požadavek na import přehledu z Luncheru
type: object
additionalProperties: false
required:
- fileName
- content
properties:
fileName:
description: Původní název souboru — určuje formát (.xlsx, .csv, .json)
type: string
content:
description: Obsah souboru zakódovaný v Base64
type: string
defaultMealType:
description: |
Typ jídla, pod kterým se importované řádky založí.
Luncher řeší výběr obědů, takže výchozí hodnota je OBED.
allOf:
- $ref: "#/MealType"
dryRun:
description: |
Pokud je true, import se pouze vyhodnotí a vrátí náhled, ale nic se neuloží.
type: boolean
ImportResult:
description: Výsledek (nebo náhled) importu přehledu z Luncheru
type: object
additionalProperties: false
required:
- dryRun
- format
- rowCount
- imported
- skipped
- days
- totalPrice
- entries
- warnings
properties:
dryRun:
description: True, pokud šlo pouze o náhled a data se neuložila
type: boolean
format:
description: Rozpoznaný formát vstupního souboru
type: string
enum: [xlsx, csv, json]
rowCount:
description: Počet řádků nalezených v souboru
type: integer
imported:
description: Počet záznamů, které import založil (u náhledu by založil)
type: integer
skipped:
description: Počet přeskočených řádků, protože už z dřívějšího importu existují
type: integer
days:
description: Dny (YYYY-MM-DD), kterých se import dotkl, vzestupně
type: array
items:
type: string
format: date
totalPrice:
description: Součet cen importovaných záznamů v haléřích
type: integer
entries:
description: Záznamy, které import založil (u náhledu založí)
type: array
items:
$ref: "#/MealEntry"
warnings:
description: Upozornění na řádky, které se nepodařilo zpracovat
type: array
items:
type: string
Config:
description: Veřejná runtime konfigurace pro klienta
type: object
additionalProperties: false
required:
- sentry
properties:
sentry:
type: object
additionalProperties: false
required:
- dsn
- environment
properties:
dsn:
type: string
nullable: true
environment:
type: string
CalorieSuggestion:
description: Návrh energetické hodnoty pro jídlo
type: object
additionalProperties: false
required:
- name
- caloriesPer100g
- origin
properties:
name:
description: Název nalezené potraviny
type: string
caloriesPer100g:
description: Energetická hodnota na 100 g v kcal
type: integer
origin:
description: |
Odkud návrh pochází — "library" je dřívější vlastní zadání uživatele,
ostatní hodnoty označují externího poskytovatele.
type: string
brand:
description: Značka nebo výrobce, pokud je známý
type: string
CalorieSearchResult:
description: Výsledek hledání energetické hodnoty pro název jídla
type: object
additionalProperties: false
required:
- query
- providerAvailable
- suggestions
- externalSearchUrl
properties:
query:
description: Hledaný název
type: string
providerAvailable:
description: |
False, pokud je externí poskytovatel nedostupný. Návrhy z vlastní
knihovny se vrací i tak — hledání kvůli tomu neselže.
type: boolean
providerName:
description: Název externího poskytovatele, který návrhy dodal
type: string
suggestions:
description: Nalezené návrhy, nejdřív z vlastní knihovny
type: array
items:
$ref: "#/CalorieSuggestion"
externalSearchUrl:
description: |
Odkaz na tabulku potravin na KalorickéTabulky.cz. Otevírá se uživateli
v novém panelu — hodnoty se odtud nestahují automaticky. Předvyplnit
hledání nejde, jejich vyhledávání běží v JavaScriptu a parametry v URL
ignoruje, proto klient název jídla kopíruje do schránky.
type: string
SourceRate:
description: Cena za 100 g u jednoho zdroje jídla
type: object
additionalProperties: false
required:
- source
- pricePer100g
properties:
source:
description: Název zdroje (podniku), např. "TechTower"
type: string
pricePer100g:
description: Cena za 100 g v haléřích
type: integer
UserSettings:
description: Uživatelské nastavení aplikace
type: object
additionalProperties: false
required:
- sourceRates
properties:
sourceRates:
description: |
Ceny za 100 g u podniků, které účtují podle váhy. Slouží jako výchozí
hodnota při zadávání jídla; u konkrétního jídla jde sazbu přepsat.
type: array
items:
$ref: "#/SourceRate"
basalCalories:
description: |
Klidový výdej (bazální metabolismus) v kcal za den. Bez něj se bilance dne
počítá jen jako jídlo proti pohybu a nejde o skutečný deficit.
type: integer
ActivityUnit:
description: Jednotka, ve které se aktivita měří
type: string
enum:
- KROKY
- MINUTY
- KM
- OPAKOVANI
ActivityEntry:
description: Jedna pohybová aktivita v konkrétní den
type: object
additionalProperties: false
required:
- id
- date
- name
- unit
- quantity
- createdAt
properties:
id:
description: Identifikátor záznamu (UUID)
type: string
date:
description: Datum aktivity ve formátu YYYY-MM-DD
type: string
format: date
name:
description: Název aktivity (Chůze, Kliky, Běh, ...)
type: string
unit:
$ref: "#/ActivityUnit"
quantity:
description: Množství v dané jednotce — počet kroků, minut, kilometrů nebo opakování
type: integer
caloriesPer100Units:
description: |
Spálené kcal na 100 jednotek. Pro kroky vychází kolem 4 (tj. 400 kcal
za 10 000 kroků), u minut běhu kolem 1000. Sto jednotek se používá kvůli
celým číslům — stejná konvence jako u energie jídla na 100 g.
type: integer
calories:
description: |
Spálené kcal. Pokud je vyplněné caloriesPer100Units, dopočte se z množství;
jinak platí ručně zadaná hodnota.
type: integer
note:
description: Poznámka uživatele k aktivitě
type: string
templateId:
description: Identifikátor šablony, ze které aktivita vznikla
type: string
createdAt:
description: Čas vzniku záznamu
type: string
format: date-time
updatedAt:
description: Čas poslední úpravy záznamu
type: string
format: date-time
ActivityInput:
description: Data pro založení nebo úpravu aktivity
type: object
additionalProperties: false
required:
- date
- name
- unit
- quantity
properties:
date:
type: string
format: date
name:
type: string
unit:
$ref: "#/ActivityUnit"
quantity:
description: Množství v dané jednotce
type: integer
caloriesPer100Units:
description: Spálené kcal na 100 jednotek
type: integer
nullable: true
calories:
description: Spálené kcal. Ignoruje se, pokud jde dopočíst z množství.
type: integer
nullable: true
note:
type: string
nullable: true
ActivityDay:
description: Přehled pohybu jednoho dne včetně součtu
type: object
additionalProperties: false
required:
- date
- entries
- totalCalories
properties:
date:
type: string
format: date
entries:
description: Aktivity daného dne, nejstarší první
type: array
items:
$ref: "#/ActivityEntry"
totalCalories:
description: Součet spálených kcal za den
type: integer
WorkoutTemplateItem:
description: Jedna položka šablony tréninku
type: object
additionalProperties: false
required:
- name
- unit
- quantity
properties:
name:
type: string
unit:
$ref: "#/ActivityUnit"
quantity:
type: integer
caloriesPer100Units:
type: integer
WorkoutTemplate:
description: |
Předpřipravený trénink. Použitím se jeho položky založí do zvoleného dne,
takže opakovaný trénink není potřeba zadávat cvik po cviku znovu.
type: object
additionalProperties: false
required:
- id
- name
- items
- estimatedCalories
properties:
id:
type: string
name:
description: Název šablony, např. "Workout day 1"
type: string
items:
type: array
items:
$ref: "#/WorkoutTemplateItem"
estimatedCalories:
description: Odhad spálených kcal za celou šablonu
type: integer
EnergyBalance:
description: Energetická bilance dne — kolik přišlo jídlem a kolik se vydalo
type: object
additionalProperties: false
required:
- intake
- activityBurn
- basal
- totalBurn
- balance
- hasBasal
properties:
intake:
description: Přijaté kcal z jídla
type: integer
activityBurn:
description: Spálené kcal pohybem
type: integer
basal:
description: |
Klidový výdej (bazální metabolismus) v kcal za den z nastavení.
Nula, pokud si ho uživatel nenastavil.
type: integer
totalBurn:
description: Celkový výdej, tedy klidový výdej plus pohyb
type: integer
balance:
description: |
Rozdíl příjmu a výdeje v kcal. Záporná hodnota znamená deficit,
kladná přebytek.
type: integer
hasBasal:
description: |
False, pokud uživatel nemá nastavený klidový výdej. Bilance pak
porovnává jen jídlo proti pohybu a není to skutečný deficit.
type: boolean
DayOverview:
description: Kompletní přehled dne — jídlo, pohyb a jejich bilance
type: object
additionalProperties: false
required:
- date
- meals
- activities
- energy
properties:
date:
type: string
format: date
meals:
$ref: "#/DayRecord"
activities:
$ref: "#/ActivityDay"
energy:
$ref: "#/EnergyBalance"