diff --git a/client/src/components/Vacay/VacayCalendar.tsx b/client/src/components/Vacay/VacayCalendar.tsx index 971cd4da0..2938cf066 100644 --- a/client/src/components/Vacay/VacayCalendar.tsx +++ b/client/src/components/Vacay/VacayCalendar.tsx @@ -5,11 +5,11 @@ import { isWeekend } from './holidays' import { tripsApi } from '../../api/client' import VacayMonthCard from './VacayMonthCard' import type { VacayEntry } from '../../types' -import { Building2, MousePointer2 } from 'lucide-react' +import { Building2, MousePointer2, Clock } from 'lucide-react' type VacayMode = 'vacation' | 'company' type HoverTip = { date: string; top: number; left: number } -export type SharedDayMark = { color: string; name: string; fraction?: number; company?: boolean } +export type SharedDayMark = { color: string; name: string; fraction?: number; company?: boolean; kind?: 'vacation' | 'comp' } export default function VacayCalendar() { const { t, locale } = useTranslation() @@ -18,6 +18,9 @@ export default function VacayCalendar() { // Half-day is a per-person modifier on the vacation action, not a mode: with it // on, clicking a day logs (or converts) it as a 0.5 day for the selected person. const [halfDay, setHalfDay] = useState(false) + // Comp/Flex day is a second per-person modifier (#1074), orthogonal to half-day: + // with it on, clicking a day logs it as kind='comp' (does not cost the entitlement). + const [compDay, setCompDay] = useState(false) const companyMode = mode === 'company' const [tripDates, setTripDates] = useState>(new Set()) const [tip, setTip] = useState(null) @@ -68,7 +71,7 @@ export default function VacayCalendar() { sharedCalendars.filter(c => !c.hidden).forEach(cal => { cal.entries.forEach(e => { if (!map[e.date]) map[e.date] = [] - map[e.date].push({ color: cal.color, name: cal.owner_name, fraction: e.fraction }) + map[e.date].push({ color: cal.color, name: cal.owner_name, fraction: e.fraction, kind: e.kind }) }) cal.companyHolidays.forEach(h => { if (!map[h.date]) map[h.date] = [] @@ -90,8 +93,8 @@ export default function VacayCalendar() { } if (blockWeekends && isWeekend(dateStr, weekendDays)) return if (companyHolidaysEnabled && companyHolidaySet.has(dateStr)) return - await toggleEntry(dateStr, selectedUserId || undefined, halfDay ? 0.5 : 1) - }, [mode, halfDay, toggleEntry, toggleCompanyHoliday, companyHolidaySet, blockWeekends, weekendDays, companyHolidaysEnabled, selectedUserId]) + await toggleEntry(dateStr, selectedUserId || undefined, halfDay ? 0.5 : 1, compDay ? 'comp' : 'vacation') + }, [mode, halfDay, compDay, toggleEntry, toggleCompanyHoliday, companyHolidaySet, blockWeekends, weekendDays, companyHolidaysEnabled, selectedUserId]) // Cells with a half day or a shared overlay report a hover, so the tooltip // appears exactly when there's something to explain. Fixed-positioned at the @@ -109,6 +112,13 @@ export default function VacayCalendar() { const tipSchool = (Array.isArray(tipHolidayRaw) ? tipHolidayRaw : tipHolidayRaw ? [tipHolidayRaw] : []).filter(h => h.type === 'school_holiday') const tipDate = tip ? new Intl.DateTimeFormat(locale, { weekday: 'short', day: 'numeric', month: 'long' }).format(new Date(tip.date + 'T00:00:00')) : '' + // Label for a day's leave type + fraction (#552/#1074): full/half vacation or comp/flex. + const dayTypeLabel = (fraction: number | undefined, kind: string | undefined) => { + const half = (fraction ?? 1) === 0.5 + if (kind === 'comp') return half ? t('vacay.compHalf') : t('vacay.modeComp') + return half ? t('vacay.modeHalf') : t('vacay.fullDay') + } + return (
@@ -145,13 +155,13 @@ export default function VacayCalendar() {
{tipDate}
{(tipEntries ?? []).map((e, i) => { - const isHalf = (e.fraction ?? 1) === 0.5 + const emphasized = (e.fraction ?? 1) === 0.5 || e.kind === 'comp' return (
{e.person_name} - - {isHalf ? t('vacay.modeHalf') : t('vacay.fullDay')} + + {dayTypeLabel(e.fraction, e.kind)}
) @@ -162,7 +172,7 @@ export default function VacayCalendar() { {m.name} - {m.company ? t('vacay.companyHoliday') : (m.fraction ?? 1) === 0.5 ? t('vacay.modeHalf') : t('vacay.fullDay')} + {m.company ? t('vacay.companyHoliday') : dayTypeLabel(m.fraction, m.kind)}
))} @@ -213,9 +223,21 @@ export default function VacayCalendar() { )} - {/* Divider — the half-day switch is a modifier, not a mode. */} + {/* Divider — comp/flex and half-day are modifiers, not modes. */} + + )} - {/* Divider — the half-day switch modifies the selected person's logging, it isn't a mode. */} + {/* Divider — comp/flex and half-day modify the selected person's logging, they aren't modes. */} +