mirror of
https://github.com/solidtime-io/solidtime.git
synced 2026-08-15 19:52:15 +01:00
add time overview page
This commit is contained in:
@@ -1,4 +1,19 @@
|
||||
import type { ApiOf } from '@zodios/core';
|
||||
import type { ApiOf, ZodiosResponseByAlias } from '@zodios/core';
|
||||
import { api } from '../../../openapi.json.client';
|
||||
|
||||
export type SolidTimeApi = ApiOf<typeof api>;
|
||||
|
||||
export type TimeEntryResponse = ZodiosResponseByAlias<
|
||||
SolidTimeApi,
|
||||
'getTimeEntries'
|
||||
>;
|
||||
export type TimeEntry = TimeEntryResponse['data'][0];
|
||||
|
||||
export type ProjectResponse = ZodiosResponseByAlias<
|
||||
SolidTimeApi,
|
||||
'getProjects'
|
||||
>;
|
||||
export type Project = ProjectResponse['data'][0];
|
||||
|
||||
export type TagIndexResponse = ZodiosResponseByAlias<SolidTimeApi, 'getTags'>;
|
||||
export type Tag = TagIndexResponse['data'][0];
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
import dayjs from 'dayjs';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import isToday from 'dayjs/plugin/isToday';
|
||||
import isYesterday from 'dayjs/plugin/isYesterday';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(isToday);
|
||||
dayjs.extend(isYesterday);
|
||||
dayjs.extend(duration);
|
||||
dayjs.extend(utc);
|
||||
|
||||
export function formatHumanReadableDuration(duration: number): string {
|
||||
return dayjs.duration(duration, 's').format('HH[h] mm[min]');
|
||||
}
|
||||
|
||||
export function calculateDifference(start: string, end: string | null) {
|
||||
if (end === null) {
|
||||
end = dayjs().utc().format();
|
||||
}
|
||||
return dayjs(end).diff(dayjs(start), 'second');
|
||||
}
|
||||
export function formatTime(date: string) {
|
||||
return dayjs(date).utc().format('HH:mm');
|
||||
}
|
||||
|
||||
export function formatDate(date: string): string {
|
||||
return dayjs(date).utc().format('DD.MM.YYYY');
|
||||
}
|
||||
|
||||
export function formatHumanReadableDate(date: string) {
|
||||
if (dayjs(date).isToday()) {
|
||||
return 'Today';
|
||||
} else if (dayjs(date).isYesterday()) {
|
||||
return 'Yesterday';
|
||||
}
|
||||
return dayjs(date).fromNow();
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { api } from '../../../openapi.json.client';
|
||||
import type { ZodiosResponseByAlias } from '@zodios/core';
|
||||
import type { SolidTimeApi } from '@/utils/api';
|
||||
import type { TimeEntry } from '@/utils/api';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { getCurrentOrganizationId, getCurrentUserId } from '@/utils/useUser';
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import { useTimeEntriesStore } from '@/utils/useTimeEntries';
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
type TimeEntryResponse = ZodiosResponseByAlias<SolidTimeApi, 'getTimeEntries'>;
|
||||
export type TimeEntry = TimeEntryResponse['data'][0];
|
||||
const emptyTimeEntry = {
|
||||
id: '',
|
||||
description: null,
|
||||
@@ -21,11 +20,16 @@ const emptyTimeEntry = {
|
||||
task_id: null,
|
||||
project_id: null,
|
||||
tags: [],
|
||||
billable: false,
|
||||
} as TimeEntry;
|
||||
|
||||
export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
|
||||
const currentTimeEntry = ref<TimeEntry>(reactive(emptyTimeEntry));
|
||||
|
||||
useLocalStorage('solidtime/current-time-entry', currentTimeEntry, {
|
||||
deep: true,
|
||||
});
|
||||
|
||||
function $reset() {
|
||||
currentTimeEntry.value = { ...emptyTimeEntry };
|
||||
}
|
||||
@@ -84,6 +88,9 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
|
||||
user_id: user,
|
||||
start: startTime,
|
||||
description: currentTimeEntry.value?.description,
|
||||
project_id: currentTimeEntry.value?.project_id,
|
||||
billable: currentTimeEntry.value.billable,
|
||||
tags: currentTimeEntry.value?.tags,
|
||||
},
|
||||
{ params: { organization: organization } }
|
||||
);
|
||||
@@ -131,6 +138,7 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
|
||||
user_id: user,
|
||||
project_id: currentTimeEntry.value.project_id,
|
||||
start: currentTimeEntry.value.start,
|
||||
billable: currentTimeEntry.value.billable,
|
||||
end: null,
|
||||
tags: currentTimeEntry.value.tags,
|
||||
},
|
||||
@@ -168,6 +176,7 @@ export const useCurrentTimeEntryStore = defineStore('currentTimeEntry', () => {
|
||||
stopLiveTimer();
|
||||
await stopTimer();
|
||||
}
|
||||
useTimeEntriesStore().fetchTimeEntries();
|
||||
}
|
||||
|
||||
startLiveTimer();
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { api } from '../../../openapi.json.client';
|
||||
import { computed, ref } from 'vue';
|
||||
import type { ZodiosResponseByAlias } from '@zodios/core';
|
||||
import type { SolidTimeApi } from '@/utils/api';
|
||||
|
||||
type ProjectResponse = ZodiosResponseByAlias<SolidTimeApi, 'getProjects'>;
|
||||
export type Project = ProjectResponse['data'][0];
|
||||
import type { Project, ProjectResponse } from '@/utils/api';
|
||||
import { getCurrentOrganizationId } from '@/utils/useUser';
|
||||
|
||||
export const useProjectsStore = defineStore('projects', () => {
|
||||
const projectResponse = ref<ProjectResponse | null>(null);
|
||||
|
||||
async function fetchProjects(organizationId: string) {
|
||||
projectResponse.value = await api.getProjects({
|
||||
params: {
|
||||
organization: organizationId,
|
||||
},
|
||||
});
|
||||
async function fetchProjects() {
|
||||
const organization = getCurrentOrganizationId();
|
||||
if (organization) {
|
||||
projectResponse.value = await api.getProjects({
|
||||
params: {
|
||||
organization: organization,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const projects = computed(() => projectResponse.value?.data || []);
|
||||
const projects = computed<Project[]>(
|
||||
() => projectResponse.value?.data || []
|
||||
);
|
||||
|
||||
return { projects, fetchProjects };
|
||||
});
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref } from 'vue';
|
||||
import type { ZodiosResponseByAlias } from '@zodios/core';
|
||||
import type { SolidTimeApi } from '@/utils/api';
|
||||
import type { Tag } from '@/utils/api';
|
||||
import { getCurrentOrganizationId } from '@/utils/useUser';
|
||||
import { api } from '../../../openapi.json.client';
|
||||
|
||||
type TagIndexResponse = ZodiosResponseByAlias<SolidTimeApi, 'getTags'>;
|
||||
export type Tag = TagIndexResponse['data'][0];
|
||||
|
||||
export const useTagsStore = defineStore('tags', () => {
|
||||
const tags = ref<Tag[]>([]);
|
||||
|
||||
|
||||
69
resources/js/utils/useTimeEntries.ts
Normal file
69
resources/js/utils/useTimeEntries.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { getCurrentOrganizationId } from '@/utils/useUser';
|
||||
import { api } from '../../../openapi.json.client';
|
||||
import { reactive, ref } from 'vue';
|
||||
import type { TimeEntry } from '@/utils/api';
|
||||
|
||||
export const useTimeEntriesStore = defineStore('timeEntries', () => {
|
||||
const timeEntries = ref<TimeEntry[]>(reactive([]));
|
||||
|
||||
async function fetchTimeEntries() {
|
||||
const organizationId = getCurrentOrganizationId();
|
||||
if (organizationId) {
|
||||
const timeEntriesResponse = await api.getTimeEntries({
|
||||
params: {
|
||||
organization: organizationId,
|
||||
},
|
||||
});
|
||||
timeEntries.value = timeEntriesResponse.data;
|
||||
}
|
||||
}
|
||||
|
||||
async function updateTimeEntry(timeEntry: TimeEntry) {
|
||||
const organizationId = getCurrentOrganizationId();
|
||||
if (organizationId) {
|
||||
await api.updateTimeEntry(timeEntry, {
|
||||
params: {
|
||||
organization: organizationId,
|
||||
timeEntry: timeEntry.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function createTimeEntry(timeEntry: TimeEntry) {
|
||||
const organizationId = getCurrentOrganizationId();
|
||||
if (organizationId) {
|
||||
await api.createTimeEntry(timeEntry, {
|
||||
params: {
|
||||
organization: organizationId,
|
||||
},
|
||||
});
|
||||
await fetchTimeEntries();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteTimeEntry(timeEntryId: string) {
|
||||
const organizationId = getCurrentOrganizationId();
|
||||
if (organizationId) {
|
||||
await api.deleteTimeEntry(
|
||||
{},
|
||||
{
|
||||
params: {
|
||||
organization: organizationId,
|
||||
timeEntry: timeEntryId,
|
||||
},
|
||||
}
|
||||
);
|
||||
await fetchTimeEntries();
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
timeEntries,
|
||||
fetchTimeEntries,
|
||||
updateTimeEntry,
|
||||
createTimeEntry,
|
||||
deleteTimeEntry,
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user