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 { categories, days, hours, } from "@lifetracker/db/schema"; import { zDaySchema, ZDay } from "@lifetracker/shared/types/days"; import type { Context } from "../index"; import { authedProcedure, router } from "../index"; import { dateFromInput } from "@lifetracker/shared/utils/days"; import { format } from "date-fns"; import { TZDate } from "@date-fns/tz"; async function createDay(date: string, ctx: Context) { return await ctx.db.transaction(async (trx) => { try { // Create the Day object const dayRes = await trx .insert(days) .values({ userId: ctx.user!.id, date: date, }) .returning({ id: days.id, date: days.date, mood: days.mood, comment: days.comment, }); const dayId = dayRes[0].id; // Generate 24 "hour" objects const hoursData = Array.from({ length: 24 }, (_, hour) => ({ userId: ctx.user!.id, dayId: dayId, time: hour })); // Insert the "hour" objects await trx.insert(hours).values(hoursData); return dayRes; } catch (e) { console.log(e); if (e instanceof SqliteError) { if (e.code == "SQLITE_CONSTRAINT_UNIQUE") { throw new TRPCError({ code: "BAD_REQUEST", message: "This day already exists", }); } } throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Something went wrong", }); } }); } export const daysAppRouter = router({ get: authedProcedure .input(z.object({ dateQuery: z.string(), })) .output(zDaySchema) .query(async ({ input, ctx }) => { const date = dateFromInput({ timezone: ctx.user.timezone, ...input }); console.log(ctx.user.timezone); // Fetch the day data let dayRes; dayRes = await ctx.db .select({ id: days.id, date: days.date, mood: days.mood, comment: days.comment, }) .from(days) .where(eq(days.date, date)); if (dayRes.length === 0) { dayRes = await createDay(date, ctx); } const day = dayRes[0]; // Fetch the hours data for the corresponding dayId const hoursRes = await ctx.db .select({ id: hours.id, dayId: hours.dayId, time: hours.time, categoryId: hours.categoryId, categoryCode: categories.code, categoryName: categories.name, comment: hours.comment, }) .from(hours) .where(eq(hours.dayId, day.id)) .leftJoin(categories, eq(categories.id, hours.categoryId)) // Combine the day and hours data const result = { ...day, hours: hoursRes, }; return result; }), update: authedProcedure .input( z.object({ mood: z.string().optional().or(z.number()), comment: z.string().optional(), dateQuery: z.string(), }), ) .mutation(async ({ input, ctx }) => { const { dateQuery, ...updatedProps } = input; // Convert mood to number, if it exists if (updatedProps.mood) { updatedProps.mood = parseInt(updatedProps.mood); } await ctx.db .update(days) .set(updatedProps) .where(eq(days.date, dateFromInput({ dateQuery: dateQuery, timezone: ctx.user.timezone }))); }), });