add sidebar timer start/stop and live timer updates

This commit is contained in:
Gregor Vostrak
2024-03-14 23:52:50 +01:00
parent 81e133528a
commit fb04cff7c1
4 changed files with 66 additions and 36 deletions

View File

@@ -1,13 +1,36 @@
<script setup lang="ts"> <script setup lang="ts">
import TimeTrackerStartStop from '@/Components/common/TimeTrackerStartStop.vue'; import TimeTrackerStartStop from '@/Components/common/TimeTrackerStartStop.vue';
import { useCurrentTimeEntryStore } from '@/utils/useCurrentTimeEntry';
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
import dayjs from 'dayjs';
import { formatHumanReadableDuration } from '@/utils/time';
const store = useCurrentTimeEntryStore();
const { currentTimeEntry, now, isActive } = storeToRefs(store);
const { onToggleButtonPress } = store;
const currentTime = computed(() => {
if (now.value && currentTimeEntry.value.start) {
const startTime = dayjs(currentTimeEntry.value.start);
const diff = now.value.diff(startTime, 's');
// return dayjs(diff).utc().format('HH:mm:ss');
return formatHumanReadableDuration(diff);
}
return formatHumanReadableDuration(0);
});
</script> </script>
<template> <template>
<div class="py-4 px-2 flex justify-between items-center"> <div class="py-4 px-2 flex justify-between items-center">
<div> <div>
<div class="text-muted font-extrabold text-xs">Current Timer</div> <div class="text-muted font-extrabold text-xs">Current Timer</div>
<div class="text-white font-medium text-lg py-1">1h 23min</div> <div class="text-white font-medium text-lg py-1">
{{ currentTime }}
</div>
</div> </div>
<TimeTrackerStartStop size="base"></TimeTrackerStartStop> <TimeTrackerStartStop
:active="isActive"
@changed="onToggleButtonPress"
size="base"></TimeTrackerStartStop>
</div> </div>
</template> </template>

View File

@@ -8,7 +8,7 @@ import ProjectDropdown from '@/Components/common/ProjectDropdown.vue';
import { usePage } from '@inertiajs/vue3'; import { usePage } from '@inertiajs/vue3';
import { type User } from '@/types/models'; import { type User } from '@/types/models';
import { computed, onMounted, ref, watch } from 'vue'; import { computed, onMounted, ref, watch } from 'vue';
import dayjs, { Dayjs } from 'dayjs'; import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc'; import utc from 'dayjs/plugin/utc';
import duration from 'dayjs/plugin/duration'; import duration from 'dayjs/plugin/duration';
@@ -27,24 +27,9 @@ dayjs.extend(duration);
dayjs.extend(utc); dayjs.extend(utc);
const currentTimeEntryStore = useCurrentTimeEntryStore(); const currentTimeEntryStore = useCurrentTimeEntryStore();
const { currentTimeEntry, isActive } = storeToRefs(currentTimeEntryStore); const { currentTimeEntry, isActive, now } = storeToRefs(currentTimeEntryStore);
const { startLiveTimer, stopLiveTimer, onToggleButtonPress } =
const now = ref<null | Dayjs>(null); currentTimeEntryStore;
const interval = ref<ReturnType<typeof setInterval> | null>(null);
function startLiveTimer() {
stopLiveTimer();
now.value = dayjs().utc();
interval.value = setInterval(() => {
now.value = dayjs().utc();
}, 1000);
}
function stopLiveTimer() {
if (interval.value !== null) {
clearInterval(interval.value);
}
}
watch(isActive, () => { watch(isActive, () => {
if (isActive.value) { if (isActive.value) {
@@ -85,18 +70,6 @@ onMounted(async () => {
} }
}); });
async function onToggleButtonPress(newState: boolean) {
if (page.props.auth.user.current_team_id) {
if (newState) {
startLiveTimer();
await useCurrentTimeEntryStore().startTimer();
} else {
stopLiveTimer();
await useCurrentTimeEntryStore().stopTimer();
}
}
}
const currentProject = ref<Project>(); const currentProject = ref<Project>();
watch(currentProject, () => { watch(currentProject, () => {
if (currentProject.value) { if (currentProject.value) {
@@ -112,6 +85,7 @@ function updateTimeEntry() {
useCurrentTimeEntryStore().updateTimer(); useCurrentTimeEntryStore().updateTimer();
} }
} }
function pauseLiveTimerUpdate() { function pauseLiveTimerUpdate() {
stopLiveTimer(); stopLiveTimer();
} }

View File

@@ -15,7 +15,7 @@ const props = withDefaults(
} }
); );
const buttonSizeClasses = { const buttonSizeClasses = {
base: 'w-8 h-8 !bg-accent-200/30 ', base: 'w-8 h-8 bg-accent-200/40 hover:scale-110 hover:bg-accent-300/70 ring-accent-200/10 focus:ring-accent-200/10 hover:ring-4',
large: 'w-11 h-11 ring-accent-200/10 focus:ring-accent-200/20 ring-8 hover:scale-110', large: 'w-11 h-11 ring-accent-200/10 focus:ring-accent-200/20 ring-8 hover:scale-110',
}; };
const iconClass = { const iconClass = {
@@ -27,7 +27,7 @@ const buttonColorClasses = computed(() => {
if (props.active) { if (props.active) {
return 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80'; return 'bg-red-400/80 hover:bg-red-500/80 focus:bg-red-500/80';
} else { } else {
return 'bg-accent-300/70 hover:bg-accent-400/80 focus:bg-accent-400/80'; return 'bg-accent-300/50 hover:bg-accent-400/70 focus:bg-accent-400/70';
} }
}); });

View File

@@ -3,7 +3,7 @@ import { computed, reactive, ref } from 'vue';
import { api } from '../../../openapi.json.client'; import { api } from '../../../openapi.json.client';
import type { ZodiosResponseByAlias } from '@zodios/core'; import type { ZodiosResponseByAlias } from '@zodios/core';
import type { SolidTimeApi } from '@/utils/api'; import type { SolidTimeApi } from '@/utils/api';
import dayjs from 'dayjs'; import dayjs, { Dayjs } from 'dayjs';
import utc from 'dayjs/plugin/utc'; import utc from 'dayjs/plugin/utc';
import { getCurrentOrganizationId, getCurrentUserId } from '@/utils/useUser'; import { getCurrentOrganizationId, getCurrentUserId } from '@/utils/useUser';
@@ -30,6 +30,23 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
currentTimeEntry.value = { ...emptyTimeEntry }; currentTimeEntry.value = { ...emptyTimeEntry };
} }
const now = ref<null | Dayjs>(null);
const interval = ref<ReturnType<typeof setInterval> | null>(null);
function startLiveTimer() {
stopLiveTimer();
now.value = dayjs().utc();
interval.value = setInterval(() => {
now.value = dayjs().utc();
}, 1000);
}
function stopLiveTimer() {
if (interval.value !== null) {
clearInterval(interval.value);
}
}
async function fetchCurrentTimeEntry() { async function fetchCurrentTimeEntry() {
const organizationId = getCurrentOrganizationId(); const organizationId = getCurrentOrganizationId();
if (organizationId) { if (organizationId) {
@@ -143,6 +160,18 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
return false; return false;
}); });
async function onToggleButtonPress(newState: boolean) {
if (newState) {
startLiveTimer();
await startTimer();
} else {
stopLiveTimer();
await stopTimer();
}
}
startLiveTimer();
return { return {
currentTimeEntry, currentTimeEntry,
fetchCurrentTimeEntry, fetchCurrentTimeEntry,
@@ -150,5 +179,9 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
stopTimer, stopTimer,
updateTimer, updateTimer,
isActive, isActive,
startLiveTimer,
stopLiveTimer,
now,
onToggleButtonPress,
}; };
}); });