feat(promo-offers): охват сегментов и прогресс доставки на странице отправки

Сегмент выбирался вслепую — в списке не было числа пользователей, а после
отправки экран показывал только количество созданных офферов: сколько
человек реально получило предложение и сколько заблокировало бота, узнать
было негде.

В селекте сегментов теперь стоит охват каждого (GET /promo-offers/segments),
под ним — сколько уйдёт по выбранному. После отправки экран результата
показывает прогресс доставки с карточками Всего/Отправлено/Заблокировали/
Ошибки и опрашивает запись рассылки раз в 3 секунды, пока она не завершится.

Прогресс-блок и бейдж статуса вынесены из AdminBroadcastDetail в
BroadcastDeliveryStats и переиспользуются обеими страницами, а условие
«доставка ещё идёт» — в утилиту broadcastStatus с тестами.
This commit is contained in:
kewldan
2026-07-28 22:24:58 +03:00
parent 51d0f0431a
commit c6d8c0afe0
10 changed files with 298 additions and 128 deletions

View File

@@ -67,8 +67,21 @@ export interface PromoOfferBroadcastResponse {
created_offers: number;
user_ids: number[];
target: string | null;
/** Счётчики email-уведомлений; доставка в Telegram отслеживается через broadcast_id */
notifications_sent: number;
notifications_failed: number;
/** Запись рассылки с прогрессом доставки в Telegram (null — слать было некому) */
broadcast_id: number | null;
telegram_recipients: number;
}
export interface PromoOfferSegment {
key: string;
count: number;
}
export interface PromoOfferSegmentListResponse {
segments: PromoOfferSegment[];
}
export interface PromoOfferTemplate {
@@ -206,6 +219,12 @@ export const promoOffersApi = {
return response.data;
},
// Get recipient counts per target segment
getSegments: async (): Promise<PromoOfferSegmentListResponse> => {
const response = await apiClient.get('/cabinet/admin/promo-offers/segments');
return response.data;
},
// Get promo offer logs
getLogs: async (params?: {
limit?: number;