Files
tgex-backend/telegram_bot/screens/my_placements.go
Artem Tsyrulnikov 0577d2a00e change domen
2026-01-02 19:37:35 +03:00

229 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package screens
import (
"context"
"fmt"
"strings"
"example.com/m/bot"
"github.com/NicoNex/echotron/v3"
"github.com/rs/zerolog/log"
)
const placementsPerPage = 6
type MyPlacements struct {
CurrentPage int
WorkspaceID string
BackState bot.State
}
func (s *MyPlacements) Enter(b *bot.Bot, mode bot.RenderMode) {
// Получаем или запрашиваем JWT
jwt, err := s.ensureJWT(b)
if err != nil {
log.Error().Err(err).Msg("Failed to get JWT")
b.SendNew("❌ Ошибка авторизации. Попробуйте /start login", bot.Keyboard())
return
}
// Если workspace еще не выбран, получаем список workspace'ов
if s.WorkspaceID == "" {
workspaces, err := b.Backend.GetWorkspaces(context.Background(), jwt)
if err != nil {
log.Error().Err(err).Msg("Failed to get workspaces")
b.SendNew("❌ Не удалось загрузить workspace'ы", bot.Keyboard())
return
}
if len(workspaces) == 0 {
b.SendNew("У вас пока нет workspace'ов", bot.Keyboard())
return
}
// Используем первый workspace по умолчанию
s.WorkspaceID = workspaces[0].ID
}
s.renderPlacements(b, mode, jwt)
}
func (s *MyPlacements) renderPlacements(b *bot.Bot, mode bot.RenderMode, jwt string) {
page, err := b.Backend.GetPlacements(
context.Background(),
jwt,
s.WorkspaceID,
nil, // projectID
nil, // placementChannelID
nil, // creativeID
false, // includeArchived
s.CurrentPage+1,
placementsPerPage,
)
if err != nil {
log.Error().Err(err).Msg("Failed to get placements")
b.SendNew("❌ Не удалось загрузить закупы", bot.Keyboard())
return
}
text := "<b>📍 Мои размещения</b>\n\n"
var buttons [][]echotron.InlineKeyboardButton
if len(page.Items) == 0 {
text += `У вас пока нет размещений.
<i>Размещения создаются автоматически при создании закупа.</i>`
} else {
text += fmt.Sprintf("Выберите размещение для деталей (стр. %d из %d):\n\n", s.CurrentPage+1, page.Pages)
// Размещения по 2 в ряду
for i := 0; i < len(page.Items); i += 2 {
var row []echotron.InlineKeyboardButton
// Первый placement в ряду
placement := page.Items[i]
placementName := placement.CreativeName
if placement.PlacementChannelTitle != nil && *placement.PlacementChannelTitle != "" {
placementName = fmt.Sprintf("%s → %s", placement.CreativeName, *placement.PlacementChannelTitle)
}
row = append(row, bot.Button(placementName, fmt.Sprintf("placement:%s", placement.ID)))
// Второй placement в ряду (если есть)
if i+1 < len(page.Items) {
placement = page.Items[i+1]
placementName = placement.CreativeName
if placement.PlacementChannelTitle != nil && *placement.PlacementChannelTitle != "" {
placementName = fmt.Sprintf("%s → %s", placement.CreativeName, *placement.PlacementChannelTitle)
}
row = append(row, bot.Button(placementName, fmt.Sprintf("placement:%s", placement.ID)))
} else {
// Добавляем пустую кнопку для выравнивания
row = append(row, bot.Button(" ", "empty"))
}
buttons = append(buttons, row)
}
// Кнопки пагинации с номером страницы посередине (только если есть закупы)
var navRow []echotron.InlineKeyboardButton
// Левая стрелка (активная или неактивная)
if s.CurrentPage > 0 {
navRow = append(navRow, bot.Button("←", "prev"))
} else {
navRow = append(navRow, bot.Button("·", "empty"))
}
// Кнопка с номером страницы (неактивная)
pageButton := bot.Button(fmt.Sprintf("• %d/%d •", s.CurrentPage+1, page.Pages), "page_info")
navRow = append(navRow, pageButton)
// Правая стрелка (активная или неактивная)
if s.CurrentPage+1 < page.Pages {
navRow = append(navRow, bot.Button("→", "next"))
} else {
navRow = append(navRow, bot.Button("·", "empty"))
}
buttons = append(buttons, navRow)
}
// Кнопки управления
buttons = append(buttons, bot.Row(
bot.Button("📊 Статистика", "statistics"),
))
// Кнопка возврата в главное меню
buttons = append(buttons, bot.Row(bot.Button("← Назад", "back")))
keyboard := bot.Keyboard(buttons...)
b.Render(text, keyboard, mode)
}
func (s *MyPlacements) ensureJWT(b *bot.Bot) (string, error) {
// Проверяем, есть ли JWT в сессии
if b.Session.JWT != "" {
return b.Session.JWT, nil
}
// Если нет, запрашиваем новый
jwt, err := b.Backend.GetJWTByTelegramID(context.Background(), b.ChatID)
if err != nil {
return "", err
}
// Сохраняем в сессию
b.Session.JWT = jwt
return jwt, nil
}
func (s *MyPlacements) HandleCallback(b *bot.Bot, u *echotron.Update) {
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
return
}
data := u.CallbackQuery.Data
switch {
case data == "prev":
if s.CurrentPage > 0 {
s.CurrentPage--
}
s.Enter(b, bot.EditMessage)
case data == "next":
s.CurrentPage++
s.Enter(b, bot.EditMessage)
case data == "back":
if s.BackState != nil {
b.SetState(s.BackState, bot.EditMessage)
} else {
b.SetState(&MainMenu{}, bot.EditMessage)
}
case data == "page_info", data == "empty":
// Неактивные кнопки - ничего не делаем
return
case data == "statistics":
// TODO: реализовать экран статистики
b.Edit("📊 Статистика размещений\n\nЭта функция в разработке", bot.Keyboard(
bot.Row(bot.Button("« Назад", "back_to_placements")),
))
case data == "back_to_placements":
s.Enter(b, bot.EditMessage)
case strings.HasPrefix(data, "placement:"):
// Открытие деталей закупа
parts := strings.Split(data, ":")
if len(parts) == 2 {
placementID := parts[1]
// TODO: реализовать экран деталей закупа
b.Edit(fmt.Sprintf("📄 Детали закупа\n\nID: %s\n\nЭта функция в разработке", placementID), bot.Keyboard(
bot.Row(bot.Button("« Назад", "back_to_placements")),
))
}
case strings.HasPrefix(data, "workspace:"):
// Позволяет переключаться между workspace'ами в будущем
parts := strings.Split(data, ":")
if len(parts) == 2 {
s.WorkspaceID = parts[1]
s.CurrentPage = 0
s.Enter(b, bot.EditMessage)
}
default:
s.Enter(b, bot.NewMessage)
}
return
}
func (s *MyPlacements) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
func (s *MyPlacements) Handle(_ *bot.Bot, _ *echotron.Update) { return }