[2026-01-08] UI Polish and Bug Fixes
Task Metadata
Date: 2026-01-08
Status: Completed
- :git: Branch:
feature/ui-polish-flash-fix
Objective
Goal: Fix two annoying UI bugs identified in the Master Plan:
- Flash of Zero: Prevent the dashboard from showing zero totals while loading data for a new day.
- Sticky Expanded State: Ensure the expanded meal state is cleared when a meal is deleted.
- Trigger: Identified in Master Plan as Phase 1 cleanup tasks.
- Constraints:
- Must maintain the clean, minimal aesthetic.
- Must work with existing TanStack Query implementation.
Technical Strategy
- Flash of Zero:
- Update
DailyTotalsVerticalcomponent to accept aloadingprop. - Apply
opacity-50to the totals whenloadingis true to indicate data is in transit while maintaining layout. - Pass
loadingstate fromuseMealshook inDashboard.tsx. - Sticky Expanded State:
- Update
handleDeleteMealinDashboard.tsxto callsetExpandedMealIndex(null)upon successful deletion.
- Key Decisions:
- Decided to use opacity reduction for loading totals instead of a full skeleton to minimize layout shift.
- Resetting
expandedMealIndextonullis the safest way to prevent accidental expansion of the wrong item after an array shift.
Testing Strategy
- Manual Test: Navigate between days and verify totals don't "flash" zero (they should stay dimmed).
- Manual Test: Expand a meal, delete it, and verify no other meal is automatically expanded.
- Automated Test: Run
task check:typesandtask test:frontend.
Risk Analysis
- Low risk, purely UI-related changes.
- Files to Modify:
Problematic Code & Fixes
1. Sticky Expanded State (Dashboard.tsx)
Currently, deleting a meal does not reset the expanded state index, causing the meal that shifts into that index to appear expanded incorrectly.
const handleDeleteMeal = async (meal: any) => {
// ... confirm ...
const success = await deleteMeal(meal);
if (success) {
// MISSING: setExpandedMealIndex(null);
setMessage({ type: 'success', text: 'Meal deleted successfully.' });
// ...
}
};
Fix: Explicitly call setExpandedMealIndex(null) on success.
2. Flash of Zero (DailyTotalsVertical.tsx)
The component currently renders raw totals without knowing if data is stale/loading.
// Current props
interface DailyTotalsVerticalProps {
totals: Totals; // Renders 0 if data isn't ready
// ...
}
Fix: Add loading?: boolean prop and apply opacity-50 class when true to indicate stale/loading state while preserving layout.
Execution Plan
- Step 1: Fix Sticky Expanded State
- Update
handleDeleteMealinDashboard.tsx.
- Update
- Step 2: Fix Flash of Zero
- Update
DailyTotalsVertical.tsxto handleloadingprop. - Pass
loadingprop fromDashboard.tsx.
- Update
Execution Notes
- Implementation verified via TypeScript checking (
tsc --noEmit). - All existing frontend unit tests passed.
- Redundant
DailyTotals.tsx(horizontal version) was confirmed deleted in a previous refactor; corrected plan to focus only onDailyTotalsVertical.tsx.
User Approval & Key Learnings
Key Learnings
- State Management: When dealing with array-based state (like indices), always consider how deletions or reorders affect currently tracked indices.
- Visual Feedback: A simple opacity transition is often less jarring than a full skeleton loader for small data transitions (like navigating between days).
(User to confirm approval and add notes/learnings)
Context Memory (AI-Only)
Summary for Future Context
Fixed UX bug where deleting a meal left the expansion state active on the next item. Added loading indicators to daily totals to prevent "Flash of Zero" during day transitions. Updated DailyTotalsVertical component to support a loading prop.