@@ -16,12 +16,25 @@ import { useProjectsStore } from '@/utils/useProjects';
1616import { useTasksStore } from ' @/utils/useTasks' ;
1717import { useTagsStore } from ' @/utils/useTags' ;
1818import TimeTrackerControls from ' @/packages/ui/src/TimeTracker/TimeTrackerControls.vue' ;
19- import type { CreateClientBody , CreateProjectBody , Project } from ' @/packages/api/src' ;
19+ import type {
20+ CreateClientBody ,
21+ CreateProjectBody ,
22+ CreateTimeEntryBody ,
23+ Project ,
24+ Tag ,
25+ } from ' @/packages/api/src' ;
2026import TimeTrackerRunningInDifferentOrganizationOverlay from ' @/packages/ui/src/TimeTracker/TimeTrackerRunningInDifferentOrganizationOverlay.vue' ;
27+ import TimeTrackerMoreOptionsDropdown from ' @/packages/ui/src/TimeTracker/TimeTrackerMoreOptionsDropdown.vue' ;
28+ import TimeEntryCreateModal from ' @/packages/ui/src/TimeEntry/TimeEntryCreateModal.vue' ;
2129import { useClientsStore } from ' @/utils/useClients' ;
2230import { getOrganizationCurrencyString } from ' @/utils/money' ;
2331import { isAllowedToPerformPremiumAction } from ' @/utils/billing' ;
2432import { canCreateProjects } from ' @/utils/permissions' ;
33+ import { ref } from ' vue' ;
34+ import { useTimeEntriesStore } from ' @/utils/useTimeEntries' ;
35+ import { useMutation , useQueryClient } from ' @tanstack/vue-query' ;
36+ import { api } from ' @/packages/api/src' ;
37+ import { useNotificationsStore } from ' @/utils/notification' ;
2538
2639const page = usePage <{
2740 auth: {
@@ -47,6 +60,8 @@ const emit = defineEmits<{
4760 change: [];
4861}>();
4962
63+ const showManualTimeEntryModal = ref (false );
64+
5065watch (isActive , () => {
5166 if (isActive .value ) {
5267 startLiveTimer ();
@@ -93,14 +108,64 @@ function switchToTimeEntryOrganization() {
93108 switchOrganization (currentTimeEntry .value .organization_id );
94109 }
95110}
96- async function createTag(tag : string ) {
111+ async function createTag(tag : string ): Promise < Tag | undefined > {
97112 return await useTagsStore ().createTag (tag );
98113}
99114
115+ async function createTimeEntry(timeEntry : Omit <CreateTimeEntryBody , ' member_id' >) {
116+ await useTimeEntriesStore ().createTimeEntry (timeEntry );
117+ showManualTimeEntryModal .value = false ;
118+ }
119+
120+ const { handleApiRequestNotifications } = useNotificationsStore ();
121+ const queryClient = useQueryClient ();
122+
123+ const deleteTimeEntryMutation = useMutation ({
124+ mutationFn : async (timeEntryId : string ) => {
125+ const organizationId = getCurrentOrganizationId ();
126+ if (! organizationId ) {
127+ throw new Error (' No organization selected' );
128+ }
129+ return await api .deleteTimeEntry (undefined , {
130+ params: {
131+ organization: organizationId ,
132+ timeEntry: timeEntryId ,
133+ },
134+ });
135+ },
136+ onSuccess : async () => {
137+ await currentTimeEntryStore .fetchCurrentTimeEntry ();
138+ await useTimeEntriesStore ().fetchTimeEntries ();
139+ queryClient .invalidateQueries ({ queryKey: [' timeEntry' ] });
140+ queryClient .invalidateQueries ({ queryKey: [' timeEntries' ] });
141+ },
142+ });
143+
144+ async function discardCurrentTimeEntry() {
145+ if (currentTimeEntry .value .id ) {
146+ await handleApiRequestNotifications (
147+ () => deleteTimeEntryMutation .mutateAsync (currentTimeEntry .value .id ),
148+ ' Time entry discarded successfully' ,
149+ ' Failed to discard time entry'
150+ );
151+ }
152+ }
153+
100154const { tags } = storeToRefs (useTagsStore ());
101155 </script >
102156
103157<template >
158+ <TimeEntryCreateModal
159+ v-model:show =" showManualTimeEntryModal"
160+ :enable-estimated-time =" isAllowedToPerformPremiumAction()"
161+ :create-project =" createProject"
162+ :create-client =" createClient"
163+ :create-tag =" createTag"
164+ :create-time-entry =" createTimeEntry"
165+ :projects
166+ :tasks
167+ :tags
168+ :clients ></TimeEntryCreateModal >
104169 <CardTitle title =" Time Tracker" :icon =" ClockIcon" ></CardTitle >
105170 <div class =" relative" >
106171 <TimeTrackerRunningInDifferentOrganizationOverlay
@@ -109,24 +174,34 @@ const { tags } = storeToRefs(useTagsStore());
109174 switchToTimeEntryOrganization
110175 " ></TimeTrackerRunningInDifferentOrganizationOverlay >
111176
112- <TimeTrackerControls
113- v-model:current-time-entry =" currentTimeEntry"
114- v-model:live-timer =" now"
115- :create-project
116- :enable-estimated-time =" isAllowedToPerformPremiumAction()"
117- :can-create-project =" canCreateProjects()"
118- :create-client
119- :clients
120- :tags
121- :tasks
122- :projects
123- :create-tag
124- :is-active
125- :currency =" getOrganizationCurrencyString()"
126- @start-live-timer =" startLiveTimer"
127- @stop-live-timer =" stopLiveTimer"
128- @start-timer =" setActiveState(true)"
129- @stop-timer =" setActiveState(false)"
130- @update-time-entry =" updateTimeEntry" ></TimeTrackerControls >
177+ <div class =" flex w-full items-center gap-2" >
178+ <div class =" flex w-full items-center gap-2" >
179+ <div class =" flex-1" >
180+ <TimeTrackerControls
181+ v-model:current-time-entry =" currentTimeEntry"
182+ v-model:live-timer =" now"
183+ :create-project
184+ :enable-estimated-time =" isAllowedToPerformPremiumAction()"
185+ :can-create-project =" canCreateProjects()"
186+ :create-client
187+ :clients
188+ :tags
189+ :tasks
190+ :projects
191+ :create-tag
192+ :is-active
193+ :currency =" getOrganizationCurrencyString()"
194+ @start-live-timer =" startLiveTimer"
195+ @stop-live-timer =" stopLiveTimer"
196+ @start-timer =" setActiveState(true)"
197+ @stop-timer =" setActiveState(false)"
198+ @update-time-entry =" updateTimeEntry" ></TimeTrackerControls >
199+ </div >
200+ <TimeTrackerMoreOptionsDropdown
201+ :has-active-timer =" isActive"
202+ @manual-entry =" showManualTimeEntryModal = true"
203+ @discard =" discardCurrentTimeEntry" ></TimeTrackerMoreOptionsDropdown >
204+ </div >
205+ </div >
131206 </div >
132207</template >
0 commit comments