143 lines
4.7 KiB
JavaScript
143 lines
4.7 KiB
JavaScript
import { z } from 'zod';
|
|
import { prisma } from '../index.js';
|
|
function parseTimeToMinutes(timeStr) {
|
|
const str = timeStr.trim();
|
|
// Format: "8,5" or "8.5" (decimal hours)
|
|
if (/^\d+[,\.]\d+$/.test(str)) {
|
|
const hours = parseFloat(str.replace(',', '.'));
|
|
return Math.round(hours * 60);
|
|
}
|
|
// Format: "8:30" (hours:minutes)
|
|
if (/^\d+:\d{1,2}$/.test(str)) {
|
|
const [hours, minutes] = str.split(':').map(Number);
|
|
return hours * 60 + minutes;
|
|
}
|
|
// Format: "8" (just hours)
|
|
if (/^\d+$/.test(str)) {
|
|
return parseInt(str, 10) * 60;
|
|
}
|
|
throw new Error('Invalid time format');
|
|
}
|
|
const createLogSchema = z.object({
|
|
date: z.string(),
|
|
time: z.string(),
|
|
description: z.string().min(1).max(1000),
|
|
});
|
|
const updateLogSchema = z.object({
|
|
date: z.string().optional(),
|
|
time: z.string().optional(),
|
|
description: z.string().min(1).max(1000).optional(),
|
|
});
|
|
const querySchema = z.object({
|
|
startDate: z.string().optional(),
|
|
endDate: z.string().optional(),
|
|
});
|
|
export async function logsRoutes(fastify) {
|
|
fastify.addHook('onRequest', fastify.authenticate);
|
|
// Get logs (default: current month)
|
|
fastify.get('/', async (request) => {
|
|
const user = request.user;
|
|
const query = querySchema.parse(request.query);
|
|
const now = new Date();
|
|
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
|
|
const startDate = query.startDate ? new Date(query.startDate) : startOfMonth;
|
|
const endDate = query.endDate ? new Date(query.endDate + 'T23:59:59') : endOfMonth;
|
|
const logs = await prisma.timeLog.findMany({
|
|
where: {
|
|
userId: user.id,
|
|
date: {
|
|
gte: startDate,
|
|
lte: endDate,
|
|
},
|
|
},
|
|
orderBy: { date: 'desc' },
|
|
});
|
|
return logs.map(log => ({
|
|
...log,
|
|
hours: Math.floor(log.minutes / 60),
|
|
mins: log.minutes % 60,
|
|
}));
|
|
});
|
|
// Create log
|
|
fastify.post('/', async (request, reply) => {
|
|
const user = request.user;
|
|
const result = createLogSchema.safeParse(request.body);
|
|
if (!result.success) {
|
|
return reply.status(400).send({ error: 'Invalid input', details: result.error.errors });
|
|
}
|
|
const { date, time, description } = result.data;
|
|
let minutes;
|
|
try {
|
|
minutes = parseTimeToMinutes(time);
|
|
}
|
|
catch {
|
|
return reply.status(400).send({ error: 'Invalid time format' });
|
|
}
|
|
const log = await prisma.timeLog.create({
|
|
data: {
|
|
date: new Date(date),
|
|
minutes,
|
|
description,
|
|
userId: user.id,
|
|
},
|
|
});
|
|
return {
|
|
...log,
|
|
hours: Math.floor(log.minutes / 60),
|
|
mins: log.minutes % 60,
|
|
};
|
|
});
|
|
// Update log
|
|
fastify.put('/:id', async (request, reply) => {
|
|
const user = request.user;
|
|
const { id } = request.params;
|
|
const result = updateLogSchema.safeParse(request.body);
|
|
if (!result.success) {
|
|
return reply.status(400).send({ error: 'Invalid input', details: result.error.errors });
|
|
}
|
|
const existing = await prisma.timeLog.findFirst({
|
|
where: { id, userId: user.id },
|
|
});
|
|
if (!existing) {
|
|
return reply.status(404).send({ error: 'Log not found' });
|
|
}
|
|
const { date, time, description } = result.data;
|
|
let minutes;
|
|
if (time) {
|
|
try {
|
|
minutes = parseTimeToMinutes(time);
|
|
}
|
|
catch {
|
|
return reply.status(400).send({ error: 'Invalid time format' });
|
|
}
|
|
}
|
|
const log = await prisma.timeLog.update({
|
|
where: { id },
|
|
data: {
|
|
...(date && { date: new Date(date) }),
|
|
...(minutes !== undefined && { minutes }),
|
|
...(description && { description }),
|
|
},
|
|
});
|
|
return {
|
|
...log,
|
|
hours: Math.floor(log.minutes / 60),
|
|
mins: log.minutes % 60,
|
|
};
|
|
});
|
|
// Delete log
|
|
fastify.delete('/:id', async (request, reply) => {
|
|
const user = request.user;
|
|
const { id } = request.params;
|
|
const existing = await prisma.timeLog.findFirst({
|
|
where: { id, userId: user.id },
|
|
});
|
|
if (!existing) {
|
|
return reply.status(404).send({ error: 'Log not found' });
|
|
}
|
|
await prisma.timeLog.delete({ where: { id } });
|
|
return { success: true };
|
|
});
|
|
}
|