Files
solidtime/resources/js/utils/useTimeEntriesMutations.ts
2026-07-21 18:54:05 +02:00

166 lines
6.3 KiB
TypeScript

import { useMutation, useQueryClient } from '@tanstack/vue-query';
import {
api,
type CreateTimeEntryBody,
type TimeEntry,
type UpdateMultipleTimeEntriesChangeset,
} from '@/packages/api/src';
import { getCurrentMembershipId, getCurrentOrganizationId } from '@/utils/useUser';
import { useNotificationsStore } from '@/utils/notification';
export function useTimeEntriesMutations() {
const queryClient = useQueryClient();
const { handleApiRequestNotifications, addNotification } = useNotificationsStore();
const { mutateAsync: createTimeEntry } = useMutation({
mutationFn: async (timeEntry: Omit<CreateTimeEntryBody, 'member_id'>) => {
const organizationId = getCurrentOrganizationId();
const memberId = getCurrentMembershipId();
if (organizationId && memberId !== undefined) {
const newTimeEntry = {
...timeEntry,
member_id: memberId,
} as CreateTimeEntryBody;
return await handleApiRequestNotifications(
() =>
api.createTimeEntry(newTimeEntry, {
params: {
organization: organizationId,
},
}),
'Time entry created successfully',
'Failed to create time entry'
);
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
},
});
const { mutateAsync: updateTimeEntry } = useMutation({
mutationFn: async (timeEntry: TimeEntry) => {
const organizationId = getCurrentOrganizationId();
if (organizationId) {
return await handleApiRequestNotifications(
() =>
api.updateTimeEntry(timeEntry, {
params: {
organization: organizationId,
timeEntry: timeEntry.id,
},
}),
'Time entry updated successfully',
'Failed to update time entry'
);
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
},
});
const { mutateAsync: updateTimeEntries } = useMutation({
mutationFn: async ({
ids,
changes,
}: {
ids: string[];
changes: UpdateMultipleTimeEntriesChangeset;
}) => {
const organizationId = getCurrentOrganizationId();
if (organizationId) {
const response = await handleApiRequestNotifications(
() =>
api.updateMultipleTimeEntries(
{
ids: ids,
changes: changes,
},
{
params: {
organization: organizationId,
},
}
),
undefined,
'Failed to update time entries'
);
// The endpoint applies the changeset per entry and skips entries it can't
// apply it to (e.g. breaks with a project/tags/billable change) — a 200
// with their ids in `error`. Surface that instead of claiming success.
const skippedCount = response?.error.length ?? 0;
if (skippedCount > 0) {
addNotification(
'error',
`${skippedCount} of ${ids.length} time entries ${skippedCount === 1 ? 'was' : 'were'} skipped`,
'No changes were applied to the skipped entries — break entries can not have a project or tags, or be billable.'
);
} else {
addNotification('success', 'Time entries updated successfully');
}
return response;
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
},
});
const { mutateAsync: deleteTimeEntry } = useMutation({
mutationFn: async (timeEntryId: string) => {
const organizationId = getCurrentOrganizationId();
if (organizationId) {
return await handleApiRequestNotifications(
() =>
api.deleteTimeEntry(undefined, {
params: {
organization: organizationId,
timeEntry: timeEntryId,
},
}),
'Time entry deleted successfully',
'Failed to delete time entry'
);
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
},
});
const { mutateAsync: deleteTimeEntries } = useMutation({
mutationFn: async (timeEntries: TimeEntry[]) => {
const organizationId = getCurrentOrganizationId();
const timeEntryIds = timeEntries.map((entry) => entry.id);
if (organizationId) {
return await handleApiRequestNotifications(
() =>
api.deleteTimeEntries(undefined, {
queries: {
ids: timeEntryIds,
},
params: {
organization: organizationId,
},
}),
'Time entries deleted successfully',
'Failed to delete time entries'
);
}
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['timeEntries'] });
},
});
return {
createTimeEntry,
updateTimeEntry,
updateTimeEntries,
deleteTimeEntry,
deleteTimeEntries,
};
}