freshyo/packages/db_helper_postgres/src/admin-apis/slots.ts
2026-03-25 19:30:01 +05:30

351 lines
9.9 KiB
TypeScript

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<AdminSlotWithProducts[]> {
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<AdminDeliverySlot[]> {
const slots = await db.query.deliverySlotInfo.findMany({
where: eq(deliverySlotInfo.isActive, true),
})
return slots.map(mapDeliverySlot)
}
export async function getSlotsAfterDate(afterDate: Date): Promise<AdminDeliverySlot[]> {
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<AdminSlotWithProductsAndSnippetsBase | null> {
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<AdminSlotCreateResult> {
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<AdminSlotUpdateResult | null> {
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<AdminDeliverySlot | null> {
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<AdminDeliverySlot | null> {
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<AdminUpdateSlotCapacityResult | null> {
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'}`,
}
}