Public Access
fix(ui): align 'this week' to upcoming Monday (Sprint 7)
User report 2026-06-05: 'webui Meal Planner page is empty' on Friday
morning after the Friday email went out. Root cause: the orchestrator
keyed plans by the most-recent-Friday while the frontend's isoMonday()
returned the most-recent-Monday — a 7-day mismatch on Fridays.
Fixes (one semantic across the stack):
- runner._current_week_start() returns the upcoming Monday (today if
Mon, else the next Mon). The Friday email subject
('Meal plan for week of <date>') automatically picks up the new
value via run.week_start_date.
- frontend isoMonday -> upcomingMonday (same logic; renamed for
intent). isoMonday kept as a deprecated alias.
- New WeekRangeNav component (Dashboard + ShoppingList share it).
Renders [<] Jun 8 - Jun 14 [>] with clickable chevrons and a
clickable range label that jumps to the upcoming week. Replaces
the Sprint 5 inline segmented control on both pages.
- New formatWeekRange(mondayIso) helper (UTC-stable; uses
timeZone: 'UTC' so the rendered date matches the stored ISO date
regardless of viewer TZ; closes a latent bug in formatIsoDate too).
- New SQL fix script that retargets the user's 3-pending-items plan
from 2026-06-05 (Friday-keyed) to 2026-06-08 (upcoming Monday).
Idempotent + transaction-wrapped. Optional block for 2026-05-29.
No backend migration. No new dependencies. Deploy is git pull +
run the SQL fix + docker compose up -d --build backend frontend.
See Review/sprint7-verification.md for the full deploy + smoke flow.
Files:
- backend/app/services/orchestrator/runner.py:20-35
- backend/scripts/fix_2026_06_05_to_2026_06_08.sql (new)
- frontend/src/lib/utils.ts:43-130
- frontend/src/components/WeekRangeNav.tsx (new)
- frontend/src/pages/Dashboard.tsx (3 call sites + 1 segmented control)
- frontend/src/pages/ShoppingList.tsx (5 call sites + 2 segmented controls)
- Review/{sprint7-verification,ui-nielsen-audit,handoff-ui-audit}.md
- fix-ui-audit.md
- docs/HANDOFF.md
- .agent/{plan,context}.md
This commit is contained in:
@@ -2,12 +2,12 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { useState } from 'react'
|
||||
import { Link, useSearchParams } from 'react-router-dom'
|
||||
import {
|
||||
CookingPot, CalendarDays, ShoppingCart, ChevronRight, ChevronLeft, Sparkles, Loader2,
|
||||
CookingPot, CalendarDays, ShoppingCart, ChevronRight, Sparkles, Loader2,
|
||||
GripVertical, X
|
||||
} from 'lucide-react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { showToast, showApiError } from '../lib/toast'
|
||||
import { isoMonday, parseIsoDate, shiftIsoDate, formatIsoDate } from '../lib/utils'
|
||||
import { upcomingMonday, parseIsoDate, shiftIsoDate, formatIsoDate } from '../lib/utils'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import {
|
||||
DragDropContext,
|
||||
@@ -26,6 +26,7 @@ import { Badge } from '../components/ui/Badge'
|
||||
import { Card, CardBody, CardHeader } from '../components/ui/Card'
|
||||
import { SkeletonCard, Skeleton } from '../components/ui/Skeleton'
|
||||
import { EmptyState } from '../components/ui/EmptyState'
|
||||
import { WeekRangeNav } from '../components/WeekRangeNav'
|
||||
|
||||
const DAY_NAMES = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
|
||||
const FULL_DAY_NAMES = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||
@@ -313,10 +314,10 @@ export default function Dashboard() {
|
||||
const parsedWeek = weekParam ? parseIsoDate(weekParam) : null
|
||||
const weekStart = weekParam && parsedWeek
|
||||
? weekParam
|
||||
: isoMonday()
|
||||
const isCurrentWeek = weekStart === isoMonday()
|
||||
: upcomingMonday()
|
||||
const isCurrentWeek = weekStart === upcomingMonday()
|
||||
const navigateWeek = (next: string) => {
|
||||
setSearchParams(next === isoMonday() ? {} : { week: next }, { replace: true })
|
||||
setSearchParams(next === upcomingMonday() ? {} : { week: next }, { replace: true })
|
||||
}
|
||||
const [planningWeek, setPlanningWeek] = useState(false)
|
||||
const [planMenuOpen, setPlanMenuOpen] = useState(false)
|
||||
@@ -477,30 +478,13 @@ export default function Dashboard() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="inline-flex items-center rounded-lg border border-surface-200 bg-white">
|
||||
<button
|
||||
onClick={() => navigateWeek(shiftIsoDate(weekStart, -7))}
|
||||
aria-label="Previous week"
|
||||
className="p-2 text-surface-600 hover:bg-surface-100 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||
>
|
||||
<ChevronLeft className="w-4 h-4" />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigateWeek(isoMonday())}
|
||||
aria-label="Jump to current week"
|
||||
title="Jump to current week"
|
||||
className={`px-3 py-2 text-sm font-medium border-x border-surface-200 focus:outline-none focus:ring-2 focus:ring-primary-400 ${isCurrentWeek ? 'text-primary-700 bg-primary-50' : 'text-surface-600 hover:bg-surface-100'}`}
|
||||
>
|
||||
{isCurrentWeek ? 'This week' : 'Current'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => navigateWeek(shiftIsoDate(weekStart, 7))}
|
||||
aria-label="Next week"
|
||||
className="p-2 text-surface-600 hover:bg-surface-100 rounded-r-lg focus:outline-none focus:ring-2 focus:ring-primary-400"
|
||||
>
|
||||
<ChevronRight className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
<WeekRangeNav
|
||||
weekStart={weekStart}
|
||||
isCurrentWeek={isCurrentWeek}
|
||||
onPrev={() => navigateWeek(shiftIsoDate(weekStart, -7))}
|
||||
onNext={() => navigateWeek(shiftIsoDate(weekStart, 7))}
|
||||
onJumpHome={() => navigateWeek(upcomingMonday())}
|
||||
/>
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setPlanMenuOpen(o => !o)}
|
||||
|
||||
Reference in New Issue
Block a user