import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/helpers/widgets/my_card.dart'; import 'package:marco/helpers/widgets/my_spacing.dart'; import 'package:marco/controller/dashboard/dashboard_controller.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; import 'package:marco/helpers/widgets/my_text.dart'; // import MyText import 'package:intl/intl.dart'; class DashboardOverviewWidgets { static final DashboardController dashboardController = Get.find(); static const _titleTextStyle = TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Colors.black87, ); static const _subtitleTextStyle = TextStyle( fontSize: 14, color: Colors.grey, ); static const _infoNumberTextStyle = TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.black87, ); static const _infoNumberGreenTextStyle = TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ); static final NumberFormat _commaFormatter = NumberFormat.decimalPattern(); /// Teams Overview Card without chart, labels & values in rows static Widget teamsOverview() { return Obx(() { if (dashboardController.isTeamsLoading.value) { return _loadingSkeletonCard("Teams"); } final total = dashboardController.totalEmployees.value; final inToday = dashboardController.inToday.value; return LayoutBuilder( builder: (context, constraints) { final cardWidth = constraints.maxWidth > 400 ? (constraints.maxWidth / 2) - 10 : constraints.maxWidth; return SizedBox( width: cardWidth, child: MyCard( borderRadiusAll: 16, paddingAll: 20, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ const Icon(Icons.group, color: Colors.blueAccent, size: 26), MySpacing.width(8), MyText("Teams", style: _titleTextStyle), ], ), MySpacing.height(16), // Labels in one row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText("Total Employees", style: _subtitleTextStyle), MyText("In Today", style: _subtitleTextStyle), ], ), MySpacing.height(4), // Values in one row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ MyText(_commaFormatter.format(total), style: _infoNumberTextStyle), MyText(_commaFormatter.format(inToday), style: _infoNumberGreenTextStyle.copyWith( color: Colors.green[700])), ], ), ], ), ), ); }, ); }); } /// Tasks Overview Card static Widget tasksOverview() { return Obx(() { if (dashboardController.isTasksLoading.value) { return _loadingSkeletonCard("Tasks"); } final total = dashboardController.totalTasks.value; final completed = dashboardController.completedTasks.value; final remaining = total - completed; final double percent = total > 0 ? completed / total : 0.0; // Task colors const completedColor = Color(0xFF64B5F6); const remainingColor =Color(0xFFE57373); final List<_ChartData> pieData = [ _ChartData('Completed', completed.toDouble(), completedColor), _ChartData('Remaining', remaining.toDouble(), remainingColor), ]; return LayoutBuilder( builder: (context, constraints) { final cardWidth = constraints.maxWidth < 300 ? constraints.maxWidth : 300.0; return SizedBox( width: cardWidth, child: MyCard( borderRadiusAll: 16, paddingAll: 20, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Icon + Title Row( children: [ const Icon(Icons.task_alt, color: completedColor, size: 26), MySpacing.width(8), MyText("Tasks", style: _titleTextStyle), ], ), MySpacing.height(16), // Main Row: Bigger Pie Chart + Full-Color Info Boxes Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ // Pie Chart Column (Bigger) SizedBox( height: 140, width: 140, child: SfCircularChart( annotations: [ CircularChartAnnotation( widget: MyText( "${(percent * 100).toInt()}%", style: _infoNumberGreenTextStyle.copyWith( fontSize: 20), ), ), ], series: >[ PieSeries<_ChartData, String>( dataSource: pieData, xValueMapper: (_ChartData data, _) => data.category, yValueMapper: (_ChartData data, _) => data.value, pointColorMapper: (_ChartData data, _) => data.color, dataLabelSettings: const DataLabelSettings(isVisible: false), radius: '100%', ), ], ), ), MySpacing.width(16), // Info Boxes Column (Full Color) Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _infoBoxFullColor( "Completed", completed, completedColor), MySpacing.height(8), _infoBoxFullColor( "Remaining", remaining, remainingColor), ], ), ), ], ), ], ), ), ); }, ); }); } /// Full-color info box static Widget _infoBoxFullColor(String label, int value, Color bgColor) { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 12), decoration: BoxDecoration( color: bgColor, // full color borderRadius: BorderRadius.circular(12), ), child: Column( children: [ MyText(_commaFormatter.format(value), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white, )), MySpacing.height(2), MyText(label, style: const TextStyle( fontSize: 12, color: Colors.white, // text in white for contrast )), ], ), ); } /// Loading Skeleton Card static Widget _loadingSkeletonCard(String title) { return LayoutBuilder(builder: (context, constraints) { final cardWidth = constraints.maxWidth < 200 ? constraints.maxWidth : 200.0; return SizedBox( width: cardWidth, child: MyCard( borderRadiusAll: 16, paddingAll: 20, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _loadingBar(width: 100), MySpacing.height(12), _loadingBar(width: 80), MySpacing.height(12), _loadingBar(width: double.infinity, height: 12), ], ), ), ); }); } static Widget _loadingBar( {double width = double.infinity, double height = 16}) { return Container( height: height, width: width, decoration: BoxDecoration( color: Colors.grey.shade300, borderRadius: BorderRadius.circular(6), ), ); } } class _ChartData { final String category; final double value; final Color color; _ChartData(this.category, this.value, this.color); }