import 'dart:async'; import 'package:flutter/material.dart'; import '../../../core/network/signalr_service.dart'; import '../../../core/theme/app_theme.dart'; import '../../../core/widgets/glass_container.dart'; import '../models/admin_user_model.dart'; /// Modern KPI Header Card Row for Admin Panel Dashboard Overview. /// Displays real-time live metrics for Users, Administrators, Microservices Health, and MQTT Bus. /// Continuously updates live EXCLUSIVELY over SignalR WebSockets (`/hubs/health`). class AdminKpiHeader extends StatefulWidget { final List users; final SignalRService? signalRService; const AdminKpiHeader({ super.key, required this.users, this.signalRService, }); @override State createState() => _AdminKpiHeaderState(); } class _AdminKpiHeaderState extends State { int? _totalServices; int? _onlineServices; bool _mqttConnected = false; StreamSubscription>>? _healthSub; @override void initState() { super.initState(); if (widget.signalRService != null) { _healthSub = widget.signalRService!.healthStream.listen((data) { if (mounted && data.isNotEmpty) { final int total = data.length; final int online = data.where((item) => item['status']?.toString().toLowerCase() == 'online').length; setState(() { _totalServices = total; _onlineServices = online; _mqttConnected = online > 0; }); } }); } } @override void dispose() { _healthSub?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final totalUsers = widget.users.length; final activeUsers = widget.users.where((u) => u.isActive).length; final adminCount = widget.users.where((u) => u.role.toLowerCase() == 'admin').length; final isMobile = MediaQuery.of(context).size.width < 700; final String servicesValue = _totalServices != null ? '$_onlineServices / $_totalServices' : 'SignalR...'; final String servicesSubtitle = (_onlineServices == _totalServices && _totalServices != null && _totalServices! > 0 ? 'Alle Dienste online (WebSocket)' : (_onlineServices != null ? '$_onlineServices von $_totalServices erreichbar' : 'Verbinde WebSocket...')); final Color servicesColor = (_onlineServices == _totalServices && _totalServices != null && _totalServices! > 0) ? AppTheme.primaryEmerald : (_onlineServices != null && _onlineServices! > 0 ? AppTheme.accentCyan : AppTheme.accentRed); final cards = [ _KpiCard( title: 'Benutzer Gesamt', value: totalUsers.toString(), subtitle: '$activeUsers aktiv • ${totalUsers - activeUsers} gesperrt', icon: Icons.people_alt_rounded, accentColor: AppTheme.primaryEmerald, ), _KpiCard( title: 'Administratoren', value: adminCount.toString(), subtitle: 'Vollzugriff auf System', icon: Icons.admin_panel_settings_rounded, accentColor: const Color(0xFFA855F7), // Purple accent ), _KpiCard( title: 'Mikrodienste', value: servicesValue, subtitle: servicesSubtitle, icon: Icons.dns_rounded, accentColor: servicesColor, showPulse: _onlineServices != null && _onlineServices! > 0, ), _KpiCard( title: 'MQTT Live-Bus', value: _mqttConnected ? 'Aktiv' : 'Offline', subtitle: _mqttConnected ? 'SignalR & RPC Bereit' : 'Warte auf WebSocket', icon: Icons.sensors_rounded, accentColor: _mqttConnected ? const Color(0xFF10B981) : AppTheme.accentRed, ), ]; if (isMobile) { return GridView.count( crossAxisCount: 2, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 10, mainAxisSpacing: 10, childAspectRatio: 1.5, children: cards, ); } return Row( children: cards .map((card) => Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 4), child: card, ), )) .toList(), ); } } class _KpiCard extends StatelessWidget { final String title; final String value; final String subtitle; final IconData icon; final Color accentColor; final bool showPulse; const _KpiCard({ required this.title, required this.value, required this.subtitle, required this.icon, required this.accentColor, this.showPulse = false, }); @override Widget build(BuildContext context) { return GlassContainer( padding: const EdgeInsets.all(14), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: AppTheme.textMuted, ), ), Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( color: accentColor.withValues(alpha: 0.15), shape: BoxShape.circle, border: Border.all(color: accentColor.withValues(alpha: 0.3)), ), child: Icon(icon, size: 16, color: accentColor), ), ], ), const SizedBox(height: 6), Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: [ Text( value, style: TextStyle( fontSize: 22, fontWeight: FontWeight.bold, color: AppTheme.textPrimary, letterSpacing: -0.5, ), ), if (showPulse) ...[ const SizedBox(width: 8), Container( width: 8, height: 8, decoration: BoxDecoration( color: AppTheme.primaryEmerald, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: AppTheme.primaryEmerald.withValues(alpha: 0.6), blurRadius: 6, spreadRadius: 2, ), ], ), ), ], ], ), const SizedBox(height: 4), Text( subtitle, style: TextStyle( fontSize: 11, color: AppTheme.textSecondary, ), overflow: TextOverflow.ellipsis, ), ], ), ); } }