feat: US-008 — Checkpoint and Note tRPC procedures (CRUD)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Roberto Musso
2026-02-19 17:05:06 +01:00
parent 3d6459850c
commit 939d503f3a

View File

@@ -3,7 +3,7 @@ import { z } from 'zod';
import { eq, asc, inArray, and, or, like, sql } from 'drizzle-orm'; import { eq, asc, inArray, and, or, like, sql } from 'drizzle-orm';
import { alias } from 'drizzle-orm/sqlite-core'; import { alias } from 'drizzle-orm/sqlite-core';
import { getDb } from '../db'; import { getDb } from '../db';
import { clients, projects, tasks } from '../db/schema'; import { clients, projects, tasks, checkpoints, notes } from '../db/schema';
import { getStore } from '../store'; import { getStore } from '../store';
const t = initTRPC.create(); const t = initTRPC.create();
@@ -295,7 +295,11 @@ const tasksRouter = router({
const checkpointsRouter = router({ const checkpointsRouter = router({
list: publicProcedure list: publicProcedure
.input(z.object({ projectId: z.string().optional() }).optional()) .input(z.object({ projectId: z.string().optional() }).optional())
.query(() => []), .query(({ input }) => {
const where = input?.projectId !== undefined ? eq(checkpoints.projectId, input.projectId) : undefined;
return getDb().select().from(checkpoints).where(where).orderBy(asc(checkpoints.date)).all();
}),
create: publicProcedure create: publicProcedure
.input(z.object({ .input(z.object({
projectId: z.string(), projectId: z.string(),
@@ -304,7 +308,21 @@ const checkpointsRouter = router({
isAiSuggested: z.number().optional(), isAiSuggested: z.number().optional(),
isApproved: z.number().optional(), isApproved: z.number().optional(),
})) }))
.mutation(() => null), .mutation(({ input }) => {
const id = crypto.randomUUID();
const now = Date.now();
getDb().insert(checkpoints).values({
id,
projectId: input.projectId,
title: input.title,
date: input.date,
isAiSuggested: input.isAiSuggested ?? 0,
isApproved: input.isApproved ?? 0,
createdAt: now,
}).run();
return { id };
}),
update: publicProcedure update: publicProcedure
.input(z.object({ .input(z.object({
id: z.string(), id: z.string(),
@@ -312,28 +330,79 @@ const checkpointsRouter = router({
date: z.number().optional(), date: z.number().optional(),
isApproved: z.number().optional(), isApproved: z.number().optional(),
})) }))
.mutation(() => null), .mutation(({ input }) => {
const set: Partial<{ title: string; date: number; isApproved: number }> = {};
if (input.title !== undefined) set.title = input.title;
if (input.date !== undefined) set.date = input.date;
if (input.isApproved !== undefined) set.isApproved = input.isApproved;
if (Object.keys(set).length > 0) {
getDb().update(checkpoints).set(set).where(eq(checkpoints.id, input.id)).run();
}
return null;
}),
delete: publicProcedure delete: publicProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
.mutation(() => null), .mutation(({ input }) => {
getDb().delete(checkpoints).where(eq(checkpoints.id, input.id)).run();
return { success: true as const };
}),
}); });
const notesRouter = router({ const notesRouter = router({
list: publicProcedure list: publicProcedure
.input(z.object({ projectId: z.string().optional() }).optional()) .input(z.object({ projectId: z.string().optional() }).optional())
.query(() => []), .query(({ input }) => {
const where = input?.projectId !== undefined ? eq(notes.projectId, input.projectId) : undefined;
return getDb()
.select({ id: notes.id, projectId: notes.projectId, title: notes.title, createdAt: notes.createdAt, updatedAt: notes.updatedAt })
.from(notes)
.where(where)
.orderBy(asc(notes.createdAt))
.all();
}),
get: publicProcedure get: publicProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
.query(() => null), .query(({ input }) => {
const result = getDb().select().from(notes).where(eq(notes.id, input.id)).all();
return result[0] ?? null;
}),
create: publicProcedure create: publicProcedure
.input(z.object({ title: z.string(), content: z.string(), projectId: z.string().optional() })) .input(z.object({ title: z.string(), content: z.string(), projectId: z.string().optional() }))
.mutation(() => null), .mutation(({ input }) => {
const id = crypto.randomUUID();
const now = Date.now();
getDb().insert(notes).values({
id,
title: input.title,
content: input.content,
projectId: input.projectId ?? null,
createdAt: now,
updatedAt: now,
}).run();
return { id };
}),
update: publicProcedure update: publicProcedure
.input(z.object({ id: z.string(), title: z.string().optional(), content: z.string().optional() })) .input(z.object({ id: z.string(), title: z.string().optional(), content: z.string().optional() }))
.mutation(() => null), .mutation(({ input }) => {
const set: Partial<{ title: string; content: string; updatedAt: number }> = {};
if (input.title !== undefined) set.title = input.title;
if (input.content !== undefined) set.content = input.content;
// Always update updatedAt
set.updatedAt = Date.now();
getDb().update(notes).set(set).where(eq(notes.id, input.id)).run();
return null;
}),
delete: publicProcedure delete: publicProcedure
.input(z.object({ id: z.string() })) .input(z.object({ id: z.string() }))
.mutation(() => null), .mutation(({ input }) => {
getDb().delete(notes).where(eq(notes.id, input.id)).run();
return { success: true as const };
}),
}); });
const settingsRouter = router({ const settingsRouter = router({