mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-30 02:23:47 +00:00
@@ -228,7 +228,7 @@ export default function SubscriptionCardExpired({
|
|||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="mb-0.5 font-mono text-[10px] font-medium uppercase tracking-wider text-dark-50/30">
|
<div className="mb-0.5 font-mono text-[10px] font-medium uppercase tracking-wider text-dark-50/30">
|
||||||
{t('dashboard.expired.expiredDate')}
|
{isLimited ? t('dashboard.expired.activeUntil') : t('dashboard.expired.expiredDate')}
|
||||||
</div>
|
</div>
|
||||||
<div className="ml-3 text-base font-bold tracking-tight text-dark-50/50">
|
<div className="ml-3 text-base font-bold tracking-tight text-dark-50/50">
|
||||||
{formattedDate}
|
{formattedDate}
|
||||||
@@ -280,7 +280,7 @@ export default function SubscriptionCardExpired({
|
|||||||
>
|
>
|
||||||
<path d="M12 4.5v15m7.5-7.5h-15" />
|
<path d="M12 4.5v15m7.5-7.5h-15" />
|
||||||
</svg>
|
</svg>
|
||||||
{t('subscription.trafficLimited')}
|
{t('subscription.buyTraffic')}
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ export default function PurchaseCTAButton({
|
|||||||
}: PurchaseCTAButtonProps) {
|
}: PurchaseCTAButtonProps) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const isExpired = !subscription || (!subscription.is_active && !subscription.is_trial);
|
const isExpired =
|
||||||
|
!subscription ||
|
||||||
|
(!subscription.is_active && !subscription.is_trial && !subscription.is_limited);
|
||||||
const isTrial = subscription?.is_trial;
|
const isTrial = subscription?.is_trial;
|
||||||
const isDaily = subscription?.is_daily;
|
const isDaily = subscription?.is_daily;
|
||||||
|
|
||||||
|
|||||||
@@ -82,7 +82,10 @@ export default function SubscriptionListCard({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isTrial = subscription.is_trial;
|
const isTrial = subscription.is_trial;
|
||||||
const isActive = subscription.status === 'active' || subscription.status === 'trial';
|
const isActive =
|
||||||
|
subscription.status === 'active' ||
|
||||||
|
subscription.status === 'trial' ||
|
||||||
|
subscription.status === 'limited';
|
||||||
const isExpired = subscription.status === 'expired' || subscription.status === 'disabled';
|
const isExpired = subscription.status === 'expired' || subscription.status === 'disabled';
|
||||||
const trafficLimit = subscription.traffic_limit_gb;
|
const trafficLimit = subscription.traffic_limit_gb;
|
||||||
const trafficUsed = subscription.traffic_used_gb;
|
const trafficUsed = subscription.traffic_used_gb;
|
||||||
@@ -95,13 +98,17 @@ export default function SubscriptionListCard({
|
|||||||
const trafficColor =
|
const trafficColor =
|
||||||
trafficPercent >= 90 ? 'bg-red-400' : trafficPercent >= 70 ? 'bg-amber-400' : 'bg-emerald-400';
|
trafficPercent >= 90 ? 'bg-red-400' : trafficPercent >= 70 ? 'bg-amber-400' : 'bg-emerald-400';
|
||||||
|
|
||||||
const borderColor = isTrial
|
const isLimitedStatus = subscription.status === 'limited';
|
||||||
|
|
||||||
|
const borderColor =
|
||||||
|
isTrial || isLimitedStatus
|
||||||
? 'rgba(251,191,36,0.2)'
|
? 'rgba(251,191,36,0.2)'
|
||||||
: isExpired
|
: isExpired
|
||||||
? 'rgba(255,59,92,0.15)'
|
? 'rgba(255,59,92,0.15)'
|
||||||
: g.cardBorder;
|
: g.cardBorder;
|
||||||
|
|
||||||
const bgColor = isTrial
|
const bgColor =
|
||||||
|
isTrial || isLimitedStatus
|
||||||
? isDark
|
? isDark
|
||||||
? 'rgba(251,191,36,0.04)'
|
? 'rgba(251,191,36,0.04)'
|
||||||
: 'rgba(251,191,36,0.03)'
|
: 'rgba(251,191,36,0.03)'
|
||||||
|
|||||||
@@ -272,7 +272,8 @@
|
|||||||
"tariffs": "Tariffs",
|
"tariffs": "Tariffs",
|
||||||
"traffic": "Traffic",
|
"traffic": "Traffic",
|
||||||
"devices": "Devices",
|
"devices": "Devices",
|
||||||
"expiredDate": "Expired"
|
"expiredDate": "Expired",
|
||||||
|
"activeUntil": "Active until"
|
||||||
},
|
},
|
||||||
"suspended": {
|
"suspended": {
|
||||||
"title": "Subscription Suspended",
|
"title": "Subscription Suspended",
|
||||||
@@ -554,8 +555,8 @@
|
|||||||
"unlimited": "Unlimited",
|
"unlimited": "Unlimited",
|
||||||
"buyTrafficGb": "Buy {{gb}} GB",
|
"buyTrafficGb": "Buy {{gb}} GB",
|
||||||
"buyUnlimited": "Buy Unlimited",
|
"buyUnlimited": "Buy Unlimited",
|
||||||
"manageServers": "Server management",
|
"manageServers": "Squad management",
|
||||||
"manageServersTitle": "Server management"
|
"manageServersTitle": "Squad management"
|
||||||
},
|
},
|
||||||
"serverManagement": {
|
"serverManagement": {
|
||||||
"statusLegend": "✅ — connected • ➕ — will be added (paid) • ➖ — will be disconnected",
|
"statusLegend": "✅ — connected • ➕ — will be added (paid) • ➖ — will be disconnected",
|
||||||
@@ -1099,7 +1100,7 @@
|
|||||||
"apps": "Apps",
|
"apps": "Apps",
|
||||||
"wheel": "Wheel",
|
"wheel": "Wheel",
|
||||||
"tariffs": "Tariffs",
|
"tariffs": "Tariffs",
|
||||||
"servers": "Servers",
|
"servers": "Squads",
|
||||||
"banSystem": "Ban Monitoring",
|
"banSystem": "Ban Monitoring",
|
||||||
"broadcasts": "Broadcasts",
|
"broadcasts": "Broadcasts",
|
||||||
"users": "Users",
|
"users": "Users",
|
||||||
@@ -2350,8 +2351,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"servers": {
|
"servers": {
|
||||||
"title": "Server Management",
|
"title": "Squad Management",
|
||||||
"subtitle": "Configure VPN servers",
|
"subtitle": "Configure squads",
|
||||||
"sync": "Sync",
|
"sync": "Sync",
|
||||||
"syncing": "Syncing...",
|
"syncing": "Syncing...",
|
||||||
"syncNow": "Sync now",
|
"syncNow": "Sync now",
|
||||||
|
|||||||
@@ -386,8 +386,8 @@
|
|||||||
"unlimited": "نامحدود",
|
"unlimited": "نامحدود",
|
||||||
"buyTrafficGb": "خرید {{gb}} GB",
|
"buyTrafficGb": "خرید {{gb}} GB",
|
||||||
"buyUnlimited": "خرید نامحدود",
|
"buyUnlimited": "خرید نامحدود",
|
||||||
"manageServers": "مدیریت سرورها",
|
"manageServers": "مدیریت اسکوادها",
|
||||||
"manageServersTitle": "مدیریت سرورها"
|
"manageServersTitle": "مدیریت اسکوادها"
|
||||||
},
|
},
|
||||||
"serverManagement": {
|
"serverManagement": {
|
||||||
"statusLegend": "✅ — متصل • ➕ — اضافه خواهد شد (پولی) • ➖ — قطع خواهد شد",
|
"statusLegend": "✅ — متصل • ➕ — اضافه خواهد شد (پولی) • ➖ — قطع خواهد شد",
|
||||||
@@ -924,7 +924,7 @@
|
|||||||
"apps": "برنامهها",
|
"apps": "برنامهها",
|
||||||
"wheel": "چرخ",
|
"wheel": "چرخ",
|
||||||
"tariffs": "تعرفهها",
|
"tariffs": "تعرفهها",
|
||||||
"servers": "سرورها",
|
"servers": "اسکوادها",
|
||||||
"broadcasts": "پیامرسانی",
|
"broadcasts": "پیامرسانی",
|
||||||
"emailTemplates": "قالبهای ایمیل",
|
"emailTemplates": "قالبهای ایمیل",
|
||||||
"paymentMethods": "روشهای پرداخت",
|
"paymentMethods": "روشهای پرداخت",
|
||||||
@@ -1986,8 +1986,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"servers": {
|
"servers": {
|
||||||
"title": "مدیریت سرورها",
|
"title": "مدیریت اسکوادها",
|
||||||
"subtitle": "پیکربندی سرورهای VPN",
|
"subtitle": "پیکربندی اسکوادها",
|
||||||
"sync": "همگامسازی",
|
"sync": "همگامسازی",
|
||||||
"syncing": "در حال همگامسازی...",
|
"syncing": "در حال همگامسازی...",
|
||||||
"syncNow": "همگامسازی الان",
|
"syncNow": "همگامسازی الان",
|
||||||
|
|||||||
@@ -284,7 +284,8 @@
|
|||||||
"tariffs": "Тарифы",
|
"tariffs": "Тарифы",
|
||||||
"traffic": "Трафик",
|
"traffic": "Трафик",
|
||||||
"devices": "Устройства",
|
"devices": "Устройства",
|
||||||
"expiredDate": "Истекла"
|
"expiredDate": "Истекла",
|
||||||
|
"activeUntil": "Активна до"
|
||||||
},
|
},
|
||||||
"suspended": {
|
"suspended": {
|
||||||
"title": "Подписка приостановлена",
|
"title": "Подписка приостановлена",
|
||||||
@@ -582,8 +583,8 @@
|
|||||||
"unlimited": "Безлимит",
|
"unlimited": "Безлимит",
|
||||||
"buyTrafficGb": "Купить {{gb}} ГБ",
|
"buyTrafficGb": "Купить {{gb}} ГБ",
|
||||||
"buyUnlimited": "Купить Безлимит",
|
"buyUnlimited": "Купить Безлимит",
|
||||||
"manageServers": "Управление серверами",
|
"manageServers": "Управление сквадами",
|
||||||
"manageServersTitle": "Управление серверами"
|
"manageServersTitle": "Управление сквадами"
|
||||||
},
|
},
|
||||||
"serverManagement": {
|
"serverManagement": {
|
||||||
"statusLegend": "✅ — подключено • ➕ — будет добавлено (платно) • ➖ — будет отключено",
|
"statusLegend": "✅ — подключено • ➕ — будет добавлено (платно) • ➖ — будет отключено",
|
||||||
@@ -1120,7 +1121,7 @@
|
|||||||
"apps": "Приложения",
|
"apps": "Приложения",
|
||||||
"wheel": "Колесо",
|
"wheel": "Колесо",
|
||||||
"tariffs": "Тарифы",
|
"tariffs": "Тарифы",
|
||||||
"servers": "Серверы",
|
"servers": "Сквады",
|
||||||
"banSystem": "Мониторинг банов",
|
"banSystem": "Мониторинг банов",
|
||||||
"broadcasts": "Рассылки",
|
"broadcasts": "Рассылки",
|
||||||
"users": "Пользователи",
|
"users": "Пользователи",
|
||||||
@@ -2856,8 +2857,8 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"servers": {
|
"servers": {
|
||||||
"title": "Управление серверами",
|
"title": "Управление сквадами",
|
||||||
"subtitle": "Настройка серверов VPN",
|
"subtitle": "Настройка сквадов",
|
||||||
"sync": "Синхронизировать",
|
"sync": "Синхронизировать",
|
||||||
"syncing": "Синхронизация...",
|
"syncing": "Синхронизация...",
|
||||||
"syncNow": "Синхронизировать сейчас",
|
"syncNow": "Синхронизировать сейчас",
|
||||||
|
|||||||
@@ -386,8 +386,8 @@
|
|||||||
"unlimited": "无限制",
|
"unlimited": "无限制",
|
||||||
"buyTrafficGb": "购买 {{gb}} GB",
|
"buyTrafficGb": "购买 {{gb}} GB",
|
||||||
"buyUnlimited": "购买无限流量",
|
"buyUnlimited": "购买无限流量",
|
||||||
"manageServers": "服务器管理",
|
"manageServers": "小队管理",
|
||||||
"manageServersTitle": "服务器管理"
|
"manageServersTitle": "小队管理"
|
||||||
},
|
},
|
||||||
"serverManagement": {
|
"serverManagement": {
|
||||||
"statusLegend": "✅ — 已连接 • ➕ — 将添加(付费)• ➖ — 将断开",
|
"statusLegend": "✅ — 已连接 • ➕ — 将添加(付费)• ➖ — 将断开",
|
||||||
@@ -924,7 +924,7 @@
|
|||||||
"apps": "应用",
|
"apps": "应用",
|
||||||
"wheel": "转盘",
|
"wheel": "转盘",
|
||||||
"tariffs": "套餐",
|
"tariffs": "套餐",
|
||||||
"servers": "服务器",
|
"servers": "小队",
|
||||||
"broadcasts": "群发",
|
"broadcasts": "群发",
|
||||||
"emailTemplates": "邮件模板",
|
"emailTemplates": "邮件模板",
|
||||||
"paymentMethods": "支付方式",
|
"paymentMethods": "支付方式",
|
||||||
|
|||||||
@@ -186,14 +186,14 @@ export default function AdminServers() {
|
|||||||
onClick={() => toggleMutation.mutate(server.id)}
|
onClick={() => toggleMutation.mutate(server.id)}
|
||||||
className={`rounded-lg p-2 transition-colors ${
|
className={`rounded-lg p-2 transition-colors ${
|
||||||
server.is_available
|
server.is_available
|
||||||
? 'bg-success-500/20 text-success-400 hover:bg-success-500/30'
|
? 'bg-error-500/20 text-error-400 hover:bg-error-500/30'
|
||||||
: 'bg-dark-700 text-dark-400 hover:bg-dark-600'
|
: 'bg-success-500/20 text-success-400 hover:bg-success-500/30'
|
||||||
}`}
|
}`}
|
||||||
title={
|
title={
|
||||||
server.is_available ? t('admin.servers.disable') : t('admin.servers.enable')
|
server.is_available ? t('admin.servers.disable') : t('admin.servers.enable')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{server.is_available ? <CheckIcon /> : <XIcon />}
|
{server.is_available ? <XIcon /> : <CheckIcon />}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* Toggle Trial */}
|
{/* Toggle Trial */}
|
||||||
@@ -201,7 +201,7 @@ export default function AdminServers() {
|
|||||||
onClick={() => toggleTrialMutation.mutate(server.id)}
|
onClick={() => toggleTrialMutation.mutate(server.id)}
|
||||||
className={`rounded-lg p-2 transition-colors ${
|
className={`rounded-lg p-2 transition-colors ${
|
||||||
server.is_trial_eligible
|
server.is_trial_eligible
|
||||||
? 'bg-accent-500/20 text-accent-400 hover:bg-accent-500/30'
|
? 'bg-warning-500/20 text-warning-400 hover:bg-warning-500/30'
|
||||||
: 'bg-dark-700 text-dark-400 hover:bg-dark-600'
|
: 'bg-dark-700 text-dark-400 hover:bg-dark-600'
|
||||||
}`}
|
}`}
|
||||||
title={t('admin.servers.toggleTrial')}
|
title={t('admin.servers.toggleTrial')}
|
||||||
|
|||||||
@@ -1658,7 +1658,10 @@ export default function AdminUserDetail() {
|
|||||||
.filter((tariffItem) => {
|
.filter((tariffItem) => {
|
||||||
const purchasedIds = new Set(
|
const purchasedIds = new Set(
|
||||||
userSubscriptions
|
userSubscriptions
|
||||||
.filter((s) => s.is_active || s.status === 'trial')
|
.filter(
|
||||||
|
(s) =>
|
||||||
|
s.is_active || s.status === 'trial' || s.status === 'limited',
|
||||||
|
)
|
||||||
.map((s) => s.tariff_id),
|
.map((s) => s.tariff_id),
|
||||||
);
|
);
|
||||||
return !purchasedIds.has(tariffItem.id);
|
return !purchasedIds.has(tariffItem.id);
|
||||||
|
|||||||
@@ -516,35 +516,14 @@ function BuyTabContent({
|
|||||||
const [selectedSubOption, setSelectedSubOption] = useState<string | null>(null);
|
const [selectedSubOption, setSelectedSubOption] = useState<string | null>(null);
|
||||||
const [submitError, setSubmitError] = useState<string | null>(null);
|
const [submitError, setSubmitError] = useState<string | null>(null);
|
||||||
|
|
||||||
// Collect ALL unique periods across ALL tariffs
|
|
||||||
const allPeriods = useMemo(() => {
|
|
||||||
const periodMap = new Map<number, GiftTariffPeriod>();
|
|
||||||
for (const tariff of config.tariffs) {
|
|
||||||
for (const period of tariff.periods) {
|
|
||||||
if (!periodMap.has(period.days)) {
|
|
||||||
periodMap.set(period.days, period);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Array.from(periodMap.values()).sort((a, b) => a.days - b.days);
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
// Filter tariffs to only those that have the selected period
|
|
||||||
const visibleTariffs = useMemo(() => {
|
|
||||||
if (!selectedPeriodDays) return config.tariffs;
|
|
||||||
return config.tariffs.filter((tariff) =>
|
|
||||||
tariff.periods.some((p) => p.days === selectedPeriodDays),
|
|
||||||
);
|
|
||||||
}, [config, selectedPeriodDays]);
|
|
||||||
|
|
||||||
// Auto-select first tariff, period, method on config load
|
// Auto-select first tariff, period, method on config load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (allPeriods.length > 0 && selectedPeriodDays === null) {
|
if (config.tariffs.length > 0 && selectedTariffId === null) {
|
||||||
setSelectedPeriodDays(allPeriods[0].days);
|
const firstTariff = config.tariffs[0];
|
||||||
|
setSelectedTariffId(firstTariff.id);
|
||||||
|
if (firstTariff.periods.length > 0 && selectedPeriodDays === null) {
|
||||||
|
setSelectedPeriodDays(firstTariff.periods[0].days);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visibleTariffs.length > 0 && selectedTariffId === null) {
|
|
||||||
setSelectedTariffId(visibleTariffs[0].id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.payment_methods.length > 0 && selectedMethod === null) {
|
if (config.payment_methods.length > 0 && selectedMethod === null) {
|
||||||
@@ -556,16 +535,19 @@ function BuyTabContent({
|
|||||||
setSelectedSubOption(null);
|
setSelectedSubOption(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [config, allPeriods, visibleTariffs, selectedTariffId, selectedPeriodDays, selectedMethod]);
|
}, [config, selectedTariffId, selectedPeriodDays, selectedMethod]);
|
||||||
|
|
||||||
// When period changes, auto-select first visible tariff if current is hidden
|
// When tariff changes, auto-select its first period
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!visibleTariffs.length) return;
|
if (!selectedTariffId) return;
|
||||||
const currentVisible = visibleTariffs.find((tariff) => tariff.id === selectedTariffId);
|
const tariff = config.tariffs.find((t) => t.id === selectedTariffId);
|
||||||
if (!currentVisible) {
|
if (tariff && tariff.periods.length > 0) {
|
||||||
setSelectedTariffId(visibleTariffs[0].id);
|
const hasCurrent = tariff.periods.some((p) => p.days === selectedPeriodDays);
|
||||||
|
if (!hasCurrent) {
|
||||||
|
setSelectedPeriodDays(tariff.periods[0].days);
|
||||||
}
|
}
|
||||||
}, [visibleTariffs, selectedTariffId]);
|
}
|
||||||
|
}, [selectedTariffId, config.tariffs, selectedPeriodDays]);
|
||||||
|
|
||||||
// Derived data
|
// Derived data
|
||||||
const selectedTariff = useMemo(
|
const selectedTariff = useMemo(
|
||||||
@@ -661,15 +643,15 @@ function BuyTabContent({
|
|||||||
return `${t('gift.fromBalance')} (${formatPrice(config.balance_kopeks)})`;
|
return `${t('gift.fromBalance')} (${formatPrice(config.balance_kopeks)})`;
|
||||||
}, [config, t]);
|
}, [config, t]);
|
||||||
|
|
||||||
const showTariffCards = visibleTariffs.length > 1;
|
const showTariffCards = config.tariffs.length > 1;
|
||||||
|
|
||||||
// Periods for the selected tariff (for period cards)
|
// Periods for the selected tariff (for period cards)
|
||||||
const periodsForDisplay = useMemo(() => {
|
const periodsForDisplay = useMemo(() => {
|
||||||
if (selectedTariff) {
|
if (selectedTariff) {
|
||||||
return [...selectedTariff.periods].sort((a, b) => a.days - b.days);
|
return [...selectedTariff.periods].sort((a, b) => a.days - b.days);
|
||||||
}
|
}
|
||||||
return allPeriods;
|
return [];
|
||||||
}, [selectedTariff, allPeriods]);
|
}, [selectedTariff]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
@@ -680,7 +662,7 @@ function BuyTabContent({
|
|||||||
{t('gift.selectTariff')}
|
{t('gift.selectTariff')}
|
||||||
</h2>
|
</h2>
|
||||||
<div role="radiogroup" aria-label={t('gift.chooseTariff')} className="space-y-2">
|
<div role="radiogroup" aria-label={t('gift.chooseTariff')} className="space-y-2">
|
||||||
{visibleTariffs.map((tariff) => (
|
{config.tariffs.map((tariff) => (
|
||||||
<TariffCard
|
<TariffCard
|
||||||
key={tariff.id}
|
key={tariff.id}
|
||||||
tariff={tariff}
|
tariff={tariff}
|
||||||
|
|||||||
@@ -957,7 +957,7 @@ export default function Subscription() {
|
|||||||
<div className="mb-5">
|
<div className="mb-5">
|
||||||
<CountdownTimer
|
<CountdownTimer
|
||||||
endDate={subscription.end_date}
|
endDate={subscription.end_date}
|
||||||
isActive={subscription.is_active}
|
isActive={subscription.is_active || subscription.is_limited}
|
||||||
glassColors={g}
|
glassColors={g}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -1316,7 +1316,11 @@ export default function Subscription() {
|
|||||||
<PurchaseCTAButton subscription={subscription} isMultiTariff={isMultiTariff} />
|
<PurchaseCTAButton subscription={subscription} isMultiTariff={isMultiTariff} />
|
||||||
|
|
||||||
{/* Delete expired subscription */}
|
{/* Delete expired subscription */}
|
||||||
{isMultiTariff && subscription && !subscription.is_active && !subscription.is_trial && (
|
{isMultiTariff &&
|
||||||
|
subscription &&
|
||||||
|
!subscription.is_active &&
|
||||||
|
!subscription.is_trial &&
|
||||||
|
!subscription.is_limited && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{!showDeleteSheet ? (
|
{!showDeleteSheet ? (
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -781,7 +781,7 @@ export default function SubscriptionPurchase() {
|
|||||||
!isCurrentTariff &&
|
!isCurrentTariff &&
|
||||||
!subscription.is_trial &&
|
!subscription.is_trial &&
|
||||||
!isSubscriptionExpired &&
|
!isSubscriptionExpired &&
|
||||||
subscription.is_active;
|
(subscription.is_active || subscription.is_limited);
|
||||||
const isLegacySubscription =
|
const isLegacySubscription =
|
||||||
subscription && !subscription.is_trial && !subscription.tariff_id;
|
subscription && !subscription.is_trial && !subscription.tariff_id;
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,28 @@ export function formatUptime(seconds: number): string {
|
|||||||
return `${minutes}m`;
|
return `${minutes}m`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatPrice(kopeks: number): string {
|
import i18next from 'i18next';
|
||||||
|
|
||||||
|
const LANG_CURRENCY_MAP: Record<string, { currency: string; locale: string; symbol: string }> = {
|
||||||
|
ru: { currency: 'RUB', locale: 'ru-RU', symbol: '₽' },
|
||||||
|
en: { currency: 'USD', locale: 'en-US', symbol: '$' },
|
||||||
|
zh: { currency: 'CNY', locale: 'zh-CN', symbol: '¥' },
|
||||||
|
fa: { currency: 'IRR', locale: 'fa-IR', symbol: '﷼' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_CURRENCY = { currency: 'RUB', locale: 'ru-RU', symbol: '₽' };
|
||||||
|
|
||||||
|
export function formatPrice(kopeks: number, lang?: string): string {
|
||||||
|
const resolvedLang = lang || i18next.language || 'ru';
|
||||||
|
const config = LANG_CURRENCY_MAP[resolvedLang] || DEFAULT_CURRENCY;
|
||||||
const rubles = kopeks / 100;
|
const rubles = kopeks / 100;
|
||||||
return new Intl.NumberFormat('ru-RU', {
|
try {
|
||||||
|
return new Intl.NumberFormat(config.locale, {
|
||||||
style: 'currency',
|
style: 'currency',
|
||||||
currency: 'RUB',
|
currency: config.currency,
|
||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
}).format(rubles);
|
}).format(rubles);
|
||||||
|
} catch {
|
||||||
|
return `${Math.round(rubles)} ${config.symbol}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user