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.
This commit is contained in:
Maurice
2026-09-24 21:56:10 +02:00
parent 196dc12762
commit 7ce31756a3
84 changed files with 511 additions and 106 deletions
@@ -259,6 +259,17 @@ describe('Tool: update_display_settings', () => {
});
});
it('writes week_start and refuses a day outside the three the app offers (#2029)', async () => {
const { user } = createUser(testDb);
await withHarness(user.id, async (h) => {
const ok = parseToolResult(await update(h, { week_start: 'sunday' })) as any;
expect(ok.settings.week_start).toBe('sunday');
const refused = await update(h, { week_start: 'friday' });
expect(refused.isError).toBe(true);
expect(readSetting(user.id, 'week_start')).toBe('sunday');
});
});
it('accepts every boolean display preference', async () => {
const { user } = createUser(testDb);
await withHarness(user.id, async (h) => {
@@ -353,6 +353,22 @@ describe('legacy quirk fixes', () => {
expect(svc.getDecryptedUserSetting(user.id, 'llm_api_key')).toBeNull();
});
it('SET-SVC-035 — a week_start default reaches users without their own, and theirs wins (#2029)', () => {
const { user: plain } = createUser(testDb);
const { user: own } = createUser(testDb);
svc.setAdminUserDefaults({ week_start: 'sunday' });
svc.upsertSetting(own.id, 'week_start', 'saturday');
expect(svc.getUserSettings(plain.id).week_start).toBe('sunday');
expect(svc.getUserSettings(own.id).week_start).toBe('saturday');
});
it('SET-SVC-036 — a week_start default outside monday/sunday/saturday is refused and not stored', () => {
expect(() => svc.setAdminUserDefaults({ week_start: 'friday' })).toThrow(/Invalid value for week_start/);
// Vacay's 0/1 numbers are not this setting's values either.
expect(() => svc.setAdminUserDefaults({ week_start: 0 })).toThrow(/Invalid value for week_start/);
expect(svc.getAdminUserDefaults().week_start).toBeUndefined();
});
it('SET-SVC-028 — a nulled defaultable key falls through to the admin default', () => {
const { user } = createUser(testDb);
svc.setAdminUserDefaults({ mapbox_style: 'admin-style' });