From 95d5fe644db2004e987377ac2d132a7e93fdda6b Mon Sep 17 00:00:00 2001 From: NW Date: Fri, 13 Dec 2024 16:41:41 +0000 Subject: [PATCH] Delet Subcategory Function --- src/config/database.js | 16 +- .../adminHandlers/adminProductHandler.js | 197 +++++++++--------- src/index.js | 14 -- src/services/productService.js | 34 +-- 4 files changed, 121 insertions(+), 140 deletions(-) diff --git a/src/config/database.js b/src/config/database.js index 2917fc0..8bb8c60 100644 --- a/src/config/database.js +++ b/src/config/database.js @@ -165,7 +165,6 @@ const initDb = async () => { id INTEGER PRIMARY KEY AUTOINCREMENT, location_id INTEGER NOT NULL, category_id INTEGER NOT NULL, - subcategory_id INTEGER NOT NULL, name TEXT NOT NULL, description TEXT, private_data TEXT, @@ -177,8 +176,7 @@ const initDb = async () => { hidden_description TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE CASCADE, - FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE, - FOREIGN KEY (subcategory_id) REFERENCES subcategories(id) ON DELETE CASCADE + FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE ) `); @@ -222,18 +220,6 @@ const initDb = async () => { ) `); - // Create subcategories table - await db.runAsync(` - CREATE TABLE IF NOT EXISTS subcategories ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - category_id INTEGER NOT NULL, - name TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE, - UNIQUE(category_id, name) - ) - `); - // Commit transaction await db.runAsync('COMMIT'); console.log('Database tables initialized successfully'); diff --git a/src/handlers/adminHandlers/adminProductHandler.js b/src/handlers/adminHandlers/adminProductHandler.js index 9d88056..192b671 100644 --- a/src/handlers/adminHandlers/adminProductHandler.js +++ b/src/handlers/adminHandlers/adminProductHandler.js @@ -327,33 +327,31 @@ export default class AdminProductHandler { if (!this.isAdmin(callbackQuery.from.id)) { return; } - + const chatId = callbackQuery.message.chat.id; const messageId = callbackQuery.message.message_id; const [locationId, categoryId] = callbackQuery.data.replace('prod_category_', '').split('_'); - + try { - const subcategories = await CategoryService.getSubcategoriesByCategoryId(categoryId); const category = await CategoryService.getCategoryById(categoryId); const location = await LocationService.getLocationById(locationId); - + + // Получаем товары для выбранной категории + const products = await ProductService.getProductsByCategoryId(categoryId); + const keyboard = { inline_keyboard: [ - ...subcategories.map(sub => [{ - text: sub.name, - callback_data: `prod_subcategory_${locationId}_${categoryId}_${sub.id}` + ...products.map(prod => [{ + text: `${prod.name} - $${prod.price} (${prod.quantity_in_stock} left)`, + callback_data: `view_product_${prod.id}` }]), - [{text: '➕ Add Subcategory', callback_data: `add_subcategory_${locationId}_${categoryId}`}], - [{text: '✏️ Edit Category', callback_data: `edit_category_${locationId}_${categoryId}`}], - [{ - text: '« Back', - callback_data: `prod_district_${location.country}_${location.city}_${location.district}` - }] + [{ text: '➕ Add Product', callback_data: `add_product_${locationId}_${categoryId}` }], + [{ text: '« Back', callback_data: `prod_district_${location.country}_${location.city}_${location.district}` }] ] }; - + await bot.editMessageText( - `📦 Category: ${category.name}\nSelect or add subcategory:`, + `📦 Category: ${category.name}\nSelect or add product:`, { chat_id: chatId, message_id: messageId, @@ -362,7 +360,7 @@ export default class AdminProductHandler { ); } catch (error) { console.error('Error in handleCategorySelection:', error); - await bot.sendMessage(chatId, 'Error loading subcategories. Please try again.'); + await bot.sendMessage(chatId, 'Error loading products. Please try again.'); } } @@ -517,40 +515,43 @@ export default class AdminProductHandler { } } - static async handleSubcategorySelection(callbackQuery) { + static async handleCategorySelection(callbackQuery) { if (!this.isAdmin(callbackQuery.from.id)) { return; } - + const chatId = callbackQuery.message.chat.id; const messageId = callbackQuery.message.message_id; - const [locationId, categoryId, subcategoryId] = callbackQuery.data.replace('prod_subcategory_', '').split('_'); - - const state = userStates.get(chatId) - - for (const msgId of state?.msgToDelete || []) { - try { - await bot.deleteMessage(chatId, msgId); - } catch (e) { - // ignore if can't delete - } - } - - userStates.delete(chatId); - + const [locationId, categoryId] = callbackQuery.data.replace('prod_category_', '').split('_'); + try { - const {text, markup} = await this.viewProductsPage(locationId, categoryId, subcategoryId, 0); - + const category = await CategoryService.getCategoryById(categoryId); + const location = await LocationService.getLocationById(locationId); + + // Получаем товары для выбранной категории + const products = await ProductService.getProductsByCategoryId(categoryId); + + const keyboard = { + inline_keyboard: [ + ...products.map(prod => [{ + text: `${prod.name} - $${prod.price} (${prod.quantity_in_stock} left)`, + callback_data: `view_product_${prod.id}` + }]), + [{ text: '➕ Add Product', callback_data: `add_product_${locationId}_${categoryId}` }], + [{ text: '« Back', callback_data: `prod_district_${location.country}_${location.city}_${location.district}` }] + ] + }; + await bot.editMessageText( - text, + `📦 Category: ${category.name}\nSelect or add product:`, { chat_id: chatId, message_id: messageId, - reply_markup: markup + reply_markup: keyboard } ); } catch (error) { - console.error('Error in handleSubcategorySelection:', error); + console.error('Error in handleCategorySelection:', error); await bot.sendMessage(chatId, 'Error loading products. Please try again.'); } } @@ -581,11 +582,11 @@ export default class AdminProductHandler { if (!this.isAdmin(callbackQuery.from.id)) { return; } - + const chatId = callbackQuery.message.chat.id; const messageId = callbackQuery.message.message_id; - const [locationId, categoryId, subcategoryId] = callbackQuery.data.replace('add_product_', '').split('_'); - + const [locationId, categoryId] = callbackQuery.data.replace('add_product_', '').split('_'); + try { const sampleProducts = [ { @@ -600,17 +601,16 @@ export default class AdminProductHandler { hidden_description: "Secret location details" } ]; - + const jsonExample = JSON.stringify(sampleProducts, null, 2); - const message = `To import products, send a JSON file with an array of products in the following format:\n\n
${jsonExample}
\n\nEach product must have all the fields shown above.\n\nYou can either:\n1. Send the JSON as text\n2. Upload a .json file`; - + const message = `To add product, send a JSON file with product in the following format:\n\n
${jsonExample}
\n\nProduct must have all the fields shown above.\n\nYou can either:\n1. Send the JSON as text\n2. Upload a .json file`; + userStates.set(chatId, { action: 'import_products', locationId, - categoryId, - subcategoryId + categoryId }); - + await bot.editMessageText(message, { chat_id: chatId, message_id: messageId, @@ -619,7 +619,7 @@ export default class AdminProductHandler { inline_keyboard: [[ { text: '❌ Cancel', - callback_data: `prod_subcategory_${locationId}_${categoryId}_${subcategoryId}` + callback_data: `prod_category_${locationId}_${categoryId}` } ]] } @@ -633,40 +633,40 @@ export default class AdminProductHandler { static async handleProductImport(msg) { const chatId = msg.chat.id; const state = userStates.get(chatId); - + if (!state || state.action !== 'import_products') { return false; } - + if (!this.isAdmin(msg.from.id)) { await bot.sendMessage(chatId, 'Unauthorized access.'); return; } - + try { let products; let jsonContent; - + // Handle file upload if (msg.document) { if (!msg.document.file_name.endsWith('.json')) { await bot.sendMessage(chatId, 'Please upload a .json file.'); return true; } - + const file = await bot.getFile(msg.document.file_id); - + const fileContent = await bot.downloadFile(file.file_id, '.'); jsonContent = await fs.readFile(fileContent, 'utf8'); await fs.rm(fileContent); - + } else if (msg.text) { jsonContent = msg.text; } else { await bot.sendMessage(chatId, 'Please send either a JSON file or JSON text.'); return true; } - + try { products = JSON.parse(jsonContent); if (!Array.isArray(products)) { @@ -676,28 +676,28 @@ export default class AdminProductHandler { await bot.sendMessage(chatId, 'Invalid JSON format. Please check the format and try again.'); return true; } - + await db.runAsync('BEGIN TRANSACTION'); - + for (const product of products) { await db.runAsync( `INSERT INTO products ( - location_id, category_id, subcategory_id, - name, price, description, private_data, - quantity_in_stock, photo_url, hidden_photo_url, - hidden_coordinates, hidden_description - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + location_id, category_id, + name, price, description, private_data, + quantity_in_stock, photo_url, hidden_photo_url, + hidden_coordinates, hidden_description + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ - state.locationId, state.categoryId, state.subcategoryId, + state.locationId, state.categoryId, product.name, product.price, product.description, product.private_data, product.quantity_in_stock, product.photo_url, product.hidden_photo_url, product.hidden_coordinates, product.hidden_description ] ); } - + await db.runAsync('COMMIT'); - + await bot.sendMessage( chatId, `✅ Successfully imported ${products.length} products!`, @@ -706,96 +706,95 @@ export default class AdminProductHandler { inline_keyboard: [[ { text: '« Back to Products', - callback_data: `prod_subcategory_${state.locationId}_${state.categoryId}_${state.subcategoryId}` + callback_data: `prod_category_${state.locationId}_${state.categoryId}` } ]] } } ); - + userStates.delete(chatId); } catch (error) { console.error('Error importing products:', error); await bot.sendMessage(chatId, 'Error importing products. Please check the data and try again.'); await db.runAsync('ROLLBACK'); } - + return true; } static async handleProductEditImport(msg) { const chatId = msg.chat.id; const state = userStates.get(chatId); - + if (!state || state.action !== 'edit_product') { return false; } - + if (!this.isAdmin(msg.from.id)) { await bot.sendMessage(chatId, 'Unauthorized access.'); return; } - + try { let product; let jsonContent; - + // Handle file upload if (msg.document) { if (!msg.document.file_name.endsWith('.json')) { await bot.sendMessage(chatId, 'Please upload a .json file.'); return true; } - + const file = await bot.getFile(msg.document.file_id); - + const fileContent = await bot.downloadFile(file.file_id, '.'); jsonContent = await fs.readFile(fileContent, 'utf8'); await fs.rm(fileContent); - + } else if (msg.text) { jsonContent = msg.text; } else { await bot.sendMessage(chatId, 'Please send either a JSON file or JSON text.'); return true; } - + try { product = JSON.parse(jsonContent); } catch (e) { await bot.sendMessage(chatId, 'Invalid JSON format. Please check the format and try again.'); return true; } - + await db.runAsync('BEGIN TRANSACTION'); - + await db.runAsync( `UPDATE products SET - location_id = ?, - category_id = ?, - subcategory_id = ?, - name = ?, - price = ?, - description = ?, - private_data = ?, - quantity_in_stock = ?, - photo_url = ?, - hidden_photo_url = ?, - hidden_coordinates = ?, - hidden_description = ? - WHERE - id = ? - `, + location_id = ?, + category_id = ?, + name = ?, + price = ?, + description = ?, + private_data = ?, + quantity_in_stock = ?, + photo_url = ?, + hidden_photo_url = ?, + hidden_coordinates = ?, + hidden_description = ? + WHERE + id = ? + `, [ - state.locationId, state.categoryId, state.subcategoryId, + state.locationId, state.categoryId, product.name, product.price, product.description, product.private_data, product.quantity_in_stock, product.photo_url, product.hidden_photo_url, product.hidden_coordinates, product.hidden_description, state.productId ] ); - + await db.runAsync('COMMIT'); - + await bot.sendMessage( chatId, `✅ Successfully edited!`, @@ -804,20 +803,20 @@ export default class AdminProductHandler { inline_keyboard: [[ { text: '« Back to Products', - callback_data: `prod_subcategory_${state.locationId}_${state.categoryId}_${state.subcategoryId}` + callback_data: `prod_category_${state.locationId}_${state.categoryId}` } ]] } } ); - + userStates.delete(chatId); } catch (error) { console.error('Error importing products:', error); await bot.sendMessage(chatId, 'Error importing products. Please check the data and try again.'); await db.runAsync('ROLLBACK'); } - + return true; } diff --git a/src/index.js b/src/index.js index 0df0a90..05f3347 100644 --- a/src/index.js +++ b/src/index.js @@ -69,11 +69,6 @@ bot.on('message', async (msg) => { return; } - // Check for admin subcategory input - if (await adminProductHandler.handleSubcategoryInput(msg)) { - return; - } - // Check for product import if (await adminProductHandler.handleProductImport(msg)) { return; @@ -217,9 +212,6 @@ bot.on('callback_query', async (callbackQuery) => { } else if (action.startsWith('shop_category_')) { logDebug(action, 'handleCategorySelection'); await userProductHandler.handleCategorySelection(callbackQuery); - } else if (action.startsWith('shop_subcategory_')) { - logDebug(action, 'handleSubcategorySelection'); - await userProductHandler.handleSubcategorySelection(callbackQuery); } else if (action.startsWith('shop_product_')) { logDebug(action, 'handleProductSelection'); await userProductHandler.handleProductSelection(callbackQuery); @@ -282,12 +274,6 @@ bot.on('callback_query', async (callbackQuery) => { } else if (action.startsWith('prod_category_')) { logDebug(action, 'handleCategorySelection'); await adminProductHandler.handleCategorySelection(callbackQuery); - } else if (action.startsWith('add_subcategory_')) { - logDebug(action, 'handleAddSubcategory'); - await adminProductHandler.handleAddSubcategory(callbackQuery); - } else if (action.startsWith('prod_subcategory_')) { - logDebug(action, 'handleSubcategorySelection'); - await adminProductHandler.handleSubcategorySelection(callbackQuery); } else if (action.startsWith('list_products_')) { logDebug(action, 'handleProductListPage'); await adminProductHandler.handleProductListPage(callbackQuery); diff --git a/src/services/productService.js b/src/services/productService.js index 52f1669..06632ad 100644 --- a/src/services/productService.js +++ b/src/services/productService.js @@ -12,26 +12,36 @@ class ProductService { static async getDetailedProductById(productId) { return await db.getAsync( - `SELECT p.*, c.name as category_name, s.name as subcategory_name - FROM products p - JOIN categories c ON p.category_id = c.id - JOIN subcategories s ON p.subcategory_id = s.id - WHERE p.id = ?`, + `SELECT p.*, c.name as category_name + FROM products p + JOIN categories c ON p.category_id = c.id + WHERE p.id = ?`, [productId] ); } - static async getProductsByLocationAndCategory(locationId, categoryId, subcategoryId) { + static async getProductsByLocationAndCategory(locationId, categoryId) { return await db.allAsync( `SELECT id, name, price, description, quantity_in_stock, photo_url - FROM products - WHERE location_id = ? AND category_id = ? AND subcategory_id = ? - AND quantity_in_stock > 0 - ORDER BY name`, - [locationId, categoryId, subcategoryId] + FROM products + WHERE location_id = ? AND category_id = ? + AND quantity_in_stock > 0 + ORDER BY name`, + [locationId, categoryId] ); - } + + static async getProductsByCategoryId(categoryId) { + return await db.allAsync( + `SELECT id, name, price, description, quantity_in_stock, photo_url + FROM products + WHERE category_id = ? + AND quantity_in_stock > 0 + ORDER BY name`, + [categoryId] + ); + } + } export default ProductService \ No newline at end of file