Skip to content

Commit 33ac994

Browse files
committed
add activity status plugin to calendar
1 parent 8d3ee58 commit 33ac994

6 files changed

Lines changed: 289 additions & 14 deletions

File tree

resources/js/packages/ui/src/FullCalendar/FullCalendarDayHeader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const dateFormat = computed(() => organization?.value?.date_format);
2323
<div class="text-xs text-muted-foreground font-medium">
2424
{{ date.format('ddd') }}
2525
</div>
26-
<span>{{ formatDate(date.toISOString(), dateFormat) }}</span>
26+
<span class="text-xs">{{ formatDate(date.toISOString(), dateFormat) }}</span>
2727
<span class="block text-xs text-muted-foreground font-medium mt-1">
2828
{{ formatHumanReadableDuration(totalSeconds, intervalFormat, numberFormat) }}
2929
</span>

resources/js/packages/ui/src/FullCalendar/FullCalendarEventContent.vue

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ const formattedDuration = computed(() =>
4040
</script>
4141

4242
<template>
43-
<div class="text-xs leading-tight">
44-
<div class="font-semibold mb-0.5">{{ title }}</div>
45-
<div v-if="projectName" class="font-medium text-[0.6875rem] opacity-90">
43+
<div class="text-2xs leading-tight px-0.5 py-1.5">
44+
<div class="font-semibold">{{ title }}</div>
45+
<div v-if="projectName" class="font-medium opacity-90">
4646
{{ projectName }}
4747
</div>
48-
<div v-if="taskName" class="font-medium text-[0.6875rem] opacity-90">
48+
<div v-if="taskName" class="font-medium">
4949
{{ taskName }}
5050
</div>
51-
<div v-if="clientName" class="text-[0.625rem] italic opacity-85">
51+
<div v-if="clientName" class="opacity-85">
5252
{{ clientName }}
5353
</div>
54-
<div class="text-[0.625rem] font-semibold opacity-90 mt-0.5">
54+
<div class="opacity-90">
5555
{{ formattedDuration }}
5656
</div>
5757
</div>

resources/js/packages/ui/src/FullCalendar/TimeEntryCalendar.vue

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,27 @@ import dayGridPlugin from '@fullcalendar/daygrid';
44
import timeGridPlugin from '@fullcalendar/timegrid';
55
import interactionPlugin from '@fullcalendar/interaction';
66
import type { DatesSetArg, EventClickArg, EventDropArg, EventChangeArg } from '@fullcalendar/core';
7-
import { computed, ref, watch, inject, type ComputedRef } from 'vue';
7+
import {
8+
computed,
9+
ref,
10+
watch,
11+
inject,
12+
type ComputedRef,
13+
nextTick,
14+
onMounted,
15+
onActivated,
16+
} from 'vue';
817
import chroma from 'chroma-js';
918
import { useCssVariable } from '@/utils/useCssVariable';
1019
import { getDayJsInstance, getLocalizedDayJs } from '../utils/time';
1120
import { getUserTimezone, getWeekStart } from '../utils/settings';
1221
import { LoadingSpinner, TimeEntryCreateModal, TimeEntryEditModal } from '..';
1322
import FullCalendarEventContent from './FullCalendarEventContent.vue';
1423
import FullCalendarDayHeader from './FullCalendarDayHeader.vue';
24+
import activityStatusPlugin, {
25+
type ActivityPeriod,
26+
renderActivityStatusBoxes,
27+
} from './idleStatusPlugin';
1528
import type {
1629
TimeEntry,
1730
Project,
@@ -37,6 +50,7 @@ const props = defineProps<{
3750
tasks: Task[];
3851
clients: Client[];
3952
tags: Tag[];
53+
activityPeriods?: ActivityPeriod[];
4054
loading?: boolean;
4155
4256
// Permissions / feature flags
@@ -165,6 +179,8 @@ const dailyTotals = computed(() => {
165179
166180
function emitDatesChange(arg: DatesSetArg) {
167181
emit('dates-change', { start: arg.start, end: arg.end });
182+
// Render activity boxes after calendar view has been rendered
183+
renderActivityBoxes();
168184
}
169185
170186
function handleDateSelect(arg: { start: Date; end: Date }) {
@@ -234,7 +250,7 @@ async function handleEventResize(arg: EventChangeArg) {
234250
}
235251
236252
const calendarOptions = computed(() => ({
237-
plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin],
253+
plugins: [dayGridPlugin, timeGridPlugin, interactionPlugin, activityStatusPlugin],
238254
initialView: 'timeGridWeek',
239255
headerToolbar: {
240256
left: 'prev,next today',
@@ -265,6 +281,7 @@ const calendarOptions = computed(() => ({
265281
datesSet: emitDatesChange,
266282
267283
events: events.value,
284+
activityPeriods: props.activityPeriods || [],
268285
}));
269286
270287
watch(showCreateTimeEntryModal, (value) => {
@@ -283,6 +300,48 @@ watch(showEditTimeEntryModal, (value) => {
283300
emit('refresh');
284301
}
285302
});
303+
304+
// Render activity status boxes after FullCalendar has rendered
305+
const renderActivityBoxes = () => {
306+
if (!calendarRef.value || !props.activityPeriods) return;
307+
308+
const calendarEl = calendarRef.value.$el as HTMLElement;
309+
if (calendarEl && props.activityPeriods.length > 0) {
310+
renderActivityStatusBoxes(calendarEl, props.activityPeriods);
311+
}
312+
};
313+
314+
// Watch for activity periods changes - re-render when data changes
315+
watch(
316+
() => props.activityPeriods,
317+
() => {
318+
renderActivityBoxes();
319+
}
320+
);
321+
322+
const scrollToCurrentTime = () => {
323+
nextTick(() => {
324+
if (calendarRef.value) {
325+
const now = getDayJsInstance()();
326+
const oneHourBefore = now.subtract(1, 'hour');
327+
328+
// If subtracting 1 hour keeps us on the same day, scroll to 1 hour before
329+
const scrollTime = now.isSame(oneHourBefore, 'day')
330+
? oneHourBefore.format('HH:mm:ss')
331+
: now.format('HH:mm:ss');
332+
333+
calendarRef.value.getApi().scrollToTime(scrollTime);
334+
}
335+
});
336+
};
337+
338+
onMounted(() => {
339+
scrollToCurrentTime();
340+
});
341+
342+
onActivated(() => {
343+
scrollToCurrentTime();
344+
});
286345
</script>
287346

288347
<template>
@@ -377,11 +436,11 @@ watch(showEditTimeEntryModal, (value) => {
377436
}
378437
379438
.fullcalendar :deep(.fc-timegrid-slot-label) {
380-
background-color: var(--theme-color-default-background);
439+
background-color: var(--background);
381440
}
382441
383442
.fullcalendar :deep(.fc-toolbar) {
384-
background-color: var(--theme-color-default-background);
443+
background-color: var(--background);
385444
padding: 0.5rem;
386445
margin-bottom: 0;
387446
}
@@ -463,7 +522,7 @@ watch(showEditTimeEntryModal, (value) => {
463522
464523
.fullcalendar :deep(.fc-event) {
465524
border-radius: var(--radius);
466-
padding: 0.45rem 0.25rem;
525+
padding: 0;
467526
font-size: 0.75rem;
468527
cursor: pointer;
469528
box-shadow: var(--theme-shadow-card);
@@ -525,7 +584,7 @@ watch(showEditTimeEntryModal, (value) => {
525584
}
526585
527586
.fullcalendar :deep(.fc-highlight) {
528-
background-color: var(--theme-color-default-background);
587+
background-color: var(--primary);
529588
}
530589
531590
.fullcalendar :deep(.fc-select-mirror) {
@@ -543,7 +602,7 @@ watch(showEditTimeEntryModal, (value) => {
543602
}
544603
545604
.fullcalendar :deep(.fc-timegrid-body) {
546-
background-color: var(--theme-color-default-background);
605+
background-color: var(--background);
547606
}
548607
549608
.fullcalendar :deep(.fc-timegrid-col) {
@@ -610,4 +669,34 @@ watch(showEditTimeEntryModal, (value) => {
610669
.fullcalendar :deep(.fc-event-main) {
611670
padding: 0.125rem 0.25rem;
612671
}
672+
673+
/* Activity status plugin styles */
674+
.fullcalendar :deep(.activity-status-box) {
675+
transition: opacity 0.2s ease;
676+
}
677+
678+
.fullcalendar :deep(.activity-status-box.idle) {
679+
background-color: rgba(239, 68, 68, 0.3) !important;
680+
}
681+
682+
.fullcalendar :deep(.activity-status-box.idle):hover {
683+
background-color: rgba(239, 68, 68, 1) !important;
684+
}
685+
686+
.fullcalendar :deep(.activity-status-box.active) {
687+
background-color: rgba(34, 197, 94, 0.3) !important;
688+
}
689+
690+
.fullcalendar :deep(.activity-status-box.active):hover {
691+
background-color: rgba(34, 197, 94, 1) !important;
692+
}
693+
694+
/* Add left margin to events only on days with activity status data */
695+
.fullcalendar :deep(.has-activity-status .fc-timegrid-event-harness) {
696+
margin-left: 15px !important;
697+
}
698+
699+
.fullcalendar :deep(.fc-timegrid-event) {
700+
margin-left: 0 !important;
701+
}
613702
</style>

0 commit comments

Comments
 (0)