Show the Kitchen reading kicker for a planned day already past
The kicker only looked at plan days from today on, so a dinner this week's plan held only on a past day showed no kicker. It now names the first planned day from today on, or the latest past one when every planned day is past. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -276,9 +276,10 @@ function statusName(dinner: DinnerDetail): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PlannedDay is the kicker of a dinner on a plan, "Saturday, Oct 10 · Side
|
// PlannedDay is the kicker of a dinner on a plan, "Saturday, Oct 10 · Side
|
||||||
// quest": the first day, from today on in the household's timezone, that
|
// quest", whenever this week's or next week's plan has it. Of the days that
|
||||||
// this week's or next week's plan has it. There is none while the plans are
|
// do, it is the first from today on in the household's timezone; when every
|
||||||
// read, nor when neither has the dinner from today on.
|
// one is already past, it is the latest of them. There is none while the
|
||||||
|
// plans are read, nor when neither plan has the dinner.
|
||||||
function PlannedDay({ dinner, timezone }: { dinner: DinnerDetail; timezone: string }) {
|
function PlannedDay({ dinner, timezone }: { dinner: DinnerDetail; timezone: string }) {
|
||||||
const today = localNow(timezone, new Date()).date
|
const today = localNow(timezone, new Date()).date
|
||||||
const thisWeek = weekStart(today)
|
const thisWeek = weekStart(today)
|
||||||
@@ -291,9 +292,8 @@ function PlannedDay({ dinner, timezone }: { dinner: DinnerDetail; timezone: stri
|
|||||||
</ErrorBanner>
|
</ErrorBanner>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const day = plans
|
const planned = plans.flatMap((plan) => plan.data?.days ?? []).filter((day) => day.dinner.id === dinner.id)
|
||||||
.flatMap((plan) => plan.data?.days ?? [])
|
const day = planned.find((day) => day.date >= today) ?? planned.at(-1)
|
||||||
.find((day) => day.dinner.id === dinner.id && day.date >= today)
|
|
||||||
if (day === undefined) return null
|
if (day === undefined) return null
|
||||||
return (
|
return (
|
||||||
<p className={styles.kicker}>
|
<p className={styles.kicker}>
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ function detail(overrides: Record<string, unknown> = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// planBody is a week's plan as GET /api/v1/plans/{weekStart} sends it: p0 to
|
// planBody is a week's plan as GET /api/v1/plans/{weekStart} sends it: p0 to
|
||||||
// p6, except the bowls (d1) on day bowlsOn, counting from Monday = 0.
|
// p6, except the bowls (d1) on each day of bowlsOn, counting from Monday = 0.
|
||||||
function planBody(weekStart: string, bowlsOn?: number) {
|
function planBody(weekStart: string, ...bowlsOn: number[]) {
|
||||||
const start = new Date(`${weekStart}T00:00:00Z`).getTime()
|
const start = new Date(`${weekStart}T00:00:00Z`).getTime()
|
||||||
return {
|
return {
|
||||||
id: `plan-${weekStart}`,
|
id: `plan-${weekStart}`,
|
||||||
@@ -58,10 +58,10 @@ function planBody(weekStart: string, bowlsOn?: number) {
|
|||||||
days: Array.from({ length: 7 }, (_, i) => ({
|
days: Array.from({ length: 7 }, (_, i) => ({
|
||||||
date: new Date(start + i * 86_400_000).toISOString().slice(0, 10),
|
date: new Date(start + i * 86_400_000).toISOString().slice(0, 10),
|
||||||
dinner:
|
dinner:
|
||||||
i === bowlsOn
|
bowlsOn.includes(i)
|
||||||
? { id: 'd1', name: 'Korean beef bowls', photo: null, minutes: 30, method: 'stovetop' }
|
? { id: 'd1', name: 'Korean beef bowls', photo: null, minutes: 30, method: 'stovetop' }
|
||||||
: { id: `p${i}`, name: `Dinner ${i}`, photo: null, minutes: 30, method: 'stovetop' },
|
: { id: `p${i}`, name: `Dinner ${i}`, photo: null, minutes: 30, method: 'stovetop' },
|
||||||
is_new: i === bowlsOn,
|
is_new: bowlsOn.includes(i),
|
||||||
start_by: null,
|
start_by: null,
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
@@ -176,13 +176,34 @@ describe('Kitchen reading mode', () => {
|
|||||||
expect(await screen.findByText('Monday, Oct 12 · Regular')).toBeInTheDocument()
|
expect(await screen.findByText('Monday, Oct 12 · Regular')).toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('is absent when no plan has the dinner from today on', async () => {
|
it("names a day of this week's plan already past when that is the only day", async () => {
|
||||||
// On Monday, already past, and on no day of next week.
|
// Today is Wednesday; the bowls were on Monday, and are on no day of next week.
|
||||||
const fetchMock = serve({
|
serve({
|
||||||
[THIS_WEEK]: () => jsonResponse(200, planBody('2026-10-05', 0)),
|
[THIS_WEEK]: () => jsonResponse(200, planBody('2026-10-05', 0)),
|
||||||
[NEXT_WEEK]: () => jsonResponse(200, planBody('2026-10-12')),
|
[NEXT_WEEK]: () => jsonResponse(200, planBody('2026-10-12')),
|
||||||
})
|
})
|
||||||
await renderPage()
|
await renderPage()
|
||||||
|
expect(await screen.findByText('Monday, Oct 5 · Side quest')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('names the first day from today on over a day already past', async () => {
|
||||||
|
serve({ [THIS_WEEK]: () => jsonResponse(200, planBody('2026-10-05', 0, 1, 4, 6)) })
|
||||||
|
await renderPage()
|
||||||
|
expect(await screen.findByText('Friday, Oct 9 · Side quest')).toBeInTheDocument()
|
||||||
|
expect(screen.queryByText(/Monday|Tuesday|Sunday/)).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('names the latest day already past when every day is past', async () => {
|
||||||
|
serve({ [THIS_WEEK]: () => jsonResponse(200, planBody('2026-10-05', 0, 1)) })
|
||||||
|
await renderPage()
|
||||||
|
expect(await screen.findByText('Tuesday, Oct 6 · Side quest')).toBeInTheDocument()
|
||||||
|
expect(screen.queryByText(/Monday/)).not.toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is absent when neither week has a plan', async () => {
|
||||||
|
const fetchMock = serve({ [THIS_WEEK]: () => errorResponse(404, 'not_found') })
|
||||||
|
await renderPage()
|
||||||
|
await waitFor(() => expect(reads(fetchMock, THIS_WEEK)).toBe(1))
|
||||||
await waitFor(() => expect(reads(fetchMock, NEXT_WEEK)).toBe(1))
|
await waitFor(() => expect(reads(fetchMock, NEXT_WEEK)).toBe(1))
|
||||||
await act(async () => {})
|
await act(async () => {})
|
||||||
expect(screen.queryByText(/· Side quest/)).not.toBeInTheDocument()
|
expect(screen.queryByText(/· Side quest/)).not.toBeInTheDocument()
|
||||||
|
|||||||
Reference in New Issue
Block a user