feat: configurable week start day in Vacay (Monday or Sunday)

- New setting in Vacay Settings to choose Mon or Sun as week start
- DB migration adds week_start column to vacay_plans (default: Monday)
- Calendar grid and weekday headers adapt to the selected start day
- Weekend column highlighting works correctly for both modes
- Translations added for all 14 languages
This commit is contained in:
Maurice
2026-04-12 02:18:45 +02:00
parent 2215395a26
commit f323952012
20 changed files with 87 additions and 12 deletions
@@ -85,6 +85,7 @@ export default function VacayCalendar() {
blockWeekends={blockWeekends}
weekendDays={weekendDays}
tripDates={tripDates}
weekStart={plan?.week_start ?? 1}
/>
))}
</div>
+18 -10
View File
@@ -24,22 +24,25 @@ interface VacayMonthCardProps {
blockWeekends: boolean
weekendDays?: number[]
tripDates?: Set<string>
weekStart?: number
}
export default function VacayMonthCard({
year, month, holidays, companyHolidaySet, companyHolidaysEnabled = true, entryMap,
onCellClick, companyMode, blockWeekends, weekendDays = [0, 6], tripDates
onCellClick, companyMode, blockWeekends, weekendDays = [0, 6], tripDates, weekStart = 1
}: VacayMonthCardProps) {
const { t, locale } = useTranslation()
const weekdays = WEEKDAY_KEYS.map(k => t(k))
const WEEKDAY_KEYS_SUNDAY = ['vacay.sun', 'vacay.mon', 'vacay.tue', 'vacay.wed', 'vacay.thu', 'vacay.fri', 'vacay.sat'] as const
const orderedKeys = weekStart === 0 ? WEEKDAY_KEYS_SUNDAY : WEEKDAY_KEYS
const weekdays = orderedKeys.map(k => t(k))
const monthName = useMemo(() => new Intl.DateTimeFormat(locale, { month: 'long' }).format(new Date(year, month, 1)), [locale, year, month])
const weeks = useMemo(() => {
const firstDay = new Date(year, month, 1)
const daysInMonth = new Date(year, month + 1, 0).getDate()
let startDow = firstDay.getDay() - 1
if (startDow < 0) startDow = 6
let startDow = firstDay.getDay() - weekStart
if (startDow < 0) startDow += 7
const cells = []
for (let i = 0; i < startDow; i++) cells.push(null)
for (let d = 1; d <= daysInMonth; d++) cells.push(d)
@@ -58,11 +61,16 @@ export default function VacayMonthCard({
</div>
<div className="grid grid-cols-7 border-b" style={{ borderColor: 'var(--border-secondary)' }}>
{weekdays.map((wd, i) => (
<div key={wd} className="text-center text-[10px] font-medium py-1" style={{ color: i >= 5 ? 'var(--text-faint)' : 'var(--text-muted)' }}>
{wd}
</div>
))}
{weekdays.map((wd, i) => {
// Map column index back to JS day (0=Sun..6=Sat) to check if it's a weekend column
const jsDay = (i + weekStart) % 7
const isWeekendCol = weekendDays.includes(jsDay)
return (
<div key={`${wd}-${i}`} className="text-center text-[10px] font-medium py-1" style={{ color: isWeekendCol ? 'var(--text-faint)' : 'var(--text-muted)' }}>
{wd}
</div>
)
})}
</div>
<div>
+32 -1
View File
@@ -1,5 +1,5 @@
import { useState, useEffect } from 'react'
import { type LucideIcon, CalendarOff, AlertCircle, Building2, Unlink, ArrowRightLeft, Globe, Plus, Trash2 } from 'lucide-react'
import { type LucideIcon, CalendarOff, AlertCircle, Building2, Unlink, ArrowRightLeft, Globe, Plus, Trash2, CalendarDays } from 'lucide-react'
import { useVacayStore } from '../../store/vacayStore'
import { getIntlLanguage, useTranslation } from '../../i18n'
import { useToast } from '../shared/Toast'
@@ -85,6 +85,37 @@ export default function VacaySettings({ onClose }: VacaySettingsProps) {
</div>
)}
{/* Week start */}
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<CalendarDays size={16} style={{ color: 'var(--text-muted)', flexShrink: 0 }} />
<div style={{ flex: 1 }}>
<span className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>{t('vacay.weekStart')}</span>
<p className="text-xs mt-0.5" style={{ color: 'var(--text-faint)' }}>{t('vacay.weekStartHint')}</p>
</div>
</div>
<div style={{ paddingLeft: 36, marginTop: 8 }} className="flex gap-1.5">
{[
{ value: 1, label: t('vacay.mon') },
{ value: 0, label: t('vacay.sun') },
].map(({ value, label }) => {
const active = (plan.week_start ?? 1) === value
return (
<button key={value} onClick={() => updatePlan({ week_start: value })}
style={{
padding: '4px 10px', borderRadius: 8, fontSize: 12, fontWeight: 600, cursor: 'pointer',
fontFamily: 'inherit', border: '1px solid', transition: 'all 0.12s',
background: active ? 'var(--text-primary)' : 'var(--bg-card)',
borderColor: active ? 'var(--text-primary)' : 'var(--border-primary)',
color: active ? 'var(--bg-primary)' : 'var(--text-muted)',
}}>
{label}
</button>
)
})}
</div>
</div>
{/* Carry-over */}
<SettingToggle
icon={ArrowRightLeft}