- Fix logic bug `{#if !isPlanner === false}` - view/cook buttons now visible for all roles, swap only for planner
- Convert Tauschen from dead button to link with suggestions href
- Add week.ts unit tests (23 tests covering getWeekStart Sunday edge case, prevWeek/nextWeek, weekDays, isToday, formatWeekRange)
- Fix isToday to use UTC consistently (.toISOString().slice(0,10)) instead of local date
- Add server-side role guard to createPlan action (403 for members)
- Add weekStart format validation in createPlan action
- Add isSelected prop to DayMealCard with green treatment
- Make variety banner sticky on mobile (always visible per spec)
- Add day name abbreviation above date badge in desktop column headers
- Remove placeholder Navigation text from desktop sidebar
- Add aria-label to desktop empty tile buttons
- Add variety score partial failure test, multiple overlaps test, WeekStrip today+selected test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
/**
|
||
* Returns the ISO Monday (YYYY-MM-DD) for the week containing `date`.
|
||
*/
|
||
export function getWeekStart(date: Date): string {
|
||
const d = new Date(date);
|
||
const day = d.getUTCDay(); // 0=Sun, 1=Mon, …
|
||
const diff = day === 0 ? -6 : 1 - day; // shift to Monday
|
||
d.setUTCDate(d.getUTCDate() + diff);
|
||
return d.toISOString().slice(0, 10);
|
||
}
|
||
|
||
/**
|
||
* Returns the Monday of the previous week relative to `weekStart`.
|
||
*/
|
||
export function prevWeek(weekStart: string): string {
|
||
const d = new Date(weekStart + 'T00:00:00Z');
|
||
d.setUTCDate(d.getUTCDate() - 7);
|
||
return d.toISOString().slice(0, 10);
|
||
}
|
||
|
||
/**
|
||
* Returns the Monday of the next week relative to `weekStart`.
|
||
*/
|
||
export function nextWeek(weekStart: string): string {
|
||
const d = new Date(weekStart + 'T00:00:00Z');
|
||
d.setUTCDate(d.getUTCDate() + 7);
|
||
return d.toISOString().slice(0, 10);
|
||
}
|
||
|
||
/**
|
||
* Formats a date string (YYYY-MM-DD) as a localized day abbreviation.
|
||
*/
|
||
export function formatDayAbbr(dateStr: string, length: 'narrow' | 'short' = 'narrow'): string {
|
||
const d = new Date(dateStr + 'T00:00:00Z');
|
||
return d.toLocaleDateString('de-DE', { weekday: length, timeZone: 'UTC' });
|
||
}
|
||
|
||
/**
|
||
* Returns an array of 7 date strings for the week starting on `weekStart`.
|
||
*/
|
||
export function weekDays(weekStart: string): string[] {
|
||
const days: string[] = [];
|
||
for (let i = 0; i < 7; i++) {
|
||
const d = new Date(weekStart + 'T00:00:00Z');
|
||
d.setUTCDate(d.getUTCDate() + i);
|
||
days.push(d.toISOString().slice(0, 10));
|
||
}
|
||
return days;
|
||
}
|
||
|
||
/**
|
||
* Formats a date string as "Mo, 30.03." style label.
|
||
*/
|
||
export function formatDayLabel(dateStr: string): string {
|
||
const d = new Date(dateStr + 'T00:00:00Z');
|
||
const day = d.toLocaleDateString('de-DE', { weekday: 'short', timeZone: 'UTC' });
|
||
const date = d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', timeZone: 'UTC' });
|
||
return `${day}, ${date}`;
|
||
}
|
||
|
||
/**
|
||
* Formats a date string as "30. März" style label.
|
||
*/
|
||
export function formatDayFull(dateStr: string): string {
|
||
const d = new Date(dateStr + 'T00:00:00Z');
|
||
return d.toLocaleDateString('de-DE', { day: 'numeric', month: 'long', timeZone: 'UTC' });
|
||
}
|
||
|
||
/**
|
||
* Returns true if dateStr is today (UTC date).
|
||
* Uses UTC consistently with all other date functions in this module.
|
||
*/
|
||
export function isToday(dateStr: string): boolean {
|
||
const todayStr = new Date().toISOString().slice(0, 10);
|
||
return dateStr === todayStr;
|
||
}
|
||
|
||
/**
|
||
* Formats a week range: "30. Mär – 5. Apr 2026".
|
||
*/
|
||
export function formatWeekRange(weekStart: string): string {
|
||
const start = new Date(weekStart + 'T00:00:00Z');
|
||
const end = new Date(weekStart + 'T00:00:00Z');
|
||
end.setUTCDate(end.getUTCDate() + 6);
|
||
const startStr = start.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', timeZone: 'UTC' });
|
||
const endStr = end.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric', timeZone: 'UTC' });
|
||
return `${startStr} – ${endStr}`;
|
||
}
|