This commit is contained in:
shafi54 2026-07-31 21:54:46 +05:30
parent f6bcc23ca2
commit 648a0a5d28
16 changed files with 207 additions and 184 deletions

View file

@ -42,6 +42,7 @@ export default function EditProduct() {
longDescription: productData.longDescription || '', longDescription: productData.longDescription || '',
storeId: productData.storeId || 1, storeId: productData.storeId || 1,
variants: (productData.skus || []).map((sku) => ({ variants: (productData.skus || []).map((sku) => ({
id: sku.id,
name: sku.name || '', name: sku.name || '',
price: sku.price || '', price: sku.price || '',
marketPrice: sku.marketPrice || '', marketPrice: sku.marketPrice || '',
@ -106,6 +107,7 @@ export default function EditProduct() {
const allUrls = [...existingUrls, ...newUrls] const allUrls = [...existingUrls, ...newUrls]
return { return {
id: variant.id,
name: variant.name || null, name: variant.name || null,
price: parseFloat(variant.price), price: parseFloat(variant.price),
marketPrice: variant.marketPrice ? parseFloat(variant.marketPrice) : undefined, marketPrice: variant.marketPrice ? parseFloat(variant.marketPrice) : undefined,

View file

@ -11,6 +11,7 @@ interface Attribute {
} }
interface Variant { interface Variant {
id?: number
name: string name: string
price: string price: string
marketPrice: string marketPrice: string

View file

@ -69,11 +69,11 @@ export {
checkProductExistsByName, checkProductExistsByName,
checkUnitExists, checkUnitExists,
getProductImagesById, getProductImagesById,
createSpecialDealsForProduct, createSpecialDealsForSku,
updateProductDeals, updateSkuDeals,
replaceProductTags, replaceProductTags,
mergeSkus, mergeSkus,
toggleProductOutOfStock, toggleSkuOutOfStock,
updateSlotProducts, updateSlotProducts,
getSlotProductIds, getSlotProductIds,
getSlotsProductIds, getSlotsProductIds,

View file

@ -310,6 +310,7 @@ export const productRouter = router({
storeId: z.number().min(1, 'Store is required'), storeId: z.number().min(1, 'Store is required'),
incrementStep: z.number().optional().default(1), incrementStep: z.number().optional().default(1),
skus: z.array(z.object({ skus: z.array(z.object({
id: z.number().optional(),
name: z.string().optional().nullable(), name: z.string().optional().nullable(),
price: z.number().positive('Price must be positive'), price: z.number().positive('Price must be positive'),
marketPrice: z.number().optional().nullable(), marketPrice: z.number().optional().nullable(),
@ -334,6 +335,7 @@ export const productRouter = router({
const allUploadUrls: string[] = skus.flatMap((sku) => sku.images) const allUploadUrls: string[] = skus.flatMap((sku) => sku.images)
const skuInputs = skus.map((sku) => ({ const skuInputs = skus.map((sku) => ({
id: sku.id,
name: sku.name ?? null, name: sku.name ?? null,
price: sku.price, price: sku.price,
marketPrice: sku.marketPrice ?? null, marketPrice: sku.marketPrice ?? null,

View file

@ -209,7 +209,7 @@ export const slotsRouter = router({
}); });
} }
const result = await updateSlotProductsInDb(String(slotId), productIds.map(String)) const result = await updateSlotProductsInDb(String(slotId), skuIds.map(String))
/* /*
// Old implementation - direct DB queries: // Old implementation - direct DB queries:

View file

@ -403,7 +403,7 @@ export const vendorSnippetsRouter = router({
const filteredOrders = matchingOrders.filter(order => { const filteredOrders = matchingOrders.filter(order => {
const status = order.orderStatus; const status = order.orderStatus;
if (status[0].isCancelled) return false; if (status[0].isCancelled) return false;
const orderProductIds = order.orderItems.map(item => item.productId); const orderProductIds = order.orderItems.map(item => item.skuId);
return snippet.skuIds.some(productId => orderProductIds.includes(productId)); return snippet.skuIds.some(productId => orderProductIds.includes(productId));
}); });
@ -411,17 +411,17 @@ export const vendorSnippetsRouter = router({
const formattedOrders = filteredOrders.map(order => { const formattedOrders = filteredOrders.map(order => {
// Filter orderItems to only include products attached to the snippet // Filter orderItems to only include products attached to the snippet
const attachedOrderItems = order.orderItems.filter(item => const attachedOrderItems = order.orderItems.filter(item =>
snippet.skuIds.includes(item.productId) snippet.skuIds.includes(item.skuId)
); );
const products = attachedOrderItems.map(item => ({ const products = attachedOrderItems.map(item => ({
orderItemId: item.id, orderItemId: item.id,
productId: item.productId, productId: item.skuId,
productName: item.product.name, productName: item.sku?.product?.name || 'Unknown',
quantity: parseFloat(item.quantity), quantity: parseFloat(item.quantity),
productSize: item.product.productQuantity, productSize: 1,
price: parseFloat((item.price ?? 0).toString()), price: parseFloat((item.price ?? 0).toString()),
unit: item.product.unit?.shortNotation || 'unit', unit: (item.sku?.features || []).map((f: any) => f.featureValue).join(' ') || 'unit',
subtotal: parseFloat((item.price ?? 0).toString()) * parseFloat(item.quantity), subtotal: parseFloat((item.price ?? 0).toString()) * parseFloat(item.quantity),
is_packaged: item.is_packaged, is_packaged: item.is_packaged,
is_package_verified: item.is_package_verified, is_package_verified: item.is_package_verified,
@ -488,9 +488,9 @@ export const vendorSnippetsRouter = router({
orderDate: order.createdAt ? order.createdAt.toISOString() : new Date(0).toISOString(), orderDate: order.createdAt ? order.createdAt.toISOString() : new Date(0).toISOString(),
totalQuantity: order.orderItems.reduce((sum, item) => sum + parseFloat(item.quantity || '0'), 0), totalQuantity: order.orderItems.reduce((sum, item) => sum + parseFloat(item.quantity || '0'), 0),
products: order.orderItems.map(item => ({ products: order.orderItems.map(item => ({
name: item.product.name, name: item.sku?.product?.name || 'Unknown',
quantity: parseFloat(item.quantity || '0'), quantity: parseFloat(item.quantity || '0'),
unit: item.product.unit?.shortNotation || 'unit', unit: (item.sku?.features || []).map((f: any) => f.featureValue).join(' ') || 'unit',
})), })),
})) }))
}), }),
@ -588,7 +588,7 @@ export const vendorSnippetsRouter = router({
const filteredOrders = matchingOrders.filter(order => { const filteredOrders = matchingOrders.filter(order => {
const status = order.orderStatus; const status = order.orderStatus;
if (status[0]?.isCancelled) return false; if (status[0]?.isCancelled) return false;
const orderProductIds = order.orderItems.map(item => item.productId); const orderProductIds = order.orderItems.map(item => item.skuId);
return snippet.skuIds.some(productId => orderProductIds.includes(productId)); return snippet.skuIds.some(productId => orderProductIds.includes(productId));
}); });
@ -596,18 +596,18 @@ export const vendorSnippetsRouter = router({
const formattedOrders = filteredOrders.map(order => { const formattedOrders = filteredOrders.map(order => {
// Filter orderItems to only include products attached to the snippet // Filter orderItems to only include products attached to the snippet
const attachedOrderItems = order.orderItems.filter(item => const attachedOrderItems = order.orderItems.filter(item =>
snippet.skuIds.includes(item.productId) snippet.skuIds.includes(item.skuId)
); );
const products = attachedOrderItems.map(item => ({ const products = attachedOrderItems.map(item => ({
orderItemId: item.id, orderItemId: item.id,
productId: item.productId, productId: item.skuId,
productName: item.product.name, productName: item.sku?.product?.name || 'Unknown',
quantity: parseFloat(item.quantity), quantity: parseFloat(item.quantity),
price: parseFloat((item.price ?? 0).toString()), price: parseFloat((item.price ?? 0).toString()),
unit: item.product.unit?.shortNotation || 'unit', unit: (item.sku?.features || []).map((f: any) => f.featureValue).join(' ') || 'unit',
subtotal: parseFloat((item.price ?? 0).toString()) * parseFloat(item.quantity), subtotal: parseFloat((item.price ?? 0).toString()) * parseFloat(item.quantity),
productSize: item.product.productQuantity, productSize: 1,
is_packaged: item.is_packaged, is_packaged: item.is_packaged,
is_package_verified: item.is_package_verified, is_package_verified: item.is_package_verified,
})); }));

View file

@ -461,6 +461,7 @@ export default function CartPage({ isFlashDelivery = false }: CartPageProps) {
</MyText> </MyText>
<MyText style={tw`text-xs text-gray-500 mr-2`}> <MyText style={tw`text-xs text-gray-500 mr-2`}>
{(() => { {(() => {
const unit = product?.unitNotation || '';
return unit; return unit;
})()} })()}
</MyText> </MyText>

View file

@ -67,7 +67,7 @@ export default function AddToCartDialog() {
// Pre-select cart's slotId and quantity if item is already in cart // Pre-select cart's slotId and quantity if item is already in cart
useEffect(() => { useEffect(() => {
if (isOpen && product) { if (isOpen && product) {
const cartItem = cartData?.items?.find((item: any) => item.productId === product.id); const cartItem = cartData?.items?.find((item: any) => item.skuId === product.id);
const cartQuantity = cartItem?.quantity || 0; const cartQuantity = cartItem?.quantity || 0;
// Set quantity: 0 → 1, >1 → keep as is // Set quantity: 0 → 1, >1 → keep as is
@ -109,7 +109,7 @@ export default function AddToCartDialog() {
.filter((slot) => dayjs(slot.freezeTime).isAfter(dayjs())); .filter((slot) => dayjs(slot.freezeTime).isAfter(dayjs()));
// Find cart item for this product // Find cart item for this product
const cartItem = cartData?.items?.find((item: any) => item.productId === product?.id); const cartItem = cartData?.items?.find((item: any) => item.skuId === product?.id);
// Determine if updating existing item (quantity > 1 means it's an update) // Determine if updating existing item (quantity > 1 means it's an update)
const isUpdate = (cartItem?.quantity || 0) >= 1; const isUpdate = (cartItem?.quantity || 0) >= 1;

View file

@ -79,11 +79,11 @@ export {
checkProductExistsByName, checkProductExistsByName,
checkUnitExists, checkUnitExists,
getProductImagesById, getProductImagesById,
createSpecialDealsForProduct, createSpecialDealsForSku,
updateProductDeals, updateSkuDeals,
replaceProductTags, replaceProductTags,
mergeSkus, mergeSkus,
toggleProductOutOfStock, toggleSkuOutOfStock,
updateSlotProducts, updateSlotProducts,
getSlotProductIds, getSlotProductIds,
getSlotsProductIds, getSlotsProductIds,

View file

@ -109,7 +109,7 @@ const mapSku = (sku: SkuRow, features: SkuFeatureRow[] = []): AdminSku => ({
const mapSpecialDeal = (deal: SpecialDealRow): AdminSpecialDeal => ({ const mapSpecialDeal = (deal: SpecialDealRow): AdminSpecialDeal => ({
id: deal.id, id: deal.id,
productId: deal.productId, skuId: deal.skuId,
quantity: String(deal.quantity ?? '0'), quantity: String(deal.quantity ?? '0'),
price: String(deal.price ?? '0'), price: String(deal.price ?? '0'),
validTill: deal.validTill, validTill: deal.validTill,
@ -298,18 +298,37 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
throw new Error('Every SKU must have a quantity feature') throw new Error('Every SKU must have a quantity feature')
} }
const existingSkuIds = await db.query.productSkus.findMany({ const existingSkus = await db.query.productSkus.findMany({
where: eq(productSkus.productId, id), where: eq(productSkus.productId, id),
columns: { id: true }, columns: { id: true },
}).then((skus) => skus.map((sku) => sku.id)) })
const existingSkuIdSet = new Set(existingSkus.map((s) => s.id))
if (existingSkuIds.length > 0) { for (const sku of skus) {
await db.delete(skuFeatures).where(inArray(skuFeatures.skuId, existingSkuIds)) if (sku.id != null && existingSkuIdSet.has(sku.id)) {
await db.delete(productSkus).where(inArray(productSkus.id, existingSkuIds)) // Update existing SKU
} await db.update(productSkus)
.set({
name: sku.name ?? null,
price: String(sku.price),
marketPrice: sku.marketPrice != null ? String(sku.marketPrice) : null,
images: sku.images ?? null,
isFlashAvailable: sku.isFlashAvailable ?? false,
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
})
.where(eq(productSkus.id, sku.id))
const skuRows = await db.insert(productSkus).values( await db.delete(skuFeatures).where(eq(skuFeatures.skuId, sku.id))
skus.map((sku: any) => ({ await db.insert(skuFeatures).values(
sku.features.map((f: any) => ({
skuId: sku.id,
featureName: f.featureName,
featureValue: f.featureValue,
}))
)
} else {
// Insert new SKU
const [newSku] = await db.insert(productSkus).values({
productId: id, productId: id,
name: sku.name ?? null, name: sku.name ?? null,
price: String(sku.price), price: String(sku.price),
@ -317,20 +336,18 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
images: sku.images ?? null, images: sku.images ?? null,
isFlashAvailable: sku.isFlashAvailable ?? false, isFlashAvailable: sku.isFlashAvailable ?? false,
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null, flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
})) }).returning()
).returning()
for (let i = 0; i < skuRows.length; i++) {
const sku = skus[i]
await db.insert(skuFeatures).values( await db.insert(skuFeatures).values(
sku.features.map((f: any) => ({ sku.features.map((f: any) => ({
skuId: skuRows[i].id, skuId: newSku.id,
featureName: f.featureName, featureName: f.featureName,
featureValue: f.featureValue, featureValue: f.featureValue,
})) }))
) )
} }
} }
}
const updatedProduct = await db.query.productInfo.findFirst({ const updatedProduct = await db.query.productInfo.findFirst({
where: eq(productInfo.id, id), where: eq(productInfo.id, id),
@ -353,9 +370,9 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
} }
} }
export async function toggleProductOutOfStock(id: number): Promise<AdminProduct | null> { export async function toggleSkuOutOfStock(id: number): Promise<AdminSku | null> {
const product = await db.query.productInfo.findFirst({ const product = await db.query.productSkus.findFirst({
where: eq(productInfo.id, id), where: eq(productSkus.id, id),
}) })
if (!product) { if (!product) {
@ -363,18 +380,18 @@ export async function toggleProductOutOfStock(id: number): Promise<AdminProduct
} }
const [updatedProduct] = await db const [updatedProduct] = await db
.update(productInfo) .update(productSkus)
.set({ .set({
isOutOfStock: !product.isOutOfStock, isOutOfStock: !product.isOutOfStock,
}) })
.where(eq(productInfo.id, id)) .where(eq(productSkus.id, id))
.returning() .returning()
if (!updatedProduct) { if (!updatedProduct) {
return null return null
} }
return mapProduct(updatedProduct) return mapSku(updatedProduct)
} }
export async function updateSlotProducts(slotId: string, productIds: string[]): Promise<AdminUpdateSlotProductsResult> { export async function updateSlotProducts(slotId: string, productIds: string[]): Promise<AdminUpdateSlotProductsResult> {
@ -386,16 +403,15 @@ export async function updateSlotProducts(slotId: string, productIds: string[]):
throw new Error(`Slot ${slotId} not found`) throw new Error(`Slot ${slotId} not found`)
} }
const currentProductIds = slot.productIds || [] const currentSkuIds = slot.skuIds || []
const newProductIds = productIds.map((id: string) => parseInt(id)) const newSkuIds = productIds.map((id: string) => parseInt(id))
// Simply update the productIds array
await db.update(deliverySlotInfo) await db.update(deliverySlotInfo)
.set({ productIds: newProductIds }) .set({ skuIds: newSkuIds })
.where(eq(deliverySlotInfo.id, parseInt(slotId))) .where(eq(deliverySlotInfo.id, parseInt(slotId)))
const productsToAdd = newProductIds.filter((id: number) => !currentProductIds.includes(id)) const productsToAdd = newSkuIds.filter((id: number) => !currentSkuIds.includes(id))
const productsToRemove = currentProductIds.filter((id: number) => !newProductIds.includes(id)) const productsToRemove = currentSkuIds.filter((id: number) => !newSkuIds.includes(id))
return { return {
message: 'Slot products updated successfully', message: 'Slot products updated successfully',
@ -409,7 +425,7 @@ export async function getSlotProductIds(slotId: string): Promise<number[]> {
where: eq(deliverySlotInfo.id, parseInt(slotId)), where: eq(deliverySlotInfo.id, parseInt(slotId)),
}) })
return slot?.productIds || [] return slot?.skuIds || []
} }
export async function getAllUnits(): Promise<AdminUnit[]> { export async function getAllUnits(): Promise<AdminUnit[]> {
@ -573,7 +589,7 @@ export async function getSlotsProductIds(slotIds: number[]): Promise<Record<numb
const result: Record<number, number[]> = {} const result: Record<number, number[]> = {}
for (const slot of slots) { for (const slot of slots) {
result[slot.id] = slot.productIds || [] result[slot.id] = slot.skuIds || []
} }
slotIds.forEach((slotId) => { slotIds.forEach((slotId) => {
@ -856,8 +872,8 @@ export async function checkUnitExists(unitId: number): Promise<boolean> {
} }
export async function getProductImagesById(productId: number): Promise<string[] | null> { export async function getProductImagesById(productId: number): Promise<string[] | null> {
const product = await db.query.productInfo.findFirst({ const product = await db.query.productSkus.findFirst({
where: eq(productInfo.id, productId), where: eq(productSkus.id, productId),
columns: { images: true }, columns: { images: true },
}) })
@ -874,7 +890,7 @@ export interface CreateSpecialDealInput {
validTill: string | Date validTill: string | Date
} }
export async function createSpecialDealsForProduct( export async function createSpecialDealsForSku(
productId: number, productId: number,
deals: CreateSpecialDealInput[] deals: CreateSpecialDealInput[]
): Promise<AdminSpecialDeal[]> { ): Promise<AdminSpecialDeal[]> {
@ -897,17 +913,17 @@ export async function createSpecialDealsForProduct(
return createdDeals.map(mapSpecialDeal) return createdDeals.map(mapSpecialDeal)
} }
export async function updateProductDeals( export async function updateSkuDeals(
productId: number, productId: number,
deals: CreateSpecialDealInput[] deals: CreateSpecialDealInput[]
): Promise<void> { ): Promise<void> {
if (deals.length === 0) { if (deals.length === 0) {
await db.delete(specialDeals).where(eq(specialDeals.productId, productId)) await db.delete(specialDeals).where(eq(specialDeals.skuId, productId))
return return
} }
const existingDeals = await db.query.specialDeals.findMany({ const existingDeals = await db.query.specialDeals.findMany({
where: eq(specialDeals.productId, productId), where: eq(specialDeals.skuId, productId),
}) })
const existingDealsMap = new Map<string, SpecialDealRow>( const existingDealsMap = new Map<string, SpecialDealRow>(

View file

@ -156,9 +156,10 @@ export async function getVendorOrdersBySlotId(slotId: number) {
with: { with: {
orderItems: { orderItems: {
with: { with: {
product: { sku: {
with: { with: {
unit: true, product: true,
features: true,
}, },
}, },
}, },
@ -177,9 +178,10 @@ export async function getVendorOrders() {
user: true, user: true,
orderItems: { orderItems: {
with: { with: {
product: { sku: {
with: { with: {
unit: true, product: true,
features: true,
}, },
}, },
}, },
@ -193,9 +195,10 @@ export async function getOrderItemsByOrderIds(orderIds: number[]) {
return await db.query.orderItems.findMany({ return await db.query.orderItems.findMany({
where: inArray(orderItems.orderId, orderIds), where: inArray(orderItems.orderId, orderIds),
with: { with: {
product: { sku: {
with: { with: {
unit: true, product: true,
features: true,
}, },
}, },
}, },

View file

@ -5,7 +5,7 @@
import { db } from '@/src/db/db_index' import { db } from '@/src/db/db_index'
import { import {
userDetails, userDetails,
productInfo, productSkus,
productTagInfo, productTagInfo,
complaints, complaints,
} from '@/src/db/schema' } from '@/src/db/schema'
@ -43,21 +43,21 @@ async function migrateUserDetails() {
} }
async function migrateProductInfo() { async function migrateProductInfo() {
console.log('Migrating productInfo...') console.log('Migrating productSkus...')
const products = await db.select().from(productInfo).where(not(isNull(productInfo.images))) const products = await db.select().from(productSkus).where(not(isNull(productSkus.images)))
console.log(`Found ${products.length} product records with images`) console.log(`Found ${products.length} product records with images`)
for (const product of products) { for (const product of products) {
if (product.images && Array.isArray(product.images)) { if (product.images && Array.isArray(product.images)) {
const cleanedUrls = cleanImageUrls(product.images) const cleanedUrls = cleanImageUrls(product.images)
await db.update(productInfo) await db.update(productSkus)
.set({ images: cleanedUrls }) .set({ images: cleanedUrls })
.where(eq(productInfo.id, product.id)) .where(eq(productSkus.id, product.id))
} }
} }
console.log('productInfo migration completed') console.log('productSkus migration completed')
} }
async function migrateProductTagInfo() { async function migrateProductTagInfo() {

View file

@ -1,5 +1,5 @@
import { db } from '../db/db_index' import { db } from '../db/db_index'
import { cartItems, productInfo, units } from '../db/schema' import { cartItems, productSkus } from '../db/schema'
import { and, eq, sql } from 'drizzle-orm' import { and, eq, sql } from 'drizzle-orm'
import type { UserCartItem } from '@packages/shared' import type { UserCartItem } from '@packages/shared'
@ -9,55 +9,51 @@ const getStringArray = (value: unknown): string[] => {
} }
export async function getCartItemsWithProducts(userId: number): Promise<UserCartItem[]> { export async function getCartItemsWithProducts(userId: number): Promise<UserCartItem[]> {
const cartItemsWithProducts = await db const cartItemsWithProducts = await db.query.cartItems.findMany({
.select({ where: eq(cartItems.userId, userId),
cartId: cartItems.id, with: {
productId: productInfo.id, sku: {
productName: productInfo.name, with: {
productPrice: productInfo.price, product: true,
productImages: productInfo.images, features: true,
productQuantity: productInfo.productQuantity, },
isOutOfStock: productInfo.isOutOfStock, },
unitShortNotation: units.shortNotation, },
quantity: cartItems.quantity,
addedAt: cartItems.addedAt,
}) })
.from(cartItems)
.innerJoin(productInfo, eq(cartItems.productId, productInfo.id))
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(eq(cartItems.userId, userId))
return cartItemsWithProducts.map((item) => { return cartItemsWithProducts.map((item) => {
const priceValue = item.productPrice ?? '0' const sku = item.sku
const features = sku?.features || []
const priceValue = sku?.price ?? '0'
const quantityValue = item.quantity ?? '0' const quantityValue = item.quantity ?? '0'
return { return {
id: item.cartId, id: item.id,
productId: item.productId, skuId: item.skuId,
quantity: parseFloat(quantityValue), quantity: parseFloat(quantityValue),
addedAt: item.addedAt, addedAt: item.addedAt,
product: { product: {
id: item.productId, id: sku?.id ?? 0,
name: item.productName, name: sku?.product?.name ?? 'Unknown',
price: priceValue.toString(), price: String(priceValue),
productQuantity: item.productQuantity, productQuantity: 1,
unit: item.unitShortNotation, unit: features.map((f) => f.featureValue).join(' '),
isOutOfStock: item.isOutOfStock, isOutOfStock: sku?.isOutOfStock ?? false,
images: getStringArray(item.productImages), images: getStringArray(sku?.images),
}, },
subtotal: parseFloat(priceValue.toString()) * parseFloat(quantityValue), subtotal: parseFloat(String(priceValue)) * parseFloat(quantityValue),
} }
}) })
} }
export async function getProductById(productId: number) { export async function getProductById(skuId: number) {
return db.query.productInfo.findFirst({ return db.query.productSkus.findFirst({
where: eq(productInfo.id, productId), where: eq(productSkus.id, skuId),
}) })
} }
export async function getCartItemByUserProduct(userId: number, productId: number) { export async function getCartItemByUserProduct(userId: number, skuId: number) {
return db.query.cartItems.findFirst({ return db.query.cartItems.findFirst({
where: and(eq(cartItems.userId, userId), eq(cartItems.productId, productId)), where: and(eq(cartItems.userId, userId), eq(cartItems.skuId, skuId)),
}) })
} }
@ -69,10 +65,10 @@ export async function incrementCartItemQuantity(itemId: number, quantity: number
.where(eq(cartItems.id, itemId)) .where(eq(cartItems.id, itemId))
} }
export async function insertCartItem(userId: number, productId: number, quantity: number): Promise<void> { export async function insertCartItem(userId: number, skuId: number, quantity: number): Promise<void> {
await db.insert(cartItems).values({ await db.insert(cartItems).values({
userId, userId,
productId, skuId,
quantity: quantity.toString(), quantity: quantity.toString(),
}) })
} }

View file

@ -649,32 +649,32 @@ export async function getProductsForRecentOrders(
productIds: number[], productIds: number[],
limit: number limit: number
): Promise<RecentProductData[]> { ): Promise<RecentProductData[]> {
const results = await db const skus = await db.query.productSkus.findMany({
.select({ where: and(
id: productInfo.id, inArray(productSkus.id, productIds),
name: productInfo.name, eq(productSkus.isSuspended, false)
shortDescription: productInfo.shortDescription, ),
price: productInfo.price, with: {
images: productInfo.images, product: true,
isOutOfStock: productInfo.isOutOfStock, features: true,
unitShortNotation: units.shortNotation, },
incrementStep: productInfo.incrementStep, orderBy: desc(productSkus.createdAt),
limit,
}) })
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(
and(
inArray(productInfo.id, productIds),
eq(productInfo.isSuspended, false)
)
)
.orderBy(desc(productInfo.createdAt))
.limit(limit)
return results.map((product) => ({ return skus.map((sku) => {
...product, const features = sku.features || []
price: String(product.price ?? '0'), return {
})) id: sku.id,
name: sku.product?.name ?? 'Unknown',
shortDescription: sku.product?.shortDescription ?? null,
price: String(sku.price ?? '0'),
images: sku.images,
isOutOfStock: sku.isOutOfStock,
unitShortNotation: features.map((f) => f.featureValue).join(' '),
incrementStep: sku.product?.incrementStep ?? 1,
}
})
} }
// ============================================================================ // ============================================================================
@ -697,9 +697,11 @@ export interface OrderWithFullData {
} | null } | null
orderItems: Array<{ orderItems: Array<{
quantity: string quantity: string
sku: {
product: { product: {
name: string name: string
} | null } | null
} | null
}> }>
slot: { slot: {
deliveryTime: Date deliveryTime: Date
@ -777,6 +779,8 @@ export async function getOrderByIdWithFullData(
}, },
}, },
orderItems: { orderItems: {
with: {
sku: {
with: { with: {
product: { product: {
columns: { columns: {
@ -785,6 +789,8 @@ export async function getOrderByIdWithFullData(
}, },
}, },
}, },
},
},
slot: { slot: {
columns: { columns: {
deliveryTime: true, deliveryTime: true,

View file

@ -1,5 +1,5 @@
import { db } from '../db/db_index' import { db } from '../db/db_index'
import { deliverySlotInfo, productInfo, productSkus, productReviews, productTags, specialDeals, storeInfo, units, users } from '../db/schema' import { deliverySlotInfo, productInfo, productSkus, productReviews, productTags, specialDeals, storeInfo, users } from '../db/schema'
import { and, desc, eq, gt, sql } from 'drizzle-orm' import { and, desc, eq, gt, sql } from 'drizzle-orm'
import type { UserProductDetailData, UserProductReview } from '@packages/shared' import type { UserProductDetailData, UserProductReview } from '@packages/shared'
@ -177,30 +177,32 @@ export async function getAllProductsWithUnits(tagId?: number): Promise<ProductSu
// Filter by product IDs if tag filtering is applied // Filter by product IDs if tag filtering is applied
if (productIds && productIds.length > 0) { if (productIds && productIds.length > 0) {
whereCondition = inArray(productInfo.id, productIds) whereCondition = inArray(productSkus.productId, productIds)
} }
const results = await db const skus = await db.query.productSkus.findMany({
.select({ where: whereCondition,
id: productInfo.id, with: {
name: productInfo.name, product: true,
shortDescription: productInfo.shortDescription, features: true,
price: productInfo.price, },
marketPrice: productInfo.marketPrice,
images: productInfo.images,
isOutOfStock: productInfo.isOutOfStock,
unitShortNotation: units.shortNotation,
productQuantity: productInfo.productQuantity,
}) })
.from(productInfo)
.innerJoin(units, eq(productInfo.unitId, units.id))
.where(whereCondition)
return results.map((product) => ({ return skus.map((sku) => {
...product, const features = sku.features || []
price: String(product.price ?? '0'), return {
marketPrice: product.marketPrice ? String(product.marketPrice) : null, id: sku.product?.id ?? 0,
})) name: sku.product?.name ?? 'Unknown',
skuId: sku.id,
skuName: sku.name ?? null,
shortDescription: sku.product?.shortDescription ?? null,
price: String(sku.price ?? '0'),
marketPrice: sku.marketPrice ? String(sku.marketPrice) : null,
images: sku.images,
isOutOfStock: sku.isOutOfStock,
features: features.map((f) => ({ featureName: f.featureName, featureValue: f.featureValue })),
}
})
} }
/** /**

View file

@ -372,13 +372,7 @@ export interface AdminSkuVariant {
export interface AdminSku { export interface AdminSku {
id: number id: number
productId: number productId: number
productName?: string name: string | null
skuCode: string | null
displayName: string
unitId: number
unit?: AdminUnit
productQuantity: number
incrementStep: number
price: string price: string
marketPrice: string | null marketPrice: string | null
images: string[] | null images: string[] | null
@ -387,11 +381,9 @@ export interface AdminSku {
isSuspended: boolean isSuspended: boolean
isFlashAvailable: boolean isFlashAvailable: boolean
flashPrice: string | null flashPrice: string | null
isComboOnly: boolean
sortOrder: number
isDefault: boolean
createdAt: Date createdAt: Date
variants: AdminSkuVariant[] features: AdminSkuFeature[]
}
} }
export interface AdminProduct { export interface AdminProduct {
@ -443,6 +435,8 @@ export interface CreateProductInput {
incrementStep?: number incrementStep?: number
skus: CreateSkuInput[] skus: CreateSkuInput[]
} }
export interface AdminProductTagInfo {
id: number; id: number;
tagName: string; tagName: string;
tagDescription: string | null; tagDescription: string | null;
@ -465,7 +459,7 @@ export interface AdminProductTagWithProducts extends AdminProductTagInfo {
export interface AdminSpecialDeal { export interface AdminSpecialDeal {
id: number; id: number;
productId: number; skuId: number;
quantity: string; quantity: string;
price: string; price: string;
validTill: Date; validTill: Date;