feat: add reset traffic toggle on tariff switch

- Add reset_traffic parameter to previewTariffSwitch and switchTariff API calls
- Add reset_traffic_available/reset_traffic_default fields to TariffSwitchPreview type
- Add toggle switch in tariff switch preview modal (shown when backend enables it)
- Initialize toggle from server default (reset_traffic_default)
- Pass reset_traffic flag on switch execution
- Add translations for all 4 locales (ru, en, zh, fa)
This commit is contained in:
Fringg
2026-02-27 09:48:28 +03:00
parent d2f02d605c
commit 49fff8e855
7 changed files with 64 additions and 6 deletions

View File

@@ -345,6 +345,7 @@ export const subscriptionApi = {
// Preview tariff switch cost
previewTariffSwitch: async (
tariffId: number,
resetTraffic?: boolean,
): Promise<{
can_switch: boolean;
current_tariff_id: number | null;
@@ -364,10 +365,14 @@ export const subscriptionApi = {
base_upgrade_cost_kopeks?: number;
discount_percent?: number;
discount_kopeks?: number;
// Traffic reset on switch
reset_traffic_available?: boolean;
reset_traffic_default?: boolean;
}> => {
const response = await apiClient.post('/cabinet/subscription/tariff/switch/preview', {
tariff_id: tariffId,
period_days: 30, // Default period for switch
period_days: 30,
...(resetTraffic !== undefined && { reset_traffic: resetTraffic }),
});
return response.data;
},
@@ -375,6 +380,7 @@ export const subscriptionApi = {
// Switch to a different tariff
switchTariff: async (
tariffId: number,
resetTraffic?: boolean,
): Promise<{
success: boolean;
message: string;
@@ -389,6 +395,7 @@ export const subscriptionApi = {
const response = await apiClient.post('/cabinet/subscription/tariff/switch', {
tariff_id: tariffId,
period_days: 30,
...(resetTraffic !== undefined && { reset_traffic: resetTraffic }),
});
return response.data;
},

View File

@@ -483,7 +483,9 @@
"switched": "Tariff switched",
"notEnoughBalance": "Insufficient balance",
"dailyPayment": "Daily payment",
"dailyChargeDescription": "Charged daily from your balance"
"dailyChargeDescription": "Charged daily from your balance",
"resetTraffic": "Reset traffic",
"resetTrafficHint": "Reset used traffic when switching tariff"
},
"switchTraffic": {
"title": "Change Traffic",

View File

@@ -378,7 +378,9 @@
"remainingDays": "روزهای باقی‌مانده",
"switch": "تغییر",
"title": "تغییر تعرفه",
"upgradeCost": "هزینه ارتقا"
"upgradeCost": "هزینه ارتقا",
"resetTraffic": "بازنشانی ترافیک",
"resetTrafficHint": "بازنشانی ترافیک مصرفی هنگام تغییر تعرفه"
},
"promoGroup": {
"yourGroup": "گروه شما: {{name}}",

View File

@@ -506,7 +506,9 @@
"switched": "Тариф изменён",
"notEnoughBalance": "Недостаточно средств",
"dailyPayment": "Оплата за день",
"dailyChargeDescription": "Списывается ежедневно с баланса"
"dailyChargeDescription": "Списывается ежедневно с баланса",
"resetTraffic": "Сбросить трафик",
"resetTrafficHint": "Обнулить использованный трафик при смене тарифа"
},
"switchTraffic": {
"title": "Изменить трафик",

View File

@@ -378,7 +378,9 @@
"remainingDays": "剩余天数",
"switch": "切换",
"title": "切换套餐",
"upgradeCost": "升级费用"
"upgradeCost": "升级费用",
"resetTraffic": "重置流量",
"resetTrafficHint": "切换套餐时重置已使用流量"
},
"promoGroup": {
"yourGroup": "您的组:{{name}}",

View File

@@ -128,12 +128,14 @@ export default function SubscriptionPurchase() {
// Tariff switch
const [switchTariffId, setSwitchTariffId] = useState<number | null>(null);
const [resetTrafficOnSwitch, setResetTrafficOnSwitch] = useState(false);
// Auto-close all modals on success notification
const handleCloseAllModals = () => {
setShowPurchaseForm(false);
setShowTariffPurchase(false);
setSwitchTariffId(null);
setResetTrafficOnSwitch(false);
setSelectedTariff(null);
setSelectedTariffPeriod(null);
};
@@ -230,13 +232,21 @@ export default function SubscriptionPurchase() {
enabled: !!switchTariffId,
});
// Initialize reset traffic toggle from server default
useEffect(() => {
if (switchPreview?.reset_traffic_default !== undefined) {
setResetTrafficOnSwitch(switchPreview.reset_traffic_default);
}
}, [switchPreview?.reset_traffic_default]);
// Tariff switch mutation
const switchTariffMutation = useMutation({
mutationFn: (tariffId: number) => subscriptionApi.switchTariff(tariffId),
mutationFn: (tariffId: number) => subscriptionApi.switchTariff(tariffId, resetTrafficOnSwitch),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['subscription'] });
queryClient.invalidateQueries({ queryKey: ['purchase-options'] });
setSwitchTariffId(null);
setResetTrafficOnSwitch(false);
navigate('/subscription', { replace: true });
},
onError: (error: unknown) => {
@@ -250,6 +260,7 @@ export default function SubscriptionPurchase() {
const targetTariff = tariffs.find((tariff) => tariff.id === switchTariffId);
if (targetTariff) {
setSwitchTariffId(null);
setResetTrafficOnSwitch(false);
setSelectedTariff(targetTariff);
setSelectedTariffPeriod(targetTariff.periods[0] || null);
setShowTariffPurchase(true);
@@ -598,6 +609,35 @@ export default function SubscriptionPurchase() {
</div>
</div>
{/* Reset traffic toggle */}
{switchPreview.reset_traffic_available && (
<label className="flex cursor-pointer items-center justify-between rounded-lg border border-dark-700/50 bg-dark-800/50 px-3 py-2.5">
<div>
<div className="text-sm font-medium text-dark-100">
{t('subscription.switchTariff.resetTraffic')}
</div>
<div className="text-xs text-dark-400">
{t('subscription.switchTariff.resetTrafficHint')}
</div>
</div>
<button
type="button"
role="switch"
aria-checked={resetTrafficOnSwitch}
onClick={() => setResetTrafficOnSwitch((prev) => !prev)}
className={`relative h-6 w-11 flex-shrink-0 rounded-full transition-colors duration-200 ${
resetTrafficOnSwitch ? 'bg-accent-500' : 'bg-dark-600'
}`}
>
<span
className={`absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform duration-200 ${
resetTrafficOnSwitch ? 'translate-x-5' : 'translate-x-0'
}`}
/>
</button>
</label>
)}
{!switchPreview.has_enough_balance &&
switchPreview.upgrade_cost_kopeks > 0 && (
<InsufficientBalancePrompt

View File

@@ -143,6 +143,9 @@ export interface TariffSwitchPreview {
base_upgrade_cost_kopeks?: number;
discount_percent?: number;
discount_kopeks?: number;
// Traffic reset on switch
reset_traffic_available?: boolean;
reset_traffic_default?: boolean;
}
export interface RenewalOption {