import { router, protectedProcedure } from '@/src/trpc/trpc-index' import { z } from 'zod'; import { db } from '@/src/db/db_index' import { productAvailabilitySchedules } from '@/src/db/schema' import { eq } from 'drizzle-orm'; import { refreshScheduleJobs } from '@/src/lib/automatedJobs'; const createScheduleSchema = z.object({ scheduleName: z.string().min(1, "Schedule name is required"), time: z.string().min(1, "Time is required").regex(/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/, "Invalid time format. Use HH:MM"), action: z.enum(['in', 'out']), productIds: z.array(z.number().int().positive()).min(1, "At least one product is required"), groupIds: z.array(z.number().int().positive()).default([]), }); const updateScheduleSchema = z.object({ id: z.number().int().positive(), updates: createScheduleSchema.partial().extend({ scheduleName: z.string().min(1).optional(), productIds: z.array(z.number().int().positive()).optional(), groupIds: z.array(z.number().int().positive()).optional(), }), }); export const productAvailabilitySchedulesRouter = router({ create: protectedProcedure .input(createScheduleSchema) .mutation(async ({ input, ctx }) => { const { scheduleName, time, action, productIds, groupIds } = input; // Get staff user ID from auth middleware const staffUserId = ctx.staffUser?.id; if (!staffUserId) { throw new Error("Unauthorized"); } // Check if schedule name already exists const existingSchedule = await db.query.productAvailabilitySchedules.findFirst({ where: eq(productAvailabilitySchedules.scheduleName, scheduleName), }); if (existingSchedule) { throw new Error("Schedule name already exists"); } // Create schedule with arrays const scheduleResult = await db.insert(productAvailabilitySchedules).values({ scheduleName, time, action, productIds, groupIds, }).returning(); // Refresh cron jobs to include new schedule await refreshScheduleJobs(); return scheduleResult[0]; }), getAll: protectedProcedure .query(async () => { const schedules = await db.query.productAvailabilitySchedules.findMany({ orderBy: (productAvailabilitySchedules, { desc }) => [desc(productAvailabilitySchedules.createdAt)], }); return schedules.map(schedule => ({ ...schedule, productCount: schedule.productIds.length, groupCount: schedule.groupIds.length, })); }), getById: protectedProcedure .input(z.object({ id: z.number().int().positive() })) .query(async ({ input }) => { const { id } = input; const schedule = await db.query.productAvailabilitySchedules.findFirst({ where: eq(productAvailabilitySchedules.id, id), }); if (!schedule) { throw new Error("Schedule not found"); } return schedule; }), update: protectedProcedure .input(updateScheduleSchema) .mutation(async ({ input }) => { const { id, updates } = input; // Check if schedule exists const existingSchedule = await db.query.productAvailabilitySchedules.findFirst({ where: eq(productAvailabilitySchedules.id, id), }); if (!existingSchedule) { throw new Error("Schedule not found"); } // Check schedule name uniqueness if being updated if (updates.scheduleName && updates.scheduleName !== existingSchedule.scheduleName) { const duplicateSchedule = await db.query.productAvailabilitySchedules.findFirst({ where: eq(productAvailabilitySchedules.scheduleName, updates.scheduleName), }); if (duplicateSchedule) { throw new Error("Schedule name already exists"); } } // Update schedule const updateData: any = {}; if (updates.scheduleName !== undefined) updateData.scheduleName = updates.scheduleName; if (updates.time !== undefined) updateData.time = updates.time; if (updates.action !== undefined) updateData.action = updates.action; if (updates.productIds !== undefined) updateData.productIds = updates.productIds; if (updates.groupIds !== undefined) updateData.groupIds = updates.groupIds; updateData.lastUpdated = new Date(); const result = await db.update(productAvailabilitySchedules) .set(updateData) .where(eq(productAvailabilitySchedules.id, id)) .returning(); if (result.length === 0) { throw new Error("Failed to update schedule"); } // Refresh cron jobs to reflect changes await refreshScheduleJobs(); return result[0]; }), delete: protectedProcedure .input(z.object({ id: z.number().int().positive() })) .mutation(async ({ input }) => { const { id } = input; const result = await db.delete(productAvailabilitySchedules) .where(eq(productAvailabilitySchedules.id, id)) .returning(); if (result.length === 0) { throw new Error("Schedule not found"); } // Refresh cron jobs to remove deleted schedule await refreshScheduleJobs(); return { message: "Schedule deleted successfully" }; }), });