import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/src/widgets/framework.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_partner/presentation/providers/manager_provider.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/models/bar_data.dart'; import '../../../providers/userRoleProvider.dart'; import '../../../themes/charts/barChart.dart'; import '../../../themes/charts/stackedBarChart.dart'; class ProductivityDashboard extends ConsumerStatefulWidget { const ProductivityDashboard({super.key}); @override ConsumerState createState() => _ProductivityDashboardState(); } class _ProductivityDashboardState extends ConsumerState { dynamic managerId; late ApiService apiService; // List> dataVal = []; List> getProductivityDshBdData = []; List> originalData = []; List> filteredData = []; List> productWise = []; List> insurerWise = []; List> originalProductWise = []; List> filteredProductWise = []; List> originalStaffWise = []; List> filteredStaffWise = []; bool isLoading = false; bool isInsureWisePolicy = true; bool isProductWisePolicy = true; bool showStaff = true; bool showProduct = true; int selectedStaffIndex = 0; List> staffVehicleTypeNames = []; // 👈 Add this at the top with other state variables Map vehicleColors = {}; List staffLabels = []; List staffLabelsId = []; // List>> staffChartData = []; List>> staffChartData = []; double maxPremium = 0; int selectedView = 0; // 0 = Staff // 1 = Broker // 2 = Product String? SelctdProductivityproduct; @override void initState() { super.initState(); apiService = ApiService(); Future.microtask(() { // final id = ref.read(managerIdProvider); final roleId = ref.read(userRoleProvider); managerId = ref.read(managerIdProvider); // print("ENQroleId - $roleId"); if (managerId != null) { getProductivityDashBrdList(managerId); } }); } // Add this helper method: double _getSmartMaxValue() { if (maxPremium <= 0) return 10; // For policies (small numbers), ensure minimum scale if (isInsureWisePolicy) { if (maxPremium < 10) { return 12; // Minimum scale of 10 for policies } // Round up to nearest 5 return ((maxPremium / 5).ceil() * 5).toDouble(); } // For premium (large numbers) return (maxPremium * 1.15).ceilToDouble(); // 15% headroom } Color _generateColorForVehicle(String vehicleType, int index) { // Predefined colors for common vehicle types const predefinedColors = [ Colors.blue, Colors.green, Colors.orange, Colors.purple, Colors.red, Colors.teal, Colors.pink, Colors.indigo, Colors.amber, Colors.cyan, Colors.lime, Colors.deepOrange, ]; // If within predefined range, use those if (index < predefinedColors.length) { return predefinedColors[index]; } // Otherwise generate using HSL for infinite colors final hue = (index * 137.5) % 360; // Golden angle for distribution return HSLColor.fromAHSL(1.0, hue, 0.6, 0.5).toColor(); } Future getProductivityDashBrdList(id) async { setState(() { isLoading = true; }); try { final response = await apiService.findProductivityDashboardData( id, SelctdProductivityproduct, ); if (response['status'] != 'success') { setState(() { isLoading = false; }); return; } final data = response['data']; final staffWise = (data['StaffWise'] ?? []) as List; print('data - $data'); print('staffWise - $staffWise'); // 🎨 STEP 1: Collect ALL unique vehicle types across all staff Set allVehicleTypes = {}; for (var staff in staffWise) { final products = (staff['products_list'] ?? []) as List; for (var p in products) { final vehicleType = p['vechile_type']?.toString() ?? 'Unknown'; allVehicleTypes.add(vehicleType); } } // 🎨 STEP 2: Generate dynamic colors for all vehicle types Map dynamicVehicleColors = {}; int colorIndex = 0; for (var vehicleType in allVehicleTypes) { dynamicVehicleColors[vehicleType] = _generateColorForVehicle( vehicleType, colorIndex++, ); } print('Dynamic Vehicle Colors: $dynamicVehicleColors'); List newLabels = []; List newLabelsId = []; List>> newChartData = []; List> vehicleNames = []; double newMax = 0.0; double _toDouble(dynamic v) { if (v == null) return 0.0; if (v is double) return v; if (v is int) return v.toDouble(); return double.tryParse(v.toString()) ?? 0.0; } for (var staff in staffWise) { final name = (staff['sales_executive_name'] ?? staff['short_name']) ?.toString() ?? ''; newLabels.add(name); final id = (staff['sales_executive_id'])?.toString() ?? ''; newLabelsId.add(id); final products = (staff['products_list'] ?? []) as List; List> currentRod = []; List> previousRod = []; List staffVehicleNames = []; double curStart = 0.0; double preStart = 0.0; for (var p in products) { final cur = isInsureWisePolicy ? _toDouble(p['total_policies_current_month']) : _toDouble(p['total_premium_current_month']); final pre = isInsureWisePolicy ? _toDouble(p['total_policies_pre_month']) : _toDouble(p['total_premium_pre_month']); // Get vehicle type and its DYNAMIC color final vehicleType = p['vechile_type']?.toString() ?? 'Unknown'; final color = dynamicVehicleColors[vehicleType] ?? Colors.grey; staffVehicleNames.add(vehicleType); // Add to current month rod with vehicle-specific color currentRod.add([curStart, curStart + cur, color]); curStart += cur; // Add to previous month rod with lighter shade (60% opacity) previousRod.add([preStart, preStart + pre, color.withOpacity(0.6)]); preStart += pre; } newChartData.add([currentRod, previousRod]); vehicleNames.add(staffVehicleNames); newMax = [newMax, curStart, preStart].reduce((a, b) => a > b ? a : b); } setState(() { originalStaffWise = staffWise .map((e) => Map.from(e)) .toList(); filteredStaffWise = List>.from(originalStaffWise); staffLabels = newLabels; staffLabelsId = newLabelsId; staffChartData = newChartData; staffVehicleTypeNames = vehicleNames; vehicleColors = dynamicVehicleColors; // Save for legend maxPremium = newMax; productWise = List>.from( data['productWise'] ?? [], ); insurerWise = List>.from( data['insurerWise'] ?? [], ); }); } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } List buildStaffLabels() { return filteredStaffWise.map((item) { final name = item['sales_executive_name'] ?? item['short_name']; if (name != null && name.toString().trim().isNotEmpty) { return name.toString(); } else { return item['sales_executive_name']?.toString() ?? ''; } }).toList(); } List> convertToProductList(List rawList) { return rawList.map>((item) { return { "vehicle_type": item[0], "current_month": double.tryParse(item[1].toString()) ?? 0.0, "previous_month": double.tryParse(item[2].toString()) ?? 0.0, }; }).toList(); } @override Widget build(BuildContext context) { return SelectionArea( child: Container( margin: EdgeInsets.only(left: 10, right: 10, bottom: 10), // color: Colors.amber.shade100, child: Column( children: [ // Row( // mainAxisAlignment: MainAxisAlignment.center, // children: [ // Text('Hide and Show Charts : ', style: _styleSmall1), // // Row( // children: [ // Transform.scale( // scale: 0.75, // child: Checkbox( // value: showStaff, // onChanged: (val) { // setState(() => showStaff = val!); // }, // ), // ), // Text( // // showStaff ? "Staff" : "Staff Hidden", // "Staff", // style: _styleSmall2, // ), // ], // ), // // // SizedBox(width: 10), // // // // Row( // // children: [ // // Transform.scale( // // scale: 0.75, // // child: Checkbox( // // value: showProduct, // // onChanged: (val) { // // setState(() => showProduct = val!); // // }, // // ), // // ), // // Text( // // // showBroker ? "Broker" : "Broker Hidden", // // "Product", // // style: _styleSmall2, // // ), // // ], // // ), // ], // ), SizedBox(height: 10), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ showStaff ? Expanded(child: insurerContainer()) : SizedBox(), // showProduct ? Expanded(child: productContainer()) : SizedBox(), ], ), ), ], ), ) ); } Widget insurerContainer() { return Card( margin: const EdgeInsets.only(right: 10), color: Colors.white, shadowColor: Colors.black, elevation: 1, // card shadow shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), clipBehavior: Clip.antiAlias, child: Padding( padding: const EdgeInsets.all(10.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Text( 'Sales Executive Product Wise (Previous / Current Month)', style: _chartHeader, ), ), Text( isInsureWisePolicy ? "Policies" : "Premium", style: _styleSmall1, ), Transform.scale( scale: 0.5, child: Switch( activeColor: Colors.teal, activeTrackColor: Colors.grey.shade200, inactiveThumbColor: Colors.orange, inactiveTrackColor: Colors.white, value: isInsureWisePolicy, onChanged: (val) { setState(() { isInsureWisePolicy = val; }); // 👇 ADD THIS: Rebuild chart data with new toggle value getProductivityDashBrdList(managerId); }, ), ), ], ), SizedBox(height: 10), Container( // color: Colors.amber.shade100, child: Expanded( child: CustomStackedBarChart( labels: staffLabels, labelsId: staffLabelsId, // maxY: (maxPremium * 1.1).ceilToDouble(), maxY: _getSmartMaxValue(), initialRotation: 1, dataList: [], productsList: staffChartData, vehicleTypeNames: staffVehicleTypeNames, // 👈 NEW: Pass vehicle names ), ), ), Row(children: [Expanded(child: legendWidget())]), ], ), ), ); } Widget legendWidget() { return Wrap( spacing: 8, runSpacing: 5, alignment: WrapAlignment.center, children: [ // Vehicle type legends (dynamically generated) ...vehicleColors.entries.map((entry) { return _legendItem(entry.key, entry.value); }), // Separator SizedBox(width: 10), Container(height: 12, width: 1, color: Colors.grey.shade300), SizedBox(width: 10), // // Month legends // _legendItem('Current (Solid)', Colors.black87), // _legendItem('Previous (Light)', Colors.black38), ], ); } Widget _legendItem(String label, Color color) { return Row( mainAxisSize: MainAxisSize.min, children: [ Container( width: 10, height: 10, decoration: BoxDecoration(color: color, shape: BoxShape.circle), ), SizedBox(width: 4), Text(label, style: _styleSmall1), ], ); } } final _headerStyle2 = GoogleFonts.poppins( fontSize: 12, color: Colors.black, fontWeight: FontWeight.w500, ); final _chartHeader = GoogleFonts.poppins( fontSize: 11.5, color: Colors.black, fontWeight: FontWeight.w500, ); final _styleSmall1 = GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, ); final _styleSmall2 = GoogleFonts.poppins( fontSize: 10, fontWeight: FontWeight.w400, color: Colors.blueGrey, );