выбор времени
This commit is contained in:
@@ -3,6 +3,7 @@ package screens
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"example.com/m/adapter/backend"
|
||||
@@ -18,14 +19,19 @@ type PurchaseChannelInput struct {
|
||||
}
|
||||
|
||||
type SelectChannelsForPurchase struct {
|
||||
WorkspaceID string
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
CreativeID string
|
||||
Channels []PurchaseChannelInput
|
||||
BackState bot.State
|
||||
WorkspaceID string
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
Channels []PurchaseChannelInput
|
||||
CurrentPage int
|
||||
InvalidUsernames []string
|
||||
BackState bot.State
|
||||
}
|
||||
|
||||
var channelUsernameRe = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9_]{4,31}$`)
|
||||
|
||||
func (s *SelectChannelsForPurchase) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
s.renderChannelSelection(b, mode)
|
||||
}
|
||||
@@ -43,7 +49,7 @@ func (s *SelectChannelsForPurchase) renderChannelSelection(b *bot.Bot, mode bot.
|
||||
Отправьте username канала (без @)
|
||||
Например: <code>channel_name</code>
|
||||
|
||||
После добавления всех каналов нажмите <b>Создать закуп</b>`
|
||||
После добавления всех каналов нажмите <b>Далее</b>`
|
||||
} else {
|
||||
text += fmt.Sprintf("Добавлено каналов: <b>%d</b>\n\n", len(s.Channels))
|
||||
|
||||
@@ -54,19 +60,83 @@ func (s *SelectChannelsForPurchase) renderChannelSelection(b *bot.Bot, mode bot.
|
||||
}
|
||||
text += channelText + "\n"
|
||||
}
|
||||
text += "\n"
|
||||
|
||||
const channelsPerPage = 6
|
||||
start := s.CurrentPage * channelsPerPage
|
||||
if start >= len(s.Channels) {
|
||||
s.CurrentPage = 0
|
||||
start = 0
|
||||
}
|
||||
end := start + channelsPerPage
|
||||
if end > len(s.Channels) {
|
||||
end = len(s.Channels)
|
||||
}
|
||||
|
||||
var slots []echotron.InlineKeyboardButton
|
||||
for i := start; i < end; i++ {
|
||||
ch := s.Channels[i]
|
||||
buttonText := fmt.Sprintf("@%s", ch.Username)
|
||||
if ch.PlannedCost != nil {
|
||||
buttonText = fmt.Sprintf("@%s — %.0f₽", ch.Username, *ch.PlannedCost)
|
||||
}
|
||||
slots = append(slots, bot.Button(buttonText, fmt.Sprintf("remove_channel:%d", i)))
|
||||
}
|
||||
|
||||
if len(s.Channels) > channelsPerPage {
|
||||
for len(slots) < channelsPerPage {
|
||||
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)
|
||||
}
|
||||
|
||||
pages := (len(s.Channels) + channelsPerPage - 1) / channelsPerPage
|
||||
if pages > 1 {
|
||||
var navRow []echotron.InlineKeyboardButton
|
||||
if s.CurrentPage > 0 {
|
||||
navRow = append(navRow, bot.Button("←", "prev_page"))
|
||||
} else {
|
||||
navRow = append(navRow, bot.Button("·", "empty"))
|
||||
}
|
||||
navRow = append(navRow, bot.Button(fmt.Sprintf("• %d/%d •", s.CurrentPage+1, pages), "page_info"))
|
||||
if s.CurrentPage+1 < pages {
|
||||
navRow = append(navRow, bot.Button("→", "next_page"))
|
||||
} else {
|
||||
navRow = append(navRow, bot.Button("·", "empty"))
|
||||
}
|
||||
buttons = append(buttons, navRow)
|
||||
}
|
||||
|
||||
text += "\nДобавьте еще каналы или создайте закуп"
|
||||
|
||||
// Кнопка создания закупа (доступна только если есть каналы)
|
||||
buttons = append(buttons, bot.Row(
|
||||
bot.Button("● Создать закуп", "create"),
|
||||
))
|
||||
}
|
||||
|
||||
if len(s.InvalidUsernames) > 0 {
|
||||
text += fmt.Sprintf("\n\n⚠️ Пропущены: %s", strings.Join(s.InvalidUsernames, ", "))
|
||||
s.InvalidUsernames = nil
|
||||
}
|
||||
|
||||
// Кнопки навигации
|
||||
buttons = append(buttons, bot.Row(
|
||||
bot.Button("← Назад", "back"),
|
||||
))
|
||||
if len(s.Channels) > 0 {
|
||||
buttons = append(buttons, bot.Row(
|
||||
bot.Button("← Назад", "back"),
|
||||
bot.Button("→ Далее", "next"),
|
||||
))
|
||||
} else {
|
||||
buttons = append(buttons, bot.Row(
|
||||
bot.Button("← Назад", "back"),
|
||||
))
|
||||
}
|
||||
|
||||
keyboard := bot.Keyboard(buttons...)
|
||||
b.Render(text, keyboard, mode)
|
||||
@@ -97,13 +167,16 @@ func (s *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Updat
|
||||
case "back":
|
||||
// Возвращаемся к выбору креатива
|
||||
b.SetState(&AddPurchase{
|
||||
WorkspaceID: s.WorkspaceID,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
BackState: s.BackState,
|
||||
WorkspaceID: s.WorkspaceID,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
ActivePicker: "",
|
||||
BackState: s.BackState,
|
||||
}, bot.EditMessage)
|
||||
|
||||
case "create":
|
||||
case "next":
|
||||
if len(s.Channels) == 0 {
|
||||
b.Edit("❌ Добавьте хотя бы один канал", bot.Keyboard(
|
||||
bot.Row(bot.Button("← Назад", "back_to_select")),
|
||||
@@ -111,20 +184,47 @@ func (s *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Updat
|
||||
return
|
||||
}
|
||||
|
||||
jwt, err := s.ensureJWT(b)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to get JWT")
|
||||
b.Edit("❌ Ошибка авторизации", bot.Keyboard())
|
||||
return
|
||||
}
|
||||
|
||||
// Создаем закуп
|
||||
s.createPurchase(b, jwt)
|
||||
b.SetState(&PurchaseOptionalDetails{
|
||||
WorkspaceID: s.WorkspaceID,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: s.Channels,
|
||||
BackState: s.BackState,
|
||||
}, bot.EditMessage)
|
||||
|
||||
case "back_to_select":
|
||||
s.Enter(b, bot.EditMessage)
|
||||
|
||||
case "prev_page":
|
||||
if s.CurrentPage > 0 {
|
||||
s.CurrentPage--
|
||||
}
|
||||
s.Enter(b, bot.EditMessage)
|
||||
|
||||
case "next_page":
|
||||
s.CurrentPage++
|
||||
s.Enter(b, bot.EditMessage)
|
||||
|
||||
default:
|
||||
if len(data) > 15 && data[:15] == "remove_channel:" {
|
||||
var index int
|
||||
if _, err := fmt.Sscanf(data[15:], "%d", &index); err == nil {
|
||||
if index >= 0 && index < len(s.Channels) {
|
||||
s.Channels = append(s.Channels[:index], s.Channels[index+1:]...)
|
||||
if s.CurrentPage > 0 {
|
||||
channelsPerPage := 6
|
||||
pages := (len(s.Channels) + channelsPerPage - 1) / channelsPerPage
|
||||
if s.CurrentPage >= pages {
|
||||
s.CurrentPage = pages - 1
|
||||
}
|
||||
}
|
||||
s.Enter(b, bot.EditMessage)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
s.Enter(b, bot.NewMessage)
|
||||
}
|
||||
}
|
||||
@@ -182,27 +282,55 @@ func (s *SelectChannelsForPurchase) HandleMessage(b *bot.Bot, u *echotron.Update
|
||||
return
|
||||
}
|
||||
|
||||
username := strings.TrimSpace(u.Message.Text)
|
||||
|
||||
// Убираем @ если пользователь его добавил
|
||||
username = strings.TrimPrefix(username, "@")
|
||||
|
||||
if username == "" {
|
||||
raw := strings.TrimSpace(u.Message.Text)
|
||||
if raw == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Проверяем, не добавлен ли уже этот канал
|
||||
for _, ch := range s.Channels {
|
||||
if ch.Username == username {
|
||||
b.SendNew(fmt.Sprintf("⚠️ Канал @%s уже добавлен в список", username), bot.Keyboard())
|
||||
tokens := strings.FieldsFunc(raw, func(r rune) bool {
|
||||
return r == ' ' || r == '\n' || r == '\t' || r == ',' || r == ';'
|
||||
})
|
||||
|
||||
added := 0
|
||||
var invalid []string
|
||||
for _, token := range tokens {
|
||||
username := strings.TrimSpace(token)
|
||||
username = strings.TrimPrefix(username, "@")
|
||||
username = strings.Trim(username, "@")
|
||||
username = strings.Trim(username, ",;")
|
||||
|
||||
if username == "" {
|
||||
continue
|
||||
}
|
||||
if !channelUsernameRe.MatchString(username) {
|
||||
invalid = append(invalid, username)
|
||||
continue
|
||||
}
|
||||
|
||||
exists := false
|
||||
for _, ch := range s.Channels {
|
||||
if ch.Username == username {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if exists {
|
||||
continue
|
||||
}
|
||||
|
||||
s.Channels = append(s.Channels, PurchaseChannelInput{
|
||||
Username: username,
|
||||
})
|
||||
added++
|
||||
}
|
||||
|
||||
if added == 0 {
|
||||
if len(invalid) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Добавляем канал в список
|
||||
s.Channels = append(s.Channels, PurchaseChannelInput{
|
||||
Username: username,
|
||||
})
|
||||
s.InvalidUsernames = invalid
|
||||
|
||||
// Обновляем экран
|
||||
s.Enter(b, bot.NewMessage)
|
||||
|
||||
Reference in New Issue
Block a user