Files
tgex-backend/telegram_bot/screens/purchase_optional_details.go
Artem Tsyrulnikov 3ca62b6f5c закуп
2026-01-03 16:56:00 +03:00

400 lines
11 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 (
"fmt"
"strconv"
"strings"
"time"
"example.com/m/bot"
"github.com/NicoNex/echotron/v3"
)
type PurchaseOptionalDetails struct {
WorkspaceID string
ProjectID string
ProjectTitle string
CreativeID string
CreativeTitle string
Channels []PurchaseChannelInput
PlacementDateTime *time.Time
PaymentDate *time.Time
CostType string
CostValue *float64
CostBeforeBargain *float64
PurchaseType string
Format string
Comment string
InputMode string
BackState bot.State
}
func (s *PurchaseOptionalDetails) Enter(b *bot.Bot, mode bot.RenderMode) {
if s.InputMode != "" {
s.renderInputPrompt(b, mode)
return
}
text := "<i>… Создание Дополнительно</i>\n\n"
text += "<b>Страница создания закупа и необязательные составляющие</b>"
text += "\n\n"
text += s.formatOptionalSummary()
var rows [][]echotron.InlineKeyboardButton
rows = append(rows, bot.Row(
s.placementButton(),
s.costButton(),
))
rows = append(rows, bot.Row(
s.paymentButton(),
s.typeButton(),
))
rows = append(rows, bot.Row(
s.costBeforeButton(),
s.formatButton(),
))
rows = append(rows, bot.Row(
s.commentButton(),
))
if row := s.costBeforeDeleteRow(); row != nil {
rows = append(rows, row)
}
if row := s.commentDeleteRow(); row != nil {
rows = append(rows, row)
}
rows = append(rows, bot.Row(bot.Button("Назад", "back"), bot.Button("Далее", "next")))
keyboard := bot.Keyboard(rows...)
b.Render(text, keyboard, mode)
}
func (s *PurchaseOptionalDetails) HandleCallback(b *bot.Bot, u *echotron.Update) {
if u.CallbackQuery == nil || u.CallbackQuery.Data == "" {
return
}
switch u.CallbackQuery.Data {
case "opt_datetime":
b.SetState(NewDateTimePicker(DateTimePickerConfig{
Title: "Дата и время размещения",
Key: "placement_datetime",
IncludeTime: true,
Selected: s.PlacementDateTime,
BackState: s,
}), bot.EditMessage)
case "opt_payment_date":
b.SetState(NewDateTimePicker(DateTimePickerConfig{
Title: "Дата оплаты",
Key: "payment_date",
IncludeTime: true,
Selected: s.PaymentDate,
BackState: s,
}), bot.EditMessage)
case "opt_cost":
s.InputMode = "cost_select"
s.Enter(b, bot.EditMessage)
case "opt_type":
s.InputMode = "type"
s.Enter(b, bot.EditMessage)
case "opt_format":
s.InputMode = "format_select"
s.Enter(b, bot.EditMessage)
case "opt_comment":
s.InputMode = "comment"
s.Enter(b, bot.EditMessage)
case "delete_comment":
s.Comment = ""
s.Enter(b, bot.EditMessage)
case "format_custom":
s.InputMode = "format_custom"
s.Enter(b, bot.EditMessage)
case "cost_type_toggle":
if s.costTypeLabel() == "СРМ" {
s.CostType = "fixed"
} else {
s.CostType = "cpm"
}
s.Enter(b, bot.EditMessage)
case "cost_value":
s.InputMode = "cost_value"
s.Enter(b, bot.EditMessage)
case "cost_before":
s.InputMode = "cost_before"
s.Enter(b, bot.EditMessage)
case "delete_cost_before":
s.CostBeforeBargain = nil
s.Enter(b, bot.EditMessage)
case "back_to_optional":
s.InputMode = ""
s.Enter(b, bot.EditMessage)
case "back":
b.SetState(&SelectChannelsForPurchase{
WorkspaceID: s.WorkspaceID,
ProjectID: s.ProjectID,
ProjectTitle: s.ProjectTitle,
CreativeID: s.CreativeID,
CreativeTitle: s.CreativeTitle,
Channels: s.Channels,
BackState: s.BackState,
}, bot.EditMessage)
default:
if strings.HasPrefix(u.CallbackQuery.Data, "format:") {
value := strings.TrimPrefix(u.CallbackQuery.Data, "format:")
s.Format = value
s.InputMode = ""
s.Enter(b, bot.EditMessage)
return
}
s.Enter(b, bot.EditMessage)
}
}
func (s *PurchaseOptionalDetails) HandleMessage(b *bot.Bot, u *echotron.Update) {
if s.InputMode == "" || u.Message == nil || u.Message.Text == "" {
return
}
text := strings.TrimSpace(u.Message.Text)
if text == "" {
return
}
switch s.InputMode {
case "type":
s.PurchaseType = text
case "format_custom":
s.Format = text
case "comment":
s.Comment = text
case "cost_value":
value, err := strconv.ParseFloat(strings.ReplaceAll(text, ",", "."), 64)
if err != nil {
s.renderInputPrompt(b, bot.EditMessage)
return
}
s.CostValue = &value
case "cost_before":
value, err := strconv.ParseFloat(strings.ReplaceAll(text, ",", "."), 64)
if err != nil {
s.renderInputPrompt(b, bot.EditMessage)
return
}
s.CostBeforeBargain = &value
}
s.InputMode = ""
s.Enter(b, bot.NewMessage)
}
func (s *PurchaseOptionalDetails) Handle(_ *bot.Bot, _ *echotron.Update) { return }
func (s *PurchaseOptionalDetails) SetDateTimeSelection(key string, value time.Time) {
switch key {
case "placement_datetime":
s.PlacementDateTime = &value
case "payment_date":
s.PaymentDate = &value
}
}
func (s *PurchaseOptionalDetails) formatOptionalSummary() string {
lines := []string{
fmt.Sprintf("Дата размещения: <b>%s</b>", s.formatDateTime(s.PlacementDateTime)),
fmt.Sprintf("Дата оплаты: <b>%s</b>", s.formatDateTime(s.PaymentDate)),
fmt.Sprintf("Стоимость: <b>%s</b>", s.formatCostValue()),
fmt.Sprintf("Стоимость до торга: <b>%s</b>", s.formatCostBefore()),
fmt.Sprintf("Тип закупа: <b>%s</b>", s.formatText(s.PurchaseType)),
fmt.Sprintf("Формат: <b>%s</b>", s.formatText(s.Format)),
fmt.Sprintf("Комментарий: <b>%s</b>", s.formatText(s.Comment)),
}
return strings.Join(lines, "\n")
}
func (s *PurchaseOptionalDetails) formatDateTime(value *time.Time) string {
if value == nil {
return "—"
}
local := value.In(mskLocation)
return fmt.Sprintf("%s %02d %s %s", weekdayName(local.Weekday()), local.Day(), monthShort(local.Month()), local.Format("15:04"))
}
func (s *PurchaseOptionalDetails) formatText(value string) string {
if value == "" {
return "—"
}
return value
}
func (s *PurchaseOptionalDetails) formatCostValue() string {
if s.CostValue == nil {
return "—"
}
return fmt.Sprintf("%s %.0f₽", s.costTypeLabel(), *s.CostValue)
}
func (s *PurchaseOptionalDetails) formatCostBefore() string {
if s.CostBeforeBargain == nil {
return "—"
}
return fmt.Sprintf("%.0f₽", *s.CostBeforeBargain)
}
func (s *PurchaseOptionalDetails) costTypeLabel() string {
if s.CostType == "cpm" {
return "СРМ"
}
return "Фикс"
}
func (s *PurchaseOptionalDetails) placementButton() echotron.InlineKeyboardButton {
if s.PlacementDateTime == nil {
return bot.Button(" Дата размещения", "opt_datetime")
}
return bot.Button("✎ Дата размещения", "opt_datetime")
}
func (s *PurchaseOptionalDetails) paymentButton() echotron.InlineKeyboardButton {
if s.PaymentDate == nil {
return bot.Button(" Дата оплаты", "opt_payment_date")
}
return bot.Button("✎ Дата оплаты", "opt_payment_date")
}
func (s *PurchaseOptionalDetails) costButton() echotron.InlineKeyboardButton {
if s.CostValue == nil {
return bot.Button(" Стоимость", "opt_cost")
}
return bot.Button("✎ Стоимость", "opt_cost")
}
func (s *PurchaseOptionalDetails) costBeforeButton() echotron.InlineKeyboardButton {
if s.CostBeforeBargain == nil {
return bot.Button(" До торга", "cost_before")
}
return bot.Button("✎ До торга", "cost_before")
}
func (s *PurchaseOptionalDetails) typeButton() echotron.InlineKeyboardButton {
if s.PurchaseType == "" {
return bot.Button(" Тип", "opt_type")
}
return bot.Button("✎ Тип", "opt_type")
}
func (s *PurchaseOptionalDetails) formatButton() echotron.InlineKeyboardButton {
if s.Format == "" {
return bot.Button(" Формат", "opt_format")
}
return bot.Button("✎ Формат", "opt_format")
}
func (s *PurchaseOptionalDetails) commentButton() echotron.InlineKeyboardButton {
if s.Comment == "" {
return bot.Button(" Комментарий", "opt_comment")
}
return bot.Button("✎ Комментарий", "opt_comment")
}
func (s *PurchaseOptionalDetails) commentDeleteRow() []echotron.InlineKeyboardButton {
if s.Comment == "" {
return nil
}
return bot.Row(bot.Button("⌫ Удалить комментарий", "delete_comment"))
}
func (s *PurchaseOptionalDetails) costBeforeDeleteRow() []echotron.InlineKeyboardButton {
if s.CostBeforeBargain == nil {
return nil
}
return bot.Row(bot.Button("⌫ Удалить до торга", "delete_cost_before"))
}
func (s *PurchaseOptionalDetails) renderInputPrompt(b *bot.Bot, mode bot.RenderMode) {
text := "<i>… Создание Дополнительно</i>\n\n"
switch s.InputMode {
case "type":
text += "<b>Введите тип закупа</b>\n\nНапример: <code>вп</code> или <code>стандарт</code>"
case "cost_select":
text = "<i>Страница ввода стоимости</i>\n\n"
text += "Ставка СРМ / Фикс цена (по умолчанию фикс)\n\n"
keyboard := bot.Keyboard(
bot.Row(bot.Button(fmt.Sprintf("Тип: %s", s.costTypeLabel()), "cost_type_toggle")),
bot.Row(bot.Button(fmt.Sprintf("Стоимость: %s", s.formatCostValue()), "cost_value")),
bot.Row(bot.Button("Назад", "back_to_optional")),
)
if mode == bot.EditMessage {
b.Edit(text, keyboard)
} else {
b.SendNew(text, keyboard)
}
return
case "cost_value":
text += "<b>Ввод стоимости (СРМ / фикс)</b>\n\nНапример: <code>15000</code>"
case "cost_before":
text += "<b>Ввод стоимости до торга</b>\n\nНапример: <code>20000</code>"
case "format_select":
text = "<i>Страница ввода формата размещения</i>\n\n"
text += "Выберите формат\n\n"
keyboard := bot.Keyboard(
bot.Row(
bot.Button("1 / 24", "format:1 / 24"),
bot.Button("1/48", "format:1/48"),
bot.Button("1 / 72", "format:1 / 72"),
),
bot.Row(
bot.Button("1 / (7 дней)", "format:1 / (7 дней)"),
bot.Button("1 / (30 дней)", "format:1 / (30 дней)"),
bot.Button("1 / (без удаления)", "format:1 / (без удаления)"),
),
bot.Row(
bot.Button("2 / 24", "format:2 / 24"),
bot.Button("2/48", "format:2/48"),
bot.Button("2/72", "format:2/72"),
),
bot.Row(
bot.Button("Назад", "back_to_optional"),
bot.Button("Свой формат", "format_custom"),
),
)
if mode == bot.EditMessage {
b.Edit(text, keyboard)
} else {
b.SendNew(text, keyboard)
}
return
case "format_custom":
text += "<b>Введите формат</b>\n\nНапример: <code>пост</code>"
case "comment":
text += "<b>Введите комментарий</b>"
default:
s.InputMode = ""
s.Enter(b, mode)
return
}
keyboard := bot.Keyboard(
bot.Row(bot.Button("← Назад", "back_to_optional")),
)
if mode == bot.EditMessage {
b.Edit(text, keyboard)
} else {
b.SendNew(text, keyboard)
}
}