From 06c7d1388308de5fc1adad0cd2697d3c03c41c49 Mon Sep 17 00:00:00 2001 From: Case211 <87642841+Case211@users.noreply.github.com> Date: Wed, 5 Aug 2026 00:30:40 +0500 Subject: [PATCH] =?UTF-8?q?feat(dashboard):=20=D0=BF=D0=BB=D0=B8=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=20=C2=AB=D0=9F=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D1=83=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81?= =?UTF-8?q?=D1=82=D0=B2=D0=BE=C2=BB=20=D0=BD=D0=B0=20=D0=B3=D0=BB=D0=B0?= =?UTF-8?q?=D0=B2=D0=BD=D0=BE=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Подписку может выдать бонус рекламной кампании — она создаётся автоматически, и человек попадает на главную с уже готовым доступом. Плитка подключения жила только в карточке подписки на отдельной странице, так что на главной ему нечем было воспользоваться: подписка есть, а как подключить устройство — не видно. Плитка вынесена из SubscriptionCardActive в отдельный компонент и переиспользована обоими экранами, второй копии разметки не появилось. На главной показывается, пока подписка одна: при нескольких выбор устройства неоднозначен, и уместнее уходить в конкретную подписку. --- .../dashboard/ConnectDeviceTile.tsx | 143 ++++++++++++++++++ .../dashboard/SubscriptionCardActive.tsx | 117 +------------- src/pages/Dashboard.tsx | 23 +++ 3 files changed, 172 insertions(+), 111 deletions(-) create mode 100644 src/components/dashboard/ConnectDeviceTile.tsx diff --git a/src/components/dashboard/ConnectDeviceTile.tsx b/src/components/dashboard/ConnectDeviceTile.tsx new file mode 100644 index 0000000..12a9387 --- /dev/null +++ b/src/components/dashboard/ConnectDeviceTile.tsx @@ -0,0 +1,143 @@ +import { useTranslation } from 'react-i18next'; +import { useNavigate } from 'react-router'; +import { useHaptic } from '../../platform'; +import { useTheme } from '../../hooks/useTheme'; +import { useTrafficZone } from '../../hooks/useTrafficZone'; +import { getGlassColors } from '../../utils/glassTheme'; +import { HoverBorderGradient } from '../ui/hover-border-gradient'; + +interface ConnectDeviceTileProps { + subscription: { + id: number; + device_limit: number; + subscription_url?: string | null; + }; + connectedDevices: number; + /** Процент израсходованного трафика — от него зависит акцентный цвет плитки. */ + usedPercent?: number; +} + +/** + * Плитка «Подключить устройство». + * + * Живёт и в карточке активной подписки, и на главной: пользователь, + * которому подписку выдал бонус рекламной кампании, попадает на главную + * с готовым доступом — и без этой плитки не понимает, что делать дальше. + */ +export default function ConnectDeviceTile({ + subscription, + connectedDevices, + usedPercent = 0, +}: ConnectDeviceTileProps) { + const { t } = useTranslation(); + const navigate = useNavigate(); + const haptic = useHaptic(); + const { isDark } = useTheme(); + const g = getGlassColors(isDark); + const zone = useTrafficZone(usedPercent); + + const isAtDeviceLimit = + subscription.device_limit > 0 && connectedDevices >= subscription.device_limit; + + if (!subscription.subscription_url) return null; + + return ( + { + if (isAtDeviceLimit) { + haptic.notification('error'); + return; + } + navigate(`/connection?sub=${subscription.id}`); + }} + 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' }} + > + {/* Monitor icon */} +
+ +
+ + {/* Text */} +
+
+ {t('dashboard.connectDevice')} +
+
+ {subscription.device_limit === 0 + ? t('dashboard.devicesConnectedUnlimited', { used: connectedDevices }) + : t('dashboard.devicesOfMax', { + used: connectedDevices, + max: subscription.device_limit, + })} +
+ {isAtDeviceLimit && ( +
+ {t('dashboard.deviceLimitReached')} +
+ )} +
+ + {/* Device indicator */} + {subscription.device_limit === 0 ? ( + + ) : subscription.device_limit <= 10 ? ( +