diff --git a/src/features/control-center/pages/InstallationsPage.tsx b/src/features/control-center/pages/InstallationsPage.tsx index cc14291..5b1fcfa 100644 --- a/src/features/control-center/pages/InstallationsPage.tsx +++ b/src/features/control-center/pages/InstallationsPage.tsx @@ -5,10 +5,12 @@ import { Monitor, Globe, AlertTriangle, + Radio, } from 'lucide-react'; import { useKccStore } from '../store/kccStore'; import { StatusBadge } from '../components/StatusBadge'; import { InstallationKind, InstallationStatus } from '../contracts'; +import { RU_PLATFORMS, RU_INSTALLATION_STATUS } from '../locales/ru'; export const InstallationsPage: React.FC = () => { const installations = useKccStore((state) => state.installations); @@ -18,11 +20,13 @@ export const InstallationsPage: React.FC = () => { const [statusFilter, setStatusFilter] = useState<'all' | InstallationStatus>('all'); const filtered = installations.filter((item) => { + const platformRu = RU_PLATFORMS[item.kind] || ''; const matchSearch = !search || item.deviceName.toLowerCase().includes(search.toLowerCase()) || item.tenantName.toLowerCase().includes(search.toLowerCase()) || - item.appVersion.includes(search); + item.appVersion.includes(search) || + platformRu.toLowerCase().includes(search.toLowerCase()); const matchPlatform = platformFilter === 'all' || item.kind === platformFilter; const matchStatus = statusFilter === 'all' || item.status === statusFilter; return matchSearch && matchPlatform && matchStatus; @@ -35,46 +39,50 @@ export const InstallationsPage: React.FC = () => { const getPlatformIcon = (kind: InstallationKind) => { switch (kind) { case 'android': - return ; + return ; case 'desktop': - return ; + return ; case 'web': - return ; + return ; } }; return ( -
+
{/* Header */} -
+
-

- +

+ Установки и клиентские приложения

-

+

Мониторинг клиентских устройств Web, Desktop и Android (ТСД терминалы сбора данных и кассы)

-
- Всего установок: {installations.length} (найдено:{' '} - {filtered.length}) +
+ + Всего: {installations.length} + + + Найдено: {filtered.length} +
- {/* Outdated Android Callout (Сценарий 4) */} + {/* Outdated Android Callout */} {outdatedAndroidList.length > 0 && ( -
+
-
+
-
+
Обнаружены устаревшие Android-установки ({outdatedAndroidList.length} устройств)
-
+
Терминалы сбора данных Zebra, Honeywell и кассы Эвотор работают на устаревших версиях клиента (v1.0.4 – v1.2.0). Рекомендуется запланировать push-обновление APK пакета через MDM канал.
@@ -87,7 +95,7 @@ export const InstallationsPage: React.FC = () => { setPlatformFilter('android'); setStatusFilter('outdated'); }} - className="shrink-0 px-3 py-1.5 rounded-lg bg-amber-500 hover:bg-amber-400 text-zinc-950 font-bold text-xs transition-colors" + className="shrink-0 min-h-[44px] px-4 py-2 rounded-lg bg-amber-500 hover:bg-amber-400 text-zinc-950 font-bold text-xs transition-colors shadow-xs" > Показать только устаревшие Android @@ -98,20 +106,20 @@ export const InstallationsPage: React.FC = () => {
{/* Search */}
- + setSearch(e.target.value)} placeholder="Поиск по устройству, организации или версии..." - className="w-full bg-[#0b1d16] border border-zinc-800 rounded-lg pl-9 pr-3 py-2 text-xs text-zinc-100 placeholder-zinc-500 focus:outline-hidden focus:border-amber-500 transition-colors" + className="w-full bg-krona-workspace border border-krona-border rounded-lg pl-9 pr-3 py-2.5 text-xs text-krona-primary placeholder:text-krona-secondary/60 focus:outline-hidden focus:border-amber-500 transition-colors min-h-[44px]" />
{/* Platform & Status Filters */}
-
- Платформа: +
+ Платформа: {[ { id: 'all', label: 'Все' }, { id: 'android', label: 'Android' }, @@ -121,10 +129,10 @@ export const InstallationsPage: React.FC = () => {
-
- Статус: +
+ Статус: {[ { id: 'all', label: 'Все' }, { id: 'outdated', label: 'Устарели' }, @@ -143,10 +151,10 @@ export const InstallationsPage: React.FC = () => {
- {/* Installations Table */} -
+ {/* Mobile Cards View (block lg:hidden) */} +
+ {filtered.map((item) => { + const platformLabel = RU_PLATFORMS[item.kind] || item.kind; + const statusLabel = RU_INSTALLATION_STATUS[item.status] || item.status; + + return ( +
+
+
+
+ {getPlatformIcon(item.kind)} + {item.deviceName} +
+
+ {item.tenantName} +
+
+ +
+ +
+
+ Версия клиента + v{item.appVersion} +
+
+ ОС устройства + {item.osVersion} +
+
+ +
+ + + {item.ipAddressMasked} + + + Активность: {new Date(item.lastSeenAt).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' })} + +
+
+ ); + })} + + {filtered.length === 0 && ( +
+ Установки не найдены по выбранным критериям +
+ )} +
+ + {/* Desktop Installations Table (hidden lg:block) */} +
- + - + @@ -172,23 +235,27 @@ export const InstallationsPage: React.FC = () => { - + {filtered.map((item) => ( - + - - - - + + + + - - + ))} {filtered.length === 0 && ( -
Платформа Устройство / МодельОрганизация (Tenant)Организация Версия клиента ОС устройства Статус поддержкиСегмент IP
-
+
{getPlatformIcon(item.kind)} - {item.kind} + {RU_PLATFORMS[item.kind] || item.kind}
{item.deviceName}{item.tenantName}v{item.appVersion}{item.osVersion}{item.deviceName}{item.tenantName}v{item.appVersion}{item.osVersion} - + + {new Date(item.lastSeenAt).toLocaleString('ru-RU', { day: '2-digit', month: '2-digit', @@ -196,13 +263,13 @@ export const InstallationsPage: React.FC = () => { minute: '2-digit', })} {item.ipAddressMasked}{item.ipAddressMasked}
+ Установки не найдены по выбранным критериям фильтрации