import { router, publicProcedure } from '@/src/trpc/trpc-index' import { db } from '../../../db/db_index' import { productInfo, units, productSlots, deliverySlotInfo, storeInfo } from '../../../db/schema' import { eq, gt, and, sql, inArray } from 'drizzle-orm'; import { generateSignedUrlsFromS3Urls, generateSignedUrlFromS3Url } from '@/src/lib/s3-client' import { getAllProducts as getAllProductsFromCache } from '@/src/stores/product-store' import { getDashboardTags as getDashboardTagsFromCache } from '@/src/stores/product-tag-store' export const getNextDeliveryDate = async (productId: number): Promise => { const result = await db .select({ deliveryTime: deliverySlotInfo.deliveryTime }) .from(productSlots) .innerJoin(deliverySlotInfo, eq(productSlots.slotId, deliverySlotInfo.id)) .where( and( eq(productSlots.productId, productId), eq(deliverySlotInfo.isActive, true), eq(deliverySlotInfo.isCapacityFull, false), gt(deliverySlotInfo.deliveryTime, new Date()) ) ) .orderBy(deliverySlotInfo.deliveryTime) .limit(1); return result[0]?.deliveryTime || null; }; export async function scaffoldProducts() { // Get all products from cache let products = await getAllProductsFromCache(); products = products.filter(item => Boolean(item.id)) // Get suspended product IDs to filter them out const suspendedProducts = await db .select({ id: productInfo.id }) .from(productInfo) .where(eq(productInfo.isSuspended, true)); const suspendedProductIds = new Set(suspendedProducts.map(sp => sp.id)); // Filter out suspended products products = products.filter(product => !suspendedProductIds.has(product.id)); // Format products to match the expected response structure const formattedProducts = await Promise.all( products.map(async (product) => { const nextDeliveryDate = await getNextDeliveryDate(product.id); return { id: product.id, name: product.name, shortDescription: product.shortDescription, price: parseFloat(product.price), marketPrice: product.marketPrice ? parseFloat(product.marketPrice) : null, unit: product.unitNotation, unitNotation: product.unitNotation, incrementStep: product.incrementStep, productQuantity: product.productQuantity, storeId: product.store?.id || null, isOutOfStock: product.isOutOfStock, isFlashAvailable: product.isFlashAvailable, nextDeliveryDate: nextDeliveryDate ? nextDeliveryDate.toISOString() : null, images: product.images, flashPrice: product.flashPrice }; }) ); return { products: formattedProducts, count: formattedProducts.length, }; } export const commonRouter = router({ getDashboardTags: publicProcedure .query(async () => { // Get dashboard tags from cache const tags = await getDashboardTagsFromCache(); return { tags: tags, }; }), getAllProductsSummary: publicProcedure .query(async () => { const response = await scaffoldProducts(); return response; }), getStoresSummary: publicProcedure .query(async () => { const stores = await db.query.storeInfo.findMany({ columns: { id: true, name: true, description: true, }, }); return { stores, }; }), healthCheck: publicProcedure .query(async () => { // Test DB connection by selecting product names await db.select({ name: productInfo.name }).from(productInfo).limit(1); return { status: "ok", }; }), });