mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-17 12:42:15 +01:00
add sidebar timer start/stop and live timer updates
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user