import 'package:flutter/material.dart'; import '../about/internal/application/TextType.dart'; import '../about/internal/application/ServerProfile.dart'; import '../about/internal/application/DeploymentRecord.dart'; import '../utils/Colors.dart'; import 'text/Text.dart'; /// Warm gradient "insights" card that surfaces computed tips from the data. class InsightsCard extends StatelessWidget { final List servers; final List history; final VoidCallback onViewHistory; const InsightsCard({ super.key, required this.servers, required this.history, required this.onViewHistory, }); List<(IconData, String)> _tips() { final tips = <(IconData, String)>[]; final failed = history .where((h) => h.status == DeployStatus.failure) .length; final totalServices = servers.fold(0, (n, s) => n + s.services.length); if (history.isEmpty) { tips.add(( Icons.rocket_launch, 'Add a service and run your first deploy to unlock insights.', )); } if (failed > 0) { tips.add(( Icons.warning_amber, '$failed failed ${failed == 1 ? 'deploy' : 'deploys'} — open the service logs to investigate.', )); } // Busiest server if (history.isNotEmpty) { final counts = {}; for (final h in history) { counts[h.serverName] = (counts[h.serverName] ?? 0) + 1; } final busiest = counts.entries.reduce( (a, b) => a.value >= b.value ? a : b, ); tips.add(( Icons.trending_up, '${busiest.key} is your busiest server (${busiest.value} deploys).', )); } final emptyServers = servers.where((s) => s.services.isEmpty).length; if (emptyServers > 0) { tips.add(( Icons.dns, '$emptyServers ${emptyServers == 1 ? 'server has' : 'servers have'} no services yet — add one to start deploying.', )); } if (totalServices > 0) { tips.add(( Icons.health_and_safety, 'Open Health to check the live status of your $totalServices ${totalServices == 1 ? 'service' : 'services'}.', )); } return tips.take(3).toList(); } @override Widget build(BuildContext context) { const ink = PeachTheme.ink; final tips = _tips(); return Container( padding: const EdgeInsets.all(18), decoration: BoxDecoration( gradient: PeachTheme.peachGradient, borderRadius: BorderRadius.circular(20), boxShadow: PeachTheme.glow(PeachTheme.seed, opacity: 0.18, blur: 30), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.auto_awesome, size: 18, color: ink), const SizedBox(width: 8), text( 'Insights', 16, TextType.Bold, color: ink, weight: FontWeight.w800, ), ], ), const SizedBox(height: 14), for (final (icon, tip) in tips) ...[ _tip(icon, tip), const SizedBox(height: 8), ], const SizedBox(height: 4), Align( alignment: Alignment.centerRight, child: FilledButton.icon( onPressed: onViewHistory, icon: const Icon(Icons.timeline, size: 16), label: const Text('View history'), style: FilledButton.styleFrom( backgroundColor: ink, foregroundColor: Colors.white, elevation: 0, padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 12, ), ), ), ), ], ), ); } Widget _tip(IconData icon, String tip) { return Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 11), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.55), borderRadius: BorderRadius.circular(12), ), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, size: 16, color: PeachTheme.ink.withValues(alpha: 0.7)), const SizedBox(width: 10), Expanded( child: text( tip, 12.5, TextType.Regular, color: PeachTheme.ink.withValues(alpha: 0.85), height: 1.35, weight: FontWeight.w600, ), ), ], ), ); } }