Merge pull request #87 from BEDOLAGA-DEV/dev

Dev
This commit is contained in:
Egor
2026-01-23 03:46:57 +03:00
committed by GitHub
4 changed files with 50 additions and 12 deletions

View File

@@ -216,6 +216,7 @@
"pausedMessage": "Subscription paused",
"resumedMessage": "Subscription resumed",
"dailyOnly": "Pause is only available for daily tariffs",
"insufficientBalance": "Insufficient balance to resume subscription",
"pausedInfo": "Subscription paused",
"pausedDescription": "Charges stopped. Subscription will be active until",
"nextCharge": "Until next charge",

View File

@@ -216,6 +216,7 @@
"pausedMessage": "Подписка приостановлена",
"resumedMessage": "Подписка возобновлена",
"dailyOnly": "Пауза доступна только для суточных тарифов",
"insufficientBalance": "Недостаточно средств для возобновления подписки",
"pausedInfo": "Подписка приостановлена",
"pausedDescription": "Списания остановлены. Подписка будет действовать до",
"nextCharge": "До следующего списания",

View File

@@ -136,7 +136,7 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
const [name, setName] = useState(tariff?.name || '')
const [description, setDescription] = useState(tariff?.description || '')
const [trafficLimitGb, setTrafficLimitGb] = useState(tariff?.traffic_limit_gb || 100)
const [trafficLimitGb, setTrafficLimitGb] = useState(tariff?.traffic_limit_gb ?? 100)
const [deviceLimit, setDeviceLimit] = useState(tariff?.device_limit || 1)
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
const [maxDeviceLimit, setMaxDeviceLimit] = useState(tariff?.max_device_limit || 0)
@@ -182,7 +182,7 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
device_price_kopeks: devicePriceKopeks > 0 ? devicePriceKopeks : undefined,
max_device_limit: maxDeviceLimit > 0 ? maxDeviceLimit : undefined,
tier_level: tierLevel,
period_prices: periodPrices.filter(p => p.price_kopeks > 0),
period_prices: periodPrices.filter(p => p.price_kopeks >= 0),
allowed_squads: selectedSquads,
traffic_topup_enabled: trafficTopupEnabled,
traffic_topup_packages: trafficTopupPackages,
@@ -757,7 +757,7 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
const [name, setName] = useState(tariff?.name || '')
const [description, setDescription] = useState(tariff?.description || '')
const [trafficLimitGb, setTrafficLimitGb] = useState(tariff?.traffic_limit_gb || 100)
const [trafficLimitGb, setTrafficLimitGb] = useState(tariff?.traffic_limit_gb ?? 100)
const [deviceLimit, setDeviceLimit] = useState(tariff?.device_limit || 1)
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
const [maxDeviceLimit, setMaxDeviceLimit] = useState(tariff?.max_device_limit || 0)

View File

@@ -21,6 +21,20 @@ const getErrorMessage = (error: unknown): string => {
return 'Произошла ошибка'
}
// Helper to extract insufficient balance error details
const getInsufficientBalanceError = (error: unknown): { required: number; balance: number } | null => {
if (error instanceof AxiosError) {
const detail = error.response?.data?.detail
if (typeof detail === 'object' && detail?.code === 'insufficient_balance') {
return {
required: detail.required || 0,
balance: detail.balance || 0,
}
}
}
return null
}
// Icons
const CopyIcon = () => (
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
@@ -742,11 +756,26 @@ export default function Subscription() {
</div>
{/* Pause mutation error */}
{pauseMutation.isError && (
<div className="mt-4 text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
{getErrorMessage(pauseMutation.error)}
</div>
)}
{pauseMutation.isError && (() => {
const balanceError = getInsufficientBalanceError(pauseMutation.error)
if (balanceError) {
const missingAmount = balanceError.required - balanceError.balance
return (
<div className="mt-4">
<InsufficientBalancePrompt
missingAmountKopeks={missingAmount}
message={t('subscription.pause.insufficientBalance')}
compact
/>
</div>
)
}
return (
<div className="mt-4 text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
{getErrorMessage(pauseMutation.error)}
</div>
)
})()}
{/* Paused info or Next charge progress bar */}
{subscription.is_daily_paused ? (
@@ -2276,10 +2305,17 @@ export default function Subscription() {
<div className="text-sm text-success-400 text-center">{preview.discount_label}</div>
)}
{!preview.can_purchase && preview.status_message && (
<div className="text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
{preview.status_message}
</div>
{!preview.can_purchase && (
preview.missing_amount_kopeks > 0 ? (
<InsufficientBalancePrompt
missingAmountKopeks={preview.missing_amount_kopeks}
compact
/>
) : preview.status_message ? (
<div className="text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
{preview.status_message}
</div>
) : null
)}
</div>
) : null}