162 lines
4.8 KiB
TypeScript
162 lines
4.8 KiB
TypeScript
import { router, publicProcedure } from '@/src/trpc/trpc-index';
|
|
import { z } from 'zod';
|
|
import { db } from '@/src/db/db_index';
|
|
import { storeInfo, productInfo, units } from '@/src/db/schema';
|
|
import { eq, and, sql } from 'drizzle-orm';
|
|
import { scaffoldAssetUrl } from '@/src/lib/s3-client';
|
|
import { ApiError } from '@/src/lib/api-error';
|
|
import { getTagsByStoreId } from '@/src/stores/product-tag-store';
|
|
|
|
export async function scaffoldStores() {
|
|
const storesData = await db
|
|
.select({
|
|
id: storeInfo.id,
|
|
name: storeInfo.name,
|
|
description: storeInfo.description,
|
|
imageUrl: storeInfo.imageUrl,
|
|
productCount: sql<number>`count(${productInfo.id})`.as('productCount'),
|
|
})
|
|
.from(storeInfo)
|
|
.leftJoin(
|
|
productInfo,
|
|
and(eq(productInfo.storeId, storeInfo.id), eq(productInfo.isSuspended, false))
|
|
)
|
|
.groupBy(storeInfo.id);
|
|
|
|
// Generate signed URLs for store images and fetch sample products
|
|
const storesWithDetails = await Promise.all(
|
|
storesData.map(async (store) => {
|
|
const signedImageUrl = store.imageUrl ? scaffoldAssetUrl(store.imageUrl) : null;
|
|
|
|
// Fetch up to 3 products for this store
|
|
const sampleProducts = await db
|
|
.select({
|
|
id: productInfo.id,
|
|
name: productInfo.name,
|
|
images: productInfo.images,
|
|
})
|
|
.from(productInfo)
|
|
.where(and(eq(productInfo.storeId, store.id), eq(productInfo.isSuspended, false)))
|
|
.limit(3);
|
|
|
|
// Generate signed URLs for product images
|
|
const productsWithSignedUrls = await Promise.all(
|
|
sampleProducts.map(async (product) => {
|
|
const images = product.images as string[];
|
|
return {
|
|
id: product.id,
|
|
name: product.name,
|
|
signedImageUrl: (images && images.length > 0) ? scaffoldAssetUrl(images[0]) : null,
|
|
};
|
|
})
|
|
);
|
|
|
|
return {
|
|
id: store.id,
|
|
name: store.name,
|
|
description: store.description,
|
|
signedImageUrl,
|
|
productCount: store.productCount,
|
|
sampleProducts: productsWithSignedUrls,
|
|
};
|
|
})
|
|
);
|
|
|
|
return {
|
|
stores: storesWithDetails,
|
|
};
|
|
}
|
|
|
|
export async function scaffoldStoreWithProducts(storeId: number) {
|
|
// Fetch store info
|
|
const storeData = await db.query.storeInfo.findFirst({
|
|
where: eq(storeInfo.id, storeId),
|
|
columns: {
|
|
id: true,
|
|
name: true,
|
|
description: true,
|
|
imageUrl: true,
|
|
},
|
|
});
|
|
|
|
if (!storeData) {
|
|
throw new ApiError('Store not found', 404);
|
|
}
|
|
|
|
// Generate signed URL for store image
|
|
const signedImageUrl = storeData.imageUrl ? scaffoldAssetUrl(storeData.imageUrl) : null;
|
|
|
|
// Fetch products for this store
|
|
const productsData = await db
|
|
.select({
|
|
id: productInfo.id,
|
|
name: productInfo.name,
|
|
shortDescription: productInfo.shortDescription,
|
|
price: productInfo.price,
|
|
marketPrice: productInfo.marketPrice,
|
|
images: productInfo.images,
|
|
isOutOfStock: productInfo.isOutOfStock,
|
|
incrementStep: productInfo.incrementStep,
|
|
unitShortNotation: units.shortNotation,
|
|
unitNotation: units.shortNotation,
|
|
productQuantity: productInfo.productQuantity,
|
|
})
|
|
.from(productInfo)
|
|
.innerJoin(units, eq(productInfo.unitId, units.id))
|
|
.where(and(eq(productInfo.storeId, storeId), eq(productInfo.isSuspended, false)));
|
|
|
|
|
|
// Generate signed URLs for product images
|
|
const productsWithSignedUrls = await Promise.all(
|
|
productsData.map(async (product) => ({
|
|
id: product.id,
|
|
name: product.name,
|
|
shortDescription: product.shortDescription,
|
|
price: product.price,
|
|
marketPrice: product.marketPrice,
|
|
incrementStep: product.incrementStep,
|
|
unit: product.unitShortNotation,
|
|
unitNotation: product.unitNotation,
|
|
images: scaffoldAssetUrl((product.images as string[]) || []),
|
|
isOutOfStock: product.isOutOfStock,
|
|
productQuantity: product.productQuantity
|
|
}))
|
|
);
|
|
|
|
const tags = await getTagsByStoreId(storeId);
|
|
|
|
return {
|
|
store: {
|
|
id: storeData.id,
|
|
name: storeData.name,
|
|
description: storeData.description,
|
|
signedImageUrl,
|
|
},
|
|
products: productsWithSignedUrls,
|
|
tags: tags.map(tag => ({
|
|
id: tag.id,
|
|
tagName: tag.tagName,
|
|
tagDescription: tag.tagDescription,
|
|
imageUrl: tag.imageUrl,
|
|
productIds: tag.productIds,
|
|
})),
|
|
};
|
|
}
|
|
|
|
export const storesRouter = router({
|
|
getStores: publicProcedure
|
|
.query(async () => {
|
|
const response = await scaffoldStores();
|
|
return response;
|
|
}),
|
|
|
|
getStoreWithProducts: publicProcedure
|
|
.input(z.object({
|
|
storeId: z.number(),
|
|
}))
|
|
.query(async ({ input }) => {
|
|
const { storeId } = input;
|
|
const response = await scaffoldStoreWithProducts(storeId);
|
|
return response;
|
|
}),
|
|
});
|