Merge pull request 'purchase viewing' (#28) from feature/user-section into main
Reviewed-on: https://git.softuniq.eu/Telegram-Market/telegram-shop/pulls/28
This commit is contained in:
commit
c1276e1187
197
src/handlers/userPurchaseHandler.js
Normal file
197
src/handlers/userPurchaseHandler.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import db from '../config/database.js';
|
||||||
|
import User from '../models/User.js';
|
||||||
|
import WalletService from "../utils/walletService.js";
|
||||||
|
import config from "../config/config.js";
|
||||||
|
|
||||||
|
export default class UserPurchaseHandler {
|
||||||
|
constructor(bot) {
|
||||||
|
this.bot = bot;
|
||||||
|
this.userStates = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
async viewPurchasePage(userId, page) {
|
||||||
|
try {
|
||||||
|
const limit = 10;
|
||||||
|
const offset = (page || 0) * limit;
|
||||||
|
|
||||||
|
const previousPage = page > 0 ? page - 1 : 0;
|
||||||
|
const nextPage = page + 1;
|
||||||
|
|
||||||
|
const purchases = await db.allAsync(`
|
||||||
|
SELECT
|
||||||
|
p.*,
|
||||||
|
pr.name as product_name,
|
||||||
|
pr.description,
|
||||||
|
l.country,
|
||||||
|
l.city,
|
||||||
|
l.district
|
||||||
|
FROM purchases p
|
||||||
|
JOIN products pr ON p.product_id = pr.id
|
||||||
|
JOIN locations l ON pr.location_id = l.id
|
||||||
|
WHERE p.user_id = ?
|
||||||
|
ORDER BY p.purchase_date DESC
|
||||||
|
LIMIT ?
|
||||||
|
OFFSET ?
|
||||||
|
`, [userId, limit, offset]);
|
||||||
|
|
||||||
|
if ((purchases.length === 0) && (page == 0)) {
|
||||||
|
return {
|
||||||
|
text: 'You haven\'t made any purchases yet.',
|
||||||
|
markup: [[
|
||||||
|
{text: '🛍 Browse Products', callback_data: 'shop_start'}
|
||||||
|
]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((purchases.length === 0) && (page > 0)) {
|
||||||
|
return await this.viewPurchasePage(userId, previousPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
...purchases.map(item => [{
|
||||||
|
text: `${item.product_name} [${new Date(item.purchase_date).toLocaleString()}]`,
|
||||||
|
callback_data: `view_purchase_${item.id}`
|
||||||
|
}]),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
keyboard.inline_keyboard.push([
|
||||||
|
{text: `«`, callback_data: `list_purchases_${previousPage}`},
|
||||||
|
{text: `»`, callback_data: `list_purchases_${nextPage}`},
|
||||||
|
]);
|
||||||
|
|
||||||
|
keyboard.inline_keyboard.push([
|
||||||
|
{text: '🛍 Browse Products', callback_data: 'shop_start'}
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
text: `📦 Select purchase to view detailed information:`,
|
||||||
|
markup: keyboard
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in showPurchases:', error);
|
||||||
|
return {text: 'Error loading purchase history. Please try again.'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePurchaseListPage(callbackQuery) {
|
||||||
|
const telegramId = callbackQuery.from.id;
|
||||||
|
const chatId = callbackQuery.message.chat.id;
|
||||||
|
|
||||||
|
const page = callbackQuery.data.replace('list_purchases_', '');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const user = await User.getById(telegramId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
await this.bot.sendMessage(chatId, 'User not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {text, markup} = await this.viewPurchasePage(user.id, parseInt(page));
|
||||||
|
await this.bot.editMessageText(text, {
|
||||||
|
chat_id: chatId,
|
||||||
|
message_id: callbackQuery.message.message_id,
|
||||||
|
reply_markup: markup,
|
||||||
|
parse_mode: 'Markdown',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async showPurchases(msg) {
|
||||||
|
// const userId = callbackQuery.from.id;
|
||||||
|
// const chatId = callbackQuery.message.chat.id;
|
||||||
|
|
||||||
|
const chatId = msg.chat.id;
|
||||||
|
const telegramId = msg.from.id;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const user = await User.getById(telegramId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
await this.bot.sendMessage(chatId, 'User not found.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {text, markup} = await this.viewPurchasePage(user.id, 0);
|
||||||
|
|
||||||
|
await this.bot.sendMessage(chatId, text, {reply_markup: markup, parse_mode: 'Markdown'});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in handleSubcategorySelection:', error);
|
||||||
|
await this.bot.sendMessage(chatId, 'Error loading products. Please try again.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async viewPurchase(callbackQuery) {
|
||||||
|
const chatId = callbackQuery.message.chat.id;
|
||||||
|
const purchaseId = callbackQuery.data.replace('view_purchase_', '');
|
||||||
|
|
||||||
|
const purchase = await db.getAsync(`
|
||||||
|
SELECT
|
||||||
|
p.*,
|
||||||
|
pr.name as product_name,
|
||||||
|
pr.description,
|
||||||
|
l.country,
|
||||||
|
l.city,
|
||||||
|
l.district
|
||||||
|
FROM purchases p
|
||||||
|
JOIN products pr ON p.product_id = pr.id
|
||||||
|
JOIN locations l ON pr.location_id = l.id
|
||||||
|
WHERE p.id = ?
|
||||||
|
`, [purchaseId]);
|
||||||
|
|
||||||
|
if (!purchase) {
|
||||||
|
await this.bot.sendMessage(chatId, "No such purchase");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const product = await db.getAsync(
|
||||||
|
`SELECT * FROM products WHERE id = ?`,
|
||||||
|
[purchase.product_id]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!product) {
|
||||||
|
await this.bot.sendMessage(chatId, "No such product");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hiddenPhotoMessage;
|
||||||
|
if (product.hidden_photo_url) {
|
||||||
|
try {
|
||||||
|
hiddenPhotoMessage = await this.bot.sendPhoto(chatId, product.hidden_photo_url, {caption: 'Hidden photo'});
|
||||||
|
} catch (e) {
|
||||||
|
hiddenPhotoMessage = await this.bot.sendPhoto(chatId, "./corrupt-photo.jpg", {caption: 'Hidden photo'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = `
|
||||||
|
📦 Purchase Details:
|
||||||
|
Name: ${purchase.product_name}
|
||||||
|
Quantity: ${purchase.quantity}
|
||||||
|
Total: $${purchase.total_price}
|
||||||
|
Location: ${purchase.country}, ${purchase.city}
|
||||||
|
Payment: ${purchase.wallet_type}
|
||||||
|
Date: ${new Date(purchase.purchase_date).toLocaleString()}
|
||||||
|
|
||||||
|
🔒 Private Information:
|
||||||
|
${product.private_data}
|
||||||
|
Hidden Location: ${product.hidden_description}
|
||||||
|
Coordinates: ${product.hidden_coordinates}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const keyboard = {
|
||||||
|
inline_keyboard: [
|
||||||
|
[{text: "I've got it!", callback_data: "Asdasdasd"}],
|
||||||
|
[{text: "Contact support", url: config.SUPPORT_LINK}]
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.bot.sendMessage(chatId, message, {reply_markup: keyboard});
|
||||||
|
await this.bot.deleteMessage(chatId, callbackQuery.message.message_id);
|
||||||
|
}
|
||||||
|
}
|
10
src/index.js
10
src/index.js
@ -12,6 +12,7 @@ import ErrorHandler from './utils/errorHandler.js';
|
|||||||
import User from './models/User.js';
|
import User from './models/User.js';
|
||||||
import AdminUserLocationHandler from "./handlers/adminUserLocationHandler.js";
|
import AdminUserLocationHandler from "./handlers/adminUserLocationHandler.js";
|
||||||
import AdminDumpHandler from "./handlers/adminDumpHandler.js";
|
import AdminDumpHandler from "./handlers/adminDumpHandler.js";
|
||||||
|
import UserPurchaseHandler from "./handlers/userPurchaseHandler.js";
|
||||||
|
|
||||||
// Debug logging function
|
// Debug logging function
|
||||||
const logDebug = (action, functionName) => {
|
const logDebug = (action, functionName) => {
|
||||||
@ -41,6 +42,7 @@ const adminLocationHandler = new AdminLocationHandler(bot);
|
|||||||
const adminUserLocationHandler = new AdminUserLocationHandler(bot);
|
const adminUserLocationHandler = new AdminUserLocationHandler(bot);
|
||||||
const adminProductHandler = new AdminProductHandler(bot);
|
const adminProductHandler = new AdminProductHandler(bot);
|
||||||
const adminDumpHandler = new AdminDumpHandler(bot);
|
const adminDumpHandler = new AdminDumpHandler(bot);
|
||||||
|
const userPurchaseHandler = new UserPurchaseHandler(bot);
|
||||||
|
|
||||||
// Start command - Create user profile
|
// Start command - Create user profile
|
||||||
bot.onText(/\/start/, async (msg) => {
|
bot.onText(/\/start/, async (msg) => {
|
||||||
@ -125,7 +127,7 @@ bot.on('message', async (msg) => {
|
|||||||
await userWalletsHandler.showBalance(msg);
|
await userWalletsHandler.showBalance(msg);
|
||||||
break;
|
break;
|
||||||
case '🛍 Purchases':
|
case '🛍 Purchases':
|
||||||
await userProductHandler.showPurchases(msg);
|
await userPurchaseHandler.showPurchases(msg);
|
||||||
break;
|
break;
|
||||||
case '📦 Manage Products':
|
case '📦 Manage Products':
|
||||||
if (adminHandler.isAdmin(msg.from.id)) {
|
if (adminHandler.isAdmin(msg.from.id)) {
|
||||||
@ -242,6 +244,12 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
} else if (action.startsWith('pay_with_')) {
|
} else if (action.startsWith('pay_with_')) {
|
||||||
logDebug(action, 'handlePay');
|
logDebug(action, 'handlePay');
|
||||||
await userProductHandler.handlePay(callbackQuery);
|
await userProductHandler.handlePay(callbackQuery);
|
||||||
|
} else if (action.startsWith('list_purchases_')) {
|
||||||
|
logDebug(action, 'handlePurchaseListPage');
|
||||||
|
await userPurchaseHandler.handlePurchaseListPage(callbackQuery);
|
||||||
|
} else if (action.startsWith('view_purchase_')) {
|
||||||
|
logDebug(action, 'viewPurchase');
|
||||||
|
await userPurchaseHandler.viewPurchase(callbackQuery);
|
||||||
}
|
}
|
||||||
// Admin location management
|
// Admin location management
|
||||||
else if (action === 'add_location') {
|
else if (action === 'add_location') {
|
||||||
|
Loading…
Reference in New Issue
Block a user