mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-05-07 20:32:26 +00:00
222 lines
7.3 KiB
TypeScript
222 lines
7.3 KiB
TypeScript
import { defineStore } from 'pinia';
|
|
import { computed, reactive, ref } from 'vue';
|
|
import { api } from '@/packages/api/src';
|
|
import type { TimeEntry } from '@/packages/api/src';
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
import utc from 'dayjs/plugin/utc';
|
|
import {
|
|
getCurrentMembershipId,
|
|
getCurrentOrganizationId,
|
|
getCurrentUserId,
|
|
} from '@/utils/useUser';
|
|
import { useLocalStorage } from '@vueuse/core';
|
|
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
|
|
import { useNotificationsStore } from '@/utils/notification';
|
|
|
|
dayjs.extend(utc);
|
|
|
|
const emptyTimeEntry = {
|
|
id: '',
|
|
description: '',
|
|
user_id: '',
|
|
start: '',
|
|
end: null,
|
|
duration: null,
|
|
task_id: null,
|
|
project_id: null,
|
|
tags: [],
|
|
billable: false,
|
|
organization_id: '',
|
|
} as TimeEntry;
|
|
|
|
export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
|
|
const currentTimeEntry = ref<TimeEntry>(reactive(emptyTimeEntry));
|
|
const { handleApiRequestNotifications } = useNotificationsStore();
|
|
|
|
useLocalStorage('solidtime/current-time-entry', currentTimeEntry, {
|
|
deep: true,
|
|
});
|
|
|
|
function $reset() {
|
|
currentTimeEntry.value = { ...emptyTimeEntry };
|
|
}
|
|
|
|
const now = ref<null | Dayjs>(null);
|
|
const interval = ref<ReturnType<typeof setInterval> | null>(null);
|
|
|
|
function startLiveTimer() {
|
|
stopLiveTimer();
|
|
now.value = dayjs().utc();
|
|
interval.value = setInterval(() => {
|
|
now.value = dayjs().utc();
|
|
}, 1000);
|
|
}
|
|
|
|
function stopLiveTimer() {
|
|
if (interval.value !== null) {
|
|
clearInterval(interval.value);
|
|
}
|
|
}
|
|
|
|
async function fetchCurrentTimeEntry() {
|
|
const organizationId = getCurrentOrganizationId();
|
|
if (organizationId) {
|
|
try {
|
|
const timeEntriesResponse = await api.getMyActiveTimeEntry({});
|
|
if (timeEntriesResponse?.data) {
|
|
if (timeEntriesResponse.data) {
|
|
currentTimeEntry.value = timeEntriesResponse.data;
|
|
if (
|
|
currentTimeEntry.value.start !== '' &&
|
|
currentTimeEntry.value.end === null
|
|
) {
|
|
startLiveTimer();
|
|
}
|
|
} else {
|
|
currentTimeEntry.value = { ...emptyTimeEntry };
|
|
}
|
|
}
|
|
} catch{
|
|
currentTimeEntry.value = { ...emptyTimeEntry };
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
'Failed to fetch current time entry because organization ID is missing.'
|
|
);
|
|
}
|
|
}
|
|
|
|
async function startTimer() {
|
|
const organization = getCurrentOrganizationId();
|
|
const membership = getCurrentMembershipId();
|
|
if (organization && membership) {
|
|
const startTime =
|
|
currentTimeEntry.value.start !== ''
|
|
? currentTimeEntry.value.start
|
|
: dayjs().utc().format();
|
|
const response = await handleApiRequestNotifications(
|
|
() =>
|
|
api.createTimeEntry(
|
|
{
|
|
member_id: membership,
|
|
start: startTime,
|
|
description: currentTimeEntry.value?.description,
|
|
project_id: currentTimeEntry.value?.project_id,
|
|
task_id: currentTimeEntry.value?.task_id,
|
|
billable: currentTimeEntry.value.billable,
|
|
tags: currentTimeEntry.value?.tags,
|
|
},
|
|
{ params: { organization: organization } }
|
|
),
|
|
'Timer started!'
|
|
);
|
|
if (response?.data) {
|
|
currentTimeEntry.value = response.data;
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
'Failed to fetch current time entry because organization ID is missing.'
|
|
);
|
|
}
|
|
}
|
|
|
|
async function stopTimer() {
|
|
const user = getCurrentUserId();
|
|
const organization = getCurrentOrganizationId();
|
|
if (organization) {
|
|
const currentDateTime = dayjs().utc().format();
|
|
await handleApiRequestNotifications(
|
|
() =>
|
|
api.updateTimeEntry(
|
|
{
|
|
user_id: user,
|
|
start: currentTimeEntry.value.start,
|
|
end: currentDateTime,
|
|
},
|
|
{
|
|
params: {
|
|
organization: organization,
|
|
timeEntry: currentTimeEntry.value.id,
|
|
},
|
|
}
|
|
),
|
|
'Timer stopped!'
|
|
);
|
|
$reset();
|
|
} else {
|
|
throw new Error(
|
|
'Failed to stop current timer because organization ID is missing.'
|
|
);
|
|
}
|
|
}
|
|
|
|
async function updateTimer() {
|
|
const user = getCurrentUserId();
|
|
const organization = getCurrentOrganizationId();
|
|
if (organization) {
|
|
const response = await handleApiRequestNotifications(
|
|
() =>
|
|
api.updateTimeEntry(
|
|
{
|
|
description: currentTimeEntry.value.description,
|
|
user_id: user,
|
|
project_id: currentTimeEntry.value.project_id,
|
|
task_id: currentTimeEntry.value.task_id,
|
|
start: currentTimeEntry.value.start,
|
|
billable: currentTimeEntry.value.billable,
|
|
end: null,
|
|
tags: currentTimeEntry.value.tags,
|
|
},
|
|
{
|
|
params: {
|
|
organization: organization,
|
|
timeEntry: currentTimeEntry.value.id,
|
|
},
|
|
}
|
|
),
|
|
'Time entry updated!'
|
|
);
|
|
if (response?.data) {
|
|
currentTimeEntry.value = response.data;
|
|
}
|
|
} else {
|
|
throw new Error(
|
|
'Failed to fetch current time entry because organization ID is missing.'
|
|
);
|
|
}
|
|
}
|
|
|
|
const isActive = computed(() => {
|
|
if (currentTimeEntry.value) {
|
|
return (
|
|
currentTimeEntry.value.start !== '' &&
|
|
currentTimeEntry.value.start !== null &&
|
|
currentTimeEntry.value.end === null
|
|
);
|
|
}
|
|
return false;
|
|
});
|
|
|
|
async function setActiveState(newState: boolean) {
|
|
if (newState) {
|
|
startLiveTimer();
|
|
await startTimer();
|
|
} else {
|
|
stopLiveTimer();
|
|
await stopTimer();
|
|
}
|
|
useTimeEntriesStore().fetchTimeEntries();
|
|
}
|
|
|
|
return {
|
|
currentTimeEntry,
|
|
fetchCurrentTimeEntry,
|
|
updateTimer,
|
|
isActive,
|
|
startLiveTimer,
|
|
stopLiveTimer,
|
|
now,
|
|
setActiveState,
|
|
};
|
|
});
|