lifetracker/packages/trpc/routers/labels.ts
2024-11-14 23:52:59 -08:00

176 lines
5.1 KiB
TypeScript

import { experimental_trpcMiddleware, TRPCError } from "@trpc/server";
import { and, desc, eq, inArray, notExists } from "drizzle-orm";
import { z } from "zod";
import { SqliteError } from "@lifetracker/db";
import { labels } from "@lifetracker/db/schema";
import {
zLabelSchema,
zGetLabelResponseSchema,
} from "@lifetracker/shared/types/labels";
import type { Context } from "../index";
import { authedProcedure, router } from "../index";
function conditionFromInput(input: { labelId: string }, userId: string) {
return and(eq(labels.id, input.labelId), eq(labels.userId, userId));
}
async function createLabel(
input: z.infer<typeof zLabelSchema>,
ctx: Context,
) {
console.log("Creating a label");
return ctx.db.transaction(async (trx) => {
try {
const result = await trx
.insert(labels)
.values({
name: input.name,
code: input.code,
description: input.description,
color: input.color,
userId: ctx.user!.id,
})
.returning({
id: labels.id,
name: labels.name,
code: labels.code,
description: labels.description,
color: labels.color,
});
return result[0];
} catch (e) {
if (e instanceof SqliteError) {
if (e.code == "SQLITE_CONSTRAINT_UNIQUE") {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Line 48 trpc routers labels.ts",
});
}
}
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Something went wrong",
});
}
});
}
export const labelsAppRouter = router({
labelStats: authedProcedure
.output(
z.record(
z.string(),
z.object({
numEntries: z.number(),
}),
),
)
.query(async ({ ctx }) => {
const [labelIds] = await Promise.all([
ctx.db.select({ id: labels.id }).from(labels)
]);
const results: Record<
string,
{ numEntries: number }
> = {};
for (const label of labelIds) {
results[label.id] = {
numEntries: 3330,
};
}
return results;
}),
list: authedProcedure
.output(
z.object({
labels: z.array(zGetLabelResponseSchema),
}),
)
.query(async ({ ctx }) => {
const res = await ctx.db
.select({
id: labels.id,
name: labels.name,
code: labels.code,
color: labels.color,
description: labels.description,
})
.from(labels)
.where(eq(labels.userId, ctx.user.id));
return {
labels: res.map((r) => ({
id: r.id,
name: r.name,
code: r.code,
color: r.color,
description: r.description,
numEntries: 420,
})),
};
}),
get: authedProcedure
.input(
z.object({
labelId: z.string(),
}),
)
.output(zGetLabelResponseSchema)
.query(async ({ input, ctx }) => {
const res = await ctx.db
.select({
id: labels.id,
name: labels.name
})
.from(labels)
.where(
and(
conditionFromInput(input, ctx.user.id),
eq(labels.userId, ctx.user.id),
),
);
if (res.length == 0) {
throw new TRPCError({ code: "NOT_FOUND" });
}
const numEntriesWithLabel = res.reduce<
Record<ZLabeledByEnum, number>
>(
(acc, curr) => {
if (curr.labeledBy) {
acc[curr.labeledBy]++;
}
return acc;
},
{ ai: 0, human: 0 },
);
return {
id: res[0].id,
name: res[0].name,
numEntries: 420
};
}),
createLabel: authedProcedure
.input(zLabelSchema)
.output(
z.object({
id: z.string(),
code: z.number(),
name: z.string(),
color: z.string().default("#000000"),
description: z.string().optional(),
}),
)
.mutation(async ({ input, ctx }) => {
console.log("Just started creating a label");
return createLabel(input, ctx);
}),
});