Files
TREK/client/src/utils/calendarWeek.ts
T
Maurice 7ce31756a3 feat(settings): every date picker starts its week on the day you pick
Settings > General > Language & region gets "Week starts on" (Monday,
Sunday or Saturday), and Admin > User Defaults a default for everyone who
has not picked one. Monday stays the built-in value, so nothing changes
for anyone who leaves it alone.

Both calendar implementations follow it: CustomDatePicker, used by trip
dates, bookings, costs, to-dos and the admin school holidays on desktop
and phone, and the journey date picker, whose weekday headers were
hardcoded English and now speak the app language. The grid math and the
header row live in one helper (utils/calendarWeek.ts). The day names on
the options come from Intl, so only the label and a hint needed
translating. Vacay keeps its own per-plan week start.

The value is validated like the other regional settings: VALID_VALUES for
admin defaults, the display-settings MCP tool, and the managed key list.

Addresses discussion #2029.
2026-09-24 21:56:12 +02:00

43 lines
2.0 KiB
TypeScript

import { DEFAULT_WEEK_START, WEEK_START_DAY, WEEK_START_VALUES, type WeekStart } from '@trek/shared'
/**
* The calendar-grid half of the week_start setting (#2029): the one place a
* date picker asks which weekday opens a row, how many blanks go before the
* 1st, and what the header row says. Both pickers (CustomDatePicker and the
* journey DatePicker) read it, so they cannot drift apart again.
*/
/** Date#getDay() of the first weekday; a missing or unknown value is Monday, what every picker did before the setting. */
export function weekStartDay(value: unknown): number {
const known = (WEEK_START_VALUES as readonly unknown[]).includes(value)
return WEEK_START_DAY[known ? value as WeekStart : DEFAULT_WEEK_START]
}
/** Blank cells before the 1st of the month when rows begin on `weekStart`. */
export function leadingBlanks(year: number, month: number, weekStart: number): number {
return (new Date(year, month, 1).getDay() - weekStart + 7) % 7
}
// 7 January 2024 is a Sunday, so the 7th plus a getDay() number is that weekday.
function sampleDay(day: number): Date {
return new Date(2024, 0, 7 + day)
}
/** The header row of a month grid, starting on `weekStart`, in the app's locale. */
export function weekdayLabels(locale: string, weekStart: number, weekday: 'narrow' | 'short' = 'narrow'): string[] {
return Array.from({ length: 7 }, (_, i) => sampleDay((weekStart + i) % 7).toLocaleDateString(locale, { weekday }))
}
/**
* The choices for the setting, labelled with the day's own name in the app's
* language, so no locale file has to spell out seven weekdays. Capitalised
* because several languages write weekday names lower-case and these stand
* alone on a button.
*/
export function weekStartOptions(locale: string): { value: WeekStart; label: string }[] {
return WEEK_START_VALUES.map(value => {
const name = sampleDay(WEEK_START_DAY[value]).toLocaleDateString(locale, { weekday: 'long' })
return { value, label: name.charAt(0).toLocaleUpperCase(locale) + name.slice(1) }
})
}