enh
This commit is contained in:
parent
f6bcc23ca2
commit
648a0a5d28
16 changed files with 207 additions and 184 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ interface Attribute {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Variant {
|
interface Variant {
|
||||||
|
id?: number
|
||||||
name: string
|
name: string
|
||||||
price: string
|
price: string
|
||||||
marketPrice: string
|
marketPrice: string
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,37 +298,54 @@ 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(
|
||||||
productId: id,
|
sku.features.map((f: any) => ({
|
||||||
name: sku.name ?? null,
|
skuId: sku.id,
|
||||||
price: String(sku.price),
|
featureName: f.featureName,
|
||||||
marketPrice: sku.marketPrice != null ? String(sku.marketPrice) : null,
|
featureValue: f.featureValue,
|
||||||
images: sku.images ?? null,
|
}))
|
||||||
isFlashAvailable: sku.isFlashAvailable ?? false,
|
)
|
||||||
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
} else {
|
||||||
}))
|
// Insert new SKU
|
||||||
).returning()
|
const [newSku] = await db.insert(productSkus).values({
|
||||||
|
productId: id,
|
||||||
|
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,
|
||||||
|
}).returning()
|
||||||
|
|
||||||
for (let i = 0; i < skuRows.length; i++) {
|
await db.insert(skuFeatures).values(
|
||||||
const sku = skus[i]
|
sku.features.map((f: any) => ({
|
||||||
await db.insert(skuFeatures).values(
|
skuId: newSku.id,
|
||||||
sku.features.map((f: any) => ({
|
featureName: f.featureName,
|
||||||
skuId: skuRows[i].id,
|
featureValue: f.featureValue,
|
||||||
featureName: f.featureName,
|
}))
|
||||||
featureValue: f.featureValue,
|
)
|
||||||
}))
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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>(
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,8 +697,10 @@ export interface OrderWithFullData {
|
||||||
} | null
|
} | null
|
||||||
orderItems: Array<{
|
orderItems: Array<{
|
||||||
quantity: string
|
quantity: string
|
||||||
product: {
|
sku: {
|
||||||
name: string
|
product: {
|
||||||
|
name: string
|
||||||
|
} | null
|
||||||
} | null
|
} | null
|
||||||
}>
|
}>
|
||||||
slot: {
|
slot: {
|
||||||
|
|
@ -778,9 +780,13 @@ export async function getOrderByIdWithFullData(
|
||||||
},
|
},
|
||||||
orderItems: {
|
orderItems: {
|
||||||
with: {
|
with: {
|
||||||
product: {
|
sku: {
|
||||||
columns: {
|
with: {
|
||||||
name: true,
|
product: {
|
||||||
|
columns: {
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -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 })),
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue