mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
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:
@@ -85,6 +85,7 @@ export default function VacayCalendar() {
|
||||
blockWeekends={blockWeekends}
|
||||
weekendDays={weekendDays}
|
||||
tripDates={tripDates}
|
||||
weekStart={plan?.week_start ?? 1}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -727,6 +727,8 @@ const ar: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'عطل الشركة',
|
||||
'vacay.companyHolidaysHint': 'السماح بوضع علامة على أيام عطلات الشركة',
|
||||
'vacay.companyHolidaysNoDeduct': 'لا تُخصم عطل الشركة من أيام الإجازة.',
|
||||
'vacay.weekStart': 'يبدأ الأسبوع في',
|
||||
'vacay.weekStartHint': 'اختر ما إذا كان الأسبوع يبدأ يوم الاثنين أو الأحد',
|
||||
'vacay.carryOver': 'الترحيل',
|
||||
'vacay.carryOverHint': 'ترحيل أيام الإجازة المتبقية تلقائيًا إلى السنة التالية',
|
||||
'vacay.sharing': 'المشاركة',
|
||||
|
||||
@@ -696,6 +696,8 @@ const br: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Feriados da empresa',
|
||||
'vacay.companyHolidaysHint': 'Permitir marcar dias de feriado em toda a empresa',
|
||||
'vacay.companyHolidaysNoDeduct': 'Feriados da empresa não contam como dias de férias.',
|
||||
'vacay.weekStart': 'Semana começa em',
|
||||
'vacay.weekStartHint': 'Escolha se a semana começa na segunda-feira ou no domingo',
|
||||
'vacay.carryOver': 'Acúmulo',
|
||||
'vacay.carryOverHint': 'Levar automaticamente os dias de férias restantes para o ano seguinte',
|
||||
'vacay.sharing': 'Compartilhamento',
|
||||
|
||||
@@ -726,6 +726,8 @@ const cs: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Firemní volno',
|
||||
'vacay.companyHolidaysHint': 'Povolit označování dnů celofiremního volna',
|
||||
'vacay.companyHolidaysNoDeduct': 'Firemní volno se nezapočítává do nároku na dovolenou.',
|
||||
'vacay.weekStart': 'Týden začíná',
|
||||
'vacay.weekStartHint': 'Zvolte, zda týden začíná v pondělí nebo v neděli',
|
||||
'vacay.carryOver': 'Převod dovolené',
|
||||
'vacay.carryOverHint': 'Automaticky převádět zbývající dny do dalšího roku',
|
||||
'vacay.sharing': 'Sdílení',
|
||||
|
||||
@@ -727,6 +727,8 @@ const de: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Betriebsferien',
|
||||
'vacay.companyHolidaysHint': 'Erlaubt das Markieren von unternehmensweiten Feiertagen',
|
||||
'vacay.companyHolidaysNoDeduct': 'Betriebsferien werden nicht vom Urlaubskontingent abgezogen.',
|
||||
'vacay.weekStart': 'Woche beginnt am',
|
||||
'vacay.weekStartHint': 'Wähle ob die Kalenderwoche am Montag oder Sonntag beginnt',
|
||||
'vacay.carryOver': 'Urlaubsmitnahme',
|
||||
'vacay.carryOverHint': 'Resturlaub automatisch ins Folgejahr übertragen',
|
||||
'vacay.sharing': 'Teilen',
|
||||
|
||||
@@ -749,6 +749,8 @@ const en: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Company Holidays',
|
||||
'vacay.companyHolidaysHint': 'Allow marking company-wide holiday days',
|
||||
'vacay.companyHolidaysNoDeduct': 'Company holidays do not count towards vacation days.',
|
||||
'vacay.weekStart': 'Week starts on',
|
||||
'vacay.weekStartHint': 'Choose whether the calendar week starts on Monday or Sunday',
|
||||
'vacay.carryOver': 'Carry Over',
|
||||
'vacay.carryOverHint': 'Automatically carry remaining vacation days into the next year',
|
||||
'vacay.sharing': 'Sharing',
|
||||
|
||||
@@ -696,6 +696,8 @@ const es: Record<string, string> = {
|
||||
'vacay.companyHolidays': 'Festivos de empresa',
|
||||
'vacay.companyHolidaysHint': 'Permitir marcar días festivos comunes de la empresa',
|
||||
'vacay.companyHolidaysNoDeduct': 'Los festivos de empresa no descuentan días de vacaciones.',
|
||||
'vacay.weekStart': 'La semana comienza el',
|
||||
'vacay.weekStartHint': 'Elige si la semana comienza el lunes o el domingo',
|
||||
'vacay.carryOver': 'Arrastrar saldo',
|
||||
'vacay.carryOverHint': 'Trasladar automáticamente los días restantes al año siguiente',
|
||||
'vacay.sharing': 'Compartir',
|
||||
|
||||
@@ -719,6 +719,8 @@ const fr: Record<string, string> = {
|
||||
'vacay.companyHolidays': 'Jours fériés d\'entreprise',
|
||||
'vacay.companyHolidaysHint': 'Autoriser le marquage des jours fériés d\'entreprise',
|
||||
'vacay.companyHolidaysNoDeduct': 'Les jours fériés d\'entreprise ne sont pas déduits des jours de vacances.',
|
||||
'vacay.weekStart': 'La semaine commence le',
|
||||
'vacay.weekStartHint': 'Choisissez si la semaine commence le lundi ou le dimanche',
|
||||
'vacay.carryOver': 'Report',
|
||||
'vacay.carryOverHint': 'Reporter automatiquement les jours de vacances restants à l\'année suivante',
|
||||
'vacay.sharing': 'Partage',
|
||||
|
||||
@@ -724,6 +724,8 @@ const hu: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Céges szabadnapok',
|
||||
'vacay.companyHolidaysHint': 'Céges szintű szabadnapok megjelölésének engedélyezése',
|
||||
'vacay.companyHolidaysNoDeduct': 'A céges szabadnapok nem számítanak bele a szabadságkeretbe.',
|
||||
'vacay.weekStart': 'A hét kezdőnapja',
|
||||
'vacay.weekStartHint': 'Válaszd ki, hogy a hét hétfőn vagy vasárnap kezdődjön',
|
||||
'vacay.carryOver': 'Szabadság átvitele',
|
||||
'vacay.carryOverHint': 'Megmaradt szabadságnapok automatikus átvitele a következő évre',
|
||||
'vacay.sharing': 'Megosztás',
|
||||
|
||||
@@ -724,6 +724,8 @@ const it: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Ferie aziendali',
|
||||
'vacay.companyHolidaysHint': 'Consenti di segnare giorni di ferie aziendali',
|
||||
'vacay.companyHolidaysNoDeduct': 'Le ferie aziendali non vengono conteggiate nei giorni di ferie.',
|
||||
'vacay.weekStart': 'La settimana inizia il',
|
||||
'vacay.weekStartHint': 'Scegli se la settimana inizia il lunedì o la domenica',
|
||||
'vacay.carryOver': 'Riporto',
|
||||
'vacay.carryOverHint': 'Riporta automaticamente i giorni di ferie rimanenti all\'anno successivo',
|
||||
'vacay.sharing': 'Condivisione',
|
||||
|
||||
@@ -719,6 +719,8 @@ const nl: Record<string, string> = {
|
||||
'vacay.companyHolidays': 'Bedrijfsvakanties',
|
||||
'vacay.companyHolidaysHint': 'Sta het markeren van bedrijfsbrede vakantiedagen toe',
|
||||
'vacay.companyHolidaysNoDeduct': 'Bedrijfsvakanties worden niet afgetrokken van vakantiedagen.',
|
||||
'vacay.weekStart': 'Week begint op',
|
||||
'vacay.weekStartHint': 'Kies of de kalenderweek op maandag of zondag begint',
|
||||
'vacay.carryOver': 'Overdracht',
|
||||
'vacay.carryOverHint': 'Draag resterende vakantiedagen automatisch over naar het volgende jaar',
|
||||
'vacay.sharing': 'Delen',
|
||||
|
||||
@@ -692,6 +692,8 @@ const pl: Record<string, string | { name: string; category: string }[]> = {
|
||||
'vacay.companyHolidays': 'Urlopy firmowe',
|
||||
'vacay.companyHolidaysHint': 'Pozwala oznaczać dni wolne od pracy w kalendarzu',
|
||||
'vacay.companyHolidaysNoDeduct': 'Urlopy firmowe nie są odejmowane od puli dni urlopowych.',
|
||||
'vacay.weekStart': 'Tydzień zaczyna się w',
|
||||
'vacay.weekStartHint': 'Wybierz czy tydzień zaczyna się w poniedziałek czy niedzielę',
|
||||
'vacay.carryOver': 'Przeniesienie na kolejny rok',
|
||||
'vacay.carryOverHint': 'Automatycznie przenosi pozostałe dni urlopowe na kolejny rok',
|
||||
'vacay.sharing': 'Udostępnianie',
|
||||
|
||||
@@ -719,6 +719,8 @@ const ru: Record<string, string> = {
|
||||
'vacay.companyHolidays': 'Корпоративные выходные',
|
||||
'vacay.companyHolidaysHint': 'Разрешить отмечать корпоративные выходные дни',
|
||||
'vacay.companyHolidaysNoDeduct': 'Корпоративные выходные не вычитаются из дней отпуска.',
|
||||
'vacay.weekStart': 'Неделя начинается с',
|
||||
'vacay.weekStartHint': 'Выберите, начинается ли неделя с понедельника или воскресенья',
|
||||
'vacay.carryOver': 'Перенос',
|
||||
'vacay.carryOverHint': 'Автоматически переносить оставшиеся дни отпуска на следующий год',
|
||||
'vacay.sharing': 'Общий доступ',
|
||||
|
||||
@@ -719,6 +719,8 @@ const zh: Record<string, string> = {
|
||||
'vacay.companyHolidays': '公司假日',
|
||||
'vacay.companyHolidaysHint': '允许标记公司统一休假日',
|
||||
'vacay.companyHolidaysNoDeduct': '公司假日不计入年假天数。',
|
||||
'vacay.weekStart': '每周开始于',
|
||||
'vacay.weekStartHint': '选择日历周从周一还是周日开始',
|
||||
'vacay.carryOver': '结转',
|
||||
'vacay.carryOverHint': '自动将剩余年假天数结转到下一年',
|
||||
'vacay.sharing': '共享',
|
||||
|
||||
@@ -744,6 +744,8 @@ const zhTw: Record<string, string> = {
|
||||
'vacay.companyHolidays': '公司假日',
|
||||
'vacay.companyHolidaysHint': '允許標記公司統一休假日',
|
||||
'vacay.companyHolidaysNoDeduct': '公司假日不計入年假天數。',
|
||||
'vacay.weekStart': '每週開始於',
|
||||
'vacay.weekStartHint': '選擇日曆週從週一還是週日開始',
|
||||
'vacay.carryOver': '結轉',
|
||||
'vacay.carryOverHint': '自動將剩餘年假天數結轉到下一年',
|
||||
'vacay.sharing': '共享',
|
||||
|
||||
@@ -337,6 +337,7 @@ export interface VacayPlan {
|
||||
block_weekends: boolean
|
||||
carry_over_enabled: boolean
|
||||
company_holidays_enabled: boolean
|
||||
week_start?: number
|
||||
name?: string
|
||||
year?: number
|
||||
owner_id?: number
|
||||
|
||||
Reference in New Issue
Block a user