2026-01-01: Targets Persistence
Task Metadata
Date: 2026-01-01
Status: Complete
1. Context
- Goal: Persist user-defined nutrition targets (Calories, Protein, Carbs, Fat) and display settings (Units) to the backend. Currently, these are stored in
localStorage, meaning they do not sync across devices and are lost if the browser cache is cleared. - Trigger: Master Plan "Phase 1: Foundation". Essential for a consistent multi-device experience (e.g., Desktop to Mobile).
2. Approach
- Schema Update: Introduce a new "Configuration" entity in DynamoDB (Single Table Design).
- PK:
USER#<sub_id> - SK:
CONFIG#TARGETS(Single item per user) - Attributes:
Calories(N),Protein(N),Carbs(N),Fat(N),DisplayUnit(S: 'kcal'|'kj').
- PK:
- API Layer (AppSync):
- Query:
getUserTargets(Fetch specific SK). - Mutation:
updateUserTargets(Update attributes).
- Query:
- Frontend:
- Hook: Update
useSettingsto use React Query (useQuery/useMutation) instead oflocalStorage. - Optimistic Updates: Ensure the UI updates instantly when the user saves settings.
- Hook: Update
- Key Decisions:
- Migration Strategy: If the user has existing targets in
localStoragebut no remote targets (first sync), we will automatically migrate the local values to the backend to preserve their setup. - Cleanup: Once the application switches to the new "Remote First" mode, the legacy
localStoragekeys (nutrition_targets,display_unit) will be ignored and eventually cleared to prevent state drift. - Conflict Resolution: If both
localStorageand Remote targets exist, Remote targets win. The server is the single source of truth. - Schema Design: Using
CONFIG#TARGETSisolates configuration from meal data, preventing pollution of the meal history and allowing for fast, single-item lookups.
- Migration Strategy: If the user has existing targets in
3. Impact Analysis
- Files to Modify:
docs/architecture/data_model.md(Document new CONFIG entity).appsync/(New resolvers for config).graphql/schema.graphql(New Query/Mutation types).frontend/src/services/ApiMealService.js(Add target methods).frontend/src/hooks/useSettings.js(Refactor from localStorage to ReactQuery).frontend/src/components/SettingsModal.jsx(Ensure it handles async saving state).
- Risks:
- Latency: Settings modal might feel slower if we wait for network. Mitigation: Optimistic updates.
- Offline: If offline, user can't save settings? Mitigation: React Query offline support handles the mutation queue.
4. Execution Plan
- Step 1: Backend Implementation
- Update
schema.graphqlwithUserTargetstype and operations. - Create
appsync/getUserTargets.jsandappsync/updateUserTargets.js. - Deploy backend updates (
sam build && sam deploy).
- Update
- Step 2: Service Integration
- Update
ApiMealService.jsto call the new GraphQL operations.
- Update
- Step 3: Frontend Refactor
- Rewrite
useSettings.jsto useuseQuery(fetchingCONFIG#TARGETS) anduseMutation. - Implement
staleTimeandgcTimestrategies consistent withuseMeals.
- Rewrite
- Step 4: Verify
- Test saving on one device and viewing on another.
- Test offline behavior.
5. Execution Notes
(AI to add details discovered during implementation)
- Tech Debt / Future Improvements:
- Attribute Casing: We are using PascalCase (
Calories,DisplayUnit) in DynamoDB for consistency with existingMealrecords, despite the JS/React stack preferring camelCase. - Action: Added "Unify Casing" task to Phase 1 to migrate the entire DB to camelCase and remove mapping layers.
- Attribute Casing: We are using PascalCase (
6. User Approval & Key Learnings
User Feedback: The "Local State vs. React Query State" pattern used in the
SettingsModalis interesting. Driving inputs directly from mutations caused race conditions and UI resets.Critical Discovery: This feature revealed a major gap in our development process. Manual verification is no longer sufficient; we need a formal Layered Testing Strategy to catch race conditions and state synchronization bugs automatically.
- Key Learning: Form Handling with React Query.
- Pattern: Buffer inputs in
useState(Local State) for instant UI feedback. - Sync: Only call
mutation.mutate(Server State) on specific events like "Save" or "Blur". - Why: Prevents "jumping" inputs caused by optimistic updates refetching or settling mid-typing.
- Pattern: Buffer inputs in