package screens import ( "context" "fmt" "strings" "example.com/m/bot" "example.com/m/ui" "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 уже создан в Bot.Update(), просто берем из сессии jwt := b.Session.JWT if jwt == "" { log.Error().Msg("JWT is empty in session") 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 := "… › Главное меню › Размещения\n\nМои размещения\n\n" var buttons [][]echotron.InlineKeyboardButton if len(page.Items) == 0 { text += `У вас пока нет размещений Размещения создаются автоматически при создании закупа` } else { text += fmt.Sprintf("Выберите размещение для деталей%s\n\n", ui.FormatPageInfo(s.CurrentPage, page.Pages)) // Размещения по 2 в ряду var slots []echotron.InlineKeyboardButton for _, placement := range page.Items { placementName := placement.CreativeName if placement.PlacementChannelTitle != nil && *placement.PlacementChannelTitle != "" { placementName = fmt.Sprintf("%s → %s", placement.CreativeName, *placement.PlacementChannelTitle) } slots = append(slots, bot.Button(placementName, fmt.Sprintf("placement:%s", placement.ID))) } if page.Pages >= 2 { for len(slots) < placementsPerPage { slots = append(slots, bot.Button(" ", "empty")) } } for i := 0; i < len(slots); i += 2 { row := []echotron.InlineKeyboardButton{slots[i]} if i+1 < len(slots) { row = append(row, slots[i+1]) } else { row = append(row, bot.Button(" ", "empty")) } buttons = append(buttons, row) } // Кнопки пагинации с номером страницы посередине (только если есть закупы) if navRow := ui.BuildNavigationRow(ui.PaginationConfig{ CurrentPage: s.CurrentPage, TotalPages: page.Pages, MiddleButtons: []echotron.InlineKeyboardButton{bot.Button(fmt.Sprintf("• %d/%d •", s.CurrentPage+1, page.Pages), "page_info")}, }); navRow != nil { 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) 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 }