mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
feat: блокировка кнопки устройств при лимите + убраны дубли трафика
- Кнопка «Подключить устройство» заблокирована при достижении лимита - Haptic feedback при нажатии на заблокированную кнопку - Предупреждение «Отключите устройства для подключения новых» - Убран дублирующийся текст трафика под прогресс-баром (безлимит)
This commit is contained in:
@@ -10,6 +10,7 @@ import { useTrafficZone } from '../../hooks/useTrafficZone';
|
||||
import { formatTraffic } from '../../utils/formatTraffic';
|
||||
import { getGlassColors } from '../../utils/glassTheme';
|
||||
import { HoverBorderGradient } from '../ui/hover-border-gradient';
|
||||
import { useHaptic } from '../../platform';
|
||||
import type { Subscription } from '../../types';
|
||||
|
||||
interface SubscriptionCardActiveProps {
|
||||
@@ -58,6 +59,10 @@ export default function SubscriptionCardActive({
|
||||
const isUnlimited = trafficData?.is_unlimited ?? subscription.traffic_limit_gb === 0;
|
||||
const zone = useTrafficZone(usedPercent);
|
||||
const animatedPercent = useAnimatedNumber(usedPercent);
|
||||
const haptic = useHaptic();
|
||||
|
||||
const isAtDeviceLimit =
|
||||
subscription.device_limit > 0 && connectedDevices >= subscription.device_limit;
|
||||
|
||||
const formattedDate = new Date(subscription.end_date).toLocaleDateString();
|
||||
const daysLeft = subscription.days_left;
|
||||
@@ -203,8 +208,15 @@ export default function SubscriptionCardActive({
|
||||
<HoverBorderGradient
|
||||
as="button"
|
||||
accentColor={zone.mainHex}
|
||||
onClick={() => navigate('/connection')}
|
||||
className="mb-2.5 flex w-full items-center gap-3.5 rounded-[14px] p-3.5 text-left transition-shadow duration-300"
|
||||
disabled={isAtDeviceLimit}
|
||||
onClick={() => {
|
||||
if (isAtDeviceLimit) {
|
||||
haptic.notification('error');
|
||||
return;
|
||||
}
|
||||
navigate('/connection');
|
||||
}}
|
||||
className={`mb-2.5 flex w-full items-center gap-3.5 rounded-[14px] p-3.5 text-left transition-shadow duration-300${isAtDeviceLimit ? 'cursor-not-allowed opacity-50' : ''}`}
|
||||
data-onboarding="connect-devices"
|
||||
style={{ fontFamily: 'inherit' }}
|
||||
>
|
||||
@@ -243,6 +255,14 @@ export default function SubscriptionCardActive({
|
||||
max: subscription.device_limit,
|
||||
})}
|
||||
</div>
|
||||
{isAtDeviceLimit && (
|
||||
<div
|
||||
className="mt-1 text-[10px] font-medium"
|
||||
style={{ color: 'rgb(var(--color-warning-400))' }}
|
||||
>
|
||||
{t('dashboard.deviceLimitReached')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Device indicator */}
|
||||
|
||||
@@ -16,7 +16,7 @@ interface TrafficProgressBarProps {
|
||||
const THRESHOLDS = [50, 75, 90];
|
||||
|
||||
export default function TrafficProgressBar({
|
||||
usedGb,
|
||||
usedGb: _usedGb,
|
||||
limitGb,
|
||||
percent,
|
||||
isUnlimited,
|
||||
@@ -83,29 +83,6 @@ export default function TrafficProgressBar({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Below bar: label + usage */}
|
||||
{!compact && (
|
||||
<div className="mt-2 flex items-center justify-between px-0.5">
|
||||
<span
|
||||
className="flex items-center gap-1.5 text-[11px] font-semibold"
|
||||
style={{ color: zone.mainVar }}
|
||||
>
|
||||
<span
|
||||
className="inline-block h-1.5 w-1.5 animate-unlimited-pulse rounded-full"
|
||||
style={{
|
||||
background: zone.mainVar,
|
||||
boxShadow: `0 0 8px ${zone.mainVar}`,
|
||||
}}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{t('dashboard.unlimitedTraffic')}
|
||||
</span>
|
||||
<span className="font-mono text-[11px] text-dark-50/30">
|
||||
{t('dashboard.usedTraffic', { amount: formatTraffic(usedGb) })}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ interface HoverBorderGradientProps extends React.HTMLAttributes<HTMLElement> {
|
||||
accentColor?: string;
|
||||
/** Full rotation duration in seconds. Default: 3 */
|
||||
duration?: number;
|
||||
/** Disables the element (applies to interactive elements like buttons) */
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
"daysRemaining": "Days left",
|
||||
"usageLast14Days": "Usage last 14 days",
|
||||
"maxUsage": "max {{amount}}",
|
||||
"deviceLimitReached": "Disconnect devices to add new ones",
|
||||
"expired": {
|
||||
"title": "Subscription Expired",
|
||||
"trialTitle": "Trial Expired",
|
||||
|
||||
@@ -242,6 +242,7 @@
|
||||
"devices": "دستگاهها",
|
||||
"expiredDate": "منقضی شده"
|
||||
},
|
||||
"deviceLimitReached": "برای افزودن دستگاه جدید، دستگاههای فعلی را قطع کنید",
|
||||
"suspended": {
|
||||
"title": "اشتراک معلق شده",
|
||||
"resume": "از سرگیری"
|
||||
|
||||
@@ -266,6 +266,7 @@
|
||||
"daysRemaining": "Дней осталось",
|
||||
"usageLast14Days": "Расход за 14 дней",
|
||||
"maxUsage": "макс {{amount}}",
|
||||
"deviceLimitReached": "Отключите устройства для подключения новых",
|
||||
"expired": {
|
||||
"title": "Подписка истекла",
|
||||
"trialTitle": "Пробный период истёк",
|
||||
|
||||
@@ -242,6 +242,7 @@
|
||||
"devices": "设备",
|
||||
"expiredDate": "过期时间"
|
||||
},
|
||||
"deviceLimitReached": "断开设备以添加新设备",
|
||||
"suspended": {
|
||||
"title": "订阅已暂停",
|
||||
"resume": "恢复"
|
||||
|
||||
@@ -35,6 +35,12 @@
|
||||
box-shadow: 0 0 20px var(--accent-glow, rgba(var(--color-accent-400), 0.25));
|
||||
}
|
||||
|
||||
.hover-border-gradient:disabled,
|
||||
.hover-border-gradient[disabled] {
|
||||
box-shadow: none;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.light .hover-border-gradient {
|
||||
--_bg: var(--border-inner-bg, rgb(var(--color-champagne-100)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user