fix
This commit is contained in:
@@ -15,26 +15,28 @@ const addPurchaseCreativesPerPage = 5
|
||||
const addPurchaseProjectsPerPage = 6
|
||||
|
||||
type AddPurchase struct {
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectTelegramID int64
|
||||
ProjectUsername string
|
||||
ProjectStatus string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
ProjectPage int
|
||||
CreativePage int
|
||||
ActivePicker string // "" | "project_list" | "creative_list"
|
||||
BackState bot.State
|
||||
lastProjects map[string]projectInfo
|
||||
lastCreatives map[string]creativeInfo
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectTelegramID int64
|
||||
ProjectUsername string
|
||||
ProjectStatus string
|
||||
ProjectDefaultLinkType string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
ProjectPage int
|
||||
CreativePage int
|
||||
ActivePicker string // "" | "project_list" | "creative_list"
|
||||
BackState bot.State
|
||||
lastProjects map[string]projectInfo
|
||||
lastCreatives map[string]creativeInfo
|
||||
}
|
||||
|
||||
type projectInfo struct {
|
||||
Title string
|
||||
TelegramID int64
|
||||
Username string
|
||||
Status string
|
||||
Title string
|
||||
TelegramID int64
|
||||
Username string
|
||||
Status string
|
||||
DefaultInviteType string
|
||||
}
|
||||
|
||||
type creativeInfo struct {
|
||||
@@ -85,10 +87,11 @@ func (s *AddPurchase) renderSelection(b *bot.Bot, mode bot.RenderMode) {
|
||||
username = *project.Username
|
||||
}
|
||||
s.lastProjects[project.ID] = projectInfo{
|
||||
Title: project.Title,
|
||||
TelegramID: project.TelegramID,
|
||||
Username: username,
|
||||
Status: project.Status,
|
||||
Title: project.Title,
|
||||
TelegramID: project.TelegramID,
|
||||
Username: username,
|
||||
Status: project.Status,
|
||||
DefaultInviteType: project.PurchaseInviteTypeDefault,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,17 +266,18 @@ func (s *AddPurchase) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
}
|
||||
if s.ProjectID != "" && s.CreativeID != "" {
|
||||
b.SetState(&SelectChannelsForPurchase{
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectTelegramID: s.ProjectTelegramID,
|
||||
ProjectUsername: s.ProjectUsername,
|
||||
ProjectStatus: s.ProjectStatus,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: []PurchaseChannelInput{},
|
||||
Duplicates: []string{},
|
||||
ParsingErrors: []ParseError{},
|
||||
BackState: s.BackState,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectTelegramID: s.ProjectTelegramID,
|
||||
ProjectUsername: s.ProjectUsername,
|
||||
ProjectStatus: s.ProjectStatus,
|
||||
ProjectDefaultLinkType: s.ProjectDefaultLinkType,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: []PurchaseChannelInput{},
|
||||
Duplicates: []string{},
|
||||
ParsingErrors: []ParseError{},
|
||||
BackState: s.BackState,
|
||||
}, bot.EditMessage)
|
||||
}
|
||||
|
||||
@@ -286,6 +290,7 @@ func (s *AddPurchase) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
s.ProjectTelegramID = info.TelegramID
|
||||
s.ProjectUsername = info.Username
|
||||
s.ProjectStatus = info.Status
|
||||
s.ProjectDefaultLinkType = info.DefaultInviteType
|
||||
}
|
||||
}
|
||||
s.ProjectID = projectID
|
||||
|
||||
@@ -33,8 +33,9 @@ func (s *Login) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
kb := Keyboard(Row(URLButton("Войти на сайт", loginURL)))
|
||||
|
||||
res, err := b.SendMessage(fmt.Sprintf(msgHelloLogin, usernameDisplay), b.ChatID, &echotron.MessageOptions{
|
||||
ReplyMarkup: kb,
|
||||
ParseMode: echotron.HTML,
|
||||
ReplyMarkup: kb,
|
||||
ParseMode: echotron.HTML,
|
||||
LinkPreviewOptions: echotron.LinkPreviewOptions{IsDisabled: true},
|
||||
})
|
||||
if err != nil {
|
||||
log.Err(err).Msg("SendMessage error")
|
||||
|
||||
@@ -14,7 +14,7 @@ type MainMenu struct{}
|
||||
const msgAbout = `
|
||||
<i>Главное меню</i>
|
||||
|
||||
<blockquote>
|
||||
<blockquote expandable>
|
||||
За что отвечает каждая кнопка в этом окне:
|
||||
|
||||
<b>• Рабочее пространство</b> — Одно можно использовать для своих проектов, а другое, например, для проектов, где вы являетесь закупщиком.
|
||||
|
||||
@@ -29,8 +29,7 @@ func (s *Placements) Enter(b *bot.Bot, mode bot.RenderMode) {
|
||||
var rows [][]echotron.InlineKeyboardButton
|
||||
|
||||
rows = append(rows, Row(Button("+ Создать размещение", "add_purchase")))
|
||||
rows = append(rows, Row(Button("Размещения", "placements_list")))
|
||||
rows = append(rows, Row(URLButton("План закупов", fmt.Sprintf("https://app.smart-post.ru/dashboard/%s/purchase-plans/%s", b.Session.WorkspaceID, s.ProjectID))))
|
||||
rows = append(rows, Row(Button("Список размещений", "placements_list"), URLButton("План закупов", fmt.Sprintf("https://app.smart-post.ru/dashboard/%s/purchase-plans/%s", b.Session.WorkspaceID, s.ProjectID))))
|
||||
rows = append(rows, Row(Button("← Назад", "back")))
|
||||
|
||||
keyboard := Keyboard(rows...)
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
type PurchaseOptionalDetails struct {
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectDefaultLinkType string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
Channels []PurchaseChannelInput
|
||||
@@ -254,15 +255,16 @@ func (s *PurchaseOptionalDetails) HandleCallback(b *bot.Bot, u *echotron.Update)
|
||||
case "back":
|
||||
// Сохраняем текущее состояние перед возвратом назад
|
||||
b.SetState(&SelectChannelsForPurchase{
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: s.Channels,
|
||||
Duplicates: []string{},
|
||||
ParsingErrors: []ParseError{},
|
||||
OptionalDetailsState: s, // Сохраняем текущее состояние
|
||||
BackState: s.BackState,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectDefaultLinkType: s.ProjectDefaultLinkType,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: s.Channels,
|
||||
Duplicates: []string{},
|
||||
ParsingErrors: []ParseError{},
|
||||
OptionalDetailsState: s, // Сохраняем текущее состояние
|
||||
BackState: s.BackState,
|
||||
}, bot.EditMessage)
|
||||
case "next":
|
||||
jwt := b.Session.JWT
|
||||
@@ -314,11 +316,6 @@ func (s *PurchaseOptionalDetails) HandleCallback(b *bot.Bot, u *echotron.Update)
|
||||
}
|
||||
if strings.HasPrefix(u.CallbackQuery.Data, "type:") {
|
||||
value := strings.TrimPrefix(u.CallbackQuery.Data, "type:")
|
||||
if value == "custom" {
|
||||
s.InputMode = "type_custom"
|
||||
s.Enter(b, bot.EditMessage)
|
||||
return
|
||||
}
|
||||
// Нормализуем значение для БД
|
||||
switch value {
|
||||
case "self_promo":
|
||||
@@ -372,12 +369,6 @@ func (s *PurchaseOptionalDetails) HandleMessage(b *bot.Bot, u *echotron.Update)
|
||||
}
|
||||
|
||||
switch s.InputMode {
|
||||
case "type_custom":
|
||||
if s.CurrentChannel != "" {
|
||||
s.PurchaseTypeByChannel[s.CurrentChannel] = text
|
||||
} else {
|
||||
s.PurchaseType = text
|
||||
}
|
||||
case "format_custom":
|
||||
s.setFormatValue(text)
|
||||
case "comment":
|
||||
@@ -639,11 +630,7 @@ func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderM
|
||||
case "type":
|
||||
text += "<b>Выберите тип закупа</b>"
|
||||
keyboard := Keyboard(
|
||||
Row(
|
||||
Button("Самопиар", "type:self_promo"),
|
||||
Button("Стандарт", "type:standard"),
|
||||
),
|
||||
Row(Button("Другое", "type:custom")),
|
||||
Row(Button("Самопиар", "type:self_promo"), Button("Стандарт", "type:standard")),
|
||||
Row(Button("← Назад", "back_to_optional")),
|
||||
)
|
||||
if mode == bot.EditMessage {
|
||||
@@ -652,8 +639,6 @@ func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderM
|
||||
b.SendNew(text, keyboard)
|
||||
}
|
||||
return
|
||||
case "type_custom":
|
||||
text += "<b>Введите тип закупа</b>\n\nНапример: <code>вп</code>"
|
||||
case "cost_value":
|
||||
text += "<b>Ввод стоимости</b>\n\nНапример: <code>15000</code>"
|
||||
if s.CurrentChannel != "" {
|
||||
@@ -1639,6 +1624,12 @@ func (s *PurchaseOptionalDetails) ensureDefaults() {
|
||||
if s.InviteLinkTypeMode == "" {
|
||||
s.InviteLinkTypeMode = "common"
|
||||
}
|
||||
if s.InviteLinkType == "" {
|
||||
s.InviteLinkType = s.ProjectDefaultLinkType
|
||||
if s.InviteLinkType == "" {
|
||||
s.InviteLinkType = "approval"
|
||||
}
|
||||
}
|
||||
if s.PlacementByChannel == nil {
|
||||
s.PlacementByChannel = make(map[string]*time.Time)
|
||||
}
|
||||
@@ -2255,22 +2246,22 @@ func (s *PurchaseOptionalDetails) buildChannelDetails(channelKey string, placeme
|
||||
// Placement date
|
||||
if s.PlacementMode == "per_channel" && len(s.Channels) > 1 {
|
||||
if value, ok := s.PlacementByChannel[channelKey]; ok && value != nil {
|
||||
formatted := value.Format(time.RFC3339)
|
||||
formatted := value.UTC().Format(time.RFC3339)
|
||||
details.PlacementAt = &formatted
|
||||
}
|
||||
} else if s.PlacementDateTime != nil {
|
||||
formatted := s.PlacementDateTime.Format(time.RFC3339)
|
||||
formatted := s.PlacementDateTime.UTC().Format(time.RFC3339)
|
||||
details.PlacementAt = &formatted
|
||||
}
|
||||
|
||||
// Payment date
|
||||
if s.PaymentDateMode == "per_channel" && len(s.Channels) > 1 {
|
||||
if value, ok := s.PaymentDateByChannel[channelKey]; ok && value != nil {
|
||||
formatted := value.Format(time.RFC3339)
|
||||
formatted := value.UTC().Format(time.RFC3339)
|
||||
details.PaymentAt = &formatted
|
||||
}
|
||||
} else if s.PaymentDate != nil {
|
||||
formatted := s.PaymentDate.Format(time.RFC3339)
|
||||
formatted := s.PaymentDate.UTC().Format(time.RFC3339)
|
||||
details.PaymentAt = &formatted
|
||||
}
|
||||
|
||||
|
||||
@@ -22,20 +22,21 @@ type PurchaseChannelInput struct {
|
||||
}
|
||||
|
||||
type SelectChannelsForPurchase struct {
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectTelegramID int64
|
||||
ProjectUsername string
|
||||
ProjectStatus string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
Channels []PurchaseChannelInput
|
||||
CurrentPage int
|
||||
InvalidUsernames []string
|
||||
Duplicates []string // дубликаты каналов
|
||||
ParsingErrors []ParseError // ошибки парсинга с предложениями
|
||||
OptionalDetailsState *PurchaseOptionalDetails // сохранённое состояние деталей
|
||||
BackState bot.State
|
||||
ProjectID string
|
||||
ProjectTitle string
|
||||
ProjectTelegramID int64
|
||||
ProjectUsername string
|
||||
ProjectStatus string
|
||||
ProjectDefaultLinkType string
|
||||
CreativeID string
|
||||
CreativeTitle string
|
||||
Channels []PurchaseChannelInput
|
||||
CurrentPage int
|
||||
InvalidUsernames []string
|
||||
Duplicates []string // дубликаты каналов
|
||||
ParsingErrors []ParseError // ошибки парсинга с предложениями
|
||||
OptionalDetailsState *PurchaseOptionalDetails // сохранённое состояние деталей
|
||||
BackState bot.State
|
||||
}
|
||||
|
||||
// ParseError представляет ошибку парсинга с предложением исправления
|
||||
@@ -225,12 +226,13 @@ func (s *SelectChannelsForPurchase) HandleCallback(b *bot.Bot, u *echotron.Updat
|
||||
optionalDetails.Channels = s.Channels
|
||||
} else {
|
||||
optionalDetails = &PurchaseOptionalDetails{
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: s.Channels,
|
||||
BackState: s,
|
||||
ProjectID: s.ProjectID,
|
||||
ProjectTitle: s.ProjectTitle,
|
||||
ProjectDefaultLinkType: s.ProjectDefaultLinkType,
|
||||
CreativeID: s.CreativeID,
|
||||
CreativeTitle: s.CreativeTitle,
|
||||
Channels: s.Channels,
|
||||
BackState: s,
|
||||
}
|
||||
}
|
||||
b.SetState(optionalDetails, bot.EditMessage)
|
||||
|
||||
@@ -130,7 +130,8 @@ func (s *DateTimePicker) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
s.view = pickerViewCalendar
|
||||
}
|
||||
case strings.HasPrefix(payload, "day:"):
|
||||
s.handleDaySelect(payload)
|
||||
s.handleDaySelect(payload, b)
|
||||
return // confirmSelection уже делает Enter
|
||||
case strings.HasPrefix(payload, "hour:"):
|
||||
s.handleHourSelect(payload)
|
||||
case strings.HasPrefix(payload, "min:"):
|
||||
@@ -140,6 +141,7 @@ func (s *DateTimePicker) HandleCallback(b *bot.Bot, u *echotron.Update) {
|
||||
return
|
||||
}
|
||||
|
||||
// Если не было подтверждения (для дат без времени), перерисовываем
|
||||
s.Enter(b, bot.EditMessage)
|
||||
}
|
||||
|
||||
@@ -365,7 +367,7 @@ func (s *DateTimePicker) displayDate() (time.Time, bool) {
|
||||
return s.selectedDate.In(MskLocation), true
|
||||
}
|
||||
|
||||
func (s *DateTimePicker) handleDaySelect(payload string) {
|
||||
func (s *DateTimePicker) handleDaySelect(payload string, b *bot.Bot) {
|
||||
datePart := strings.TrimPrefix(payload, "day:")
|
||||
parsed, err := time.ParseInLocation("2006-01-02", datePart, MskLocation)
|
||||
if err != nil {
|
||||
@@ -377,7 +379,8 @@ func (s *DateTimePicker) handleDaySelect(payload string) {
|
||||
s.selectedHour = nil
|
||||
s.selectedMinute = nil
|
||||
} else {
|
||||
s.view = pickerViewConfirm
|
||||
// Сразу подтверждаем выбор для даты без времени
|
||||
s.confirmSelection(b)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user