11 KiB
11 KiB
AGENTS.md
Purpose
This file is a quick handover for future agents working on D:\www\Nutrio.
It describes what the project is, what is already implemented, and what still needs work.
Project Summary
- Project name:
Nutrio - Goal: meal planning + nutrition tracking + daily diary totals.
- Backend: PHP (
tpsoft/apilite,tpsoft/dbmodel) - Frontend: Vue 3 + Vite + TypeScript
- Monorepo layout:
backend/frontend/
Current State (as of 2026-02-14)
README.mdalready contains product specification in Slovak and English.- Backend DB migrations exist in
backend/src/Maintenance.phpup to version7. - Backend API methods are implemented in
backend/src/API.php. - Frontend auth page is implemented:
frontend/src/App.vuerenders router view.frontend/src/router/index.tsmaps/tofrontend/src/views/AuthView.vuefor guests.AuthViewserves as login + registration entry (single form, email + password).- Login now uses
frontend/src/stores/auth.ts(Pinia) and redirects to authenticated app routes. frontend/src/views/AuthView.vueformatting now uses tab-based indentation.
- Frontend authenticated area is implemented:
frontend/src/views/AppLayout.vueis shell layout.- Desktop navigation:
frontend/src/components/navigation/AppSidebar.vue. - Mobile navigation:
frontend/src/components/navigation/AppBottomTabs.vue. - Top bar/title:
frontend/src/components/navigation/AppTopbar.vue. - Pages:
TodayView,MealsView,MealDetailView,IngredientsView,StatsView,SettingsView. - Route guard is active for
/app/*(requires token), guest-only for/.
- Frontend UX layer is implemented:
- global toasts:
frontend/src/components/common/ToastHost.vue - global confirm modal:
frontend/src/components/common/ConfirmModalHost.vue - state management:
frontend/src/stores/ui.ts - mounted globally in
frontend/src/App.vue - card-level loading/error states are wired in
TodayView,MealsView,MealDetailView,IngredientsView - expired-session handling is global:
- token error detector in
frontend/src/utils/error.tsemitsauth:session-expired - listener in
frontend/src/main.tsclears session, shows info toast, and redirects to auth route
- token error detector in
- global toasts:
- Frontend i18n is wired and used across new UI:
- setup in
frontend/src/i18n/index.ts - locale files in
frontend/src/locales/{sk,cs,en,es,de}.json - language switcher updates locale dynamically.
- app UI keys include (
nav,pageTitles,mealTypes,common,nutrition,today,meals,ingredients,stats,settings,ux) nutrition.short.fiberexists in all locales and is used in macro badgesux.toast.sessionExpiredexists in all locales for auto-logout flow
- setup in
- Frontend theme system is implemented:
- centralized theme store:
frontend/src/stores/theme.ts - shared toggle component:
frontend/src/components/common/ThemeToggle.vue - toggle available in auth, app topbar, and settings
- design tokens in
frontend/src/assets/css/style.css(:rootvariables).
- centralized theme store:
- Frontend macro-badge visual system is implemented:
- shared component:
frontend/src/components/common/MacroBadge.vue - shared colors/tokens/styles in
frontend/src/assets/css/style.css - used in
IngredientsView,MealsView,DayTotalsCard, andDayMealCard Todayday totals card layout: kcal block on the left, macros on the right in one row- macro colors:
- protein
#3B82F6 - carbs
#F59E0B - fat
#EF4444 - fiber
#10B981
- protein
- style uses subtle tinted background + full-color text, pill shape (no saturated full backgrounds)
- shared component:
- Frontend typography is local (no remote font CDN):
frontend/src/assets/css/style.cssuses local@font-faceforDM SansandSpace Grotesk- font files are stored in
frontend/src/assets/fonts/*.woff2
- App logo is served from
frontend/public/Nutrio.png(copied fromdoc/Nutrio.png). - Font Awesome is installed and registered globally in
frontend/src/main.ts. - Pinia is installed and configured in
frontend/src/main.tsviafrontend/src/stores/index.ts. - Frontend domain/store structure exists:
frontend/src/types/domain.tsfrontend/src/stores/{auth,theme,ui,ingredients,meals,diary}.tsfrontend/src/utils/{nutrition,api,date,error}.ts
- Ingredients form UX detail:
- create/edit form in
IngredientsViewis hidden by default - shows only after
Nova surovinaorUpravit - hides again after successful save (form remount resets fields)
- create/edit form in
frontend/src/BackendAPI.tsis generated viabackend/scripts/buildTypeScript.phpand should not be edited manually.backend/data.jsoncontains sample meal data (not currently wired into DB/API flow).
Backend Architecture
- Entry point:
backend/public/API.php - Bootstrap and DB init:
backend/src/Init.php - Configuration:
backend/config/Configuration.php- Supports
mysqlandsqlite - Loads override config files from
backend/config/*.php(except the base file itself)
- Supports
- Migration logic:
backend/src/Maintenance.php - API implementation:
backend/src/API.php - DB table models:
backend/src/Models/*.php
Database Schema Snapshot
Migration creates these tables:
options(key,value) for internal settings, including DB version.users(user_id,email,password_hash,token,token_expires,created_at).ingredients:ingredient_id,user_id,name- per-100g values:
protein_g_100,carbs_g_100,sugar_g_100,fat_g_100,fiber_g_100,kcal_100
meals:meal_id,user_id,name,meal_type(breakfast|lunch|dinner)
meal_items:meal_item_id,meal_id,ingredient_id,grams,position
diary_days:diary_day_id,user_id,day_date- unique:
(user_id, day_date)
diary_entries:diary_entry_id,diary_day_id,meal_type,meal_id- unique:
(diary_day_id, meal_type)
API Surface (Implemented)
All actions are invoked through backend/public/API.php with ?action=<method_name>.
- Utility:
health
- Auth / Users:
userRegistration(email, password)userLogin(email, password)userLogout(token)userDelete(email, password)
- Ingredients:
ingredientList(token, query = "", include_global = true)ingredientGet(token, ingredient_id)ingredientCreate(token, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)ingredientUpdate(token, ingredient_id, name, protein_g_100, carbs_g_100, sugar_g_100, fat_g_100, fiber_g_100 = 0, kcal_100 = 0)ingredientDelete(token, ingredient_id)
- Meals:
mealList(token, meal_type = "", with_items = false, with_totals = false)mealGet(token, meal_id, with_items = true, with_totals = true)mealCreate(token, name, meal_type)mealUpdate(token, meal_id, name, meal_type)mealDelete(token, meal_id)
- Meal items:
mealItemList(token, meal_id, with_calculated = true)mealItemAdd(token, meal_id, ingredient_id, grams, position = 1)mealItemUpdate(token, meal_item_id, ingredient_id, grams, position)mealItemDelete(token, meal_item_id)mealItemReorder(token, meal_id, ordered_item_ids)
- Calculations:
mealTotals(token, meal_id)
- Diary:
diaryDayGet(token, day_date, with_totals = true)diaryDaySetMeal(token, day_date, meal_type, meal_id)diaryDayUnsetMeal(token, day_date, meal_type)diaryRange(token, date_from, date_to)
Behavior and Validation Rules
meal_typemust be one of:breakfast,lunch,dinner.- Date params must use format
YYYY-MM-DD. gramsmust be> 0.- Nutrition input values are validated as non-negative.
- If
kcal_100is0, API computes kcal by formula:protein*4 + carbs*4 + fat*9
- User-bound actions now require
tokenand resolveuser_idfrom it at method start. - Token validation path:
Users::getUserIDbyToken(token)->Users::verifyToken(user_id, token)- valid token refreshes
token_expires - expired token clears
tokenandtoken_expirestoNULL
- Ownership checks are enforced by resolved
user_id:- meals and meal items must belong to the user
- ingredients can be user-owned or global (
user_id = null) for read/select
- Registration/login generate and store user token in DB.
userLogout(token)invalidates session by settingtokenandtoken_expirestoNULL.userLogin(email, password)now auto-registers user when:- email is valid
- user does not exist
- then proceeds with normal login/token generation flow
userLoginresponse now includesauto_registeredflag (true|false).
Known Pitfalls and Notes
- The historical FK issue in
meal_itemsshould referencemeals(meal_id).
Current file already uses the correct FK. - If someone ran migrations before FK fix, old MySQL state may still be broken.
In that case reset affected table(s) or rebuild DB from clean state. - Some comments in
Maintenance.phpshow encoding artifacts, but SQL structure is valid. - Basic token auth is implemented, but token is still passed as plain API parameter.
- For
arrayparameters (for exampleordered_item_ids), APIlite expects JSON in request payload. - APIlite response handling detail:
- raw API response is wrapped as
{ status, data } - generated
BackendAPI.tscurrently resolvesresponse.dataincallPromisefor non-__HELP__actions - frontend stores use
frontend/src/utils/api.ts(unwrapApiData) to normalize both envelope and unwrapped runtime shapes
- raw API response is wrapped as
- Auto-logout on expired token currently depends on the shared error mapper:
- if a view/store handles API errors without
toErrorMessage(...), globalauth:session-expiredevent is not emitted there
- if a view/store handles API errors without
frontend/src/BackendAPI.tsis generated output; regenerate when backend API changes, do not patch manually.- In vue-i18n locale strings,
@must be escaped as{'@'}to avoid "Invalid linked format" errors.
Local Runbook
Backend:
cd backendcomposer install- configure DB via
backend/config/*.phpoverride - serve
backend/publicthrough web server/PHP runtime - first API hit triggers
Maintenance->database()migration flow - when backend API signature changes, regenerate frontend API client:
php scripts/buildTypeScript.php
Frontend:
cd frontendnpm installnpm run dev- optional checks:
npm run type-check,npm run build,npm run lint
Product Behavior Target (what to build next)
- Harden auth (token transport/header strategy, token revoke strategy, brute-force/rate-limits).
- Polish authenticated frontend UX further (more granular field validation, retry actions, richer empty states).
- Add diary range screen/workflow on frontend (backend endpoint already exists).
- Add i18n coverage for any future UI additions and keep keys stable.
- Add API tests for validation, ownership checks, and totals calculation consistency.
- Add pagination/filter strategy where list endpoints grow.
Practical Conventions
- Keep IDs as
BIGINT UNSIGNEDand use*_idnaming consistently. - Keep MySQL + SQLite compatibility in SQL where possible (project supports both).
- When changing schema, always bump DB version in
Maintenance.phpwith forward-only migration steps. - Keep API action names stable unless frontend is updated at the same time.
- In source files, use tab characters for indentation (do not add space-based indentation).