1
1
<script setup lang="ts">
2
2
import { useQuery } from ' @tanstack/vue-query'
3
- import { type Component , computed , nextTick , onMounted , watch , watchEffect } from ' vue'
3
+ import { type Component , computed , nextTick , onMounted , ref , watch , watchEffect } from ' vue'
4
4
5
5
import {
6
6
TimeEntryGroupedTable ,
7
7
TimeTrackerControls ,
8
8
TimeTrackerRunningInDifferentOrganizationOverlay ,
9
+ TimeEntryMassActionRow ,
9
10
} from ' @solidtime/ui'
10
11
import {
11
12
emptyTimeEntry ,
@@ -17,6 +18,7 @@ import {
17
18
useCurrentTimeEntryUpdateMutation ,
18
19
useTimeEntriesUpdateMutation ,
19
20
useTimeEntryUpdateMutation ,
21
+ useTimeEntriesDeleteMutation ,
20
22
} from ' ../utils/timeEntries.ts'
21
23
import { getAllProjects , useProjectCreateMutation } from ' ../utils/projects.ts'
22
24
import { getAllTasks } from ' ../utils/tasks.ts'
@@ -27,6 +29,7 @@ import type {
27
29
Project ,
28
30
Tag ,
29
31
TimeEntry ,
32
+ UpdateMultipleTimeEntriesChangeset ,
30
33
} from ' @solidtime/api'
31
34
import { getAllTags , useTagCreateMutation } from ' ../utils/tags.ts'
32
35
import { LoadingSpinner } from ' @solidtime/ui'
@@ -44,11 +47,15 @@ import { apiClient } from '../utils/api'
44
47
import { updateTrayState } from ' ../utils/tray'
45
48
import { getMe } from ' ../utils/me'
46
49
47
- const { currentOrganizationId } = useMyMemberships ()
48
- const currentOrganizationLoaded = computed (() => !! currentOrganizationId )
50
+ const { currentOrganizationId, currentMembership } = useMyMemberships ()
51
+ const currentOrganizationLoaded = computed (() => !! currentOrganizationId . value )
49
52
50
53
const { liveTimer, startLiveTimer, stopLiveTimer } = useLiveTimer ()
51
54
55
+ watch (currentOrganizationId , () => {
56
+ selectedTimeEntries .value = []
57
+ })
58
+
52
59
const { data : timeEntriesResponse } = useQuery ({
53
60
queryKey: [' timeEntries' , currentOrganizationId ],
54
61
queryFn : () => getAllTimeEntries (currentOrganizationId .value , currentMembershipId .value ),
@@ -61,8 +68,8 @@ const { data: currentTimeEntryResponse, isError: currentTimeEntryResponseIsError
61
68
queryFn : () => getCurrentTimeEntry (),
62
69
})
63
70
64
- const currentTimeEntry = useStorage (' currentTimeEntry' , { ... emptyTimeEntry })
65
- const lastTimeEntry = useStorage (' lastTimeEntry' , { ... emptyTimeEntry })
71
+ const currentTimeEntry = useStorage < TimeEntry > (' currentTimeEntry' , { ... emptyTimeEntry })
72
+ const lastTimeEntry = useStorage < TimeEntry > (' lastTimeEntry' , { ... emptyTimeEntry })
66
73
67
74
watch (timeEntries , () => {
68
75
if (timeEntries .value ?.[0 ]) {
@@ -116,6 +123,7 @@ const tags = computed(() => tagsResponse.value?.data)
116
123
117
124
const currentTimeEntryUpdateMutation = useCurrentTimeEntryUpdateMutation ()
118
125
const timeEntriesUpdate = useTimeEntriesUpdateMutation ()
126
+ const timeEntriesDelete = useTimeEntriesDeleteMutation ()
119
127
const timeEntryUpdate = useTimeEntryUpdateMutation ()
120
128
const timeEntryDelete = useTimeEntryDeleteMutation ()
121
129
const timeEntryCreate = useTimeEntryCreateMutation ()
@@ -264,8 +272,30 @@ watch(meResponse, () => {
264
272
}
265
273
})
266
274
275
+ const selectedTimeEntries = ref ([] as TimeEntry [])
276
+
277
+ function deleteSelected() {
278
+ timeEntriesDelete .mutate (selectedTimeEntries .value )
279
+ selectedTimeEntries .value = []
280
+ }
281
+
282
+ async function clearSelectionAndState() {
283
+ selectedTimeEntries .value = []
284
+ }
285
+
267
286
// TODO: Fix me
268
287
const currency = ' EUR'
288
+
289
+ const canCreateProjects = computed (() => {
290
+ if (currentMembership .value ) {
291
+ return (
292
+ currentMembership .value ?.role === ' admin' ||
293
+ currentMembership .value ?.role === ' owner' ||
294
+ currentMembership .value ?.role === ' manager'
295
+ )
296
+ }
297
+ return false
298
+ })
269
299
</script >
270
300
271
301
<template >
@@ -289,6 +319,8 @@ const currency = 'EUR'
289
319
v-model:currentTimeEntry =" currentTimeEntry"
290
320
v-model:liveTimer =" liveTimer"
291
321
:tags
322
+ :enableEstimatedTime =" false"
323
+ :canCreateProject =" canCreateProjects"
292
324
:createProject
293
325
:createClient
294
326
:tasks
@@ -305,8 +337,33 @@ const currency = 'EUR'
305
337
</div >
306
338
</div >
307
339
<div class =" overflow-y-scroll w-full flex-1" >
340
+ <TimeEntryMassActionRow
341
+ :selectedTimeEntries
342
+ :deleteSelected
343
+ :allSelected =" selectedTimeEntries.length === timeEntries.length"
344
+ :tasks
345
+ :tags
346
+ :currency
347
+ :enableEstimatedTime =" false"
348
+ :canCreateProject =" canCreateProjects"
349
+ :projects
350
+ :clients
351
+ :updateTimeEntries ="
352
+ (args: UpdateMultipleTimeEntriesChangeset) =>
353
+ timeEntriesUpdate.mutate({
354
+ ids: selectedTimeEntries.map((timeEntry) => timeEntry.id),
355
+ changes: args,
356
+ })
357
+ "
358
+ :createProject
359
+ :createClient
360
+ :createTag
361
+ @submit =" clearSelectionAndState"
362
+ @select-all =" selectedTimeEntries = [...timeEntries]"
363
+ @unselect-all =" selectedTimeEntries = []" ></TimeEntryMassActionRow >
308
364
<TimeEntryGroupedTable
309
365
v-if =" timeEntries && projects && tasks && tags && clients"
366
+ v-model:selected =" selectedTimeEntries"
310
367
:projects
311
368
:tasks
312
369
:tags
@@ -324,7 +381,10 @@ const currency = 'EUR'
324
381
timeEntriesUpdate.mutate({ ids: ids, changes: changes })
325
382
"
326
383
:deleteTimeEntries ="
327
- (args) => args.forEach((timeEntry) => timeEntryDelete.mutate(timeEntry))
384
+ (args: TimeEntry[]) =>
385
+ args.forEach((timeEntry: TimeEntry) =>
386
+ timeEntryDelete.mutate(timeEntry)
387
+ )
328
388
"
329
389
:createTimeEntry =" createTimeEntry"
330
390
:createTag
0 commit comments