diff --git a/frontend/src/routes/plan.tsx b/frontend/src/routes/plan.tsx index e3c7703..72627eb 100644 --- a/frontend/src/routes/plan.tsx +++ b/frontend/src/routes/plan.tsx @@ -13,6 +13,8 @@ import { applyDiscount, resolveDiscountPercent } from '@/shared/lib/pricing' import { getMyConfigs } from '@/features/configs/api' import { getSupportPricing } from '@/features/support/api' import { changePlan, getMyPlanStatus, listPlans } from '@/features/plans/api' +import { getMyBillingStatus } from '@/features/billing/api' +import { getDaysRemaining } from '@/features/billing/remaining' export const Route = createFileRoute('/plan')({ component: PlanPage }) @@ -35,6 +37,17 @@ function PlanContent() { const statusQuery = useQuery({ queryKey: ['my-plan-status'], queryFn: getMyPlanStatus }) const pricingQuery = useQuery({ queryKey: ['support-pricing'], queryFn: getSupportPricing }) const configsQuery = useQuery({ queryKey: ['my-configs'], queryFn: getMyConfigs }) + const billingStatusQuery = useQuery({ queryKey: ['my-billing-status'], queryFn: getMyBillingStatus }) + + // Если оплата включена для роли, но сейчас нет действующего оплаченного периода (ни разу не + // платил, либо срок истёк) — не ждём, пока это всплывёт как 403 при попытке создать конфиг: + // сразу предлагаем оформить оплату прямо на странице тарифа. Не показываем, если заявка уже + // отправлена и ждёт решения админа (activeRequest) — тогда достаточно билинг-страницы. + const billingData = billingStatusQuery.data + const needsPayment = + !!billingData?.billingEnabled && + !billingData.activeRequest && + (!billingData.paidUntil || getDaysRemaining(billingData.paidUntil) <= 0) const [selection, setSelection] = useState('') const [customCount, setCustomCount] = useState('3') @@ -104,6 +117,19 @@ function PlanContent() {

{t('plan.pageHint')}

+ {needsPayment && ( + + + {t('plan.paymentRequiredNotice')} + + + + {t('plan.goToBilling')} + + + + )} +
{plansQuery.data?.map((p) => (
diff --git a/frontend/src/shared/lib/i18n.ts b/frontend/src/shared/lib/i18n.ts index 4f28a85..8fe226f 100644 --- a/frontend/src/shared/lib/i18n.ts +++ b/frontend/src/shared/lib/i18n.ts @@ -117,6 +117,7 @@ const resources = { downgradeNotice: 'Новая квота меньше текущего числа конфигов, занимающих место в квоте (включая приостановленные за неуплату) — выберите, какие отозвать:', selectExactly: 'Нужно выбрать ровно {{count}} конфиг(ов) для отзыва.', topUpCreated: 'Тариф увеличен. Создана доплата за оставшийся оплаченный период.', + paymentRequiredNotice: 'Оплата ещё не произведена (или срок истёк) — конфиги недоступны, пока не оформите оплату.', goToBilling: 'Перейти к оплате', mustSelectConfigs: 'Выберите конфиги для отзыва.', perQuarter: '3 мес: {{price}}', @@ -708,6 +709,7 @@ const resources = { downgradeNotice: 'The new quota is lower than your current number of configs that still count against it (including ones suspended for non-payment) — choose which to revoke:', selectExactly: 'Select exactly {{count}} config(s) to revoke.', topUpCreated: 'Plan increased. A top-up was created for the remaining paid period.', + paymentRequiredNotice: "You haven't paid yet (or your paid period expired) — configs stay unavailable until you complete a payment.", goToBilling: 'Go to payment', mustSelectConfigs: 'Select configs to revoke.', perQuarter: '3 months: {{price}}',