Files
tgex-backend/telegram_bot/screens/creatives.go
2026-01-03 11:29:44 +03:00

227 lines
5.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 creativesPerPage = 6
type Creatives struct {
CurrentPage int
ProjectID string
ProjectTitle string
WorkspaceID string
BackState bot.State
}
func (s *Creatives) Enter(b *bot.Bot, mode bot.RenderMode) {
jwt, err := s.ensureJWT(b)
if err != nil {
log.Error().Err(err).Msg("Failed to get JWT")
b.SendNew("❌ Ошибка авторизации. Попробуйте /start login", bot.Keyboard())
return
}
s.renderCreatives(b, mode, jwt)
}
func (s *Creatives) renderCreatives(b *bot.Bot, mode bot.RenderMode, jwt string) {
page, err := b.Backend.GetCreatives(
context.Background(),
jwt,
s.WorkspaceID,
&s.ProjectID,
false, // includeArchived
s.CurrentPage+1,
creativesPerPage,
)
if err != nil {
log.Error().Err(err).Msg("Failed to get creatives")
b.SendNew("❌ Не удалось загрузить креативы", bot.Keyboard())
return
}
var text string
var buttons [][]echotron.InlineKeyboardButton
// Навигация: 2 последних уровня
navPath := "… Креативы"
if s.ProjectTitle != "" {
navPath = fmt.Sprintf("… %s Креативы", s.ProjectTitle)
}
if len(page.Items) == 0 {
text = fmt.Sprintf(`<i>%s</i>
<b>Креативы</b>
У вас пока нет креативов
<i>Креатив — это рекламный материал:</i>
‣ Текст объявления
‣ Медиа контент (фото, видео)
‣ Шаблон для автопостинга
‣ Варианты для A/B тестирования
Начните с добавления первого креатива`, navPath)
} else {
pageInfo := ""
if page.Pages > 1 {
pageInfo = fmt.Sprintf(" <i>(стр. %d/%d)</i>", page.Page, page.Pages)
}
text = fmt.Sprintf(`<i>%s</i>
<b>Креативы</b>%s
Управление рекламными материалами
Выберите креатив`,
navPath,
pageInfo)
}
// Создаем слоты для креативов
var slots []echotron.InlineKeyboardButton
for _, creative := range page.Items {
slots = append(slots, bot.Button(creative.Name, fmt.Sprintf("creative:%s", creative.ID)))
}
// Если страниц >= 2, заполняем до 6 слотов пустыми кнопками
if page.Pages >= 2 {
for len(slots) < creativesPerPage {
slots = append(slots, bot.Button(" ", "empty"))
}
}
// Разбиваем слоты по 2 в ряду
for i := 0; i < len(slots); i += 2 {
var row []echotron.InlineKeyboardButton
row = append(row, slots[i])
// Второй слот в ряду (если есть)
if i+1 < len(slots) {
row = append(row, slots[i+1])
} 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"))
}
// Кнопка "Добавить креатив" всегда в центре
navRow = append(navRow, bot.Button(" Добавить", "add_creative"))
// Правая стрелка (только если есть следующая страница)
if s.CurrentPage+1 < page.Pages {
navRow = append(navRow, bot.Button("→", "next"))
}
buttons = append(buttons, navRow)
// Нижний ряд: Назад + Архив
buttons = append(buttons, bot.Row(
bot.Button("← Назад", "back"),
bot.Button("≡ Архив", "archive"),
))
keyboard := bot.Keyboard(buttons...)
b.Render(text, keyboard, mode)
}
func (s *Creatives) 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 *Creatives) 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)
}
case data == "page_info", data == "empty":
// Неактивные кнопки - ничего не делаем
return
case data == "archive":
// TODO: реализовать экран архива креативов
b.Edit("📦 Архив креативов\n\nЭта функция в разработке", bot.Keyboard(
bot.Row(bot.Button("← Назад", "back")),
))
case data == "add_creative":
b.SetState(&AddCreative{
ProjectID: s.ProjectID,
WorkspaceID: s.WorkspaceID,
BackState: s,
}, bot.EditMessage)
case strings.HasPrefix(data, "creative:"):
// Открытие деталей креатива
parts := strings.Split(data, ":")
if len(parts) == 2 {
creativeID := parts[1]
b.SetState(&CreativeDetails{
CreativeID: creativeID,
ProjectID: s.ProjectID,
WorkspaceID: s.WorkspaceID,
BackState: s,
}, bot.NewMessage)
}
default:
s.Enter(b, bot.NewMessage)
}
return
}
func (s *Creatives) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return }
func (s *Creatives) Handle(_ *bot.Bot, _ *echotron.Update) { return }