import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; import 'package:inventory_monitor_app/models/dashboard_models.dart'; import 'package:inventory_monitor_app/services/api_service.dart'; import 'package:inventory_monitor_app/widgets/chart_card.dart'; import 'package:inventory_monitor_app/widgets/kpi_card.dart'; class DashboardScreen extends StatefulWidget { const DashboardScreen({super.key}); @override State createState() => _DashboardScreenState(); } class _DashboardScreenState extends State { final ApiService _apiService = ApiService(); late Future _summaryFuture; late Future> _osDistributionFuture; late Future> _storageFuture; late Future> _adminsFuture; @override void initState() { super.initState(); _summaryFuture = _apiService.getDashboardSummary(); _osDistributionFuture = _apiService.getOsDistribution(); _storageFuture = _apiService.getStorageByType(); _adminsFuture = _apiService.getTopAdminAccounts(); } @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildKpiSection(), const SizedBox(height: 24), _buildChartSection(), ], ), ), ); } Widget _buildKpiSection() { return FutureBuilder( future: _summaryFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('Error: ${snapshot.error}')); } if (!snapshot.hasData) { return const Center(child: Text('No summary data available.')); } final summary = snapshot.data!; return Wrap( spacing: 16, runSpacing: 16, children: [ KpiCard( title: 'Total Devices', value: summary.totalDevices.toString(), icon: Icons.devices), KpiCard( title: 'Laptops', value: summary.totalLaptops.toString(), icon: Icons.laptop), KpiCard( title: 'Desktops', value: summary.totalDesktops.toString(), icon: Icons.desktop_windows), KpiCard( title: 'Servers', value: summary.totalServers.toString(), icon: Icons.dns), KpiCard( title: 'Printers w/ SN', value: summary.printersWithSerial.toString(), icon: Icons.print), KpiCard( title: 'Drives Failing', value: summary.drivesFailing.toString(), icon: Icons.error_outline, color: Colors.red.shade700), ], ); }, ); } Widget _buildChartSection() { return LayoutBuilder( builder: (context, constraints) { final crossAxisCount = constraints.maxWidth > 1200 ? 2 : 1; return GridView.count( crossAxisCount: crossAxisCount, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 16, mainAxisSpacing: 16, childAspectRatio: 1.8, children: [ _buildDeviceTypeChart(), _buildOsDistributionChart(), _buildStorageChart(), _buildAdminAccountsChart(), ], ); }, ); } Widget _buildDeviceTypeChart() { return FutureBuilder( future: _summaryFuture, builder: (context, snapshot) { if (!snapshot.hasData) return const ChartCard( title: 'Devices by Type', child: Center(child: CircularProgressIndicator())); final summary = snapshot.data!; return ChartCard( title: 'Devices by Type', child: PieChart( PieChartData( sections: [ PieChartSectionData( value: summary.totalLaptops.toDouble(), title: 'Laptops', color: Colors.blue, radius: 50), PieChartSectionData( value: summary.totalDesktops.toDouble(), title: 'Desktops', color: Colors.green, radius: 50), PieChartSectionData( value: summary.totalServers.toDouble(), title: 'Servers', color: Colors.orange, radius: 50), ], ), ), ); }, ); } Widget _buildOsDistributionChart() { return FutureBuilder>( future: _osDistributionFuture, builder: (context, snapshot) { if (!snapshot.hasData) return const ChartCard( title: 'OS Distribution', child: Center(child: CircularProgressIndicator())); final data = snapshot.data!; return ChartCard( title: 'OS Distribution', child: BarChart( BarChartData( barGroups: data.asMap().entries.map((entry) { return BarChartGroupData(x: entry.key, barRods: [ BarChartRodData( toY: entry.value.count.toDouble(), color: Colors.purple) ]); }).toList(), titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) => Text( data[value.toInt()].label.split(' ').last, style: const TextStyle(fontSize: 10)), reservedSize: 40)), leftTitles: const AxisTitles( sideTitles: SideTitles(showTitles: true, reservedSize: 40)), topTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), rightTitles: const AxisTitles(sideTitles: SideTitles(showTitles: false)), ), ), ), ); }, ); } Widget _buildStorageChart() { return FutureBuilder>( future: _storageFuture, builder: (context, snapshot) { if (!snapshot.hasData) return const ChartCard( title: 'Total Storage (TB)', child: Center(child: CircularProgressIndicator())); final data = snapshot.data!; return ChartCard( title: 'Total Storage (TB)', child: BarChart(BarChartData( barGroups: [ BarChartGroupData(x: 0, barRods: [ BarChartRodData( toY: data['ssd']!, color: Colors.cyan, width: 40) ], showingTooltipIndicators: [ 0 ]), BarChartGroupData(x: 1, barRods: [ BarChartRodData( toY: data['hdd']!, color: Colors.amber, width: 40) ], showingTooltipIndicators: [ 0 ]), ], titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) => Text(value == 0 ? 'SSD' : 'HDD')))), )), ); }, ); } Widget _buildAdminAccountsChart() { return FutureBuilder>( future: _adminsFuture, builder: (context, snapshot) { if (!snapshot.hasData) return const ChartCard( title: 'Top 5 Local Admins', child: Center(child: CircularProgressIndicator())); final data = snapshot.data!; return ChartCard( title: 'Top 5 Local Admins', child: BarChart(BarChartData( barGroups: data .asMap() .entries .map((e) => BarChartGroupData(x: e.key, barRods: [ BarChartRodData( toY: e.value.count.toDouble(), color: Colors.teal) ])) .toList(), titlesData: FlTitlesData( bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, getTitlesWidget: (value, meta) => Text( data[value.toInt()].label, style: const TextStyle(fontSize: 10)), reservedSize: 40))), )), ); }, ); } }