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 := "… › Создание › Дополнительно\n\n" text += "Страница создания закупа и необязательные составляющие" 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("Дата размещения: %s", s.formatDateTime(s.PlacementDateTime)), fmt.Sprintf("Дата оплаты: %s", s.formatDateTime(s.PaymentDate)), fmt.Sprintf("Стоимость: %s", s.formatCostValue()), fmt.Sprintf("Стоимость до торга: %s", s.formatCostBefore()), fmt.Sprintf("Тип закупа: %s", s.formatText(s.PurchaseType)), fmt.Sprintf("Формат: %s", s.formatText(s.Format)), fmt.Sprintf("Комментарий: %s", 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 := "… › Создание › Дополнительно\n\n" switch s.InputMode { case "type": text += "Введите тип закупа\n\nНапример: вп или стандарт" case "cost_select": text = "Страница ввода стоимости\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 += "Ввод стоимости (СРМ / фикс)\n\nНапример: 15000" case "cost_before": text += "Ввод стоимости до торга\n\nНапример: 20000" case "format_select": text = "Страница ввода формата размещения\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 += "Введите формат\n\nНапример: пост" case "comment": text += "Введите комментарий" 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) } }