nhance_partner/lib/presentation/screens/dashboard/tabs/productivity.dart
2026-01-08 16:01:06 +05:30

482 lines
14 KiB
Dart

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<ProductivityDashboard> createState() =>
_ProductivityDashboardState();
}
class _ProductivityDashboardState extends ConsumerState<ProductivityDashboard> {
dynamic managerId;
late ApiService apiService;
// List<Map<String, dynamic>> dataVal = [];
List<Map<String, dynamic>> getProductivityDshBdData = [];
List<Map<String, dynamic>> originalData = [];
List<Map<String, dynamic>> filteredData = [];
List<Map<String, dynamic>> productWise = [];
List<Map<String, dynamic>> insurerWise = [];
List<Map<String, dynamic>> originalProductWise = [];
List<Map<String, dynamic>> filteredProductWise = [];
List<Map<String, dynamic>> originalStaffWise = [];
List<Map<String, dynamic>> filteredStaffWise = [];
bool isLoading = false;
bool isInsureWisePolicy = true;
bool isProductWisePolicy = true;
bool showStaff = true;
bool showProduct = true;
int selectedStaffIndex = 0;
List<List<String>> staffVehicleTypeNames =
[]; // 👈 Add this at the top with other state variables
Map<String, Color> vehicleColors = {};
List<String> staffLabels = [];
List<String> staffLabelsId = [];
// List<List<List<dynamic>>> staffChartData = [];
List<List<List<dynamic>>> 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<void> 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<dynamic>;
print('data - $data');
print('staffWise - $staffWise');
// 🎨 STEP 1: Collect ALL unique vehicle types across all staff
Set<String> allVehicleTypes = {};
for (var staff in staffWise) {
final products = (staff['products_list'] ?? []) as List<dynamic>;
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<String, Color> dynamicVehicleColors = {};
int colorIndex = 0;
for (var vehicleType in allVehicleTypes) {
dynamicVehicleColors[vehicleType] = _generateColorForVehicle(
vehicleType,
colorIndex++,
);
}
print('Dynamic Vehicle Colors: $dynamicVehicleColors');
List<String> newLabels = [];
List<String> newLabelsId = [];
List<List<List<dynamic>>> newChartData = [];
List<List<String>> 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<dynamic>;
List<List<dynamic>> currentRod = [];
List<List<dynamic>> previousRod = [];
List<String> 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<String, dynamic>.from(e))
.toList();
filteredStaffWise = List<Map<String, dynamic>>.from(originalStaffWise);
staffLabels = newLabels;
staffLabelsId = newLabelsId;
staffChartData = newChartData;
staffVehicleTypeNames = vehicleNames;
vehicleColors = dynamicVehicleColors; // Save for legend
maxPremium = newMax;
productWise = List<Map<String, dynamic>>.from(
data['productWise'] ?? [],
);
insurerWise = List<Map<String, dynamic>>.from(
data['insurerWise'] ?? [],
);
});
} catch (e) {
print('Exception occurred: $e');
} finally {
setState(() {
isLoading = false;
});
}
}
List<String> buildStaffLabels() {
return filteredStaffWise.map<String>((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<Map<String, dynamic>> convertToProductList(List<dynamic> rawList) {
return rawList.map<Map<String, dynamic>>((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,
);