import { db } from '../db/db_index' import { deliverySlotInfo, productSlots, productInfo, vendorSnippets, productGroupInfo, } from '../db/schema' import { and, asc, desc, eq, gt, inArray } from 'drizzle-orm' import type { AdminDeliverySlot, AdminSlotWithProducts, AdminSlotWithProductsAndSnippetsBase, AdminSlotCreateResult, AdminSlotUpdateResult, AdminVendorSnippet, AdminSlotProductSummary, AdminUpdateSlotCapacityResult, } from '@packages/shared' type SlotSnippetInput = { name: string productIds: number[] validTill?: string } const getStringArray = (value: unknown): string[] | null => { if (!Array.isArray(value)) return null return value.map((item) => String(item)) } const getNumberArray = (value: unknown): number[] => { if (!Array.isArray(value)) return [] return value.map((item) => Number(item)) } const mapDeliverySlot = (slot: typeof deliverySlotInfo.$inferSelect): AdminDeliverySlot => ({ id: slot.id, deliveryTime: slot.deliveryTime, freezeTime: slot.freezeTime, isActive: slot.isActive, isFlash: slot.isFlash, isCapacityFull: slot.isCapacityFull, deliverySequence: slot.deliverySequence, groupIds: slot.groupIds, }) const mapSlotProductSummary = (product: { id: number; name: string; images: unknown }): AdminSlotProductSummary => ({ id: product.id, name: product.name, images: getStringArray(product.images), }) const mapVendorSnippet = (snippet: typeof vendorSnippets.$inferSelect): AdminVendorSnippet => ({ id: snippet.id, snippetCode: snippet.snippetCode, slotId: snippet.slotId ?? null, productIds: snippet.productIds, isPermanent: snippet.isPermanent, validTill: snippet.validTill ?? null, createdAt: snippet.createdAt, }) export async function getActiveSlotsWithProducts(): Promise { const slots = await db.query.deliverySlotInfo .findMany({ where: eq(deliverySlotInfo.isActive, true), orderBy: desc(deliverySlotInfo.deliveryTime), with: { productSlots: { with: { product: { columns: { id: true, name: true, images: true, }, }, }, }, }, }) return slots.map((slot) => ({ ...mapDeliverySlot(slot), deliverySequence: getNumberArray(slot.deliverySequence), products: slot.productSlots.map((ps) => mapSlotProductSummary(ps.product)), })) } export async function getActiveSlots(): Promise { const slots = await db.query.deliverySlotInfo.findMany({ where: eq(deliverySlotInfo.isActive, true), }) return slots.map(mapDeliverySlot) } export async function getSlotsAfterDate(afterDate: Date): Promise { const slots = await db.query.deliverySlotInfo.findMany({ where: and( eq(deliverySlotInfo.isActive, true), gt(deliverySlotInfo.deliveryTime, afterDate) ), orderBy: asc(deliverySlotInfo.deliveryTime), }) return slots.map(mapDeliverySlot) } export async function getSlotByIdWithRelations(id: number): Promise { const slot = await db.query.deliverySlotInfo.findFirst({ where: eq(deliverySlotInfo.id, id), with: { productSlots: { with: { product: { columns: { id: true, name: true, images: true, }, }, }, }, vendorSnippets: true, }, }) if (!slot) { return null } return { ...mapDeliverySlot(slot), deliverySequence: getNumberArray(slot.deliverySequence), groupIds: getNumberArray(slot.groupIds), products: slot.productSlots.map((ps) => mapSlotProductSummary(ps.product)), vendorSnippets: slot.vendorSnippets.map(mapVendorSnippet), } } export async function createSlotWithRelations(input: { deliveryTime: string freezeTime: string isActive?: boolean productIds?: number[] vendorSnippets?: SlotSnippetInput[] groupIds?: number[] }): Promise { const { deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets, groupIds } = input const result = await db.transaction(async (tx) => { const [newSlot] = await tx .insert(deliverySlotInfo) .values({ deliveryTime: new Date(deliveryTime), freezeTime: new Date(freezeTime), isActive: isActive !== undefined ? isActive : true, groupIds: groupIds !== undefined ? groupIds : [], }) .returning() if (productIds && productIds.length > 0) { const associations = productIds.map((productId) => ({ productId, slotId: newSlot.id, })) await tx.insert(productSlots).values(associations) } let createdSnippets: AdminVendorSnippet[] = [] if (snippets && snippets.length > 0) { for (const snippet of snippets) { const products = await tx.query.productInfo.findMany({ where: inArray(productInfo.id, snippet.productIds), }) if (products.length !== snippet.productIds.length) { throw new Error(`One or more invalid product IDs in snippet "${snippet.name}"`) } const existingSnippet = await tx.query.vendorSnippets.findFirst({ where: eq(vendorSnippets.snippetCode, snippet.name), }) if (existingSnippet) { throw new Error(`Snippet name "${snippet.name}" already exists`) } const [createdSnippet] = await tx.insert(vendorSnippets).values({ snippetCode: snippet.name, slotId: newSlot.id, productIds: snippet.productIds, validTill: snippet.validTill ? new Date(snippet.validTill) : undefined, }).returning() createdSnippets.push(mapVendorSnippet(createdSnippet)) } } return { slot: mapDeliverySlot(newSlot), createdSnippets, message: 'Slot created successfully', } }) return result } export async function updateSlotWithRelations(input: { id: number deliveryTime: string freezeTime: string isActive?: boolean productIds?: number[] vendorSnippets?: SlotSnippetInput[] groupIds?: number[] }): Promise { const { id, deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets, groupIds } = input let validGroupIds = groupIds if (groupIds && groupIds.length > 0) { const existingGroups = await db.query.productGroupInfo.findMany({ where: inArray(productGroupInfo.id, groupIds), columns: { id: true }, }) validGroupIds = existingGroups.map((group) => group.id) } const result = await db.transaction(async (tx) => { const [updatedSlot] = await tx .update(deliverySlotInfo) .set({ deliveryTime: new Date(deliveryTime), freezeTime: new Date(freezeTime), isActive: isActive !== undefined ? isActive : true, groupIds: validGroupIds !== undefined ? validGroupIds : [], }) .where(eq(deliverySlotInfo.id, id)) .returning() if (!updatedSlot) { return null } if (productIds !== undefined) { await tx.delete(productSlots).where(eq(productSlots.slotId, id)) if (productIds.length > 0) { const associations = productIds.map((productId) => ({ productId, slotId: id, })) await tx.insert(productSlots).values(associations) } } let createdSnippets: AdminVendorSnippet[] = [] if (snippets && snippets.length > 0) { for (const snippet of snippets) { const products = await tx.query.productInfo.findMany({ where: inArray(productInfo.id, snippet.productIds), }) if (products.length !== snippet.productIds.length) { throw new Error(`One or more invalid product IDs in snippet "${snippet.name}"`) } const existingSnippet = await tx.query.vendorSnippets.findFirst({ where: eq(vendorSnippets.snippetCode, snippet.name), }) if (existingSnippet) { throw new Error(`Snippet name "${snippet.name}" already exists`) } const [createdSnippet] = await tx.insert(vendorSnippets).values({ snippetCode: snippet.name, slotId: id, productIds: snippet.productIds, validTill: snippet.validTill ? new Date(snippet.validTill) : undefined, }).returning() createdSnippets.push(mapVendorSnippet(createdSnippet)) } } return { slot: mapDeliverySlot(updatedSlot), createdSnippets, message: 'Slot updated successfully', } }) return result } export async function deleteSlotById(id: number): Promise { const [deletedSlot] = await db .update(deliverySlotInfo) .set({ isActive: false }) .where(eq(deliverySlotInfo.id, id)) .returning() if (!deletedSlot) { return null } return mapDeliverySlot(deletedSlot) } export async function getSlotDeliverySequence(slotId: number): Promise { const slot = await db.query.deliverySlotInfo.findFirst({ where: eq(deliverySlotInfo.id, slotId), }) if (!slot) { return null } return mapDeliverySlot(slot) } export async function updateSlotDeliverySequence(slotId: number, sequence: unknown) { const [updatedSlot] = await db .update(deliverySlotInfo) .set({ deliverySequence: sequence }) .where(eq(deliverySlotInfo.id, slotId)) .returning({ id: deliverySlotInfo.id, deliverySequence: deliverySlotInfo.deliverySequence, }) return updatedSlot || null } export async function updateSlotCapacity(slotId: number, isCapacityFull: boolean): Promise { const [updatedSlot] = await db .update(deliverySlotInfo) .set({ isCapacityFull }) .where(eq(deliverySlotInfo.id, slotId)) .returning() if (!updatedSlot) { return null } return { success: true, slot: mapDeliverySlot(updatedSlot), message: `Slot ${isCapacityFull ? 'marked as full capacity' : 'capacity reset'}`, } }