From 7064625cbac6c4d79f56dd887c1475d0ed679848 Mon Sep 17 00:00:00 2001 From: shafi54 <108669266+shafi-aviz@users.noreply.github.com> Date: Wed, 2 Sep 2026 21:44:05 +0530 Subject: [PATCH 01/30] enh --- AGENTS.md | 3 + apps/backend/index.ts | 3 - apps/backend/src/lib/axios.ts | 10 - apps/backend/src/lib/catch-async.ts | 11 - apps/backend/src/lib/delete-image.ts | 44 -- apps/backend/src/lib/disk-persisted-set.ts | 79 --- apps/backend/src/lib/event-queue.ts | 13 - apps/backend/src/lib/notif-service.ts | 245 ------- apps/backend/src/lib/redis-client.ts | 133 ---- apps/backend/src/lib/redisKeyGetters.ts | 3 - apps/backend/src/lib/signed-url-cache-old.ts | 263 -------- apps/backend/src/lib/signed-url-cache.ts | 24 - apps/backend/src/middleware/auth.ts | 60 -- apps/backend/src/postgresImporter.ts | 281 -------- apps/backend/src/sqliteImporter.ts | 12 - apps/backend/src/types/db.types.ts | 4 - change-log.txt | 472 +++++++++++++ packages/db_helper_postgres/index.ts | 18 +- .../src/admin-apis/product.ts | 139 ---- .../src/admin-apis/staff-user.ts | 10 - .../src/admin-apis/vendor-snippets.ts | 19 - .../src/common-apis/utils.ts | 19 - packages/db_helper_postgres/src/db/schema.ts | 46 +- packages/db_helper_postgres/src/db/types.ts | 45 -- .../src/helper_methods/banner.ts | 112 ---- .../src/helper_methods/complaint.ts | 74 -- .../src/helper_methods/const.ts | 29 - .../src/helper_methods/coupon.ts | 633 ------------------ .../src/helper_methods/order.ts | 259 ------- .../src/helper_methods/product.ts | 125 ---- .../src/helper_methods/slots.ts | 166 ----- .../src/helper_methods/staff-user.ts | 153 ----- .../src/helper_methods/store.ts | 151 ----- .../src/helper_methods/user.ts | 270 -------- .../src/helper_methods/vendor-snippets.ts | 130 ---- .../src/lib/automated-jobs.ts | 19 +- .../db_helper_postgres/src/user-apis/order.ts | 26 - .../src/user-apis/product.ts | 2 +- .../db_helper_postgres/src/user-apis/tags.ts | 28 - packages/db_helper_sqlite/index.ts | 22 - .../src/admin-apis/product.ts | 247 ------- .../src/admin-apis/staff-user.ts | 10 - .../src/admin-apis/vendor-snippets.ts | 20 - .../db_helper_sqlite/src/common-apis/utils.ts | 19 - packages/db_helper_sqlite/src/db/schema.ts | 46 +- packages/db_helper_sqlite/src/db/types.ts | 49 -- .../src/helper_methods/banner.ts | 114 ---- .../src/helper_methods/complaint.ts | 74 -- .../src/helper_methods/const.ts | 39 -- .../src/helper_methods/coupon.ts | 632 ----------------- .../src/helper_methods/order.ts | 269 -------- .../src/helper_methods/product.ts | 125 ---- .../src/helper_methods/slots.ts | 170 ----- .../src/helper_methods/staff-user.ts | 153 ----- .../src/helper_methods/store.ts | 150 ----- .../src/helper_methods/user.ts | 270 -------- .../src/helper_methods/vendor-snippets.ts | 130 ---- .../db_helper_sqlite/src/user-apis/order.ts | 26 - .../db_helper_sqlite/src/user-apis/tags.ts | 28 - packages/shared/index.ts | 2 - 60 files changed, 481 insertions(+), 6247 deletions(-) delete mode 100755 apps/backend/src/lib/axios.ts delete mode 100755 apps/backend/src/lib/catch-async.ts delete mode 100644 apps/backend/src/lib/delete-image.ts delete mode 100644 apps/backend/src/lib/disk-persisted-set.ts delete mode 100644 apps/backend/src/lib/event-queue.ts delete mode 100755 apps/backend/src/lib/notif-service.ts delete mode 100644 apps/backend/src/lib/redis-client.ts delete mode 100644 apps/backend/src/lib/redisKeyGetters.ts delete mode 100755 apps/backend/src/lib/signed-url-cache-old.ts delete mode 100644 apps/backend/src/lib/signed-url-cache.ts delete mode 100755 apps/backend/src/middleware/auth.ts delete mode 100644 apps/backend/src/postgresImporter.ts delete mode 100644 apps/backend/src/types/db.types.ts create mode 100644 change-log.txt delete mode 100644 packages/db_helper_postgres/src/common-apis/utils.ts delete mode 100755 packages/db_helper_postgres/src/db/types.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/banner.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/complaint.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/const.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/coupon.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/order.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/product.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/slots.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/staff-user.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/store.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/user.ts delete mode 100644 packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts delete mode 100644 packages/db_helper_postgres/src/user-apis/tags.ts delete mode 100644 packages/db_helper_sqlite/src/common-apis/utils.ts delete mode 100644 packages/db_helper_sqlite/src/db/types.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/banner.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/complaint.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/const.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/coupon.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/order.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/product.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/slots.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/staff-user.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/store.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/user.ts delete mode 100644 packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts delete mode 100644 packages/db_helper_sqlite/src/user-apis/tags.ts diff --git a/AGENTS.md b/AGENTS.md index ae98ac1..feff7cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -48,3 +48,6 @@ react-native. They are available in the common-ui as MyText, MyTextInput, MyTouc ## Important Notes - Don't do anything with git. Don't do git add or git commit. That will be managed entirely by the user + +## Change Log +- there should be a change log file. unless explicitly specified, assume change-log.txt as the name of the log file. Ensure the file exists. If it doesn't exist create it. Before changing any file which is not a md file or txt file write the change to that file exactly. Write the diff to it and then start making changes. for every change add the current time stamp. diff --git a/apps/backend/index.ts b/apps/backend/index.ts index 01e61d5..4d04cc4 100755 --- a/apps/backend/index.ts +++ b/apps/backend/index.ts @@ -2,15 +2,12 @@ import 'dotenv/config'; import { serve } from '@hono/node-server'; import initFunc from '@/src/lib/init'; import { createApp } from '@/src/app' -// import signedUrlCache from '@/src/lib/signed-url-cache'; import { seed } from '@/src/lib/seed'; import '@/src/jobs/jobs-index'; seed() initFunc() -// signedUrlCache.loadFromDisk(); // Disabled for Workers compatibility - const app = createApp() serve({ diff --git a/apps/backend/src/lib/axios.ts b/apps/backend/src/lib/axios.ts deleted file mode 100755 index 69cd048..0000000 --- a/apps/backend/src/lib/axios.ts +++ /dev/null @@ -1,10 +0,0 @@ -import axiosParent from "axios"; -import { getPhonePeBaseUrl } from "@/src/lib/env-exporter" - -export const phonepeAxios = axiosParent.create({ - baseURL: getPhonePeBaseUrl(), - timeout: 40000, - headers: { - "Content-Type": "application/x-www-form-urlencoded", - }, -}); diff --git a/apps/backend/src/lib/catch-async.ts b/apps/backend/src/lib/catch-async.ts deleted file mode 100755 index 878afc9..0000000 --- a/apps/backend/src/lib/catch-async.ts +++ /dev/null @@ -1,11 +0,0 @@ -// catchAsync is no longer needed with Hono -// Hono handles async errors automatically -// This file is kept for backward compatibility but should be removed in the future - -import { Context } from 'hono'; - -const catchAsync = (fn: (c: Context) => Promise) => { - return fn; -}; - -export default catchAsync; diff --git a/apps/backend/src/lib/delete-image.ts b/apps/backend/src/lib/delete-image.ts deleted file mode 100644 index 5d6c2b0..0000000 --- a/apps/backend/src/lib/delete-image.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { deleteImageUtil, getOriginalUrlFromSignedUrl } from "@/src/lib/s3-client" -import { getS3Url } from "@/src/lib/env-exporter" - -function extractS3Key(url: string): string | null { - try { - // Check if this is a signed URL first and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(url) || url; - - // Find the index of '.com/' in the URL - // const comIndex = originalUrl.indexOf(".com/"); - const baseUrlIndex = originalUrl.indexOf(getS3Url()); - - // If '.com/' is found, return everything after it - if (baseUrlIndex !== -1) { - return originalUrl.substring(baseUrlIndex + getS3Url().length); // +5 to skip '.com/' - } - } catch (error) { - console.error("Error extracting key from URL:", error); - } - - // Return null if the pattern isn't found or there was an error - return null; -} - - -export async function deleteS3Image(imageUrl: string) { - try { - // First check if this is a signed URL and get the original if it is - const originalUrl = getOriginalUrlFromSignedUrl(imageUrl) || imageUrl; - - const key = extractS3Key(originalUrl || ""); - - - if (!key) { - throw new Error("Invalid image URL format"); - } - const deleteS3 = await deleteImageUtil({keys: [key] }); - if (!deleteS3) { - throw new Error("Failed to delete image from S3"); - } - } catch (error) { - console.error("Error deleting image from S3:", error); - } -} diff --git a/apps/backend/src/lib/disk-persisted-set.ts b/apps/backend/src/lib/disk-persisted-set.ts deleted file mode 100644 index e0fccec..0000000 --- a/apps/backend/src/lib/disk-persisted-set.ts +++ /dev/null @@ -1,79 +0,0 @@ -// import fs from "fs"; -// import path from "path"; -// -// export class DiskPersistedSet { -// private set: Set; -// private readonly filePath: string; -// private dirty = false; -// -// constructor(filePath: string = "./persister") { -// this.filePath = path.resolve(filePath); -// -// // โœ… Ensure file exists -// if (!fs.existsSync(this.filePath)) { -// fs.writeFileSync(this.filePath, "", "utf8"); -// } -// -// // โœ… Load existing values from file -// const contents = fs.readFileSync(this.filePath, "utf8"); -// this.set = new Set( -// contents.split("\n").map(x => x.trim()).filter(x => x.length > 0) -// ); -// -// this.registerExitHandlers(); -// } -// -// private persist() { -// if (!this.dirty) return; -// fs.writeFileSync(this.filePath, Array.from(this.set).join("\n"), "utf8"); -// this.dirty = false; -// } -// -// private markDirty() { -// this.dirty = true; -// } -// -// add(value: string): void { -// if (!this.set.has(value)) { -// this.set.add(value); -// this.markDirty(); -// this.persist(); -// } -// } -// -// delete(value: string): void { -// if (this.set.delete(value)) { -// this.markDirty(); -// this.persist(); -// } -// } -// -// has(value: string): boolean { -// return this.set.has(value); -// } -// -// values(): string[] { -// return Array.from(this.set); -// } -// -// clear(): void { -// if (this.set.size > 0) { -// this.set.clear(); -// this.markDirty(); -// this.persist(); -// } -// } -// -// private registerExitHandlers() { -// const flush = () => this.persist(); -// -// process.on("exit", flush); -// process.on("SIGINT", () => { flush(); process.exit(); }); -// process.on("SIGTERM", () => { flush(); process.exit(); }); -// process.on("uncaughtException", (err) => { -// console.error("Uncaught exception. Flushing DiskPersistedSet:", err); -// flush(); -// process.exit(1); -// }); -// } -// } diff --git a/apps/backend/src/lib/event-queue.ts b/apps/backend/src/lib/event-queue.ts deleted file mode 100644 index d5baa8c..0000000 --- a/apps/backend/src/lib/event-queue.ts +++ /dev/null @@ -1,13 +0,0 @@ -// import redisClient from '@/src/lib/redis-client' - -export async function enqueue(queueName: string, eventData: any): Promise { - try { - const jsonData = JSON.stringify(eventData); - // const result = await redisClient.lPush(queueName, jsonData); - // return result > 0; - return false; - } catch (error) { - console.error('Event enqueue error:', error); - return false; - } -} \ No newline at end of file diff --git a/apps/backend/src/lib/notif-service.ts b/apps/backend/src/lib/notif-service.ts deleted file mode 100755 index 40baf9f..0000000 --- a/apps/backend/src/lib/notif-service.ts +++ /dev/null @@ -1,245 +0,0 @@ -import { sendPushNotificationsMany } from "@/src/lib/expo-service" -// import { usersTable, notifCredsTable, notificationTable } from "@/src/db/schema"; - -// Core notification dispatch methods (renamed for clarity) -export async function dispatchBulkNotification({ - userIds = [], - pushTokens = [], - title, - body, - data, -}: { - userIds?: number[]; - pushTokens?: string[]; - title: string; - body: string; - data?: Record; -}) { - try { - let allPushTokens: string[] = []; - - // Add provided pushTokens directly - if (pushTokens && pushTokens.length > 0) { - allPushTokens.push(...pushTokens.filter(Boolean)); - } - - // Fetch push tokens for userIds - // if (userIds && userIds.length > 0) { - // const tokensFromDb = await db.query.notifCredsTable.findMany({ - // where: inArray(notifCredsTable.userId, userIds), - // columns: { pushToken: true }, - // }); - // allPushTokens.push( - // ...tokensFromDb.map((t) => t.pushToken).filter(Boolean) - // ); - // } - - // Remove duplicates - allPushTokens = Array.from(new Set(allPushTokens)); - - if (allPushTokens.length === 0) { - console.warn(`No push tokens found for users: ${userIds?.join(", ")}`); - return; - } - - const messages = allPushTokens.map((pushToken) => ({ - pushToken, - title, - body, - data, - })); - await sendPushNotificationsMany(messages); - } catch (error) { - console.error("Error dispatching bulk notifications:", error); - throw new Error("Failed to dispatch notifications"); - } -} - -// Helper to dispatch notification to a single user or pushToken -export async function dispatchUserNotification({ - userId, - pushToken, - title, - body, - data, -}: { - userId?: number; - pushToken?: string; - title: string; - body: string; - data?: Record; -}) { - // Add entry to notificationTable if userId is provided - if (userId) { - try { - // await db.insert(notificationTable).values({ - // userId, - // title, - // body, - // payload: data, - // // addedOn will default to now - // }); - } catch (err) { - console.error('Failed to insert notificationTable entry:', err); - } - } - - await dispatchBulkNotification({ - userIds: userId ? [userId] : [], - pushTokens: pushToken ? [pushToken] : [], - title, - body, - data, - }); -} - -// ============================================================================= -// PURPOSE-SPECIFIC NOTIFICATION METHODS -// ============================================================================= - -// Order-related notifications -export const notifyOrderPlaced = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Placed Successfully! ๐ŸŽ‰', - body: `Your order #${orderId} has been placed and is being processed.`, - userId, - data: { orderId, type: 'order_placed' } - }); - -export const notifyOrderConfirmed = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Confirmed! โœ…', - body: `Your order #${orderId} has been confirmed and is being prepared.`, - userId, - data: { orderId, type: 'order_confirmed' } - }); - -export const notifyOrderReady = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Ready for Pickup! ๐Ÿฝ๏ธ', - body: `Your order #${orderId} is ready for pickup.`, - userId, - data: { orderId, type: 'order_ready' } - }); - -export const notifyOrderCancelled = (orderId: number, userId: number, reason?: string) => - dispatchUserNotification({ - title: 'Order Cancelled โŒ', - body: `Your order #${orderId} has been cancelled.${reason ? ` Reason: ${reason}` : ''}`, - userId, - data: { orderId, type: 'order_cancelled', reason } - }); - -// Delivery notifications -export const notifyDeliveryAssigned = (orderId: number, userId: number, deliveryPerson: string) => - dispatchUserNotification({ - title: 'Delivery Partner Assigned ๐Ÿšš', - body: `${deliveryPerson} will deliver your order #${orderId}.`, - userId, - data: { orderId, deliveryPerson, type: 'delivery_assigned' } - }); - -export const notifyOutForDelivery = (orderId: number, userId: number, eta: string) => - dispatchUserNotification({ - title: 'Out for Delivery! ๐Ÿ“ฆ', - body: `Your order #${orderId} is out for delivery. Expected arrival: ${eta}`, - userId, - data: { orderId, eta, type: 'out_for_delivery' } - }); - -export const notifyDelivered = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Order Delivered! ๐ŸŽŠ', - body: `Your order #${orderId} has been delivered successfully.`, - userId, - data: { orderId, type: 'delivered' } - }); - -// Payment notifications -export const notifyPaymentSuccess = (orderId: number, userId: number, amount: number) => - dispatchUserNotification({ - title: 'Payment Successful ๐Ÿ’ณ', - body: `Payment of โ‚น${amount} for order #${orderId} was successful.`, - userId, - data: { orderId, amount, type: 'payment_success' } - }); - -export const notifyPaymentFailed = (orderId: number, userId: number) => - dispatchUserNotification({ - title: 'Payment Failed โŒ', - body: `Payment for order #${orderId} failed. Please try again.`, - userId, - data: { orderId, type: 'payment_failed' } - }); - -export const notifyRefundProcessed = (orderId: number, userId: number, amount: number) => - dispatchUserNotification({ - title: 'Refund Processed ๐Ÿ’ฐ', - body: `Refund of โ‚น${amount} for order #${orderId} has been processed.`, - userId, - data: { orderId, amount, type: 'refund_processed' } - }); - -// Promotional notifications -export const notifyNewOffer = (userIds: number[], offerTitle: string, offerDetails: string) => - dispatchBulkNotification({ - title: `New Offer: ${offerTitle} ๐ŸŽ`, - body: offerDetails, - userIds, - data: { type: 'promotion', offerTitle } - }); - -export const notifyFlashSale = (userIds: number[], productName: string, discount: number) => - dispatchBulkNotification({ - title: 'Flash Sale! โšก', - body: `Get ${discount}% off on ${productName}. Limited time offer!`, - userIds, - data: { type: 'flash_sale', productName, discount } - }); - -export const notifyLoyaltyPointsEarned = (userId: number, points: number, reason: string) => - dispatchUserNotification({ - title: 'Loyalty Points Earned! โญ', - body: `You earned ${points} points for ${reason}.`, - userId, - data: { points, reason, type: 'loyalty_points' } - }); - -// Account notifications -export const notifyWelcome = (userId: number, userName: string) => - dispatchUserNotification({ - title: 'Welcome to Meat Farmer! ๐Ÿฅฉ', - body: `Hi ${userName}, welcome to our fresh meat delivery service.`, - userId, - data: { type: 'welcome' } - }); - -export const notifyPasswordReset = (userId: number) => - dispatchUserNotification({ - title: 'Password Reset Successful ๐Ÿ”', - body: 'Your password has been reset successfully.', - userId, - data: { type: 'password_reset' } - }); - -export const notifyAccountVerified = (userId: number) => - dispatchUserNotification({ - title: 'Account Verified! โœ…', - body: 'Your account has been successfully verified.', - userId, - data: { type: 'account_verified' } - }); - -// ============================================================================= -// BACKWARD COMPATIBILITY (DEPRECATED) -// ============================================================================= - -/** - * @deprecated Use notifyOrderConfirmed() or other purpose-specific methods instead - */ -export const sendNotifToSingleUser = dispatchUserNotification; - -/** - * @deprecated Use notifyNewOffer() or other purpose-specific methods instead - */ -export const sendNotifToManyUsers = dispatchBulkNotification; diff --git a/apps/backend/src/lib/redis-client.ts b/apps/backend/src/lib/redis-client.ts deleted file mode 100644 index fb8d855..0000000 --- a/apps/backend/src/lib/redis-client.ts +++ /dev/null @@ -1,133 +0,0 @@ -// import { createClient, RedisClientType } from 'redis'; -import { getRedisUrl } from '@/src/lib/env-exporter' - -const createClient = (args:any) => {} -class RedisClient { - // private client: RedisClientType; - // private subscriberClient: RedisClientType | null = null; - // private isConnected: boolean = false; - // - private client: any; - private subscriberrlient: any; - private isConnected: any = false; - - - constructor() { - this.client = createClient({ - url: getRedisUrl(), - }); - - // this.client.on('error', (err) => { - // console.error('Redis Client Error:', err); - // }); - // - // this.client.on('connect', () => { - // console.log('Redis Client Connected'); - // this.isConnected = true; - // }); - // - // this.client.on('disconnect', () => { - // console.log('Redis Client Disconnected'); - // this.isConnected = false; - // }); - // - // this.client.on('ready', () => { - // console.log('Redis Client Ready'); - // }); - // - // this.client.on('reconnecting', () => { - // console.log('Redis Client Reconnecting'); - // }); - - // Connect immediately (fire and forget) - // this.client.connect().catch((err) => { - // console.error('Failed to connect Redis:', err); - // }); - } - - async set(key: string, value: string, ttlSeconds?: number): Promise { - if (ttlSeconds) { - return await this.client.setEx(key, ttlSeconds, value); - } else { - return await this.client.set(key, value); - } - } - - async get(key: string): Promise { - return await this.client.get(key); - } - - async exists(key: string): Promise { - const result = await this.client.exists(key); - return result === 1; - } - - async delete(key: string): Promise { - return await this.client.del(key); - } - - async lPush(key: string, value: string): Promise { - return await this.client.lPush(key, value); - } - - async KEYS(pattern: string): Promise { - return await this.client.KEYS(pattern); - } - - async MGET(keys: string[]): Promise<(string | null)[]> { - return await this.client.MGET(keys); - } - - // Publish message to a channel - async publish(channel: string, message: string): Promise { - return await this.client.publish(channel, message); - } - - // Subscribe to a channel with callback - async subscribe(channel: string, callback: (message: string) => void): Promise { - // if (!this.subscriberClient) { - // this.subscriberClient = createClient({ - // url: redisUrl, - // }); - // - // this.subscriberClient.on('error', (err) => { - // console.error('Redis Subscriber Error:', err); - // }); - // - // this.subscriberClient.on('connect', () => { - // console.log('Redis Subscriber Connected'); - // }); - // - // await this.subscriberClient.connect(); - // } - // - // await this.subscriberClient.subscribe(channel, callback); - console.log(`Subscribed to channel: ${channel}`); - } - - // Unsubscribe from a channel - async unsubscribe(channel: string): Promise { - // if (this.subscriberClient) { - // await this.subscriberClient.unsubscribe(channel); - // console.log(`Unsubscribed from channel: ${channel}`); - // } - } - - disconnect(): void { - // if (this.isConnected) { - // this.client.disconnect(); - // } - // if (this.subscriberClient) { - // this.subscriberClient.disconnect(); - // } - } - - get isClientConnected(): boolean { - return this.isConnected; - } -} - -const redisClient = new RedisClient(); - -export default redisClient; -export { RedisClient }; diff --git a/apps/backend/src/lib/redisKeyGetters.ts b/apps/backend/src/lib/redisKeyGetters.ts deleted file mode 100644 index c72f66e..0000000 --- a/apps/backend/src/lib/redisKeyGetters.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function getSlotSequenceKey(slotId: number | string): string { - return `slot_sequence_${slotId}`; -} \ No newline at end of file diff --git a/apps/backend/src/lib/signed-url-cache-old.ts b/apps/backend/src/lib/signed-url-cache-old.ts deleted file mode 100755 index 0f4e98c..0000000 --- a/apps/backend/src/lib/signed-url-cache-old.ts +++ /dev/null @@ -1,263 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -const CACHE_FILE_PATH = path.join('.', 'assets', 'signed-url-cache.json'); - -// Interface for cache entries with TTL -interface CacheEntry { - value: string; - expiresAt: number; // Timestamp when this entry expires -} - -class SignedURLCache { - private originalToSignedCache: Map; - private signedToOriginalCache: Map; - - constructor() { - this.originalToSignedCache = new Map(); - this.signedToOriginalCache = new Map(); - - // Create cache directory if it doesn't exist - const cacheDir = path.dirname(CACHE_FILE_PATH); - if (!fs.existsSync(cacheDir)) { - console.log('creating the directory') - - fs.mkdirSync(cacheDir, { recursive: true }); - } - else { - console.log('the directory is already present') - - } - } - - /** - * Get a signed URL from the cache using an original URL as the key - */ - get(originalUrl: string): string | undefined { - const entry = this.originalToSignedCache.get(originalUrl); - - // If no entry or entry has expired, return undefined - if (!entry || Date.now() > entry.expiresAt) { - if (entry) { - // Remove expired entry - this.originalToSignedCache.delete(originalUrl); - // Also remove from reverse mapping if it exists - this.signedToOriginalCache.delete(entry.value); - } - return undefined; - } - - return entry.value; - } - - /** - * Get the original URL from the cache using a signed URL as the key - */ - getOriginalUrl(signedUrl: string): string | undefined { - const entry = this.signedToOriginalCache.get(signedUrl); - - // If no entry or entry has expired, return undefined - if (!entry || Date.now() > entry.expiresAt) { - if (entry) { - // Remove expired entry - this.signedToOriginalCache.delete(signedUrl); - // Also remove from primary mapping if it exists - this.originalToSignedCache.delete(entry.value); - } - return undefined; - } - - return entry.value; - } - - /** - * Set a value in the cache with a TTL (Time To Live) - * @param originalUrl The original S3 URL - * @param signedUrl The signed URL - * @param ttlMs Time to live in milliseconds (default: 3 days) - */ - set(originalUrl: string, signedUrl: string, ttlMs: number = 259200000): void { - const expiresAt = Date.now() + ttlMs; - - const entry: CacheEntry = { - value: signedUrl, - expiresAt - }; - - const reverseEntry: CacheEntry = { - value: originalUrl, - expiresAt - }; - - this.originalToSignedCache.set(originalUrl, entry); - this.signedToOriginalCache.set(signedUrl, reverseEntry); - } - - has(originalUrl: string): boolean { - const entry = this.originalToSignedCache.get(originalUrl); - - // Entry exists and hasn't expired - return !!entry && Date.now() <= entry.expiresAt; - } - - hasSignedUrl(signedUrl: string): boolean { - const entry = this.signedToOriginalCache.get(signedUrl); - - // Entry exists and hasn't expired - return !!entry && Date.now() <= entry.expiresAt; - } - - clear(): void { - this.originalToSignedCache.clear(); - this.signedToOriginalCache.clear(); - this.saveToDisk(); - } - - /** - * Remove all expired entries from the cache - * @returns The number of expired entries that were removed - */ - clearExpired(): number { - const now = Date.now(); - let removedCount = 0; - - // Clear expired entries from original to signed cache - for (const [originalUrl, entry] of this.originalToSignedCache.entries()) { - if (now > entry.expiresAt) { - this.originalToSignedCache.delete(originalUrl); - removedCount++; - } - } - - // Clear expired entries from signed to original cache - for (const [signedUrl, entry] of this.signedToOriginalCache.entries()) { - if (now > entry.expiresAt) { - this.signedToOriginalCache.delete(signedUrl); - // No need to increment removedCount as we've already counted these in the first loop - } - } - - if (removedCount > 0) { - console.log(`SignedURLCache: Cleared ${removedCount} expired entries`); - } - - return removedCount; - } - - /** - * Save the cache to disk - */ - saveToDisk(): void { - try { - // Remove expired entries before saving - const removedCount = this.clearExpired(); - - // Convert Maps to serializable objects - const serializedOriginalToSigned: Record = {}; - const serializedSignedToOriginal: Record = {}; - - for (const [originalUrl, entry] of this.originalToSignedCache.entries()) { - serializedOriginalToSigned[originalUrl] = { - value: entry.value, - expiresAt: entry.expiresAt - }; - } - - for (const [signedUrl, entry] of this.signedToOriginalCache.entries()) { - serializedSignedToOriginal[signedUrl] = { - value: entry.value, - expiresAt: entry.expiresAt - }; - } - - const serializedCache = { - originalToSigned: serializedOriginalToSigned, - signedToOriginal: serializedSignedToOriginal - }; - - // Write to file - fs.writeFileSync( - CACHE_FILE_PATH, - JSON.stringify(serializedCache), - 'utf8' - ); - - console.log(`SignedURLCache: Saved ${this.originalToSignedCache.size} entries to disk`); - } catch (error) { - console.error('Error saving SignedURLCache to disk:', error); - } - } - - /** - * Load the cache from disk - */ - loadFromDisk(): void { - try { - if (fs.existsSync(CACHE_FILE_PATH)) { - // Read from file - const data = fs.readFileSync(CACHE_FILE_PATH, 'utf8'); - - // Parse the data - const parsedData = JSON.parse(data) as { - originalToSigned: Record, - signedToOriginal: Record - }; - - // Only load entries that haven't expired yet - const now = Date.now(); - let loadedCount = 0; - let expiredCount = 0; - - // Load original to signed mappings - if (parsedData.originalToSigned) { - for (const [originalUrl, entry] of Object.entries(parsedData.originalToSigned)) { - if (now <= entry.expiresAt) { - this.originalToSignedCache.set(originalUrl, entry); - loadedCount++; - } else { - expiredCount++; - } - } - } - - // Load signed to original mappings - if (parsedData.signedToOriginal) { - for (const [signedUrl, entry] of Object.entries(parsedData.signedToOriginal)) { - if (now <= entry.expiresAt) { - this.signedToOriginalCache.set(signedUrl, entry); - // Don't increment loadedCount as these are pairs of what we already counted - } else { - // Don't increment expiredCount as these are pairs of what we already counted - } - } - } - - console.log(`SignedURLCache: Loaded ${loadedCount} valid entries from disk (skipped ${expiredCount} expired entries)`); - } else { - console.log('SignedURLCache: No cache file found, starting with empty cache'); - } - } catch (error) { - console.error('Error loading SignedURLCache from disk:', error); - // Start with empty caches if loading fails - this.originalToSignedCache = new Map(); - this.signedToOriginalCache = new Map(); - } - } -} - -// Create a singleton instance to be used throughout the application -const signedUrlCache = new SignedURLCache(); - -process.on('SIGINT', () => { - console.log('SignedURLCache: Saving cache before shutdown...'); - signedUrlCache.saveToDisk(); - process.exit(0); -}); - -process.on('SIGTERM', () => { - console.log('SignedURLCache: Saving cache before shutdown...'); - signedUrlCache.saveToDisk(); - process.exit(0); -}); - -export default signedUrlCache; \ No newline at end of file diff --git a/apps/backend/src/lib/signed-url-cache.ts b/apps/backend/src/lib/signed-url-cache.ts deleted file mode 100644 index 69a391a..0000000 --- a/apps/backend/src/lib/signed-url-cache.ts +++ /dev/null @@ -1,24 +0,0 @@ -// SIGNED URL CACHE - DISABLED -// This file has been disabled to make the backend compatible with Cloudflare Workers. -// File system operations are not available in the Workers environment. -// -// To re-enable caching, migrate to Cloudflare R2 or another object storage solution. -// Original file saved as: signed-url-cache-old.ts -// -// Impact of disabling: -// - S3 signed URLs are generated fresh on every request -// - Increased AWS API calls (higher costs) -// - Slightly slower image loading -// - No file system dependencies (Workers-compatible) - -export default { - get: () => undefined, - set: () => {}, - getOriginalUrl: () => undefined, - has: () => false, - hasSignedUrl: () => false, - clear: () => {}, - clearExpired: () => 0, - saveToDisk: () => {}, - loadFromDisk: () => {}, -}; diff --git a/apps/backend/src/middleware/auth.ts b/apps/backend/src/middleware/auth.ts deleted file mode 100755 index d34c035..0000000 --- a/apps/backend/src/middleware/auth.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Context, Next } from 'hono'; -import { jwtVerify, errors } from 'jose'; -import { ApiError } from '@/src/lib/api-error' -import { getEncodedJwtSecret } from '@/src/lib/env-exporter'; - -export const verifyToken = async (c: Context, next: Next) => { - try { - // Get token from Authorization header - const authHeader = c.req.header('authorization'); - - - if (!authHeader || !authHeader.startsWith('Bearer ')) { - throw new ApiError('Access denied. No token provided', 401); - } - - const token = authHeader.split(' ')[1]; - - if (!token) { - throw new ApiError('Access denied. Invalid token format', 401); - } - - // Verify token - const { payload } = await jwtVerify(token, getEncodedJwtSecret()); - - - // Add user info to context - c.set('user', payload); - - await next(); - } catch (error) { - if (error instanceof errors.JOSEError) { - throw new ApiError('Invalid Auth Credentials', 401); - } else { - throw error; - } - } -}; - -export const requireRole = (roles: string[]) => { - return async (c: Context, next: Next) => { - try { - const user = c.get('user'); - if (!user) { - throw new ApiError('Authentication required', 401); - } - - // Check if user has any of the required roles - const userRoles = user.roles || []; - const hasPermission = roles.some(role => userRoles.includes(role)); - - if (!hasPermission) { - throw new ApiError('Access denied. Insufficient permissions', 403); - } - - await next(); - } catch (error) { - throw error; - } - }; -}; diff --git a/apps/backend/src/postgresImporter.ts b/apps/backend/src/postgresImporter.ts deleted file mode 100644 index 3645f61..0000000 --- a/apps/backend/src/postgresImporter.ts +++ /dev/null @@ -1,281 +0,0 @@ -// // Postgres Importer - Intermediate layer to avoid direct postgresService imports in dbService -// // This file re-exports everything from postgresService -// -// // Re-export database connection -// export { db } from 'postgresService' -// -// // Re-export all schema exports -// export * from 'postgresService' -// -// // Re-export all helper methods from postgresService -// export { -// // Admin - Banner -// getBanners, -// getBannerById, -// createBanner, -// updateBanner, -// deleteBanner, -// // Admin - Complaint -// getComplaints, -// resolveComplaint, -// // Admin - Constants -// getAllConstants, -// upsertConstants, -// // Admin - Coupon -// getAllCoupons, -// getCouponById, -// invalidateCoupon, -// validateCoupon, -// getReservedCoupons, -// getUsersForCoupon, -// createCouponWithRelations, -// updateCouponWithRelations, -// generateCancellationCoupon, -// createReservedCouponWithProducts, -// createCouponForUser, -// checkUsersExist, -// checkCouponExists, -// checkReservedCouponExists, -// getOrderWithUser, -// // Admin - Order -// updateOrderNotes, -// getOrderDetails, -// updateOrderPackaged, -// updateOrderDelivered, -// updateOrderItemPackaging, -// removeDeliveryCharge, -// getSlotOrders, -// updateAddressCoords, -// getAllOrders, -// rebalanceSlots, -// cancelOrder, -// deleteOrderById, -// // Admin - Product -// getAllProducts, -// getProductById, -// deleteProduct, -// createProduct, -// updateProduct, -// checkProductExistsByName, -// checkUnitExists, -// getProductImagesById, -// createSpecialDealsForProduct, -// updateProductDeals, -// replaceProductTags, -// toggleProductOutOfStock, -// updateSlotProducts, -// getSlotsProductIds, -// getAllUnits, -// getAllProductTags, -// getAllProductTagInfos, -// getProductTagInfoById, -// createProductTag, -// getProductTagById, -// updateProductTag, -// deleteProductTag, -// checkProductTagExistsByName, -// getProductReviews, -// respondToReview, -// getAllProductGroups, -// createProductGroup, -// updateProductGroup, -// deleteProductGroup, -// addProductToGroup, -// removeProductFromGroup, -// updateProductPrices, -// // Admin - Slots -// getActiveSlotsWithProducts, -// getActiveSlots, -// getSlotsAfterDate, -// getSlotByIdWithRelations, -// createSlotWithRelations, -// updateSlotWithRelations, -// deleteSlotById, -// updateSlotCapacity, -// getSlotDeliverySequence, -// updateSlotDeliverySequence, -// // Admin - Staff User -// getStaffUserByName, -// getStaffUserById, -// getAllStaff, -// getAllUsers, -// getUserWithDetails, -// updateUserSuspensionStatus, -// checkStaffUserExists, -// checkStaffRoleExists, -// createStaffUser, -// getAllRoles, -// // Admin - Store -// getAllStores, -// getStoreById, -// createStore, -// updateStore, -// deleteStore, -// // Admin - User -// createUserByMobile, -// getUserByMobile, -// getUnresolvedComplaintsCount, -// getAllUsersWithFilters, -// getOrderCountsByUserIds, -// getLastOrdersByUserIds, -// getSuspensionStatusesByUserIds, -// getUserBasicInfo, -// getUserSuspensionStatus, -// getUserOrders, -// getOrderStatusesByOrderIds, -// getItemCountsByOrderIds, -// upsertUserSuspension, -// searchUsers, -// getAllNotifCreds, -// getAllUnloggedTokens, -// getNotifTokensByUserIds, -// getUserIncidentsWithRelations, -// createUserIncident, -// // Admin - Vendor Snippets -// checkVendorSnippetExists, -// getVendorSnippetById, -// getVendorSnippetByCode, -// getAllVendorSnippets, -// createVendorSnippet, -// updateVendorSnippet, -// deleteVendorSnippet, -// getProductsByIds, -// getVendorSlotById, -// getVendorOrdersBySlotId, -// getOrderItemsByOrderIds, -// getOrderStatusByOrderIds, -// updateVendorOrderItemPackaging, -// getVendorOrders, -// // User - Address -// getUserDefaultAddress, -// getUserAddresses, -// getUserAddressById, -// clearUserDefaultAddress, -// createUserAddress, -// updateUserAddress, -// deleteUserAddress, -// hasOngoingOrdersForAddress, -// // User - Banners -// getUserActiveBanners, -// // User - Complaint -// getUserComplaints, -// createUserComplaint, -// // User - Stores -// getUserStoreSummaries, -// getUserStoreDetail, -// // User - Product -// getUserProductDetailById, -// getUserProductReviews, -// getUserProductByIdBasic, -// createUserProductReview, -// getAllProductsWithUnits, -// type ProductSummaryData, -// // User - Slots -// getUserActiveSlotsList, -// getUserProductAvailability, -// // User - Payments -// getUserPaymentOrderById, -// getUserPaymentByOrderId, -// getUserPaymentByMerchantOrderId, -// updateUserPaymentSuccess, -// updateUserOrderPaymentStatus, -// markUserPaymentFailed, -// // User - Auth -// getUserAuthByEmail, -// getUserAuthByMobile, -// getUserAuthById, -// getUserAuthCreds, -// getUserAuthDetails, -// isUserSuspended, -// createUserAuthWithCreds, -// createUserAuthWithMobile, -// upsertUserAuthPassword, -// deleteUserAuthAccount, -// // UV API helpers -// createUserWithProfile, -// getUserDetailsByUserId, -// updateUserProfile, -// // User - Coupon -// getUserActiveCouponsWithRelations, -// getUserAllCouponsWithRelations, -// getUserReservedCouponByCode, -// redeemUserReservedCoupon, -// // User - Profile -// getUserProfileById, -// getUserProfileDetailById, -// getUserWithCreds, -// getUserNotifCred, -// upsertUserNotifCred, -// deleteUserUnloggedToken, -// getUserUnloggedToken, -// upsertUserUnloggedToken, -// // User - Order -// validateAndGetUserCoupon, -// applyDiscountToUserOrder, -// getUserAddressByIdAndUser, -// getOrderProductById, -// checkUserSuspended, -// getUserSlotCapacityStatus, -// placeUserOrderTransaction, -// deleteUserCartItemsForOrder, -// recordUserCouponUsage, -// getUserOrdersWithRelations, -// getUserOrderCount, -// getUserOrderByIdWithRelations, -// getUserCouponUsageForOrder, -// getUserOrderBasic, -// cancelUserOrderTransaction, -// updateUserOrderNotes, -// // Store Helpers -// getAllBannersForCache, -// getAllProductsForCache, -// getAllStoresForCache, -// getAllDeliverySlotsForCache, -// getAllSpecialDealsForCache, -// getAllProductTagsForCache, -// getAllTagsForCache, -// getAllTagProductMappings, -// getAllSlotsWithProductsForCache, -// getAllUserNegativityScores, -// getUserNegativityScore, -// type BannerData, -// type ProductBasicData, -// type StoreBasicData, -// type DeliverySlotData, -// type SpecialDealData, -// type ProductTagData, -// type TagBasicData, -// type TagProductMapping, -// type SlotWithProductsData, -// type UserNegativityData, -// // Automated Jobs -// toggleFlashDeliveryForItems, -// toggleKeyVal, -// getAllKeyValStore, -// // Post-order handler helpers -// getOrdersByIdsWithFullData, -// getOrderByIdWithFullData, -// type OrderWithFullData, -// type OrderWithCancellationData, -// // Common API helpers -// getSuspendedProductIds, -// getNextDeliveryDateWithCapacity, -// getStoresSummary, -// healthCheck, -// // Delete orders helper -// deleteOrdersWithRelations, -// // Seed helpers -// seedUnits, -// seedStaffRoles, -// seedStaffPermissions, -// seedRolePermissions, -// seedKeyValStore, -// type UnitSeedData, -// type RolePermissionAssignment, -// type KeyValSeedData, -// type StaffRoleName, -// type StaffPermissionName, -// // Upload URL Helpers -// createUploadUrlStatus, -// claimUploadUrlStatus, -// } from 'postgresService' diff --git a/apps/backend/src/sqliteImporter.ts b/apps/backend/src/sqliteImporter.ts index 60745fc..070055a 100644 --- a/apps/backend/src/sqliteImporter.ts +++ b/apps/backend/src/sqliteImporter.ts @@ -69,14 +69,10 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForSku, - updateSkuDeals, replaceProductTags, replaceTagProducts, - mergeSkus, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -91,8 +87,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, // Merge duplicate products into multi-SKU products mergeDuplicateProducts, @@ -114,7 +108,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -156,8 +149,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, // User - Address @@ -218,7 +209,6 @@ export { getUserProfileById, getUserProfileDetailById, getUserWithCreds, - getUserNotifCred, upsertUserNotifCred, deleteUserUnloggedToken, getUserUnloggedToken, @@ -269,8 +259,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, // Common API helpers getSuspendedSkuIds, getNextDeliveryDateWithCapacity, diff --git a/apps/backend/src/types/db.types.ts b/apps/backend/src/types/db.types.ts deleted file mode 100644 index 50dbdf9..0000000 --- a/apps/backend/src/types/db.types.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Database Types - Re-exports from shared package -// Central type definitions for backend database operations - -export type { Banner } from '@packages/shared'; diff --git a/change-log.txt b/change-log.txt new file mode 100644 index 0000000..2892936 --- /dev/null +++ b/change-log.txt @@ -0,0 +1,472 @@ +=== Session change log === +Session started: 2026-09-02 +Rule: Before changing any file that is not a .md or .txt file, write the current timestamp followed by the exact diff here first, then make the change. + +(no file changes made yet in this session) + +[2026-09-02 20:34:48] DELETE dead files per DEAD_CODE_REPORT.md ยง1 (28 files): + +Removed in packages/db_helper_sqlite/src/helper_methods/: +- banner.ts (entire file โ€” legacy duplicate, never imported) +- complaint.ts (entire file โ€” legacy duplicate, never imported) +- const.ts (entire file โ€” legacy duplicate, never imported) +- coupon.ts (entire file โ€” legacy duplicate, never imported) +- order.ts (entire file โ€” legacy duplicate, never imported) +- product.ts (entire file โ€” legacy duplicate, never imported) +- slots.ts (entire file โ€” legacy duplicate, never imported) +- staff-user.ts (entire file โ€” legacy duplicate, never imported) +- store.ts (entire file โ€” legacy duplicate, never imported) +- user.ts (entire file โ€” legacy duplicate, never imported) +- vendor-snippets.ts (entire file โ€” legacy duplicate, never imported) + +Removed in packages/db_helper_postgres/src/helper_methods/: (same 11 files as above) +- banner.ts, complaint.ts, const.ts, coupon.ts, order.ts, product.ts, slots.ts, staff-user.ts, store.ts, user.ts, vendor-snippets.ts (entire files โ€” legacy duplicates, never imported) + +Removed in both packages (db_helper_sqlite + db_helper_postgres): +- src/user-apis/tags.ts (entire file โ€” imported by no one) +- src/common-apis/utils.ts (entire file โ€” imported by no one; formatDate/generateCode/calculateDiscount unreferenced) +- src/db/types.ts (entire file โ€” imported by no one) + +Kept: helper_methods/upload-url.ts (alive โ€” re-exported by index.ts, used by backend). + +Command: rm + +[2026-09-02 20:38:02] COMPLETED deletion of 28 dead files (DEAD_CODE_REPORT.md ยง1). +Verification: +- grep for 'helper_methods|user-apis/tags|common-apis/utils|db/types' across both packages + apps/backend: only remaining match is the live import of helper_methods/upload-url.ts in both index.ts files. +- tsc --noEmit on both packages reports pre-existing errors only (in untouched live files: src/db/seed.ts, src/user-apis/order.ts, src/user-apis/product.ts, src/admin-apis/complaint.ts, src/admin-apis/product.ts). No errors reference any deleted module. + +[2026-09-02 20:45:45] DELETE dead exported methods per DEAD_CODE_REPORT.md ยง2a (8 symbols, both packages). + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed getAllUnits (lines 650-657): + export async function getAllUnits(): Promise { + const allUnits = await db.query.units.findMany({ + orderBy: units.shortNotation, + }) + return allUnits.map(mapUnit) + } +- removed addProductToGroup (lines 1032-1034): + export async function addProductToGroup(groupId: number, productId: number): Promise { + await db.insert(productGroupMembership).values({ groupId, productId }) + } +- removed removeProductFromGroup (lines 1036-1042): + export async function removeProductFromGroup(groupId: number, productId: number): Promise { + await db.delete(productGroupMembership) + .where(and( + eq(productGroupMembership.groupId, groupId), + eq(productGroupMembership.productId, productId) + )) + } + +=== packages/db_helper_postgres/src/admin-apis/product.ts === +- identical removals (getAllUnits at 232-239, addProductToGroup at 496-498, removeProductFromGroup at 500-506) + +=== packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts === +- removed getOrderItemsByOrderIds (lines 196-209): + export async function getOrderItemsByOrderIds(orderIds: number[]) { + return await db.query.orderItems.findMany({ + where: inArray(orderItems.orderId, orderIds), + with: { sku: { with: { product: true, features: true } } }, + }) + } +- removed getOrderStatusByOrderIds (lines 210-214): + export async function getOrderStatusByOrderIds(orderIds: number[]) { + return await db.query.orderStatus.findMany({ + where: inArray(orderStatus.orderId, orderIds), + }) + } + +=== packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts === +- removed getOrderItemsByOrderIds (lines 188-200) โ€” same signature; body joins product.unit instead of sku: + export async function getOrderItemsByOrderIds(orderIds: number[]) { + return await db.query.orderItems.findMany({ + where: inArray(orderItems.orderId, orderIds), + with: { product: { with: { unit: true } } }, + }) + } +- removed getOrderStatusByOrderIds (lines 201-205) โ€” identical to sqlite + +=== packages/db_helper_sqlite/src/admin-apis/staff-user.ts + packages/db_helper_postgres/src/admin-apis/staff-user.ts === +- removed updateUserSuspensionStatus (lines 101-109 in both) โ€” duplicate of live upsertUserSuspension: + export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { + await db + .insert(userDetails) + .values({ userId, isSuspended }) + .onConflictDoUpdate({ + target: userDetails.userId, + set: { isSuspended }, + }) + } + +=== packages/db_helper_sqlite/src/user-apis/user.ts + packages/db_helper_postgres/src/user-apis/user.ts === +- removed getUserWithCreds (lines 15-28 in both): + export async function getUserWithCreds(userId: number) { + const result = await db + .select() + .from(users) + .leftJoin(userCreds, eq(users.id, userCreds.userId)) + .where(eq(users.id, userId)) + .limit(1) + if (result.length === 0) return null + return { user: result[0].users, creds: result[0].user_creds } + } + +=== packages/db_helper_sqlite/src/user-apis/auth.ts + packages/db_helper_postgres/src/user-apis/auth.ts === +- removed createUserWithCreds (lines 145-168 in both): + export async function createUserWithCreds(input: { name; email; mobile; hashedPassword }) { + return db.transaction(async (tx) => { ...insert user, insert userCreds, return user... }) + } + +=== packages/db_helper_sqlite/index.ts (export list cleanup) === +- removed line: getAllUnits, +- removed lines: addProductToGroup, + removeProductFromGroup, +- removed lines: getOrderItemsByOrderIds, + getOrderStatusByOrderIds, +- removed line: updateUserSuspensionStatus, +- removed line: getUserWithCreds as getUserWithCreds, +- removed line: createUserWithCreds as createUserAuthWithCreds, + +=== packages/db_helper_postgres/index.ts (export list cleanup) === +- same 8 export-line removals as sqlite index.ts + +=== apps/backend/src/sqliteImporter.ts (re-export chain cleanup) === +- removed line: getAllUnits, +- removed lines: addProductToGroup, + removeProductFromGroup, +- removed lines: getOrderItemsByOrderIds, + getOrderStatusByOrderIds, +- removed line: updateUserSuspensionStatus, +- removed line: createUserAuthWithCreds, +- removed line: getUserWithCreds, + +Note: apps/backend/src/dbService.ts uses `export * from '@/src/sqliteImporter'` โ€” no per-symbol change needed there. +Any imports left unused by these deletions (e.g. productGroupMembership, inArray, userCreds) will be removed in a follow-up entry if tsc/grep confirms they are unused. + +[2026-09-02 20:49:23] FOLLOW-UP: remove imports/orphans left unused by ยง2a deletions. + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed now-unused local type + mapper (only consumer was deleted getAllUnits): + line 103: type UnitRow = InferSelectModel + line 117: const mapUnit = (unit: UnitRow): AdminUnit => ({ + id: unit.id, + shortNotation: unit.shortNotation, + fullName: unit.fullName, + }) + (postgres product.ts keeps mapUnit โ€” still used there at lines 111, 142) + +=== packages/db_helper_sqlite/src/user-apis/user.ts === +- import line 2: removed 'userCreds' from import { notifCreds, unloggedUserTokens, userCreds, userDetails, users } from '../db/schema' + +=== packages/db_helper_postgres/src/user-apis/user.ts === +- same import fix as sqlite user.ts + +[2026-09-02 20:51:30] COMPLETED ยง2a deletion (8 dead exported methods, both packages + re-export chains). +Removed: getAllUnits, addProductToGroup, removeProductFromGroup, getOrderItemsByOrderIds, getOrderStatusByOrderIds, updateUserSuspensionStatus, getUserWithCreds, createUserWithCreds (+ orphaned mapUnit/UnitRow in sqlite product.ts, orphaned userCreds imports in both user.ts). +Verification: +- grep across packages + apps: zero live references remain (only commented lines in postgresImporter.ts). +- tsc --noEmit: sqlite = 4 errors (same as pre-change baseline), postgres = 40 errors (all pre-existing drizzle inference mismatches in untouched files), backend = 17 errors (all pre-existing, in untouched files). No error references any deleted symbol. + +[2026-09-02 20:54:32] DELETE ยง2b/ยง2c dead exports + PARITY RENAME + RESTORE two methods for future use. + +--- DELETIONS, packages/db_helper_sqlite/src/admin-apis/product.ts --- +- removed createSpecialDealsForSku (lines 1124-1145): inserts specialDeals rows keyed by skuId, returns mapped AdminSpecialDeal[] +- removed updateSkuDeals (lines 1147-1241): diff-based sync of specialDeals for a skuId (add/remove/update validTill) +- removed mergeSkus (lines 1243-1366, end of file): merges fromSkuId into toSkuId across orderItems, specialDeals, cartItems, couponApplicableProducts, JSON skuIds columns (deliverySlotInfo, homeBanners, coupons, reservedCoupons, vendorSnippets), keyValStore popularItems, then deletes skuFeatures + sku + orphaned product + +--- DELETIONS, packages/db_helper_postgres/src/admin-apis/product.ts --- +- removed toggleProductOutOfStock (lines 181-203): flips productInfo.isOutOfStock +- removed createSpecialDealsForProduct (lines 677-698): postgres counterpart of createSpecialDealsForSku (keyed by productId) +- removed updateProductDeals (lines 700-775): postgres counterpart of updateSkuDeals + +--- DELETION, packages/db_helper_postgres/src/lib/automated-jobs.ts --- +- removed toggleFlashDeliveryForItems (lines 10-16): bulk productInfo.isFlashAvailable update +- import line 2: removed 'productInfo' from import { productInfo, keyValStore } from '../db/schema' +- import line 3: removed 'inArray' from import { inArray, eq } from 'drizzle-orm' + +--- EXPORT LIST, packages/db_helper_sqlite/index.ts --- +- removed lines: createSpecialDealsForSku, + updateSkuDeals, + mergeSkus, + +--- EXPORT LIST, packages/db_helper_postgres/index.ts --- +- removed lines: createSpecialDealsForProduct, + updateProductDeals, + toggleProductOutOfStock, +- removed line: toggleFlashDeliveryForItems, + +--- EXPORT LIST, apps/backend/src/sqliteImporter.ts --- +- removed lines: createSpecialDealsForSku, + updateSkuDeals, + mergeSkus, + +--- PARITY RENAME, packages/db_helper_postgres/src/user-apis/product.ts --- +- renamed getSuspendedProductIds -> getSuspendedSkuIds (backend calls getSuspendedSkuIds; sqlite exports that name). + NOTE: implementation unchanged (queries productInfo.isSuspended) โ€” postgres schema has no sku/marketStats model, so this is the closest postgres equivalent of the sqlite query on productMarketStats.skuId. +- packages/db_helper_postgres/index.ts: export line getSuspendedProductIds, -> getSuspendedSkuIds, + +--- RESTORE (user request: needed in future), both packages --- +packages/db_helper_sqlite/src/user-apis/user.ts + packages/db_helper_postgres/src/user-apis/user.ts: +- re-added getUserWithCreds: + export async function getUserWithCreds(userId: number) { + const result = await db + .select() + .from(users) + .leftJoin(userCreds, eq(users.id, userCreds.userId)) + .where(eq(users.id, userId)) + .limit(1) + + if (result.length === 0) return null + return { + user: result[0].users, + creds: result[0].user_creds, + } + } +- re-added 'userCreds' to the schema import line in both files. + +packages/db_helper_sqlite/src/user-apis/auth.ts + packages/db_helper_postgres/src/user-apis/auth.ts: +- re-added createUserWithCreds: + export async function createUserWithCreds(input: { + name: string + email: string + mobile: string + hashedPassword: string + }) { + return db.transaction(async (tx) => { + const [user] = await tx.insert(users).values({ + name: input.name, + email: input.email, + mobile: input.mobile, + }).returning() + + await tx.insert(userCreds).values({ + userId: user.id, + userPassword: input.hashedPassword, + }) + + return user + }) + } + +packages/db_helper_sqlite/index.ts + packages/db_helper_postgres/index.ts: +- re-added export lines: createUserWithCreds as createUserAuthWithCreds, (user auth block) + getUserWithCreds as getUserWithCreds, (user profile block) + +apps/backend/src/sqliteImporter.ts: +- re-added re-export lines: createUserAuthWithCreds, and getUserWithCreds, + +[2026-09-02 20:58:19] FOLLOW-UP: remove orphaned CreateSpecialDealInput (only consumers were the deleted deal functions; zero usages repo-wide). + +=== packages/db_helper_sqlite/src/admin-apis/product.ts === +- removed (line ~1118): + export interface CreateSpecialDealInput { + quantity: number + price: number + validTill: string | Date + } + +=== packages/db_helper_postgres/src/admin-apis/product.ts === +- removed identical interface (line ~647) + +[2026-09-02 20:59:28] COMPLETED ยง2b/ยง2c deletions + parity rename + restoration of createUserWithCreds/getUserWithCreds. +Verification: +- grep: zero references to createSpecialDealsForSku, updateSkuDeals, mergeSkus, createSpecialDealsForProduct, updateProductDeals, toggleProductOutOfStock, toggleFlashDeliveryForItems, getSuspendedProductIds (only commented postgresImporter.ts lines excluded from check). +- Restored methods present in both packages' src + index.ts + backend sqliteImporter.ts. +- Postgres now exports getSuspendedSkuIds (renamed from getSuspendedProductIds) matching sqlite/backend usage. +- tsc --noEmit baselines unchanged: sqlite 4, postgres 40, backend 17 errors โ€” all pre-existing, none reference touched symbols. + +[2026-09-02 21:01:52] ยง3 EXPORT-ONLY DEAD cleanup: remove from index.ts (implementations kept), delete non-exported dead types. + +=== packages/db_helper_sqlite/index.ts === +- removed line 10 block: export { staffRoleEnum, staffPermissionEnum } from './src/db/schema' + (these enum values still flow to consumers via the existing `export * from './src/db/schema'` โ€” the explicit line was redundant) +- user product block: removed type lines type SkuSummary, + type OffersPageData, + type OffersPageProductData, +- store helpers block: removed line type AvailabilityCacheData, +- user order block: removed type lines type OrderWithFullData, + type OrderWithCancellationData, +- SKU Features block: removed lines cleanFeatureValue, + splitQuantityFeature, + type SkuFeatureLike, + (kept composeUnitNotation, composeSkuName โ€” used by backend) + +=== packages/db_helper_postgres/index.ts === +- removed line 11 block: export { staffRoleEnum, staffPermissionEnum } from './src/db/schema'; +- user order block: removed type lines type OrderWithFullData, + type OrderWithCancellationData, + +=== apps/backend/src/sqliteImporter.ts === +- removed type lines type OrderWithFullData, + type OrderWithCancellationData, +- removed line getUserNotifCred, + +Note: getNotifCred index export line in both packages: getNotifCred as getUserNotifCred, โ€” removed (see below). + +=== packages/db_helper_sqlite/index.ts + packages/db_helper_postgres/index.ts === +- user profile block: removed line getNotifCred as getUserNotifCred, + (function getNotifCred kept in user.ts โ€” called internally by upsertNotifCred) + +=== packages/db_helper_sqlite/src/user-apis/order.ts === +- removed dead interface (lines 33-40): + export interface PlaceOrderInput { + userId: number + selectedItems: OrderItemInput[] + addressId: number + paymentMethod: 'online' | 'cod' + couponId?: number + userNotes?: string + isFlash?: boolean + } +- removed dead interface (lines 43-51): + export interface OrderGroupData { + slotId: number | null + items: Array<{ + productId: number + quantity: number + slotId: number | null + product: typeof productInfo.$inferSelect + }> + } + +=== packages/db_helper_postgres/src/user-apis/order.ts === +- removed identical PlaceOrderInput (lines 29-36) and OrderGroupData (lines 39-47) + +[2026-09-02 21:04:10] CORRECTION to previous entry: while editing packages/db_helper_sqlite/index.ts user product block I accidentally removed getAllSkusSummary, getOffersAndCombos and type ProductSummaryData (all three are USED by backend). Immediately restored them; final state of that block keeps: getAllProductsWithUnits, getAllSkusSummary, getOffersAndCombos, type ProductSummaryData. Only type SkuSummary, type OffersPageData, type OffersPageProductData were meant to be (and now are) removed. + +[2026-09-02 21:07:40] CORRECTION: while editing the user order block of packages/db_helper_sqlite/index.ts I accidentally removed getOrdersByIdsWithFullData and getOrderByIdWithFullData (both USED by backend post-order-handler). Immediately restored. Final state: those two functions kept; only type OrderWithFullData and type OrderWithCancellationData removed from that block. Also fixed an earlier accidental newline removal after the store-helpers export close brace. + +[2026-09-02 21:05:24] FOLLOW-UP: remove orphaned OrderItemInput from user-apis/order.ts in both packages (its only consumer, PlaceOrderInput, was deleted; definition-only now). + +=== packages/db_helper_sqlite/src/user-apis/order.ts === +- removed (lines 27-31): + export interface OrderItemInput { + productId: number + quantity: number + slotId: number | null + } + +=== packages/db_helper_postgres/src/user-apis/order.ts === +- removed identical interface (lines 23-27) + +[2026-09-02 21:06:16] COMPLETED ยง3 export-only-dead cleanup. +Removed from index.ts (implementations kept in src): +- both packages: redundant staffRoleEnum/staffPermissionEnum export block (still exported via `export * from './src/db/schema'`), type OrderWithFullData, type OrderWithCancellationData, getNotifCred as getUserNotifCred +- sqlite only: type SkuSummary, type OffersPageData, type OffersPageProductData, type AvailabilityCacheData, cleanFeatureValue, splitQuantityFeature, type SkuFeatureLike (kept composeUnitNotation/composeSkuName) +Removed from apps/backend/src/sqliteImporter.ts: type OrderWithFullData, type OrderWithCancellationData, getUserNotifCred +Deleted dead types in user-apis/order.ts (both packages): PlaceOrderInput, OrderGroupData, OrderItemInput +Verification: +- Backend-used exports confirmed intact: getAllSkusSummary, getOffersAndCombos, ProductSummaryData, getOrdersByIdsWithFullData, getOrderByIdWithFullData, getAvailabilityForCache, getAllProductCombosForCache, getUserWithCreds, createUserAuthWithCreds, composeSkuName, composeUnitNotation +- tsc --noEmit baselines unchanged: sqlite 4 (seed.ts 1, order.ts 2, product.ts 1), postgres 40, backend 17 โ€” all pre-existing, none reference touched symbols. + +[2026-09-02 21:21:13] ยง4: remove never-referenced schema tables โ€” addressZones, addressAreas, userNotifications, productCategories (BOTH packages). +NOTE: this only removes them from the drizzle schema (code). The actual DB tables/columns/FK constraints still exist in D1/Postgres โ€” dropping them in the DB requires a migration, which the user will handle (per AGENTS.md). Both schema.ts files drift from the live DB until then; drizzle-kit push/generate must NOT be run by the agent. + +=== packages/db_helper_sqlite/src/db/schema.ts === +- removed addressZones table (lines 106-110): + export const addressZones = sqliteTable('address_zones', { + id: integer().primaryKey({ autoIncrement: true }), + zoneName: text('zone_name').notNull(), + addedAt: timestampText('added_at').notNull().default(sql`CURRENT_TIMESTAMP`), + }) +- removed addressAreas table (lines 112-117): + export const addressAreas = sqliteTable('address_areas', { + id: integer().primaryKey({ autoIncrement: true }), + placeName: text('place_name').notNull(), + zoneId: integer('zone_id').references(() => addressZones.id), + createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), + }) +- addresses table: changed zoneId column to drop FK (column KEPT โ€” shared UserAddress type and user-apis/address.ts mapUserAddress still expose zoneId): + before: zoneId: integer('zone_id').references(() => addressZones.id), + after: zoneId: integer('zone_id'), +- removed productCategories table (lines 431-434): + export const productCategories = sqliteTable('product_categories', { + id: integer().primaryKey({ autoIncrement: true }), + name: text().notNull(), + description: text(), + }) +- removed userNotifications table (lines 545-551): + export const userNotifications = sqliteTable('user_notifications', { + id: integer().primaryKey({ autoIncrement: true }), + title: text('title').notNull(), + imageUrl: text('image_url'), + createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), + body: text('body').notNull(), + applicableUsers: jsonText('applicable_users'), + }) +- addressesRelations: removed line zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), +- removed productCategoriesRelations (line 686): export const productCategoriesRelations = relations(productCategories, ({}) => ({})) +- removed userNotificationsRelations (lines 720-722): + export const userNotificationsRelations = relations(userNotifications, ({}) => ({ + // No relations needed for now + })) +- removed addressZonesRelations (lines 749-752): + export const addressZonesRelations = relations(addressZones, ({ many }) => ({ + addresses: many(addresses), + areas: many(addressAreas), + })) +- removed addressAreasRelations (lines 754-756): + export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ + zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), + })) + +=== packages/db_helper_postgres/src/db/schema.ts === (same removals, postgres style) +- removed addressZones table (lines 58-62, mf.table('address_zones', ...)) +- removed addressAreas table (lines 64-69, mf.table('address_areas', ...)) +- addresses.zoneId (line 54): dropped .references(() => addressZones.id) โ€” column kept +- removed productCategories table (lines 318-322, mf.table('product_categories', ...)) +- removed userNotifications table (lines 434-440, mf.table('user_notifications', ...)) +- addressesRelations: removed zone relation line (line 496) +- removed productCategoriesRelations (line 573) +- removed userNotificationsRelations (lines 607-609) +- removed addressZonesRelations (lines 636-639) and addressAreasRelations (lines 641-643) + +[2026-09-02 21:23:14] COMPLETED ยง4 schema table removal. +- Removed from both packages' src/db/schema.ts: addressZones, addressAreas, productCategories, userNotifications tables + productCategoriesRelations, userNotificationsRelations, addressZonesRelations, addressAreasRelations. +- addresses.zoneId column KEPT (FK reference + addressesRelations.zone relation removed) because shared UserAddress type and user-apis/address.ts still expose zoneId. +- Verified: zero remaining references to the 4 tables anywhere; jsonText/jsonb/text imports still used by other tables; tsc baselines unchanged (sqlite 4, postgres 40, backend 17 โ€” all pre-existing). +- REMINDER: DB tables still exist in D1/Postgres until the user runs a drop migration (agent must not run drizzle migrations). + +[2026-09-02 21:24:48] ยง6: DELETE dead backend files (10 files) + clean commented references to them. + +DELETED (all verified: zero live imports โ€” only commented mentions remain): +- apps/backend/src/postgresImporter.ts (entirely commented-out swap scaffold; restore from git when swapping to postgres) +- apps/backend/src/lib/signed-url-cache-old.ts +- apps/backend/src/lib/signed-url-cache.ts +- apps/backend/src/lib/disk-persisted-set.ts (body fully commented out) +- apps/backend/src/lib/catch-async.ts +- apps/backend/src/lib/axios.ts (phonepeAxios never imported) +- apps/backend/src/lib/delete-image.ts (deleteS3Image never imported) +- apps/backend/src/lib/event-queue.ts (enqueue never imported; queue-consumer.ts is the live path) +- apps/backend/src/lib/notif-service.ts +- apps/backend/src/types/db.types.ts + +COMMENT CLEANUP (files are .ts โ†’ logged here): +=== apps/backend/index.ts === +- removed line 5: // import signedUrlCache from '@/src/lib/signed-url-cache'; +- removed line 12: // signedUrlCache.loadFromDisk(); // Disabled for Workers compatibility + Left untouched: s3-client.ts:141 prose comment (mentions the concept, not an import), dbService.ts prose header. +KEPT (verified alive): jobs/cache-creator.ts (Durable Object), lib/queue-consumer.ts, lib/flash-delivery-cron.ts, lib/seed.ts, lib/init.ts, lib/worker-init.ts, src/apis/**, src/test-controller.ts, types/hono.d.ts (ambient). + +[2026-09-02 21:25:58] COMPLETED ยง6 backend dead-file deletion (10 files) + index.ts comment cleanup. +Verification: backend tsc --noEmit now reports 11 errors (was 17) โ€” the 6 that vanished were signed-url-cache-old.ts's own pre-existing errors; remaining 11 are the same pre-existing set in untouched files (app.ts, lib/init.ts, s3-client.ts, main-router.ts, trpc-index.ts, sqlite order.ts/product.ts). No new errors. +Note: apps/backend/src/lib/s3-client.ts.txt (a .txt copy of s3-client) still exists โ€” left alone, flagging as possible future cleanup. + +[2026-09-02 21:31:17] Remove 4 dead-code items missed by our report but caught by cmd_v4_dead.md. + +DELETED (3 files, verified zero live imports โ€” only commented references): +- apps/backend/src/lib/redis-client.ts (stubbed: createClient = (args:any) => {}; only '// import redisClient' comments remain elsewhere) +- apps/backend/src/lib/redisKeyGetters.ts (only commented imports in trpc/apis/admin-apis/apis/slots.ts:7,613,694) +- apps/backend/src/middleware/auth.ts (verifyToken/requireRole never imported; app uses middleware/auth.middleware.ts + staff-auth.ts) + +EDITED: +=== packages/shared/index.ts === +- removed line 10 (dead type, definition-only): + export type CacheFilename = typeof CACHE_FILENAMES[keyof typeof CACHE_FILENAMES] + (CACHE_FILENAMES const itself is heavily used by backend cloud_cache.ts and web-ui/user-ui hooks โ€” kept) + +[2026-09-02 21:32:23] COMPLETED removal of 4 missed dead-code items. +- Deleted: apps/backend/src/lib/redis-client.ts, apps/backend/src/lib/redisKeyGetters.ts, apps/backend/src/middleware/auth.ts (middleware/ dir still has auth.middleware.ts + staff-auth.ts). +- Removed CacheFilename type from packages/shared/index.ts (CACHE_FILENAMES const kept โ€” used by cloud_cache.ts + web-ui/user-ui hooks). +- Verified: zero remaining references (excluding comments); backend tsc --noEmit = 11 errors, same pre-existing baseline. diff --git a/packages/db_helper_postgres/index.ts b/packages/db_helper_postgres/index.ts index 09b1a4b..3bec1bd 100644 --- a/packages/db_helper_postgres/index.ts +++ b/packages/db_helper_postgres/index.ts @@ -7,9 +7,6 @@ export { db } from './src/db/db_index'; // Re-export schema export * from './src/db/schema'; -// Export enum types for type safety -export { staffRoleEnum, staffPermissionEnum } from './src/db/schema'; - // Admin API helpers - explicitly namespaced exports to avoid duplicates export { // Banner @@ -80,13 +77,9 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForProduct, - updateProductDeals, replaceProductTags, - toggleProductOutOfStock, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -101,8 +94,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, } from './src/admin-apis/product'; @@ -128,7 +119,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -179,8 +169,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, } from './src/admin-apis/vendor-snippets'; @@ -271,7 +259,6 @@ export { getUserById as getUserProfileById, getUserDetailByUserId as getUserProfileDetailById, getUserWithCreds as getUserWithCreds, - getNotifCred as getUserNotifCred, upsertNotifCred as upsertUserNotifCred, deleteUnloggedToken as deleteUserUnloggedToken, getUnloggedToken as getUserUnloggedToken, @@ -299,8 +286,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, } from './src/user-apis/order'; // Store Helpers (for cache initialization) @@ -335,7 +320,6 @@ export { // Automated Jobs Helpers export { - toggleFlashDeliveryForItems, toggleKeyVal, getAllKeyValStore, } from './src/lib/automated-jobs'; @@ -347,7 +331,7 @@ export { // Common API Helpers export { - getSuspendedProductIds, + getSuspendedSkuIds, getNextDeliveryDateWithCapacity, } from './src/user-apis/product'; diff --git a/packages/db_helper_postgres/src/admin-apis/product.ts b/packages/db_helper_postgres/src/admin-apis/product.ts index 7f6f61f..f935a35 100644 --- a/packages/db_helper_postgres/src/admin-apis/product.ts +++ b/packages/db_helper_postgres/src/admin-apis/product.ts @@ -178,30 +178,6 @@ export async function updateProduct(id: number, updates: ProductInfoUpdate): Pro return mapProduct(product) } -export async function toggleProductOutOfStock(id: number): Promise { - const product = await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - }) - - if (!product) { - return null - } - - const [updatedProduct] = await db - .update(productInfo) - .set({ - isOutOfStock: !product.isOutOfStock, - }) - .where(eq(productInfo.id, id)) - .returning() - - if (!updatedProduct) { - return null - } - - return mapProduct(updatedProduct) -} - export async function updateSlotProducts(slotId: string, productIds: string[]): Promise { const slot = await db.query.deliverySlotInfo.findFirst({ where: eq(deliverySlotInfo.id, parseInt(slotId)), @@ -229,14 +205,6 @@ export async function updateSlotProducts(slotId: string, productIds: string[]): } } -export async function getAllUnits(): Promise { - const allUnits = await db.query.units.findMany({ - orderBy: units.shortNotation, - }) - - return allUnits.map(mapUnit) -} - export async function getAllProductTags(): Promise { const tags = await db.query.productTagInfo.findMany({ with: { @@ -493,18 +461,6 @@ export async function deleteProductGroup(id: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }) -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )) -} - export async function updateProductPrices(updates: Array<{ productId: number price?: number @@ -688,101 +644,6 @@ export async function getProductImagesById(productId: number): Promise { - if (deals.length === 0) { - return [] - } - - const dealInserts = deals.map((deal) => ({ - productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - - const createdDeals = await db - .insert(specialDeals) - .values(dealInserts) - .returning() - - return createdDeals.map(mapSpecialDeal) -} - -export async function updateProductDeals( - productId: number, - deals: CreateSpecialDealInput[] -): Promise { - if (deals.length === 0) { - await db.delete(specialDeals).where(eq(specialDeals.productId, productId)) - return - } - - const existingDeals = await db.query.specialDeals.findMany({ - where: eq(specialDeals.productId, productId), - }) - - const existingDealsMap = new Map( - existingDeals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - const newDealsMap = new Map( - deals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - - const dealsToAdd = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !existingDealsMap.has(key) - }) - - const dealsToRemove = existingDeals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !newDealsMap.has(key) - }) - - const dealsToUpdate = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - const existing = existingDealsMap.get(key) - const nextValidTill = deal.validTill instanceof Date - ? deal.validTill.toISOString().split('T')[0] - : String(deal.validTill) - return existing && existing.validTill.toISOString().split('T')[0] !== nextValidTill - }) - - if (dealsToRemove.length > 0) { - await db.delete(specialDeals).where( - inArray(specialDeals.id, dealsToRemove.map((deal) => deal.id)) - ) - } - - if (dealsToAdd.length > 0) { - const dealInserts = dealsToAdd.map((deal) => ({ - productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - await db.insert(specialDeals).values(dealInserts) - } - - for (const deal of dealsToUpdate) { - const key = `${deal.quantity}-${deal.price}` - const existingDeal = existingDealsMap.get(key) - if (existingDeal) { - await db.update(specialDeals) - .set({ validTill: new Date(deal.validTill) }) - .where(eq(specialDeals.id, existingDeal.id)) - } - } -} - export async function replaceProductTags(productId: number, tagIds: number[]): Promise { await db.delete(productTags).where(eq(productTags.productId, productId)) diff --git a/packages/db_helper_postgres/src/admin-apis/staff-user.ts b/packages/db_helper_postgres/src/admin-apis/staff-user.ts index b0ecfcf..075a0c9 100644 --- a/packages/db_helper_postgres/src/admin-apis/staff-user.ts +++ b/packages/db_helper_postgres/src/admin-apis/staff-user.ts @@ -98,16 +98,6 @@ export async function getUserWithDetails(userId: number): Promise { return user || null; } -export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - export async function checkStaffUserExists(name: string): Promise { const existingUser = await db.query.staffUsers.findFirst({ where: eq(staffUsers.name, name), diff --git a/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts b/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts index 5bd6fcc..cd0ec9c 100644 --- a/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts +++ b/packages/db_helper_postgres/src/admin-apis/vendor-snippets.ts @@ -185,25 +185,6 @@ export async function getVendorOrders() { }) } -export async function getOrderItemsByOrderIds(orderIds: number[]) { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }) -} - -export async function getOrderStatusByOrderIds(orderIds: number[]) { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }) -} - export async function updateVendorOrderItemPackaging( orderItemId: number, isPackaged: boolean diff --git a/packages/db_helper_postgres/src/common-apis/utils.ts b/packages/db_helper_postgres/src/common-apis/utils.ts deleted file mode 100644 index f41a32c..0000000 --- a/packages/db_helper_postgres/src/common-apis/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Common utility functions that can be used by both admin and user APIs - -export function formatDate(date: Date): string { - return date.toISOString(); -} - -export function generateCode(prefix: string, length: number = 6): string { - const timestamp = Date.now().toString().slice(-length); - const random = Math.random().toString(36).substring(2, 8).toUpperCase(); - return `${prefix}${timestamp}${random}`; -} - -export function calculateDiscount(amount: number, percent: number, maxDiscount?: number): number { - let discount = (amount * percent) / 100; - if (maxDiscount && discount > maxDiscount) { - discount = maxDiscount; - } - return discount; -} diff --git a/packages/db_helper_postgres/src/db/schema.ts b/packages/db_helper_postgres/src/db/schema.ts index 97687af..5239529 100755 --- a/packages/db_helper_postgres/src/db/schema.ts +++ b/packages/db_helper_postgres/src/db/schema.ts @@ -51,20 +51,7 @@ export const addresses = mf.table('addresses', { googleMapsUrl: varchar('google_maps_url', { length: 500 }), adminLatitude: real('admin_latitude'), adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestamp('created_at').notNull().defaultNow(), -}); - -export const addressZones = mf.table('address_zones', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - zoneName: varchar('zone_name', { length: 255 }).notNull(), - addedAt: timestamp('added_at').notNull().defaultNow(), -}); - -export const addressAreas = mf.table('address_areas', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - placeName: varchar('place_name', { length: 255 }).notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), + zoneId: integer('zone_id'), createdAt: timestamp('created_at').notNull().defaultNow(), }); @@ -315,12 +302,6 @@ export const notifications = mf.table('notifications', { createdAt: timestamp('created_at').notNull().defaultNow(), }); -export const productCategories = mf.table('product_categories', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - name: varchar({ length: 255 }).notNull(), - description: varchar({ length: 500 }), -}); - export const cartItems = mf.table('cart_items', { id: integer().primaryKey().generatedAlwaysAsIdentity(), userId: integer('user_id').notNull().references(() => users.id), @@ -431,15 +412,6 @@ export const unloggedUserTokens = mf.table('unlogged_user_tokens', { lastVerified: timestamp('last_verified'), }); -export const userNotifications = mf.table('user_notifications', { - id: integer().primaryKey().generatedAlwaysAsIdentity(), - title: varchar('title', { length: 255 }).notNull(), - imageUrl: varchar('image_url', { length: 500 }), - createdAt: timestamp('created_at').notNull().defaultNow(), - body: text('body').notNull(), - applicableUsers: jsonb('applicable_users'), -}); - export const staffRoles = mf.table('staff_roles', { id: integer().primaryKey().generatedAlwaysAsIdentity(), roleName: staffRoleEnum('role_name').notNull(), @@ -493,7 +465,6 @@ export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ export const addressesRelations = relations(addresses, ({ one, many }) => ({ user: one(users, { fields: [addresses.userId], references: [users.id] }), orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), })); export const unitsRelations = relations(units, ({ many }) => ({ @@ -570,8 +541,6 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ user: one(users, { fields: [notifications.userId], references: [users.id] }), })); -export const productCategoriesRelations = relations(productCategories, ({}) => ({})); - export const cartItemsRelations = relations(cartItems, ({ one }) => ({ user: one(users, { fields: [cartItems.userId], references: [users.id] }), product: one(productInfo, { fields: [cartItems.productId], references: [productInfo.id] }), @@ -604,10 +573,6 @@ export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ user: one(users, { fields: [notifCreds.userId], references: [users.id] }), })); -export const userNotificationsRelations = relations(userNotifications, ({}) => ({ - // No relations needed for now -})); - export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), products: many(productInfo), @@ -633,15 +598,6 @@ export const productReviewsRelations = relations(productReviews, ({ one }) => ({ product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), })); -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})); - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})); - export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ memberships: many(productGroupMembership), })); diff --git a/packages/db_helper_postgres/src/db/types.ts b/packages/db_helper_postgres/src/db/types.ts deleted file mode 100755 index 93c1f99..0000000 --- a/packages/db_helper_postgres/src/db/types.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { InferSelectModel } from "drizzle-orm"; -import type { - users, - addresses, - units, - productInfo, - deliverySlotInfo, - specialDeals, - orders, - orderItems, - payments, - notifications, - productCategories, - cartItems, - coupons, -} from "@/src/db/schema"; - -export type User = InferSelectModel; -export type Address = InferSelectModel; -export type Unit = InferSelectModel; -export type ProductInfo = InferSelectModel; -export type DeliverySlotInfo = InferSelectModel; -export type SpecialDeal = InferSelectModel; -export type Order = InferSelectModel; -export type OrderItem = InferSelectModel; -export type Payment = InferSelectModel; -export type Notification = InferSelectModel; -export type ProductCategory = InferSelectModel; -export type CartItem = InferSelectModel; -export type Coupon = InferSelectModel; - -// Combined types -export type ProductWithUnit = ProductInfo & { - unit: Unit; -}; - -export type OrderWithItems = Order & { - items: (OrderItem & { product: ProductInfo })[]; - address: Address; - slot: DeliverySlotInfo; -}; - -export type CartItemWithProduct = CartItem & { - product: ProductInfo; -}; diff --git a/packages/db_helper_postgres/src/helper_methods/banner.ts b/packages/db_helper_postgres/src/helper_methods/banner.ts deleted file mode 100644 index 23b6f56..0000000 --- a/packages/db_helper_postgres/src/helper_methods/banner.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { db } from '../db/db_index'; -import { homeBanners } from '../db/schema'; -import { desc, eq } from 'drizzle-orm'; - -export interface Banner { - id: number; - name: string; - imageUrl: string; - description: string | null; - productIds: number[] | null; - redirectUrl: string | null; - serialNum: number | null; - isActive: boolean; - createdAt: Date; - lastUpdated: Date; -} - -export async function getBanners(): Promise { - const banners = await db.query.homeBanners.findMany({ - orderBy: desc(homeBanners.createdAt), - }); - - return banners.map((banner) => ({ - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - })); -} - -export async function getBannerById(id: number): Promise { - const banner = await db.query.homeBanners.findFirst({ - where: eq(homeBanners.id, id), - }); - - if (!banner) return null; - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export type CreateBannerInput = Omit; - -export async function createBanner(input: CreateBannerInput): Promise { - const [banner] = await db.insert(homeBanners).values({ - name: input.name, - imageUrl: input.imageUrl, - description: input.description, - productIds: input.productIds, - redirectUrl: input.redirectUrl, - serialNum: input.serialNum, - isActive: input.isActive, - }).returning(); - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export type UpdateBannerInput = Partial>; - -export async function updateBanner(id: number, input: UpdateBannerInput): Promise { - const [banner] = await db.update(homeBanners) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(homeBanners.id, id)) - .returning(); - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - }; -} - -export async function deleteBanner(id: number): Promise { - await db.delete(homeBanners).where(eq(homeBanners.id, id)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/complaint.ts b/packages/db_helper_postgres/src/helper_methods/complaint.ts deleted file mode 100644 index 0e2cec8..0000000 --- a/packages/db_helper_postgres/src/helper_methods/complaint.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { db } from '../db/db_index'; -import { complaints, users } from '../db/schema'; -import { eq, desc, lt } from 'drizzle-orm'; - -export interface Complaint { - id: number; - complaintBody: string; - userId: number; - orderId: number | null; - isResolved: boolean; - response: string | null; - createdAt: Date; - images: string[] | null; -} - -export interface ComplaintWithUser extends Complaint { - userName: string | null; - userMobile: string | null; -} - -export async function getComplaints( - cursor?: number, - limit: number = 20 -): Promise<{ complaints: ComplaintWithUser[]; hasMore: boolean }> { - let whereCondition = cursor ? lt(complaints.id, cursor) : undefined; - - const complaintsData = await db - .select({ - id: complaints.id, - complaintBody: complaints.complaintBody, - userId: complaints.userId, - orderId: complaints.orderId, - isResolved: complaints.isResolved, - response: complaints.response, - createdAt: complaints.createdAt, - images: complaints.images, - userName: users.name, - userMobile: users.mobile, - }) - .from(complaints) - .leftJoin(users, eq(complaints.userId, users.id)) - .where(whereCondition) - .orderBy(desc(complaints.id)) - .limit(limit + 1); - - const hasMore = complaintsData.length > limit; - const complaintsToReturn = hasMore ? complaintsData.slice(0, limit) : complaintsData; - - return { - complaints: complaintsToReturn.map((c) => ({ - id: c.id, - complaintBody: c.complaintBody, - userId: c.userId, - orderId: c.orderId, - isResolved: c.isResolved, - response: c.response, - createdAt: c.createdAt, - images: c.images, - userName: c.userName, - userMobile: c.userMobile, - })), - hasMore, - }; -} - -export async function resolveComplaint( - id: number, - response?: string -): Promise { - await db - .update(complaints) - .set({ isResolved: true, response }) - .where(eq(complaints.id, id)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/const.ts b/packages/db_helper_postgres/src/helper_methods/const.ts deleted file mode 100644 index 9d3ba86..0000000 --- a/packages/db_helper_postgres/src/helper_methods/const.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { db } from '../db/db_index'; -import { keyValStore } from '../db/schema'; - -export interface Constant { - key: string; - value: any; -} - -export async function getAllConstants(): Promise { - const constants = await db.select().from(keyValStore); - - return constants.map(c => ({ - key: c.key, - value: c.value, - })); -} - -export async function upsertConstants(constants: Constant[]): Promise { - await db.transaction(async (tx) => { - for (const { key, value } of constants) { - await tx.insert(keyValStore) - .values({ key, value }) - .onConflictDoUpdate({ - target: keyValStore.key, - set: { value }, - }); - } - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/coupon.ts b/packages/db_helper_postgres/src/helper_methods/coupon.ts deleted file mode 100644 index b87eee5..0000000 --- a/packages/db_helper_postgres/src/helper_methods/coupon.ts +++ /dev/null @@ -1,633 +0,0 @@ -import { db } from '../db/db_index'; -import { coupons, reservedCoupons, users } from '../db/schema'; -import { eq, and, like, or, inArray, lt, desc } from 'drizzle-orm'; - -export interface Coupon { - id: number; - couponCode: string; - isUserBased: boolean; - discountPercent: string | null; - flatDiscount: string | null; - minOrder: string | null; - productIds: number[] | null; - maxValue: string | null; - isApplyForAll: boolean; - validTill: Date | null; - maxLimitForUser: number | null; - exclusiveApply: boolean; - isInvalidated: boolean; - createdAt: Date; - createdBy: number; -} - -export async function getAllCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(coupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(like(coupons.couponCode, `%${search}%`)); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.coupons.findMany({ - where: whereCondition, - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - orderBy: (coupons, { desc }) => [desc(coupons.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export async function getCouponById(id: number): Promise { - const result = await db.query.coupons.findFirst({ - where: eq(coupons.id, id), - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - }); - - return result || null; -} - -export async function invalidateCoupon(id: number): Promise { - const result = await db.update(coupons) - .set({ isInvalidated: true }) - .where(eq(coupons.id, id)) - .returning(); - - return result[0]; -} - -export interface CouponValidationResult { - valid: boolean; - message?: string; - discountAmount?: number; - coupon?: Partial; -} - -export async function validateCoupon( - code: string, - userId: number, - orderAmount: number -): Promise { - const coupon = await db.query.coupons.findFirst({ - where: and( - eq(coupons.couponCode, code.toUpperCase()), - eq(coupons.isInvalidated, false) - ), - }); - - if (!coupon) { - return { valid: false, message: "Coupon not found or invalidated" }; - } - - // Check expiry date - if (coupon.validTill && new Date(coupon.validTill) < new Date()) { - return { valid: false, message: "Coupon has expired" }; - } - - // Check if coupon applies to all users or specific user - if (!coupon.isApplyForAll && !coupon.isUserBased) { - return { valid: false, message: "Coupon is not available for use" }; - } - - // Check minimum order amount - const minOrderValue = coupon.minOrder ? parseFloat(coupon.minOrder) : 0; - if (minOrderValue > 0 && orderAmount < minOrderValue) { - return { valid: false, message: `Minimum order amount is ${minOrderValue}` }; - } - - // Calculate discount - let discountAmount = 0; - if (coupon.discountPercent) { - const percent = parseFloat(coupon.discountPercent); - discountAmount = (orderAmount * percent) / 100; - } else if (coupon.flatDiscount) { - discountAmount = parseFloat(coupon.flatDiscount); - } - - // Apply max value limit - const maxValueLimit = coupon.maxValue ? parseFloat(coupon.maxValue) : 0; - if (maxValueLimit > 0 && discountAmount > maxValueLimit) { - discountAmount = maxValueLimit; - } - - return { - valid: true, - discountAmount, - coupon: { - id: coupon.id, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - maxValue: coupon.maxValue, - } - }; -} - -export async function getReservedCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(reservedCoupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(or( - like(reservedCoupons.secretCode, `%${search}%`), - like(reservedCoupons.couponCode, `%${search}%`) - )); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.reservedCoupons.findMany({ - where: whereCondition, - with: { - redeemedUser: true, - creator: true, - }, - orderBy: (reservedCoupons, { desc }) => [desc(reservedCoupons.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export interface UserMiniInfo { - id: number; - name: string; - mobile: string | null; -} - -export async function getUsersForCoupon( - search?: string, - limit: number = 20, - offset: number = 0 -): Promise<{ users: UserMiniInfo[] }> { - let whereCondition = undefined; - if (search && search.trim()) { - whereCondition = or( - like(users.name, `%${search}%`), - like(users.mobile, `%${search}%`) - ); - } - - const userList = await db.query.users.findMany({ - where: whereCondition, - columns: { - id: true, - name: true, - mobile: true, - }, - limit: limit, - offset: offset, - orderBy: (users, { asc }) => [asc(users.name)], - }); - - return { - users: userList.map(user => ({ - id: user.id, - name: user.name || 'Unknown', - mobile: user.mobile, - })) - }; -} - -// ============================================================================ -// BATCH 2: Transaction Methods -// ============================================================================ - -import { couponApplicableUsers, couponApplicableProducts, orders, orderStatus } from '../db/schema'; - -export interface CreateCouponInput { - couponCode: string; - isUserBased: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - isApplyForAll: boolean; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createCouponWithRelations( - input: CreateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode: input.couponCode, - isUserBased: input.isUserBased, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - productIds: input.productIds, - createdBy: input.createdBy, - maxValue: input.maxValue, - isApplyForAll: input.isApplyForAll, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - }).returning(); - - // Insert applicable users - if (applicableUsers && applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: coupon.id, - userId, - })) - ); - } - - // Insert applicable products - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: coupon.id, - productId, - })) - ); - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface UpdateCouponInput { - couponCode?: string; - isUserBased?: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - isApplyForAll?: boolean; - validTill?: Date | null; - maxLimitForUser?: number; - exclusiveApply?: boolean; - isInvalidated?: boolean; -} - -export async function updateCouponWithRelations( - id: number, - input: UpdateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Update the coupon - const [coupon] = await tx.update(coupons) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(coupons.id, id)) - .returning(); - - // Update applicable users: delete existing and insert new - if (applicableUsers !== undefined) { - await tx.delete(couponApplicableUsers).where(eq(couponApplicableUsers.couponId, id)); - if (applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: id, - userId, - })) - ); - } - } - - // Update applicable products: delete existing and insert new - if (applicableProducts !== undefined) { - await tx.delete(couponApplicableProducts).where(eq(couponApplicableProducts.couponId, id)); - if (applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: id, - productId, - })) - ); - } - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export async function generateCancellationCoupon( - orderId: number, - staffUserId: number, - userId: number, - orderAmount: number, - couponCode: string -): Promise { - return await db.transaction(async (tx) => { - // Calculate expiry date (30 days from now) - const expiryDate = new Date(); - expiryDate.setDate(expiryDate.getDate() + 30); - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - flatDiscount: orderAmount.toString(), - minOrder: orderAmount.toString(), - maxValue: orderAmount.toString(), - validTill: expiryDate, - maxLimitForUser: 1, - createdBy: staffUserId, - isApplyForAll: false, - }).returning(); - - // Insert applicable users - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId, - }); - - // Update order_status with refund coupon ID - await tx.update(orderStatus) - .set({ refundCouponId: coupon.id }) - .where(eq(orderStatus.orderId, orderId)); - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface CreateReservedCouponInput { - secretCode: string; - couponCode: string; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - productIds?: number[] | null; - maxValue?: string; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createReservedCouponWithProducts( - input: CreateReservedCouponInput, - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - const [coupon] = await tx.insert(reservedCoupons).values({ - secretCode: input.secretCode, - couponCode: input.couponCode, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - productIds: input.productIds, - maxValue: input.maxValue, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - createdBy: input.createdBy, - }).returning(); - - // Insert applicable products if provided - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(productId => ({ - couponId: coupon.id, - productId, - })) - ); - } - - return coupon; - }); -} - -export async function getOrCreateUserByMobile( - mobile: string -): Promise<{ id: number; mobile: string; name: string | null }> { - return await db.transaction(async (tx) => { - // Check if user exists - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - // Create new user - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - return { - id: user.id, - mobile: user.mobile, - name: user.name, - }; - }); -} - -export async function createCouponForUser( - mobile: string, - couponCode: string, - staffUserId: number -): Promise<{ coupon: Coupon; user: { id: number; mobile: string; name: string | null } }> { - return await db.transaction(async (tx) => { - // Get or create user - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - discountPercent: "20", - minOrder: "1000", - maxValue: "500", - maxLimitForUser: 1, - isApplyForAll: false, - exclusiveApply: false, - createdBy: staffUserId, - validTill: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days from now - }).returning(); - - // Associate coupon with user - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId: user.id, - }); - - return { - coupon: { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - productIds: coupon.productIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }, - user: { - id: user.id, - mobile: user.mobile, - name: user.name, - }, - }; - }); -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -export async function checkUsersExist(userIds: number[]): Promise { - const existingUsers = await db.query.users.findMany({ - where: inArray(users.id, userIds), - columns: { id: true }, - }); - return existingUsers.length === userIds.length; -} - -export async function checkCouponExists(couponCode: string): Promise { - const existing = await db.query.coupons.findFirst({ - where: eq(coupons.couponCode, couponCode), - }); - return !!existing; -} - -export async function checkReservedCouponExists(secretCode: string): Promise { - const existing = await db.query.reservedCoupons.findFirst({ - where: eq(reservedCoupons.secretCode, secretCode), - }); - return !!existing; -} - -export async function getOrderWithUser(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - user: true, - }, - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/order.ts b/packages/db_helper_postgres/src/helper_methods/order.ts deleted file mode 100644 index 355a9d2..0000000 --- a/packages/db_helper_postgres/src/helper_methods/order.ts +++ /dev/null @@ -1,259 +0,0 @@ -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 { - const [result] = await db - .update(orders) - .set({ adminNotes }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function getOrderWithDetails(orderId: number): Promise { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - await db.update(addresses) - .set({ lat, lng }) - .where(eq(addresses.id, addressId)); -} - -export async function getOrderStatus(orderId: number): Promise { - return await db.query.orderStatus.findFirst({ - where: eq(orderStatus.orderId, orderId), - }); -} - -export async function cancelOrder(orderId: number, reason: string): Promise { - 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 { - 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 { - const [result] = await db - .update(orders) - .set({ deliveryCharge: '0' }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} diff --git a/packages/db_helper_postgres/src/helper_methods/product.ts b/packages/db_helper_postgres/src/helper_methods/product.ts deleted file mode 100644 index a9eb10b..0000000 --- a/packages/db_helper_postgres/src/helper_methods/product.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { db } from '../db/db_index'; -import { productInfo, units, specialDeals, productTags, productReviews, productGroupInfo, productGroupMembership } from '../db/schema'; -import { eq, and, inArray, desc, sql, asc } from 'drizzle-orm'; - -export async function getAllProducts(): Promise { - return await db.query.productInfo.findMany({ - orderBy: productInfo.name, - with: { - unit: true, - store: true, - }, - }); -} - -export async function getProductById(id: number): Promise { - return await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - with: { - unit: true, - store: true, - specialDeals: true, - productTags: { - with: { - tag: true, - }, - }, - }, - }); -} - -export async function createProduct(input: any): Promise { - const [product] = await db.insert(productInfo).values(input).returning(); - return product; -} - -export async function updateProduct(id: number, updates: any): Promise { - 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 { - const [product] = await db.update(productInfo) - .set({ isOutOfStock }) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function getAllUnits(): Promise { - return await db.query.units.findMany({ - orderBy: units.name, - }); -} - -export async function getAllProductTags(): Promise { - return await db.query.productTags.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function getProductReviews(productId: number): Promise { - 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 { - await db.update(productReviews) - .set({ adminResponse }) - .where(eq(productReviews.id, reviewId)); -} - -export async function getAllProductGroups(): Promise { - return await db.query.productGroupInfo.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function createProductGroup(name: string): Promise { - const [group] = await db.insert(productGroupInfo).values({ name }).returning(); - return group; -} - -export async function updateProductGroup(id: number, name: string): Promise { - const [group] = await db.update(productGroupInfo) - .set({ name }) - .where(eq(productGroupInfo.id, id)) - .returning(); - return group; -} - -export async function deleteProductGroup(id: number): Promise { - await db.delete(productGroupInfo).where(eq(productGroupInfo.id, id)); -} - -export async function addProductToGroup(groupId: number, productId: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }); -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )); -} diff --git a/packages/db_helper_postgres/src/helper_methods/slots.ts b/packages/db_helper_postgres/src/helper_methods/slots.ts deleted file mode 100644 index 6779bc2..0000000 --- a/packages/db_helper_postgres/src/helper_methods/slots.ts +++ /dev/null @@ -1,166 +0,0 @@ -import { db } from '../db/db_index'; -import { deliverySlotInfo, productInfo, vendorSnippets } from '../db/schema'; -import { eq, and, inArray, desc } from 'drizzle-orm'; - -export async function getAllSlots(): Promise { - const slots = await db.query.deliverySlotInfo.findMany({ - orderBy: desc(deliverySlotInfo.createdAt), - with: { - vendorSnippets: true, - }, - }); - - // Fetch products for all slots - const allProductIds = new Set(); - for (const slot of slots) { - for (const productId of (slot.productIds || [])) { - allProductIds.add(productId); - } - } - - const productIdsArray = Array.from(allProductIds); - const productsData = productIdsArray.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIdsArray), - }) - : []; - - const productMap = new Map(productsData.map(p => [p.id, p])); - - return slots.map(slot => ({ - ...slot, - products: (slot.productIds || []) - .map(productId => productMap.get(productId)) - .filter((p): p is NonNullable => p != null), - })); -} - -export async function getSlotById(id: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, id), - with: { - vendorSnippets: { - with: { - slot: true, - }, - }, - }, - }); - - if (!slot) { - return null; - } - - // Fetch products for this slot - const productIds = slot.productIds || []; - const productsData = productIds.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }) - : []; - - return { - ...slot, - products: productsData, - }; -} - -export async function createSlot(input: any): Promise { - const [slot] = await db.insert(deliverySlotInfo).values(input).returning(); - return slot; -} - -export async function updateSlot(id: number, updates: any): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set(updates) - .where(eq(deliverySlotInfo.id, id)) - .returning(); - return slot; -} - -export async function deleteSlot(id: number): Promise { - await db.delete(deliverySlotInfo).where(eq(deliverySlotInfo.id, id)); -} - -export async function getSlotProducts(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - return []; - } - - const productIds = slot.productIds || []; - if (productIds.length === 0) { - return []; - } - - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }); -} - -export async function addProductToSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - if (!currentProductIds.includes(productId)) { - await db.update(deliverySlotInfo) - .set({ productIds: [...currentProductIds, productId] }) - .where(eq(deliverySlotInfo.id, slotId)); - } -} - -export async function removeProductFromSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - const updatedProductIds = currentProductIds.filter(id => id !== productId); - - await db.update(deliverySlotInfo) - .set({ productIds: updatedProductIds }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function clearSlotProducts(slotId: number): Promise { - await db.update(deliverySlotInfo) - .set({ productIds: [] }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function updateSlotCapacity(slotId: number, maxCapacity: number): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set({ maxCapacity }) - .where(eq(deliverySlotInfo.id, slotId)) - .returning(); - return slot; -} - -export async function getSlotDeliverySequence(slotId: number): Promise { - 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 { - await db.update(deliverySlotInfo) - .set({ deliverySequence: sequence }) - .where(eq(deliverySlotInfo.id, slotId)); -} diff --git a/packages/db_helper_postgres/src/helper_methods/staff-user.ts b/packages/db_helper_postgres/src/helper_methods/staff-user.ts deleted file mode 100644 index f02f8b0..0000000 --- a/packages/db_helper_postgres/src/helper_methods/staff-user.ts +++ /dev/null @@ -1,153 +0,0 @@ -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 { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - - return staff || null; -} - -export async function getAllStaff(): Promise { - 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 { - 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 { - 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 { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - -export async function checkStaffUserExists(name: string): Promise { - const existingUser = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return !!existingUser; -} - -export async function checkStaffRoleExists(roleId: number): Promise { - 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 { - 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 { - const roles = await db.query.staffRoles.findMany({ - columns: { - id: true, - roleName: true, - }, - }); - - return roles; -} diff --git a/packages/db_helper_postgres/src/helper_methods/store.ts b/packages/db_helper_postgres/src/helper_methods/store.ts deleted file mode 100644 index cb1d755..0000000 --- a/packages/db_helper_postgres/src/helper_methods/store.ts +++ /dev/null @@ -1,151 +0,0 @@ -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 { - const stores = await db.query.storeInfo.findMany({ - with: { - owner: true, - }, - }); - - return stores; -} - -export async function getStoreById(id: number): Promise { - 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 { - 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 { - 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", - }; - }); -} diff --git a/packages/db_helper_postgres/src/helper_methods/user.ts b/packages/db_helper_postgres/src/helper_methods/user.ts deleted file mode 100644 index e25fcac..0000000 --- a/packages/db_helper_postgres/src/helper_methods/user.ts +++ /dev/null @@ -1,270 +0,0 @@ -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 { - const [newUser] = await db - .insert(users) - .values({ - name: null, - email: null, - mobile, - }) - .returning(); - - return newUser; -} - -export async function getUserByMobile(mobile: string): Promise { - const [existingUser] = await db - .select() - .from(users) - .where(eq(users.mobile, mobile)) - .limit(1); - - return existingUser || null; -} - -export async function getUnresolvedComplaintsCount(): Promise { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - const [incident] = await db.insert(userIncidents) - .values({ - userId, - orderId, - adminComment, - addedBy: adminUserId, - negativityScore, - }) - .returning(); - - return incident; -} diff --git a/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts b/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts deleted file mode 100644 index 8cec6a6..0000000 --- a/packages/db_helper_postgres/src/helper_methods/vendor-snippets.ts +++ /dev/null @@ -1,130 +0,0 @@ -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 { - const existingSnippet = await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); - return !!existingSnippet; -} - -export async function getVendorSnippetById(id: number): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.id, id), - with: { - slot: true, - }, - }); -} - -export async function getVendorSnippetByCode(snippetCode: string): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); -} - -export async function getAllVendorSnippets(): Promise { - 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 { - 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 { - const [result] = await db.update(vendorSnippets) - .set(updates) - .where(eq(vendorSnippets.id, id)) - .returning(); - - return result; -} - -export async function deleteVendorSnippet(id: number): Promise { - await db.delete(vendorSnippets) - .where(eq(vendorSnippets.id, id)); -} - -export async function getProductsByIds(productIds: number[]): Promise { - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - columns: { id: true, name: true }, - }); -} - -export async function getVendorSlotById(slotId: number): Promise { - return await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); -} - -export async function getVendorOrdersBySlotId(slotId: number): Promise { - 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 { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }); -} - -export async function getOrderStatusByOrderIds(orderIds: number[]): Promise { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }); -} - -export async function updateVendorOrderItemPackaging(orderItemId: number, isPackaged: boolean, isPackageVerified: boolean): Promise { - await db.update(orderItems) - .set({ - is_packaged: isPackaged, - is_package_verified: isPackageVerified, - }) - .where(eq(orderItems.id, orderItemId)); -} diff --git a/packages/db_helper_postgres/src/lib/automated-jobs.ts b/packages/db_helper_postgres/src/lib/automated-jobs.ts index 75f5723..756ab1f 100644 --- a/packages/db_helper_postgres/src/lib/automated-jobs.ts +++ b/packages/db_helper_postgres/src/lib/automated-jobs.ts @@ -1,21 +1,6 @@ import { db } from '../db/db_index' -import { productInfo, keyValStore } from '../db/schema' -import { inArray, eq } from 'drizzle-orm' - -/** - * Toggle flash delivery availability for specific products - * @param isAvailable - Whether flash delivery should be available - * @param productIds - Array of product IDs to update - */ -export async function toggleFlashDeliveryForItems( - isAvailable: boolean, - productIds: number[] -): Promise { - await db - .update(productInfo) - .set({ isFlashAvailable: isAvailable }) - .where(inArray(productInfo.id, productIds)) -} +import { keyValStore } from '../db/schema' +import { eq } from 'drizzle-orm' /** * Update key-value store diff --git a/packages/db_helper_postgres/src/user-apis/order.ts b/packages/db_helper_postgres/src/user-apis/order.ts index eb9d8e6..9c094de 100644 --- a/packages/db_helper_postgres/src/user-apis/order.ts +++ b/packages/db_helper_postgres/src/user-apis/order.ts @@ -20,32 +20,6 @@ import type { UserOrderDetail, } from '@packages/shared' -export interface OrderItemInput { - productId: number - quantity: number - slotId: number | null -} - -export interface PlaceOrderInput { - userId: number - selectedItems: OrderItemInput[] - addressId: number - paymentMethod: 'online' | 'cod' - couponId?: number - userNotes?: string - isFlash?: boolean -} - -export interface OrderGroupData { - slotId: number | null - items: Array<{ - productId: number - quantity: number - slotId: number | null - product: typeof productInfo.$inferSelect - }> -} - export interface PlacedOrder { id: number userId: number diff --git a/packages/db_helper_postgres/src/user-apis/product.ts b/packages/db_helper_postgres/src/user-apis/product.ts index 42ebcc8..6be8a5e 100644 --- a/packages/db_helper_postgres/src/user-apis/product.ts +++ b/packages/db_helper_postgres/src/user-apis/product.ts @@ -215,7 +215,7 @@ export async function getAllProductsWithUnits(tagId?: number): Promise { +export async function getSuspendedSkuIds(): Promise { const suspendedProducts = await db .select({ id: productInfo.id }) .from(productInfo) diff --git a/packages/db_helper_postgres/src/user-apis/tags.ts b/packages/db_helper_postgres/src/user-apis/tags.ts deleted file mode 100644 index 49524b2..0000000 --- a/packages/db_helper_postgres/src/user-apis/tags.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from '../db/db_index'; -import { productTags } from '../db/schema'; -import { eq } from 'drizzle-orm'; - -export async function getAllTags(): Promise { - return await db.query.productTags.findMany({ - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }); -} - -export async function getTagById(id: number): Promise { - return await db.query.productTags.findFirst({ - where: eq(productTags.id, id), - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }); -} diff --git a/packages/db_helper_sqlite/index.ts b/packages/db_helper_sqlite/index.ts index ea3700c..29af47c 100644 --- a/packages/db_helper_sqlite/index.ts +++ b/packages/db_helper_sqlite/index.ts @@ -6,9 +6,6 @@ export { db, initDb } from './src/db/db_index' // Re-export schema export * from './src/db/schema' -// Export enum types for type safety -export { staffRoleEnum, staffPermissionEnum } from './src/db/schema' - // Admin API helpers - explicitly namespaced exports to avoid duplicates export { // Banner @@ -83,14 +80,10 @@ export { checkProductExistsByName, checkUnitExists, getProductImagesById, - createSpecialDealsForSku, - updateSkuDeals, replaceProductTags, replaceTagProducts, - mergeSkus, updateSlotProducts, getSlotsProductIds, - getAllUnits, getAllProductTags, getAllProductTagInfos, getProductTagInfoById, @@ -105,8 +98,6 @@ export { createProductGroup, updateProductGroup, deleteProductGroup, - addProductToGroup, - removeProductFromGroup, updateProductPrices, } from './src/admin-apis/product' @@ -137,7 +128,6 @@ export { getAllStaff, getAllUsers, getUserWithDetails, - updateUserSuspensionStatus, checkStaffUserExists, checkStaffRoleExists, createStaffUser, @@ -188,8 +178,6 @@ export { getProductsByIds, getVendorSlotById, getVendorOrdersBySlotId, - getOrderItemsByOrderIds, - getOrderStatusByOrderIds, updateVendorOrderItemPackaging, getVendorOrders, } from './src/admin-apis/vendor-snippets' @@ -233,9 +221,6 @@ export { getAllSkusSummary, getOffersAndCombos, type ProductSummaryData, - type SkuSummary, - type OffersPageData, - type OffersPageProductData, } from './src/user-apis/product' export { @@ -285,7 +270,6 @@ export { getUserById as getUserProfileById, getUserDetailByUserId as getUserProfileDetailById, getUserWithCreds as getUserWithCreds, - getNotifCred as getUserNotifCred, upsertNotifCred as upsertUserNotifCred, deleteUnloggedToken as deleteUserUnloggedToken, getUnloggedToken as getUserUnloggedToken, @@ -313,8 +297,6 @@ export { // Post-order handler helpers getOrdersByIdsWithFullData, getOrderByIdWithFullData, - type OrderWithFullData, - type OrderWithCancellationData, } from './src/user-apis/order' // Store Helpers (for cache initialization) @@ -331,7 +313,6 @@ export { getAllProductTagsForCache, getAllProductCombosForCache, type ProductBasicData, - type AvailabilityCacheData, type StoreBasicData, type DeliverySlotData, type SpecialDealData, @@ -379,11 +360,8 @@ export { // SKU Features Helper export { - cleanFeatureValue, - splitQuantityFeature, composeUnitNotation, composeSkuName, - type SkuFeatureLike, } from './src/lib/sku-features' // Upload URL Helpers diff --git a/packages/db_helper_sqlite/src/admin-apis/product.ts b/packages/db_helper_sqlite/src/admin-apis/product.ts index 0b7ff88..96a0452 100644 --- a/packages/db_helper_sqlite/src/admin-apis/product.ts +++ b/packages/db_helper_sqlite/src/admin-apis/product.ts @@ -100,7 +100,6 @@ interface CreateProductInput { productType?: 'item' | 'combo' skus: CreateSkuInput[] } -type UnitRow = InferSelectModel type StoreRow = InferSelectModel type SpecialDealRow = InferSelectModel type ProductTagInfoRow = InferSelectModel @@ -114,12 +113,6 @@ const getStringArray = (value: unknown): string[] | null => { return value.map((item) => String(item)) } -const mapUnit = (unit: UnitRow): AdminUnit => ({ - id: unit.id, - shortNotation: unit.shortNotation, - fullName: unit.fullName, -}) - const mapStore = (store: StoreRow): Store => ({ id: store.id, name: store.name, @@ -647,14 +640,6 @@ export async function updateSlotProducts(slotId: string, productIds: string[]): } } -export async function getAllUnits(): Promise { - const allUnits = await db.query.units.findMany({ - orderBy: units.shortNotation, - }) - - return allUnits.map(mapUnit) -} - export async function getAllProductTags(): Promise { const tags = await db.query.productTagInfo.findMany({ with: { @@ -1029,18 +1014,6 @@ export async function deleteProductGroup(id: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }) -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )) -} - export async function updateProductPrices(updates: Array<{ productId: number price?: number @@ -1142,101 +1115,6 @@ export async function getProductImagesById(productId: number): Promise { - if (deals.length === 0) { - return [] - } - - const dealInserts = deals.map((deal) => ({ - skuId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - - const createdDeals = await db - .insert(specialDeals) - .values(dealInserts) - .returning() - - return createdDeals.map(mapSpecialDeal) -} - -export async function updateSkuDeals( - productId: number, - deals: CreateSpecialDealInput[] -): Promise { - if (deals.length === 0) { - await db.delete(specialDeals).where(eq(specialDeals.skuId, productId)) - return - } - - const existingDeals = await db.query.specialDeals.findMany({ - where: eq(specialDeals.skuId, productId), - }) - - const existingDealsMap = new Map( - existingDeals.map((deal: SpecialDealRow) => [`${deal.quantity}-${deal.price}`, deal]) - ) - const newDealsMap = new Map( - deals.map((deal) => [`${deal.quantity}-${deal.price}`, deal]) - ) - - const dealsToAdd = deals.filter((deal) => { - const key = `${deal.quantity}-${deal.price}` - return !existingDealsMap.has(key) - }) - - const dealsToRemove = existingDeals.filter((deal: SpecialDealRow) => { - const key = `${deal.quantity}-${deal.price}` - return !newDealsMap.has(key) - }) - - const dealsToUpdate = deals.filter((deal: CreateSpecialDealInput) => { - const key = `${deal.quantity}-${deal.price}` - const existing = existingDealsMap.get(key) - const nextValidTill = deal.validTill instanceof Date - ? deal.validTill.toISOString().split('T')[0] - : String(deal.validTill) - return existing && existing.validTill.toISOString().split('T')[0] !== nextValidTill - }) - - if (dealsToRemove.length > 0) { - await db.delete(specialDeals).where( - inArray(specialDeals.id, dealsToRemove.map((deal: SpecialDealRow) => deal.id)) - ) - } - - if (dealsToAdd.length > 0) { - const dealInserts = dealsToAdd.map((deal) => ({ - skuId: productId, - quantity: deal.quantity.toString(), - price: deal.price.toString(), - validTill: new Date(deal.validTill), - })) - await db.insert(specialDeals).values(dealInserts) - } - - for (const deal of dealsToUpdate) { - const key = `${deal.quantity}-${deal.price}` - const existingDeal = existingDealsMap.get(key) - if (existingDeal) { - await db.update(specialDeals) - .set({ validTill: new Date(deal.validTill) }) - .where(eq(specialDeals.id, existingDeal.id)) - } - } -} - export async function replaceProductTags(productId: number, tagIds: number[]): Promise { await db.delete(productTags).where(eq(productTags.productId, productId)) @@ -1266,128 +1144,3 @@ export async function replaceTagProducts(tagId: number, productIds: number[]): P await db.insert(productTags).values(productAssociations) } - -export async function mergeSkus(fromSkuId: number, toSkuId: number) { - if (fromSkuId === toSkuId) { - return { fromSkuId, toSkuId, message: 'SKUs are the same, no merge needed', counts: {} } - } - - const fromSku = await db.query.productSkus.findFirst({ where: eq(productSkus.id, fromSkuId) }) - if (!fromSku) throw new Error(`SKU ${fromSkuId} not found`) - - const toSku = await db.query.productSkus.findFirst({ where: eq(productSkus.id, toSkuId) }) - if (!toSku) throw new Error(`SKU ${toSkuId} not found`) - - const counts: Record = {} - - // 1. order_items โ€” direct update - const orderItemsResult = await db.update(orderItems) - .set({ skuId: toSkuId }) - .where(eq(orderItems.skuId, fromSkuId)) - counts.orderItems = orderItemsResult.changes ?? 0 - - // 2. special_deals โ€” direct update - const specialDealsResult = await db.update(specialDeals) - .set({ skuId: toSkuId }) - .where(eq(specialDeals.skuId, fromSkuId)) - counts.specialDeals = specialDealsResult.changes ?? 0 - - // 3. cart_items โ€” delete all with fromSkuId - const cartResult = await db.delete(cartItems) - .where(eq(cartItems.skuId, fromSkuId)) - counts.cartItems = cartResult.changes ?? 0 - - // 4. coupon_applicable_products โ€” update, but handle unique constraint - // First delete rows where (coupon_id, toSkuId) already exists - const existingCapRows = await db.query.couponApplicableProducts.findMany({ - where: eq(couponApplicableProducts.skuId, toSkuId), - columns: { couponId: true }, - }) - const existingCouponIds = new Set(existingCapRows.map((r) => r.couponId)) - - if (existingCouponIds.size > 0) { - const dupResult = await db.delete(couponApplicableProducts) - .where( - and( - eq(couponApplicableProducts.skuId, fromSkuId), - inArray(couponApplicableProducts.couponId, Array.from(existingCouponIds)) - ) - ) - counts.couponDedupDeleted = dupResult.changes ?? 0 - } - - // Now update remaining rows - const capResult = await db.update(couponApplicableProducts) - .set({ skuId: toSkuId }) - .where(eq(couponApplicableProducts.skuId, fromSkuId)) - counts.couponApplicable = capResult.changes ?? 0 - - // 5. JSON arrays โ€” remap fromSkuId to toSkuId - const jsonTables: Array<{ table: any; column: string; name: string }> = [ - { table: deliverySlotInfo, column: 'skuIds', name: 'deliverySlotInfo' }, - { table: homeBanners, column: 'skuIds', name: 'homeBanners' }, - { table: coupons, column: 'skuIds', name: 'coupons' }, - { table: reservedCoupons, column: 'skuIds', name: 'reservedCoupons' }, - { table: vendorSnippets, column: 'skuIds', name: 'vendorSnippets' }, - ] - - for (const { table, column, name } of jsonTables) { - const rows = await db.select({ id: table.id, ids: table[column] }).from(table) - let updated = 0 - for (const row of rows) { - const ids: number[] = (row.ids as number[]) || [] - if (!ids.includes(fromSkuId)) continue - const newIds = ids.map((id) => (id === fromSkuId ? toSkuId : id)) - const deduped = [...new Set(newIds)] - if (deduped.length !== ids.length || deduped.some((id, i) => id !== ids[i])) { - await db.update(table).set({ [column]: deduped } as any).where(eq(table.id, row.id)) - updated++ - } - } - counts[name] = updated - } - - // popularItems in key_val_store - const kvRow = await db.query.keyValStore.findFirst({ - where: eq(keyValStore.key, 'popularItems'), - }) - if (kvRow && kvRow.value) { - try { - const arr: number[] = JSON.parse(kvRow.value) - if (arr.includes(fromSkuId)) { - const newArr = [...new Set(arr.map((id) => (id === fromSkuId ? toSkuId : id)))] - await db.update(keyValStore) - .set({ value: JSON.stringify(newArr) }) - .where(eq(keyValStore.key, 'popularItems')) - counts.popularItems = 1 - } - } catch { /* value not valid JSON, skip */ } - } - - // 6. Delete SKU features and the SKU itself - const featuresResult = await db.delete(skuFeatures).where(eq(skuFeatures.skuId, fromSkuId)) - counts.skuFeatures = featuresResult.changes ?? 0 - - const skuResult = await db.delete(productSkus).where(eq(productSkus.id, fromSkuId)) - counts.productSkus = skuResult.changes ?? 0 - - // 7. Delete orphaned product - const remainingSkus = await db.query.productSkus.findMany({ - where: eq(productSkus.productId, fromSku.productId), - columns: { id: true }, - }) - - let orphanedProductId: number | undefined - if (remainingSkus.length === 0) { - await db.delete(productInfo).where(eq(productInfo.id, fromSku.productId)) - orphanedProductId = fromSku.productId - counts.orphanedProduct = 1 - } - - return { - fromSkuId, - toSkuId, - orphanedProductId, - counts, - } -} diff --git a/packages/db_helper_sqlite/src/admin-apis/staff-user.ts b/packages/db_helper_sqlite/src/admin-apis/staff-user.ts index 64005da..6f18e3e 100644 --- a/packages/db_helper_sqlite/src/admin-apis/staff-user.ts +++ b/packages/db_helper_sqlite/src/admin-apis/staff-user.ts @@ -98,16 +98,6 @@ export async function getUserWithDetails(userId: number): Promise { return user || null } -export async function updateUserSuspensionStatus(userId: number, isSuspended: boolean): Promise { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }) -} - export async function checkStaffUserExists(name: string): Promise { const existingUser = await db.query.staffUsers.findFirst({ where: eq(staffUsers.name, name), diff --git a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts index 3a4c76a..036390e 100644 --- a/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts +++ b/packages/db_helper_sqlite/src/admin-apis/vendor-snippets.ts @@ -193,26 +193,6 @@ export async function getVendorOrders() { }) } -export async function getOrderItemsByOrderIds(orderIds: number[]) { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - sku: { - with: { - product: true, - features: true, - }, - }, - }, - }) -} - -export async function getOrderStatusByOrderIds(orderIds: number[]) { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }) -} - export async function updateVendorOrderItemPackaging( orderItemId: number, isPackaged: boolean diff --git a/packages/db_helper_sqlite/src/common-apis/utils.ts b/packages/db_helper_sqlite/src/common-apis/utils.ts deleted file mode 100644 index c733a7f..0000000 --- a/packages/db_helper_sqlite/src/common-apis/utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Common utility functions that can be used by both admin and user APIs - -export function formatDate(date: Date): string { - return date.toISOString() -} - -export function generateCode(prefix: string, length: number = 6): string { - const timestamp = Date.now().toString().slice(-length) - const random = Math.random().toString(36).substring(2, 8).toUpperCase() - return `${prefix}${timestamp}${random}` -} - -export function calculateDiscount(amount: number, percent: number, maxDiscount?: number): number { - let discount = (amount * percent) / 100 - if (maxDiscount && discount > maxDiscount) { - discount = maxDiscount - } - return discount -} diff --git a/packages/db_helper_sqlite/src/db/schema.ts b/packages/db_helper_sqlite/src/db/schema.ts index c2247b3..5eeed24 100644 --- a/packages/db_helper_sqlite/src/db/schema.ts +++ b/packages/db_helper_sqlite/src/db/schema.ts @@ -103,19 +103,6 @@ export const userCreds = sqliteTable('user_creds', { createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) -export const addressZones = sqliteTable('address_zones', { - id: integer().primaryKey({ autoIncrement: true }), - zoneName: text('zone_name').notNull(), - addedAt: timestampText('added_at').notNull().default(sql`CURRENT_TIMESTAMP`), -}) - -export const addressAreas = sqliteTable('address_areas', { - id: integer().primaryKey({ autoIncrement: true }), - placeName: text('place_name').notNull(), - zoneId: integer('zone_id').references(() => addressZones.id), - createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), -}) - export const addresses = sqliteTable('addresses', { id: integer().primaryKey({ autoIncrement: true }), userId: integer('user_id').notNull().references(() => users.id), @@ -132,7 +119,7 @@ export const addresses = sqliteTable('addresses', { googleMapsUrl: text('google_maps_url'), adminLatitude: real('admin_latitude'), adminLongitude: real('admin_longitude'), - zoneId: integer('zone_id').references(() => addressZones.id), + zoneId: integer('zone_id'), createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) @@ -428,12 +415,6 @@ export const notifications = sqliteTable('notifications', { createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), }) -export const productCategories = sqliteTable('product_categories', { - id: integer().primaryKey({ autoIncrement: true }), - name: text().notNull(), - description: text(), -}) - export const cartItems = sqliteTable('cart_items', { id: integer().primaryKey({ autoIncrement: true }), userId: integer('user_id').notNull().references(() => users.id), @@ -542,15 +523,6 @@ export const unloggedUserTokens = sqliteTable('unlogged_user_tokens', { lastVerified: timestampText('last_verified'), }) -export const userNotifications = sqliteTable('user_notifications', { - id: integer().primaryKey({ autoIncrement: true }), - title: text('title').notNull(), - imageUrl: text('image_url'), - createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`), - body: text('body').notNull(), - applicableUsers: jsonText('applicable_users'), -}) - // Relations export const usersRelations = relations(users, ({ many, one }) => ({ addresses: many(addresses), @@ -579,7 +551,6 @@ export const staffUsersRelations = relations(staffUsers, ({ one, many }) => ({ export const addressesRelations = relations(addresses, ({ one, many }) => ({ user: one(users, { fields: [addresses.userId], references: [users.id] }), orders: many(orders), - zone: one(addressZones, { fields: [addresses.zoneId], references: [addressZones.id] }), })) export const unitsRelations = relations(units, ({}) => ({ @@ -683,8 +654,6 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ user: one(users, { fields: [notifications.userId], references: [users.id] }), })) -export const productCategoriesRelations = relations(productCategories, ({}) => ({})) - export const cartItemsRelations = relations(cartItems, ({ one }) => ({ user: one(users, { fields: [cartItems.userId], references: [users.id] }), sku: one(productSkus, { fields: [cartItems.skuId], references: [productSkus.id] }), @@ -717,10 +686,6 @@ export const notifCredsRelations = relations(notifCreds, ({ one }) => ({ user: one(users, { fields: [notifCreds.userId], references: [users.id] }), })) -export const userNotificationsRelations = relations(userNotifications, ({}) => ({ - // No relations needed for now -})) - export const storeInfoRelations = relations(storeInfo, ({ one, many }) => ({ owner: one(staffUsers, { fields: [storeInfo.owner], references: [staffUsers.id] }), products: many(productInfo), @@ -746,15 +711,6 @@ export const productReviewsRelations = relations(productReviews, ({ one }) => ({ product: one(productInfo, { fields: [productReviews.productId], references: [productInfo.id] }), })) -export const addressZonesRelations = relations(addressZones, ({ many }) => ({ - addresses: many(addresses), - areas: many(addressAreas), -})) - -export const addressAreasRelations = relations(addressAreas, ({ one }) => ({ - zone: one(addressZones, { fields: [addressAreas.zoneId], references: [addressZones.id] }), -})) - export const productGroupInfoRelations = relations(productGroupInfo, ({ many }) => ({ memberships: many(productGroupMembership), })) diff --git a/packages/db_helper_sqlite/src/db/types.ts b/packages/db_helper_sqlite/src/db/types.ts deleted file mode 100644 index 84e2c17..0000000 --- a/packages/db_helper_sqlite/src/db/types.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { InferSelectModel } from 'drizzle-orm' -import type { - users, - addresses, - units, - productInfo, - productSkus, - skuFeatures, - deliverySlotInfo, - specialDeals, - orders, - orderItems, - payments, - notifications, - productCategories, - cartItems, - coupons, -} from '@/src/db/schema' - -export type User = InferSelectModel -export type Address = InferSelectModel -export type Unit = InferSelectModel -export type ProductInfo = InferSelectModel -export type ProductSku = InferSelectModel -export type SkuFeature = InferSelectModel -export type DeliverySlotInfo = InferSelectModel -export type SpecialDeal = InferSelectModel -export type Order = InferSelectModel -export type OrderItem = InferSelectModel -export type Payment = InferSelectModel -export type Notification = InferSelectModel -export type ProductCategory = InferSelectModel -export type CartItem = InferSelectModel -export type Coupon = InferSelectModel - -// Combined types -export type ProductWithSkus = ProductInfo & { - skus: (ProductSku & { features: SkuFeature[] })[] -} - -export type OrderWithItems = Order & { - items: (OrderItem & { sku: ProductSku & { product: ProductInfo } })[] - address: Address - slot: DeliverySlotInfo -} - -export type CartItemWithSku = CartItem & { - sku: ProductSku & { product: ProductInfo } -} diff --git a/packages/db_helper_sqlite/src/helper_methods/banner.ts b/packages/db_helper_sqlite/src/helper_methods/banner.ts deleted file mode 100644 index 67d6250..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/banner.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { db } from '../db/db_index' -import { homeBanners } from '../db/schema' -import { desc, eq } from 'drizzle-orm' - -export interface Banner { - id: number - name: string - imageUrl: string - description: string | null - productIds: number[] | null - redirectUrl: string | null - serialNum: number | null - isActive: boolean - createdAt: Date - lastUpdated: Date -} - -type BannerRow = typeof homeBanners.$inferSelect - -export async function getBanners(): Promise { - const banners = await db.query.homeBanners.findMany({ - orderBy: desc(homeBanners.createdAt), - }) as BannerRow[] - - return banners.map((banner) => ({ - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - })) -} - -export async function getBannerById(id: number): Promise { - const banner = await db.query.homeBanners.findFirst({ - where: eq(homeBanners.id, id), - }) - - if (!banner) return null - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export type CreateBannerInput = Omit - -export async function createBanner(input: CreateBannerInput): Promise { - const [banner] = await db.insert(homeBanners).values({ - name: input.name, - imageUrl: input.imageUrl, - description: input.description, - productIds: input.productIds, - redirectUrl: input.redirectUrl, - serialNum: input.serialNum, - isActive: input.isActive, - }).returning() - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export type UpdateBannerInput = Partial> - -export async function updateBanner(id: number, input: UpdateBannerInput): Promise { - const [banner] = await db.update(homeBanners) - .set({ - ...input, - lastUpdated: new Date(), - }) - .where(eq(homeBanners.id, id)) - .returning() - - return { - id: banner.id, - name: banner.name, - imageUrl: banner.imageUrl, - description: banner.description, - productIds: banner.productIds || [], - redirectUrl: banner.redirectUrl, - serialNum: banner.serialNum, - isActive: banner.isActive, - createdAt: banner.createdAt, - lastUpdated: banner.lastUpdated, - } -} - -export async function deleteBanner(id: number): Promise { - await db.delete(homeBanners).where(eq(homeBanners.id, id)) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/complaint.ts b/packages/db_helper_sqlite/src/helper_methods/complaint.ts deleted file mode 100644 index a58d7b2..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/complaint.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { db } from '../db/db_index' -import { complaints, users } from '../db/schema' -import { eq, desc, lt } from 'drizzle-orm' - -export interface Complaint { - id: number - complaintBody: string - userId: number - orderId: number | null - isResolved: boolean - response: string | null - createdAt: Date - images: string[] | null -} - -export interface ComplaintWithUser extends Complaint { - userName: string | null - userMobile: string | null -} - -export async function getComplaints( - cursor?: number, - limit: number = 20 -): Promise<{ complaints: ComplaintWithUser[]; hasMore: boolean }> { - const whereCondition = cursor ? lt(complaints.id, cursor) : undefined - - const complaintsData = await db - .select({ - id: complaints.id, - complaintBody: complaints.complaintBody, - userId: complaints.userId, - orderId: complaints.orderId, - isResolved: complaints.isResolved, - response: complaints.response, - createdAt: complaints.createdAt, - images: complaints.images, - userName: users.name, - userMobile: users.mobile, - }) - .from(complaints) - .leftJoin(users, eq(complaints.userId, users.id)) - .where(whereCondition) - .orderBy(desc(complaints.id)) - .limit(limit + 1) - - const hasMore = complaintsData.length > limit - const complaintsToReturn = hasMore ? complaintsData.slice(0, limit) : complaintsData - - return { - complaints: complaintsToReturn.map((c) => ({ - id: c.id, - complaintBody: c.complaintBody, - userId: c.userId, - orderId: c.orderId, - isResolved: c.isResolved, - response: c.response, - createdAt: c.createdAt, - images: c.images, - userName: c.userName, - userMobile: c.userMobile, - })), - hasMore, - } -} - -export async function resolveComplaint( - id: number, - response?: string -): Promise { - await db - .update(complaints) - .set({ isResolved: true, response }) - .where(eq(complaints.id, id)) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/const.ts b/packages/db_helper_sqlite/src/helper_methods/const.ts deleted file mode 100644 index 0b9b6c5..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/const.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { db } from '../db/db_index' -import { keyValStore } from '../db/schema' -import { eq } from 'drizzle-orm' -import { castConstValue } from '../lib/const-keys' - -export interface Constant { - key: string - value: any -} - -export async function getAllConstants(): Promise { - const constants = await db.select().from(keyValStore) - - return constants.map(c => ({ - key: c.key, - value: castConstValue(c.key, c.value), - })) -} - -export async function upsertConstants(constants: Constant[]): Promise { - await db.transaction(async (tx) => { - for (const { key, value } of constants) { - const castedValue = castConstValue(key, value) - const existing = await tx.query.keyValStore.findFirst({ - where: eq(keyValStore.key, key), - columns: { key: true }, - }) - - if (existing) { - await tx.update(keyValStore) - .set({ value: castedValue }) - .where(eq(keyValStore.key, key)) - } else { - await tx.insert(keyValStore) - .values({ key, value: castedValue }) - } - } - }) -} diff --git a/packages/db_helper_sqlite/src/helper_methods/coupon.ts b/packages/db_helper_sqlite/src/helper_methods/coupon.ts deleted file mode 100644 index be1c187..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/coupon.ts +++ /dev/null @@ -1,632 +0,0 @@ -import { db } from '../db/db_index'; -import { coupons, reservedCoupons, users } from '../db/schema'; -import { eq, and, like, or, inArray, lt, desc, asc } from 'drizzle-orm'; - -export interface Coupon { - id: number; - couponCode: string; - isUserBased: boolean; - discountPercent: string | null; - flatDiscount: string | null; - minOrder: string | null; - skuIds: number[] | null; - maxValue: string | null; - isApplyForAll: boolean; - validTill: Date | null; - maxLimitForUser: number | null; - exclusiveApply: boolean; - isInvalidated: boolean; - createdAt: Date; - createdBy: number; -} - -export async function getAllCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(coupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(like(coupons.couponCode, `%${search}%`)); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.coupons.findMany({ - where: whereCondition, - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - orderBy: (couponsTable: typeof coupons) => [desc(couponsTable.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export async function getCouponById(id: number): Promise { - const result = await db.query.coupons.findFirst({ - where: eq(coupons.id, id), - with: { - creator: true, - applicableUsers: { - with: { - user: true, - }, - }, - applicableProducts: { - with: { - product: true, - }, - }, - }, - }); - - return result || null; -} - -export async function invalidateCoupon(id: number): Promise { - const result = await db.update(coupons) - .set({ isInvalidated: true }) - .where(eq(coupons.id, id)) - .returning(); - - return result[0]; -} - -export interface CouponValidationResult { - valid: boolean; - message?: string; - discountAmount?: number; - coupon?: Partial; -} - -export async function validateCoupon( - code: string, - userId: number, - orderAmount: number -): Promise { - const coupon = await db.query.coupons.findFirst({ - where: and( - eq(coupons.couponCode, code.toUpperCase()), - eq(coupons.isInvalidated, false) - ), - }); - - if (!coupon) { - return { valid: false, message: "Coupon not found or invalidated" }; - } - - // Check expiry date - if (coupon.validTill && new Date(coupon.validTill) < new Date()) { - return { valid: false, message: "Coupon has expired" }; - } - - // Check if coupon applies to all users or specific user - if (!coupon.isApplyForAll && !coupon.isUserBased) { - return { valid: false, message: "Coupon is not available for use" }; - } - - // Check minimum order amount - const minOrderValue = coupon.minOrder ? parseFloat(coupon.minOrder) : 0; - if (minOrderValue > 0 && orderAmount < minOrderValue) { - return { valid: false, message: `Minimum order amount is ${minOrderValue}` }; - } - - // Calculate discount - let discountAmount = 0; - if (coupon.discountPercent) { - const percent = parseFloat(coupon.discountPercent); - discountAmount = (orderAmount * percent) / 100; - } else if (coupon.flatDiscount) { - discountAmount = parseFloat(coupon.flatDiscount); - } - - // Apply max value limit - const maxValueLimit = coupon.maxValue ? parseFloat(coupon.maxValue) : 0; - if (maxValueLimit > 0 && discountAmount > maxValueLimit) { - discountAmount = maxValueLimit; - } - - return { - valid: true, - discountAmount, - coupon: { - id: coupon.id, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - maxValue: coupon.maxValue, - } - }; -} - -export async function getReservedCoupons( - cursor?: number, - limit: number = 50, - search?: string -): Promise<{ coupons: any[]; hasMore: boolean }> { - let whereCondition = undefined; - const conditions = []; - - if (cursor) { - conditions.push(lt(reservedCoupons.id, cursor)); - } - - if (search && search.trim()) { - conditions.push(or( - like(reservedCoupons.secretCode, `%${search}%`), - like(reservedCoupons.couponCode, `%${search}%`) - )); - } - - if (conditions.length > 0) { - whereCondition = and(...conditions); - } - - const result = await db.query.reservedCoupons.findMany({ - where: whereCondition, - with: { - redeemedUser: true, - creator: true, - }, - orderBy: (reservedCouponsTable: typeof reservedCoupons) => [desc(reservedCouponsTable.createdAt)], - limit: limit + 1, - }); - - const hasMore = result.length > limit; - const couponsList = hasMore ? result.slice(0, limit) : result; - - return { coupons: couponsList, hasMore }; -} - -export interface UserMiniInfo { - id: number; - name: string; - mobile: string | null; -} - -export async function getUsersForCoupon( - search?: string, - limit: number = 20, - offset: number = 0 -): Promise<{ users: UserMiniInfo[] }> { - let whereCondition = undefined; - if (search && search.trim()) { - whereCondition = or( - like(users.name, `%${search}%`), - like(users.mobile, `%${search}%`) - ); - } - - const userList = await db.query.users.findMany({ - where: whereCondition, - columns: { - id: true, - name: true, - mobile: true, - }, - limit: limit, - offset: offset, - orderBy: (usersTable: typeof users) => [asc(usersTable.name)], - }); - - return { - users: userList.map((user: typeof users.$inferSelect) => ({ - id: user.id, - name: user.name || 'Unknown', - mobile: user.mobile, - })) - }; -} - -// ============================================================================ -// BATCH 2: Transaction Methods -// ============================================================================ - -import { couponApplicableUsers, couponApplicableProducts, orders, orderStatus } from '../db/schema'; - -export interface CreateCouponInput { - couponCode: string; - isUserBased: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - isApplyForAll: boolean; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createCouponWithRelations( - input: CreateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode: input.couponCode, - isUserBased: input.isUserBased, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - skuIds: input.skuIds, - createdBy: input.createdBy, - maxValue: input.maxValue, - isApplyForAll: input.isApplyForAll, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - }).returning(); - - // Insert applicable users - if (applicableUsers && applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: coupon.id, - userId, - })) - ); - } - - // Insert applicable products - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: coupon.id, - skuId, - })) - ); - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface UpdateCouponInput { - couponCode?: string; - isUserBased?: boolean; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - isApplyForAll?: boolean; - validTill?: Date | null; - maxLimitForUser?: number; - exclusiveApply?: boolean; - isInvalidated?: boolean; -} - -export async function updateCouponWithRelations( - id: number, - input: UpdateCouponInput, - applicableUsers?: number[], - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - // Update the coupon - const [coupon] = await tx.update(coupons) - .set({ - ...input, - }) - .where(eq(coupons.id, id)) - .returning(); - - // Update applicable users: delete existing and insert new - if (applicableUsers !== undefined) { - await tx.delete(couponApplicableUsers).where(eq(couponApplicableUsers.couponId, id)); - if (applicableUsers.length > 0) { - await tx.insert(couponApplicableUsers).values( - applicableUsers.map(userId => ({ - couponId: id, - userId, - })) - ); - } - } - - // Update applicable products: delete existing and insert new - if (applicableProducts !== undefined) { - await tx.delete(couponApplicableProducts).where(eq(couponApplicableProducts.couponId, id)); - if (applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: id, - skuId, - })) - ); - } - } - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export async function generateCancellationCoupon( - orderId: number, - staffUserId: number, - userId: number, - orderAmount: number, - couponCode: string -): Promise { - return await db.transaction(async (tx) => { - // Calculate expiry date (30 days from now) - const expiryDate = new Date(); - expiryDate.setDate(expiryDate.getDate() + 30); - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - flatDiscount: orderAmount.toString(), - minOrder: orderAmount.toString(), - maxValue: orderAmount.toString(), - validTill: expiryDate, - maxLimitForUser: 1, - createdBy: staffUserId, - isApplyForAll: false, - }).returning(); - - // Insert applicable users - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId, - }); - - // Update order_status with refund coupon ID - await tx.update(orderStatus) - .set({ refundCouponId: coupon.id }) - .where(eq(orderStatus.orderId, orderId)); - - return { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }; - }); -} - -export interface CreateReservedCouponInput { - secretCode: string; - couponCode: string; - discountPercent?: string; - flatDiscount?: string; - minOrder?: string; - skuIds?: number[] | null; - maxValue?: string; - validTill?: Date; - maxLimitForUser?: number; - exclusiveApply: boolean; - createdBy: number; -} - -export async function createReservedCouponWithProducts( - input: CreateReservedCouponInput, - applicableProducts?: number[] -): Promise { - return await db.transaction(async (tx) => { - const [coupon] = await tx.insert(reservedCoupons).values({ - secretCode: input.secretCode, - couponCode: input.couponCode, - discountPercent: input.discountPercent, - flatDiscount: input.flatDiscount, - minOrder: input.minOrder, - skuIds: input.skuIds, - maxValue: input.maxValue, - validTill: input.validTill, - maxLimitForUser: input.maxLimitForUser, - exclusiveApply: input.exclusiveApply, - createdBy: input.createdBy, - }).returning(); - - // Insert applicable products if provided - if (applicableProducts && applicableProducts.length > 0) { - await tx.insert(couponApplicableProducts).values( - applicableProducts.map(skuId => ({ - couponId: coupon.id, - skuId, - })) - ); - } - - return coupon; - }); -} - -export async function getOrCreateUserByMobile( - mobile: string -): Promise<{ id: number; mobile: string; name: string | null }> { - return await db.transaction(async (tx) => { - // Check if user exists - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - // Create new user - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - return { - id: user.id, - mobile: user.mobile, - name: user.name, - }; - }); -} - -export async function createCouponForUser( - mobile: string, - couponCode: string, - staffUserId: number -): Promise<{ coupon: Coupon; user: { id: number; mobile: string; name: string | null } }> { - return await db.transaction(async (tx) => { - // Get or create user - let user = await tx.query.users.findFirst({ - where: eq(users.mobile, mobile), - }); - - if (!user) { - const [newUser] = await tx.insert(users).values({ - name: null, - email: null, - mobile, - }).returning(); - user = newUser; - } - - // Create the coupon - const [coupon] = await tx.insert(coupons).values({ - couponCode, - isUserBased: true, - discountPercent: "20", - minOrder: "1000", - maxValue: "500", - maxLimitForUser: 1, - isApplyForAll: false, - exclusiveApply: false, - createdBy: staffUserId, - validTill: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days from now - }).returning(); - - // Associate coupon with user - await tx.insert(couponApplicableUsers).values({ - couponId: coupon.id, - userId: user.id, - }); - - return { - coupon: { - id: coupon.id, - couponCode: coupon.couponCode, - isUserBased: coupon.isUserBased, - discountPercent: coupon.discountPercent, - flatDiscount: coupon.flatDiscount, - minOrder: coupon.minOrder, - skuIds: coupon.skuIds, - maxValue: coupon.maxValue, - isApplyForAll: coupon.isApplyForAll, - validTill: coupon.validTill, - maxLimitForUser: coupon.maxLimitForUser, - exclusiveApply: coupon.exclusiveApply, - isInvalidated: coupon.isInvalidated, - createdAt: coupon.createdAt, - createdBy: coupon.createdBy, - }, - user: { - id: user.id, - mobile: user.mobile, - name: user.name, - }, - }; - }); -} - -// ============================================================================ -// Utility Functions -// ============================================================================ - -export async function checkUsersExist(userIds: number[]): Promise { - const existingUsers = await db.query.users.findMany({ - where: inArray(users.id, userIds), - columns: { id: true }, - }); - return existingUsers.length === userIds.length; -} - -export async function checkCouponExists(couponCode: string): Promise { - const existing = await db.query.coupons.findFirst({ - where: eq(coupons.couponCode, couponCode), - }); - return !!existing; -} - -export async function checkReservedCouponExists(secretCode: string): Promise { - const existing = await db.query.reservedCoupons.findFirst({ - where: eq(reservedCoupons.secretCode, secretCode), - }); - return !!existing; -} - -export async function getOrderWithUser(orderId: number): Promise { - return await db.query.orders.findFirst({ - where: eq(orders.id, orderId), - with: { - user: true, - }, - }); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/order.ts b/packages/db_helper_sqlite/src/helper_methods/order.ts deleted file mode 100644 index abc5130..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/order.ts +++ /dev/null @@ -1,269 +0,0 @@ -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 { - const [result] = await db - .update(orders) - .set({ adminNotes }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} - -export async function getOrderWithDetails(orderId: number): Promise { - 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 { - 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 { - 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( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isPackaged} = 1)` - ); - } else if (filters.packagedFilter === 'not_packaged') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isPackaged} = 0)` - ); - } - - if (filters.deliveredFilter === 'delivered') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isDelivered} = 1)` - ); - } else if (filters.deliveredFilter === 'not_delivered') { - whereConditions.push( - sql`${orders.id} IN (SELECT ${orderStatus.orderId} FROM ${orderStatus} WHERE ${orderStatus.isDelivered} = 0)` - ); - } - - 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 { - 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 { - const [result] = await db - .update(orderStatus) - .set({ isPackaged }) - .where(eq(orderStatus.orderId, orderId)) - .returning(); - return result; -} - -export async function updateOrderDelivered(orderId: number, isDelivered: boolean): Promise { - const [result] = await db - .update(orderStatus) - .set({ isDelivered }) - .where(eq(orderStatus.orderId, orderId)) - .returning(); - return result; -} - -export async function updateOrderItemPackaging( - orderItemId: number, - isPackaged: boolean, - isPackageVerified: boolean -): Promise { - 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 { - await db.update(addresses) - .set({ adminLatitude: lat, adminLongitude: lng }) - .where(eq(addresses.id, addressId)); -} - -export async function getOrderStatus(orderId: number): Promise { - return await db.query.orderStatus.findFirst({ - where: eq(orderStatus.orderId, orderId), - }); -} - -export async function cancelOrder(orderId: number, reason: string): Promise { - return await db.transaction(async (tx) => { - const order = await tx.query.orders.findFirst({ - where: eq(orders.id, orderId), - }) - - if (!order) { - return null - } - - await tx.update(orderStatus) - .set({ - isCancelled: true, - cancelReason: reason, - }) - .where(eq(orderStatus.orderId, orderId)) - - return order - }); -} - -export async function getTodaysOrders(slotId?: number): Promise { - 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 { - const [result] = await db - .update(orders) - .set({ deliveryCharge: '0' }) - .where(eq(orders.id, orderId)) - .returning(); - return result; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/product.ts b/packages/db_helper_sqlite/src/helper_methods/product.ts deleted file mode 100644 index 52c3722..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/product.ts +++ /dev/null @@ -1,125 +0,0 @@ -import { db } from '../db/db_index'; -import { productInfo, units, specialDeals, productTags, productReviews, productGroupInfo, productGroupMembership } from '../db/schema'; -import { eq, and, inArray, desc, sql, asc } from 'drizzle-orm'; - -export async function getAllProducts(): Promise { - return await db.query.productInfo.findMany({ - orderBy: productInfo.name, - with: { - unit: true, - store: true, - }, - }); -} - -export async function getProductById(id: number): Promise { - return await db.query.productInfo.findFirst({ - where: eq(productInfo.id, id), - with: { - unit: true, - store: true, - specialDeals: true, - productTags: { - with: { - tag: true, - }, - }, - }, - }); -} - -export async function createProduct(input: any): Promise { - const [product] = await db.insert(productInfo).values(input).returning(); - return product; -} - -export async function updateProduct(id: number, updates: any): Promise { - 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 { - const [product] = await db.update(productInfo) - .set({ isOutOfStock }) - .where(eq(productInfo.id, id)) - .returning(); - return product; -} - -export async function getAllUnits(): Promise { - return await db.query.units.findMany({ - orderBy: units.shortNotation, - }); -} - -export async function getAllProductTags(): Promise { - return await db.query.productTags.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function getProductReviews(productId: number): Promise { - return await db.query.productReviews.findMany({ - where: eq(productReviews.productId, productId), - with: { - user: true, - }, - orderBy: desc(productReviews.reviewTime), - }); -} - -export async function respondToReview(reviewId: number, adminResponse: string): Promise { - await db.update(productReviews) - .set({ adminResponse }) - .where(eq(productReviews.id, reviewId)); -} - -export async function getAllProductGroups(): Promise { - return await db.query.productGroupInfo.findMany({ - with: { - products: { - with: { - product: true, - }, - }, - }, - }); -} - -export async function createProductGroup(name: string): Promise { - const [group] = await db.insert(productGroupInfo).values({ groupName: name }).returning(); - return group; -} - -export async function updateProductGroup(id: number, name: string): Promise { - const [group] = await db.update(productGroupInfo) - .set({ groupName: name }) - .where(eq(productGroupInfo.id, id)) - .returning(); - return group; -} - -export async function deleteProductGroup(id: number): Promise { - await db.delete(productGroupInfo).where(eq(productGroupInfo.id, id)); -} - -export async function addProductToGroup(groupId: number, productId: number): Promise { - await db.insert(productGroupMembership).values({ groupId, productId }); -} - -export async function removeProductFromGroup(groupId: number, productId: number): Promise { - await db.delete(productGroupMembership) - .where(and( - eq(productGroupMembership.groupId, groupId), - eq(productGroupMembership.productId, productId) - )); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/slots.ts b/packages/db_helper_sqlite/src/helper_methods/slots.ts deleted file mode 100644 index 43dfc9a..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/slots.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { db } from '../db/db_index'; -import { deliverySlotInfo, productInfo, vendorSnippets } from '../db/schema'; -import { eq, and, inArray, desc } from 'drizzle-orm'; - -export async function getAllSlots(): Promise { - const slots = await db.query.deliverySlotInfo.findMany({ - orderBy: desc(deliverySlotInfo.deliveryTime), - with: { - vendorSnippets: true, - }, - }); - - // Fetch products for all slots - const allProductIds = new Set(); - for (const slot of slots) { - for (const productId of (slot.productIds || [])) { - allProductIds.add(productId); - } - } - - const productIdsArray = Array.from(allProductIds); - const productsData = productIdsArray.length > 0 - ? await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIdsArray), - }) - : []; - - const productMap = new Map(productsData.map(p => [p.id, p])); - - return slots.map(slot => ({ - ...slot, - products: (slot.productIds || []) - .map(productId => productMap.get(productId)) - .filter((p): p is NonNullable => p != null), - })); -} - -export async function getSlotById(id: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, id), - with: { - vendorSnippets: { - with: { - slot: true, - }, - }, - }, - }); - - if (!slot) { - return null; - } - - // Fetch products for this slot - const productIds = slot.productIds || []; - const productIdSet = new Set(productIds) - // const productsData = productIds.length > 0 - // ? await db.query.productInfo.findMany({ - // where: inArray(productInfo.id, productIds), - // }) - // : []; - - let productsData = productIds.length > 0 ? await db.query.productInfo.findMany({}) : []; - productsData = productsData.filter(item => productIdSet.has(item.id)) - - return { - ...slot, - products: productsData, - }; -} - -export async function createSlot(input: any): Promise { - const [slot] = await db.insert(deliverySlotInfo).values(input).returning(); - return slot; -} - -export async function updateSlot(id: number, updates: any): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set(updates) - .where(eq(deliverySlotInfo.id, id)) - .returning(); - return slot; -} - -export async function deleteSlot(id: number): Promise { - await db.delete(deliverySlotInfo).where(eq(deliverySlotInfo.id, id)); -} - -export async function getSlotProducts(slotId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - return []; - } - - const productIds = slot.productIds || []; - if (productIds.length === 0) { - return []; - } - - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - }); -} - -export async function addProductToSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - if (!currentProductIds.includes(productId)) { - await db.update(deliverySlotInfo) - .set({ productIds: [...currentProductIds, productId] }) - .where(eq(deliverySlotInfo.id, slotId)); - } -} - -export async function removeProductFromSlot(slotId: number, productId: number): Promise { - const slot = await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); - - if (!slot) { - throw new Error(`Slot ${slotId} not found`); - } - - const currentProductIds = slot.productIds || []; - const updatedProductIds = currentProductIds.filter(id => id !== productId); - - await db.update(deliverySlotInfo) - .set({ productIds: updatedProductIds }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function clearSlotProducts(slotId: number): Promise { - await db.update(deliverySlotInfo) - .set({ productIds: [] }) - .where(eq(deliverySlotInfo.id, slotId)); -} - -export async function updateSlotCapacity(slotId: number, maxCapacity: number): Promise { - const [slot] = await db.update(deliverySlotInfo) - .set({ isCapacityFull: Boolean(maxCapacity) }) - .where(eq(deliverySlotInfo.id, slotId)) - .returning(); - return slot; -} - -export async function getSlotDeliverySequence(slotId: number): Promise { - 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 { - await db.update(deliverySlotInfo) - .set({ deliverySequence: sequence }) - .where(eq(deliverySlotInfo.id, slotId)); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/staff-user.ts b/packages/db_helper_sqlite/src/helper_methods/staff-user.ts deleted file mode 100644 index 50c3bec..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/staff-user.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { db } from '../db/db_index'; -import { staffUsers, staffRoles, users, userDetails, orders } from '../db/schema'; -import { eq, or, and, lt, desc, like } from 'drizzle-orm'; - -export interface StaffUser { - id: number; - name: string; - password: string; - staffRoleId: number; - createdAt: Date; -} - -export async function getStaffUserByName(name: string): Promise { - const staff = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - - return staff || null; -} - -export async function getAllStaff(): Promise { - 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 { - 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( - like(users.name, `%${search}%`), - like(users.email, `%${search}%`), - like(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 { - 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 { - await db - .insert(userDetails) - .values({ userId, isSuspended }) - .onConflictDoUpdate({ - target: userDetails.userId, - set: { isSuspended }, - }); -} - -export async function checkStaffUserExists(name: string): Promise { - const existingUser = await db.query.staffUsers.findFirst({ - where: eq(staffUsers.name, name), - }); - return !!existingUser; -} - -export async function checkStaffRoleExists(roleId: number): Promise { - 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 { - 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 ?? roleId, - createdAt: newUser.createdAt, - }; -} - -export async function getAllRoles(): Promise { - const roles = await db.query.staffRoles.findMany({ - columns: { - id: true, - roleName: true, - }, - }); - - return roles; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/store.ts b/packages/db_helper_sqlite/src/helper_methods/store.ts deleted file mode 100644 index c5ffe44..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/store.ts +++ /dev/null @@ -1,150 +0,0 @@ -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 { - const stores = await db.query.storeInfo.findMany({ - with: { - owner: true, - }, - }); - - return stores; -} - -export async function getStoreById(id: number): Promise { - 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 { - 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.createdAt, - }; -} - -export interface UpdateStoreInput { - name?: string; - description?: string; - imageUrl?: string; - owner?: number; -} - -export async function updateStore( - id: number, - input: UpdateStoreInput, - products?: number[] -): Promise { - const [updatedStore] = await db - .update(storeInfo) - .set({ - ...input, - }) - .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.createdAt, - }; -} - -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", - }; - }); -} diff --git a/packages/db_helper_sqlite/src/helper_methods/user.ts b/packages/db_helper_sqlite/src/helper_methods/user.ts deleted file mode 100644 index c51ee27..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/user.ts +++ /dev/null @@ -1,270 +0,0 @@ -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, like } from 'drizzle-orm'; - -export async function createUserByMobile(mobile: string): Promise { - const [newUser] = await db - .insert(users) - .values({ - name: null, - email: null, - mobile, - }) - .returning(); - - return newUser; -} - -export async function getUserByMobile(mobile: string): Promise { - const [existingUser] = await db - .select() - .from(users) - .where(eq(users.mobile, mobile)) - .limit(1); - - return existingUser || null; -} - -export async function getUnresolvedComplaintsCount(): Promise { - 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} LIKE ${`%${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 { - 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 { - 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 { - 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 { - 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 { - if (search && search.trim()) { - return await db - .select({ - id: users.id, - name: users.name, - mobile: users.mobile, - }) - .from(users) - .where(sql`${users.mobile} LIKE ${`%${search.trim()}%`} OR ${users.name} LIKE ${`%${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 { - 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 { - const [incident] = await db.insert(userIncidents) - .values({ - userId, - orderId, - adminComment, - addedBy: adminUserId, - negativityScore, - }) - .returning(); - - return incident; -} diff --git a/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts b/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts deleted file mode 100644 index 20d4722..0000000 --- a/packages/db_helper_sqlite/src/helper_methods/vendor-snippets.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { db } from '../db/db_index'; -import { vendorSnippets, deliverySlotInfo, productInfo, orders, orderItems, orderStatus } from '../db/schema'; -import { eq, and, inArray, gt, sql, asc, desc } from 'drizzle-orm'; - -export async function checkVendorSnippetExists(snippetCode: string): Promise { - const existingSnippet = await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); - return !!existingSnippet; -} - -export async function getVendorSnippetById(id: number): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.id, id), - with: { - slot: true, - }, - }); -} - -export async function getVendorSnippetByCode(snippetCode: string): Promise { - return await db.query.vendorSnippets.findFirst({ - where: eq(vendorSnippets.snippetCode, snippetCode), - }); -} - -export async function getAllVendorSnippets(): Promise { - return await db.query.vendorSnippets.findMany({ - with: { - slot: true, - }, - orderBy: desc(vendorSnippets.createdAt), - }); -} - -export interface CreateVendorSnippetInput { - snippetCode: string; - slotId?: number; - productIds: number[]; - isPermanent: boolean; - validTill?: Date; -} - -export async function createVendorSnippet(input: CreateVendorSnippetInput): Promise { - 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 { - const [result] = await db.update(vendorSnippets) - .set(updates) - .where(eq(vendorSnippets.id, id)) - .returning(); - - return result; -} - -export async function deleteVendorSnippet(id: number): Promise { - await db.delete(vendorSnippets) - .where(eq(vendorSnippets.id, id)); -} - -export async function getProductsByIds(productIds: number[]): Promise { - return await db.query.productInfo.findMany({ - where: inArray(productInfo.id, productIds), - columns: { id: true, name: true }, - }); -} - -export async function getVendorSlotById(slotId: number): Promise { - return await db.query.deliverySlotInfo.findFirst({ - where: eq(deliverySlotInfo.id, slotId), - }); -} - -export async function getVendorOrdersBySlotId(slotId: number): Promise { - 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: desc(orders.createdAt), - }); -} - -export async function getOrderItemsByOrderIds(orderIds: number[]): Promise { - return await db.query.orderItems.findMany({ - where: inArray(orderItems.orderId, orderIds), - with: { - product: { - with: { - unit: true, - }, - }, - }, - }); -} - -export async function getOrderStatusByOrderIds(orderIds: number[]): Promise { - return await db.query.orderStatus.findMany({ - where: inArray(orderStatus.orderId, orderIds), - }); -} - -export async function updateVendorOrderItemPackaging(orderItemId: number, isPackaged: boolean, isPackageVerified: boolean): Promise { - await db.update(orderItems) - .set({ - is_packaged: isPackaged, - is_package_verified: isPackageVerified, - }) - .where(eq(orderItems.id, orderItemId)); -} diff --git a/packages/db_helper_sqlite/src/user-apis/order.ts b/packages/db_helper_sqlite/src/user-apis/order.ts index aa82550..0db4c32 100644 --- a/packages/db_helper_sqlite/src/user-apis/order.ts +++ b/packages/db_helper_sqlite/src/user-apis/order.ts @@ -24,32 +24,6 @@ import { coerceDate } from '../lib/date' import { runBatched } from '../lib/run-batched' import { composeSkuName, composeUnitNotation } from '../lib/sku-features' -export interface OrderItemInput { - productId: number - quantity: number - slotId: number | null -} - -export interface PlaceOrderInput { - userId: number - selectedItems: OrderItemInput[] - addressId: number - paymentMethod: 'online' | 'cod' - couponId?: number - userNotes?: string - isFlash?: boolean -} - -export interface OrderGroupData { - slotId: number | null - items: Array<{ - productId: number - quantity: number - slotId: number | null - product: typeof productInfo.$inferSelect - }> -} - export interface PlacedOrder { id: number userId: number diff --git a/packages/db_helper_sqlite/src/user-apis/tags.ts b/packages/db_helper_sqlite/src/user-apis/tags.ts deleted file mode 100644 index 2b32f81..0000000 --- a/packages/db_helper_sqlite/src/user-apis/tags.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { db } from '../db/db_index' -import { productTags } from '../db/schema' -import { eq } from 'drizzle-orm' - -export async function getAllTags(): Promise { - return db.query.productTags.findMany({ - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }) -} - -export async function getTagById(id: number): Promise { - return db.query.productTags.findFirst({ - where: eq(productTags.id, id), - with: { - // products: { - // with: { - // product: true, - // }, - // }, - }, - }) -} diff --git a/packages/shared/index.ts b/packages/shared/index.ts index 79d2335..27fd3b5 100644 --- a/packages/shared/index.ts +++ b/packages/shared/index.ts @@ -7,7 +7,5 @@ export const CACHE_FILENAMES = { banners: 'banners.json', } as const -export type CacheFilename = typeof CACHE_FILENAMES[keyof typeof CACHE_FILENAMES] - // Re-export all types from the types folder export * from './types' -- 2.45.3 From adb81a6b3d64554850c14584bb2d9e287b44c733 Mon Sep 17 00:00:00 2001 From: shafi54 <108669266+shafi-aviz@users.noreply.github.com> Date: Wed, 2 Sep 2026 23:12:49 +0530 Subject: [PATCH 02/30] enh --- .commandcode/settings.json | 14 +- .commandcode/taste/taste/taste.md | 5 +- apps/admin-ui/.detoxrc.js | 83 ------ apps/admin-ui/.expo/types/router.d.ts | 6 +- .../coupons/reserved-coupons/index.tsx | 248 ---------------- .../admin-ui/app/(drawer)/dashboard/index.tsx | 1 - .../delivery-sequences/index.tsx | 5 - .../dashboard/send-notifications/index.tsx | 52 ---- apps/admin-ui/components/AddressPlaceForm.tsx | 64 ---- apps/admin-ui/components/AddressZoneForm.tsx | 51 ---- .../components/HorizontalImageScroller.tsx | 45 --- apps/admin-ui/components/StoreForm.tsx | 13 +- apps/admin-ui/components/TabNavigation.tsx | 37 --- .../components/UserIncidentDialog.tsx | 109 ------- apps/admin-ui/components/app-container.tsx | 3 - .../components/context/auth-context.tsx | 270 ----------------- .../components/context/roles-context.tsx | 43 --- apps/admin-ui/components/dashboard-header.tsx | 115 -------- apps/admin-ui/components/date-time-picker.tsx | 278 ------------------ apps/admin-ui/components/day-account-view.tsx | 77 ----- .../admin-ui/components/ui/IconSymbol.ios.tsx | 32 -- apps/admin-ui/components/ui/IconSymbol.tsx | 41 --- .../components/ui/TabBarBackground.ios.tsx | 19 -- .../components/ui/TabBarBackground.tsx | 6 - apps/admin-ui/constants/Colors.ts | 26 -- apps/admin-ui/e2e/jest.config.js | 12 - apps/admin-ui/e2e/starter.test.js | 23 -- apps/admin-ui/hooks/useColorScheme.ts | 1 - apps/admin-ui/hooks/useColorScheme.web.ts | 21 -- apps/admin-ui/hooks/useCurrentUserId.ts | 13 - apps/admin-ui/hooks/useHideDrawerHeader.ts | 26 -- apps/admin-ui/hooks/useJWT.ts | 44 +-- apps/admin-ui/hooks/usePhonepeSdk.ts | 31 -- apps/admin-ui/hooks/useThemeColor.ts | 21 -- apps/admin-ui/scripts/reset-project.js | 112 ------- apps/admin-ui/services/axios-admin-ui.ts | 59 ---- .../services/notif-service/notif-checker.tsx | 71 ----- .../services/notif-service/notif-context.tsx | 111 ------- .../services/notif-service/notif-register.ts | 51 ---- apps/admin-ui/src/api-hooks/banner.api.ts | 26 -- apps/admin-ui/src/components/CouponForm.tsx | 1 - apps/admin-ui/utils/getCurrentUserId.ts | 14 - .../app/(drawer)/(tabs)/me/about/_layout.tsx | 9 - .../app/(drawer)/(tabs)/me/about/index.tsx | 167 ----------- .../(drawer)/(tabs)/me/my-orders/index.tsx | 28 -- apps/user-ui/app/_layout.tsx | 4 +- apps/user-ui/app/api/auth/authorize+api.ts | 55 ---- apps/user-ui/app/api/auth/callback+api.ts | 28 -- apps/user-ui/app/api/auth/token+api.ts | 3 - apps/user-ui/assets/images/adaptive-icon.png | Bin 17547 -> 0 bytes apps/user-ui/assets/images/farm2door-logo.png | Bin 1543449 -> 0 bytes apps/user-ui/assets/images/freshyo-logoo.png | Bin 292932 -> 0 bytes apps/user-ui/assets/images/freshyo-logoo.svg | 30 -- apps/user-ui/assets/images/logo_mini.jpg | Bin 83500 -> 0 bytes .../assets/images/partial-react-logo.png | Bin 5075 -> 0 bytes apps/user-ui/assets/images/react-logo.png | Bin 6341 -> 0 bytes apps/user-ui/assets/images/react-logo@2x.png | Bin 14225 -> 0 bytes apps/user-ui/assets/images/react-logo@3x.png | Bin 21252 -> 0 bytes apps/user-ui/assets/images/splash-icon.png | Bin 17547 -> 0 bytes apps/user-ui/assets/images/symbuyote.png | Bin 1060078 -> 0 bytes apps/user-ui/components/BannerCarousel.tsx | 143 --------- .../components/PaymentAndOrderComponent.tsx | 54 ---- apps/user-ui/constants/Colors.ts | 26 -- apps/user-ui/hooks/useJWT.ts | 24 -- apps/user-ui/services/toaster.tsx | 34 --- .../user-ui/src/components/google-sign-in.tsx | 38 --- apps/user-ui/src/hooks/prominent-api-hooks.ts | 21 +- apps/user-ui/src/store/addressStore.ts | 2 - apps/user-ui/src/store/centralProductStore.ts | 2 - apps/user-ui/src/store/centralSlotStore.ts | 2 - apps/user-ui/src/store/quickDeliveryStore.ts | 2 - bun.lock | 214 ++------------ change-log.txt | 134 +++++++++ scripts/dead-code/user-ui-files.js | 48 +++ scripts/dead-code/user-ui-store-actions.js | 34 +++ scripts/dead-code/user-ui-store-final.js | 36 +++ scripts/dead-code/user-ui-store-precise.js | 33 +++ scripts/dead-code/user-ui-suspects.js | 54 ++++ scripts/dead-code/user-ui-symbols.js | 62 ++++ 79 files changed, 459 insertions(+), 3108 deletions(-) delete mode 100644 apps/admin-ui/.detoxrc.js delete mode 100644 apps/admin-ui/app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx delete mode 100644 apps/admin-ui/components/AddressPlaceForm.tsx delete mode 100644 apps/admin-ui/components/AddressZoneForm.tsx delete mode 100755 apps/admin-ui/components/HorizontalImageScroller.tsx delete mode 100755 apps/admin-ui/components/TabNavigation.tsx delete mode 100644 apps/admin-ui/components/UserIncidentDialog.tsx delete mode 100755 apps/admin-ui/components/app-container.tsx delete mode 100755 apps/admin-ui/components/context/auth-context.tsx delete mode 100755 apps/admin-ui/components/context/roles-context.tsx delete mode 100755 apps/admin-ui/components/dashboard-header.tsx delete mode 100755 apps/admin-ui/components/date-time-picker.tsx delete mode 100755 apps/admin-ui/components/day-account-view.tsx delete mode 100755 apps/admin-ui/components/ui/IconSymbol.ios.tsx delete mode 100755 apps/admin-ui/components/ui/IconSymbol.tsx delete mode 100755 apps/admin-ui/components/ui/TabBarBackground.ios.tsx delete mode 100755 apps/admin-ui/components/ui/TabBarBackground.tsx delete mode 100755 apps/admin-ui/constants/Colors.ts delete mode 100644 apps/admin-ui/e2e/jest.config.js delete mode 100644 apps/admin-ui/e2e/starter.test.js delete mode 100755 apps/admin-ui/hooks/useColorScheme.ts delete mode 100755 apps/admin-ui/hooks/useColorScheme.web.ts delete mode 100755 apps/admin-ui/hooks/useCurrentUserId.ts delete mode 100755 apps/admin-ui/hooks/useHideDrawerHeader.ts delete mode 100755 apps/admin-ui/hooks/usePhonepeSdk.ts delete mode 100755 apps/admin-ui/hooks/useThemeColor.ts delete mode 100755 apps/admin-ui/scripts/reset-project.js delete mode 100644 apps/admin-ui/services/axios-admin-ui.ts delete mode 100755 apps/admin-ui/services/notif-service/notif-checker.tsx delete mode 100755 apps/admin-ui/services/notif-service/notif-context.tsx delete mode 100755 apps/admin-ui/services/notif-service/notif-register.ts delete mode 100644 apps/admin-ui/src/api-hooks/banner.api.ts delete mode 100755 apps/admin-ui/utils/getCurrentUserId.ts delete mode 100644 apps/user-ui/app/(drawer)/(tabs)/me/about/_layout.tsx delete mode 100644 apps/user-ui/app/(drawer)/(tabs)/me/about/index.tsx delete mode 100644 apps/user-ui/app/api/auth/authorize+api.ts delete mode 100644 apps/user-ui/app/api/auth/callback+api.ts delete mode 100644 apps/user-ui/app/api/auth/token+api.ts delete mode 100755 apps/user-ui/assets/images/adaptive-icon.png delete mode 100644 apps/user-ui/assets/images/farm2door-logo.png delete mode 100644 apps/user-ui/assets/images/freshyo-logoo.png delete mode 100644 apps/user-ui/assets/images/freshyo-logoo.svg delete mode 100644 apps/user-ui/assets/images/logo_mini.jpg delete mode 100755 apps/user-ui/assets/images/partial-react-logo.png delete mode 100755 apps/user-ui/assets/images/react-logo.png delete mode 100755 apps/user-ui/assets/images/react-logo@2x.png delete mode 100755 apps/user-ui/assets/images/react-logo@3x.png delete mode 100755 apps/user-ui/assets/images/splash-icon.png delete mode 100644 apps/user-ui/assets/images/symbuyote.png delete mode 100644 apps/user-ui/components/BannerCarousel.tsx delete mode 100755 apps/user-ui/constants/Colors.ts delete mode 100644 apps/user-ui/src/components/google-sign-in.tsx create mode 100644 scripts/dead-code/user-ui-files.js create mode 100644 scripts/dead-code/user-ui-store-actions.js create mode 100644 scripts/dead-code/user-ui-store-final.js create mode 100644 scripts/dead-code/user-ui-store-precise.js create mode 100644 scripts/dead-code/user-ui-suspects.js create mode 100644 scripts/dead-code/user-ui-symbols.js diff --git a/.commandcode/settings.json b/.commandcode/settings.json index d373f99..34a0542 100644 --- a/.commandcode/settings.json +++ b/.commandcode/settings.json @@ -32,7 +32,19 @@ "Shell(python3 -c import sys,json; d=json.load(sys.stdin); print('products:', len(d.get('products',[]))); print('tags:', [t['tagName'] for t in d.get('tags',[])][:12]) 2 >& 1)", "Shell(curl -s http://localhost:4174/src/styles.css 2 > /dev/null)", "Shell(printf:*)", - "Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo/apps/backend && python3 - <<'EOF' import re with open('dumps/local_8_aug.sql') as f: content = f.read() # Find all CREATE TABLE positions and all REFERENCES target tables create_positions = {} # table name -> line for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(', content): create_positions[m.group(1)] = content.count('\\n', 0, m.start()) + 1 # Verify each REFERENCES target is defined before the referencing CREATE errors = [] for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(([^;]*?)\\)', content, re.S): table = m.group(1) table_line = content.count('\\n', 0, m.start()) + 1 body = m.group(2) for ref in re.findall(r'REFERENCES `?([A-Za-z_]+)`?\\(', body): if ref in create_positions and create_positions[ref] > table_line: errors.append(f\"{table} (line {table_line}) references {ref} (defined line {create_positions[ref]})\") if errors: print(\"FAIL - out of order:\") for e in errors: print(\" \", e) else: print(\"PASS - all REFERENCES resolve to tables defined earlier\") EOF)" + "Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo/apps/backend && python3 - <<'EOF' import re with open('dumps/local_8_aug.sql') as f: content = f.read() # Find all CREATE TABLE positions and all REFERENCES target tables create_positions = {} # table name -> line for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(', content): create_positions[m.group(1)] = content.count('\\n', 0, m.start()) + 1 # Verify each REFERENCES target is defined before the referencing CREATE errors = [] for m in re.finditer(r'CREATE TABLE(?: IF NOT EXISTS)? \"?([A-Za-z_]+)\"? \\(([^;]*?)\\)', content, re.S): table = m.group(1) table_line = content.count('\\n', 0, m.start()) + 1 body = m.group(2) for ref in re.findall(r'REFERENCES `?([A-Za-z_]+)`?\\(', body): if ref in create_positions and create_positions[ref] > table_line: errors.append(f\"{table} (line {table_line}) references {ref} (defined line {create_positions[ref]})\") if errors: print(\"FAIL - out of order:\") for e in errors: print(\" \", e) else: print(\"PASS - all REFERENCES resolve to tables defined earlier\") EOF)", + "Shell(find packages/db_helper_sqlite/src packages/db_helper_postgres/src -name \"*.ts\" | xargs wc -l | tail -5 && echo \"=== apps imports of db_helper ===\" && grep -rn \"db_helper\" apps packages --include=\"*.ts\" --include=\"*.tsx\" --include=\"*.json\" -l | grep -v node_modules | grep -v \"db_helper_sqlite/\\|db_helper_postgres/\\|\\.turbo\\|dist\" | sort -u)", + "Shell(cat apps/backend/wrangler.toml 2>/dev/null | head -30; echo \"=== package.json ===\"; cat apps/backend/package.json; echo \"=== how build/dev resolves ===\"; ls apps/backend/.wrangler/tmp/dev-*/worker.js 2>/dev/null | head -1 | xargs grep -o \"sqliteService\\|getAllSlotsWithProductsForCache\" 2>/dev/null | head -3; echo \"=== tsx alias config? ===\"; grep -rn \"tsx\\|alias\" apps/backend/package.json apps/backend/wrangler.toml 2>/dev/null | head; echo \"=== node_modules links ===\"; ls -la apps/backend/node_modules/ 2>/dev/null | grep -i \"db_helper\\|sqlite\\|postgres\" | head)", + "Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo && for pkg in packages/db_helper_sqlite packages/db_helper_postgres; do echo \"########## $pkg ##########\"; for f in $(find $pkg/src -name \"*.ts\" | sort); do # relative imports only grep -oE \"from '(\\.\\.?/[^']+)'\" $f | sed \"s/from '//;s/'//\" | while read imp; do resolved=$(cd $(dirname $f) && node -e \"const p=require('path');try{const r=p.resolve('$imp');console.log(r.startsWith(process.cwd())?p.relative(process.cwd(),r):'')}catch(e){}\" 2>/dev/null) if [ -n \"$resolved\" ] && [ -f \"$resolved.ts\" ] || [ -n \"$resolved\" ] && [ -f \"$resolved\" ]; then echo \"$(echo $f | sed 's|^packages/||') -> $resolved\"; fi done; done; done 2>/dev/null | sort -u | grep -v \"db_index\\|schema.ts ->\" | head -80)", + "Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const files = []; (function walk(d){ for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkg); const edges = {}; for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { const base = path.dirname(f); let target = path.resolve(base, m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(process.cwd()) && fs.existsSync(target)) { const rel = path.relative(pkg, target); if (!rel.startsWith(\"..\")) { (edges[f] = edges[f] || []).push(rel); } } } } const allRel = files.map(f => path.relative(pkg, f)); const imported = new Set(Object.values(edges).flat()); const deadFiles = allRel.filter(r => r !== \"index.ts\" && !imported.has(r) && !r.includes(\"/db/seed\") && !r.includes(\"drizzle.config\")); // internal cross-file usage counts: who imports this rel path (excluding itself) for (const f of files) { const rel = path.relative(pkg, f); const importers = Object.entries(edges).filter(([src, tgts]) => tgts.includes(rel) && src !== f).map(([src]) => path.relative(pkg, src)); if (rel.endsWith(\".ts\") && importers.length) console.log(rel + \" <== imported by: \" + importers.join(\", \")); } console.log(\"--- files with NO internal importers (candidates for dead file): ---\"); console.log(deadFiles.join(\"\\n\")); } )", + "Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const files = []; (function walk(d){ if (d.includes(\"node_modules\")) return; for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkg); const imported = new Set(); const importers = {}; // target -> [sources] for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { let target = path.resolve(path.dirname(f), m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(pkg) && fs.existsSync(target)) { const rel = path.relative(pkg, target); imported.add(rel); (importers[rel] = importers[rel] || []).push(path.relative(pkg, f)); } } } const excluded = new Set([\"index.ts\",\"drizzle.config.ts\",\"src/db/seed.ts\",\"src/db/db_index.ts\",\"src/db/schema.ts\"]); const noImporter = files.map(f=>path.relative(pkg,f)).filter(r => !imported.has(r) && !excluded.has(r) && !r.includes(\"drizzle.config\")); console.log(\"Files never imported internally (incl. index.ts not importing them? checked separately):\"); for (const r of noImporter) console.log(\" \" + r); // now check which of those are imported by index.ts explicitly const idx = fs.readFileSync(path.join(pkg,\"index.ts\"),\"utf8\"); for (const r of noImporter) { const basename = path.basename(r, \".ts\"); if (!idx.includes(basename)) console.log(\" [also NOT in index.ts] \" + r); } } )", + "Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); const pkg = \"packages/db_helper_sqlite\"; const idx = fs.readFileSync(path.join(pkg,\"index.ts\"),\"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(idx))) { const raw = m[1]; let target = path.resolve(pkg, raw); if (!fs.existsSync(target) && fs.existsSync(target+\".ts\")) target += \".ts\"; console.log(JSON.stringify(raw), \"exists:\", fs.existsSync(target), \"rel:\", path.relative(pkg, target), \"startsWithPkg:\", target.startsWith(pkg), \"target:\", target); } )", + "Shell(node -e const fs = require(\"fs\"), path = require(\"path\"); const cwd = process.cwd(); for (const pkg of [\"packages/db_helper_sqlite\", \"packages/db_helper_postgres\"]) { console.log(\"########## \" + pkg + \" ##########\"); const pkgAbs = path.resolve(pkg); const files = []; (function walk(d){ if (d.includes(\"node_modules\")) return; for (const e of fs.readdirSync(d)) { const p = path.join(d,e); if (fs.statSync(p).isDirectory()) walk(p); else if (e.endsWith(\".ts\")) files.push(p); } })(pkgAbs); const imported = new Set(); for (const f of files) { const src = fs.readFileSync(f, \"utf8\"); const re = /from\\s+[\\x27\"](\\.\\.?\\/[^\\x27\"]+)[\\x27\"]/g; let m; while ((m = re.exec(src))) { let target = path.resolve(path.dirname(f), m[1]); if (!fs.existsSync(target) && fs.existsSync(target + \".ts\")) target += \".ts\"; if (target.startsWith(pkgAbs) && fs.existsSync(target) && target !== f) { imported.add(path.relative(pkgAbs, target)); } } } const allRel = files.map(f => path.relative(pkgAbs, f)).sort(); console.log(\"Total ts files:\", allRel.length); for (const r of allRel) { if (r === \"index.ts\" || r.includes(\"drizzle.config\")) continue; if (!imported.has(r)) console.log(\" NEVER-IMPORTED FILE: \" + r); } } )", + "Shell(for:*)", + "Shell(do:*)", + "Shell(done:*)", + "Shell(cd /Users/mohammedshafiuddin/WebDev/freshyo && echo \"=== product.ts trpc import line & usage count ===\" && sed -n '1,40p' apps/backend/src/trpc/apis/admin-apis/apis/product.ts && echo \"=== count occurrences of each beyond import ===\" && for fn in checkUnitExists getProductImagesById replaceProductTags; do echo \"$fn: total=$(grep -c \"$fn\" apps/backend/src/trpc/apis/admin-apis/apis/product.ts)\"; done)", + "Shell(node -e const fs = require(\"fs\"); const parse = (p) => { const src = fs.readFileSync(p,\"utf8\"); const names = new Set(); // export { a, b as c, ... } from / re-export lists const re = /\\bexport\\s*\\{([^}]*)\\}/g; let m; while ((m = re.exec(src))) { const body = m[1]; for (let line of body.split(\",\")) { line = line.trim().replace(/\\/\\/.*$/,\"\").trim(); if (!line) continue; const asMatch = line.match(/^(.+?)\\s+as\\s+(.+)$/); if (asMatch) names.add(asMatch[2].trim()); else if (line.match(/^[A-Za-z_$][\\w$]*$/)) names.add(line); } } return names; }; const sqlite = parse(\"packages/db_helper_sqlite/index.ts\"); const pg = parse(\"packages/db_helper_postgres/index.ts\"); const onlySqlite = [...sqlite].filter(x=>!pg.has(x)).sort(); const onlyPg = [...pg].filter(x=>!sqlite.has(x)).sort(); console.log(\"ONLY IN SQLITE INDEX:\", onlySqlite.join(\", \")); console.log(\"ONLY IN POSTGRES INDEX:\", onlyPg.join(\", \")); )" ], "deny": [], "defaultMode": "default" diff --git a/.commandcode/taste/taste/taste.md b/.commandcode/taste/taste/taste.md index 60c4b9d..53fa3f5 100644 --- a/.commandcode/taste/taste/taste.md +++ b/.commandcode/taste/taste/taste.md @@ -26,7 +26,7 @@ - Wants feature parity maintained between the web-ui and user-ui apps (port logic/data patterns, rebuild UI per platform) โ€” e.g., when he asks for a link in the cart page "at the coupons section" of @apps/user-ui/components/cart-page.tsx, he means mirror user-ui's coupons section (card, header, icon) in web-ui and add the requested link there; and conversely asks for features added in one app to be added to the sibling app "too" (e.g., "add the coupon redirection on the user-ui cart-page too"), expecting the agent to locate the equivalent component/route and replicate it. Confidence: 0.98 - Wants the web-ui home page section/component order to match the reference user-ui home page exactly (e.g., "on the home page have the order same as that of @apps/user-ui/.../home/index.tsx"), including when deciding placement of sections like Explore, Stores, Slots, and All Products. Confidence: 0.7 - Wants brand/logo spots to use the single real brand logo asset (shared across apps, e.g., the Freshyo splash logo) displayed as an image, not a generic/decorative icon (e.g., a "meat piece" Meat/Beef icon) โ€” web-ui should be on par with user-ui and show the same logo; applies to favicon, PWA icons, and every in-UI branding spot (sidebar, topbar, login, home banner). Confidence: 0.85 -- Prefers dead-code audits to be documented in a markdown file. Confidence: 0.8 +- Prefers dead-code audits to be documented in a markdown file. Confidence: 0.9 - Prefers detailed technical documentation of system architecture, data models, flows, and integrations in markdown format. Confidence: 0.8 - Wants edge cases explicitly enumerated when documenting or analyzing existing code/systems. Confidence: 0.8 - Values end-to-end analysis of features (architecture, data model, flows, integrations, and edge cases) when asked to explain how something works. Confidence: 0.7 @@ -90,6 +90,9 @@ er-ui/app/(drawer)/(tabs)/home/index.tsx, I want similar here") and expects the - For bulk/sequential generation from a code pattern (e.g., ABC12 โ†’ ABC13...), expects numbering to continue from the HIGHEST existing value so regeneration never re-emits already-entered codes โ€” after entering ABC12, the next batch must start at ABC13, not restart at 12 (the first matching code). Confidence: 0.6 - Dislikes UI that renders transient loading states as definitive business states โ€” e.g., the home page showed every item as "out of stock" until slot data loaded, because the out-of-stock check treated "no slot yet" (data not loaded) as genuinely out of stock. Expects a "data loaded" guard so the UI never flashes a wrong definitive status (out of stock) before data arrives, while still applying real out-of-stock detection once loaded. Confidence: 0.7 - When deleting a record, prefers the existing soft-delete/invalidate path over hard-deleting rows: the supported delete already sets `isInvalidated: true` (keeping history/audit and avoiding broken FK joins), and a raw hard delete should only be considered when confirmed nothing references the row (no usage records), with related rows (couponApplicableUsers/Products) removed transactionally to avoid orphans. Confidence: 0.75 +- When commissioning a dead-code audit of an app/package, expects a "close analysis" that is exhaustive across every category โ€” dead, unreachable, and unused code: orphaned files, unreachable/unlinked routes, dormant or disabled-by-flag features, dead branches and commented-out blocks, and unused imports/exports โ€” finding "every such code which serves no purpose" rather than only obviously-unreferenced files; findings should distinguish verified-dead items from merely dormant/judgment-call items. Confidence: 0.65 +- When an analysis/audit has been produced for one app in the monorepo, expects the same analysis run for the sibling apps too โ€” after the user-ui dead-code audit he immediately asked "now do the same for the @apps/admin-ui" โ€” applying the identical exhaustive methodology and document style to each named app. Confidence: 0.85 +- For per-app analysis documents, expects a consistent naming pattern following the established convention (`user_ui_dpsk.md` โ†’ `admin_ui_dpsk.md`, i.e., `{app}_dpsk.md` at the repo root) rather than an ad-hoc filename โ€” "make another md file with same naming pattern". Confidence: 0.9 e changes. Confidence: 0.95 er-ui/app/(drawer)/(tabs)/home/index.tsx, I want similar here") and expects the agent to mirror that pattern exactly, including details like item counts and container styling. Confidence: 0.9 als) rather than introducing new brand palettes; approval of a redesign's layout/composition does not imply approval of color or theme changes. Confidence: 0.95 diff --git a/apps/admin-ui/.detoxrc.js b/apps/admin-ui/.detoxrc.js deleted file mode 100644 index 35b91ea..0000000 --- a/apps/admin-ui/.detoxrc.js +++ /dev/null @@ -1,83 +0,0 @@ -/** @type {Detox.DetoxConfig} */ -module.exports = { - testRunner: { - args: { - '$0': 'jest', - config: 'e2e/jest.config.js' - }, - jest: { - setupTimeout: 120000 - } - }, - apps: { - 'ios.debug': { - type: 'ios.app', - binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/YOUR_APP.app', - build: 'xcodebuild -workspace ios/YOUR_APP.xcworkspace -scheme YOUR_APP -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build' - }, - 'ios.release': { - type: 'ios.app', - binaryPath: 'ios/build/Build/Products/Release-iphonesimulator/YOUR_APP.app', - build: 'xcodebuild -workspace ios/YOUR_APP.xcworkspace -scheme YOUR_APP -configuration Release -sdk iphonesimulator -derivedDataPath ios/build' - }, - 'android.debug': { - type: 'android.apk', - binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk', - build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug', - reversePorts: [ - 8081 - ] - }, - 'android.release': { - type: 'android.apk', - binaryPath: 'android/app/build/outputs/apk/release/app-release.apk', - build: 'cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release' - } - }, - devices: { - simulator: { - type: 'ios.simulator', - device: { - type: 'iPhone 15' - } - }, - attached: { - type: 'android.attached', - device: { - adbName: '.*' - } - }, - emulator: { - type: 'android.emulator', - device: { - avdName: 'Pixel_3a_API_30_x86' - } - } - }, - configurations: { - 'ios.sim.debug': { - device: 'simulator', - app: 'ios.debug' - }, - 'ios.sim.release': { - device: 'simulator', - app: 'ios.release' - }, - 'android.att.debug': { - device: 'attached', - app: 'android.debug' - }, - 'android.att.release': { - device: 'attached', - app: 'android.release' - }, - 'android.emu.debug': { - device: 'emulator', - app: 'android.debug' - }, - 'android.emu.release': { - device: 'emulator', - app: 'android.release' - } - } -}; diff --git a/apps/admin-ui/.expo/types/router.d.ts b/apps/admin-ui/.expo/types/router.d.ts index e2dc4a9..fe7d472 100644 --- a/apps/admin-ui/.expo/types/router.d.ts +++ b/apps/admin-ui/.expo/types/router.d.ts @@ -6,9 +6,9 @@ export * from 'expo-router'; declare module 'expo-router' { export namespace ExpoRouter { export interface __routes { - hrefInputParams: { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/login`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/reserved-coupons` | `/dashboard/coupons/reserved-coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownInputParams & { id: string | number; } }; - hrefOutputParams: { pathname: Router.RelativePathString, params?: Router.UnknownOutputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownOutputParams } | { pathname: `/`; params?: Router.UnknownOutputParams; } | { pathname: `/login`; params?: Router.UnknownOutputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/reserved-coupons` | `/dashboard/coupons/reserved-coupons`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownOutputParams & { id: string; } }; - href: Router.RelativePathString | Router.ExternalPathString | `/${`?${string}` | `#${string}` | ''}` | `/login${`?${string}` | `#${string}` | ''}` | `/_sitemap${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard${`?${string}` | `#${string}` | ''}` | `/dashboard${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/complaints${`?${string}` | `#${string}` | ''}` | `/dashboard/complaints${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/coupons/create${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/coupons${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/coupons/reserved-coupons${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons/reserved-coupons${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app/all-items-order${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app/all-items-order${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app/popular-items${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app/popular-items${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners/create${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/delivery-sequences${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/orders${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/prices-overview${`?${string}` | `#${string}` | ''}` | `/dashboard/prices-overview${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings/create${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/add${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/order${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/order${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/add${`?${string}` | `#${string}` | ''}` | `/dashboard/products/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/products/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products${`?${string}` | `#${string}` | ''}` | `/dashboard/products${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/rebalance-orders${`?${string}` | `#${string}` | ''}` | `/dashboard/rebalance-orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/send-notifications${`?${string}` | `#${string}` | ''}` | `/dashboard/send-notifications${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/add${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots${`?${string}` | `#${string}` | ''}` | `/dashboard/slots${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/slot-details${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/slot-details${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores/add${`?${string}` | `#${string}` | ''}` | `/dashboard/stores/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/stores/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores${`?${string}` | `#${string}` | ''}` | `/dashboard/stores${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/user-management${`?${string}` | `#${string}` | ''}` | `/dashboard/user-management${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/vendor-snippets${`?${string}` | `#${string}` | ''}` | `/dashboard/vendor-snippets${`?${string}` | `#${string}` | ''}` | { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/login`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/reserved-coupons` | `/dashboard/coupons/reserved-coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownInputParams; } | `${'/(drawer)'}/dashboard/coupons/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/order-details/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/order-details/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/detail/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/products/detail/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/user-management/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/user-management/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownInputParams & { id: string | number; } }; + hrefInputParams: { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/login`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownInputParams & { id: string | number; } }; + hrefOutputParams: { pathname: Router.RelativePathString, params?: Router.UnknownOutputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownOutputParams } | { pathname: `/`; params?: Router.UnknownOutputParams; } | { pathname: `/login`; params?: Router.UnknownOutputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownOutputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownOutputParams & { id: string; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownOutputParams & { id: string; } }; + href: Router.RelativePathString | Router.ExternalPathString | `/${`?${string}` | `#${string}` | ''}` | `/login${`?${string}` | `#${string}` | ''}` | `/_sitemap${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard${`?${string}` | `#${string}` | ''}` | `/dashboard${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/complaints${`?${string}` | `#${string}` | ''}` | `/dashboard/complaints${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/coupons/create${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/coupons${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app/all-items-order${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app/all-items-order${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/customize-app/popular-items${`?${string}` | `#${string}` | ''}` | `/dashboard/customize-app/popular-items${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners/create${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/delivery-sequences${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/orders${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/prices-overview${`?${string}` | `#${string}` | ''}` | `/dashboard/prices-overview${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings/create${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings/create${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/add${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/order${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/order${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-tags/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/product-tags/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/add${`?${string}` | `#${string}` | ''}` | `/dashboard/products/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/products/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products${`?${string}` | `#${string}` | ''}` | `/dashboard/products${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/rebalance-orders${`?${string}` | `#${string}` | ''}` | `/dashboard/rebalance-orders${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/send-notifications${`?${string}` | `#${string}` | ''}` | `/dashboard/send-notifications${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/add${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots${`?${string}` | `#${string}` | ''}` | `/dashboard/slots${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/slot-details${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/slot-details${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores/add${`?${string}` | `#${string}` | ''}` | `/dashboard/stores/add${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores/edit${`?${string}` | `#${string}` | ''}` | `/dashboard/stores/edit${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/stores${`?${string}` | `#${string}` | ''}` | `/dashboard/stores${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/user-management${`?${string}` | `#${string}` | ''}` | `/dashboard/user-management${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/vendor-snippets${`?${string}` | `#${string}` | ''}` | `/dashboard/vendor-snippets${`?${string}` | `#${string}` | ''}` | { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/login`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard` | `/dashboard`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/complaints` | `/dashboard/complaints`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons/create` | `/dashboard/coupons/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/coupons` | `/dashboard/coupons`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/all-items-order` | `/dashboard/customize-app/all-items-order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app` | `/dashboard/customize-app`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/customize-app/popular-items` | `/dashboard/customize-app/popular-items`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/create` | `/dashboard/dashboard-banners/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners` | `/dashboard/dashboard-banners`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders` | `/dashboard/manage-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/delivery-sequences` | `/dashboard/manage-orders/delivery-sequences`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/orders` | `/dashboard/manage-orders/orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/prices-overview` | `/dashboard/prices-overview`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/create` | `/dashboard/product-groupings/create`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-groupings` | `/dashboard/product-groupings`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/add` | `/dashboard/product-tags/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags` | `/dashboard/product-tags`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/order` | `/dashboard/product-tags/order`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/product-tags/edit` | `/dashboard/product-tags/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/add` | `/dashboard/products/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products/edit` | `/dashboard/products/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/products` | `/dashboard/products`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/rebalance-orders` | `/dashboard/rebalance-orders`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/send-notifications` | `/dashboard/send-notifications`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/add` | `/dashboard/slots/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots` | `/dashboard/slots`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/slots/slot-details` | `/dashboard/slots/slot-details`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/add` | `/dashboard/stores/add`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores/edit` | `/dashboard/stores/edit`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/stores` | `/dashboard/stores`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/user-management` | `/dashboard/user-management`; params?: Router.UnknownInputParams; } | { pathname: `${'/(drawer)'}/dashboard/vendor-snippets` | `/dashboard/vendor-snippets`; params?: Router.UnknownInputParams; } | `${'/(drawer)'}/dashboard/coupons/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/coupons/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/dashboard-banners/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/dashboard-banners/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/manage-orders/order-details/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/manage-orders/order-details/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/product-groupings/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/product-groupings/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/products/detail/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/products/detail/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/slots/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/slots/edit/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `${'/(drawer)'}/dashboard/user-management/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | `/dashboard/user-management/${Router.SingleRoutePart}${`?${string}` | `#${string}` | ''}` | { pathname: `${'/(drawer)'}/dashboard/coupons/edit/[id]` | `/dashboard/coupons/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/dashboard-banners/edit/[id]` | `/dashboard/dashboard-banners/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/manage-orders/order-details/[id]` | `/dashboard/manage-orders/order-details/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/product-groupings/edit/[id]` | `/dashboard/product-groupings/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/products/detail/[id]` | `/dashboard/products/detail/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/slots/edit/[id]` | `/dashboard/slots/edit/[id]`, params: Router.UnknownInputParams & { id: string | number; } } | { pathname: `${'/(drawer)'}/dashboard/user-management/[id]` | `/dashboard/user-management/[id]`, params: Router.UnknownInputParams & { id: string | number; } }; } } } diff --git a/apps/admin-ui/app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx b/apps/admin-ui/app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx deleted file mode 100644 index 6c32c32..0000000 --- a/apps/admin-ui/app/(drawer)/dashboard/coupons/reserved-coupons/index.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import React, { useState, useCallback, useMemo } from 'react'; -import { View, TouchableOpacity, Alert, RefreshControl } from 'react-native'; -import { MaterialCommunityIcons } from '@expo/vector-icons'; -import { LinearGradient } from 'expo-linear-gradient'; -import { tw, MyButton, MyText, SearchBar, MyFlatList, useMarkDataFetchers, MyTouchableOpacity, BottomDropdown } from 'common-ui'; -import useManualRefresh from 'common-ui/hooks/useManualRefresh'; -import { trpc } from '@/src/trpc-client'; -import { useRouter } from 'expo-router'; -import { useInfiniteQuery } from '@tanstack/react-query'; - -const ReservedCouponItem = ({ item }: { item: any }) => { - const getStatus = () => { - if (item.isRedeemed) return 'redeemed'; - if (item.validTill && new Date(item.validTill) <= new Date()) return 'expired'; - return 'active'; - }; - - const status = getStatus(); - - const getBorderColor = () => { - if (status === 'active') return 'border-green-500'; - if (status === 'expired') return 'border-yellow-500'; - return 'border-blue-500'; - }; - - const getBgColor = () => { - if (status === 'active') return 'bg-green-100'; - if (status === 'expired') return 'bg-yellow-100'; - return 'bg-blue-100'; - }; - - const getTextColor = () => { - if (status === 'active') return 'text-green-600'; - if (status === 'expired') return 'text-yellow-600'; - return 'text-blue-600'; - }; - - const getIconColor = () => { - if (status === 'active') return '#10b981'; - if (status === 'expired') return '#f59e0b'; - return '#3b82f6'; - }; - - return ( - - - - - - - {item.secretCode} - Coupon: {item.couponCode} - - - - {status === 'active' ? 'Active' : status === 'expired' ? 'Expired' : 'Redeemed'} - - - - - - - Discount: {item.discountPercent ? `${item.discountPercent}% off` : item.flatDiscount ? `โ‚น${item.flatDiscount} off` : 'N/A'} - - - Min Order: {item.minOrder ? `โ‚น${item.minOrder}` : 'None'} - Max: {item.maxValue ? `โ‚น${item.maxValue}` : 'None'} - - - Valid Till: {item.validTill ? new Date(item.validTill).toLocaleDateString() : 'No expiry'} - - - - {item.isRedeemed && item.redeemedUser && ( - - - Redeemed by: {item.redeemedUser.name || 'Unknown'} ({item.redeemedUser.mobile}) - - - Redeemed on: {item.redeemedAt ? new Date(item.redeemedAt).toLocaleDateString() : 'N/A'} - - - )} - - - - Created by: {item.creator?.name || 'Unknown'} - - - - ); -}; - -export default function ReservedCoupons() { - const router = useRouter(); - const [refreshing, setRefreshing] = useState(false); - const [searchQuery, setSearchQuery] = useState(''); - const [statusFilters, setStatusFilters] = useState([]); - - const { - data, - fetchNextPage, - hasNextPage, - isLoading, - isFetchingNextPage, - refetch, - } = trpc.admin.coupon.getReservedCoupons.useInfiniteQuery( - { limit: 50 }, - { - getNextPageParam: (lastPage) => lastPage.nextCursor, - } - ); - - const coupons = data?.pages.flatMap(page => page.coupons) || []; - - const getStatus = (coupon: any) => { - if (coupon.isRedeemed) return 'redeemed'; - if (coupon.validTill && new Date(coupon.validTill) <= new Date()) return 'expired'; - return 'active'; - }; - - const filteredCoupons = useMemo(() => { - let filtered = coupons.filter(coupon => - coupon.secretCode.toLowerCase().includes(searchQuery.toLowerCase()) || - coupon.couponCode.toLowerCase().includes(searchQuery.toLowerCase()) - ); - if (statusFilters.length > 0) { - filtered = filtered.filter(coupon => statusFilters.includes(getStatus(coupon))); - } - return filtered; - }, [coupons, searchQuery, statusFilters]); - - const handleRefresh = useCallback(async () => { - setRefreshing(true); - await refetch(); - setRefreshing(false); - }, [refetch]); - - useManualRefresh(() => refetch()); - - useMarkDataFetchers(() => { - refetch(); - }); - - if (isLoading) { - return ( - - - - - Loading Reserved Coupons... - - ); - } - - return ( - - - - - - setStatusFilters(value as string[])} - multiple={true} - triggerComponent={({ onPress }) => ( - - - - )} - /> - - item.id.toString()} - renderItem={({ item }) => } - refreshControl={ - - } - contentContainerStyle={tw`px-4 pb-4`} - onEndReached={() => { - if (hasNextPage && !isFetchingNextPage) { - fetchNextPage(); - } - }} - onEndReachedThreshold={0.5} - ListEmptyComponent={ - searchQuery ? ( - - - - - No Results - No reserved coupons match “{searchQuery}” - setSearchQuery('')} style={tw`bg-gray-500`}> - Clear Search - - - ) : ( - - - - - No Reserved Coupons Yet - Create your first reserved coupon to start offering secret discounts - router.push('/(drawer)/dashboard/coupons/create')} style={tw`bg-blue-500`}> - - - Create Reserved Coupon - - - - ) - } - /> - - {/* FAB for Add New Reserved Coupon */} - router.push('/(drawer)/dashboard/coupons/create')} - activeOpacity={0.95} - style={{ position: 'absolute', bottom: 32, right: 24, zIndex: 100 }} - > - - - - - - ); -} \ No newline at end of file diff --git a/apps/admin-ui/app/(drawer)/dashboard/index.tsx b/apps/admin-ui/app/(drawer)/dashboard/index.tsx index 003723e..fbc31b9 100644 --- a/apps/admin-ui/app/(drawer)/dashboard/index.tsx +++ b/apps/admin-ui/app/(drawer)/dashboard/index.tsx @@ -3,7 +3,6 @@ import { View, ScrollView, Pressable } from 'react-native'; import { useRouter } from 'expo-router'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; import { MyText, tw } from 'common-ui'; -import { LinearGradient } from 'expo-linear-gradient'; import { theme } from 'common-ui/src/theme'; import { trpc } from '@/src/trpc-client'; import { useNavigationTarget } from 'common-ui/hooks/useNavigationTarget'; diff --git a/apps/admin-ui/app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx b/apps/admin-ui/app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx index 0519753..5013288 100644 --- a/apps/admin-ui/app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx +++ b/apps/admin-ui/app/(drawer)/dashboard/manage-orders/delivery-sequences/index.tsx @@ -537,11 +537,6 @@ export default function DeliverySequences() { ) : ( - {/* - - Long press an item to drag and reorder - - */} ( diff --git a/apps/admin-ui/app/(drawer)/dashboard/send-notifications/index.tsx b/apps/admin-ui/app/(drawer)/dashboard/send-notifications/index.tsx index a52ddbb..f892d1b 100644 --- a/apps/admin-ui/app/(drawer)/dashboard/send-notifications/index.tsx +++ b/apps/admin-ui/app/(drawer)/dashboard/send-notifications/index.tsx @@ -14,11 +14,8 @@ import { tw, MyTextInput, BottomDropdown, - ImageUploader, } from 'common-ui'; import { trpc } from '@/src/trpc-client'; -import usePickImage from 'common-ui/src/components/use-pick-image'; -import { useUploadToObjectStorage } from '@/hooks/useUploadToObjectStore'; interface User { id: number; @@ -32,8 +29,6 @@ export default function SendNotifications() { const [selectedUserIds, setSelectedUserIds] = useState([]); const [title, setTitle] = useState(''); const [message, setMessage] = useState(''); - const [selectedImage, setSelectedImage] = useState<{ blob: Blob; mimeType: string } | null>(null); - const [displayImage, setDisplayImage] = useState<{ uri?: string } | null>(null); const [searchQuery, setSearchQuery] = useState(''); // Query users eligible for notifications @@ -41,8 +36,6 @@ export default function SendNotifications() { search: searchQuery, }); - const { uploadSingle } = useUploadToObjectStorage(); - // Send notification mutation const sendNotification = trpc.admin.user.sendNotification.useMutation({ onSuccess: () => { @@ -51,8 +44,6 @@ export default function SendNotifications() { setSelectedUserIds([]); setTitle(''); setMessage(''); - setSelectedImage(null); - setDisplayImage(null); }, onError: (error: any) => { Alert.alert('Error', error.message || 'Failed to send notification'); @@ -66,29 +57,6 @@ export default function SendNotifications() { value: user.id, })); - const handleImagePick = usePickImage({ - setFile: async (assets: any) => { - if (!assets || (Array.isArray(assets) && assets.length === 0)) { - setSelectedImage(null); - setDisplayImage(null); - return; - } - - const file = Array.isArray(assets) ? assets[0] : assets; - const response = await fetch(file.uri); - const blob = await response.blob(); - - setSelectedImage({ blob, mimeType: file.mimeType || 'image/jpeg' }); - setDisplayImage({ uri: file.uri }); - }, - multiple: false, - }); - - const handleRemoveImage = () => { - setSelectedImage(null); - setDisplayImage(null); - }; - const handleSend = async () => { if (title.trim().length === 0) { Alert.alert('Error', 'Please enter a title'); @@ -117,20 +85,11 @@ export default function SendNotifications() { } try { - let imageUrl: string | undefined; - - // Upload image if selected - if (selectedImage) { - const { key } = await uploadSingle(selectedImage.blob, selectedImage.mimeType, 'notification'); - imageUrl = key; - } - // Send notification await sendNotification.mutateAsync({ userIds: selectedUserIds, title: title.trim(), text: message.trim(), - imageUrl, }); } catch (error: any) { Alert.alert('Error', error.message || 'Failed to send notification'); @@ -196,17 +155,6 @@ export default function SendNotifications() { /> - {/* Image Upload - Hidden for now */} - {/* - Image (Optional) - - */} - {/* User Selection */} Select Users (Optional) diff --git a/apps/admin-ui/components/AddressPlaceForm.tsx b/apps/admin-ui/components/AddressPlaceForm.tsx deleted file mode 100644 index 89ea40f..0000000 --- a/apps/admin-ui/components/AddressPlaceForm.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react' -import { Formik } from 'formik' -import * as Yup from 'yup' -import { View, Text, TouchableOpacity } from 'react-native' -import { MyTextInput, BottomDropdown, tw } from 'common-ui' -import { trpc } from '@/src/trpc-client' - -interface AddressPlaceFormProps { - onSubmit: (values: { placeName: string; zoneId: number | null }) => void - onClose: () => void -} - -const AddressPlaceForm: React.FC = ({ onSubmit, onClose }) => { - const { data: zones } = trpc.admin.address.getZones.useQuery() - - const validationSchema = Yup.object({ - placeName: Yup.string().required('Place name is required'), - zoneId: Yup.number().optional(), - }) - - const zoneOptions = zones?.map(z => ({ label: z.zoneName, value: z.id })) || [] - - return ( - - Add Place - { - onSubmit(values) - onClose() - }} - > - {({ handleChange, setFieldValue, handleSubmit, values, errors, touched }) => ( - - - setFieldValue('zoneId', value as number | undefined)} - placeholder="Select Zone" - /> - - - Cancel - - handleSubmit()}> - Create - - - - )} - - - ) -} - -export default AddressPlaceForm \ No newline at end of file diff --git a/apps/admin-ui/components/AddressZoneForm.tsx b/apps/admin-ui/components/AddressZoneForm.tsx deleted file mode 100644 index db00ecd..0000000 --- a/apps/admin-ui/components/AddressZoneForm.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import React from 'react' -import { Formik } from 'formik' -import * as Yup from 'yup' -import { View, Text, TouchableOpacity } from 'react-native' -import { MyTextInput, tw } from 'common-ui' - -interface AddressZoneFormProps { - onSubmit: (values: { zoneName: string }) => void - onClose: () => void -} - -const AddressZoneForm: React.FC = ({ onSubmit, onClose }) => { - const validationSchema = Yup.object({ - zoneName: Yup.string().required('Zone name is required'), - }) - - return ( - - Add Zone - { - onSubmit(values) - onClose() - }} - > - {({ handleChange, handleSubmit, values, errors, touched }) => ( - - - - - Cancel - - handleSubmit()}> - Create - - - - )} - - - ) -} - -export default AddressZoneForm \ No newline at end of file diff --git a/apps/admin-ui/components/HorizontalImageScroller.tsx b/apps/admin-ui/components/HorizontalImageScroller.tsx deleted file mode 100755 index b7afad8..0000000 --- a/apps/admin-ui/components/HorizontalImageScroller.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import React from "react"; -import { ScrollView, View, StyleSheet } from "react-native"; -import { ImageViewerURI } from "common-ui"; - -interface HorizontalImageScrollerProps { - urls: string[]; - imageHeight?: number; - imageWidth?: number; -} - -const HorizontalImageScroller: React.FC = ({ - urls, - imageHeight = 128, - imageWidth = 128, -}) => { - if (!urls || urls.length === 0) return null; - - - return ( - - - {urls.map((url, idx) => ( - - - - ))} - - - ); -}; - -const styles = StyleSheet.create({ - container: { - marginVertical: 8, - }, -}); - -export default HorizontalImageScroller; diff --git a/apps/admin-ui/components/StoreForm.tsx b/apps/admin-ui/components/StoreForm.tsx index fd5399e..da48a60 100644 --- a/apps/admin-ui/components/StoreForm.tsx +++ b/apps/admin-ui/components/StoreForm.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useState, useEffect, useMemo } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { View, TouchableOpacity, Alert } from 'react-native'; import { Formik } from 'formik'; import * as Yup from 'yup'; @@ -16,10 +16,6 @@ export interface StoreFormData { products: number[]; } -export interface StoreFormRef { - // Add methods if needed -} - interface StoreFormProps { mode: 'create' | 'edit'; initialValues: StoreFormData; @@ -36,8 +32,7 @@ const validationSchema = Yup.object().shape({ products: Yup.array().of(Yup.number()), }); -const StoreForm = forwardRef((props, ref) => { - const { mode, initialValues, onSubmit, isLoading, storeId } = props; +function StoreForm({ mode, initialValues, onSubmit, isLoading, storeId }: StoreFormProps) { const { data: staffData } = trpc.admin.staffUser.getStaff.useQuery(); const { data: productsData } = trpc.admin.product.getProducts.useQuery(); @@ -199,8 +194,6 @@ const StoreForm = forwardRef((props, ref) => { }} ); -}); - -StoreForm.displayName = 'StoreForm'; +} export default StoreForm; diff --git a/apps/admin-ui/components/TabNavigation.tsx b/apps/admin-ui/components/TabNavigation.tsx deleted file mode 100755 index 02ec64c..0000000 --- a/apps/admin-ui/components/TabNavigation.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react'; -import { View, TouchableOpacity } from 'react-native'; -import { MyText , tw } from "common-ui"; - -interface TabNavigationProps { - tabs: { key: string; title: string }[]; - activeTab: string; - onTabChange: (tabKey: string) => void; -} - -const TabNavigation: React.FC = ({ tabs, activeTab, onTabChange }) => { - return ( - - {tabs.map((tab) => ( - onTabChange(tab.key)} - > - - {tab.title} - - - ))} - - ); -}; - -export default TabNavigation; \ No newline at end of file diff --git a/apps/admin-ui/components/UserIncidentDialog.tsx b/apps/admin-ui/components/UserIncidentDialog.tsx deleted file mode 100644 index 4842ab2..0000000 --- a/apps/admin-ui/components/UserIncidentDialog.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useState } from 'react'; -import { View, TouchableOpacity } from 'react-native'; -import { MyText, tw, BottomDialog, MyTextInput } from 'common-ui'; -import { trpc } from '@/src/trpc-client'; -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { Alert } from 'react-native'; - -interface UserIncidentDialogProps { - orderId: number; - userId: number; - open: boolean; - onClose: () => void; - onSuccess?: () => void; -} - -export default function UserIncidentDialog({ orderId, userId, open, onClose, onSuccess }: UserIncidentDialogProps) { - const [adminComment, setAdminComment] = useState(''); - const [negativityScore, setNegativityScore] = useState(''); - - const addIncidentMutation = trpc.admin.user.addUserIncident.useMutation({ - onSuccess: () => { - Alert.alert('Success', 'Incident added successfully'); - setAdminComment(''); - setNegativityScore(''); - onClose(); - onSuccess?.(); - }, - onError: (error: any) => { - Alert.alert('Error', error.message || 'Failed to add incident'); - }, - }); - - const handleAddIncident = () => { - const score = negativityScore ? parseInt(negativityScore) : undefined; - - if (!adminComment.trim() && !negativityScore) { - Alert.alert('Error', 'Please enter a comment or negativity score'); - return; - } - - addIncidentMutation.mutate({ - userId, - orderId, - adminComment: adminComment || undefined, - negativityScore: score, - }); - }; - - return ( - - - - - - - - Add User Incident - - - Record an incident for this user. This will be visible in their profile. - - - - - - - - - - - Higher negativity scores indicate more serious incidents (e.g., repeated cancellations, abusive behavior). - - - - - - Cancel - - - - {addIncidentMutation.isPending ? 'Adding...' : 'Add Incident'} - - - - - - ); -} \ No newline at end of file diff --git a/apps/admin-ui/components/app-container.tsx b/apps/admin-ui/components/app-container.tsx deleted file mode 100755 index 50e2b54..0000000 --- a/apps/admin-ui/components/app-container.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { AppContainer } from "common-ui"; - -export default AppContainer; \ No newline at end of file diff --git a/apps/admin-ui/components/context/auth-context.tsx b/apps/admin-ui/components/context/auth-context.tsx deleted file mode 100755 index ed365b9..0000000 --- a/apps/admin-ui/components/context/auth-context.tsx +++ /dev/null @@ -1,270 +0,0 @@ -// import React, { -// createContext, -// useContext, -// useEffect, -// useState, -// ReactNode, -// } from "react"; -// import { -// getJWT, -// deleteJWT, -// getRoles, -// saveJWT, -// saveRoles, -// saveUserId, -// getUserId, -// } from "../../hooks/useJWT"; -// import { useFocusEffect, usePathname, useRouter } from "expo-router"; -// import queryClient from "@/utils/queryClient"; -// import { DeviceEventEmitter } from "react-native"; -// import { FORCE_LOGOUT_EVENT, SESSION_EXPIRED_MSG } from "common-ui/src/lib/const-strs"; -// import { useLogin, useLogout } from "@/api-hooks/auth.api"; -// import { -// useUserResponsibilities, -// UserResponsibilities, -// } from "@/api-hooks/user.api"; -// import { InfoToast, SuccessToast } from "@/services/toaster"; -// import { useNotification } from "@/services/notif-service/notif-context"; - -// interface LoginFormInputs { -// login: string; -// password: string; -// useUsername?: boolean; -// expoPushToken?: string | null; -// } - -// interface AuthContextType { -// isLoggedIn: boolean; -// setIsLoggedIn: (value: boolean) => void; -// logout: ({ -// isSessionExpired, -// }: { -// isSessionExpired?: boolean; -// }) => Promise; -// responsibilities: UserResponsibilities | null; -// responsibilitiesLoading: boolean; -// responsibilitiesError: Error | null; -// roles: string[] | null; -// setRoles: (roles: string[] | null) => void; -// refreshRoles: () => Promise; -// loginFunc: (payload: LoginFormInputs) => Promise; -// userId: number | null; -// isLoggingIn: boolean; -// loginError?: string; -// } - -// const defaultResponsibilities: UserResponsibilities = { -// hospitalAdminFor: null, -// secretaryFor: [], -// }; - -// export const AuthContext = createContext( -// undefined -// ); - -// export const AuthProvider = ({ children }: { children: ReactNode }) => { -// const { mutate: loginApi, isPending: isLoggingIn, error: loginError } = useLogin(); -// const [isLoggedIn, setIsLoggedIn] = useState(false); -// const [roles, setRoles] = useState(null); -// const [userId, setUserId] = useState(null); - -// const refreshRoles = async () => { -// const r = await getRoles(); -// setRoles(r); -// }; - - - -// useEffect(() => { -// refreshRoles(); -// }, []); -// const { mutate: logoutApi } = useLogout(); -// const router = useRouter(); -// const [responsibilitiesError, setResponsibilitiesError] = -// useState(null); - -// const { -// data: responsibilities, -// isLoading: responsibilitiesLoading, -// isFetching: responsibilitiesFetching, -// refetch: refetchResponsibilities, -// error: queryError, -// } = useUserResponsibilities(userId); - - -// React.useEffect(() => { -// (async () => { -// const token = await getJWT(); - -// setIsLoggedIn(!!token); -// if (!token) { -// if (!pathname.includes("login")) { -// router.replace("/login" as any); -// } -// } else { -// refetchResponsibilities(); -// router.replace("/(drawer)/dashboard"); -// const userId = await getUserId(); -// setUserId(userId ? parseInt(userId) : null); -// } -// })(); -// }, []); - -// const pathname = usePathname(); - - -// const logout = async ({ -// isSessionExpired, -// }: { -// isSessionExpired?: boolean; -// }) => { - -// const pageConditon = -// pathname.includes("/login") || -// pathname.includes("/signup") || -// pathname === "/"; - -// if (!isSessionExpired) { -// logoutApi({} as any, { -// onSuccess: () => {}, -// onSettled: () => { - -// if (!pageConditon) { -// router.replace({ -// pathname: "/login" as any, -// params: isSessionExpired ? { message: SESSION_EXPIRED_MSG } : {}, -// }); -// } -// deleteJWT(); -// }, -// }); -// setIsLoggedIn(false); -// } else { -// deleteJWT(); -// setIsLoggedIn(false); -// InfoToast("Session expired. Please log in again."); -// if (!pageConditon) { -// router.replace({ -// pathname: "/login" as any, -// params: { message: SESSION_EXPIRED_MSG }, -// }); -// } -// } -// queryClient.clear(); -// }; - -// const loginFunc = async (data: LoginFormInputs) => { -// loginApi(data, { -// onSuccess: async (result) => { - -// // refetchUserId(); -// // await refetchUserData(); - -// await saveUserId(result.user.id.toString()); -// setUserId(result.user.id); - -// await saveJWT(result.token); - -// // Update login state in auth context -// setIsLoggedIn(true); - -// // Handle roles if available -// if (result.user.roles) { -// await saveRoles(result.user.roles); -// await refreshRoles(); -// } -// await refetchResponsibilities(); - -// // Clear the 'message' search param from the URL after login -// router.replace({ -// pathname: "/(drawer)/dashboard", -// params: {}, -// }); -// }, -// onError: (e: any) => { -// // setError("login", { -// // type: "manual", -// // message: e.message || "Login failed", -// // }); -// }, -// }); -// }; - -// React.useEffect(() => { -// const subscription = DeviceEventEmitter.addListener( -// FORCE_LOGOUT_EVENT, -// () => { -// logout({ isSessionExpired: true }); -// } -// ); - -// return () => { -// subscription.remove(); -// }; -// }, []); - - -// return ( -// -// {children} -// -// ); -// }; - -// export const useAuth = () => { -// const context = useContext(AuthContext); -// if (!context) { -// throw new Error("useAuth must be used within an AuthProvider"); -// } -// return context; -// }; - -// /** -// * Hook to check if the current user is a hospital admin -// * @param hospitalId Hospital ID to check against -// * @returns Boolean indicating if user is admin for that hospital -// */ -// export const useIsHospitalAdmin = (hospitalId?: number | string): boolean => { -// const { responsibilities } = useAuth(); - -// // If no hospitalId provided, return false -// if (hospitalId === undefined) { -// return false; -// } - -// // Check if user is admin for the specified hospital -// return responsibilities?.hospitalAdminFor === hospitalId; -// }; - -// /** -// * Hook to check if the current user is a secretary for a specific doctor -// * @param doctorId Doctor ID to check against -// * @returns Boolean indicating if user is a secretary for that doctor -// */ -// export const useIsDoctorSecretary = (doctorId?: number): boolean => { -// const { responsibilities } = useAuth(); - -// // If no doctorId provided, return false -// if (doctorId === undefined) { -// return false; -// } - -// // Check if user is a secretary for the specified doctor -// return responsibilities?.secretaryFor?.includes(doctorId) || false; -// }; diff --git a/apps/admin-ui/components/context/roles-context.tsx b/apps/admin-ui/components/context/roles-context.tsx deleted file mode 100755 index e8936d5..0000000 --- a/apps/admin-ui/components/context/roles-context.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { createContext, useContext, useEffect, useState, ReactNode } from "react"; -import { AuthContext } from "./auth-context"; -import { ROLE_NAMES } from "common-ui"; -// import { getRoles, saveRoles, deleteRoles } from "../../hooks/useJWT"; -// import { ROLE_NAMES } from "../../lib/constants"; - -// interface RolesContextType { -// roles: string[] | null; -// setRoles: (roles: string[] | null) => void; -// refreshRoles: () => Promise; -// } - -// const RolesContext = createContext(undefined); - -// export const RolesProvider = ({ children }: { children: ReactNode }) => { -// const [roles, setRoles] = useState(null); - -// const refreshRoles = async () => { -// const r = await getRoles(); -// setRoles(r); -// }; - -// useEffect(() => { -// refreshRoles(); -// }, []); - -// return ( -// -// {children} -// -// ); -// }; - -export const useRoles = () => { - const ctx = useContext(AuthContext); - if (!ctx) throw new Error("useRoles must be used within a RolesProvider"); - return ctx.roles; -}; - -export const useIsAdmin = () => { - const roles = useRoles(); - return roles?.includes(ROLE_NAMES.ADMIN); -} diff --git a/apps/admin-ui/components/dashboard-header.tsx b/apps/admin-ui/components/dashboard-header.tsx deleted file mode 100755 index 000f7c4..0000000 --- a/apps/admin-ui/components/dashboard-header.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import React from 'react'; -import { View, TouchableOpacity, Animated, Easing } from 'react-native'; -import { SafeAreaView } from 'react-native-safe-area-context'; -import { MyText , tw , colors } from "common-ui"; -import { Ionicons } from '@expo/vector-icons'; -import { useThemeColor } from '@/hooks/useThemeColor'; -import { IconButton } from 'react-native-paper'; - -interface DashboardHeaderProps { - onMenuPress: () => void; - onNotificationsPress: () => void; - onProfilePress: () => void; - onRefreshPress?: () => void; - refreshing?: boolean; -} - -const DashboardHeader: React.FC = ({ - onMenuPress, - onNotificationsPress, - onProfilePress, - onRefreshPress, - refreshing = false -}) => { - const accentColor = useThemeColor({ light: '#4f46e5', dark: '#818cf8' }, 'tint'); - - // For the refresh animation - const spinAnim = React.useRef(new Animated.Value(0)).current; - - // Update animation when refreshing prop changes - React.useEffect(() => { - let animation: Animated.CompositeAnimation | null = null; - - if (refreshing) { - animation = Animated.loop( - Animated.timing(spinAnim, { - toValue: 1, - duration: 1000, - easing: Easing.linear, - useNativeDriver: true, - }) - ); - animation.start(); - } else { - spinAnim.stopAnimation(); - spinAnim.setValue(0); - } - - return () => { - if (animation) { - animation.stop(); - } - }; - }, [refreshing]); - - const spin = spinAnim.interpolate({ - inputRange: [0, 1], - outputRange: ['0deg', '360deg'], - }); - - return ( - - - {/* Menu Button */} - - - - - {/* Logo/App Name */} - - HealthPetal - - - {/* Right Actions */} - - {/* Refresh Button */} - {onRefreshPress && ( - - - - )} - - {/* Notifications */} - - - {/* Notification Badge */} - - - - {/* Profile */} - - - - - - - ); -}; - -export default DashboardHeader; \ No newline at end of file diff --git a/apps/admin-ui/components/date-time-picker.tsx b/apps/admin-ui/components/date-time-picker.tsx deleted file mode 100755 index ca43829..0000000 --- a/apps/admin-ui/components/date-time-picker.tsx +++ /dev/null @@ -1,278 +0,0 @@ -// import { useTheme } from "@/hooks/theme-context"; -import { MaterialCommunityIcons } from "@expo/vector-icons"; -import DateTimePicker, { - AndroidNativeProps, - DateTimePickerAndroid, - DateTimePickerEvent, -} from "@react-native-community/datetimepicker"; -import React, { useState } from "react"; -import { - Modal, - Platform, - StyleSheet, - Text, - TouchableOpacity, - View, -} from "react-native"; - -import { useTheme, MyText } from "common-ui"; - -interface Props { - value: Date | null; - setValue: (date: Date | null) => void; - showLabels?: boolean; // Optional prop to control label visibility - timeOnly?: boolean; // Optional prop to show only time picker -} - -type Mode = "date" | "time" | "datetime"; - -function DateTimePickerMod(props: Props) { - const { value, setValue, showLabels = true, timeOnly = false } = props; - const [show, setShow] = useState(false); - const [mode, setMode] = useState("date"); - - const onChange = (event: DateTimePickerEvent, selectedDate?: Date) => { - const currentDate = selectedDate || value; - if (Platform.OS === "ios") setShow(false); - setValue(currentDate); - }; - - const showMode = (currentMode: Mode) => { - if (Platform.OS === "android") { - DateTimePickerAndroid.open({ - value: value || new Date(), - onChange: onChange, - mode: currentMode, - is24Hour: true, - display: "default", - } as AndroidNativeProps); - } else { - setShow(true); - setMode(currentMode); - } - }; - const showDatepicker = () => { - showMode("date"); - }; - - const showTimepicker = () => { - showMode("time"); - }; - const { theme } = useTheme(); - - return ( - - {timeOnly ? ( - - {showLabels && Select Time} - - - {value?.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - }) || "Select Time"} - - - - - ) : ( - - - {showLabels && Select Date} - - {value?.toLocaleDateString() || "Select Date"} - - - - - - {showLabels && Select Time} - - - {value?.toLocaleTimeString([], { - hour: "2-digit", - minute: "2-digit", - }) || "Select Time"} - - - - - - )} - {/* Conditional rendering for iOS, as it uses the declarative API */} - {show && Platform.OS === "ios" && ( - setShow(false)} - > - - - - setShow(false)} - style={styles.doneButton} - > - Done - - - - - )} - - ); -} - -export default DateTimePickerMod; - -const styles = StyleSheet.create({ - container: { - // flex: 1, // Remove flex to avoid taking up extra space - justifyContent: "center", - alignItems: "flex-start", - padding: 0, // Reduce padding for compactness - marginBottom: 16, // Add margin for spacing in forms - }, - iconRow: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - marginBottom: 20, - }, - iconRowSingleLine: { - flexDirection: "row", - alignItems: "center", - justifyContent: "center", - marginBottom: 0, - }, - iconNoBg: { - backgroundColor: "transparent", - borderRadius: 0, - padding: 0, - elevation: 0, - }, - spacerHorizontal: { - width: 30, - }, - spacerHorizontalSmall: { - width: 8, - }, - spacer: { - height: 20, // Add some space between buttons - }, - selectedText: { - marginTop: 30, - fontSize: 18, - fontWeight: "bold", - textAlign: "center", - }, - timeTextContainer: { - justifyContent: "center", - alignItems: "center", - }, - timeText: { - fontSize: 15, - fontWeight: "500", - color: "#333", - marginLeft: 2, - }, - dateText: { - fontSize: 15, - fontWeight: "500", - color: "#333", - marginLeft: 2, - marginRight: 2, - }, - - modalOverlay: { - flex: 1, - justifyContent: "center", - alignItems: "center", - backgroundColor: "rgba(0, 0, 0, 0.5)", - }, - pickerContainer: { - width: "80%", - backgroundColor: "white", - borderRadius: 10, - padding: 20, - elevation: 5, - }, - doneButton: { - marginTop: 10, - backgroundColor: "#007bff", - borderRadius: 5, - padding: 10, - alignItems: "center", - }, - doneButtonText: { - color: "white", - fontWeight: "bold", - }, -}); diff --git a/apps/admin-ui/components/day-account-view.tsx b/apps/admin-ui/components/day-account-view.tsx deleted file mode 100755 index 686d9e7..0000000 --- a/apps/admin-ui/components/day-account-view.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React from 'react'; -import { View } from 'react-native'; -import { MyText , tw } from "common-ui"; -import { Ionicons } from '@expo/vector-icons'; - -// Define the types -export interface DoctorWiseCount { - doctorName: string; - doctorId: number; - fee: number; - issuedTokens: number; - totalAmount: number; -} - -export interface DayAccountData { - doctorWiseCount: DoctorWiseCount[]; - date: string; - totalAmount: number; - settled: boolean; -} - -interface DayAccountViewProps { - dayData: DayAccountData; -} - -const DayAccountView: React.FC = ({ dayData }) => { - return ( - - - - {dayData.date} - - - - โ‚น{dayData.totalAmount} - - {dayData.settled ? ( - - - Settled - - ) : ( - - - Pending - - )} - - - - - {dayData.doctorWiseCount.map((doctor, idx) => ( - - - - {doctor.doctorName} - - - โ‚น{doctor.fee} ร— {doctor.issuedTokens} tokens - - - - โ‚น{doctor.totalAmount} - - - ))} - - - ); -}; - -export default DayAccountView; \ No newline at end of file diff --git a/apps/admin-ui/components/ui/IconSymbol.ios.tsx b/apps/admin-ui/components/ui/IconSymbol.ios.tsx deleted file mode 100755 index 9177f4d..0000000 --- a/apps/admin-ui/components/ui/IconSymbol.ios.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; -import { StyleProp, ViewStyle } from 'react-native'; - -export function IconSymbol({ - name, - size = 24, - color, - style, - weight = 'regular', -}: { - name: SymbolViewProps['name']; - size?: number; - color: string; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ( - - ); -} diff --git a/apps/admin-ui/components/ui/IconSymbol.tsx b/apps/admin-ui/components/ui/IconSymbol.tsx deleted file mode 100755 index b7ece6b..0000000 --- a/apps/admin-ui/components/ui/IconSymbol.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// Fallback for using MaterialIcons on Android and web. - -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; -import { ComponentProps } from 'react'; -import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; - -type IconMapping = Record['name']>; -type IconSymbolName = keyof typeof MAPPING; - -/** - * Add your SF Symbols to Material Icons mappings here. - * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). - * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. - */ -const MAPPING = { - 'house.fill': 'home', - 'paperplane.fill': 'send', - 'chevron.left.forwardslash.chevron.right': 'code', - 'chevron.right': 'chevron-right', -} as IconMapping; - -/** - * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. - * This ensures a consistent look across platforms, and optimal resource usage. - * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. - */ -export function IconSymbol({ - name, - size = 24, - color, - style, -}: { - name: IconSymbolName; - size?: number; - color: string | OpaqueColorValue; - style?: StyleProp; - weight?: SymbolWeight; -}) { - return ; -} diff --git a/apps/admin-ui/components/ui/TabBarBackground.ios.tsx b/apps/admin-ui/components/ui/TabBarBackground.ios.tsx deleted file mode 100755 index 495b2d4..0000000 --- a/apps/admin-ui/components/ui/TabBarBackground.ios.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; -import { BlurView } from 'expo-blur'; -import { StyleSheet } from 'react-native'; - -export default function BlurTabBarBackground() { - return ( - - ); -} - -export function useBottomTabOverflow() { - return useBottomTabBarHeight(); -} diff --git a/apps/admin-ui/components/ui/TabBarBackground.tsx b/apps/admin-ui/components/ui/TabBarBackground.tsx deleted file mode 100755 index 70d1c3c..0000000 --- a/apps/admin-ui/components/ui/TabBarBackground.tsx +++ /dev/null @@ -1,6 +0,0 @@ -// This is a shim for web and Android where the tab bar is generally opaque. -export default undefined; - -export function useBottomTabOverflow() { - return 0; -} diff --git a/apps/admin-ui/constants/Colors.ts b/apps/admin-ui/constants/Colors.ts deleted file mode 100755 index 14e6784..0000000 --- a/apps/admin-ui/constants/Colors.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Below are the colors that are used in the app. The colors are defined in the light and dark mode. - * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. - */ - -const tintColorLight = '#0a7ea4'; -const tintColorDark = '#fff'; - -export const Colors = { - light: { - text: '#11181C', - background: '#fff', - tint: tintColorLight, - icon: '#687076', - tabIconDefault: '#687076', - tabIconSelected: tintColorLight, - }, - dark: { - text: '#ECEDEE', - background: '#151718', - tint: tintColorDark, - icon: '#9BA1A6', - tabIconDefault: '#9BA1A6', - tabIconSelected: tintColorDark, - }, -}; diff --git a/apps/admin-ui/e2e/jest.config.js b/apps/admin-ui/e2e/jest.config.js deleted file mode 100644 index 4f98020..0000000 --- a/apps/admin-ui/e2e/jest.config.js +++ /dev/null @@ -1,12 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -module.exports = { - rootDir: '..', - testMatch: ['/e2e/**/*.test.js'], - testTimeout: 120000, - maxWorkers: 1, - globalSetup: 'detox/runners/jest/globalSetup', - globalTeardown: 'detox/runners/jest/globalTeardown', - reporters: ['detox/runners/jest/reporter'], - testEnvironment: 'detox/runners/jest/testEnvironment', - verbose: true, -}; diff --git a/apps/admin-ui/e2e/starter.test.js b/apps/admin-ui/e2e/starter.test.js deleted file mode 100644 index 054c2a1..0000000 --- a/apps/admin-ui/e2e/starter.test.js +++ /dev/null @@ -1,23 +0,0 @@ -describe('Example', () => { - beforeAll(async () => { - await device.launchApp(); - }); - - beforeEach(async () => { - await device.reloadReactNative(); - }); - - it('should have welcome screen', async () => { - await expect(element(by.id('welcome'))).toBeVisible(); - }); - - it('should show hello screen after tap', async () => { - await element(by.id('hello_button')).tap(); - await expect(element(by.text('Hello!!!'))).toBeVisible(); - }); - - it('should show world screen after tap', async () => { - await element(by.id('world_button')).tap(); - await expect(element(by.text('World!!!'))).toBeVisible(); - }); -}); diff --git a/apps/admin-ui/hooks/useColorScheme.ts b/apps/admin-ui/hooks/useColorScheme.ts deleted file mode 100755 index 17e3c63..0000000 --- a/apps/admin-ui/hooks/useColorScheme.ts +++ /dev/null @@ -1 +0,0 @@ -export { useColorScheme } from 'react-native'; diff --git a/apps/admin-ui/hooks/useColorScheme.web.ts b/apps/admin-ui/hooks/useColorScheme.web.ts deleted file mode 100755 index 7eb1c1b..0000000 --- a/apps/admin-ui/hooks/useColorScheme.web.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useColorScheme as useRNColorScheme } from 'react-native'; - -/** - * To support static rendering, this value needs to be re-calculated on the client side for web - */ -export function useColorScheme() { - const [hasHydrated, setHasHydrated] = useState(false); - - useEffect(() => { - setHasHydrated(true); - }, []); - - const colorScheme = useRNColorScheme(); - - if (hasHydrated) { - return colorScheme; - } - - return 'light'; -} diff --git a/apps/admin-ui/hooks/useCurrentUserId.ts b/apps/admin-ui/hooks/useCurrentUserId.ts deleted file mode 100755 index 7f92190..0000000 --- a/apps/admin-ui/hooks/useCurrentUserId.ts +++ /dev/null @@ -1,13 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { getCurrentUserId } from '@/utils/getCurrentUserId'; - -export function useCurrentUserId(): {userId:number|null, refetchUserId: () => void} { - const [userId, setUserId] = useState(null); - const refetchUserId = React.useCallback(() => { - getCurrentUserId().then(setUserId); - },[]) - useEffect(() => { - getCurrentUserId().then(setUserId); - }, []); - return {userId,refetchUserId}; -} diff --git a/apps/admin-ui/hooks/useHideDrawerHeader.ts b/apps/admin-ui/hooks/useHideDrawerHeader.ts deleted file mode 100755 index c277ec2..0000000 --- a/apps/admin-ui/hooks/useHideDrawerHeader.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { useFocusEffect, useNavigation } from "expo-router"; -import React from "react"; - - -function useHideDrawerHeader() { - - const navigation = useNavigation(); - useFocusEffect(() => { - let drawerNav = navigation.getParent(); - const drawerNavList:any = []; - // Collect all parent navigators - while (drawerNav) { - drawerNavList.push(drawerNav); - drawerNav = drawerNav.getParent(); - } - - drawerNavList.at(-3)?.setOptions({ headerShown: false }); - - return () => { - drawerNavList.at(-3)?.setOptions({ headerShown: true }); - }; - }); - return null; -} - -export default useHideDrawerHeader; diff --git a/apps/admin-ui/hooks/useJWT.ts b/apps/admin-ui/hooks/useJWT.ts index 03a9246..8576f18 100755 --- a/apps/admin-ui/hooks/useJWT.ts +++ b/apps/admin-ui/hooks/useJWT.ts @@ -1,49 +1,15 @@ -// import { StorageService } from '@/lib/StorageService'; -import {StorageService} from 'common-ui'; +import { StorageService } from 'common-ui' -export const JWT_KEY = 'jwt_token'; -export const ROLES_KEY = 'user_roles'; -export const USER_ID_KEY = 'userId'; - -export async function saveUserId(userId:string) { - await StorageService.setItem(USER_ID_KEY, userId); -} - -export async function getUserId() { - return await StorageService.getItem(USER_ID_KEY); -} +export const JWT_KEY = 'jwt_token' export async function saveJWT(token: string) { - await StorageService.setItem(JWT_KEY, token); + await StorageService.setItem(JWT_KEY, token) } export async function getJWT() { - return await StorageService.getItem(JWT_KEY); + return await StorageService.getItem(JWT_KEY) } export async function deleteJWT() { - await StorageService.removeItem(JWT_KEY); -} - -export async function saveRoles(roles: string[]) { - await StorageService.setItem(ROLES_KEY, JSON.stringify(roles)); -} - -export async function getRoles(): Promise { - const jwt = await getJWT(); - if (!jwt) { - StorageService.removeItem(ROLES_KEY); - return null; - } - const rolesStr = await StorageService.getItem(ROLES_KEY); - if (!rolesStr) return null; - try { - return JSON.parse(rolesStr); - } catch { - return null; - } -} - -export async function deleteRoles() { - await StorageService.removeItem(ROLES_KEY); + await StorageService.removeItem(JWT_KEY) } diff --git a/apps/admin-ui/hooks/usePhonepeSdk.ts b/apps/admin-ui/hooks/usePhonepeSdk.ts deleted file mode 100755 index 0ba4f0a..0000000 --- a/apps/admin-ui/hooks/usePhonepeSdk.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { usePhonepeCreds } from '@/api-hooks/payment.api'; -import { useEffect } from 'react'; -import PhonePePaymentSDK from 'react-native-phonepe-pg'; - -export function usePhonepeSdk() { - const { data: creds, isLoading, isError } = usePhonepeCreds(); - - useEffect(() => { - if (creds && creds.clientId && creds.clientVersion) { - PhonePePaymentSDK.init('SANDBOX', creds.clientId, creds.clientId, true); - } - }, [creds]); - - const startTransaction = async (orderId: string, token: string) => { - try { - const request = { - orderId, - token, - merchantId: creds?.merchantId, - paymentMode: { type: 'PAY_PAGE' } - }; - const stringReq = JSON.stringify(request); - const response = await PhonePePaymentSDK.startTransaction(stringReq, null); - return response; - } catch (error) { - throw error; - } - }; - - return { startTransaction, isLoading, isError, creds }; -} diff --git a/apps/admin-ui/hooks/useThemeColor.ts b/apps/admin-ui/hooks/useThemeColor.ts deleted file mode 100755 index 0608e73..0000000 --- a/apps/admin-ui/hooks/useThemeColor.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Learn more about light and dark modes: - * https://docs.expo.dev/guides/color-schemes/ - */ - -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -export function useThemeColor( - props: { light?: string; dark?: string }, - colorName: keyof typeof Colors.light & keyof typeof Colors.dark -) { - const theme = useColorScheme() ?? 'light'; - const colorFromProps = props[theme]; - - if (colorFromProps) { - return colorFromProps; - } else { - return Colors[theme][colorName]; - } -} diff --git a/apps/admin-ui/scripts/reset-project.js b/apps/admin-ui/scripts/reset-project.js deleted file mode 100755 index 51dff15..0000000 --- a/apps/admin-ui/scripts/reset-project.js +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. - * You can remove the `reset-project` script from package.json and safely delete this file after running it. - */ - -const fs = require("fs"); -const path = require("path"); -const readline = require("readline"); - -const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const exampleDir = "app-example"; -const newAppDir = "app"; -const exampleDirPath = path.join(root, exampleDir); - -const indexContent = `import { Text, View } from "react-native"; - -export default function Index() { - return ( - - Edit app/index.tsx to edit this screen. - - ); -} -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ; -} -`; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const moveDirectories = async (userInput) => { - try { - if (userInput === "y") { - // Create the app-example directory - await fs.promises.mkdir(exampleDirPath, { recursive: true }); - console.log(`๐Ÿ“ /${exampleDir} directory created.`); - } - - // Move old directories to new app-example directory or delete them - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - if (fs.existsSync(oldDirPath)) { - if (userInput === "y") { - const newDirPath = path.join(root, exampleDir, dir); - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`โžก๏ธ /${dir} moved to /${exampleDir}/${dir}.`); - } else { - await fs.promises.rm(oldDirPath, { recursive: true, force: true }); - console.log(`โŒ /${dir} deleted.`); - } - } else { - console.log(`โžก๏ธ /${dir} does not exist, skipping.`); - } - } - - // Create new /app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\n๐Ÿ“ New /app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("๐Ÿ“„ app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("๐Ÿ“„ app/_layout.tsx created."); - - console.log("\nโœ… Project reset complete. Next steps:"); - console.log( - `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ - userInput === "y" - ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` - : "" - }` - ); - } catch (error) { - console.error(`โŒ Error during script execution: ${error.message}`); - } -}; - -rl.question( - "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", - (answer) => { - const userInput = answer.trim().toLowerCase() || "y"; - if (userInput === "y" || userInput === "n") { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("โŒ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } - } -); diff --git a/apps/admin-ui/services/axios-admin-ui.ts b/apps/admin-ui/services/axios-admin-ui.ts deleted file mode 100644 index 5fac5e4..0000000 --- a/apps/admin-ui/services/axios-admin-ui.ts +++ /dev/null @@ -1,59 +0,0 @@ -import axiosParent from 'axios'; -import { FORCE_LOGOUT_EVENT } from 'common-ui/src/lib/const-strs'; -import { DeviceEventEmitter } from 'react-native' -import { getJWT } from '@/hooks/useJWT'; -import { BASE_API_URL } from 'common-ui'; - -// export const API_BASE_URL = 'http://192.168.100.95:4000'; // Change to your API base URL -// const API_BASE_URL = 'https://www.technocracy.ovh/mf'; // Change to your API base URL -// const API_BASE_URL = 'http://10.195.26.42:4000'; // Change to your API base URL -// const API_BASE_URL = 'http://localhost:4000/api/mobile/'; // Change to your API base URL -// const API_BASE_URL = 'https://car-safar.com/api/mobile/'; // Change to your API base URL - -const axios = axiosParent.create({ - baseURL: BASE_API_URL + '/api/v1', - timeout: 60000, - // headers: { - // 'Content-Type': 'application/json', - // }, -}); - - -axios.interceptors.request.use( - async (config) => { - const token = await getJWT(); - - if (token) { - config.headers = config.headers || {}; - config.headers['Authorization'] = `Bearer ${token}`; - } - return config; - }, - (error) => Promise.reject(error) -); - -axios.interceptors.response.use( - (response) => response, - (error) => { - - const status = error?.status; - const msg = error.response?.data?.error; - - if (status === 401 && msg.startsWith('Access denied')) { - // Handle unauthorized access - DeviceEventEmitter.emit(FORCE_LOGOUT_EVENT); - } - const message = error?.response?.data?.error; - - if (msg) { - // Optionally, you can attach the message to the error object or throw a new error - const err = new Error(msg); - // Optionally attach the original error for debugging - (err as any).original = error; - return Promise.reject(err); - } - return Promise.reject(error); - } -); - -export default axios; diff --git a/apps/admin-ui/services/notif-service/notif-checker.tsx b/apps/admin-ui/services/notif-service/notif-checker.tsx deleted file mode 100755 index 40cd786..0000000 --- a/apps/admin-ui/services/notif-service/notif-checker.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import {useAddPushToken, useHasPushToken } from "@/api-hooks/user.api"; -import React from "react"; -import { useNotification } from "./notif-context"; -import { BottomDialog } from "common-ui"; -import { MyText } from "common-ui"; -import { View, Linking } from "react-native"; -import { tw } from "common-ui"; -import { MyButton } from "common-ui"; -import { useAuth } from "@/components/context/auth-context"; - -interface Props {} - -function NotifChecker(props: Props) { - const {} = props; - const [showPermissionDialog, setShowPermissionDialog] = React.useState(false); - - const {isLoggedIn} = useAuth(); - const { data: hasPushToken, isLoading, isError } = useHasPushToken({enabled: isLoggedIn}); - const { mutate: addPushToken } = useAddPushToken(); - const { notifPermission, expoPushToken } = useNotification(); - React.useEffect(() => { - if(isLoggedIn && !hasPushToken && notifPermission =='granted') { - addPushToken(expoPushToken!); - } - },[isLoggedIn, hasPushToken]) - - React.useEffect(() => { - if (notifPermission === "denied") { - setShowPermissionDialog(true); - } - }, [notifPermission]); - - return ( - <> - setShowPermissionDialog(false)} - > - - - Notification Permission Denied - - - It seems you have denied notification permissions. Please enable - them in your device settings. - - - setShowPermissionDialog(false)} - style={tw`flex-1`} - > - Cancel - - { - Linking.openSettings(); - }} - style={tw`flex-1`} - > - Settings - - - - - - ); -} - -export default NotifChecker; diff --git a/apps/admin-ui/services/notif-service/notif-context.tsx b/apps/admin-ui/services/notif-service/notif-context.tsx deleted file mode 100755 index 52ad74e..0000000 --- a/apps/admin-ui/services/notif-service/notif-context.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import React, { - createContext, - useContext, - useState, - useEffect, - useRef, - ReactNode, -} from "react"; -import * as Notifications from "expo-notifications"; -import { registerForPushNotificationsAsync } from "./notif-register"; -import { useRouter } from "expo-router"; -import { NotificationToast } from "../toaster"; -import { NOTIF_PERMISSION_DENIED } from "common-ui/src/lib/const-strs"; - -interface NotificationContextType { - expoPushToken: string | null; - notification: Notifications.Notification | null; - error: Error | null; - notifPermission: 'pending' | 'granted' | 'denied' -} - -export const NotificationContext = createContext< - NotificationContextType | undefined ->(undefined); - -export const useNotification = () => { - const context = useContext(NotificationContext); - if (context === undefined) { - throw new Error( - "useNotification must be used within a NotificationProvider" - ); - } - return context; -}; - -interface NotificationProviderProps { - children: ReactNode; -} - -export const NotificationProvider: React.FC = ({ - children, -}) => { - const [expoPushToken, setExpoPushToken] = useState(null); - const [notification, setNotification] = - useState(null); - const [error, setError] = useState(null); - const [notifPermission, setNotifPermission] = React.useState("pending"); - - const notificationListener = useRef(null); - const responseListener = useRef(null); - const router = useRouter(); - - useEffect(() => { - registerForPushNotificationsAsync() - .then((token) => { - setExpoPushToken(token); - setNotifPermission("granted"); - }) - .catch((errorRaw) => { - - const err = String(errorRaw).slice(7); //remove the "Error: " string component in beginning - - if (err === NOTIF_PERMISSION_DENIED) { - setNotifPermission("denied"); - } - }); - - notificationListener.current = - Notifications.addNotificationReceivedListener((notification) => { - setNotification(notification); - // Show a visible toast when app is in foreground - const content = notification.request?.content; - if (content) { - NotificationToast( - content.title || "Notification", - content.body || "", - content.data || {} - ); - } - }); - - responseListener.current = - Notifications.addNotificationResponseReceivedListener((response) => { - const data = response.notification.request.content.data; - if (data && data.doctorId) { - router.navigate(`/(drawer)/dashboard`); - } else if (data && data.tokenId) { - router.navigate(`/(drawer)/dashboard`); - } - }); - - return () => { - if (notificationListener.current) { - Notifications.removeNotificationSubscription( - notificationListener.current - ); - } - if (responseListener.current) { - Notifications.removeNotificationSubscription(responseListener.current); - } - }; - }, []); - - return ( - - {children} - - ); -}; diff --git a/apps/admin-ui/services/notif-service/notif-register.ts b/apps/admin-ui/services/notif-service/notif-register.ts deleted file mode 100755 index dc317f6..0000000 --- a/apps/admin-ui/services/notif-service/notif-register.ts +++ /dev/null @@ -1,51 +0,0 @@ -import * as Notifications from "expo-notifications"; -import * as Device from "expo-device"; -import Constants from "expo-constants"; -import { Platform } from "react-native"; -import { NOTIF_PERMISSION_DENIED } from "common-ui/src/lib/const-strs"; - -export async function registerForPushNotificationsAsync() { - if (Platform.OS === "android") { - await Notifications.setNotificationChannelAsync("default", { - name: "default", - importance: Notifications.AndroidImportance.MAX, - vibrationPattern: [0, 250, 250, 250], - lightColor: "#FF231F7C", - }); - } - - if (Device.isDevice) { - const { status: existingStatus } = - await Notifications.getPermissionsAsync(); - let finalStatus = existingStatus; - if (existingStatus !== "granted") { - const { status } = await Notifications.requestPermissionsAsync(); - finalStatus = status; - } - - if (finalStatus !== "granted") { - throw new Error( - NOTIF_PERMISSION_DENIED - ); - } - const projectId = - Constants?.expoConfig?.extra?.eas?.projectId ?? - Constants?.easConfig?.projectId; - if (!projectId) { - throw new Error("Project ID not found"); - } - try { - const pushTokenString = ( - await Notifications.getExpoPushTokenAsync({ - projectId, - }) - ).data; - console.log(pushTokenString); - return pushTokenString; - } catch (e: unknown) { - throw new Error(`${e}`); - } - } else { - throw new Error("Must use physical device for push notifications"); - } -} \ No newline at end of file diff --git a/apps/admin-ui/src/api-hooks/banner.api.ts b/apps/admin-ui/src/api-hooks/banner.api.ts deleted file mode 100644 index cdcefbe..0000000 --- a/apps/admin-ui/src/api-hooks/banner.api.ts +++ /dev/null @@ -1,26 +0,0 @@ -// Types -export interface Banner { - id: number; - name: string; - imageUrl: string; - description?: string; - skuIds?: number[]; - redirectUrl?: string; - serialNum: number; - isActive: boolean; - createdAt: string; - lastUpdated: string; -} - -export interface CreateBannerPayload { - name: string; - imageUrl: string; - description?: string; - skuIds?: number[]; - redirectUrl?: string; - serialNum: number; -} - -export interface UpdateBannerPayload extends Partial { - isActive?: boolean; -} diff --git a/apps/admin-ui/src/components/CouponForm.tsx b/apps/admin-ui/src/components/CouponForm.tsx index 1e33c2e..a6b94d9 100644 --- a/apps/admin-ui/src/components/CouponForm.tsx +++ b/apps/admin-ui/src/components/CouponForm.tsx @@ -150,7 +150,6 @@ export default function CouponForm({ onSubmit, isLoading, initialValues }: Coupo const newSelection = current.includes(userId) ? current.filter(id => id !== userId) : [...current, userId]; - console.log('Toggling user:', userId, 'New selection:', newSelection); setFieldValue('applicableUsers', newSelection); }; diff --git a/apps/admin-ui/utils/getCurrentUserId.ts b/apps/admin-ui/utils/getCurrentUserId.ts deleted file mode 100755 index d2a8af4..0000000 --- a/apps/admin-ui/utils/getCurrentUserId.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {jwtDecode} from 'jwt-decode'; -import { getJWT } from '@/hooks/useJWT'; - -export async function getCurrentUserId(): Promise { - const token = await getJWT(); - if (!token) return null; - try { - const decoded: any = jwtDecode(token); - // Adjust this if your JWT uses a different field for user id - return decoded.id || decoded.userId || null; - } catch { - return null; - } -} diff --git a/apps/user-ui/app/(drawer)/(tabs)/me/about/_layout.tsx b/apps/user-ui/app/(drawer)/(tabs)/me/about/_layout.tsx deleted file mode 100644 index 51cdaa5..0000000 --- a/apps/user-ui/app/(drawer)/(tabs)/me/about/_layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Stack } from 'expo-router' - -function AboutLayout() { - return ( - - ) -} - -export default AboutLayout \ No newline at end of file diff --git a/apps/user-ui/app/(drawer)/(tabs)/me/about/index.tsx b/apps/user-ui/app/(drawer)/(tabs)/me/about/index.tsx deleted file mode 100644 index 845bbd1..0000000 --- a/apps/user-ui/app/(drawer)/(tabs)/me/about/index.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import React from 'react'; -import { View, ScrollView, Linking } from 'react-native'; -import { Image } from 'expo-image'; -import { AppContainer, MyText, tw, MyTouchableOpacity } from 'common-ui'; -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import FontAwesome5 from '@expo/vector-icons/FontAwesome5'; - -export default function About() { - const openLink = (url: string) => { - Linking.openURL(url).catch((err) => {}); - }; - - return ( - - - {/* Hero Section */} - - - - - - - - Meat Farmer - - - Bringing local trust and online convenience together. - - - - - {/* Mission Cards */} - - Our Mission - - - - - - Local Roots - Based in MBNR, supporting our community. - - - - - - Best Price - Minimizing costs to enhance your buying experience. - - - - - - Quality First - Committed to fresh, high-quality meat products. - - - - - - Farmers First - Dedicated to supporting local farmers. - - - - - {/* Sourcing Section */} - - - - - - - Sourcing & Quality - - - - - - - All items are procured directly from authorized dealers. - - - - - - 100% local products sourced from trusted suppliers. - - - - - - All products are purely Halal certified. - - - - - - - {/* Payments & Refunds Section */} - - - - - - - Payments & Refunds - - - - - Payment Options: Online or Cash on Delivery (COD). - - - Complaints: Must be raised within 12 hours of delivery. - - - - - - - - Refunds are processed to the original payment method. - - - - - - Alternatively, receive a refund coupon for future purchases. - - - - - - Processing time: Up to 3 business days. - - - - - - - {/* Footer */} - - Follow us - - - - - - - - - - - - - ยฉ 2024 Meat Farmer. All rights reserved. - - - - - ); -} \ No newline at end of file diff --git a/apps/user-ui/app/(drawer)/(tabs)/me/my-orders/index.tsx b/apps/user-ui/app/(drawer)/(tabs)/me/my-orders/index.tsx index 119b25c..57f308e 100755 --- a/apps/user-ui/app/(drawer)/(tabs)/me/my-orders/index.tsx +++ b/apps/user-ui/app/(drawer)/(tabs)/me/my-orders/index.tsx @@ -561,34 +561,6 @@ export default function MyOrders() { createRazorpayOrderMutation.mutate({ orderId: orderId.toString() }); }; - // const initiateRazorpayPayment = (razorpayOrderId: string, key: string, amount: number) => { - // const options = { - // key, - // amount: amount * 100, // in paisa - // currency: 'INR', - // order_id: razorpayOrderId, - // name: 'Meat Farmer', - // description: 'Order Payment Retry', - // prefill: { - // // Add user details if available - // }, - // }; - - // RazorpayCheckout.open(options) - // .then((data: any) => { - // // Payment success - // verifyPaymentMutation.mutate({ - // razorpay_payment_id: data.razorpay_payment_id, - // razorpay_order_id: data.razorpay_order_id, - // razorpay_signature: data.razorpay_signature, - // }); - // }) - // .catch((error: any) => { - // Alert.alert('Payment Failed', 'Payment failed. Please try again.'); - // refetch(); - // }); - // }; - if (isLoading && currentPage === 1) { return ( diff --git a/apps/user-ui/app/_layout.tsx b/apps/user-ui/app/_layout.tsx index f6da538..4efd7af 100755 --- a/apps/user-ui/app/_layout.tsx +++ b/apps/user-ui/app/_layout.tsx @@ -8,7 +8,7 @@ import { Stack } from "expo-router"; import "react-native-reanimated"; import { useColorScheme } from "@/hooks/useColorScheme"; -import { Appearance, Dimensions, StatusBar, View } from "react-native"; +import { Appearance, StatusBar, View } from "react-native"; import { QueryClientProvider } from "@tanstack/react-query"; import { theme , MyStatusBar } from "common-ui"; import queryClient from "@/utils/queryClient"; @@ -37,8 +37,6 @@ export default function RootLayout() { SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), }); - console.log('from layout') - React.useEffect(() => { Appearance.setColorScheme('light') }, []); diff --git a/apps/user-ui/app/api/auth/authorize+api.ts b/apps/user-ui/app/api/auth/authorize+api.ts deleted file mode 100644 index 215e170..0000000 --- a/apps/user-ui/app/api/auth/authorize+api.ts +++ /dev/null @@ -1,55 +0,0 @@ -import constants from "@/src/constants"; - -const GOOGLE_CLIENT_ID = constants.GOOGLE_CLIENT_ID; -export async function GET(request: Request) { - - if (!GOOGLE_CLIENT_ID) { - return Response.json( - { error: "Missing GOOGLE_CLIENT_ID environment variable" }, - { status: 500 } - ); - } - - const url = new URL(request.url); - let idpClientId: string; - - const internalClient = url.searchParams.get("client_id"); - - const redirectUri = url.searchParams.get("redirect_uri"); - - - let platform; - - if (redirectUri === constants.APP_SCHEME) { - platform = "mobile"; - } else if (redirectUri === constants.BASE_URL) { - platform = "web"; - } else { - return Response.json({ error: "Invalid redirect_uri" }, { status: 400 }); - } - - // use state to drive redirect back to platform - let state = platform + "|" + url.searchParams.get("state"); - - if (internalClient === "google") { - idpClientId = GOOGLE_CLIENT_ID; - } else { - return Response.json({ error: "Invalid client" }, { status: 400 }); - } - - // additional enforcement - if (!state) { - return Response.json({ error: "Invalid state" }, { status: 400 }); - } - - const params = new URLSearchParams({ - client_id: idpClientId, - redirect_uri: constants.BASE_URL + "/api/auth/callback", - response_type: "code", - scope: url.searchParams.get("scope") || "identity", - state: state, - prompt: "select_account", - }); - - return Response.redirect(constants.GOOGLE_AUTH_URL + "?" + params.toString()); -} \ No newline at end of file diff --git a/apps/user-ui/app/api/auth/callback+api.ts b/apps/user-ui/app/api/auth/callback+api.ts deleted file mode 100644 index 06ac1d2..0000000 --- a/apps/user-ui/app/api/auth/callback+api.ts +++ /dev/null @@ -1,28 +0,0 @@ -// import { BASE_URL, APP_SCHEME } from "@/utils/constants"; - -import constants from "@/src/constants"; - -const BASE_URL = constants.BASE_URL; -const APP_SCHEME = constants.APP_SCHEME; - -export async function GET(request: Request) { - const incomingParams = new URLSearchParams(request.url.split("?")[1]); - const combinedPlatformAndState = incomingParams.get("state"); - if (!combinedPlatformAndState) { - return Response.json({ error: "Invalid state" }, { status: 400 }); - } - // strip platform to return state as it was set on the client - const platform = combinedPlatformAndState.split("|")[0]; - const state = combinedPlatformAndState.split("|")[1]; - - const outgoingParams = new URLSearchParams({ - code: incomingParams.get("code")?.toString() || "", - state, - }); - - return Response.redirect( - (platform === "web" ? BASE_URL : APP_SCHEME) + - "?" + - outgoingParams.toString() - ); -} \ No newline at end of file diff --git a/apps/user-ui/app/api/auth/token+api.ts b/apps/user-ui/app/api/auth/token+api.ts deleted file mode 100644 index e89e4c2..0000000 --- a/apps/user-ui/app/api/auth/token+api.ts +++ /dev/null @@ -1,3 +0,0 @@ -export async function GET(request:Request) { - -} \ No newline at end of file diff --git a/apps/user-ui/assets/images/adaptive-icon.png b/apps/user-ui/assets/images/adaptive-icon.png deleted file mode 100755 index 03d6f6b6c6727954aec1d8206222769afd178d8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17547 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�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�b}cC>j)J5H_cydN;4K-_;n-9s&t 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 fuJlK6B+v4s1d`{mdl=zgU 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