Delet Subcategory Function

This commit is contained in:
NW 2024-12-13 16:41:41 +00:00
parent 3e78e231f3
commit 95d5fe644d
4 changed files with 121 additions and 140 deletions

View File

@ -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');

View File

@ -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<pre>${jsonExample}</pre>\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<pre>${jsonExample}</pre>\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;
}

View File

@ -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);

View File

@ -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