mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 17:43:47 +00:00
feat: gift subscription redesign — code-only purchase + 3-tab UI
- Remove recipient input from buy flow (code-only gifts) - 3-tab UI: Buy, Activate, My Gifts with animations - After purchase, switch to My Gifts tab to show gift code - Gift code display with copy/share buttons - Activate tab: enter gift code to activate subscription - My Gifts tab: sent/received gifts with status badges - Fix: use navigator.language instead of hardcoded ru-RU for dates - Fix: spread array before sort to prevent React Query cache mutation - Fix: ARIA tab linkage with proper id/aria-labelledby - Fix: guard VITE_BOT_USERNAME against undefined in share URLs - Fix: wrap clipboard/share fallbacks in try/catch - Add error state to MyGiftsTabContent
This commit is contained in:
@@ -45,8 +45,8 @@ export interface GiftConfig {
|
||||
export interface GiftPurchaseRequest {
|
||||
tariff_id: number;
|
||||
period_days: number;
|
||||
recipient_type: 'email' | 'telegram';
|
||||
recipient_value: string;
|
||||
recipient_type?: 'email' | 'telegram';
|
||||
recipient_value?: string;
|
||||
gift_message?: string;
|
||||
payment_mode: 'balance' | 'gateway';
|
||||
payment_method?: string;
|
||||
@@ -86,6 +86,35 @@ export interface PendingGift {
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
export interface SentGift {
|
||||
token: string;
|
||||
tariff_name: string | null;
|
||||
period_days: number;
|
||||
device_limit: number;
|
||||
status: string;
|
||||
gift_recipient_value: string | null;
|
||||
gift_message: string | null;
|
||||
activated_by_username: string | null;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
export interface ReceivedGift {
|
||||
token: string;
|
||||
tariff_name: string | null;
|
||||
period_days: number;
|
||||
device_limit: number;
|
||||
status: string;
|
||||
sender_display: string | null;
|
||||
gift_message: string | null;
|
||||
created_at: string | null;
|
||||
}
|
||||
|
||||
export interface ActivateGiftResponse {
|
||||
status: string;
|
||||
tariff_name: string | null;
|
||||
period_days: number | null;
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
export const giftApi = {
|
||||
@@ -108,4 +137,19 @@ export const giftApi = {
|
||||
const { data } = await apiClient.get<PendingGift[]>('/cabinet/gift/pending');
|
||||
return data;
|
||||
},
|
||||
|
||||
getSentGifts: async (): Promise<SentGift[]> => {
|
||||
const { data } = await apiClient.get<SentGift[]>('/cabinet/gift/sent');
|
||||
return data;
|
||||
},
|
||||
|
||||
getReceivedGifts: async (): Promise<ReceivedGift[]> => {
|
||||
const { data } = await apiClient.get<ReceivedGift[]>('/cabinet/gift/received');
|
||||
return data;
|
||||
},
|
||||
|
||||
activateGiftCode: async (code: string): Promise<ActivateGiftResponse> => {
|
||||
const { data } = await apiClient.post<ActivateGiftResponse>('/cabinet/gift/activate', { code });
|
||||
return data;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -4201,6 +4201,41 @@
|
||||
},
|
||||
"warning": {
|
||||
"telegram_unresolvable": "Could not verify this Telegram username. The recipient may not receive a notification about the gift."
|
||||
}
|
||||
},
|
||||
"pageTitle": "Gifts",
|
||||
"tabBuy": "Buy",
|
||||
"tabActivate": "Activate",
|
||||
"tabMyGifts": "My Gifts",
|
||||
"selectTariff": "SELECT TARIFF",
|
||||
"selectPeriod": "SUBSCRIPTION PERIOD",
|
||||
"deviceCount": "{{count}} device",
|
||||
"deviceCount_other": "{{count}} devices",
|
||||
"activateTitle": "Enter gift code",
|
||||
"activateDescription": "Enter the gift code you received from a friend",
|
||||
"activateCodePlaceholder": "GIFT-XXXXXXXXXXXX",
|
||||
"activateButton": "Activate gift",
|
||||
"activating": "Activating...",
|
||||
"activateSuccess": "Gift activated!",
|
||||
"activateSuccessDesc": "{{tariff}} — {{days}} days added to your subscription",
|
||||
"activateError": "Failed to activate gift",
|
||||
"myGiftsEmpty": "No gifts yet",
|
||||
"myGiftsEmptyDesc": "Buy a gift for a friend or activate a received code",
|
||||
"sentGiftsTitle": "Sent",
|
||||
"receivedGiftsTitle": "Received",
|
||||
"statusAvailable": "AVAILABLE",
|
||||
"statusActivated": "ACTIVATED",
|
||||
"statusPending": "PENDING",
|
||||
"statusDelivered": "DELIVERED",
|
||||
"statusFailed": "FAILED",
|
||||
"statusExpired": "EXPIRED",
|
||||
"statusPendingActivation": "PENDING ACTIVATION",
|
||||
"copyCode": "COPY",
|
||||
"codeCopied": "Code copied!",
|
||||
"shareGift": "SHARE",
|
||||
"shareText": "I have a gift for you! Activate it here:",
|
||||
"activatedBy": "Activated by {{username}}",
|
||||
"sentTo": "Sent to {{recipient}}",
|
||||
"daysShort": "days",
|
||||
"devicesShort": "dev."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4763,6 +4763,41 @@
|
||||
},
|
||||
"warning": {
|
||||
"telegram_unresolvable": "Не удалось проверить этот Telegram-юзернейм. Получатель может не получить уведомление о подарке."
|
||||
}
|
||||
},
|
||||
"pageTitle": "Подарки",
|
||||
"tabBuy": "Купить",
|
||||
"tabActivate": "Активировать",
|
||||
"tabMyGifts": "Мои подарки",
|
||||
"selectTariff": "ВЫБЕРИТЕ ТАРИФ",
|
||||
"selectPeriod": "ПЕРИОД ПОДПИСКИ",
|
||||
"deviceCount": "{{count}} устройство",
|
||||
"deviceCount_other": "{{count}} устройств",
|
||||
"activateTitle": "Введите код подарка",
|
||||
"activateDescription": "Введите код подарка, полученный от друга или знакомого",
|
||||
"activateCodePlaceholder": "GIFT-XXXXXXXXXXXX",
|
||||
"activateButton": "Активировать подарок",
|
||||
"activating": "Активация...",
|
||||
"activateSuccess": "Подарок активирован!",
|
||||
"activateSuccessDesc": "{{tariff}} — {{days}} дн. добавлено к вашей подписке",
|
||||
"activateError": "Не удалось активировать подарок",
|
||||
"myGiftsEmpty": "У вас пока нет подарков",
|
||||
"myGiftsEmptyDesc": "Купите подарок для друга или активируйте полученный код",
|
||||
"sentGiftsTitle": "Отправленные",
|
||||
"receivedGiftsTitle": "Полученные",
|
||||
"statusAvailable": "ДОСТУПЕН",
|
||||
"statusActivated": "АКТИВИРОВАН",
|
||||
"statusPending": "ОЖИДАЕТ",
|
||||
"statusDelivered": "ДОСТАВЛЕН",
|
||||
"statusFailed": "ОШИБКА",
|
||||
"statusExpired": "ПРОСРОЧЕН",
|
||||
"statusPendingActivation": "ОЖИДАЕТ АКТИВАЦИИ",
|
||||
"copyCode": "КОПИРОВАТЬ",
|
||||
"codeCopied": "Код скопирован!",
|
||||
"shareGift": "ПОДЕЛИТЬСЯ",
|
||||
"shareText": "У меня есть подарок для тебя! Активируй его здесь:",
|
||||
"activatedBy": "Активирован пользователем {{username}}",
|
||||
"sentTo": "Отправлен {{recipient}}",
|
||||
"daysShort": "дн.",
|
||||
"devicesShort": "устр."
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user