feat(time-tracking) : add TimeEntry DTO, service, timer store, and i18n keys

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-10 22:22:40 +01:00
parent 1e07eb1d64
commit cf021d6136
5 changed files with 197 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import type { TaskPriority } from './task-priority'
import type { TaskType } from './task-type'
import type { TaskGroup } from './task-group'
import type { UserData } from './user-data'
import type { Project } from './project'
export type Task = {
id: number
@@ -15,6 +16,7 @@ export type Task = {
priority: TaskPriority | null
assignee: UserData | null
group: TaskGroup | null
project: Project | null
types: TaskType[]
}

View File

@@ -0,0 +1,28 @@
import type { UserData } from './user-data'
import type { Project } from './project'
import type { Task } from './task'
import type { TaskType } from './task-type'
export type TimeEntry = {
id: number
'@id'?: string
title: string | null
description: string | null
startedAt: string
stoppedAt: string | null
user: UserData
project: Project | null
task: Task | null
types: TaskType[]
}
export type TimeEntryWrite = {
title?: string | null
description?: string | null
startedAt: string
stoppedAt?: string | null
user: string
project?: string | null
task?: string | null
types?: string[]
}

View File

@@ -0,0 +1,53 @@
import type { TimeEntry, TimeEntryWrite } from './dto/time-entry'
import type { HydraCollection } from '~/utils/api'
import { extractHydraMembers } from '~/utils/api'
export function useTimeEntryService() {
const api = useApi()
async function getByDateRange(params: {
after: string
before: string
user?: number
types?: number[]
}): Promise<TimeEntry[]> {
const query: Record<string, unknown> = {
'startedAt[after]': params.after,
'startedAt[before]': params.before,
}
if (params.user) {
query.user = `/api/users/${params.user}`
}
const data = await api.get<HydraCollection<TimeEntry>>('/time_entries', query)
return extractHydraMembers(data)
}
async function getActive(): Promise<TimeEntry | null> {
try {
const result = await api.get<TimeEntry | null>('/time_entries/active', {}, { toast: false })
return result ?? null
} catch {
return null
}
}
async function create(payload: TimeEntryWrite): Promise<TimeEntry> {
return api.post<TimeEntry>('/time_entries', payload as Record<string, unknown>, {
toastSuccessKey: 'timeEntries.created',
})
}
async function update(id: number, payload: Partial<TimeEntryWrite>): Promise<TimeEntry> {
return api.patch<TimeEntry>(`/time_entries/${id}`, payload as Record<string, unknown>, {
toastSuccessKey: 'timeEntries.updated',
})
}
async function remove(id: number): Promise<void> {
await api.delete(`/time_entries/${id}`, {}, {
toastSuccessKey: 'timeEntries.deleted',
})
}
return { getByDateRange, getActive, create, update, remove }
}