Skip to content

[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:

  1. Flash of Zero: Prevent the dashboard from showing zero totals while loading data for a new day.
  2. 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

  1. Flash of Zero:
  2. Update DailyTotalsVertical component to accept a loading prop.
  3. Apply opacity-50 to the totals when loading is true to indicate data is in transit while maintaining layout.
  4. Pass loading state from useMeals hook in Dashboard.tsx.
  5. Sticky Expanded State:
  6. Update handleDeleteMeal in Dashboard.tsx to call setExpandedMealIndex(null) upon successful deletion.
  • Key Decisions:
    • Decided to use opacity reduction for loading totals instead of a full skeleton to minimize layout shift.
    • Resetting expandedMealIndex to null is 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:types and task test:frontend.

Risk Analysis

  • Low risk, purely UI-related changes.

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 handleDeleteMeal in Dashboard.tsx.
  • Step 2: Fix Flash of Zero
    • Update DailyTotalsVertical.tsx to handle loading prop.
    • Pass loading prop from Dashboard.tsx.

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 on DailyTotalsVertical.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.