mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-16 20:22:15 +01:00
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:
@@ -19,6 +19,9 @@ const dateFormat = computed(() => organization?.value?.date_format);
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="fc-day-header-custom">
|
<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>{{ formatDate(date.toISOString(), dateFormat) }}</span>
|
||||||
<span class="block text-xs text-muted-foreground font-medium mt-1">
|
<span class="block text-xs text-muted-foreground font-medium mt-1">
|
||||||
{{ formatHumanReadableDuration(totalSeconds, intervalFormat, numberFormat) }}
|
{{ formatHumanReadableDuration(totalSeconds, intervalFormat, numberFormat) }}
|
||||||
@@ -4,12 +4,12 @@ import dayGridPlugin from '@fullcalendar/daygrid';
|
|||||||
import timeGridPlugin from '@fullcalendar/timegrid';
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
||||||
import interactionPlugin from '@fullcalendar/interaction';
|
import interactionPlugin from '@fullcalendar/interaction';
|
||||||
import type { DatesSetArg, EventClickArg, EventDropArg, EventChangeArg } from '@fullcalendar/core';
|
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 { getDayJsInstance, getLocalizedDayJs } from '../utils/time';
|
||||||
import { getUserTimezone } from '../utils/settings';
|
import { getUserTimezone, getWeekStart } from '../utils/settings';
|
||||||
import { LoadingSpinner, TimeEntryCreateModal, TimeEntryEditModal } from '..';
|
import { LoadingSpinner, TimeEntryCreateModal, TimeEntryEditModal } from '..';
|
||||||
import CalendarEventContent from './CalendarEventContent.vue';
|
import FullCalendarEventContent from './FullCalendarEventContent.vue';
|
||||||
import CalendarDayHeader from './CalendarDayHeader.vue';
|
import FullCalendarDayHeader from './FullCalendarDayHeader.vue';
|
||||||
import type {
|
import type {
|
||||||
TimeEntry,
|
TimeEntry,
|
||||||
Project,
|
Project,
|
||||||
@@ -18,6 +18,7 @@ import type {
|
|||||||
CreateProjectBody,
|
CreateProjectBody,
|
||||||
CreateClientBody,
|
CreateClientBody,
|
||||||
Tag,
|
Tag,
|
||||||
|
Organization,
|
||||||
} from '@/packages/api/src';
|
} from '@/packages/api/src';
|
||||||
import type { Dayjs } from 'dayjs';
|
import type { Dayjs } from 'dayjs';
|
||||||
|
|
||||||
@@ -58,6 +59,41 @@ const selectedTimeEntry = ref<TimeEntry | null>(null);
|
|||||||
|
|
||||||
const calendarRef = ref<InstanceType<typeof FullCalendar> | 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
|
// Map API entries to FullCalendar events
|
||||||
const events = computed(() => {
|
const events = computed(() => {
|
||||||
return props.timeEntries
|
return props.timeEntries
|
||||||
@@ -188,7 +224,9 @@ const calendarOptions = computed(() => ({
|
|||||||
slotMaxTime: '24:00:00',
|
slotMaxTime: '24:00:00',
|
||||||
slotDuration: '00:15:00',
|
slotDuration: '00:15:00',
|
||||||
slotLabelInterval: '01:00:00',
|
slotLabelInterval: '01:00:00',
|
||||||
|
slotLabelFormat: getSlotLabelFormat(),
|
||||||
snapDuration: '00:15:00',
|
snapDuration: '00:15:00',
|
||||||
|
firstDay: getFirstDay(),
|
||||||
allDaySlot: false,
|
allDaySlot: false,
|
||||||
nowIndicator: true,
|
nowIndicator: true,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
@@ -261,7 +299,7 @@ watch(showEditTimeEntryModal, (value) => {
|
|||||||
:clients="clients" />
|
:clients="clients" />
|
||||||
<FullCalendar ref="calendarRef" class="fullcalendar" :options="calendarOptions">
|
<FullCalendar ref="calendarRef" class="fullcalendar" :options="calendarOptions">
|
||||||
<template #eventContent="arg">
|
<template #eventContent="arg">
|
||||||
<CalendarEventContent
|
<FullCalendarEventContent
|
||||||
:title="arg.event.title"
|
:title="arg.event.title"
|
||||||
:project-name="(arg.event.extendedProps as any).project?.name"
|
:project-name="(arg.event.extendedProps as any).project?.name"
|
||||||
:task-name="(arg.event.extendedProps as any).task?.name"
|
:task-name="(arg.event.extendedProps as any).task?.name"
|
||||||
@@ -275,7 +313,7 @@ watch(showEditTimeEntryModal, (value) => {
|
|||||||
:end="arg.event.end as any" />
|
:end="arg.event.end as any" />
|
||||||
</template>
|
</template>
|
||||||
<template #dayHeaderContent="arg">
|
<template #dayHeaderContent="arg">
|
||||||
<CalendarDayHeader
|
<FullCalendarDayHeader
|
||||||
:date="arg.date"
|
:date="arg.date"
|
||||||
:total-minutes="
|
:total-minutes="
|
||||||
dailyTotals[getDayJsInstance()(arg.date).format('YYYY-MM-DD')] || 0
|
dailyTotals[getDayJsInstance()(arg.date).format('YYYY-MM-DD')] || 0
|
||||||
@@ -29,9 +29,9 @@ import TimeEntryMassActionRow from './TimeEntry/TimeEntryMassActionRow.vue';
|
|||||||
import TimeEntryCreateModal from './TimeEntry/TimeEntryCreateModal.vue';
|
import TimeEntryCreateModal from './TimeEntry/TimeEntryCreateModal.vue';
|
||||||
import TimeEntryEditModal from './TimeEntry/TimeEntryEditModal.vue';
|
import TimeEntryEditModal from './TimeEntry/TimeEntryEditModal.vue';
|
||||||
import MoreOptionsDropdown from './MoreOptionsDropdown.vue';
|
import MoreOptionsDropdown from './MoreOptionsDropdown.vue';
|
||||||
import CalendarEventContent from './Calendar/CalendarEventContent.vue';
|
import FullCalendarEventContent from './FullCalendar/FullCalendarEventContent.vue';
|
||||||
import CalendarDayHeader from './Calendar/CalendarDayHeader.vue';
|
import FullCalendarDayHeader from './FullCalendar/FullCalendarDayHeader.vue';
|
||||||
import TimeEntryCalendar from './Calendar/TimeEntryCalendar.vue';
|
import TimeEntryCalendar from './FullCalendar/TimeEntryCalendar.vue';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
money,
|
money,
|
||||||
@@ -57,7 +57,7 @@ export {
|
|||||||
MoreOptionsDropdown,
|
MoreOptionsDropdown,
|
||||||
TimeEntryCreateModal,
|
TimeEntryCreateModal,
|
||||||
TimeEntryEditModal,
|
TimeEntryEditModal,
|
||||||
CalendarEventContent,
|
FullCalendarEventContent,
|
||||||
CalendarDayHeader,
|
FullCalendarDayHeader,
|
||||||
TimeEntryCalendar,
|
TimeEntryCalendar,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user