import { db } from "@/lib/db"; import { nextDueDate } from "@/lib/tasks/schedule"; import { ApiError, type AuthContext } from "@/lib/api/authenticate"; import type { TaskCreateInput, TaskCompleteInput } from "@/lib/api/schemas"; export function listTasks(_ctx: AuthContext) { return db.task.findMany({ where: { active: true }, orderBy: { nextDue: "asc" }, include: { plant: { select: { id: true, commonName: true } }, item: { select: { id: true, name: true } }, animal: { select: { id: true, name: true } }, location: { select: { id: true, name: true } }, }, }); } export async function createTask(ctx: AuthContext, input: TaskCreateInput) { const task = await db.task.create({ data: { title: input.title.trim(), notes: input.notes?.trim() || null, category: input.category, plantId: input.plantId || null, itemId: input.itemId || null, animalId: input.animalId || null, locationId: input.locationId || null, recurrence: input.recurrence, intervalDays: input.intervalDays ?? null, month: input.month ?? null, nextDue: new Date(input.nextDue), }, }); await db.auditEvent.create({ data: { action: "task.create", entityId: task.id, actorId: ctx.userId, payload: { title: task.title, recurrence: task.recurrence, via: ctx.via }, }, }); return task; } export async function completeTask(ctx: AuthContext, id: string, input: TaskCompleteInput) { const task = await db.task.findUnique({ where: { id } }); if (!task || !task.active) throw new ApiError(404, "Task not found"); const now = new Date(); const next = nextDueDate(task.recurrence, now, task.intervalDays, task.month); const [completion] = await db.$transaction([ db.taskCompletion.create({ data: { taskId: task.id, doneAt: now, notes: input.notes?.trim() || null, actorId: ctx.userId }, }), db.task.update({ where: { id: task.id }, data: { lastDone: now, nextDue: next ?? task.nextDue, active: task.recurrence === "ONCE" ? false : true, }, }), ]); await db.auditEvent.create({ data: { action: "task.complete", entityId: task.id, actorId: ctx.userId, payload: { title: task.title, nextDue: next, via: ctx.via }, }, }); return { completion, nextDue: next }; }