feat: add Telegram customers management system with admin panel

Implement comprehensive Telegram customers storage and management functionality:

Backend:
- Add database migration for telecart_customers table with indexes
- Create TelegramCustomer model with CRUD operations
- Implement TelegramCustomerService for business logic
- Add TelegramCustomerHandler for API endpoint (saveOrUpdate)
- Add TelegramCustomersHandler for admin API (getCustomers with pagination, filtering, sorting)
- Add SendMessageHandler for sending messages to customers via Telegram
- Create custom exceptions: TelegramCustomerNotFoundException, TelegramCustomerWriteNotAllowedException
- Refactor TelegramInitDataDecoder to separate decoding logic
- Add TelegramHeader enum for header constants
- Update SignatureValidator to use TelegramInitDataDecoder
- Register new routes in bastion/routes.php and src/routes.php

Frontend (Admin):
- Add CustomersView.vue component with PrimeVue DataTable
- Implement advanced filtering (text, date, boolean filters)
- Add column visibility toggle functionality
- Add global search with debounce
- Implement message sending dialog with validation
- Add Russian locale for PrimeVue components
- Add navigation link in App.vue
- Register route in router

Frontend (SPA):
- Add saveTelegramCustomer utility function
- Integrate automatic customer data saving on app initialization
- Extract user data from Telegram.WebApp.initDataUnsafe

The system automatically saves/updates customer data when users access the Telegram Mini App,
and provides admin interface for viewing, filtering, and messaging customers.

BREAKING CHANGE: None
This commit is contained in:
2025-11-23 16:59:30 +03:00
committed by Nikita Kiselev
parent 6a59dcc0c9
commit 9a93cc7342
34 changed files with 3245 additions and 66 deletions

View File

@@ -36,6 +36,49 @@ onReady(async () => {
options: {
cssLayer: false, // если используешь Tailwind, отключает layering
},
},
locale: {
startsWith: 'Начинается с',
contains: 'Содержит',
notContains: 'Не содержит',
endsWith: 'Заканчивается на',
equals: 'Равно',
notEquals: 'Не равно',
noFilter: 'Без фильтра',
lt: 'Меньше чем',
lte: 'Меньше или равно',
gt: 'Больше чем',
gte: 'Больше или равно',
dateIs: 'Дата равна',
dateIsNot: 'Дата не равна',
dateBefore: 'Дата до',
dateAfter: 'Дата после',
clear: 'Очистить',
apply: 'Применить',
matchAll: 'Совпадает со всеми',
matchAny: 'Совпадает с любым',
addRule: 'Добавить правило',
removeRule: 'Удалить правило',
accept: 'Да',
reject: 'Нет',
choose: 'Выбрать',
upload: 'Загрузить',
cancel: 'Отмена',
dayNames: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
dayNamesShort: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
dayNamesMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
monthNames: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
monthNamesShort: ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'],
today: 'Сегодня',
weekHeader: 'Неделя',
firstDayOfWeek: 1,
dateFormat: 'dd.mm.yy',
weak: 'Слабый',
medium: 'Средний',
strong: 'Сильный',
passwordPrompt: 'Введите пароль',
emptyMessage: 'Нет доступных записей',
emptyFilterMessage: 'Нет доступных записей'
}
});
app.use(ToastService);