package screens import ( "context" "fmt" "strings" "time" "github.com/NicoNex/echotron/v3" "github.com/TelegramExchange/tgex-backend/tg_bot/backend" "github.com/TelegramExchange/tgex-backend/tg_bot/bot" ) type PlacementDetails struct { ProjectID string PlacementID string BackState bot.State } func (s *PlacementDetails) Enter(b *bot.Bot, mode bot.RenderMode) { s.renderDetails(b, mode) } func (s *PlacementDetails) renderDetails(b *bot.Bot, mode bot.RenderMode) { placement, err := b.Backend.GetPlacement(context.Background(), b.Session.JWT, b.Session.WorkspaceID, s.ProjectID, s.PlacementID) if err != nil { b.SendNew("❌ Не удалось загрузить размещение", Keyboard()) return } var placementPost *backend.PlacementPostOutput if len(placement.PlacementPosts) > 0 { placementPost = &placement.PlacementPosts[0] } text := "Детали размещения\n\n" channelName := formatChannelName(placement.Channel) text += fmt.Sprintf("• Канал: %s\n", channelName) text += fmt.Sprintf("• Статус: %s\n", formatPlacementStatus(placement.Status)) text += fmt.Sprintf("• Ссылка: %s\n", formatInviteLinkType(placement.InviteLinkType)) if placement.Details != nil { if placement.Details.PlacementType != nil { text += fmt.Sprintf("• Тип: %s\n", formatPlacementType(*placement.Details.PlacementType)) } if placement.Details.PlacementAt != nil && *placement.Details.PlacementAt != "" { text += fmt.Sprintf("• Дата размещения: %s\n", formatDateTime(*placement.Details.PlacementAt)) } if placement.Details.PaymentAt != nil && *placement.Details.PaymentAt != "" { text += fmt.Sprintf("• Дата оплаты: %s\n", formatDateTime(*placement.Details.PaymentAt)) } if placement.Details.Cost != nil { text += fmt.Sprintf("• Стоимость: %s %.0f₽\n", formatCostType(placement.Details.Cost.Type), placement.Details.Cost.Value, ) } if placement.Details.CostBeforeBargain != nil { text += fmt.Sprintf("• До торга: %.0f₽\n", *placement.Details.CostBeforeBargain) } if placement.Details.Format != nil && *placement.Details.Format != "" { text += fmt.Sprintf("• Формат: %s\n", *placement.Details.Format) } if placement.Details.Comment != nil && *placement.Details.Comment != "" { text += fmt.Sprintf("• Комментарий: %s\n", *placement.Details.Comment) } } if placementPost != nil { text += "\nПост\n" if placementPost.AdPostURL != nil && *placementPost.AdPostURL != "" { text += fmt.Sprintf("• Ссылка на пост: %s\n", *placementPost.AdPostURL) } else { text += "• Ссылка на пост: не найдена\n" } text += fmt.Sprintf("• Статус поста: %s\n", formatPlacementPostStatus(placementPost.Status)) } keyboard := Keyboard(Row(Button("← Назад", "back"))) b.Render(text, keyboard, mode) } func formatChannelName(channel backend.Channel) string { if channel.Title != nil && *channel.Title != "" { return *channel.Title } if channel.Username != nil && *channel.Username != "" { return "@" + *channel.Username } return "Без названия" } func formatInviteLinkType(value string) string { if value == "approval" { return "с одобрением" } return "публичная" } func formatPlacementStatus(status string) string { normalized := strings.TrimSpace(strings.ToLower(status)) switch normalized { case "planned": return "Планируется" case "approved": return "Согласовано" case "rejected": return "Отклонено" case "in_progress": return "В работе" case "completed": return "Размещено" default: return status } } func formatPlacementType(value string) string { normalized := strings.TrimSpace(strings.ToLower(value)) switch normalized { case "self_promo": return "Самопиар" case "standard": return "Стандарт" default: return value } } func formatPlacementPostStatus(status string) string { normalized := strings.TrimSpace(strings.ToLower(status)) switch normalized { case "active": return "Активен" case "archived": return "Архивирован" default: return status } } func formatCostType(value string) string { switch strings.TrimSpace(strings.ToLower(value)) { case "cpm": return "СРМ" default: return "Фикс" } } func formatDateTime(value string) string { if value == "" { return value } if parsed, err := time.Parse(time.RFC3339, value); err == nil { return parsed.Format("02.01.2006 15:04") } if parsed, err := time.Parse(time.RFC3339Nano, value); err == nil { return parsed.Format("02.01.2006 15:04") } return value } func (s *PlacementDetails) HandleCallback(b *bot.Bot, u *echotron.Update) { if u.CallbackQuery == nil || u.CallbackQuery.Data == "" { return } switch u.CallbackQuery.Data { case "back": if s.BackState != nil { b.SetState(s.BackState, bot.EditMessage) } default: s.Enter(b, bot.NewMessage) } } func (s *PlacementDetails) HandleMessage(_ *bot.Bot, _ *echotron.Update) { return } func (s *PlacementDetails) Handle(_ *bot.Bot, _ *echotron.Update) { return } func (s *PlacementDetails) Exit() {}