Files
Nutrio/AGENTS.md
2026-02-14 13:27:04 +01:00

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.md already contains product specification in Slovak and English.
  • Backend DB migrations exist in backend/src/Maintenance.php up to version 7.
  • Backend API methods are implemented in backend/src/API.php.
  • Frontend auth page is implemented:
    • frontend/src/App.vue renders router view.
    • frontend/src/router/index.ts maps / to frontend/src/views/AuthView.vue for guests.
    • AuthView serves 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.vue formatting now uses tab-based indentation.
  • Frontend authenticated area is implemented:
    • frontend/src/views/AppLayout.vue is 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.ts emits auth:session-expired
      • listener in frontend/src/main.ts clears session, shows info toast, and redirects to auth route
  • 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.fiber exists in all locales and is used in macro badges
    • ux.toast.sessionExpired exists in all locales for auto-logout flow
  • 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 (:root variables).
  • 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, and DayMealCard
    • Today day 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
    • style uses subtle tinted background + full-color text, pill shape (no saturated full backgrounds)
  • Frontend typography is local (no remote font CDN):
    • frontend/src/assets/css/style.css uses local @font-face for DM Sans and Space Grotesk
    • font files are stored in frontend/src/assets/fonts/*.woff2
  • App logo is served from frontend/public/Nutrio.png (copied from doc/Nutrio.png).
  • Font Awesome is installed and registered globally in frontend/src/main.ts.
  • Pinia is installed and configured in frontend/src/main.ts via frontend/src/stores/index.ts.
  • Frontend domain/store structure exists:
    • frontend/src/types/domain.ts
    • frontend/src/stores/{auth,theme,ui,ingredients,meals,diary}.ts
    • frontend/src/utils/{nutrition,api,date,error}.ts
  • Ingredients form UX detail:
    • create/edit form in IngredientsView is hidden by default
    • shows only after Nova surovina or Upravit
    • hides again after successful save (form remount resets fields)
  • frontend/src/BackendAPI.ts is generated via backend/scripts/buildTypeScript.php and should not be edited manually.
  • backend/data.json contains 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 mysql and sqlite
    • Loads override config files from backend/config/*.php (except the base file itself)
  • 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_type must be one of: breakfast, lunch, dinner.
  • Date params must use format YYYY-MM-DD.
  • grams must be > 0.
  • Nutrition input values are validated as non-negative.
  • If kcal_100 is 0, API computes kcal by formula:
    • protein*4 + carbs*4 + fat*9
  • User-bound actions now require token and resolve user_id from it at method start.
  • Token validation path:
    • Users::getUserIDbyToken(token) -> Users::verifyToken(user_id, token)
    • valid token refreshes token_expires
    • expired token clears token and token_expires to NULL
  • 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 setting token and token_expires to NULL.
  • userLogin(email, password) now auto-registers user when:
    • email is valid
    • user does not exist
    • then proceeds with normal login/token generation flow
  • userLogin response now includes auto_registered flag (true|false).

Known Pitfalls and Notes

  • The historical FK issue in meal_items should reference meals(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.php show encoding artifacts, but SQL structure is valid.
  • Basic token auth is implemented, but token is still passed as plain API parameter.
  • For array parameters (for example ordered_item_ids), APIlite expects JSON in request payload.
  • APIlite response handling detail:
    • raw API response is wrapped as { status, data }
    • generated BackendAPI.ts currently resolves response.data in callPromise for non-__HELP__ actions
    • frontend stores use frontend/src/utils/api.ts (unwrapApiData) to normalize both envelope and unwrapped runtime shapes
  • Auto-logout on expired token currently depends on the shared error mapper:
    • if a view/store handles API errors without toErrorMessage(...), global auth:session-expired event is not emitted there
  • frontend/src/BackendAPI.ts is 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 backend
  • composer install
  • configure DB via backend/config/*.php override
  • serve backend/public through 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 frontend
  • npm install
  • npm 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 UNSIGNED and use *_id naming consistently.
  • Keep MySQL + SQLite compatibility in SQL where possible (project supports both).
  • When changing schema, always bump DB version in Maintenance.php with 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).