125 lines
3.6 KiB
TypeScript
125 lines
3.6 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
import type { TimeEntry } from '~/services/dto/time-entry'
|
|
import type { Task } from '~/services/dto/task'
|
|
import { useTimeEntryService } from '~/services/time-entries'
|
|
|
|
export const useTimerStore = defineStore('timer', () => {
|
|
const activeEntry = ref<TimeEntry | null>(null)
|
|
const pendingCompleteEntry = ref<TimeEntry | null>(null)
|
|
const now = ref(Date.now())
|
|
let intervalId: ReturnType<typeof setInterval> | null = null
|
|
|
|
const isRunning = computed(() => activeEntry.value !== null)
|
|
|
|
const elapsed = computed(() => {
|
|
if (!activeEntry.value) return 0
|
|
const start = new Date(activeEntry.value.startedAt).getTime()
|
|
return Math.floor((now.value - start) / 1000)
|
|
})
|
|
|
|
const elapsedFormatted = computed(() => {
|
|
const total = elapsed.value
|
|
const h = Math.floor(total / 3600)
|
|
const m = Math.floor((total % 3600) / 60)
|
|
const s = total % 60
|
|
return [h, m, s].map((v) => String(v).padStart(2, '0')).join(':')
|
|
})
|
|
|
|
function startTicking() {
|
|
stopTicking()
|
|
now.value = Date.now()
|
|
intervalId = setInterval(() => {
|
|
now.value = Date.now()
|
|
}, 1000)
|
|
}
|
|
|
|
function stopTicking() {
|
|
if (intervalId) {
|
|
clearInterval(intervalId)
|
|
intervalId = null
|
|
}
|
|
}
|
|
|
|
async function fetchActive() {
|
|
const { getActive } = useTimeEntryService()
|
|
activeEntry.value = await getActive()
|
|
if (activeEntry.value) {
|
|
startTicking()
|
|
} else {
|
|
stopTicking()
|
|
}
|
|
}
|
|
|
|
async function start() {
|
|
const authStore = useAuthStore()
|
|
if (!authStore.user) return
|
|
|
|
if (isRunning.value) {
|
|
await stop()
|
|
}
|
|
|
|
const { create } = useTimeEntryService()
|
|
activeEntry.value = await create({
|
|
startedAt: new Date().toISOString(),
|
|
user: `/api/users/${authStore.user.id}`,
|
|
})
|
|
startTicking()
|
|
}
|
|
|
|
async function startFromTask(task: Task) {
|
|
const authStore = useAuthStore()
|
|
if (!authStore.user) return
|
|
|
|
if (isRunning.value) {
|
|
await stop()
|
|
}
|
|
|
|
const { create } = useTimeEntryService()
|
|
activeEntry.value = await create({
|
|
startedAt: new Date().toISOString(),
|
|
user: `/api/users/${authStore.user.id}`,
|
|
title: task.title,
|
|
project: task.project
|
|
? (typeof task.project === 'string' ? task.project : (task.project['@id'] ?? (task.project.id ? `/api/projects/${task.project.id}` : null)))
|
|
: null,
|
|
task: typeof task === 'string' ? task : (task['@id'] ?? `/api/tasks/${task.id}`),
|
|
tags: task.tags?.map((t) => typeof t === 'string' ? t : (t['@id'] ?? `/api/task_tags/${t.id}`)) ?? [],
|
|
})
|
|
startTicking()
|
|
}
|
|
|
|
async function stop() {
|
|
if (!activeEntry.value) return
|
|
|
|
const wasEmpty = !activeEntry.value.task
|
|
|
|
const { update } = useTimeEntryService()
|
|
const stoppedEntry = await update(activeEntry.value.id, {
|
|
stoppedAt: new Date().toISOString(),
|
|
})
|
|
activeEntry.value = null
|
|
stopTicking()
|
|
|
|
if (wasEmpty) {
|
|
pendingCompleteEntry.value = stoppedEntry
|
|
}
|
|
}
|
|
|
|
function clearPendingEntry() {
|
|
pendingCompleteEntry.value = null
|
|
}
|
|
|
|
return {
|
|
activeEntry,
|
|
pendingCompleteEntry,
|
|
isRunning,
|
|
elapsed,
|
|
elapsedFormatted,
|
|
fetchActive,
|
|
start,
|
|
startFromTask,
|
|
stop,
|
|
clearPendingEntry,
|
|
}
|
|
})
|