mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
@@ -216,6 +216,7 @@
|
|||||||
"pausedMessage": "Subscription paused",
|
"pausedMessage": "Subscription paused",
|
||||||
"resumedMessage": "Subscription resumed",
|
"resumedMessage": "Subscription resumed",
|
||||||
"dailyOnly": "Pause is only available for daily tariffs",
|
"dailyOnly": "Pause is only available for daily tariffs",
|
||||||
|
"insufficientBalance": "Insufficient balance to resume subscription",
|
||||||
"pausedInfo": "Subscription paused",
|
"pausedInfo": "Subscription paused",
|
||||||
"pausedDescription": "Charges stopped. Subscription will be active until",
|
"pausedDescription": "Charges stopped. Subscription will be active until",
|
||||||
"nextCharge": "Until next charge",
|
"nextCharge": "Until next charge",
|
||||||
|
|||||||
@@ -216,6 +216,7 @@
|
|||||||
"pausedMessage": "Подписка приостановлена",
|
"pausedMessage": "Подписка приостановлена",
|
||||||
"resumedMessage": "Подписка возобновлена",
|
"resumedMessage": "Подписка возобновлена",
|
||||||
"dailyOnly": "Пауза доступна только для суточных тарифов",
|
"dailyOnly": "Пауза доступна только для суточных тарифов",
|
||||||
|
"insufficientBalance": "Недостаточно средств для возобновления подписки",
|
||||||
"pausedInfo": "Подписка приостановлена",
|
"pausedInfo": "Подписка приостановлена",
|
||||||
"pausedDescription": "Списания остановлены. Подписка будет действовать до",
|
"pausedDescription": "Списания остановлены. Подписка будет действовать до",
|
||||||
"nextCharge": "До следующего списания",
|
"nextCharge": "До следующего списания",
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ function PeriodTariffModal({ tariff, servers, onSave, onClose, isLoading }: Peri
|
|||||||
|
|
||||||
const [name, setName] = useState(tariff?.name || '')
|
const [name, setName] = useState(tariff?.name || '')
|
||||||
const [description, setDescription] = useState(tariff?.description || '')
|
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 [deviceLimit, setDeviceLimit] = useState(tariff?.device_limit || 1)
|
||||||
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
|
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
|
||||||
const [maxDeviceLimit, setMaxDeviceLimit] = useState(tariff?.max_device_limit || 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,
|
device_price_kopeks: devicePriceKopeks > 0 ? devicePriceKopeks : undefined,
|
||||||
max_device_limit: maxDeviceLimit > 0 ? maxDeviceLimit : undefined,
|
max_device_limit: maxDeviceLimit > 0 ? maxDeviceLimit : undefined,
|
||||||
tier_level: tierLevel,
|
tier_level: tierLevel,
|
||||||
period_prices: periodPrices.filter(p => p.price_kopeks > 0),
|
period_prices: periodPrices.filter(p => p.price_kopeks >= 0),
|
||||||
allowed_squads: selectedSquads,
|
allowed_squads: selectedSquads,
|
||||||
traffic_topup_enabled: trafficTopupEnabled,
|
traffic_topup_enabled: trafficTopupEnabled,
|
||||||
traffic_topup_packages: trafficTopupPackages,
|
traffic_topup_packages: trafficTopupPackages,
|
||||||
@@ -757,7 +757,7 @@ function DailyTariffModal({ tariff, servers, onSave, onClose, isLoading }: Daily
|
|||||||
|
|
||||||
const [name, setName] = useState(tariff?.name || '')
|
const [name, setName] = useState(tariff?.name || '')
|
||||||
const [description, setDescription] = useState(tariff?.description || '')
|
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 [deviceLimit, setDeviceLimit] = useState(tariff?.device_limit || 1)
|
||||||
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
|
const [devicePriceKopeks, setDevicePriceKopeks] = useState(tariff?.device_price_kopeks || 0)
|
||||||
const [maxDeviceLimit, setMaxDeviceLimit] = useState(tariff?.max_device_limit || 0)
|
const [maxDeviceLimit, setMaxDeviceLimit] = useState(tariff?.max_device_limit || 0)
|
||||||
|
|||||||
@@ -21,6 +21,20 @@ const getErrorMessage = (error: unknown): string => {
|
|||||||
return 'Произошла ошибка'
|
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
|
// Icons
|
||||||
const CopyIcon = () => (
|
const CopyIcon = () => (
|
||||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={1.5}>
|
<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>
|
</div>
|
||||||
|
|
||||||
{/* Pause mutation error */}
|
{/* Pause mutation error */}
|
||||||
{pauseMutation.isError && (
|
{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">
|
<div className="mt-4 text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
|
||||||
{getErrorMessage(pauseMutation.error)}
|
{getErrorMessage(pauseMutation.error)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
})()}
|
||||||
|
|
||||||
{/* Paused info or Next charge progress bar */}
|
{/* Paused info or Next charge progress bar */}
|
||||||
{subscription.is_daily_paused ? (
|
{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>
|
<div className="text-sm text-success-400 text-center">{preview.discount_label}</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!preview.can_purchase && preview.status_message && (
|
{!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">
|
<div className="text-sm text-error-400 bg-error-500/10 px-4 py-3 rounded-lg text-center">
|
||||||
{preview.status_message}
|
{preview.status_message}
|
||||||
</div>
|
</div>
|
||||||
|
) : null
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|||||||
Reference in New Issue
Block a user