From e1844828b8e6cc371e1fdafe98429654fa9d3024 Mon Sep 17 00:00:00 2001 From: "sanjeev.p" Date: Tue, 17 Feb 2026 10:28:54 +0530 Subject: [PATCH 1/2] FIX_MinorTask --- lib/presentation/RaiseClaimForm.dart | 648 ++++++++++++++---------- lib/presentation/excelVerification.dart | 9 +- 2 files changed, 391 insertions(+), 266 deletions(-) diff --git a/lib/presentation/RaiseClaimForm.dart b/lib/presentation/RaiseClaimForm.dart index 2380ddc..6451d02 100644 --- a/lib/presentation/RaiseClaimForm.dart +++ b/lib/presentation/RaiseClaimForm.dart @@ -71,6 +71,7 @@ class _RaiseClaimDialogState extends State { dynamic emailId; List> employeePolicyList = []; String? selectedFileNames; + // html.File? uploadedFile; // List uploadedFiles = []; List uploadedFiles = []; @@ -144,6 +145,7 @@ class _RaiseClaimDialogState extends State { bool isIntimationDateValid = true; bool isAdmitDateValid = true; bool isDischargeDateValid = true; + bool isAccidentService = false; @override void initState() { @@ -372,7 +374,6 @@ class _RaiseClaimDialogState extends State { } Future sendFormDataToApi() async { - setState(() { isServiceValid = serviceId != null; isPolicyValid = selectedClientPolicyId != null; @@ -395,6 +396,8 @@ class _RaiseClaimDialogState extends State { // ☠ Accident / Death final isAccident = [2, 3, 4].contains(serviceId); + isAccidentService = isAccident ? true : false; + isAccidentDateValid = !isAccident || accidentDate != null; isIntimationDateValid = !isAccident || intimationDate != null; }); @@ -550,9 +553,10 @@ class _RaiseClaimDialogState extends State { pdf.addPage( pw.Page( - build: (pw.Context context) => pw.Center( - child: pw.Image(image, fit: pw.BoxFit.contain), - ), + build: (pw.Context context) => + pw.Center( + child: pw.Image(image, fit: pw.BoxFit.contain), + ), ), ); @@ -752,236 +756,291 @@ class _RaiseClaimDialogState extends State { const SizedBox(height: 16), - Form( - key: formKey, - child: Column( - children: [ - _row([ - buildDropdownField( - 'Service', - (value) { - final selectedItem = departmentList.firstWhere( - (item) => item['id'] == value, - orElse: () => {}, - ); - setState(() { - serviceId = value; - serviceName = selectedItem['name']; - policyNumberId = null; - isServiceValid = true; - }); - print('🔥 serviceId set to $serviceId'); - filterPoliciesByService(value!); - }, - departmentList, - 'name', - serviceId, - ), - buildDropdownField( - 'Select Policy', - (value) { - final selectedPolicy = - policyNumberList.firstWhere((p) => p['id'] == value); - - setState(() { - // ✅ THIS is what you send to API - selectedClientPolicyId = selectedPolicy['id']; - - // optional - selectedPolicyTypeId = selectedPolicy['policy_type_id']; - policyNumberId = value; - - isPolicyValid = true; - }); - - getCDPoliciesDetails(); - }, - policyNumberList, - 'label', // 👈 DISPLAY FIELD - policyNumberId, - ), - - buildDropdownFieldSearch( - 'Member Name', - (value) { - final member = employeePolicyList.firstWhere( - (m) => m['id'] == value, - ); - - setState(() { - selectedMemberId = value; - selectedMemberObject = member; // ✅ FULL OBJECT - selectedMemberName = member['name']; - isMemberValid = true; - print('selectedMemberObject $selectedMemberObject'); - }); - }, - employeePolicyList, - 'name', - selectedMemberId, - ), - - ]), - _row([ - buildTextField('Message', messageController), - if (serviceId == 1 || serviceId == 72)...[ - buildTextField('Hospital Name', hospitalNameController), - buildTextField('Hospital Address', hospitalAddressController), - ] - ]), - - if (serviceId == 1 || serviceId == 72) + Form( + key: formKey, + child: Column( + children: [ _row([ - buildTextField('Hospital City', hospitalCityController), - buildTextField('Hospital State', hospitalStateController), - buildTextField( - 'Hospital Pincode', - hospitalPinCodeController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(6), - ], - ), - ]), - if (serviceId == 1 || serviceId == 72) - _row([ - buildTextField( - 'Hospital Phone No', - hospitalPhoneNoController, - keyboardType: TextInputType.number, - inputFormatters: [ - FilteringTextInputFormatter.digitsOnly, - LengthLimitingTextInputFormatter(10), - ], - ), - buildDatePickerField( - label: 'Admit Date', - selectedDate: admitDate, - allowFuture: false, - onDateSelected: (d) { + buildDropdownField( + 'Service', + (value) { + final selectedItem = departmentList + .firstWhere( + (item) => item['id'] == value, + orElse: () => {}, + ); setState(() { - admitDate = d; - dischargeDate = null; + serviceId = value; + serviceName = selectedItem['name']; + policyNumberId = null; + isServiceValid = true; + }); + print('🔥 serviceId set to $serviceId'); + filterPoliciesByService(value!); + }, + departmentList, + 'name', + serviceId, + isRequired: true, + isValid: isServiceValid, + ), + buildDropdownField( + 'Select Policy', + (value) { + final selectedPolicy = + policyNumberList.firstWhere(( + p) => p['id'] == value); + + setState(() { + // ✅ THIS is what you send to API + selectedClientPolicyId = + selectedPolicy['id']; + + // optional + selectedPolicyTypeId = + selectedPolicy['policy_type_id']; + policyNumberId = value; + + isPolicyValid = true; + }); + + getCDPoliciesDetails(); + }, + policyNumberList, + 'label', // 👈 DISPLAY FIELD + policyNumberId, + isRequired: true, + isValid: isPolicyValid, // 👈 Hooked to your state + ), + + buildDropdownFieldSearch( + 'Member Name', + (value) { + final member = employeePolicyList + .firstWhere( + (m) => m['id'] == value, + ); + + setState(() { + selectedMemberId = value; + selectedMemberObject = + member; // ✅ FULL OBJECT + selectedMemberName = member['name']; + isMemberValid = true; + print( + 'selectedMemberObject $selectedMemberObject'); }); }, + employeePolicyList, + 'name', + selectedMemberId, + isRequired: true, + isValid: isMemberValid, // 👈 Hooked to your state ), - buildDatePickerField( - label: 'Discharge Date', - selectedDate: dischargeDate, - allowFuture: true, - minDate: admitDate?.add(const Duration(days: 1)), - onDateSelected: (d) => setState(() => dischargeDate = d), - ), - buildTextField( - 'Claims Amount', - claimAmountController, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - ), + + ]), + _row([ + buildTextField('Message', messageController, + isRequired: false, isValid: true), + if (serviceId == 1 || serviceId == 72)...[ + buildTextField( + 'Hospital Name', hospitalNameController, + isRequired: true, + isValid: isHospitalNameValid), + buildTextField('Hospital Address', + hospitalAddressController, + isRequired: true, + isValid: isHospitalAddressValid), + ] ]), - if ([2, 3, 4].contains(serviceId)) - _row([ - buildDatePickerField( - label: 'Date of Birth', - selectedDate: birthDate, - allowFuture: false, - onDateSelected: (d) => setState(() => birthDate = d), - ), - buildDatePickerField( - label: 'Accident Date', - selectedDate: accidentDate, - allowFuture: false, - onDateSelected: (d) { - setState(() { - accidentDate = d; - deathDate = null; - intimationDate = null; - }); - }, - ), - buildDatePickerField( - label: 'Date of Death', - selectedDate: deathDate, - allowFuture: false, - onDateSelected: (d) => setState(() => deathDate = d), - ), - ]), - if ([2, 3, 4].contains(serviceId)) - _row([ - buildDatePickerField( - label: 'Date of Intimation', - selectedDate: intimationDate, - allowFuture: false, - onDateSelected: (d) => setState(() => intimationDate = d), - ), - buildTextField( - 'Sum Insured', - sumInsuredController, - keyboardType: TextInputType.number, - inputFormatters: [FilteringTextInputFormatter.digitsOnly], - ), - const SizedBox(), - ]), - - _row([ - MultiFileUploadWidget(), - ]), - Align( - alignment: Alignment.centerRight, - child: SizedBox( - width: 120, - height: 42, - child: ElevatedButton( - onPressed: isSubmitting ? null : sendFormDataToApi, - style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFFE26728), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), + if (serviceId == 1 || serviceId == 72) + _row([ + buildTextField( + 'Hospital City', hospitalCityController, + isRequired: true, + isValid: isHospitalCityValid), + buildTextField( + 'Hospital State', hospitalStateController, + isRequired: true, + isValid: isHospitalStateValid), + buildTextField( + 'Hospital Pincode', + hospitalPinCodeController, + keyboardType: TextInputType.number, + isRequired: true, + isValid: isHospitalPincodeValid, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(6), + ], ), - child: isSubmitting - ? const SizedBox( - height: 18, - width: 18, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.white, + ]), + if (serviceId == 1 || serviceId == 72) + _row([ + buildTextField( + 'Hospital Phone No', + hospitalPhoneNoController, + keyboardType: TextInputType.number, + isRequired: true, + isValid: isHospitalPhoneNoValid, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + LengthLimitingTextInputFormatter(10), + ], + ), + buildDatePickerField( + label: 'Admit Date', + selectedDate: admitDate, + allowFuture: false, + isRequired: true, + isValid: isAdmitDateValid, + onDateSelected: (d) { + setState(() { + admitDate = d; + dischargeDate = null; + }); + }, + ), + buildDatePickerField( + label: 'Discharge Date', + selectedDate: dischargeDate, + allowFuture: true, + minDate: admitDate?.add( + const Duration(days: 1)), + isRequired: true, + isValid: isDischargeDateValid, + onDateSelected: (d) => + setState(() => dischargeDate = d), + ), + buildTextField( + 'Claims Amount', + claimAmountController, + keyboardType: TextInputType.number, + isRequired: true, + isValid: isClaimAmountValid, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + ), + ]), + + if ([2, 3, 4].contains(serviceId)) + _row([ + buildDatePickerField( + label: 'Date of Birth', + selectedDate: birthDate, + allowFuture: false, + isRequired: false,isValid: true, + onDateSelected: (d) => + setState(() => birthDate = d), + ), + buildDatePickerField( + label: 'Accident Date', + selectedDate: accidentDate, + allowFuture: false, + isRequired: isAccidentService, + isValid: isAccidentDateValid, + onDateSelected: (d) { + setState(() { + accidentDate = d; + deathDate = null; + intimationDate = null; + }); + }, + ), + buildDatePickerField( + label: 'Date of Death', + selectedDate: deathDate, + allowFuture: false, + isRequired: false, + isValid: true, + onDateSelected: (d) => + setState(() => deathDate = d), + ), + ]), + if ([2, 3, 4].contains(serviceId)) + _row([ + buildDatePickerField( + label: 'Date of Intimation', + selectedDate: intimationDate, + allowFuture: false, + isRequired: isAccidentService, + isValid: isIntimationDateValid, + onDateSelected: (d) => + setState(() => intimationDate = d), + ), + buildTextField( + 'Sum Insured', + sumInsuredController, + keyboardType: TextInputType.number, + isRequired: false,isValid: true, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly + ], + ), + const SizedBox(), + ]), + + _row([ + MultiFileUploadWidget(), + ]), + Align( + alignment: Alignment.centerRight, + child: SizedBox( + width: 120, + height: 42, + child: ElevatedButton( + onPressed: isSubmitting + ? null + : sendFormDataToApi, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + child: isSubmitting + ? const SizedBox( + height: 18, + width: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ) + : const Text( + 'Send', + style: TextStyle(color: Colors.white, + fontWeight: FontWeight.w600), ), - ) - : const Text( - 'Send', - style: TextStyle(color: Colors.white, - fontWeight: FontWeight.w600), ), ), ), - ), - ], + ], + ), ), - ), - ], - ), - ), - ), - - /// 🔄 LOADER OVERLAY (UNCHANGED) - if (isLoading) - Container( - color: const Color(0x98FFFCE5), - child: Center( - child: Image.asset( - 'assets/nhance-loader.gif', - height: 60, - width: 60, + ], ), ), ), - ], + + /// 🔄 LOADER OVERLAY (UNCHANGED) + if (isLoading) + Container( + color: const Color(0x98FFFCE5), + child: Center( + child: Image.asset( + 'assets/nhance-loader.gif', + height: 60, + width: 60, + ), + ), + ), + ], + ), ), - ), - ) + ) ); } @@ -1003,29 +1062,31 @@ class _RaiseClaimDialogState extends State { ); } - Widget buildTextField( - String label, - TextEditingController controller, { - TextInputType keyboardType = TextInputType.text, - List? inputFormatters, - }) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - fieldLabel(label), - formBox( - child: TextField( - controller: controller, - keyboardType: keyboardType, - inputFormatters: inputFormatters, - decoration: const InputDecoration( - isDense: true, - border: InputBorder.none, + Widget buildTextField(String label, + TextEditingController controller, { + TextInputType keyboardType = TextInputType.text, + bool isRequired = true, bool isValid = true, + List? inputFormatters, + }) { + return Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + fieldLabel(label, isRequired: isRequired), + formBox( + child: TextField( + controller: controller, + keyboardType: keyboardType, + inputFormatters: inputFormatters, + decoration: const InputDecoration( + isDense: true, + border: InputBorder.none, + ), ), ), - ), - ], - ); + if (!isValid) validationText(), + ], + ),); } Widget buildTextAreaField(String label, TextEditingController controller) { @@ -1053,53 +1114,81 @@ class _RaiseClaimDialogState extends State { } Widget buildDropdownField( - String label, - void Function(int?) onChanged, - List> itemsList, - String displayField, - int? selectedValue, - ) { + String label, + void Function(int?) onChanged, + List> itemsList, + String displayField, + int? selectedValue, { + bool isRequired = true, + bool isValid = true, + }) { + + // Logic to calculate font size based on string length + double getDynamicFontSize(String text) { + if (text.length > 25) return 9.0; + if (text.length > 15) return 10.0; + return 12.0; + } + return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - fieldLabel(label), + fieldLabel(label, isRequired: isRequired), formBox( child: DropdownButtonHideUnderline( child: DropdownButton( isExpanded: true, value: selectedValue, - hint: const Text('Select'), + hint: const Text( + 'Select', + style: TextStyle(fontSize: 12), + ), icon: const Icon(Icons.keyboard_arrow_down), items: itemsList.map>((item) { + String textValue = item[displayField].toString(); // Get the text first return DropdownMenuItem( value: item['id'], - child: Text(item[displayField]), + child: Text( + textValue, + overflow: TextOverflow.ellipsis, + softWrap: false, + maxLines: 1, // Strictly keep to one line + style: TextStyle( + // Call the function here and pass the textValue + fontSize: getDynamicFontSize(textValue), + ), + ), ); }).toList(), onChanged: onChanged, ), ), ), + if (!isValid) + Padding( + padding: const EdgeInsets.only(top: 4, left: 4), + child: Text( + "Required", + style: TextStyle(color: Colors.red, fontSize: 12), + ), + ), ], ); } - - Widget buildDropdownFieldSearch( - String label, + Widget buildDropdownFieldSearch(String label, void Function(int?) onChanged, List> itemsList, String displayField, int? selectedValue, - ) { - + { + bool isRequired = true, // Added + bool isValid = true, // Added + }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - label, - style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500), - ), + fieldLabel(label, isRequired: isRequired), const SizedBox(height: 4), Container( height: 40, @@ -1179,6 +1268,12 @@ class _RaiseClaimDialogState extends State { ), ), ), + if (!isValid) + Padding( + padding: const EdgeInsets.only(top: 4, left: 4), + child: Text("Required", + style: GoogleFonts.poppins(color: Colors.red, fontSize: 12)), + ), ], ); } @@ -1191,18 +1286,20 @@ class _RaiseClaimDialogState extends State { required ValueChanged onDateSelected, DateTime? minDate, DateTime? maxDate, + bool isRequired = true, + bool isValid = true, }) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - fieldLabel(label), + fieldLabel(label, isRequired: isRequired), formBox( child: InkWell( onTap: () async { final DateTime now = DateTime.now(); final DateTime first = minDate ?? DateTime(1980); final DateTime last = - allowFuture ? (maxDate ?? DateTime(2100)) : now; + allowFuture ? (maxDate ?? DateTime(2100)) : now; final DateTime initialDate = selectedDate ?? (first.isAfter(now) ? first : now); @@ -1232,6 +1329,7 @@ class _RaiseClaimDialogState extends State { ), ), ), + if (!isValid) validationText(), ], ); } @@ -1270,19 +1368,39 @@ class _RaiseClaimDialogState extends State { ); } - Widget fieldLabel(String text) { + Widget fieldLabel(String text, {bool isRequired = false}) { return Padding( padding: const EdgeInsets.only(bottom: 6), - child: Text( - text, - style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w500, - color: Colors.black, + child: RichText( + text: TextSpan( + text: text, + style: GoogleFonts.poppins( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Colors.black, + ), + children: [ + if (isRequired) + const TextSpan( + text: ' *', + style: TextStyle( + color: Colors.red, fontWeight: FontWeight.bold), + ), + ], ), ), ); } + Widget validationText() { + return Padding( + padding: const EdgeInsets.only(top: 4, left: 4), + child: Text( + "Required", + style: GoogleFonts.poppins( + color: Colors.red, fontSize: 12, fontWeight: FontWeight.w500), + ), + ); + } } // class RaiseClaimDialog extends StatelessWidget { diff --git a/lib/presentation/excelVerification.dart b/lib/presentation/excelVerification.dart index 58e962a..6911202 100644 --- a/lib/presentation/excelVerification.dart +++ b/lib/presentation/excelVerification.dart @@ -114,7 +114,14 @@ class _activePolicyExcelErrorState extends State // 🟢 CASE 2: Success with data if (response['status'] == true) { - ToastHelper.showSuccessToast(context, response['message']); + if (response['message'] == "Error data feteched successfully") { + // This runs if the message matches EXACTLY (including the typo 'feteched') + ToastHelper.showErrorToast(context, response['message']); + }else { + ToastHelper.showSuccessToast(context, response['message']); + } + + setState(() { isLoading = false; From 87ed64734410afee45a2184bde4d6d5fd8cc7bf2 Mon Sep 17 00:00:00 2001 From: "sanjeev.p" Date: Tue, 17 Feb 2026 15:20:33 +0530 Subject: [PATCH 2/2] FIX_minor issues --- lib/presentation/RaiseClaimForm.dart | 1 + lib/presentation/cdTransactionDetails.dart | 138 +++++++++++++- lib/presentation/claims.dart | 164 +++++++++------- lib/presentation/claimshistory.dart | 11 +- lib/presentation/hrPolicyDetails.dart | 206 +++++++++++---------- lib/presentation/postFileUpload.dart | 104 +++++++++-- lib/presentation/preFileUpload.dart | 76 +++++--- lib/service/hrDashboardTabs/cd.dart | 59 ++++-- lib/service/multi_file_upload_widget.dart | 1 + 9 files changed, 546 insertions(+), 214 deletions(-) diff --git a/lib/presentation/RaiseClaimForm.dart b/lib/presentation/RaiseClaimForm.dart index 6451d02..1ac19e9 100644 --- a/lib/presentation/RaiseClaimForm.dart +++ b/lib/presentation/RaiseClaimForm.dart @@ -749,6 +749,7 @@ class _RaiseClaimDialogState extends State { ), IconButton( icon: const Icon(Icons.close), + tooltip: 'Close', onPressed: () => Navigator.pop(context), ), ], diff --git a/lib/presentation/cdTransactionDetails.dart b/lib/presentation/cdTransactionDetails.dart index 72ffb39..61fe4fe 100755 --- a/lib/presentation/cdTransactionDetails.dart +++ b/lib/presentation/cdTransactionDetails.dart @@ -790,12 +790,14 @@ class _cdTransactionDetailsState extends State { if (isAllowedSubType) _ActionIconButton( icon: Icons.picture_as_pdf_outlined, + toolTip: 'View Endorsement PDF', onTap: () => getCdEndorsementDetails(item['id']), ), const SizedBox(width: 8), if (isAllowedSubType && hasSplitUpFile) _ActionIconButton( icon: Icons.folder_open_outlined, + toolTip: 'View Files', onTap: () => _launchURL(item['split_up_url']), ), ], @@ -819,6 +821,126 @@ class _cdTransactionDetailsState extends State { } Widget _buildPagination(BuildContext context) { + final totalItems = filteredData.length; + + // Calculate entries range + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + + // Calculate total pages + final int totalPages = (totalItems / _rowsPerPage).ceil(); + const int visiblePageCount = 5; + + // Helper logic for page numbers + List getVisiblePages() { + if (totalPages <= visiblePageCount) { + return List.generate(totalPages, (i) => i + 1); + } + if (_currentPage <= 3) { + return [1, 2, 3, 4, 5]; + } + if (_currentPage >= totalPages - 2) { + return [ + totalPages - 4, + totalPages - 3, + totalPages - 2, + totalPages - 1, + totalPages + ]; + } + return [ + _currentPage - 2, + _currentPage - 1, + _currentPage, + _currentPage + 1, + _currentPage + 2, + ]; + } + + List visiblePages = getVisiblePages(); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Standard Table Footer Layout + children: [ + /// --- LEFT SIDE: ENTRY DETAILS --- + Text( + 'Showing $startEntry to $endEntry of $totalItems entries', + style: GoogleFonts.poppins( + fontSize: 13, + fontWeight: FontWeight.w500, + color: const Color(0xFF666666), + ), + ), + + /// --- RIGHT SIDE: CONTROLS --- + Row( + children: [ + + DropdownButton( + value: _rowsPerPage, + items: [5, 10, 15, 20, 50].map((int value) { + return DropdownMenuItem( + value: value, + child: Text(' $value ', + style: GoogleFonts.poppins(fontSize: 15)), + ); + }).toList(), + onChanged: (newValue) { + setState(() { + _rowsPerPage = newValue!; + _currentPage = 1; + }); + }, + ), + + const SizedBox(width: 16), + + IconButton( + onPressed: _currentPage > 1 + ? () => setState(() => _currentPage--) + : null, + icon: const Icon(Icons.chevron_left), + ), + + if (!visiblePages.contains(1)) + Row(children: [ + _buildPageButton(1), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: Text("..."), + ), + ]), + + // Visible page buttons + for (int page in visiblePages) _buildPageButton(page), + + if (!visiblePages.contains(totalPages) && totalPages > 0) + Row(children: [ + const Padding( + padding: EdgeInsets.symmetric(horizontal: 4), + child: Text("..."), + ), + _buildPageButton(totalPages), + ]), + + // Next button + IconButton( + onPressed: _currentPage < totalPages + ? () => setState(() => _currentPage++) + : null, + icon: const Icon(Icons.chevron_right), + ), + ], + ), + ], + ), + ); + } + + Widget _buildPagination_backup(BuildContext context) { final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -1033,16 +1155,19 @@ class _ActionIconButton extends StatelessWidget { final IconData icon; final VoidCallback onTap; final bool enabled; + final String? toolTip; // 1. Define the optional tooltip string const _ActionIconButton({ required this.icon, required this.onTap, + this.toolTip, // 2. Added to constructor this.enabled = true, }); @override Widget build(BuildContext context) { - return SizedBox( + // 3. Define the main button widget + Widget button = SizedBox( width: 36, height: 36, child: Material( @@ -1061,6 +1186,17 @@ class _ActionIconButton extends StatelessWidget { ), ), ); + + // 4. Wrap with Tooltip only if enabled and tooltip text exists + if (enabled && toolTip != null) { + return Tooltip( + message: toolTip!, + preferBelow: false, // Shows tooltip above the button + child: button, + ); + } + + return button; } } diff --git a/lib/presentation/claims.dart b/lib/presentation/claims.dart index 10a7373..f8aa84b 100755 --- a/lib/presentation/claims.dart +++ b/lib/presentation/claims.dart @@ -810,51 +810,60 @@ class _ClaimsPolicieState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - GestureDetector( - onTap: () { - // print('id ${item['id']}'); - // print('emp_name ${item['emp_name']}'); - // print('emp_code ${item['emp_code']}'); - // print('policy_type ${item['policy_type']}'); - // print('client_policy_no ${item['client_policy_no']}'); - // print('claim_amount ${item['claim_amount']}'); - // print('claim_no ${item['claim_no']}'); - // print(widget.postToken); - // return; - showDialog( - context: context, - barrierDismissible: true, - builder: (BuildContext context) { - return Dialog( - backgroundColor: Colors.transparent, - insetPadding: EdgeInsets.all(16), - child: ClaimHistoryPopup( - ticket_id: item['id'] ?? '', - empName: item['emp_name'] ?? '', // example - empCode: item['emp_code'] ?? '', // example - policyType: item['policy_type'] ?? '', - clientPolicyNo: item['client_policy_no'] ?? '', - claimAmount: item['claim_amount']?.toString() ?? '', - claimNo: item['claim_no'] ?? '', - postToken: _postPreToken ?? '', - ), + // 1. Tooltip for the hover message + Tooltip( + message: 'View Claim History', + child: + // 2. MouseRegion for the hand pointer + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + // print('id ${item['id']}'); + // print('emp_name ${item['emp_name']}'); + // print('emp_code ${item['emp_code']}'); + // print('policy_type ${item['policy_type']}'); + // print('client_policy_no ${item['client_policy_no']}'); + // print('claim_amount ${item['claim_amount']}'); + // print('claim_no ${item['claim_no']}'); + // print(widget.postToken); + // return; + showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + return Dialog( + backgroundColor: Colors.transparent, + insetPadding: EdgeInsets.all(16), + child: ClaimHistoryPopup( + ticket_id: item['id'] ?? '', + empName: item['emp_name'] ?? '', // example + empCode: item['emp_code'] ?? '', // example + policyType: item['policy_type'] ?? '', + clientPolicyNo: item['client_policy_no'] ?? '', + claimAmount: item['claim_amount']?.toString() ?? '', + claimNo: item['claim_no'] ?? '', + postToken: _postPreToken ?? '', + ), + ); + }, ); }, - ); - }, - child: Container( - height: 35, - width: 35, - decoration: BoxDecoration( - color: Color(0xFF86D1D4), - borderRadius: BorderRadius.circular(8), - ), - child: Padding( - padding: EdgeInsets.all(10), // You can adjust this value - child: Image.asset( - 'assets/claimHistory.png', - fit: BoxFit.contain, - width: 5, + child: Container( + height: 35, + width: 35, + decoration: BoxDecoration( + color: Color(0xFF86D1D4), + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: EdgeInsets.all(10), // You can adjust this value + child: Image.asset( + 'assets/claimHistory.png', + fit: BoxFit.contain, + width: 5, + ), + ), ), ), ), @@ -937,6 +946,12 @@ class _ClaimsPolicieState extends State { ); Widget _buildPagination(BuildContext context) { + // 1. Calculate the range of entries being shown + final totalItems = filteredData.length; + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -947,7 +962,8 @@ class _ClaimsPolicieState extends State { if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } else if (_currentPage >= totalPages - 2) { + } + if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -955,29 +971,42 @@ class _ClaimsPolicieState extends State { totalPages - 1, totalPages ]; - } else { - return [ - _currentPage - 2, - _currentPage - 1, - _currentPage, - _currentPage + 1, - _currentPage + 2, - ]; } + return [ + _currentPage - 2, + _currentPage - 1, + _currentPage, + _currentPage + 1, + _currentPage + 2, + ]; + } List visiblePages = getVisiblePages(); - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( + return Padding( + // Match this horizontal padding (16) to your Table Header padding for perfect alignment + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right + children: [ + // --- LEFT SIDE: Showing Text --- + Text( + "Showing $startEntry to $endEntry of $totalItems entries", + style: GoogleFonts.poppins( + fontSize: 13, + color: const Color(0xFF585757), + fontWeight: FontWeight.w400, + ), + ), + + // --- RIGHT SIDE: Controls --- + Row( children: [ // Dropdown for rows per page DropdownButton( value: _rowsPerPage, + // focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change items: [5, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, @@ -1015,7 +1044,7 @@ class _ClaimsPolicieState extends State { for (int page in visiblePages) _buildPageButton(page), // Right ellipsis + last page - if (!visiblePages.contains(totalPages)) + if (!visiblePages.contains(totalPages) && totalPages > 0) Row(children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 4), @@ -1033,8 +1062,8 @@ class _ClaimsPolicieState extends State { ), ], ), - ), - ], + ], + ), ); } @@ -1641,11 +1670,13 @@ class _ClaimsPolicieState extends State { _IconActionButton( icon: Icons.filter_alt_outlined, onTap: applyFilter, + tooltip:"Filter" ), const SizedBox(width: 12), _IconActionButton( icon: Icons.refresh_outlined, onTap: reset, + tooltip:"Reset" ), ], ), @@ -1703,21 +1734,24 @@ class _ClaimsHeaderDelegate extends SliverPersistentHeaderDelegate { bool shouldRebuild(_) => false; } - - // ================= ICON BUTTON ================= class _IconActionButton extends StatelessWidget { final IconData icon; final VoidCallback onTap; + final String tooltip; const _IconActionButton({ required this.icon, required this.onTap, + required this.tooltip, }); @override Widget build(BuildContext context) { - return SizedBox( + return Tooltip( + message: tooltip, + preferBelow: false, // Optional: Shows tooltip above the button + child: SizedBox( width: 40, height: 40, child: Material( @@ -1729,6 +1763,6 @@ class _IconActionButton extends StatelessWidget { child: Icon(icon, color: Colors.white, size: 20), ), ), - ); + ),); } } diff --git a/lib/presentation/claimshistory.dart b/lib/presentation/claimshistory.dart index 6038e09..cad4feb 100755 --- a/lib/presentation/claimshistory.dart +++ b/lib/presentation/claimshistory.dart @@ -796,7 +796,9 @@ class _ClaimHistoryPopupState extends State const SizedBox(width: 8), /// DOWNLOAD ICON - InkWell( + Tooltip( + message: 'Download', // Added tooltip name + child: InkWell( borderRadius: BorderRadius.circular(6), onTap: () => _launchURL(file['url']), child: Container( @@ -812,6 +814,7 @@ class _ClaimHistoryPopupState extends State ), ), ), + ), ], ), ), @@ -866,6 +869,7 @@ class _ClaimHistoryPopupState extends State ), IconButton( icon: Icon(Icons.close), + tooltip: 'Remove', // Built-in property onPressed: () { _resetIRDocs(); setState(() => showIRDocs = false); @@ -972,6 +976,7 @@ class _ClaimHistoryPopupState extends State ), trailing: IconButton( icon: Icon(Icons.cancel, color: Colors.red), + tooltip: 'Remove', // Built-in property onPressed: () => removeAssignedFile(title), ), ), @@ -1070,6 +1075,7 @@ class _ClaimHistoryPopupState extends State fontSize: 18, fontWeight: FontWeight.w600))), IconButton( icon: Icon(Icons.close), + tooltip: 'Close', // Built-in property onPressed: () { _resetIRDocs(); setState(() => showIRDocs = false); @@ -1139,6 +1145,7 @@ class _ClaimHistoryPopupState extends State maxLines: 1, overflow: TextOverflow.ellipsis), trailing: IconButton( icon: Icon(Icons.cancel, color: Colors.red), + tooltip: 'Remove', // Built-in property onPressed: () => removeAssignedFile(title)), ), ); @@ -1234,6 +1241,7 @@ class _ClaimHistoryPopupState extends State fontSize: 16, fontWeight: FontWeight.w600))), IconButton( icon: Icon(Icons.close), + tooltip: 'Close', // Built-in property onPressed: () { _resetIRDocs(); setState(() => showIRDocs = false); @@ -1304,6 +1312,7 @@ class _ClaimHistoryPopupState extends State maxLines: 1, overflow: TextOverflow.ellipsis), trailing: IconButton( icon: Icon(Icons.cancel, color: Colors.red), + tooltip: 'Remove', // Built-in property onPressed: () => removeAssignedFile(title)), ), ); diff --git a/lib/presentation/hrPolicyDetails.dart b/lib/presentation/hrPolicyDetails.dart index 5bcba05..fd0bc36 100755 --- a/lib/presentation/hrPolicyDetails.dart +++ b/lib/presentation/hrPolicyDetails.dart @@ -361,7 +361,7 @@ class _HrPolicyDetailsState extends State // Filter the original data based on the search query setState(() { filteredData = originalData.where((row) { - final empStatus = row['emp_is_active'] == 1 ? 'active' : 'inactive'; + final empStatus = row['emp_is_active'] == 1 ? 'active' : 'Inactive'; // Implement your filter logic here // For example, check if any field in the row contains the query // Adjust this logic based on your data structure @@ -377,7 +377,7 @@ class _HrPolicyDetailsState extends State .toString() .toLowerCase() .contains(query.toLowerCase()) || - row['formatted_dob'] + row['formatted_dob'].replaceAll("/", "-") .toString() .toLowerCase() .contains(query.toLowerCase()) || @@ -595,6 +595,7 @@ class _HrPolicyDetailsState extends State IconButton( tooltip: 'Previous Page', onPressed: () => {Navigator.pop(context)}, + // splashRadius: 20, icon: const Icon( Icons.arrow_back_ios, size: 18, @@ -1569,92 +1570,87 @@ class _HrPolicyDetailsState extends State child: Builder( builder: (context) { final isSelf = item['relationship'] == 'Self'; - final hasEcard = - item['ecard_download_link'] != null; + final hasEcard = item['ecard_download_link'] != null; final showEcard = isSelf && hasEcard; - final showClaim = - widget.TokenType == "post" && hasModule; + final showClaim = widget.TokenType == "post" && hasModule; if (!showEcard && !showClaim) { return SizedBox(); // No icon to show } return Row( - mainAxisAlignment: showEcard && !showClaim - ? MainAxisAlignment - .start // Only eCard, push to right - : MainAxisAlignment - .end, // eCard + claim OR only claim + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, children: [ + // --- eCard Button --- if (showEcard) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 4.0), - child: GestureDetector( - onTap: () { - getEcardDownload( - item['emp_code'], - item['employee_id'], - item['client_policy_id'], - item['policy_no']); - }, - child: Container( - height: 40, - width: 40, - decoration: BoxDecoration( - color: Color(0xFFE6F5F6), - borderRadius: - BorderRadius.circular(8), - ), - child: Padding( - padding: EdgeInsets.all(4), - child: Image.asset( - 'assets/credit_card.png', - fit: BoxFit.contain, + Tooltip( + message: 'Download e-Card', + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + getEcardDownload( + item['emp_code'], + item['employee_id'], + item['client_policy_id'], + item['policy_no']); + }, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + color: Color(0xFFE6F5F6), + borderRadius: BorderRadius.circular(8), + ), + child: Padding( + padding: EdgeInsets.all(4), + child: Image.asset( + 'assets/credit_card.png', + fit: BoxFit.contain, + ), ), ), ), ), ), - if (showEcard && showClaim) - SizedBox(width: 8), + + if (showEcard && showClaim) SizedBox(width: 8), + + // --- Claim Button --- if (showClaim) - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 4.0), + Tooltip( + message: 'View Claims', child: MouseRegion( - cursor: - SystemMouseCursors - .click, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => ClaimsPolicies( - empCode: item['emp_code']!, + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ClaimsPolicies( + empCode: item['emp_code']!, + ), ), + ); + }, + child: Container( + height: 40, + width: 40, + decoration: BoxDecoration( + color: Color(0xFFE6F5F6), + borderRadius: BorderRadius.circular(8), ), - ); - }, - child: Container( - height: 40, - width: 40, - decoration: BoxDecoration( - color: Color(0xFFE6F5F6), - borderRadius: - BorderRadius.circular(8), - ), - child: Padding( - padding: EdgeInsets.all(4), - child: Image.asset( - 'assets/claim.png', - fit: BoxFit.contain, + child: Padding( + padding: EdgeInsets.all(4), + child: Image.asset( + 'assets/claim.png', + fit: BoxFit.contain, + ), ), ), ), ), - ) ), ], ); @@ -1822,6 +1818,12 @@ class _HrPolicyDetailsState extends State } Widget _buildPagination(BuildContext context) { + // 1. Calculate the range of entries being shown + final totalItems = filteredData.length; + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -1832,7 +1834,8 @@ class _HrPolicyDetailsState extends State if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } else if (_currentPage >= totalPages - 2) { + } + if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -1840,7 +1843,7 @@ class _HrPolicyDetailsState extends State totalPages - 1, totalPages ]; - } else { + } return [ _currentPage - 2, _currentPage - 1, @@ -1848,35 +1851,50 @@ class _HrPolicyDetailsState extends State _currentPage + 1, _currentPage + 2, ]; - } + } List visiblePages = getVisiblePages(); - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( + return Padding( + // Match this horizontal padding (16) to your Table Header padding for perfect alignment + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right + children: [ + // --- LEFT SIDE: Showing Text --- + Text( + "Showing $startEntry to $endEntry of $totalItems entries", + style: GoogleFonts.poppins( + fontSize: 13, + color: const Color(0xFF585757), + fontWeight: FontWeight.w400, + ), + ), + + // --- RIGHT SIDE: Controls --- + Row( children: [ // Dropdown for rows per page - DropdownButton( - value: _rowsPerPage, - items: [5, 10, 15, 20, 50].map((int value) { - return DropdownMenuItem( - value: value, - child: Text(' $value ', - style: GoogleFonts.poppins(fontSize: 15)), - ); - }).toList(), - onChanged: (newValue) { - setState(() { - _rowsPerPage = newValue!; - _currentPage = 1; - }); - }, - ), + DropdownButton( + value: _rowsPerPage, + // focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change + items: [5, 10, 15, 20, 50].map((int value) { + return DropdownMenuItem( + value: value, + child: Text(' $value ', + style: GoogleFonts.poppins(fontSize: 15)), + ); + }).toList(), + onChanged: (newValue) { + setState(() { + _rowsPerPage = newValue!; + _currentPage = 1; + }); + }, + ), + + const SizedBox(width: 8), // Previous button IconButton( @@ -1901,7 +1919,7 @@ class _HrPolicyDetailsState extends State for (int page in visiblePages) _buildPageButton(page), // Right ellipsis + last page - if (!visiblePages.contains(totalPages)) + if (!visiblePages.contains(totalPages) && totalPages > 0) Row(children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 4), @@ -1919,8 +1937,8 @@ class _HrPolicyDetailsState extends State ), ], ), - ), - ], + ], + ), ); } diff --git a/lib/presentation/postFileUpload.dart b/lib/presentation/postFileUpload.dart index 1aed3c4..dfcf815 100755 --- a/lib/presentation/postFileUpload.dart +++ b/lib/presentation/postFileUpload.dart @@ -98,6 +98,9 @@ class _postFileUploadState extends State { String? _selectedOption; final List _allowedExtensions = ['xlsx', 'xls']; + bool showSampleButton = false; + String? currentApiValue; // To store the 'value' for the 2nd param + int _currentPage = 1; int _rowsPerPage = 5; @@ -291,6 +294,54 @@ class _postFileUploadState extends State { } } + Future downloadPostSampleFile(String apiParam) async { + print("fun Sam f - in"); + + final post_file_name = apiParam+'_sample_file.xlsx'; + print("fun Sam f - name $post_file_name" ); + final apiurl = Environment.apiUrlPost; + final String url = '$apiurl/downloadSampleExcel/$apiParam'; + final token = widget.Token; + + final response = await http.get( + Uri.parse(url), + headers: { + 'APP-SIGNATURE': 'nhance-2025-signature-T8f6gV9k5fK2wf5V5c4vId3b0J8z6cAm2x8Y', + 'Authorization': 'Bearer $token', + 'Content-Type': 'application/json', + // 'app-signature': 'ts-traveltool-2025-signature-123456', + }, + ); + + if (response.statusCode == 200) { + try { + print("fun sam f - ${response.statusCode}" ); + + // ✅ Create a blob from the response body bytes + final blob = html.Blob([response.bodyBytes]); + + // ✅ Generate a download URL + final url = html.Url.createObjectUrlFromBlob(blob); + + // ✅ Trigger file download automatically + final anchor = html.AnchorElement(href: url) + ..setAttribute('download', '$post_file_name') + ..click(); + + // ✅ Revoke the URL to free memory + html.Url.revokeObjectUrl(url); + + ToastHelper.showSuccessToast(context, 'File Downloaded Successfully'); + } catch (e) { + print("fun sam f - fail" ); + throw Exception('Error parsing response: $e'); + } + } else { + ToastHelper.showErrorToast(context, 'Failed to download'); + print("Download failed with status: ${response.statusCode}"); + } + } + void _uploadFile() async { print('Test'); if (kIsWeb) { @@ -562,11 +613,10 @@ class _postFileUploadState extends State { constraints: const BoxConstraints(), ), const SizedBox(width: 6), - Container( - // color: Colors.redAccent.shade100, + Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "${widget.cardType} - ${widget.cardPolicyNo} " ?? @@ -581,15 +631,35 @@ class _postFileUploadState extends State { widget.TokenType == 'pre' ? "${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})" : "${widget.cardInsurer_name} - ${widget.cardPolicy_name} (${widget.cardPolicy_ExpDate})", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 12, - fontWeight: FontWeight.w400, - ), + style: GoogleFonts.poppins(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400), ), ], ), ), + // Visibility toggles based on dropdown selection + Visibility( + visible: showSampleButton, + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: SizedBox( + child: ElevatedButton( + onPressed: () { + // Pass the dynamic value to the function + downloadPostSampleFile(currentApiValue ?? ''); + }, + style: ElevatedButton.styleFrom( + backgroundColor: const Color(0xFFE26728), + elevation: 0, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + ), + child: Text( + 'Sample Excel', + style: GoogleFonts.poppins(fontSize: 14, fontWeight: FontWeight.w700, color: Colors.white), + ), + ), + ), + ), + ), ], ), SizedBox(height:20), @@ -606,8 +676,10 @@ class _postFileUploadState extends State { onChanged: (val) { setState(() { selectedKey = val; - selectedValue = getFileUploadMasterList - .firstWhere((e) => e['key'] == val)['value']; + final selectedItem = getFileUploadMasterList.firstWhere((e) => e['key'] == val); + selectedValue = selectedItem['value']; + currentApiValue = selectedItem['key']; + showSampleButton = true; }); }, ), @@ -1089,6 +1161,12 @@ class _postFileUploadState extends State { ); Widget _buildPagination(BuildContext context) { + + final totalItems = filteredData.length; + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -1099,7 +1177,7 @@ class _postFileUploadState extends State { if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } else if (_currentPage >= totalPages - 2) { + } if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -1107,7 +1185,7 @@ class _postFileUploadState extends State { totalPages - 1, totalPages ]; - } else { + } return [ _currentPage - 2, _currentPage - 1, @@ -1115,7 +1193,7 @@ class _postFileUploadState extends State { _currentPage + 1, _currentPage + 2, ]; - } + } List visiblePages = getVisiblePages(); diff --git a/lib/presentation/preFileUpload.dart b/lib/presentation/preFileUpload.dart index b9fa36b..3469d36 100755 --- a/lib/presentation/preFileUpload.dart +++ b/lib/presentation/preFileUpload.dart @@ -882,7 +882,9 @@ class _excelVerifyState extends State { SizedBox( width: 40, height: 40 , - child: ElevatedButton( + child: Tooltip( + message: 'Upload', // The text that appears on hover + child: ElevatedButton( onPressed: () => null, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xFFD4F1F2), @@ -903,6 +905,7 @@ class _excelVerifyState extends State { color: Color(0xFF00999E), ) ), + ), ), const SizedBox(height: 15), Text( @@ -941,7 +944,9 @@ class _excelVerifyState extends State { SizedBox( width: 40, height: 40 , - child: ElevatedButton( + child: Tooltip( + message: 'Upload', // The text that appears on hover + child: ElevatedButton( onPressed: () { if (!_validateDatesBeforeUpload()) return; if (fileName == null) { @@ -967,6 +972,7 @@ class _excelVerifyState extends State { color: Color(0xFF00999E), ) ), + ), ), SizedBox(height: 12), Text('Upload Your Documents', @@ -1175,7 +1181,9 @@ class _excelVerifyState extends State { Row( children: [ if (item['file_error_status'] == '1') - InkWell( + Tooltip( + message: 'Info', // Added tooltip name + child:InkWell( onTap: () async { print(item); // return; @@ -1227,10 +1235,13 @@ class _excelVerifyState extends State { color: Colors.red, ), ), + ), SizedBox(width: 10), _buildStatusChip(item['status']), SizedBox(width: 10), - InkWell( + Tooltip( + message: 'Download', // Added tooltip name + child:InkWell( onTap: () { getHrFileDownload(item['id'], item['file_name']); }, @@ -1249,6 +1260,7 @@ class _excelVerifyState extends State { ), ), ), + ), ], ), /// ⬇ Download @@ -1293,6 +1305,12 @@ class _excelVerifyState extends State { } Widget _buildPagination(BuildContext context) { + // 1. Calculate the range of entries being shown + final totalItems = filteredData.length; + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -1303,7 +1321,8 @@ class _excelVerifyState extends State { if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } else if (_currentPage >= totalPages - 2) { + } + if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -1311,29 +1330,42 @@ class _excelVerifyState extends State { totalPages - 1, totalPages ]; - } else { - return [ - _currentPage - 2, - _currentPage - 1, - _currentPage, - _currentPage + 1, - _currentPage + 2, - ]; } + return [ + _currentPage - 2, + _currentPage - 1, + _currentPage, + _currentPage + 1, + _currentPage + 2, + ]; + } List visiblePages = getVisiblePages(); - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( + return Padding( + // Match this horizontal padding (16) to your Table Header padding for perfect alignment + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right + children: [ + // --- LEFT SIDE: Showing Text --- + Text( + "Showing $startEntry to $endEntry of $totalItems entries", + style: GoogleFonts.poppins( + fontSize: 13, + color: const Color(0xFF585757), + fontWeight: FontWeight.w400, + ), + ), + + // --- RIGHT SIDE: Controls --- + Row( children: [ // Dropdown for rows per page DropdownButton( value: _rowsPerPage, + // focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change items: [5, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, @@ -1371,7 +1403,7 @@ class _excelVerifyState extends State { for (int page in visiblePages) _buildPageButton(page), // Right ellipsis + last page - if (!visiblePages.contains(totalPages)) + if (!visiblePages.contains(totalPages) && totalPages > 0) Row(children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 4), @@ -1389,8 +1421,8 @@ class _excelVerifyState extends State { ), ], ), - ), - ], + ], + ), ); } diff --git a/lib/service/hrDashboardTabs/cd.dart b/lib/service/hrDashboardTabs/cd.dart index 0f11fca..254d2ab 100755 --- a/lib/service/hrDashboardTabs/cd.dart +++ b/lib/service/hrDashboardTabs/cd.dart @@ -553,6 +553,12 @@ class _CdPolicieState extends State { ); Widget _buildPagination(BuildContext context) { + // 1. Calculate the range of entries being shown + final totalItems = filteredData.length; + final int startEntry = totalItems == 0 ? 0 : ((_currentPage - 1) * _rowsPerPage) + 1; + int endEntry = _currentPage * _rowsPerPage; + if (endEntry > totalItems) endEntry = totalItems; + final totalPages = (filteredData.length / _rowsPerPage).ceil(); const visiblePageCount = 5; @@ -563,7 +569,8 @@ class _CdPolicieState extends State { if (_currentPage <= 3) { return [1, 2, 3, 4, 5]; - } else if (_currentPage >= totalPages - 2) { + } + if (_currentPage >= totalPages - 2) { return [ totalPages - 4, totalPages - 3, @@ -571,29 +578,42 @@ class _CdPolicieState extends State { totalPages - 1, totalPages ]; - } else { - return [ - _currentPage - 2, - _currentPage - 1, - _currentPage, - _currentPage + 1, - _currentPage + 2, - ]; } + return [ + _currentPage - 2, + _currentPage - 1, + _currentPage, + _currentPage + 1, + _currentPage + 2, + ]; + } List visiblePages = getVisiblePages(); - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 12), - child: Row( + return Padding( + // Match this horizontal padding (16) to your Table Header padding for perfect alignment + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, // Pushes text to left, buttons to right + children: [ + // --- LEFT SIDE: Showing Text --- + Text( + "Showing $startEntry to $endEntry of $totalItems entries", + style: GoogleFonts.poppins( + fontSize: 13, + color: const Color(0xFF585757), + fontWeight: FontWeight.w400, + ), + ), + + // --- RIGHT SIDE: Controls --- + Row( children: [ // Dropdown for rows per page DropdownButton( value: _rowsPerPage, + // focusColor: Colors.transparent, // Fix: Removes the grey/blue highlight on change items: [5, 10, 15, 20, 50].map((int value) { return DropdownMenuItem( value: value, @@ -609,8 +629,11 @@ class _CdPolicieState extends State { }, ), + const SizedBox(width: 8), + // Previous button IconButton( + tooltip: 'Previous Page', onPressed: _currentPage > 1 ? () => setState(() => _currentPage--) : null, @@ -631,7 +654,7 @@ class _CdPolicieState extends State { for (int page in visiblePages) _buildPageButton(page), // Right ellipsis + last page - if (!visiblePages.contains(totalPages)) + if (!visiblePages.contains(totalPages) && totalPages > 0) Row(children: [ const Padding( padding: EdgeInsets.symmetric(horizontal: 4), @@ -649,8 +672,8 @@ class _CdPolicieState extends State { ), ], ), - ), - ], + ], + ), ); } diff --git a/lib/service/multi_file_upload_widget.dart b/lib/service/multi_file_upload_widget.dart index 57d23f2..a58487f 100755 --- a/lib/service/multi_file_upload_widget.dart +++ b/lib/service/multi_file_upload_widget.dart @@ -203,6 +203,7 @@ class _MultiFileUploadWidgetState extends State { title: Text(uploaded.file.name, style: const TextStyle(fontSize: 14)), trailing: IconButton( icon: const Icon(Icons.close, color: Colors.red), + tooltip: 'Remove', // Built-in property onPressed: () => _removeFile(index), ), ),