work-timer/app/models/timeEntry.server.ts

218 lines
4.1 KiB
TypeScript

import type { User, TimeEntry, Project } from '@prisma/client';
import { prisma } from '~/db.server';
export type { TimeEntry } from '@prisma/client';
export function getTimeEntry({
id,
userId
}: Pick<TimeEntry, 'id'> & {
userId: User['id'];
}) {
return prisma.timeEntry.findFirst({
where: { id, userId },
include: {
project: true
}
});
}
export async function getTimeEntries({
userId,
projectId,
page,
size,
orderBy,
order
}: {
userId: User['id'];
projectId?: Project['id'];
page?: number;
size?: number;
orderBy?: string;
order?: 'asc' | 'desc';
}) {
const totalTimeEntries = await prisma.timeEntry.count({
where: { userId, projectId }
});
const paginatedEntries = await prisma.timeEntry.findMany({
where: { userId, projectId },
include: {
project: true
},
orderBy: {
[orderBy || 'startTime']: order || 'desc'
},
skip: page && size ? (page - 1) * size : 0,
take: size
});
const monthAgo = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
const weekAgo = new Date(
new Date().getFullYear(),
new Date().getMonth(),
new Date().getDate() - 7
);
const monthEntries = await prisma.timeEntry.findMany({
where: {
userId,
projectId,
startTime: { gte: monthAgo },
endTime: { lte: new Date() }
}
});
const monthTotalHours =
monthEntries.reduce(
(acc, entry) =>
acc +
((entry.endTime || new Date(Date.now())).getTime() -
entry.startTime.getTime()),
0
) /
1000 /
60 /
60;
const weekTotalHours =
monthEntries
.filter((e) => e.startTime >= weekAgo)
.reduce(
(acc, entry) =>
acc +
((entry.endTime || new Date(Date.now())).getTime() -
entry.startTime.getTime()),
0
) /
1000 /
60 /
60;
const nextPage =
page && size && totalTimeEntries > page * size ? page + 1 : null;
const previousPage = page && page > 2 ? page - 1 : null;
return {
total: totalTimeEntries,
monthTotalHours,
weekTotalHours,
timeEntries: paginatedEntries,
nextPage,
previousPage
};
}
export function getTimeEntriesByDateAndProject({
userId,
dateFrom,
dateTo
}: {
userId: User['id'];
dateFrom: Date;
dateTo: Date;
}) {
return prisma.timeEntry.groupBy({
by: ['projectId'],
_sum: {
duration: true
},
where: {
userId,
startTime: { gte: dateFrom, lte: dateTo }
}
});
}
export function createTimeEntry({
description,
startTime,
endTime,
duration,
userId,
projectId
}: Pick<TimeEntry, 'description' | 'startTime' | 'endTime' | 'duration'> & {
userId: User['id'];
projectId: Project['id'];
}) {
return prisma.timeEntry.create({
data: {
description,
startTime,
endTime,
duration,
projectId,
userId
}
});
}
export function stopAllTimeEntries(userId: User['id']) {
return prisma.timeEntry.updateMany({
where: { userId, endTime: null },
data: { endTime: new Date() }
});
}
export function updateTimeEntry({
timeEntryId,
description,
startTime,
endTime,
duration,
projectId
}: Partial<
Pick<
TimeEntry,
'description' | 'startTime' | 'endTime' | 'duration' | 'projectId'
>
> & {
timeEntryId: TimeEntry['id'];
}) {
return prisma.timeEntry.update({
data: {
description,
startTime,
endTime,
duration,
projectId
},
where: {
id: timeEntryId
}
});
}
export function deleteTimeEntry({
id,
userId
}: Pick<TimeEntry, 'id'> & { userId: User['id'] }) {
return prisma.timeEntry.deleteMany({
where: { id, userId }
});
}
export async function exportTimeEntries({ userId }: { userId: User['id'] }) {
const entries = await prisma.timeEntry.findMany({
where: {
userId
},
select: {
id: true,
description: true,
startTime: true,
endTime: true,
duration: true,
createdAt: true,
project: {
select: {
name: true
}
}
}
});
return entries.map((entry) => ({
...entry,
project: entry.project?.name
}));
}