import 'dart:math' show min; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:nhance_partner/data/utils/toastNotification.dart'; import '../../../core/services/api_service.dart'; import '../../layouts/responsive_layout.dart'; import '../../providers/manager_provider.dart'; import '../../providers/userRoleProvider.dart'; import '../../themes/indicators/date_range_picker_field.dart'; import '../../themes/indicators/input_field_decoration.dart'; class DateFilterRowPayout extends ConsumerStatefulWidget { final bool showBrokerFilter; final TextEditingController startController; final String? selectedBroker; final List? selectedParnter; final String? selectedStaffId; final TextEditingController endController; final ValueChanged? onBrokerChanged; final ValueChanged>? onPartnerChanges; final String? dataFrom; final VoidCallback onFilter; final VoidCallback onRefresh; final GlobalKey formKey; final bool isMobile; final role; final id; /// When non-null, toggles filter layout for payout details: /// - `false` — no table rows: show date range only (hide partner + actions). /// - `true` — table has rows: show partner (+ broker if [showBrokerFilter]) and /// search/reset aligned to the right; hide date range. /// When `null`, legacy: date range and partner row together. final bool? policyTableHasRows; /// Optional per-column table search fields (shown between date range and partner). final Widget? tableSearchFields; /// When true, [getAgentUnusedCommissionList] receives [startController]/[endController] dates. final bool passDateRangeToAgentList; const DateFilterRowPayout({ super.key, this.showBrokerFilter = true, required this.startController, required this.endController, required this.onFilter, required this.onRefresh, this.onBrokerChanged, this.onPartnerChanges, required this.selectedStaffId, required this.selectedParnter, this.selectedBroker, required this.formKey, required this.role, required this.id, this.isMobile = false, this.dataFrom, this.policyTableHasRows, this.tableSearchFields, this.passDateRangeToAgentList = false, }); @override ConsumerState createState() => _DateFilterRowState(); } class _DateFilterRowState extends ConsumerState { final GlobalKey>> dropDownKey = GlobalKey>>(); final GlobalKey>> dropDownKeyBroker = GlobalKey>>(); final GlobalKey>> dropDownKeyPartner = GlobalKey>>(); late ApiService apiService; // List? selectedPartnerIds = []; List> getPartnerData = []; List> filteredPartnerData = []; List> getBrokerData = []; List> filteredBrokerData = []; String? selectedStaff; String? selectedBroker; String? selectedPartner; String? selectedStaffName; dynamic role; dynamic managerId; bool isLoading = false; bool isLoadingBroker = false; bool isLoadingAgentList = false; @override void initState() { super.initState(); apiService = ApiService(); Future.microtask(() { managerId = ref.watch(managerIdProvider); final userID = ref.watch(userIdProvider); role = ref.watch(userRoleProvider); print("managerId - $managerId"); // if (userID != null && role != null) { // print('hansles'); // getPartnerDetails(userID); // } if (widget.showBrokerFilter && managerId != null && widget.selectedBroker != null) { print('managerId - $managerId'); print('SelectedBroker - $widget.selectedBroker'); getAgentList(managerId); } if (!widget.showBrokerFilter && managerId != null) { // Broker filter hidden: load partner list directly by manager. if (!_shouldDeferAgentListUntilDateRange()) { getAgentList(managerId); } } }); if (widget.showBrokerFilter) { getBroker(); } } Future getBroker() async { print('getBroker called'); setState(() { isLoadingBroker = true; }); try { final response = await apiService.fetchMasterDropDown('Broker'); if (response['status'] == 200) { print('getBroker - ${response['data']}'); setState(() { getBrokerData = List>.from(response['data']); print('API Data - $getBrokerData'); filteredBrokerData = List.from(getBrokerData); print('originalData - $filteredBrokerData'); }); } else { getBrokerData = []; filteredBrokerData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoadingBroker = false; }); } } bool _hasSelectedDateRange() { return widget.startController.text.trim().isNotEmpty && widget.endController.text.trim().isNotEmpty; } bool _shouldDeferAgentListUntilDateRange() { return widget.passDateRangeToAgentList && !_hasSelectedDateRange(); } String? _agentListFromDate() { if (!widget.passDateRangeToAgentList) return null; final value = widget.startController.text.trim(); return value.isEmpty ? null : value; } String? _agentListToDate() { if (!widget.passDateRangeToAgentList) return null; final value = widget.endController.text.trim(); return value.isEmpty ? null : value; } Future getAgentList(id) async { if (_shouldDeferAgentListUntilDateRange()) { setState(() { getPartnerData = []; filteredPartnerData = []; }); return; } print('getAgentListData called'); setState(() { isLoadingAgentList = true; }); try { final response = await apiService.fetchAgentUnusedCommissionList( id, fromDate: _agentListFromDate(), toDate: _agentListToDate(), ); print('getAgentListData called response'); print('get Agent- ${response['data']}'); if (response['status'] == 'success') { print('get Agent- ${response['data']}'); setState(() { // 1. Convert the response to a list final rawList = List>.from(response['data']); print('API Data - rawList'); // 2. Filter out items where agent_id is null or empty getPartnerData = rawList.where((item) { final id = item['agent_id']; return id != null && id.toString().isNotEmpty; }).toList(); print('Filtered API Data (No Null IDs) - $getPartnerData'); // 3. Sync the filtered data to your display list filteredPartnerData = List.from(getPartnerData); }); } else { getPartnerData = []; filteredPartnerData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoadingAgentList = false; }); } } // Future getPartnerDetails(int id) async { // print('getPartnerDetails called By handler'); // setState(() { // isLoading = true; // }); // // try { // // final response = await apiService.fetchStaffUserList(id, role); // final response = await apiService.fetchStaffListForEnquiryAssignDropDown( // managerId, // id, // role, // ); // if (response['status'] == 'success') { // print('getStaffDetails - ${response['data']}'); // setState(() { // getPartnerData = List>.from(response['data']); // print('API Data - $getPartnerData'); // // filteredPartnerData = List.from(getPartnerData); // print('originalData - $filteredPartnerData'); // }); // } else { // getPartnerData = []; // filteredPartnerData = []; // } // } catch (e) { // print('Exception occurred: $e'); // } finally { // setState(() { // isLoading = false; // }); // } // } @override Widget build(BuildContext context) { final spacing = 5.0; List getRowChildren(bool isMobile, double spacing) { final hasDateRange = widget.startController.text.trim().isNotEmpty && widget.endController.text.trim().isNotEmpty; double partnerMaxWidth() { final sw = MediaQuery.sizeOf(context).width; return min(sw * 0.32, 420); } final buttons = [ Padding( padding: const EdgeInsets.symmetric(vertical: 0.0), child: Tooltip( message: 'Filter', child: IconButton( icon: const Icon( Icons.search_rounded, size: 18, color: const Color(0xFF94A3B8), ), onPressed: () { if (widget.formKey.currentState?.validate() != true) return; final partners = widget.selectedParnter; final hasPartner = partners != null && partners.isNotEmpty && partners.any((e) => e.toString().trim().isNotEmpty); if (!hasPartner) { ToastHelper.showWarningToast( context, 'Please select a partner before searching.', ); return; } widget.onFilter(); }, ), ), ), Padding( padding: const EdgeInsets.all(0.0), child: Tooltip( message: 'Refresh', child: IconButton( onPressed: () { if (widget.passDateRangeToAgentList) { setState(() { getPartnerData = []; filteredPartnerData = []; }); } widget.onRefresh(); }, icon: const Icon( Icons.refresh, size: 18, color: const Color(0xFF94A3B8), ), ), ), ), ]; final partnerActionsRow = [ if (widget.showBrokerFilter) ...[ buildBroker(context), SizedBox(width: spacing), ], buildPartner(context, maxWidth: partnerMaxWidth()), SizedBox(width: spacing), ...buttons, ]; if (!hasDateRange) { return [buildDateRangeFilter(context)]; } if (isMobile) { return [ buildDateRangeFilter(context), if (widget.tableSearchFields != null) ...[ const SizedBox(height: 10), widget.tableSearchFields!, ], const SizedBox(height: 10), Wrap( spacing: spacing, runSpacing: 8, crossAxisAlignment: WrapCrossAlignment.end, children: partnerActionsRow, ), ]; } return [ buildDateRangeFilter(context), if (widget.tableSearchFields != null) ...[ SizedBox(width: spacing), widget.tableSearchFields!, ], SizedBox(width: spacing), Spacer(), ...partnerActionsRow, ]; } return Form( key: widget.formKey, child: widget.isMobile ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: getRowChildren(widget.isMobile, spacing), ) : Row( crossAxisAlignment: CrossAxisAlignment.end, children: getRowChildren(widget.isMobile, spacing), ), ); } Widget buildDateRangeFilter(BuildContext context) { final w = ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.15; return DateRangePickerField( startController: widget.startController, endController: widget.endController, hintText: 'Select date range', txtwidth: w, txtheight: 32, lastDate: DateTime.now(), rangeValidator: DateRangePickerField.defaultFilterValidator, onRangeSelected: (_) => Future.microtask(() { if (widget.passDateRangeToAgentList && managerId != null) { getAgentList(managerId); } widget.onFilter(); }), ); } Widget buildBroker(BuildContext context) { Map? selectedBrokers = filteredBrokerData.firstWhere( (item) => item['id'].toString() == widget.selectedBroker, orElse: () => {}, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Broker *', style: _textStyle), SizedBox(height: 5), SizedBox( height: 35, width: MediaQuery.of(context).size.width * 0.15, child: DropdownSearch>( key: dropDownKeyBroker, selectedItem: selectedBrokers.isNotEmpty ? selectedBrokers : null, items: (filter, infiniteScrollProps) { return filteredBrokerData; }, itemAsString: (val) => val['name'].toString(), // what to show compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id // validator: (val) { // if (val == null) { // return "Required"; // ✅ error message // } // return null; // }, suffixProps: DropdownSuffixProps( // make sure the dropdown button is visible dropdownButtonProps: DropdownButtonProps( isVisible: true, padding: EdgeInsets.zero, // remove default padding constraints: const BoxConstraints( // shrink icon tap area minWidth: 12, minHeight: 12, ), iconSize: 15, // smaller icon // icon: const Icon(Icons.arrow_drop_down), ), ), dropdownBuilder: (context, selectedItem) => Align( alignment: Alignment.centerLeft, child: Text( selectedItem != null ? selectedItem['name'].toString() : "", style: GoogleFonts.poppins( fontSize: 11, color: Colors.black, // color: Color(0XFF6366F1), ), overflow: TextOverflow.ellipsis, maxLines: 1, softWrap: false, ), ), decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( label: "Broker", ).copyWith( filled: true, fillColor: Colors.white, // 👈 makes the dropdown input white isDense: true, border: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: const BorderSide( color: Color(0xFFE2E8F0), width: 0.1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(5), borderSide: const BorderSide( color: Color(0xFFE2E8F0), width: 0.5, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 8, vertical: 6, ), ), ), popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), menuProps: MenuProps( backgroundColor: Colors.white, // 👈 sets dropdown background to white ), showSearchBox: true, searchFieldProps: TextFieldProps( autofocus: true, style: GoogleFonts.inter(fontSize: 11, color: Colors.black), decoration: InputDecoration( contentPadding: EdgeInsets.all(1), filled: true, fillColor: Colors.white, hintText: "Search Broker...", hintStyle: GoogleFonts.inter( fontSize: 12, color: Colors.black, ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, ), // 👈 Normal border ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, width: 1.5, ), // 👈 Focused border ), ), ), itemBuilder: (context, item, isDisabled, isSelected) { return Container( // color: isSelected ? Colors.blue.withOpacity(0.1) : null, padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), child: Text( item['name'].toString(), style: GoogleFonts.inter(fontSize: 12, color: Colors.black), ), ); }, // constraints: BoxConstraints(), ), // onChanged: (val) { // if (val != null) { // print("Selected Broker : ${val['name']}"); // print("Id: ${val['id']}"); // // selectedBroker = val['id']; // widget.onBrokerChanged?.call(val['id'].toString()); // // widget.onBrokerChanged?.call(val['id']); // // controllers['agentId']?.text = val['agent_code']; // // agentId = agent['id']; // } // }, onChanged: (val) { if (val != null) { String selectedId = val['id'].toString(); // 1. Notify parent of the change widget.onBrokerChanged?.call(selectedId); // 2. Clear current partner list so user doesn't see old data setState(() { filteredPartnerData = []; }); // 3. Fetch new agents based on this broker getAgentList(managerId!); } }, ), ), ], ); } Widget buildPartner(BuildContext context, {double? maxWidth}) { final w = maxWidth ?? MediaQuery.of(context).size.width * 0.4; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Partner", style: _textStyle), SizedBox(height: 5), SizedBox( height: 35, width: w, child: DropdownSearch>.multiSelection( key: dropDownKeyPartner, // selectedItem: selectedHandlered.isNotEmpty // ? selectedHandlered // : null, selectedItems: filteredPartnerData .where( (item) => (widget.selectedParnter ?? []).contains(item['agent_id']), ) .toList(), // selectedItems: filteredPartnerData // .where( // (item) => (selectedPartnerIds ?? []).contains(item['id']), // ) // .toList(), items: (filter, infiniteScrollProps) { return filteredPartnerData; }, itemAsString: (val) => // what to show "${val['agent_name']} | ${val['unused_commission_amount']} (${val['total_policies']})", compareFn: (item, selectedItem) => item['agent_id'] == selectedItem['agent_id'], // ✅ compare by id dropdownBuilder: (context, selectedItems) { final safeItems = selectedItems; final text = safeItems.isEmpty ? 'Select Partner' : safeItems.length == 1 ? "${safeItems.first['agent_name']} | ${safeItems.first['unused_commission_amount']} (${safeItems.first['total_policies']})" : '${safeItems.length} partners selected'; return Align( alignment: Alignment.centerLeft, child: Text( text, style: GoogleFonts.inter( fontSize: 12, color: safeItems.isEmpty ? const Color(0xFF64748B) : Colors.black, ), maxLines: 1, overflow: TextOverflow.ellipsis, ), ); }, // validator: (val) { // if (val == null) { // return "Required"; // ✅ error message // } // return null; // }, // validator: (val) { // if (val == null || val.isEmpty) { // return ""; // 👈 triggers error border, no text // } // return null; // // // if (val == null || val.isEmpty) { // // return "Required"; // // } // // return null; // }, decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( label: "", ).copyWith( hintText: null, hintStyle: GoogleFonts.inter( fontSize: 12, color: Colors.black, ), filled: true, errorStyle: const TextStyle(height: 0, fontSize: 0), fillColor: Colors.white, // 👈 makes the dropdown input white enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Color(0xFFE2E8F0), // color: Colors.white, ), // 👈 Normal border ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Color(0xFFE2E8F0), // color: Colors.white, width: 1.5, ), // 👈 Focused border ), contentPadding: EdgeInsets.symmetric( horizontal: 8, vertical: 6, ), ), ), popupProps: PopupPropsMultiSelection.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), showSearchBox: true, menuProps: MenuProps(backgroundColor: Colors.white), searchFieldProps: TextFieldProps( autofocus: true, decoration: InputDecoration( filled: true, fillColor: Colors.white, hintText: "Search Partner...", hintStyle: GoogleFonts.inter( fontSize: 11, color: Colors.black, ), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( // color: Colors.blue, color: Color(0xFFEDF6F5), width: 1.5, ), ), ), ), itemBuilder: (context, item, isDisabled, isSelected) { return Container( // color: isSelected ? Colors.blue.withOpacity(0.1) : null, padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 3, ), child: Text( "${item['agent_name']} | ${item['unused_commission_amount']} (${item['total_policies']})", style: GoogleFonts.inter(fontSize: 12, color: Colors.black), ), ); }, ), onChanged: (List> selectedVals) { // selectedPartnerIds = selectedVals // .map((v) => v['id'].toString()) // .toList(); final ids = selectedVals.map((v) => v['agent_id'].toString()).toList(); print("Selected PArnter IDs: $widget.selectedPartnerIds"); widget.onPartnerChanges?.call(ids); // widget.onPartnerChanges!(v['id']); }, ), ), ], ); } // Widget buildSelectStaffMem(BuildContext context) { // Map? selectedVehicle; // if (widget.selectedStaffId == null) { // selectedVehicle = null; // } else if (widget.selectedStaffId != null) { // selectedVehicle = filteredPartnerData.firstWhere( // (item) => item['id'] == widget.selectedStaffId, // orElse: () => {}, // empty map // ); // if (selectedVehicle.isEmpty) selectedVehicle = null; // } else if (selectedStaff != null) { // selectedVehicle = filteredPartnerData.firstWhere( // (item) => item['id'] == selectedStaff, // orElse: () => {}, // empty map // ); // if (selectedVehicle.isEmpty) selectedVehicle = null; // } else { // selectedVehicle = null; // } // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // // Text("Staff Name", style: _textStyle), // // SizedBox(height: 10), // SizedBox( // height: 35, // child: Container( // decoration: BoxDecoration( // borderRadius: BorderRadius.circular(5.0), // border: Border.all(color: Color(0xFFE2E8F0)), // // color: Colors.black, // ), // // width: ResponsiveLayout.isMobile(context) // ? null // : MediaQuery.of(context).size.width * 0.13, // // height: 45, // child: DropdownSearch>( // // key: dropDownKey, // key: ValueKey(selectedStaff), // // selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, // selectedItem: selectedVehicle, // // items: (filter, infiniteScrollProps) { // return filteredPartnerData; // }, // // itemAsString: (val) => val['name'].toString(), // compareFn: (item, selectedItem) => // item['id'] == selectedItem['id'], // ✅ compare by id // dropdownBuilder: (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( // selectedItem != null ? selectedItem['name'].toString() : "", // style: GoogleFonts.poppins(fontSize: 11, color: Colors.black), // overflow: TextOverflow.ellipsis, // maxLines: 1, // softWrap: false, // ), // ), // decoratorProps: DropDownDecoratorProps( // decoration: // AppInputDecorations.dropdownDecoration( // label: "Select Staff ", // ).copyWith( // filled: true, // fillColor: // Colors.white, // 👈 makes the dropdown input white // contentPadding: const EdgeInsets.symmetric( // horizontal: 6, // vertical: 1, // 👈 adjust this to make the field shorter // ), // ), // ), // // popupProps: PopupProps.menu( // fit: FlexFit.loose, // constraints: BoxConstraints(maxHeight: 250), // menuProps: MenuProps( // backgroundColor: // Colors.white, // 👈 sets dropdown background to white // ), // showSearchBox: true, // searchFieldProps: TextFieldProps( // decoration: InputDecoration( // filled: true, // fillColor: Colors.white, // hintText: "Search Staff ...", // hintStyle: GoogleFonts.inter( // fontSize: 12, // color: Colors.black, // ), // enabledBorder: OutlineInputBorder( // borderSide: BorderSide( // color: Colors.white, // ), // 👈 Normal border // ), // focusedBorder: OutlineInputBorder( // borderSide: BorderSide( // color: Colors.white, // width: 1.5, // ), // 👈 Focused border // ), // ), // ), // // itemBuilder: (context, item, isDisabled, isSelected) { // return Container( // // color: isSelected ? Colors.blue.withOpacity(0.1) : null, // padding: const EdgeInsets.symmetric( // horizontal: 8, // vertical: 3, // ), // child: Text( // item['name'].toString(), // style: GoogleFonts.inter( // fontSize: 13, // color: Colors.black, // ), // ), // ); // }, // // // constraints: BoxConstraints(), // ), // // onChanged: (val) { // if (val != null) { // print("Selected Staff : ${val['name']}"); // print("Id: ${val['id']}"); // // selectedStaffName = val['name']; // // selectedStaff = val['id']; // widget.onBrokerChanged!(val['id']); // // if (widget.onFilterStaff != null) // // widget.onFilterStaff!(val['id']); // // // Future.microtask(() => widget.onFilter()); // // controllers['agentId']?.text = val['agent_code']; // // agentId = agent['id']; // } // }, // ), // ), // ), // ], // ); // } static final _textStyle = GoogleFonts.poppins( fontSize: 11, fontWeight: FontWeight.w500, ); }