import { useState } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { promoApi, PromoOffer } from '../api/promo'; // Icons const GiftIcon = () => ( ); const ClockIcon = () => ( ); const SparklesIcon = () => ( ); const CheckIcon = () => ( ); const ServerIcon = () => ( ); // Helper functions const formatTimeLeft = (expiresAt: string): string => { const now = new Date(); // Ensure UTC parsing - if no timezone specified, assume UTC let expires: Date; if (expiresAt.includes('Z') || expiresAt.includes('+') || expiresAt.includes('-', 10)) { expires = new Date(expiresAt); } else { // No timezone - treat as UTC expires = new Date(expiresAt + 'Z'); } const diffMs = expires.getTime() - now.getTime(); if (diffMs <= 0) return 'Истекло'; const hours = Math.floor(diffMs / (1000 * 60 * 60)); const minutes = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60)); if (hours > 24) { const days = Math.floor(hours / 24); return `${days} дн.`; } if (hours > 0) { return `${hours}ч ${minutes}м`; } return `${minutes}м`; }; const getOfferIcon = (effectType: string) => { if (effectType === 'test_access') return ; return ; }; const getOfferTitle = (offer: PromoOffer): string => { if (offer.effect_type === 'test_access') { return 'Тестовый доступ'; } if (offer.discount_percent) { return `Скидка ${offer.discount_percent}%`; } return 'Специальное предложение'; }; const getOfferDescription = (offer: PromoOffer): string => { if (offer.effect_type === 'test_access') { const squads = offer.extra_data?.test_squad_uuids?.length || 0; return squads > 0 ? `Доступ к ${squads} серверам` : 'Доступ к дополнительным серверам'; } return 'Активируйте скидку на покупку подписки'; }; interface PromoOffersSectionProps { className?: string; } export default function PromoOffersSection({ className = '' }: PromoOffersSectionProps) { const queryClient = useQueryClient(); const [claimingId, setClaimingId] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [errorMessage, setErrorMessage] = useState(null); // Fetch available offers const { data: offers = [], isLoading: offersLoading } = useQuery({ queryKey: ['promo-offers'], queryFn: promoApi.getOffers, staleTime: 30000, }); // Fetch active discount const { data: activeDiscount } = useQuery({ queryKey: ['active-discount'], queryFn: promoApi.getActiveDiscount, staleTime: 30000, }); // Claim offer mutation const claimMutation = useMutation({ mutationFn: promoApi.claimOffer, onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: ['promo-offers'] }); queryClient.invalidateQueries({ queryKey: ['active-discount'] }); queryClient.invalidateQueries({ queryKey: ['subscription'] }); setSuccessMessage(result.message); setClaimingId(null); setTimeout(() => setSuccessMessage(null), 5000); }, onError: (error: unknown) => { const axiosErr = error as { response?: { data?: { detail?: string } } }; setErrorMessage(axiosErr.response?.data?.detail || 'Не удалось активировать предложение'); setClaimingId(null); setTimeout(() => setErrorMessage(null), 5000); }, }); const handleClaim = (offerId: number) => { setClaimingId(offerId); setErrorMessage(null); setSuccessMessage(null); claimMutation.mutate(offerId); }; // Filter unclaimed and active offers const availableOffers = offers.filter((o) => o.is_active && !o.is_claimed); // Don't render if no offers and no active discount if ( !offersLoading && availableOffers.length === 0 && (!activeDiscount || !activeDiscount.is_active) ) { return null; } return (
{/* Active Discount Banner */} {activeDiscount && activeDiscount.is_active && activeDiscount.discount_percent > 0 && (

Скидка {activeDiscount.discount_percent}% активна

Действует
{activeDiscount.expires_at && (
Истекает: {formatTimeLeft(activeDiscount.expires_at)}
)}
)} {/* Success/Error Messages */} {successMessage && (
{successMessage}
)} {errorMessage && (
{errorMessage}
)} {/* Available Offers */} {availableOffers.length > 0 && (
{availableOffers.map((offer) => (
{getOfferIcon(offer.effect_type)}

{getOfferTitle(offer)}

{offer.effect_type === 'test_access' && ( Тест )}

{getOfferDescription(offer)}

Осталось: {formatTimeLeft(offer.expires_at)}
))}
)} {/* Loading State */} {offersLoading && (
)}
); }