Architecture: UI Data Validation
This document outlines the rules and behaviors for data input in the Frontend UI, serving as the contract between the User Interaction and the Backend Schema.
1. User Targets (Settings)
Data Types & Constraints
-
Calories (Energy):
- Type: Float (stored as
kcal). - UI Input: Number.
- Min Value:
0. - Meaning of
0: No target set / Target disabled. - Empty Input: Allowed in UI (stored as
""in local state). - Normalization: On Save, converted to Float (or
0if empty). - Display Logic:
- Initial values: If
0, displays"". - User Input: Displays exactly what the user types (allows
0,05,2000.5etc. while editing).
- Initial values: If
- Type: Float (stored as
-
Macros (Protein, Carbs, Fat):
- Type: Float (grams).
- UI Input: Number (Step: 5g for better UX).
- Min Value:
0. - Meaning of
0: No target set. - Empty Input: Allowed in UI (stored as
""in local state). - Normalization: On Save, converted to Float (or
0if empty). - Display Logic:
- Initial values: If
0, displays"". - User Input: Displays exactly what the user types.
- Initial values: If
- Storage: Always stored as Floats.
Unit Conversion (Presentation Layer)
The application supports toggling between Calories (kcal) and Kilojoules (kJ). This is a presentation-only transformation for the Energy field.
- Storage: Always Kcal.
- Conversion Factor:
1 kcal = 4.184 kJ. - Rounding: Rounded to the nearest integer for display, but stored with precision.
- Input Logic:
- When user types in kJ mode, the value is divided by
4.184before being stored in state as kcal. - Note: This can lead to minor precision loss (e.g., typing 1000 kJ -> 239.005 kcal -> displays as 1000 kJ).
- When user types in kJ mode, the value is divided by
2. Validation Philosophy
- Loose UI, Strict Backend: We allow the user to type freely in the UI (stored as strings in local state). This ensures that if a user types
0or clears the field, the UI reflects their action exactly. - Normalization on Save: Data is sanitized and converted to Floats only when clicking Done/Save. Empty strings or invalid inputs are normalized to
0. - Explicit "No Value": We use
0to represent the absence of a numeric target, rather thannull. - Backend Alignment: The backend ignores
nullvalues, reinforcing the reliance on0as the explicit "clear" signal from the UI.
3. Advanced Nutrient Tracking
Unit Immutability
To prevent historical data corruption (e.g., summing grams and milligrams), the Unit of a nutrient is permanent once defined in the Registry.
- Rule: A nutrient definition (Key + Unit) cannot be edited.
- Change Workflow:
- Disable: The user sets the status of "Sodium (mg)" to
ARCHIVED. - Create: The user creates a new nutrient "Sodium (g)".
- Disable: The user sets the status of "Sodium (mg)" to
- UI Constraints: The "Unit" dropdown is disabled for any existing registry item.
Snapshot Protocol (Performance)
To avoid expensive "Read-Before-Write" checks on every addMeal call, the Frontend provides a validation snapshot.
- Mechanism: The
addMealmutation accepts aregistrySnapshotargument (JSON array of strings). - Source: The Frontend derives this list from the currently loaded
UserTargets. - Backend Behavior: The resolver uses this list to filter the incoming
extendedNutrientsmap. Any key NOT in the snapshot is silently discarded. - Trust Model: This is a "Trust but Verify" optimization. It prevents AI hallucinations from polluting the DB with random keys, without incurring a DB Read cost.
Optimistic Locking (Settings)
The UserTargets entity ("God Row") is protected against concurrent overwrites (e.g., editing on Phone while Tablet is open).
- Attribute:
version(Number). - Protocol:
- Read: Frontend fetches
versionwithgetUserTargets. - Write: Frontend sends
expectedVersionwithupdateUserTargets. - Conflict: If
DB.version !== expectedVersion, the write fails. - Resolution: The UI must catch this error, refresh the settings, and ask the user to re-apply their changes.
- Read: Frontend fetches