Add files via upload

This commit is contained in:
Egor
2026-01-19 00:36:19 +03:00
committed by GitHub
parent 9d1b1ce6c3
commit a7f0d705ff
2 changed files with 65 additions and 39 deletions

View File

@@ -7,6 +7,7 @@ import { subscriptionApi } from '../api/subscription'
import { promoApi } from '../api/promo' import { promoApi } from '../api/promo'
import type { PurchaseSelection, PeriodOption, Tariff, TariffPeriod, ClassicPurchaseOptions } from '../types' import type { PurchaseSelection, PeriodOption, Tariff, TariffPeriod, ClassicPurchaseOptions } from '../types'
import ConnectionModal from '../components/ConnectionModal' import ConnectionModal from '../components/ConnectionModal'
import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt'
import { useCurrency } from '../hooks/useCurrency' import { useCurrency } from '../hooks/useCurrency'
// Helper to extract error message from axios/api errors // Helper to extract error message from axios/api errors
@@ -772,9 +773,16 @@ export default function Subscription() {
</div> </div>
)} )}
{devicePriceData && purchaseOptions && devicePriceData.total_price_kopeks && devicePriceData.total_price_kopeks > purchaseOptions.balance_kopeks && (
<InsufficientBalancePrompt
missingAmountKopeks={devicePriceData.total_price_kopeks - purchaseOptions.balance_kopeks}
compact
/>
)}
<button <button
onClick={() => devicePurchaseMutation.mutate()} onClick={() => devicePurchaseMutation.mutate()}
disabled={devicePurchaseMutation.isPending} disabled={devicePurchaseMutation.isPending || (devicePriceData?.total_price_kopeks && purchaseOptions && devicePriceData.total_price_kopeks > purchaseOptions.balance_kopeks)}
className="btn-primary w-full py-3" className="btn-primary w-full py-3"
> >
{devicePurchaseMutation.isPending ? ( {devicePurchaseMutation.isPending ? (
@@ -862,21 +870,36 @@ export default function Subscription() {
))} ))}
</div> </div>
{selectedTrafficPackage && ( {selectedTrafficPackage && (() => {
<button const selectedPkg = trafficPackages.find(p => p.gb === selectedTrafficPackage)
onClick={() => trafficPurchaseMutation.mutate(selectedTrafficPackage)} const hasEnoughBalance = !selectedPkg || !purchaseOptions || selectedPkg.price_kopeks <= purchaseOptions.balance_kopeks
disabled={trafficPurchaseMutation.isPending} const missingAmount = selectedPkg && purchaseOptions ? selectedPkg.price_kopeks - purchaseOptions.balance_kopeks : 0
className="btn-primary w-full py-3"
> return (
{trafficPurchaseMutation.isPending ? ( <>
<span className="flex items-center justify-center gap-2"> {!hasEnoughBalance && missingAmount > 0 && (
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" /> <InsufficientBalancePrompt
</span> missingAmountKopeks={missingAmount}
) : ( compact
`Купить ${selectedTrafficPackage} ГБ` className="mb-3"
)} />
</button> )}
)} <button
onClick={() => trafficPurchaseMutation.mutate(selectedTrafficPackage)}
disabled={trafficPurchaseMutation.isPending || !hasEnoughBalance}
className="btn-primary w-full py-3"
>
{trafficPurchaseMutation.isPending ? (
<span className="flex items-center justify-center gap-2">
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
</span>
) : (
`Купить ${selectedTrafficPackage} ГБ`
)}
</button>
</>
)
})()}
{trafficPurchaseMutation.isError && ( {trafficPurchaseMutation.isError && (
<div className="text-sm text-error-400 text-center"> <div className="text-sm text-error-400 text-center">
@@ -1050,9 +1073,10 @@ export default function Subscription() {
</div> </div>
{!switchPreview.has_enough_balance && switchPreview.upgrade_cost_kopeks > 0 && ( {!switchPreview.has_enough_balance && switchPreview.upgrade_cost_kopeks > 0 && (
<div className="text-sm text-error-400 bg-error-500/10 px-3 py-2 rounded-lg text-center"> <InsufficientBalancePrompt
{t('subscription.switchTariff.notEnoughBalance')}: {switchPreview.missing_amount_label} missingAmountKopeks={switchPreview.missing_amount_kopeks}
</div> compact
/>
)} )}
<button <button
@@ -1341,11 +1365,11 @@ export default function Subscription() {
return ( return (
<div className="mt-6"> <div className="mt-6">
{purchaseOptions && !hasEnoughBalance && ( {purchaseOptions && !hasEnoughBalance && (
<div className="text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center mb-4"> <InsufficientBalancePrompt
{t('subscription.insufficientBalance', { missingAmountKopeks={dailyPrice - purchaseOptions.balance_kopeks}
missing: ((dailyPrice - purchaseOptions.balance_kopeks) / 100).toFixed(2) compact
})} className="mb-4"
</div> />
)} )}
<button <button
@@ -1624,11 +1648,11 @@ export default function Subscription() {
</div> </div>
{purchaseOptions && !hasEnoughBalance && ( {purchaseOptions && !hasEnoughBalance && (
<div className="text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center mb-4"> <InsufficientBalancePrompt
{t('subscription.insufficientBalance', { missingAmountKopeks={totalPrice - purchaseOptions.balance_kopeks}
missing: ((totalPrice - purchaseOptions.balance_kopeks) / 100).toFixed(2) compact
})} className="mb-4"
</div> />
)} )}
<button <button

View File

@@ -3,6 +3,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { wheelApi, type SpinResult, type SpinHistoryItem } from '../api/wheel' import { wheelApi, type SpinResult, type SpinHistoryItem } from '../api/wheel'
import FortuneWheel from '../components/wheel/FortuneWheel' import FortuneWheel from '../components/wheel/FortuneWheel'
import InsufficientBalancePrompt from '../components/InsufficientBalancePrompt'
import { useCurrency } from '../hooks/useCurrency' import { useCurrency } from '../hooks/useCurrency'
// Icons // Icons
@@ -574,20 +575,21 @@ export default function Wheel() {
{/* Cannot spin hint */} {/* Cannot spin hint */}
{!config.can_spin && !isSpinning && ( {!config.can_spin && !isSpinning && (
<div className="text-center p-4 rounded-xl bg-dark-800/50 border border-dark-700"> <>
{config.can_spin_reason === 'daily_limit_reached' ? ( {config.can_spin_reason === 'daily_limit_reached' ? (
<p className="text-dark-400">{t('wheel.errors.dailyLimitReached')}</p> <div className="text-center p-4 rounded-xl bg-dark-800/50 border border-dark-700">
) : config.can_spin_reason === 'insufficient_balance' ? ( <p className="text-dark-400">{t('wheel.errors.dailyLimitReached')}</p>
<div className="space-y-1">
<p className="text-warning-400 font-medium">{t('wheel.errors.insufficientBalance')}</p>
<p className="text-dark-500 text-sm">
{t('wheel.errors.topUpRequired')} ({formatAmount((config.required_balance_kopeks - config.user_balance_kopeks) / 100)} {currencySymbol})
</p>
</div> </div>
) : config.can_spin_reason === 'insufficient_balance' ? (
<InsufficientBalancePrompt
missingAmountKopeks={config.required_balance_kopeks - config.user_balance_kopeks}
/>
) : ( ) : (
<p className="text-dark-400">{t('wheel.errors.cannotSpin')}</p> <div className="text-center p-4 rounded-xl bg-dark-800/50 border border-dark-700">
<p className="text-dark-400">{t('wheel.errors.cannotSpin')}</p>
</div>
)} )}
</div> </>
)} )}
</div> </div>
</div> </div>