enh
This commit is contained in:
parent
d9652405ca
commit
038733c14a
15 changed files with 1584 additions and 464 deletions
|
|
@ -39,6 +39,101 @@ export {
|
|||
checkCouponExists,
|
||||
checkReservedCouponExists,
|
||||
getOrderWithUser,
|
||||
// Store methods
|
||||
getAllStores,
|
||||
getStoreById,
|
||||
createStore,
|
||||
updateStore,
|
||||
deleteStore,
|
||||
// Staff-user methods
|
||||
getStaffUserByName,
|
||||
getAllStaff,
|
||||
getStaffByName,
|
||||
getAllUsers,
|
||||
getUserWithDetails,
|
||||
updateUserSuspension,
|
||||
checkStaffUserExists,
|
||||
checkStaffRoleExists,
|
||||
createStaffUser,
|
||||
getAllRoles,
|
||||
// User methods
|
||||
createUserByMobile,
|
||||
getUserByMobile,
|
||||
getUnresolvedComplaintsCount,
|
||||
getAllUsersWithFilters,
|
||||
getOrderCountsByUserIds,
|
||||
getLastOrdersByUserIds,
|
||||
getSuspensionStatusesByUserIds,
|
||||
getUserBasicInfo,
|
||||
getUserSuspensionStatus,
|
||||
getUserOrders,
|
||||
getOrderStatusesByOrderIds,
|
||||
getItemCountsByOrderIds,
|
||||
upsertUserSuspension,
|
||||
searchUsers,
|
||||
getAllNotifCreds,
|
||||
getAllUnloggedTokens,
|
||||
getNotifTokensByUserIds,
|
||||
getUserIncidentsWithRelations,
|
||||
createUserIncident,
|
||||
// Vendor-snippets methods
|
||||
checkVendorSnippetExists,
|
||||
getVendorSnippetById,
|
||||
getVendorSnippetByCode,
|
||||
getAllVendorSnippets,
|
||||
createVendorSnippet,
|
||||
updateVendorSnippet,
|
||||
deleteVendorSnippet,
|
||||
getProductsByIds,
|
||||
getVendorSlotById,
|
||||
getVendorOrdersBySlotId,
|
||||
getOrderItemsByOrderIds,
|
||||
getOrderStatusByOrderIds,
|
||||
updateVendorOrderItemPackaging,
|
||||
// Product methods
|
||||
getAllProducts,
|
||||
getProductById,
|
||||
createProduct,
|
||||
updateProduct,
|
||||
toggleProductOutOfStock,
|
||||
getAllUnits,
|
||||
getAllProductTags,
|
||||
getProductReviews,
|
||||
respondToReview,
|
||||
getAllProductGroups,
|
||||
createProductGroup,
|
||||
updateProductGroup,
|
||||
deleteProductGroup,
|
||||
addProductToGroup,
|
||||
removeProductFromGroup,
|
||||
// Slots methods
|
||||
getAllSlots,
|
||||
getSlotById,
|
||||
createSlot,
|
||||
updateSlot,
|
||||
deleteSlot,
|
||||
getSlotProducts,
|
||||
addProductToSlot,
|
||||
removeProductFromSlot,
|
||||
clearSlotProducts,
|
||||
updateSlotCapacity,
|
||||
getSlotDeliverySequence,
|
||||
updateSlotDeliverySequence,
|
||||
// Order methods
|
||||
updateOrderNotes,
|
||||
getOrderWithDetails,
|
||||
getFullOrder,
|
||||
getOrderDetails,
|
||||
getAllOrders,
|
||||
getOrdersBySlotId,
|
||||
updateOrderPackaged,
|
||||
updateOrderDelivered,
|
||||
updateOrderItemPackaging,
|
||||
updateAddressCoords,
|
||||
getOrderStatus,
|
||||
cancelOrder,
|
||||
getTodaysOrders,
|
||||
removeDeliveryCharge,
|
||||
} from 'postgresService';
|
||||
|
||||
// Re-export types from local types file (to avoid circular dependencies)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,20 @@
|
|||
import { router, publicProcedure, protectedProcedure } from '@/src/trpc/trpc-index'
|
||||
import { z } from 'zod';
|
||||
import { db } from '@/src/db/db_index'
|
||||
import { staffUsers, staffRoles, users, userDetails, orders } from '@/src/db/schema'
|
||||
import { eq, or, ilike, and, lt, desc } from 'drizzle-orm';
|
||||
import bcrypt from 'bcryptjs';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { ApiError } from '@/src/lib/api-error'
|
||||
import {
|
||||
getStaffUserByName,
|
||||
getAllStaff,
|
||||
getAllUsers,
|
||||
getUserWithDetails,
|
||||
updateUserSuspension,
|
||||
checkStaffUserExists,
|
||||
checkStaffRoleExists,
|
||||
createStaffUser,
|
||||
getAllRoles,
|
||||
} from '@/src/dbService'
|
||||
import type { StaffUser, StaffRole } from '@packages/shared'
|
||||
|
||||
export const staffUserRouter = router({
|
||||
login: publicProcedure
|
||||
|
|
@ -20,9 +29,7 @@ export const staffUserRouter = router({
|
|||
throw new ApiError('Name and password are required', 400);
|
||||
}
|
||||
|
||||
const staff = await db.query.staffUsers.findFirst({
|
||||
where: eq(staffUsers.name, name),
|
||||
});
|
||||
const staff = await getStaffUserByName(name);
|
||||
|
||||
if (!staff) {
|
||||
throw new ApiError('Invalid credentials', 401);
|
||||
|
|
@ -48,23 +55,7 @@ export const staffUserRouter = router({
|
|||
|
||||
getStaff: protectedProcedure
|
||||
.query(async ({ ctx }) => {
|
||||
const staff = await db.query.staffUsers.findMany({
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
with: {
|
||||
role: {
|
||||
with: {
|
||||
rolePermissions: {
|
||||
with: {
|
||||
permission: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const staff = await getAllStaff();
|
||||
|
||||
// Transform the data to include role and permissions in a cleaner format
|
||||
const transformedStaff = staff.map((user) => ({
|
||||
|
|
@ -74,7 +65,7 @@ export const staffUserRouter = router({
|
|||
id: user.role.id,
|
||||
name: user.role.roleName,
|
||||
} : null,
|
||||
permissions: user.role?.rolePermissions.map((rp) => ({
|
||||
permissions: user.role?.rolePermissions.map((rp: any) => ({
|
||||
id: rp.permission.id,
|
||||
name: rp.permission.permissionName,
|
||||
})) || [],
|
||||
|
|
@ -94,34 +85,9 @@ export const staffUserRouter = router({
|
|||
.query(async ({ input }) => {
|
||||
const { cursor, limit, search } = input;
|
||||
|
||||
let whereCondition = undefined;
|
||||
const { users: usersToReturn, hasMore } = await getAllUsers(cursor, limit, search);
|
||||
|
||||
if (search) {
|
||||
whereCondition = or(
|
||||
ilike(users.name, `%${search}%`),
|
||||
ilike(users.email, `%${search}%`),
|
||||
ilike(users.mobile, `%${search}%`)
|
||||
);
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
const cursorCondition = lt(users.id, cursor);
|
||||
whereCondition = whereCondition ? and(whereCondition, cursorCondition) : cursorCondition;
|
||||
}
|
||||
|
||||
const allUsers = await db.query.users.findMany({
|
||||
where: whereCondition,
|
||||
with: {
|
||||
userDetails: true,
|
||||
},
|
||||
orderBy: desc(users.id),
|
||||
limit: limit + 1, // fetch one extra to check if there's more
|
||||
});
|
||||
|
||||
const hasMore = allUsers.length > limit;
|
||||
const usersToReturn = hasMore ? allUsers.slice(0, limit) : allUsers;
|
||||
|
||||
const formattedUsers = usersToReturn.map(user => ({
|
||||
const formattedUsers = usersToReturn.map((user: any) => ({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
|
|
@ -140,16 +106,7 @@ export const staffUserRouter = router({
|
|||
.query(async ({ input }) => {
|
||||
const { userId } = input;
|
||||
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.id, userId),
|
||||
with: {
|
||||
userDetails: true,
|
||||
orders: {
|
||||
orderBy: desc(orders.createdAt),
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
const user = await getUserWithDetails(userId);
|
||||
|
||||
if (!user) {
|
||||
throw new ApiError("User not found", 404);
|
||||
|
|
@ -173,13 +130,7 @@ export const staffUserRouter = router({
|
|||
.mutation(async ({ input }) => {
|
||||
const { userId, isSuspended } = input;
|
||||
|
||||
await db
|
||||
.insert(userDetails)
|
||||
.values({ userId, isSuspended })
|
||||
.onConflictDoUpdate({
|
||||
target: userDetails.userId,
|
||||
set: { isSuspended },
|
||||
});
|
||||
await updateUserSuspension(userId, isSuspended);
|
||||
|
||||
return { success: true };
|
||||
}),
|
||||
|
|
@ -194,20 +145,16 @@ export const staffUserRouter = router({
|
|||
const { name, password, roleId } = input;
|
||||
|
||||
// Check if staff user already exists
|
||||
const existingUser = await db.query.staffUsers.findFirst({
|
||||
where: eq(staffUsers.name, name),
|
||||
});
|
||||
const existingUser = await checkStaffUserExists(name);
|
||||
|
||||
if (existingUser) {
|
||||
throw new ApiError('Staff user with this name already exists', 409);
|
||||
}
|
||||
|
||||
// Check if role exists
|
||||
const role = await db.query.staffRoles.findFirst({
|
||||
where: eq(staffRoles.id, roleId),
|
||||
});
|
||||
const roleExists = await checkStaffRoleExists(roleId);
|
||||
|
||||
if (!role) {
|
||||
if (!roleExists) {
|
||||
throw new ApiError('Invalid role selected', 400);
|
||||
}
|
||||
|
||||
|
|
@ -215,29 +162,20 @@ export const staffUserRouter = router({
|
|||
const hashedPassword = await bcrypt.hash(password, 12);
|
||||
|
||||
// Create staff user
|
||||
const [newUser] = await db.insert(staffUsers).values({
|
||||
name: name.trim(),
|
||||
password: hashedPassword,
|
||||
staffRoleId: roleId,
|
||||
}).returning();
|
||||
const newUser = await createStaffUser(name, hashedPassword, roleId);
|
||||
|
||||
return { success: true, user: { id: newUser.id, name: newUser.name } };
|
||||
}),
|
||||
|
||||
getRoles: protectedProcedure
|
||||
.query(async ({ ctx }) => {
|
||||
const roles = await db.query.staffRoles.findMany({
|
||||
columns: {
|
||||
id: true,
|
||||
roleName: true,
|
||||
},
|
||||
});
|
||||
const roles = await getAllRoles();
|
||||
|
||||
return {
|
||||
roles: roles.map(role => ({
|
||||
roles: roles.map((role: any) => ({
|
||||
id: role.id,
|
||||
name: role.roleName,
|
||||
})),
|
||||
};
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,30 +1,29 @@
|
|||
import { router, protectedProcedure } from '@/src/trpc/trpc-index'
|
||||
import { z } from 'zod';
|
||||
import { db } from '@/src/db/db_index'
|
||||
import { storeInfo, productInfo } from '@/src/db/schema'
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
import { ApiError } from '@/src/lib/api-error'
|
||||
import { extractKeyFromPresignedUrl, deleteImageUtil, generateSignedUrlFromS3Url } from '@/src/lib/s3-client'
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { extractKeyFromPresignedUrl, deleteImageUtil, generateSignedUrlFromS3Url } from '@/src/lib/s3-client'
|
||||
import { scheduleStoreInitialization } from '@/src/stores/store-initializer'
|
||||
|
||||
import {
|
||||
getAllStores as getAllStoresFromDb,
|
||||
getStoreById as getStoreByIdFromDb,
|
||||
createStore as createStoreInDb,
|
||||
updateStore as updateStoreInDb,
|
||||
deleteStore as deleteStoreFromDb,
|
||||
} from '@/src/dbService'
|
||||
import type { Store } from '@packages/shared'
|
||||
|
||||
export const storeRouter = router({
|
||||
getStores: protectedProcedure
|
||||
.query(async ({ ctx }) => {
|
||||
const stores = await db.query.storeInfo.findMany({
|
||||
with: {
|
||||
owner: true,
|
||||
},
|
||||
});
|
||||
.query(async ({ ctx }): Promise<{ stores: any[]; count: number }> => {
|
||||
const stores = await getAllStoresFromDb();
|
||||
|
||||
Promise.all(stores.map(async store => {
|
||||
await Promise.all(stores.map(async store => {
|
||||
if(store.imageUrl)
|
||||
store.imageUrl = await generateSignedUrlFromS3Url(store.imageUrl)
|
||||
})).catch((e) => {
|
||||
throw new ApiError("Unable to find store image urls")
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
return {
|
||||
stores,
|
||||
count: stores.length,
|
||||
|
|
@ -35,15 +34,10 @@ export const storeRouter = router({
|
|||
.input(z.object({
|
||||
id: z.number(),
|
||||
}))
|
||||
.query(async ({ input, ctx }) => {
|
||||
.query(async ({ input, ctx }): Promise<{ store: any }> => {
|
||||
const { id } = input;
|
||||
|
||||
const store = await db.query.storeInfo.findFirst({
|
||||
where: eq(storeInfo.id, id),
|
||||
with: {
|
||||
owner: true,
|
||||
},
|
||||
});
|
||||
const store = await getStoreByIdFromDb(id);
|
||||
|
||||
if (!store) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
|
|
@ -54,19 +48,31 @@ export const storeRouter = router({
|
|||
};
|
||||
}),
|
||||
|
||||
createStore: protectedProcedure
|
||||
.input(z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.string().optional(),
|
||||
imageUrl: z.string().optional(),
|
||||
owner: z.number().min(1, "Owner is required"),
|
||||
products: z.array(z.number()).optional(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
createStore: protectedProcedure
|
||||
.input(z.object({
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.string().optional(),
|
||||
imageUrl: z.string().optional(),
|
||||
owner: z.number().min(1, "Owner is required"),
|
||||
products: z.array(z.number()).optional(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }): Promise<{ store: Store; message: string }> => {
|
||||
const { name, description, imageUrl, owner, products } = input;
|
||||
|
||||
const imageKey = imageUrl ? extractKeyFromPresignedUrl(imageUrl) : undefined;
|
||||
|
||||
const newStore = await createStoreInDb(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
imageUrl: imageKey,
|
||||
owner,
|
||||
},
|
||||
products
|
||||
);
|
||||
|
||||
/*
|
||||
// Old implementation - direct DB query:
|
||||
const [newStore] = await db
|
||||
.insert(storeInfo)
|
||||
.values({
|
||||
|
|
@ -84,6 +90,7 @@ export const storeRouter = router({
|
|||
.set({ storeId: newStore.id })
|
||||
.where(inArray(productInfo.id, products));
|
||||
}
|
||||
*/
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
scheduleStoreInitialization()
|
||||
|
|
@ -94,117 +101,134 @@ export const storeRouter = router({
|
|||
};
|
||||
}),
|
||||
|
||||
updateStore: protectedProcedure
|
||||
.input(z.object({
|
||||
id: z.number(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.string().optional(),
|
||||
imageUrl: z.string().optional(),
|
||||
owner: z.number().min(1, "Owner is required"),
|
||||
products: z.array(z.number()).optional(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
updateStore: protectedProcedure
|
||||
.input(z.object({
|
||||
id: z.number(),
|
||||
name: z.string().min(1, "Name is required"),
|
||||
description: z.string().optional(),
|
||||
imageUrl: z.string().optional(),
|
||||
owner: z.number().min(1, "Owner is required"),
|
||||
products: z.array(z.number()).optional(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }): Promise<{ store: Store; message: string }> => {
|
||||
const { id, name, description, imageUrl, owner, products } = input;
|
||||
|
||||
const existingStore = await db.query.storeInfo.findFirst({
|
||||
where: eq(storeInfo.id, id),
|
||||
});
|
||||
const existingStore = await getStoreByIdFromDb(id);
|
||||
|
||||
if (!existingStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
if (!existingStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
|
||||
const oldImageKey = existingStore.imageUrl;
|
||||
const newImageKey = imageUrl ? extractKeyFromPresignedUrl(imageUrl) : oldImageKey;
|
||||
const oldImageKey = existingStore.imageUrl;
|
||||
const newImageKey = imageUrl ? extractKeyFromPresignedUrl(imageUrl) : oldImageKey;
|
||||
|
||||
// Delete old image only if:
|
||||
// 1. New image provided and keys are different, OR
|
||||
// 2. No new image but old exists (clearing the image)
|
||||
if (oldImageKey && (
|
||||
(newImageKey && newImageKey !== oldImageKey) ||
|
||||
(!newImageKey)
|
||||
)) {
|
||||
try {
|
||||
await deleteImageUtil({keys: [oldImageKey]});
|
||||
} catch (error) {
|
||||
console.error('Failed to delete old image:', error);
|
||||
// Continue with update even if deletion fails
|
||||
}
|
||||
// Delete old image only if:
|
||||
// 1. New image provided and keys are different, OR
|
||||
// 2. No new image but old exists (clearing the image)
|
||||
if (oldImageKey && (
|
||||
(newImageKey && newImageKey !== oldImageKey) ||
|
||||
(!newImageKey)
|
||||
)) {
|
||||
try {
|
||||
await deleteImageUtil({keys: [oldImageKey]});
|
||||
} catch (error) {
|
||||
console.error('Failed to delete old image:', error);
|
||||
// Continue with update even if deletion fails
|
||||
}
|
||||
}
|
||||
|
||||
const [updatedStore] = await db
|
||||
.update(storeInfo)
|
||||
.set({
|
||||
name,
|
||||
description,
|
||||
imageUrl: newImageKey,
|
||||
owner,
|
||||
})
|
||||
.where(eq(storeInfo.id, id))
|
||||
const updatedStore = await updateStoreInDb(
|
||||
id,
|
||||
{
|
||||
name,
|
||||
description,
|
||||
imageUrl: newImageKey,
|
||||
owner,
|
||||
},
|
||||
products
|
||||
);
|
||||
|
||||
/*
|
||||
// Old implementation - direct DB query:
|
||||
const [updatedStore] = await db
|
||||
.update(storeInfo)
|
||||
.set({
|
||||
name,
|
||||
description,
|
||||
imageUrl: newImageKey,
|
||||
owner,
|
||||
})
|
||||
.where(eq(storeInfo.id, id))
|
||||
.returning();
|
||||
|
||||
if (!updatedStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
|
||||
// Update products if provided
|
||||
if (products) {
|
||||
// First, set storeId to null for products not in the list but currently assigned to this store
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, id));
|
||||
|
||||
// Then, assign the selected products to this store
|
||||
if (products.length > 0) {
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: id })
|
||||
.where(inArray(productInfo.id, products));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
scheduleStoreInitialization()
|
||||
|
||||
return {
|
||||
store: updatedStore,
|
||||
message: "Store updated successfully",
|
||||
};
|
||||
}),
|
||||
|
||||
deleteStore: protectedProcedure
|
||||
.input(z.object({
|
||||
storeId: z.number(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }): Promise<{ message: string }> => {
|
||||
const { storeId } = input;
|
||||
|
||||
const result = await deleteStoreFromDb(storeId);
|
||||
|
||||
/*
|
||||
// Old implementation - direct DB query with transaction:
|
||||
const result = await db.transaction(async (tx) => {
|
||||
// First, update all products of this store to set storeId to null
|
||||
await tx
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, storeId));
|
||||
|
||||
// Then delete the store
|
||||
const [deletedStore] = await tx
|
||||
.delete(storeInfo)
|
||||
.where(eq(storeInfo.id, storeId))
|
||||
.returning();
|
||||
|
||||
if (!updatedStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
if (!deletedStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
|
||||
// Update products if provided
|
||||
if (products) {
|
||||
// First, set storeId to null for products not in the list but currently assigned to this store
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, id));
|
||||
return {
|
||||
message: "Store deleted successfully",
|
||||
};
|
||||
});
|
||||
*/
|
||||
|
||||
// Then, assign the selected products to this store
|
||||
if (products.length > 0) {
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: id })
|
||||
.where(inArray(productInfo.id, products));
|
||||
}
|
||||
}
|
||||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
scheduleStoreInitialization()
|
||||
|
||||
// Reinitialize stores to reflect changes
|
||||
scheduleStoreInitialization()
|
||||
|
||||
return {
|
||||
store: updatedStore,
|
||||
message: "Store updated successfully",
|
||||
};
|
||||
}),
|
||||
|
||||
deleteStore: protectedProcedure
|
||||
.input(z.object({
|
||||
storeId: z.number(),
|
||||
}))
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { storeId } = input;
|
||||
|
||||
const result = await db.transaction(async (tx) => {
|
||||
// First, update all products of this store to set storeId to null
|
||||
await tx
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, storeId));
|
||||
|
||||
// Then delete the store
|
||||
const [deletedStore] = await tx
|
||||
.delete(storeInfo)
|
||||
.where(eq(storeInfo.id, storeId))
|
||||
.returning();
|
||||
|
||||
if (!deletedStore) {
|
||||
throw new ApiError("Store not found", 404);
|
||||
}
|
||||
|
||||
return {
|
||||
message: "Store deleted successfully",
|
||||
};
|
||||
});
|
||||
|
||||
// Reinitialize stores to reflect changes (outside transaction)
|
||||
scheduleStoreInitialization()
|
||||
|
||||
return result;
|
||||
}),
|
||||
});
|
||||
return result;
|
||||
}),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,44 +1,29 @@
|
|||
import { protectedProcedure } from '@/src/trpc/trpc-index';
|
||||
import { z } from 'zod';
|
||||
import { db } from '@/src/db/db_index';
|
||||
import { users, complaints, orders, orderItems, notifCreds, unloggedUserTokens, userDetails, userIncidents } from '@/src/db/schema';
|
||||
import { eq, sql, desc, asc, count, max, inArray } from 'drizzle-orm';
|
||||
import { ApiError } from '@/src/lib/api-error';
|
||||
import { notificationQueue } from '@/src/lib/notif-job';
|
||||
import { recomputeUserNegativityScore } from '@/src/stores/user-negativity-store';
|
||||
|
||||
async function createUserByMobile(mobile: string): Promise<typeof users.$inferSelect> {
|
||||
// Clean mobile number (remove non-digits)
|
||||
const cleanMobile = mobile.replace(/\D/g, '');
|
||||
|
||||
// Validate: exactly 10 digits
|
||||
if (cleanMobile.length !== 10) {
|
||||
throw new ApiError('Mobile number must be exactly 10 digits', 400);
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.mobile, cleanMobile))
|
||||
.limit(1);
|
||||
|
||||
if (existingUser) {
|
||||
throw new ApiError('User with this mobile number already exists', 409);
|
||||
}
|
||||
|
||||
// Create user
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
name: null,
|
||||
email: null,
|
||||
mobile: cleanMobile,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return newUser;
|
||||
}
|
||||
import {
|
||||
createUserByMobile,
|
||||
getUserByMobile,
|
||||
getUnresolvedComplaintsCount,
|
||||
getAllUsersWithFilters,
|
||||
getOrderCountsByUserIds,
|
||||
getLastOrdersByUserIds,
|
||||
getSuspensionStatusesByUserIds,
|
||||
getUserBasicInfo,
|
||||
getUserSuspensionStatus,
|
||||
getUserOrders,
|
||||
getOrderStatusesByOrderIds,
|
||||
getItemCountsByOrderIds,
|
||||
upsertUserSuspension,
|
||||
searchUsers,
|
||||
getAllNotifCreds,
|
||||
getAllUnloggedTokens,
|
||||
getNotifTokensByUserIds,
|
||||
getUserIncidentsWithRelations,
|
||||
createUserIncident,
|
||||
} from '@/src/dbService';
|
||||
|
||||
export const userRouter = {
|
||||
createUserByMobile: protectedProcedure
|
||||
|
|
@ -46,7 +31,22 @@ export const userRouter = {
|
|||
mobile: z.string().min(1, 'Mobile number is required'),
|
||||
}))
|
||||
.mutation(async ({ input }) => {
|
||||
const newUser = await createUserByMobile(input.mobile);
|
||||
// Clean mobile number (remove non-digits)
|
||||
const cleanMobile = input.mobile.replace(/\D/g, '');
|
||||
|
||||
// Validate: exactly 10 digits
|
||||
if (cleanMobile.length !== 10) {
|
||||
throw new ApiError('Mobile number must be exactly 10 digits', 400);
|
||||
}
|
||||
|
||||
// Check if user already exists
|
||||
const existingUser = await getUserByMobile(cleanMobile);
|
||||
|
||||
if (existingUser) {
|
||||
throw new ApiError('User with this mobile number already exists', 409);
|
||||
}
|
||||
|
||||
const newUser = await createUserByMobile(cleanMobile);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -56,10 +56,10 @@ export const userRouter = {
|
|||
|
||||
getEssentials: protectedProcedure
|
||||
.query(async () => {
|
||||
const count = await db.$count(complaints, eq(complaints.isResolved, false));
|
||||
const count = await getUnresolvedComplaintsCount();
|
||||
|
||||
return {
|
||||
unresolvedComplaints: count || 0,
|
||||
unresolvedComplaints: count,
|
||||
};
|
||||
}),
|
||||
|
||||
|
|
@ -72,79 +72,22 @@ export const userRouter = {
|
|||
.query(async ({ input }) => {
|
||||
const { limit, cursor, search } = input;
|
||||
|
||||
// Build where conditions
|
||||
const whereConditions = [];
|
||||
|
||||
if (search && search.trim()) {
|
||||
whereConditions.push(sql`${users.mobile} ILIKE ${`%${search.trim()}%`}`);
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
whereConditions.push(sql`${users.id} > ${cursor}`);
|
||||
}
|
||||
|
||||
// Get users with filters applied
|
||||
const usersList = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(whereConditions.length > 0 ? sql.join(whereConditions, sql` AND `) : undefined)
|
||||
.orderBy(asc(users.id))
|
||||
.limit(limit + 1); // Get one extra to determine if there's more
|
||||
|
||||
// Check if there are more results
|
||||
const hasMore = usersList.length > limit;
|
||||
const usersToReturn = hasMore ? usersList.slice(0, limit) : usersList;
|
||||
const { users: usersToReturn, hasMore } = await getAllUsersWithFilters(limit, cursor, search);
|
||||
|
||||
// Get order stats for each user
|
||||
const userIds = usersToReturn.map(u => u.id);
|
||||
const userIds = usersToReturn.map((u: any) => u.id);
|
||||
|
||||
let orderCounts: { userId: number; totalOrders: number }[] = [];
|
||||
let lastOrders: { userId: number; lastOrderDate: Date | null }[] = [];
|
||||
let suspensionStatuses: { userId: number; isSuspended: boolean }[] = [];
|
||||
const orderCounts = await getOrderCountsByUserIds(userIds);
|
||||
const lastOrders = await getLastOrdersByUserIds(userIds);
|
||||
const suspensionStatuses = await getSuspensionStatusesByUserIds(userIds);
|
||||
|
||||
if (userIds.length > 0) {
|
||||
// Get total orders per user
|
||||
orderCounts = await db
|
||||
.select({
|
||||
userId: orders.userId,
|
||||
totalOrders: count(orders.id),
|
||||
})
|
||||
.from(orders)
|
||||
.where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`)
|
||||
.groupBy(orders.userId);
|
||||
|
||||
// Get last order date per user
|
||||
lastOrders = await db
|
||||
.select({
|
||||
userId: orders.userId,
|
||||
lastOrderDate: max(orders.createdAt),
|
||||
})
|
||||
.from(orders)
|
||||
.where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`)
|
||||
.groupBy(orders.userId);
|
||||
|
||||
// Get suspension status for each user
|
||||
suspensionStatuses = await db
|
||||
.select({
|
||||
userId: userDetails.userId,
|
||||
isSuspended: userDetails.isSuspended,
|
||||
})
|
||||
.from(userDetails)
|
||||
.where(sql`${userDetails.userId} IN (${sql.join(userIds, sql`, `)})`);
|
||||
}
|
||||
|
||||
// Create lookup maps
|
||||
const orderCountMap = new Map(orderCounts.map(o => [o.userId, o.totalOrders]));
|
||||
const lastOrderMap = new Map(lastOrders.map(o => [o.userId, o.lastOrderDate]));
|
||||
const suspensionMap = new Map(suspensionStatuses.map(s => [s.userId, s.isSuspended]));
|
||||
|
||||
// Combine data
|
||||
const usersWithStats = usersToReturn.map(user => ({
|
||||
const usersWithStats = usersToReturn.map((user: any) => ({
|
||||
...user,
|
||||
totalOrders: orderCountMap.get(user.id) || 0,
|
||||
lastOrderDate: lastOrderMap.get(user.id) || null,
|
||||
|
|
@ -169,69 +112,24 @@ export const userRouter = {
|
|||
const { userId } = input;
|
||||
|
||||
// Get user info
|
||||
const user = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
const user = await getUserBasicInfo(userId);
|
||||
|
||||
if (!user || user.length === 0) {
|
||||
if (!user) {
|
||||
throw new ApiError('User not found', 404);
|
||||
}
|
||||
|
||||
// Get user suspension status
|
||||
const userDetail = await db
|
||||
.select({
|
||||
isSuspended: userDetails.isSuspended,
|
||||
})
|
||||
.from(userDetails)
|
||||
.where(eq(userDetails.userId, userId))
|
||||
.limit(1);
|
||||
const isSuspended = await getUserSuspensionStatus(userId);
|
||||
|
||||
// Get all orders for this user with order items count
|
||||
const userOrders = await db
|
||||
.select({
|
||||
id: orders.id,
|
||||
readableId: orders.readableId,
|
||||
totalAmount: orders.totalAmount,
|
||||
createdAt: orders.createdAt,
|
||||
isFlashDelivery: orders.isFlashDelivery,
|
||||
})
|
||||
.from(orders)
|
||||
.where(eq(orders.userId, userId))
|
||||
.orderBy(desc(orders.createdAt));
|
||||
// Get all orders for this user
|
||||
const userOrders = await getUserOrders(userId);
|
||||
|
||||
// Get order status for each order
|
||||
const orderIds = userOrders.map(o => o.id);
|
||||
|
||||
let orderStatuses: { orderId: number; isDelivered: boolean; isCancelled: boolean }[] = [];
|
||||
|
||||
if (orderIds.length > 0) {
|
||||
const { orderStatus } = await import('@/src/db/schema');
|
||||
orderStatuses = await db
|
||||
.select({
|
||||
orderId: orderStatus.orderId,
|
||||
isDelivered: orderStatus.isDelivered,
|
||||
isCancelled: orderStatus.isCancelled,
|
||||
})
|
||||
.from(orderStatus)
|
||||
.where(sql`${orderStatus.orderId} IN (${sql.join(orderIds, sql`, `)})`);
|
||||
}
|
||||
const orderIds = userOrders.map((o: any) => o.id);
|
||||
const orderStatuses = await getOrderStatusesByOrderIds(orderIds);
|
||||
|
||||
// Get item counts for each order
|
||||
const itemCounts = await db
|
||||
.select({
|
||||
orderId: orderItems.orderId,
|
||||
itemCount: count(orderItems.id),
|
||||
})
|
||||
.from(orderItems)
|
||||
.where(sql`${orderItems.orderId} IN (${sql.join(orderIds, sql`, `)})`)
|
||||
.groupBy(orderItems.orderId);
|
||||
const itemCounts = await getItemCountsByOrderIds(orderIds);
|
||||
|
||||
// Create lookup maps
|
||||
const statusMap = new Map(orderStatuses.map(s => [s.orderId, s]));
|
||||
|
|
@ -246,7 +144,7 @@ export const userRouter = {
|
|||
};
|
||||
|
||||
// Combine data
|
||||
const ordersWithDetails = userOrders.map(order => {
|
||||
const ordersWithDetails = userOrders.map((order: any) => {
|
||||
const status = statusMap.get(order.id);
|
||||
return {
|
||||
id: order.id,
|
||||
|
|
@ -261,8 +159,8 @@ export const userRouter = {
|
|||
|
||||
return {
|
||||
user: {
|
||||
...user[0],
|
||||
isSuspended: userDetail[0]?.isSuspended ?? false,
|
||||
...user,
|
||||
isSuspended,
|
||||
},
|
||||
orders: ordersWithDetails,
|
||||
};
|
||||
|
|
@ -276,39 +174,7 @@ export const userRouter = {
|
|||
.mutation(async ({ input }) => {
|
||||
const { userId, isSuspended } = input;
|
||||
|
||||
// Check if user exists
|
||||
const user = await db
|
||||
.select({ id: users.id })
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
if (!user || user.length === 0) {
|
||||
throw new ApiError('User not found', 404);
|
||||
}
|
||||
|
||||
// Check if user_details record exists
|
||||
const existingDetail = await db
|
||||
.select({ id: userDetails.id })
|
||||
.from(userDetails)
|
||||
.where(eq(userDetails.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
if (existingDetail.length > 0) {
|
||||
// Update existing record
|
||||
await db
|
||||
.update(userDetails)
|
||||
.set({ isSuspended })
|
||||
.where(eq(userDetails.userId, userId));
|
||||
} else {
|
||||
// Insert new record
|
||||
await db
|
||||
.insert(userDetails)
|
||||
.values({
|
||||
userId,
|
||||
isSuspended,
|
||||
});
|
||||
}
|
||||
await upsertUserSuspension(userId, isSuspended);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -323,36 +189,15 @@ export const userRouter = {
|
|||
.query(async ({ input }) => {
|
||||
const { search } = input;
|
||||
|
||||
// Get all users
|
||||
let usersList;
|
||||
if (search && search.trim()) {
|
||||
usersList = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
})
|
||||
.from(users)
|
||||
.where(sql`${users.mobile} ILIKE ${`%${search.trim()}%`} OR ${users.name} ILIKE ${`%${search.trim()}%`}`);
|
||||
} else {
|
||||
usersList = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
})
|
||||
.from(users);
|
||||
}
|
||||
const usersList = await searchUsers(search);
|
||||
|
||||
// Get eligible users (have notif_creds entry)
|
||||
const eligibleUsers = await db
|
||||
.select({ userId: notifCreds.userId })
|
||||
.from(notifCreds);
|
||||
const eligibleUsers = await getAllNotifCreds();
|
||||
|
||||
const eligibleSet = new Set(eligibleUsers.map(u => u.userId));
|
||||
|
||||
return {
|
||||
users: usersList.map(user => ({
|
||||
users: usersList.map((user: any) => ({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
mobile: user.mobile,
|
||||
|
|
@ -375,8 +220,8 @@ export const userRouter = {
|
|||
|
||||
if (userIds.length === 0) {
|
||||
// Send to all users - get tokens from both logged-in and unlogged users
|
||||
const loggedInTokens = await db.select({ token: notifCreds.token }).from(notifCreds);
|
||||
const unloggedTokens = await db.select({ token: unloggedUserTokens.token }).from(unloggedUserTokens);
|
||||
const loggedInTokens = await getAllNotifCreds();
|
||||
const unloggedTokens = await getAllUnloggedTokens();
|
||||
|
||||
tokens = [
|
||||
...loggedInTokens.map(t => t.token),
|
||||
|
|
@ -384,11 +229,7 @@ export const userRouter = {
|
|||
];
|
||||
} else {
|
||||
// Send to specific users - get their tokens
|
||||
const userTokens = await db
|
||||
.select({ token: notifCreds.token })
|
||||
.from(notifCreds)
|
||||
.where(inArray(notifCreds.userId, userIds));
|
||||
|
||||
const userTokens = await getNotifTokensByUserIds(userIds);
|
||||
tokens = userTokens.map(t => t.token);
|
||||
}
|
||||
|
||||
|
|
@ -427,21 +268,10 @@ export const userRouter = {
|
|||
.query(async ({ input }) => {
|
||||
const { userId } = input;
|
||||
|
||||
const incidents = await db.query.userIncidents.findMany({
|
||||
where: eq(userIncidents.userId, userId),
|
||||
with: {
|
||||
order: {
|
||||
with: {
|
||||
orderStatus: true,
|
||||
},
|
||||
},
|
||||
addedBy: true,
|
||||
},
|
||||
orderBy: desc(userIncidents.dateAdded),
|
||||
});
|
||||
const incidents = await getUserIncidentsWithRelations(userId);
|
||||
|
||||
return {
|
||||
incidents: incidents.map(incident => ({
|
||||
incidents: incidents.map((incident: any) => ({
|
||||
id: incident.id,
|
||||
userId: incident.userId,
|
||||
orderId: incident.orderId,
|
||||
|
|
@ -470,14 +300,13 @@ export const userRouter = {
|
|||
throw new ApiError('Admin user not authenticated', 401);
|
||||
}
|
||||
|
||||
|
||||
const incidentObj = { userId, orderId, adminComment, addedBy: adminUserId, negativityScore };
|
||||
|
||||
const [incident] = await db.insert(userIncidents)
|
||||
.values({
|
||||
...incidentObj,
|
||||
})
|
||||
.returning();
|
||||
const incident = await createUserIncident(
|
||||
userId,
|
||||
orderId,
|
||||
adminComment,
|
||||
adminUserId,
|
||||
negativityScore
|
||||
);
|
||||
|
||||
recomputeUserNegativityScore(userId);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,3 +12,10 @@ export * from './src/helper_methods/banner';
|
|||
export * from './src/helper_methods/complaint';
|
||||
export * from './src/helper_methods/const';
|
||||
export * from './src/helper_methods/coupon';
|
||||
export * from './src/helper_methods/store';
|
||||
export * from './src/helper_methods/staff-user';
|
||||
export * from './src/helper_methods/user';
|
||||
export * from './src/helper_methods/vendor-snippets';
|
||||
export * from './src/helper_methods/product';
|
||||
export * from './src/helper_methods/slots';
|
||||
export * from './src/helper_methods/order';
|
||||
|
|
|
|||
259
packages/db_helper_postgres/src/helper_methods/order.ts
Normal file
259
packages/db_helper_postgres/src/helper_methods/order.ts
Normal file
|
|
@ -0,0 +1,259 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { orders, orderItems, orderStatus, users, addresses, refunds, complaints, payments } from '../db/schema';
|
||||
import { eq, and, gte, lt, desc, inArray, sql } from 'drizzle-orm';
|
||||
|
||||
export async function updateOrderNotes(orderId: number, adminNotes: string | null): Promise<any> {
|
||||
const [result] = await db
|
||||
.update(orders)
|
||||
.set({ adminNotes })
|
||||
.where(eq(orders.id, orderId))
|
||||
.returning();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function getOrderWithDetails(orderId: number): Promise<any | null> {
|
||||
return await db.query.orders.findFirst({
|
||||
where: eq(orders.id, orderId),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: {
|
||||
with: {
|
||||
unit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
address: true,
|
||||
orderStatus: true,
|
||||
slot: true,
|
||||
payments: true,
|
||||
refunds: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getFullOrder(orderId: number): Promise<any | null> {
|
||||
return await db.query.orders.findFirst({
|
||||
where: eq(orders.id, orderId),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: {
|
||||
with: {
|
||||
unit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
with: {
|
||||
userDetails: true,
|
||||
},
|
||||
},
|
||||
address: true,
|
||||
orderStatus: true,
|
||||
slot: true,
|
||||
payments: true,
|
||||
refunds: true,
|
||||
complaints: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOrderDetails(orderId: number): Promise<any | null> {
|
||||
return await db.query.orders.findFirst({
|
||||
where: eq(orders.id, orderId),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: {
|
||||
with: {
|
||||
unit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
address: true,
|
||||
orderStatus: true,
|
||||
slot: true,
|
||||
payments: true,
|
||||
refunds: true,
|
||||
complaints: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllOrders(
|
||||
limit: number,
|
||||
cursor?: number,
|
||||
slotId?: number | null,
|
||||
filters?: any
|
||||
): Promise<{ orders: any[]; hasMore: boolean }> {
|
||||
let whereConditions = [];
|
||||
|
||||
if (cursor) {
|
||||
whereConditions.push(lt(orders.id, cursor));
|
||||
}
|
||||
|
||||
if (slotId) {
|
||||
whereConditions.push(eq(orders.slotId, slotId));
|
||||
}
|
||||
|
||||
// Add filter conditions
|
||||
if (filters) {
|
||||
if (filters.packagedFilter === 'packaged') {
|
||||
whereConditions.push(eq(orders.isPackaged, true));
|
||||
} else if (filters.packagedFilter === 'not_packaged') {
|
||||
whereConditions.push(eq(orders.isPackaged, false));
|
||||
}
|
||||
|
||||
if (filters.deliveredFilter === 'delivered') {
|
||||
whereConditions.push(eq(orders.isDelivered, true));
|
||||
} else if (filters.deliveredFilter === 'not_delivered') {
|
||||
whereConditions.push(eq(orders.isDelivered, false));
|
||||
}
|
||||
|
||||
if (filters.flashDeliveryFilter === 'flash') {
|
||||
whereConditions.push(eq(orders.isFlashDelivery, true));
|
||||
} else if (filters.flashDeliveryFilter === 'regular') {
|
||||
whereConditions.push(eq(orders.isFlashDelivery, false));
|
||||
}
|
||||
}
|
||||
|
||||
const ordersList = await db.query.orders.findMany({
|
||||
where: whereConditions.length > 0 ? and(...whereConditions) : undefined,
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
orderStatus: true,
|
||||
slot: true,
|
||||
},
|
||||
orderBy: desc(orders.id),
|
||||
limit: limit + 1,
|
||||
});
|
||||
|
||||
const hasMore = ordersList.length > limit;
|
||||
return { orders: hasMore ? ordersList.slice(0, limit) : ordersList, hasMore };
|
||||
}
|
||||
|
||||
export async function getOrdersBySlotId(slotId: number): Promise<any[]> {
|
||||
return await db.query.orders.findMany({
|
||||
where: eq(orders.slotId, slotId),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
orderStatus: true,
|
||||
address: true,
|
||||
},
|
||||
orderBy: desc(orders.createdAt),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateOrderPackaged(orderId: number, isPackaged: boolean): Promise<any> {
|
||||
const [result] = await db
|
||||
.update(orders)
|
||||
.set({ isPackaged })
|
||||
.where(eq(orders.id, orderId))
|
||||
.returning();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function updateOrderDelivered(orderId: number, isDelivered: boolean): Promise<any> {
|
||||
const [result] = await db
|
||||
.update(orders)
|
||||
.set({ isDelivered })
|
||||
.where(eq(orders.id, orderId))
|
||||
.returning();
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function updateOrderItemPackaging(
|
||||
orderItemId: number,
|
||||
isPackaged: boolean,
|
||||
isPackageVerified: boolean
|
||||
): Promise<void> {
|
||||
await db.update(orderItems)
|
||||
.set({ is_packaged: isPackaged, is_package_verified: isPackageVerified })
|
||||
.where(eq(orderItems.id, orderItemId));
|
||||
}
|
||||
|
||||
export async function updateAddressCoords(addressId: number, lat: number, lng: number): Promise<void> {
|
||||
await db.update(addresses)
|
||||
.set({ lat, lng })
|
||||
.where(eq(addresses.id, addressId));
|
||||
}
|
||||
|
||||
export async function getOrderStatus(orderId: number): Promise<any | null> {
|
||||
return await db.query.orderStatus.findFirst({
|
||||
where: eq(orderStatus.orderId, orderId),
|
||||
});
|
||||
}
|
||||
|
||||
export async function cancelOrder(orderId: number, reason: string): Promise<any> {
|
||||
return await db.transaction(async (tx) => {
|
||||
// Update order status
|
||||
const [order] = await tx.update(orders)
|
||||
.set({ isCancelled: true, cancellationReason: reason })
|
||||
.where(eq(orders.id, orderId))
|
||||
.returning();
|
||||
|
||||
// Create order status entry
|
||||
await tx.insert(orderStatus).values({
|
||||
orderId,
|
||||
isCancelled: true,
|
||||
cancelReason: reason,
|
||||
});
|
||||
|
||||
return order;
|
||||
});
|
||||
}
|
||||
|
||||
export async function getTodaysOrders(slotId?: number): Promise<any[]> {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
let whereConditions = [
|
||||
gte(orders.createdAt, today),
|
||||
lt(orders.createdAt, tomorrow),
|
||||
];
|
||||
|
||||
if (slotId) {
|
||||
whereConditions.push(eq(orders.slotId, slotId));
|
||||
}
|
||||
|
||||
return await db.query.orders.findMany({
|
||||
where: and(...whereConditions),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
orderStatus: true,
|
||||
},
|
||||
orderBy: desc(orders.createdAt),
|
||||
});
|
||||
}
|
||||
|
||||
export async function removeDeliveryCharge(orderId: number): Promise<any> {
|
||||
const [result] = await db
|
||||
.update(orders)
|
||||
.set({ deliveryCharge: '0' })
|
||||
.where(eq(orders.id, orderId))
|
||||
.returning();
|
||||
return result;
|
||||
}
|
||||
130
packages/db_helper_postgres/src/helper_methods/product.ts
Normal file
130
packages/db_helper_postgres/src/helper_methods/product.ts
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { productInfo, units, specialDeals, productSlots, productTags, productReviews, productGroupInfo, productGroupMembership } from '../db/schema';
|
||||
import { eq, and, inArray, desc, sql, asc } from 'drizzle-orm';
|
||||
|
||||
export async function getAllProducts(): Promise<any[]> {
|
||||
return await db.query.productInfo.findMany({
|
||||
orderBy: productInfo.name,
|
||||
with: {
|
||||
unit: true,
|
||||
store: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProductById(id: number): Promise<any | null> {
|
||||
return await db.query.productInfo.findFirst({
|
||||
where: eq(productInfo.id, id),
|
||||
with: {
|
||||
unit: true,
|
||||
store: true,
|
||||
productSlots: {
|
||||
with: {
|
||||
slot: true,
|
||||
},
|
||||
},
|
||||
specialDeals: true,
|
||||
productTags: {
|
||||
with: {
|
||||
tag: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function createProduct(input: any): Promise<any> {
|
||||
const [product] = await db.insert(productInfo).values(input).returning();
|
||||
return product;
|
||||
}
|
||||
|
||||
export async function updateProduct(id: number, updates: any): Promise<any> {
|
||||
const [product] = await db.update(productInfo)
|
||||
.set(updates)
|
||||
.where(eq(productInfo.id, id))
|
||||
.returning();
|
||||
return product;
|
||||
}
|
||||
|
||||
export async function toggleProductOutOfStock(id: number, isOutOfStock: boolean): Promise<any> {
|
||||
const [product] = await db.update(productInfo)
|
||||
.set({ isOutOfStock })
|
||||
.where(eq(productInfo.id, id))
|
||||
.returning();
|
||||
return product;
|
||||
}
|
||||
|
||||
export async function getAllUnits(): Promise<any[]> {
|
||||
return await db.query.units.findMany({
|
||||
orderBy: units.name,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllProductTags(): Promise<any[]> {
|
||||
return await db.query.productTags.findMany({
|
||||
with: {
|
||||
products: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProductReviews(productId: number): Promise<any[]> {
|
||||
return await db.query.productReviews.findMany({
|
||||
where: eq(productReviews.productId, productId),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
orderBy: desc(productReviews.createdAt),
|
||||
});
|
||||
}
|
||||
|
||||
export async function respondToReview(reviewId: number, adminResponse: string): Promise<void> {
|
||||
await db.update(productReviews)
|
||||
.set({ adminResponse })
|
||||
.where(eq(productReviews.id, reviewId));
|
||||
}
|
||||
|
||||
export async function getAllProductGroups(): Promise<any[]> {
|
||||
return await db.query.productGroupInfo.findMany({
|
||||
with: {
|
||||
products: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function createProductGroup(name: string): Promise<any> {
|
||||
const [group] = await db.insert(productGroupInfo).values({ name }).returning();
|
||||
return group;
|
||||
}
|
||||
|
||||
export async function updateProductGroup(id: number, name: string): Promise<any> {
|
||||
const [group] = await db.update(productGroupInfo)
|
||||
.set({ name })
|
||||
.where(eq(productGroupInfo.id, id))
|
||||
.returning();
|
||||
return group;
|
||||
}
|
||||
|
||||
export async function deleteProductGroup(id: number): Promise<void> {
|
||||
await db.delete(productGroupInfo).where(eq(productGroupInfo.id, id));
|
||||
}
|
||||
|
||||
export async function addProductToGroup(groupId: number, productId: number): Promise<void> {
|
||||
await db.insert(productGroupMembership).values({ groupId, productId });
|
||||
}
|
||||
|
||||
export async function removeProductFromGroup(groupId: number, productId: number): Promise<void> {
|
||||
await db.delete(productGroupMembership)
|
||||
.where(and(
|
||||
eq(productGroupMembership.groupId, groupId),
|
||||
eq(productGroupMembership.productId, productId)
|
||||
));
|
||||
}
|
||||
101
packages/db_helper_postgres/src/helper_methods/slots.ts
Normal file
101
packages/db_helper_postgres/src/helper_methods/slots.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { deliverySlotInfo, productSlots, productInfo, vendorSnippets } from '../db/schema';
|
||||
import { eq, and, inArray, desc } from 'drizzle-orm';
|
||||
|
||||
export async function getAllSlots(): Promise<any[]> {
|
||||
return await db.query.deliverySlotInfo.findMany({
|
||||
orderBy: desc(deliverySlotInfo.createdAt),
|
||||
with: {
|
||||
productSlots: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
vendorSnippets: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getSlotById(id: number): Promise<any | null> {
|
||||
return await db.query.deliverySlotInfo.findFirst({
|
||||
where: eq(deliverySlotInfo.id, id),
|
||||
with: {
|
||||
productSlots: {
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
},
|
||||
vendorSnippets: {
|
||||
with: {
|
||||
slot: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function createSlot(input: any): Promise<any> {
|
||||
const [slot] = await db.insert(deliverySlotInfo).values(input).returning();
|
||||
return slot;
|
||||
}
|
||||
|
||||
export async function updateSlot(id: number, updates: any): Promise<any> {
|
||||
const [slot] = await db.update(deliverySlotInfo)
|
||||
.set(updates)
|
||||
.where(eq(deliverySlotInfo.id, id))
|
||||
.returning();
|
||||
return slot;
|
||||
}
|
||||
|
||||
export async function deleteSlot(id: number): Promise<void> {
|
||||
await db.delete(deliverySlotInfo).where(eq(deliverySlotInfo.id, id));
|
||||
}
|
||||
|
||||
export async function getSlotProducts(slotId: number): Promise<any[]> {
|
||||
return await db.query.productSlots.findMany({
|
||||
where: eq(productSlots.slotId, slotId),
|
||||
with: {
|
||||
product: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function addProductToSlot(slotId: number, productId: number): Promise<void> {
|
||||
await db.insert(productSlots).values({ slotId, productId });
|
||||
}
|
||||
|
||||
export async function removeProductFromSlot(slotId: number, productId: number): Promise<void> {
|
||||
await db.delete(productSlots)
|
||||
.where(and(
|
||||
eq(productSlots.slotId, slotId),
|
||||
eq(productSlots.productId, productId)
|
||||
));
|
||||
}
|
||||
|
||||
export async function clearSlotProducts(slotId: number): Promise<void> {
|
||||
await db.delete(productSlots).where(eq(productSlots.slotId, slotId));
|
||||
}
|
||||
|
||||
export async function updateSlotCapacity(slotId: number, maxCapacity: number): Promise<any> {
|
||||
const [slot] = await db.update(deliverySlotInfo)
|
||||
.set({ maxCapacity })
|
||||
.where(eq(deliverySlotInfo.id, slotId))
|
||||
.returning();
|
||||
return slot;
|
||||
}
|
||||
|
||||
export async function getSlotDeliverySequence(slotId: number): Promise<any | null> {
|
||||
const slot = await db.query.deliverySlotInfo.findFirst({
|
||||
where: eq(deliverySlotInfo.id, slotId),
|
||||
columns: {
|
||||
deliverySequence: true,
|
||||
},
|
||||
});
|
||||
return slot?.deliverySequence || null;
|
||||
}
|
||||
|
||||
export async function updateSlotDeliverySequence(slotId: number, sequence: any): Promise<void> {
|
||||
await db.update(deliverySlotInfo)
|
||||
.set({ deliverySequence: sequence })
|
||||
.where(eq(deliverySlotInfo.id, slotId));
|
||||
}
|
||||
153
packages/db_helper_postgres/src/helper_methods/staff-user.ts
Normal file
153
packages/db_helper_postgres/src/helper_methods/staff-user.ts
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { staffUsers, staffRoles, users, userDetails, orders } from '../db/schema';
|
||||
import { eq, or, ilike, and, lt, desc } from 'drizzle-orm';
|
||||
|
||||
export interface StaffUser {
|
||||
id: number;
|
||||
name: string;
|
||||
password: string;
|
||||
staffRoleId: number;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export async function getStaffUserByName(name: string): Promise<StaffUser | null> {
|
||||
const staff = await db.query.staffUsers.findFirst({
|
||||
where: eq(staffUsers.name, name),
|
||||
});
|
||||
|
||||
return staff || null;
|
||||
}
|
||||
|
||||
export async function getAllStaff(): Promise<any[]> {
|
||||
const staff = await db.query.staffUsers.findMany({
|
||||
columns: {
|
||||
id: true,
|
||||
name: true,
|
||||
},
|
||||
with: {
|
||||
role: {
|
||||
with: {
|
||||
rolePermissions: {
|
||||
with: {
|
||||
permission: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return staff;
|
||||
}
|
||||
|
||||
export async function getStaffByName(name: string): Promise<StaffUser | null> {
|
||||
const staff = await db.query.staffUsers.findFirst({
|
||||
where: eq(staffUsers.name, name),
|
||||
});
|
||||
return staff || null;
|
||||
}
|
||||
|
||||
export async function getAllUsers(
|
||||
cursor?: number,
|
||||
limit: number = 20,
|
||||
search?: string
|
||||
): Promise<{ users: any[]; hasMore: boolean }> {
|
||||
let whereCondition = undefined;
|
||||
|
||||
if (search) {
|
||||
whereCondition = or(
|
||||
ilike(users.name, `%${search}%`),
|
||||
ilike(users.email, `%${search}%`),
|
||||
ilike(users.mobile, `%${search}%`)
|
||||
);
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
const cursorCondition = lt(users.id, cursor);
|
||||
whereCondition = whereCondition ? and(whereCondition, cursorCondition) : cursorCondition;
|
||||
}
|
||||
|
||||
const allUsers = await db.query.users.findMany({
|
||||
where: whereCondition,
|
||||
with: {
|
||||
userDetails: true,
|
||||
},
|
||||
orderBy: desc(users.id),
|
||||
limit: limit + 1,
|
||||
});
|
||||
|
||||
const hasMore = allUsers.length > limit;
|
||||
const usersToReturn = hasMore ? allUsers.slice(0, limit) : allUsers;
|
||||
|
||||
return { users: usersToReturn, hasMore };
|
||||
}
|
||||
|
||||
export async function getUserWithDetails(userId: number): Promise<any | null> {
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.id, userId),
|
||||
with: {
|
||||
userDetails: true,
|
||||
orders: {
|
||||
orderBy: desc(orders.createdAt),
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return user || null;
|
||||
}
|
||||
|
||||
export async function updateUserSuspension(userId: number, isSuspended: boolean): Promise<void> {
|
||||
await db
|
||||
.insert(userDetails)
|
||||
.values({ userId, isSuspended })
|
||||
.onConflictDoUpdate({
|
||||
target: userDetails.userId,
|
||||
set: { isSuspended },
|
||||
});
|
||||
}
|
||||
|
||||
export async function checkStaffUserExists(name: string): Promise<boolean> {
|
||||
const existingUser = await db.query.staffUsers.findFirst({
|
||||
where: eq(staffUsers.name, name),
|
||||
});
|
||||
return !!existingUser;
|
||||
}
|
||||
|
||||
export async function checkStaffRoleExists(roleId: number): Promise<boolean> {
|
||||
const role = await db.query.staffRoles.findFirst({
|
||||
where: eq(staffRoles.id, roleId),
|
||||
});
|
||||
return !!role;
|
||||
}
|
||||
|
||||
export async function createStaffUser(
|
||||
name: string,
|
||||
password: string,
|
||||
roleId: number
|
||||
): Promise<StaffUser> {
|
||||
const [newUser] = await db.insert(staffUsers).values({
|
||||
name: name.trim(),
|
||||
password,
|
||||
staffRoleId: roleId,
|
||||
}).returning();
|
||||
|
||||
return {
|
||||
id: newUser.id,
|
||||
name: newUser.name,
|
||||
password: newUser.password,
|
||||
staffRoleId: newUser.staffRoleId,
|
||||
createdAt: newUser.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAllRoles(): Promise<any[]> {
|
||||
const roles = await db.query.staffRoles.findMany({
|
||||
columns: {
|
||||
id: true,
|
||||
roleName: true,
|
||||
},
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
151
packages/db_helper_postgres/src/helper_methods/store.ts
Normal file
151
packages/db_helper_postgres/src/helper_methods/store.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { storeInfo, productInfo } from '../db/schema';
|
||||
import { eq, inArray } from 'drizzle-orm';
|
||||
|
||||
export interface Store {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
imageUrl: string | null;
|
||||
owner: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
export async function getAllStores(): Promise<any[]> {
|
||||
const stores = await db.query.storeInfo.findMany({
|
||||
with: {
|
||||
owner: true,
|
||||
},
|
||||
});
|
||||
|
||||
return stores;
|
||||
}
|
||||
|
||||
export async function getStoreById(id: number): Promise<any | null> {
|
||||
const store = await db.query.storeInfo.findFirst({
|
||||
where: eq(storeInfo.id, id),
|
||||
with: {
|
||||
owner: true,
|
||||
},
|
||||
});
|
||||
|
||||
return store || null;
|
||||
}
|
||||
|
||||
export interface CreateStoreInput {
|
||||
name: string;
|
||||
description?: string;
|
||||
imageUrl?: string;
|
||||
owner: number;
|
||||
}
|
||||
|
||||
export async function createStore(
|
||||
input: CreateStoreInput,
|
||||
products?: number[]
|
||||
): Promise<Store> {
|
||||
const [newStore] = await db
|
||||
.insert(storeInfo)
|
||||
.values({
|
||||
name: input.name,
|
||||
description: input.description,
|
||||
imageUrl: input.imageUrl,
|
||||
owner: input.owner,
|
||||
})
|
||||
.returning();
|
||||
|
||||
// Assign selected products to this store
|
||||
if (products && products.length > 0) {
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: newStore.id })
|
||||
.where(inArray(productInfo.id, products));
|
||||
}
|
||||
|
||||
return {
|
||||
id: newStore.id,
|
||||
name: newStore.name,
|
||||
description: newStore.description,
|
||||
imageUrl: newStore.imageUrl,
|
||||
owner: newStore.owner,
|
||||
createdAt: newStore.createdAt,
|
||||
updatedAt: newStore.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export interface UpdateStoreInput {
|
||||
name?: string;
|
||||
description?: string;
|
||||
imageUrl?: string;
|
||||
owner?: number;
|
||||
}
|
||||
|
||||
export async function updateStore(
|
||||
id: number,
|
||||
input: UpdateStoreInput,
|
||||
products?: number[]
|
||||
): Promise<Store> {
|
||||
const [updatedStore] = await db
|
||||
.update(storeInfo)
|
||||
.set({
|
||||
...input,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(storeInfo.id, id))
|
||||
.returning();
|
||||
|
||||
if (!updatedStore) {
|
||||
throw new Error("Store not found");
|
||||
}
|
||||
|
||||
// Update products if provided
|
||||
if (products !== undefined) {
|
||||
// First, set storeId to null for products not in the list but currently assigned to this store
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, id));
|
||||
|
||||
// Then, assign the selected products to this store
|
||||
if (products.length > 0) {
|
||||
await db
|
||||
.update(productInfo)
|
||||
.set({ storeId: id })
|
||||
.where(inArray(productInfo.id, products));
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
id: updatedStore.id,
|
||||
name: updatedStore.name,
|
||||
description: updatedStore.description,
|
||||
imageUrl: updatedStore.imageUrl,
|
||||
owner: updatedStore.owner,
|
||||
createdAt: updatedStore.createdAt,
|
||||
updatedAt: updatedStore.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export async function deleteStore(id: number): Promise<{ message: string }> {
|
||||
return await db.transaction(async (tx) => {
|
||||
// First, update all products of this store to set storeId to null
|
||||
await tx
|
||||
.update(productInfo)
|
||||
.set({ storeId: null })
|
||||
.where(eq(productInfo.storeId, id));
|
||||
|
||||
// Then delete the store
|
||||
const [deletedStore] = await tx
|
||||
.delete(storeInfo)
|
||||
.where(eq(storeInfo.id, id))
|
||||
.returning();
|
||||
|
||||
if (!deletedStore) {
|
||||
throw new Error("Store not found");
|
||||
}
|
||||
|
||||
return {
|
||||
message: "Store deleted successfully",
|
||||
};
|
||||
});
|
||||
}
|
||||
270
packages/db_helper_postgres/src/helper_methods/user.ts
Normal file
270
packages/db_helper_postgres/src/helper_methods/user.ts
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { users, userDetails, orders, orderItems, complaints, notifCreds, unloggedUserTokens, userIncidents, orderStatus } from '../db/schema';
|
||||
import { eq, sql, desc, asc, count, max, inArray } from 'drizzle-orm';
|
||||
|
||||
export async function createUserByMobile(mobile: string): Promise<any> {
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
name: null,
|
||||
email: null,
|
||||
mobile,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return newUser;
|
||||
}
|
||||
|
||||
export async function getUserByMobile(mobile: string): Promise<any | null> {
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.mobile, mobile))
|
||||
.limit(1);
|
||||
|
||||
return existingUser || null;
|
||||
}
|
||||
|
||||
export async function getUnresolvedComplaintsCount(): Promise<number> {
|
||||
const result = await db
|
||||
.select({ count: count(complaints.id) })
|
||||
.from(complaints)
|
||||
.where(eq(complaints.isResolved, false));
|
||||
|
||||
return result[0]?.count || 0;
|
||||
}
|
||||
|
||||
export async function getAllUsersWithFilters(
|
||||
limit: number,
|
||||
cursor?: number,
|
||||
search?: string
|
||||
): Promise<{ users: any[]; hasMore: boolean }> {
|
||||
const whereConditions = [];
|
||||
|
||||
if (search && search.trim()) {
|
||||
whereConditions.push(sql`${users.mobile} ILIKE ${`%${search.trim()}%`}`);
|
||||
}
|
||||
|
||||
if (cursor) {
|
||||
whereConditions.push(sql`${users.id} > ${cursor}`);
|
||||
}
|
||||
|
||||
const usersList = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(whereConditions.length > 0 ? sql.join(whereConditions, sql` AND `) : undefined)
|
||||
.orderBy(asc(users.id))
|
||||
.limit(limit + 1);
|
||||
|
||||
const hasMore = usersList.length > limit;
|
||||
const usersToReturn = hasMore ? usersList.slice(0, limit) : usersList;
|
||||
|
||||
return { users: usersToReturn, hasMore };
|
||||
}
|
||||
|
||||
export async function getOrderCountsByUserIds(userIds: number[]): Promise<{ userId: number; totalOrders: number }[]> {
|
||||
if (userIds.length === 0) return [];
|
||||
|
||||
return await db
|
||||
.select({
|
||||
userId: orders.userId,
|
||||
totalOrders: count(orders.id),
|
||||
})
|
||||
.from(orders)
|
||||
.where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`)
|
||||
.groupBy(orders.userId);
|
||||
}
|
||||
|
||||
export async function getLastOrdersByUserIds(userIds: number[]): Promise<{ userId: number; lastOrderDate: Date | null }[]> {
|
||||
if (userIds.length === 0) return [];
|
||||
|
||||
return await db
|
||||
.select({
|
||||
userId: orders.userId,
|
||||
lastOrderDate: max(orders.createdAt),
|
||||
})
|
||||
.from(orders)
|
||||
.where(sql`${orders.userId} IN (${sql.join(userIds, sql`, `)})`)
|
||||
.groupBy(orders.userId);
|
||||
}
|
||||
|
||||
export async function getSuspensionStatusesByUserIds(userIds: number[]): Promise<{ userId: number; isSuspended: boolean }[]> {
|
||||
if (userIds.length === 0) return [];
|
||||
|
||||
return await db
|
||||
.select({
|
||||
userId: userDetails.userId,
|
||||
isSuspended: userDetails.isSuspended,
|
||||
})
|
||||
.from(userDetails)
|
||||
.where(sql`${userDetails.userId} IN (${sql.join(userIds, sql`, `)})`);
|
||||
}
|
||||
|
||||
export async function getUserBasicInfo(userId: number): Promise<any | null> {
|
||||
const user = await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
createdAt: users.createdAt,
|
||||
})
|
||||
.from(users)
|
||||
.where(eq(users.id, userId))
|
||||
.limit(1);
|
||||
|
||||
return user[0] || null;
|
||||
}
|
||||
|
||||
export async function getUserSuspensionStatus(userId: number): Promise<boolean> {
|
||||
const userDetail = await db
|
||||
.select({
|
||||
isSuspended: userDetails.isSuspended,
|
||||
})
|
||||
.from(userDetails)
|
||||
.where(eq(userDetails.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
return userDetail[0]?.isSuspended ?? false;
|
||||
}
|
||||
|
||||
export async function getUserOrders(userId: number): Promise<any[]> {
|
||||
return await db
|
||||
.select({
|
||||
id: orders.id,
|
||||
readableId: orders.readableId,
|
||||
totalAmount: orders.totalAmount,
|
||||
createdAt: orders.createdAt,
|
||||
isFlashDelivery: orders.isFlashDelivery,
|
||||
})
|
||||
.from(orders)
|
||||
.where(eq(orders.userId, userId))
|
||||
.orderBy(desc(orders.createdAt));
|
||||
}
|
||||
|
||||
export async function getOrderStatusesByOrderIds(orderIds: number[]): Promise<{ orderId: number; isDelivered: boolean; isCancelled: boolean }[]> {
|
||||
if (orderIds.length === 0) return [];
|
||||
|
||||
return await db
|
||||
.select({
|
||||
orderId: orderStatus.orderId,
|
||||
isDelivered: orderStatus.isDelivered,
|
||||
isCancelled: orderStatus.isCancelled,
|
||||
})
|
||||
.from(orderStatus)
|
||||
.where(sql`${orderStatus.orderId} IN (${sql.join(orderIds, sql`, `)})`);
|
||||
}
|
||||
|
||||
export async function getItemCountsByOrderIds(orderIds: number[]): Promise<{ orderId: number; itemCount: number }[]> {
|
||||
if (orderIds.length === 0) return [];
|
||||
|
||||
return await db
|
||||
.select({
|
||||
orderId: orderItems.orderId,
|
||||
itemCount: count(orderItems.id),
|
||||
})
|
||||
.from(orderItems)
|
||||
.where(sql`${orderItems.orderId} IN (${sql.join(orderIds, sql`, `)})`)
|
||||
.groupBy(orderItems.orderId);
|
||||
}
|
||||
|
||||
export async function upsertUserSuspension(userId: number, isSuspended: boolean): Promise<void> {
|
||||
const existingDetail = await db
|
||||
.select({ id: userDetails.id })
|
||||
.from(userDetails)
|
||||
.where(eq(userDetails.userId, userId))
|
||||
.limit(1);
|
||||
|
||||
if (existingDetail.length > 0) {
|
||||
await db
|
||||
.update(userDetails)
|
||||
.set({ isSuspended })
|
||||
.where(eq(userDetails.userId, userId));
|
||||
} else {
|
||||
await db
|
||||
.insert(userDetails)
|
||||
.values({
|
||||
userId,
|
||||
isSuspended,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function searchUsers(search?: string): Promise<any[]> {
|
||||
if (search && search.trim()) {
|
||||
return await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
})
|
||||
.from(users)
|
||||
.where(sql`${users.mobile} ILIKE ${`%${search.trim()}%`} OR ${users.name} ILIKE ${`%${search.trim()}%`}`);
|
||||
} else {
|
||||
return await db
|
||||
.select({
|
||||
id: users.id,
|
||||
name: users.name,
|
||||
mobile: users.mobile,
|
||||
})
|
||||
.from(users);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllNotifCreds(): Promise<{ userId: number }[]> {
|
||||
return await db
|
||||
.select({ userId: notifCreds.userId })
|
||||
.from(notifCreds);
|
||||
}
|
||||
|
||||
export async function getAllUnloggedTokens(): Promise<{ token: string }[]> {
|
||||
return await db
|
||||
.select({ token: unloggedUserTokens.token })
|
||||
.from(unloggedUserTokens);
|
||||
}
|
||||
|
||||
export async function getNotifTokensByUserIds(userIds: number[]): Promise<{ token: string }[]> {
|
||||
return await db
|
||||
.select({ token: notifCreds.token })
|
||||
.from(notifCreds)
|
||||
.where(inArray(notifCreds.userId, userIds));
|
||||
}
|
||||
|
||||
export async function getUserIncidentsWithRelations(userId: number): Promise<any[]> {
|
||||
return await db.query.userIncidents.findMany({
|
||||
where: eq(userIncidents.userId, userId),
|
||||
with: {
|
||||
order: {
|
||||
with: {
|
||||
orderStatus: true,
|
||||
},
|
||||
},
|
||||
addedBy: true,
|
||||
},
|
||||
orderBy: desc(userIncidents.dateAdded),
|
||||
});
|
||||
}
|
||||
|
||||
export async function createUserIncident(
|
||||
userId: number,
|
||||
orderId: number | undefined,
|
||||
adminComment: string | undefined,
|
||||
adminUserId: number,
|
||||
negativityScore: number | undefined
|
||||
): Promise<any> {
|
||||
const [incident] = await db.insert(userIncidents)
|
||||
.values({
|
||||
userId,
|
||||
orderId,
|
||||
adminComment,
|
||||
addedBy: adminUserId,
|
||||
negativityScore,
|
||||
})
|
||||
.returning();
|
||||
|
||||
return incident;
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import { db } from '../db/db_index';
|
||||
import { vendorSnippets, deliverySlotInfo, productInfo, orders, orderItems, orderStatus } from '../db/schema';
|
||||
import { eq, and, inArray, gt, sql, asc } from 'drizzle-orm';
|
||||
|
||||
export async function checkVendorSnippetExists(snippetCode: string): Promise<boolean> {
|
||||
const existingSnippet = await db.query.vendorSnippets.findFirst({
|
||||
where: eq(vendorSnippets.snippetCode, snippetCode),
|
||||
});
|
||||
return !!existingSnippet;
|
||||
}
|
||||
|
||||
export async function getVendorSnippetById(id: number): Promise<any | null> {
|
||||
return await db.query.vendorSnippets.findFirst({
|
||||
where: eq(vendorSnippets.id, id),
|
||||
with: {
|
||||
slot: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getVendorSnippetByCode(snippetCode: string): Promise<any | null> {
|
||||
return await db.query.vendorSnippets.findFirst({
|
||||
where: eq(vendorSnippets.snippetCode, snippetCode),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getAllVendorSnippets(): Promise<any[]> {
|
||||
return await db.query.vendorSnippets.findMany({
|
||||
with: {
|
||||
slot: true,
|
||||
},
|
||||
orderBy: (vendorSnippets, { desc }) => [desc(vendorSnippets.createdAt)],
|
||||
});
|
||||
}
|
||||
|
||||
export interface CreateVendorSnippetInput {
|
||||
snippetCode: string;
|
||||
slotId?: number;
|
||||
productIds: number[];
|
||||
isPermanent: boolean;
|
||||
validTill?: Date;
|
||||
}
|
||||
|
||||
export async function createVendorSnippet(input: CreateVendorSnippetInput): Promise<any> {
|
||||
const [result] = await db.insert(vendorSnippets).values({
|
||||
snippetCode: input.snippetCode,
|
||||
slotId: input.slotId,
|
||||
productIds: input.productIds,
|
||||
isPermanent: input.isPermanent,
|
||||
validTill: input.validTill,
|
||||
}).returning();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function updateVendorSnippet(id: number, updates: any): Promise<any> {
|
||||
const [result] = await db.update(vendorSnippets)
|
||||
.set(updates)
|
||||
.where(eq(vendorSnippets.id, id))
|
||||
.returning();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function deleteVendorSnippet(id: number): Promise<void> {
|
||||
await db.delete(vendorSnippets)
|
||||
.where(eq(vendorSnippets.id, id));
|
||||
}
|
||||
|
||||
export async function getProductsByIds(productIds: number[]): Promise<any[]> {
|
||||
return await db.query.productInfo.findMany({
|
||||
where: inArray(productInfo.id, productIds),
|
||||
columns: { id: true, name: true },
|
||||
});
|
||||
}
|
||||
|
||||
export async function getVendorSlotById(slotId: number): Promise<any | null> {
|
||||
return await db.query.deliverySlotInfo.findFirst({
|
||||
where: eq(deliverySlotInfo.id, slotId),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getVendorOrdersBySlotId(slotId: number): Promise<any[]> {
|
||||
return await db.query.orders.findMany({
|
||||
where: eq(orders.slotId, slotId),
|
||||
with: {
|
||||
orderItems: {
|
||||
with: {
|
||||
product: {
|
||||
with: {
|
||||
unit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
orderStatus: true,
|
||||
user: true,
|
||||
slot: true,
|
||||
},
|
||||
orderBy: (orders, { desc }) => [desc(orders.createdAt)],
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOrderItemsByOrderIds(orderIds: number[]): Promise<any[]> {
|
||||
return await db.query.orderItems.findMany({
|
||||
where: inArray(orderItems.orderId, orderIds),
|
||||
with: {
|
||||
product: {
|
||||
with: {
|
||||
unit: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOrderStatusByOrderIds(orderIds: number[]): Promise<any[]> {
|
||||
return await db.query.orderStatus.findMany({
|
||||
where: inArray(orderStatus.orderId, orderIds),
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateVendorOrderItemPackaging(orderItemId: number, isPackaged: boolean, isPackageVerified: boolean): Promise<void> {
|
||||
await db.update(orderItems)
|
||||
.set({
|
||||
is_packaged: isPackaged,
|
||||
is_package_verified: isPackageVerified,
|
||||
})
|
||||
.where(eq(orderItems.id, orderItemId));
|
||||
}
|
||||
|
|
@ -5,3 +5,5 @@ export type { Banner } from './banner.types';
|
|||
export type { Complaint, ComplaintWithUser } from './complaint.types';
|
||||
export type { Constant, ConstantUpdateResult } from './const.types';
|
||||
export type { Coupon, ReservedCoupon, CouponValidationResult, UserMiniInfo } from './coupon.types';
|
||||
export type { Store } from './store.types';
|
||||
export type { StaffUser, StaffRole } from './staff-user.types';
|
||||
|
|
|
|||
17
packages/shared/types/staff-user.types.ts
Normal file
17
packages/shared/types/staff-user.types.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/**
|
||||
* Staff User Types
|
||||
* Central type definitions for staff user-related data structures
|
||||
*/
|
||||
|
||||
export interface StaffUser {
|
||||
id: number;
|
||||
name: string;
|
||||
password: string;
|
||||
staffRoleId: number;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface StaffRole {
|
||||
id: number;
|
||||
roleName: string;
|
||||
}
|
||||
14
packages/shared/types/store.types.ts
Normal file
14
packages/shared/types/store.types.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Store Types
|
||||
* Central type definitions for store-related data structures
|
||||
*/
|
||||
|
||||
export interface Store {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string | null;
|
||||
imageUrl: string | null;
|
||||
owner: number;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue