From 694683455bd447ca0e0ac1a18f5058a8d509ef78 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Thu, 23 Jul 2026 23:03:45 +0300 Subject: [PATCH] Add page hints to admin and user plan interfaces - Introduced page hints in the PlanContent, AdminPlansPage, and AdminRolesPage components to provide users with contextual information. - Updated i18n resource files to include new translations for page hints related to plans and roles, enhancing user experience and clarity. - Improved existing text for downgrade notices and discount tier hints for better understanding of billing implications. --- frontend/src/routes/admin/plans.tsx | 1 + frontend/src/routes/admin/roles.tsx | 1 + frontend/src/routes/plan.tsx | 2 ++ frontend/src/shared/lib/i18n.ts | 20 ++++++++++++++------ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/frontend/src/routes/admin/plans.tsx b/frontend/src/routes/admin/plans.tsx index 436baa0..e78a9ec 100644 --- a/frontend/src/routes/admin/plans.tsx +++ b/frontend/src/routes/admin/plans.tsx @@ -51,6 +51,7 @@ function AdminPlansPage() { return (
+

{t('admin.plans.pageHint')}

diff --git a/frontend/src/routes/admin/roles.tsx b/frontend/src/routes/admin/roles.tsx index d09e82a..8cbd9d4 100644 --- a/frontend/src/routes/admin/roles.tsx +++ b/frontend/src/routes/admin/roles.tsx @@ -29,6 +29,7 @@ function AdminRolesPage() { return (
+

{t('admin.roles.pageHint')}

diff --git a/frontend/src/routes/plan.tsx b/frontend/src/routes/plan.tsx index 47a4eff..e3c7703 100644 --- a/frontend/src/routes/plan.tsx +++ b/frontend/src/routes/plan.tsx @@ -101,6 +101,7 @@ function PlanContent() { : t('plan.current', { count: statusQuery.data.configQuota })}

)} +

{t('plan.pageHint')}

@@ -136,6 +137,7 @@ function PlanContent() { }} /> + {selection === 'custom' &&

{t('plan.customHint')}

}
{needsRevokePicker && ( diff --git a/frontend/src/shared/lib/i18n.ts b/frontend/src/shared/lib/i18n.ts index 2df3498..4f28a85 100644 --- a/frontend/src/shared/lib/i18n.ts +++ b/frontend/src/shared/lib/i18n.ts @@ -105,14 +105,16 @@ const resources = { plan: { title: 'Тариф', + pageHint: 'Смена тарифа применяется сразу, без подтверждения администратора. Если у вашей роли включена оплата и уже есть оплаченный период — при увеличении тарифа автоматически будет выставлена доплата за разницу в цене на оставшийся срок.', changePlan: 'Изменить тариф', current: 'Текущая квота: {{count}} конфигов', currentUnlimited: 'Текущая квота: без лимита', custom: 'Своё количество', customConfigCount: 'Количество конфигов', + customHint: 'Если ни один из вариантов выше не подходит — укажите нужное количество вручную (в разумных пределах).', submit: 'Применить', changed: 'Тариф изменён.', - downgradeNotice: 'Новая квота меньше текущего числа активных конфигов — выберите, какие отозвать:', + downgradeNotice: 'Новая квота меньше текущего числа конфигов, занимающих место в квоте (включая приостановленные за неуплату) — выберите, какие отозвать:', selectExactly: 'Нужно выбрать ровно {{count}} конфиг(ов) для отзыва.', topUpCreated: 'Тариф увеличен. Создана доплата за оставшийся оплаченный период.', goToBilling: 'Перейти к оплате', @@ -333,6 +335,7 @@ const resources = { }, roles: { create: 'Создать роль', + pageHint: 'Роль определяет лимит устройств на конфиг, доступ к инбаундам и участие в оплате. Количество конфигов (квота) больше не задаётся на роли — см. вкладку «Тарифы».', name: 'Название', maxIpLimit: 'Лимит IP на конфиг', maxIpLimitHint: '−1 = без лимита. Применяется только к новым конфигам.', @@ -349,6 +352,7 @@ const resources = { }, plans: { create: 'Создать тариф', + pageHint: 'Каталог тарифов, из которых пользователи выбирают квоту конфигов сами, без подтверждения (страница «Изменить тариф»). Каталожные тарифы не поддерживают безлимит — он доступен только роли admin.', name: 'Название', configCount: 'Количество конфигов', sortOrder: 'Порядок сортировки', @@ -370,7 +374,7 @@ const resources = { pricing: { title: 'Справочная цена конфига', pricePerConfigPerQuarter: 'Цена за конфиг в месяц / оплата раз в 3 мес', - pricePerConfigPerQuarterHint: 'Минимальный период оплаты. Видно только админу. Итог за период = ставка × число месяцев × квота роли.', + pricePerConfigPerQuarterHint: 'Минимальный период оплаты. Видно только админу. Итог за период = ставка × число месяцев × количество конфигов в тарифе пользователя.', pricePerConfigPerHalfYear: 'Цена за конфиг в месяц / оплата раз в полгода', pricePerConfigPerHalfYearHint: 'Может быть ниже квартальной (скидка за оплату на полгода вперёд), но итог за полгода не должен быть дешевле итога за квартал.', halfYearCheaperThanQuarter: 'Цена за полгода (в пересчёте на 6 месяцев) не может быть меньше цены за 3 месяца.', @@ -380,7 +384,7 @@ const resources = { yearCheaperThanQuarter: 'Цена за год (в пересчёте на 12 месяцев) не может быть меньше цены за 3 месяца.', updated: 'Цена обновлена.', discountTiersTitle: 'Скидка за объём', - discountTiersHint: 'Роль с квотой конфигов не меньше порога получает указанную скидку от итоговой цены за период. Действует наивысший подходящий порог, скидки не суммируются.', + discountTiersHint: 'Если количество конфигов в тарифе пользователя не меньше порога — применяется указанная скидка от итоговой цены за период. Действует наивысший подходящий порог, скидки не суммируются.', discountTierMinConfigs: 'От скольки конфигов', discountTierPercent: 'Скидка, %', addDiscountTier: 'Добавить ступень', @@ -692,14 +696,16 @@ const resources = { plan: { title: 'Plan', + pageHint: "Changing your plan applies immediately, with no admin approval needed. If your role has billing enabled and you already have a paid period, increasing your plan automatically creates a top-up charge for the price difference over the remaining time.", changePlan: 'Change plan', current: 'Current quota: {{count}} configs', currentUnlimited: 'Current quota: unlimited', custom: 'Custom amount', customConfigCount: 'Number of configs', + customHint: "If none of the plans above fit, enter the amount you need (within reasonable limits).", submit: 'Apply', changed: 'Plan changed.', - downgradeNotice: 'The new quota is lower than your current number of active configs — choose which to revoke:', + 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.', goToBilling: 'Go to payment', @@ -920,6 +926,7 @@ const resources = { }, roles: { create: 'Create role', + pageHint: "A role sets the device limit per config, inbound access, and billing eligibility. Config quota is no longer set on the role — see the Plans tab.", name: 'Name', maxIpLimit: 'IP limit per config', maxIpLimitHint: '−1 = unlimited. Applies to new configs only.', @@ -936,6 +943,7 @@ const resources = { }, plans: { create: 'Create plan', + pageHint: "The catalog of plans users can pick a config quota from themselves, with no approval needed (the \"Change plan\" page). Catalog plans cannot be unlimited — that's reserved for the admin role.", name: 'Name', configCount: 'Number of configs', sortOrder: 'Sort order', @@ -957,7 +965,7 @@ const resources = { pricing: { title: 'Reference config price', pricePerConfigPerQuarter: 'Price per config per month / billed every 3 months', - pricePerConfigPerQuarterHint: 'Minimum billing period. Visible to admin only. Total for a period = rate × months × role quota.', + pricePerConfigPerQuarterHint: "Minimum billing period. Visible to admin only. Total for a period = rate × months × the user's plan config count.", pricePerConfigPerHalfYear: 'Price per config per month / billed every 6 months', pricePerConfigPerHalfYearHint: 'Can be lower than the quarterly rate (discount for a 6-month prepayment), but the 6-month total must not be cheaper than the 3-month total.', halfYearCheaperThanQuarter: 'The 6-month price (over 6 months) cannot be lower than the 3-month price.', @@ -967,7 +975,7 @@ const resources = { yearCheaperThanQuarter: 'The annual price (over 12 months) cannot be lower than the 3-month price.', updated: 'Pricing updated.', discountTiersTitle: 'Volume discount', - discountTiersHint: 'A role with a config quota at or above the threshold gets the listed discount off the period total. The highest applicable threshold wins — discounts do not stack.', + discountTiersHint: "If the user's plan config count is at or above the threshold, the listed discount applies to the period total. The highest applicable threshold wins — discounts do not stack.", discountTierMinConfigs: 'From how many configs', discountTierPercent: 'Discount, %', addDiscountTier: 'Add tier',