add support for week_start and time_format in calendar

also rename them so that they do not conflict with the datepicker calendar component
This commit is contained in:
Gregor Vostrak
2025-08-14 16:46:41 +02:00
parent 9fa9522237
commit 4e635cde83
4 changed files with 52 additions and 11 deletions

View File

@@ -19,6 +19,9 @@ const dateFormat = computed(() => organization?.value?.date_format);
<template>
<div class="fc-day-header-custom">
<div class="text-xs text-muted-foreground font-medium">
{{ date.toLocaleDateString('en-US', { weekday: 'short' }) }}
</div>
<span>{{ formatDate(date.toISOString(), dateFormat) }}</span>
<span class="block text-xs text-muted-foreground font-medium mt-1">
{{ formatHumanReadableDuration(totalSeconds, intervalFormat, numberFormat) }}

View File

@@ -4,12 +4,12 @@ import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import type { DatesSetArg, EventClickArg, EventDropArg, EventChangeArg } from '@fullcalendar/core';
import { computed, ref, watch } from 'vue';
import { computed, ref, watch, inject, type ComputedRef } from 'vue';
import { getDayJsInstance, getLocalizedDayJs } from '../utils/time';
import { getUserTimezone } from '../utils/settings';
import { getUserTimezone, getWeekStart } from '../utils/settings';
import { LoadingSpinner, TimeEntryCreateModal, TimeEntryEditModal } from '..';
import CalendarEventContent from './CalendarEventContent.vue';
import CalendarDayHeader from './CalendarDayHeader.vue';
import FullCalendarEventContent from './FullCalendarEventContent.vue';
import FullCalendarDayHeader from './FullCalendarDayHeader.vue';
import type {
TimeEntry,
Project,
@@ -18,6 +18,7 @@ import type {
CreateProjectBody,
CreateClientBody,
Tag,
Organization,
} from '@/packages/api/src';
import type { Dayjs } from 'dayjs';
@@ -58,6 +59,41 @@ const selectedTimeEntry = ref<TimeEntry | null>(null);
const calendarRef = ref<InstanceType<typeof FullCalendar> | null>(null);
// Inject organization data for settings
const organization = inject<ComputedRef<Organization>>('organization');
// Helper function to convert week start to FullCalendar firstDay value
const getFirstDay = () => {
const weekStart = getWeekStart();
const weekStartMap: Record<string, number> = {
'sunday': 0,
'monday': 1,
'tuesday': 2,
'wednesday': 3,
'thursday': 4,
'friday': 5,
'saturday': 6,
};
return weekStartMap[weekStart] ?? 1; // Default to Monday if not found
};
// Helper function to get time format for slot labels
const getSlotLabelFormat = () => {
const timeFormat = organization?.value?.time_format || '24-hours';
if (timeFormat === '12-hours') {
return {
hour: 'numeric' as const,
hour12: true,
};
} else {
return {
hour: '2-digit' as const,
minute: '2-digit' as const,
hour12: false,
};
}
};
// Map API entries to FullCalendar events
const events = computed(() => {
return props.timeEntries
@@ -188,7 +224,9 @@ const calendarOptions = computed(() => ({
slotMaxTime: '24:00:00',
slotDuration: '00:15:00',
slotLabelInterval: '01:00:00',
slotLabelFormat: getSlotLabelFormat(),
snapDuration: '00:15:00',
firstDay: getFirstDay(),
allDaySlot: false,
nowIndicator: true,
selectable: true,
@@ -261,7 +299,7 @@ watch(showEditTimeEntryModal, (value) => {
:clients="clients" />
<FullCalendar ref="calendarRef" class="fullcalendar" :options="calendarOptions">
<template #eventContent="arg">
<CalendarEventContent
<FullCalendarEventContent
:title="arg.event.title"
:project-name="(arg.event.extendedProps as any).project?.name"
:task-name="(arg.event.extendedProps as any).task?.name"
@@ -275,7 +313,7 @@ watch(showEditTimeEntryModal, (value) => {
:end="arg.event.end as any" />
</template>
<template #dayHeaderContent="arg">
<CalendarDayHeader
<FullCalendarDayHeader
:date="arg.date"
:total-minutes="
dailyTotals[getDayJsInstance()(arg.date).format('YYYY-MM-DD')] || 0

View File

@@ -29,9 +29,9 @@ import TimeEntryMassActionRow from './TimeEntry/TimeEntryMassActionRow.vue';
import TimeEntryCreateModal from './TimeEntry/TimeEntryCreateModal.vue';
import TimeEntryEditModal from './TimeEntry/TimeEntryEditModal.vue';
import MoreOptionsDropdown from './MoreOptionsDropdown.vue';
import CalendarEventContent from './Calendar/CalendarEventContent.vue';
import CalendarDayHeader from './Calendar/CalendarDayHeader.vue';
import TimeEntryCalendar from './Calendar/TimeEntryCalendar.vue';
import FullCalendarEventContent from './FullCalendar/FullCalendarEventContent.vue';
import FullCalendarDayHeader from './FullCalendar/FullCalendarDayHeader.vue';
import TimeEntryCalendar from './FullCalendar/TimeEntryCalendar.vue';
export {
money,
@@ -57,7 +57,7 @@ export {
MoreOptionsDropdown,
TimeEntryCreateModal,
TimeEntryEditModal,
CalendarEventContent,
CalendarDayHeader,
FullCalendarEventContent,
FullCalendarDayHeader,
TimeEntryCalendar,
};