Commit d2939607 authored by Sai Srinivas's avatar Sai Srinivas
Browse files

Correction and changes

parent a5ccb211
class ValidateBankAccountDetailsResponse {
String? nameAtBank;
String? bankName;
String? branch;
String? error;
String? message;
int? sessionExists;
ValidateBankAccountDetailsResponse(
{this.nameAtBank,
this.bankName,
this.branch,
this.error,
this.message,
this.sessionExists});
ValidateBankAccountDetailsResponse.fromJson(Map<String, dynamic> json) {
nameAtBank = json['name_at_bank'];
bankName = json['bank_name'];
branch = json['branch'];
error = json['error'];
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name_at_bank'] = this.nameAtBank;
data['bank_name'] = this.bankName;
data['branch'] = this.branch;
data['error'] = this.error;
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class ValidateGstNumResponse {
String? legalNameOfBusiness;
String? address;
String? error;
String? message;
int? sessionExists;
ValidateGstNumResponse(
{this.legalNameOfBusiness,
this.address,
this.error,
this.message,
this.sessionExists});
ValidateGstNumResponse.fromJson(Map<String, dynamic> json) {
legalNameOfBusiness = json['legal_name_of_business'];
address = json['address'];
error = json['error'];
message = json['message'];
sessionExists = json['session_exists'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['legal_name_of_business'] = this.legalNameOfBusiness;
data['address'] = this.address;
data['error'] = this.error;
data['message'] = this.message;
data['session_exists'] = this.sessionExists;
return data;
}
}
class CommonResponse {
String? error;
String? message;
String? qrCode;
String? qrId;
int? sessionExists;
CommonResponse({this.error, this.message, this.sessionExists});
CommonResponse.fromJson(Map<String, dynamic> json) {
error = json['error'];
qrCode = json['qr_code'];
qrId = json['razorpay_order_id'];
message = json['message'];
sessionExists = json['session_exists'];
}
......@@ -15,6 +18,8 @@ class CommonResponse {
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['error'] = error;
data['qr_code'] = qrCode;
data['razorpay_order_id'] = qrId;
data['message'] = message;
data['session_exists'] = sessionExists;
return data;
......
......@@ -208,6 +208,7 @@ class Paymentdetailsprovider extends ChangeNotifier {
_imageName = null;
_image_picked = 0;
resetErrors();
notifyListeners();
} else {}
} else {
......@@ -775,6 +776,22 @@ class Paymentdetailsprovider extends ChangeNotifier {
}
}
void resetErrors() {
nameError = null;
designationError = null;
mobError = null;
altMobError = null;
telError = null;
emailError = null;
selectContactError = null;
selectPaymentError = null;
selectAmountError = null;
ReferenceError = null;
imageError = null;
notifyListeners();
}
resetAddContect() {
Amountcontroller.clear();
Referencecontroller.clear();
......
import 'dart:async';
import 'package:flutter/material.dart';
import '../Models/ordersModels/commonResponse.dart';
import '../Utils/custom_snackbar.dart';
import '../services/api_calling.dart';
class QrProvider extends ChangeNotifier {
CommonResponse? _qrResponse;
bool _isLoading = false;
String? _errorMessage;
int _secondsLeft = 120; // 2 minutes
CommonResponse? get qrResponse => _qrResponse;
bool get isLoading => _isLoading;
String? get errorMessage => _errorMessage;
int get secondsLeft => _secondsLeft;
Timer? _timer;
/// Fetch Razorpay QR API
Future<void> fetchRazorpayQr({
required String sessionId,
required String empId,
required String amount,
required String refType,
required String refId,
}) async {
_isLoading = true;
_errorMessage = null;
notifyListeners();
try {
final response = await ApiCalling.fetchRazorpayUpiQrApi(
sessionId,
empId,
amount,
refType,
refId,
);
if (response != null && response.error == "0") {
_qrResponse = response;
_startTimer();
} else {
_errorMessage = response?.message ?? "Something went wrong";
}
} catch (e) {
_errorMessage = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
/// Start 2-minute countdown
void _startTimer() {
_secondsLeft = 120;
_timer?.cancel();
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
if (_secondsLeft > 0) {
_secondsLeft--;
notifyListeners();
} else {
timer.cancel();
}
});
}
/// Dispose timer properly
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
bool _isPaymentUpdating = false;
bool get isPaymentUpdating => _isPaymentUpdating;
set isPaymentUpdating(bool value) {
_isPaymentUpdating = value;
notifyListeners();
}
/// Fetch Razorpay QR Payment Status
Future<CommonResponse?> fetchRazorpayUpiQrStatus({
required BuildContext context,
required String sessionId,
required String empId,
required String razorpayOrderId,
}) async {
try {
isPaymentUpdating = true;
final response = await ApiCalling.fetchRazorpayUpiQrStatusApi(
sessionId,
empId,
razorpayOrderId,
);
if (response != null) {
if (response.sessionExists == 1 && response.error == "0") {
debugPrint(" Payment Status: ${response.message}");
} else {
CustomSnackBar.showWarning(
context: context,
message: "⚠️ Payment not yet completed or failed"
);
debugPrint("⚠️ Payment not yet completed or failed");
}
} else {
debugPrint("❌ Null response from Razorpay QR Status API");
}
return response; // return here
} catch (e) {
debugPrint("❌ fetchRazorpayUpiQrStatus error: $e");
return null;
} finally {
isPaymentUpdating = false;
}
}
}
import 'package:flutter/material.dart';
import 'package:generp/services/api_calling.dart';
import '../../Models/financeModels/ValidateBankAccountDetailsResponse.dart';
import '../../Models/financeModels/ValidateGstNumResponse.dart';
class ValidationProvider extends ChangeNotifier {
bool isLoading = false;
ValidateGstNumResponse? gstResponse;
ValidateBankAccountDetailsResponse? bankResponse;
String? errorMessage;
/// Validate GST Number
/// -----------------------------
Future<void> validateGstNumber(
String empId, String sessionId, String gstNumber) async {
try {
isLoading = true;
errorMessage = null;
notifyListeners();
gstResponse = await ApiCalling.validateGstNumberApi(
empId,
sessionId,
gstNumber,
);
if (gstResponse == null) {
errorMessage = "Failed to validate GST number";
}
} catch (e) {
errorMessage = e.toString();
}
isLoading = false;
notifyListeners();
}
/// Validate Bank Account Details
/// -----------------------------
Future<void> validateBankDetails(
String empId, String sessionId, String accountNumber) async {
try {
isLoading = true;
errorMessage = null;
notifyListeners();
bankResponse = await ApiCalling.validateBankAccountDetailsApi(
empId,
sessionId,
accountNumber,
""
);
if (bankResponse == null) {
errorMessage = "Failed to validate bank account";
}
} catch (e) {
errorMessage = e.toString();
}
isLoading = false;
notifyListeners();
}
/// Reset before calling again
void clear() {
gstResponse = null;
bankResponse = null;
errorMessage = null;
notifyListeners();
}
}
// accountsListProvider.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
......@@ -12,6 +13,8 @@ import '../../Models/commonModels/DistrictsResponse.dart';
import '../../Models/commonModels/SubLocationsResponse.dart';
import '../../Models/commonModels/commonAccountlistResponse.dart';
import '../../Models/commonModels/commonAddAccountsViewResponse.dart';
import '../../Models/financeModels/ValidateBankAccountDetailsResponse.dart';
import '../../Models/financeModels/ValidateGstNumResponse.dart';
class Accountslistprovider extends ChangeNotifier {
TextEditingController nameController = TextEditingController();
......@@ -21,11 +24,11 @@ class Accountslistprovider extends ChangeNotifier {
TextEditingController branchNameController = TextEditingController();
TextEditingController bankIfscCotroller = TextEditingController();
TextEditingController bankHolderNameController = TextEditingController();
TextEditingController gstNumberController = TextEditingController();
TextEditingController bankAcNumberController = TextEditingController();
TextEditingController bankUpiController = TextEditingController();
TextEditingController contactPersonController = TextEditingController();
TextEditingController contectPersonDesignationController =
TextEditingController();
TextEditingController contectPersonDesignationController = TextEditingController();
TextEditingController contectPersonAltMobController = TextEditingController();
TextEditingController contectPersonTeleController = TextEditingController();
TextEditingController contectPersonMailController = TextEditingController();
......@@ -43,6 +46,7 @@ class Accountslistprovider extends ChangeNotifier {
String? districtError;
String? localityError;
String? addressError;
String? gstNumberError;
String? banknameError;
String? bankBranchError;
String? bankIFSCError;
......@@ -98,6 +102,11 @@ class Accountslistprovider extends ChangeNotifier {
notifyListeners();
}
ValidateGstNumResponse? gstResponse;
ValidateBankAccountDetailsResponse? bankResponse;
String? errorMessage;
States? _selectedState;
Districts? _selectedDistricts;
SubLocations? _selectedSubLocations;
......@@ -132,6 +141,12 @@ class Accountslistprovider extends ChangeNotifier {
String? get selectedAccountType => _selectedAccountType;
// --- NEW: setter for isLoading so callers can do `isLoading = true;` ---
set isLoading(bool value) {
_isLoading = value;
notifyListeners();
}
set selectedAccountType(String? value) {
_selectedAccountType = value;
accountError = null;
......@@ -140,24 +155,24 @@ class Accountslistprovider extends ChangeNotifier {
set selectedState(States? value) {
_selectedState = value;
_selectedStateID = value!.id;
_selectedStateValue = value.name;
_selectedStateID = value?.id;
_selectedStateValue = value?.name;
stateError = null;
notifyListeners();
}
set selectedDistricts(Districts? value) {
_selectedDistricts = value;
_selectedDistrictID = value!.id;
_selectedDistrictValue = value.district;
_selectedDistrictID = value?.id;
_selectedDistrictValue = value?.district;
districtError = null;
notifyListeners();
}
set selectedSubLocations(SubLocations? value) {
_selectedSubLocations = value;
_selectedSubLocID = value!.id;
_selectedSubLocValue = value.subLocality!;
_selectedSubLocID = value?.id;
_selectedSubLocValue = value?.subLocality;
localityError = null;
notifyListeners();
}
......@@ -192,6 +207,8 @@ class Accountslistprovider extends ChangeNotifier {
notifyListeners();
}
// ---------- API / data loading ----------
Future<void> addCommonAccountViewAPI(context) async {
try {
var homeProv = Provider.of<HomescreenNotifier>(context, listen: false);
......@@ -205,12 +222,15 @@ class Accountslistprovider extends ChangeNotifier {
if (data != null) {
if (data.error == "0") {
ChechkDropdownValues();
_accountTypes = data.accountTypes!;
_states = data.states!;
_accountTypes = data.accountTypes ?? [];
_states = data.states ?? [];
notifyListeners();
}
}
} catch (e) {}
} catch (e) {
// keep quiet but log in debug
if (kDebugMode) debugPrint('addCommonAccountViewAPI error: $e');
}
}
Future<void> getDistrictAPI(context, stateID) async {
......@@ -225,11 +245,13 @@ class Accountslistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_districts = data.districts!;
_districts = data.districts ?? [];
notifyListeners();
}
}
} catch (e) {}
} catch (e) {
if (kDebugMode) debugPrint('getDistrictAPI error: $e');
}
}
Future<void> getSubLocationAPI(context, districtID) async {
......@@ -244,11 +266,13 @@ class Accountslistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_subLocations = data.subLocations!;
_subLocations = data.subLocations ?? [];
notifyListeners();
}
}
} catch (e) {}
} catch (e) {
if (kDebugMode) debugPrint('getSubLocationAPI error: $e');
}
}
Future<void> checkInputsAPI(context, type, value) async {
......@@ -272,10 +296,133 @@ class Accountslistprovider extends ChangeNotifier {
mobileError = data.message ?? "";
}
notifyListeners();
// toast(context, data.message);
}
}
} catch (e) {}
} catch (e) {
if (kDebugMode) debugPrint('checkInputsAPI error: $e');
}
}
/// Validate GST Number
/// Returns true if validated successfully (and sets gstResponse). Caller can await.
Future<bool> validateGstNumber(
String empId, String sessionId, String gstNumber) async {
// if empty — don't call API; clear any previous errors/responses
if (gstNumber.trim().isEmpty) {
gstResponse = null;
gstNumberError = null;
notifyListeners();
return false;
}
try {
isLoading = true;
errorMessage = null;
// call API
final response = await ApiCalling.validateGstNumberApi(
empId,
sessionId,
gstNumber,
);
// assign
gstResponse = response;
if (response == null) {
gstNumberError = "Failed to validate GST number";
notifyListeners();
return false;
}
// Response object expected to contain `error` and `address` and `legal_name_of_business`
if (response.error == "0") {
gstNumberError = null;
// Optionally update address or other fields in UI (UI will read gstResponse)
notifyListeners();
return true;
} else {
// API returned invalid GST
gstNumberError = response.message ?? "Invalid GST number";
notifyListeners();
return false;
}
} catch (e) {
gstNumberError = "Error validating GST: ${e.toString()}";
if (kDebugMode) debugPrint('validateGstNumber error: $e');
notifyListeners();
return false;
} finally {
isLoading = false;
notifyListeners();
}
}
/// Validate Bank Account Details
/// Returns true if validated successfully (and sets bankResponse). Caller can await.
Future<bool> validateBankDetails(
String empId, String sessionId, String accountNumber) async {
// require accountNumber and IFSC to be present
final ifsc = bankIfscCotroller.text.trim();
if (accountNumber.trim().isEmpty || ifsc.isEmpty) {
// clear previous bankResponse if user cleared input
bankResponse = null;
bankAcNumberError = null;
bankIFSCError = null;
notifyListeners();
return false;
}
// basic IFSC format check (caller/UI also checks) — we trust UI but double-check
final reg = RegExp(r'^[A-Za-z]{4}0[A-Za-z0-9]{6}$');
if (!reg.hasMatch(ifsc)) {
bankIFSCError = "Invalid IFSC format";
notifyListeners();
return false;
}
try {
isLoading = true;
errorMessage = null;
// call API - pass account number and IFSC from controller (some APIs require both)
final response = await ApiCalling.validateBankAccountDetailsApi(
empId,
sessionId,
accountNumber,
ifsc,
);
bankResponse = response;
if (response == null) {
bankAcNumberError = "Failed to validate bank account";
notifyListeners();
return false;
}
if (response.error == "0") {
// success — populate fields in UI from the provider.bankResponse (UI will take the values)
bankAcNumberError = null;
bankIFSCError = null;
banknameError = null;
bankBranchError = null;
bankHolderNameError = null;
notifyListeners();
return true;
} else {
// error returned
bankAcNumberError = response.message ?? "Invalid account details";
notifyListeners();
return false;
}
} catch (e) {
bankAcNumberError = "Error validating account: ${e.toString()}";
if (kDebugMode) debugPrint('validateBankDetails error: $e');
notifyListeners();
return false;
} finally {
isLoading = false;
notifyListeners();
}
}
bool hasFilledAdditionalDetails = false;
......@@ -292,7 +439,6 @@ class Accountslistprovider extends ChangeNotifier {
Future<void> submitCommonAccountsAPI(context, from) async {
try {
if (!validatereceiptForm(context)) {
// _submitClicked = false;
return;
}
_submitClicked = true;
......@@ -327,14 +473,12 @@ class Accountslistprovider extends ChangeNotifier {
if (from == "Requesition") {
Navigator.pop(context, res);
} else if (from == "Dashboard") {
print("here");
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => Accountslistdetails(accountID: data.id),
),
(Route<dynamic> route) {
(Route<dynamic> route) {
return route.settings.arguments == 'Financedashboard';
},
);
......@@ -374,8 +518,7 @@ class Accountslistprovider extends ChangeNotifier {
nameError = "Please Enter a Name";
isValid = false;
}
if (mobileController.text.trim().isNotEmpty &&
mobileController.text.length < 10) {
if (mobileController.text.trim().isNotEmpty && mobileController.text.length < 10) {
mobileError = "Mobile Number should be 10 digits";
isValid = false;
}
......@@ -398,22 +541,7 @@ class Accountslistprovider extends ChangeNotifier {
localityError = null;
addressError = null;
bool isValid = true;
// if (_selectedState == null || _selectedStateID!.isEmpty) {
// stateError = "Please select a State";
// isValid = false;
// }
// if (_selectedDistricts == null || _selectedDistrictID!.isEmpty) {
// districtError = "Please select a District";
// isValid = false;
// }
// if (_selectedSubLocations == null || _selectedSubLocID!.isEmpty) {
// localityError = "Please select a Locality";
// isValid = false;
// }
// if (addressController.text.trim().isEmpty) {
// addressError = "Please Enter an Address";
// isValid = false;
// }
// optional step — you commented it optional. Keep commented validations deactivated.
_submitClicked = false;
notifyListeners();
return isValid;
......@@ -426,44 +554,8 @@ class Accountslistprovider extends ChangeNotifier {
bankHolderNameError = null;
bankAcNumberError = null;
upiError = null;
gstNumberError = null;
bool isValid = true;
// if (bankNameController.text
// .trim()
// .isEmpty) {
// banknameError = "Please Enter a Bank Name";
// isValid = false;
// }
//
// if (branchNameController.text
// .trim()
// .isEmpty) {
// bankBranchError = "Please Enter Branch Name";
// isValid = false;
// }
// if (bankIfscCotroller.text
// .trim()
// .isEmpty) {
// bankIFSCError = "Please Enter Bank IFSC";
// isValid = false;
// }
// if (bankHolderNameController.text
// .trim()
// .isEmpty) {
// bankHolderNameError = "Please Enter Account Holder Name";
// isValid = false;
// }
// if (bankAcNumberController.text
// .trim()
// .isEmpty) {
// bankAcNumberError = "Please Enter Bank Account Number";
// isValid = false;
// }
// if (bankUpiController.text
// .trim()
// .isEmpty) {
// upiError = "Please Enter UPI ID";
// isValid = false;
// }
_submitClicked = false;
notifyListeners();
return isValid;
......@@ -471,30 +563,8 @@ class Accountslistprovider extends ChangeNotifier {
bool validateStep4() {
bool isValid = true;
// if (contectPersonDesignationController.text
// .trim()
// .isEmpty) {
// desigantionError = "Please Enter Designation";
// isValid = false;
// }
// if (contectPersonAltMobController.text
// .trim()
// .isEmpty) {
// altMobError = "Please Enter Mobile Number";
// isValid = false;
// }
//
// }
// if (contectPersonTeleController.text.trim().isEmpty) {
// teleError= "Please Enter Telephone"; isValid = false;
// }
// if (contectPersonMailController.text.trim().isEmpty) {
// teleError= "Please Enter"; isValid = false;
// }
_submitClicked = false;
notifyListeners();
return isValid;
}
......@@ -507,6 +577,7 @@ class Accountslistprovider extends ChangeNotifier {
localityError = null;
addressError = null;
banknameError = null;
gstNumberError = null;
bankBranchError = null;
bankIFSCError = null;
bankHolderNameError = null;
......@@ -535,94 +606,7 @@ class Accountslistprovider extends ChangeNotifier {
contactPersonError = "Please Enter Contact Person Name";
isValid = false;
}
//
//
// if (!_isVisible && !hasFilledAdditionalDetails) {
// addMoreDetailsError = "Please Add More Details to Continue";
// isValid = false;
// }
// if(_isVisible) {
//
// if (_selectedState == null || _selectedStateID!.isEmpty) {
// stateError = "Please select a State";
// isValid = false;
// }
// if (_selectedDistricts == null || _selectedDistrictID!.isEmpty) {
// districtError = "Please select a District";
// isValid = false;
// }
// if (_selectedSubLocations == null || _selectedSubLocID!.isEmpty) {
// localityError = "Please select a Locality";
// isValid = false;
// }
// if (addressController.text.trim().isEmpty) {
// addressError = "Please Enter an Address";
// isValid = false;
// }
// if (bankNameController.text
// .trim()
// .isEmpty) {
// banknameError = "Please Enter a Bank Name";
// isValid = false;
// }
//
// if (branchNameController.text
// .trim()
// .isEmpty) {
// bankBranchError = "Please Enter Branch Name";
// isValid = false;
// }
// if (bankIfscCotroller.text
// .trim()
// .isEmpty) {
// bankIFSCError = "Please Enter Bank IFSC";
// isValid = false;
// }
// if (bankHolderNameController.text
// .trim()
// .isEmpty) {
// bankHolderNameError = "Please Enter Account Holder Name";
// isValid = false;
// }
// if (bankAcNumberController.text
// .trim()
// .isEmpty) {
// bankAcNumberError = "Please Enter Bank Account Number";
// isValid = false;
// }
// if (bankUpiController.text
// .trim()
// .isEmpty) {
// upiError = "Please Enter UPI ID";
// isValid = false;
// }
// if (contactPersonController.text
// .trim()
// .isEmpty) {
// contactPersonError = "Please Enter Contact Person Name";
// isValid = false;
// }
// if (contectPersonDesignationController.text
// .trim()
// .isEmpty) {
// desigantionError = "Please Enter Designation";
// isValid = false;
// }
// if (contectPersonAltMobController.text
// .trim()
// .isEmpty) {
// altMobError = "Please Enter Mobile Number";
// isValid = false;
// }
//
// }
// if (contectPersonTeleController.text.trim().isEmpty) {
// teleError= "Please Enter Telephone"; isValid = false;
// }
// if (contectPersonMailController.text.trim().isEmpty) {
// teleError= "Please Enter"; isValid = false;
// }
_submitClicked = false;
notifyListeners();
return isValid;
......@@ -645,67 +629,64 @@ class Accountslistprovider extends ChangeNotifier {
void updateBankName(String value) {
banknameError = null;
notifyListeners();
}
/// simplified update - kept for compatibility if UI uses it
Future<void> updateGSTNumber(BuildContext context, String value) async {
// We won't call the GST API here — UI flow calls validateGstNumber explicitly.
// This function simply clears previous error on typing.
gstNumberError = null;
notifyListeners();
}
void updateBankBranch(String value) {
bankBranchError = null;
notifyListeners();
}
void updateIFSC(String value) {
bankIFSCError = null;
notifyListeners();
}
void updateHolder(String value) {
bankHolderNameError = null;
notifyListeners();
}
void updateNumber(String value) {
bankAcNumberError = null;
notifyListeners();
}
void updateUPI(String value) {
upiError = null;
notifyListeners();
}
void updateContactPerson(String value) {
contactPersonError = null;
notifyListeners();
}
void updateDesignation(String value) {
desigantionError = null;
notifyListeners();
}
void updateAltMobile(String value) {
altMobError = null;
notifyListeners();
}
void updateTeleMobile(String value) {
teleError = null;
notifyListeners();
}
void updateMail(String value) {
mailError = null;
notifyListeners();
}
......@@ -717,14 +698,11 @@ class Accountslistprovider extends ChangeNotifier {
notifyListeners();
}
String? _errorMessage;
String? get errorMessage => _errorMessage;
// Common Account list provider
Future<void> commonAccountListAPIFunction(
BuildContext context, {
bool append = false,
}) async {
BuildContext context, {
bool append = false,
}) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
......@@ -747,19 +725,13 @@ class Accountslistprovider extends ChangeNotifier {
companyNameController.text,
mobileNumberController.text,
);
debugPrint(
'empId: ${prov.empId}, session: ${prov.session}, pageNumber: $_pageNum',
);
debugPrint('empId: ${prov.empId}, session: ${prov.session}, pageNumber: $_pageNum');
if (data != null && data.error == "0") {
if (data.accountList != null) {
if (append) {
// Append with deduplication
final existingIds = _accountsList.map((e) => e.id).toSet();
final newItems =
data.accountList!
.where((item) => !existingIds.contains(item.id))
.toList();
final newItems = data.accountList!.where((item) => !existingIds.contains(item.id)).toList();
_accountsList.addAll(newItems);
} else {
_accountsList = data.accountList!;
......@@ -779,7 +751,7 @@ class Accountslistprovider extends ChangeNotifier {
}
resetValues() {
print("rv");
if (kDebugMode) debugPrint("rv");
_selectedAccountType = null;
_selectedState = null;
_selectedDistricts = null;
......@@ -791,6 +763,10 @@ class Accountslistprovider extends ChangeNotifier {
_selectedSubLocID = null;
_selectedSubLocValue = null;
gstResponse = null;
bankResponse = null;
errorMessage = null;
_isVisible = false;
stateSearchController.clear();
districtSearchController.clear();
......@@ -819,6 +795,7 @@ class Accountslistprovider extends ChangeNotifier {
localityError = null;
addressError = null;
banknameError = null;
gstNumberError = null;
bankBranchError = null;
bankIFSCError = null;
bankHolderNameError = null;
......@@ -836,21 +813,18 @@ class Accountslistprovider extends ChangeNotifier {
}
void ChechkDropdownValues() {
if (!_accountTypes.contains(_selectedAccountType) &&
_selectedAccountType != null) {
if (!_accountTypes.contains(_selectedAccountType) && _selectedAccountType != null) {
_selectedAccountType = null;
}
if (!_states.contains(_selectedState) && _selectedState != null) {
_selectedStateID = null;
_selectedStateValue = null;
}
if (!_districts.contains(_selectedDistricts) &&
_selectedDistricts != null) {
if (!_districts.contains(_selectedDistricts) && _selectedDistricts != null) {
_selectedDistrictID = null;
_selectedDistrictValue = null;
}
if (!_subLocations.contains(_selectedSubLocations) &&
_selectedSubLocations != null) {
if (!_subLocations.contains(_selectedSubLocations) && _selectedSubLocations != null) {
_selectedSubLocID = null;
_selectedSubLocValue = null;
}
......
// import 'package:flutter/cupertino.dart';
// import 'package:flutter/foundation.dart';
// import 'package:generp/Models/crmModels/GetSegmentOnTeamResponse.dart';
// import 'package:generp/Models/crmModels/GetSourceOnReferenceResponse.dart';
// import 'package:generp/Models/crmModels/LeadListViewResponse.dart';
// import 'package:generp/Models/crmModels/SubmitLeadListFilterResponse.dart';
// import 'package:provider/provider.dart';
//
// import '../../Models/crmModels/GetDistrictOnStateResponse.dart';
// import '../../Models/crmModels/GetSubLocOnDistrictResponse.dart';
// import '../../Models/crmModels/LeadListContactPopUpResponse.dart' show Contacts;
// import '../../services/api_calling.dart';
// import '../HomeScreenNotifier.dart';
//
// class Leadlistprovider extends ChangeNotifier {
// TextEditingController sLeadIDController = TextEditingController();
// TextEditingController mobileNumberController = TextEditingController();
// TextEditingController companyNameController = TextEditingController(
// text: "a",
// );
//
// bool _isLoading = true;
//
// List<Sources> _sourcesList = [];
// List<Teams> _teamsList = [];
// List<States> _statesList = [];
// List<Employees> _employeesList = [];
// List<References> _referencesList = [];
// List<Segments> _segmentsList = [];
// List<Districts> _districtsList = [];
// List<SubLocations> _subLocationsList = [];
// List<Contacts> _contactsList = [];
// List<LeadList> _crmLeadList = [];
// List<String?> _leadStatusList = [];
// List<String?> _openStatusList = [];
// List<String?> _alphabetList = [];
//
//
// Sources? _selectedSources;
// Teams? _selectedTeams;
// States? _selectedStates;
// Employees? _selectedEmployees;
// References? _selectedReferences;
// Segments? _selectedSegments;
// Districts? _selectedDistricts;
// SubLocations? _selectedSubLocations;
// Contacts? _selectedContacts;
// String? _selectedLeadStatus;
// String? _selectedOpenStatus;
// String? _selectedAlphabet;
//
// String? _selectedSourceId;
// String? _selectedSourceValue;
//
// String? _selectedTeamId;
// String? _selectedTeamValue;
//
// String? _selectedStateId;
// String? _selectedStateValue;
//
// String? _selectedEmployeeId;
// String? _selectedEmployeeValue;
//
// String? _selectedReferenceId;
// String? _selectedReferenceValue;
//
// String? _selectedSegmentId;
// String? _selectedSegmentValue;
//
// String? _selectedDistrictId;
// String? _selectedDistrictValue;
//
// String? _selectedSubLocationId;
// String? _selectedSubLocationValue;
//
// String? _selectedContactId;
// String? _selectedContactValue;
//
// bool get isLoading => _isLoading;
//
// List<Sources> get sourcesList => _sourcesList;
//
// List<Teams> get teamsList => _teamsList;
//
// List<States> get statesList => _statesList;
//
// List<Employees> get employeesList => _employeesList;
//
// List<References> get referencesList => _referencesList;
//
// List<Segments> get segmentsList => _segmentsList;
//
// List<Districts> get districtsList => _districtsList;
//
// List<SubLocations> get subLocationsList => _subLocationsList;
//
// List<Contacts> get contactsList => _contactsList;
//
// List<LeadList> get crmLeadList => _crmLeadList;
//
// List<String?> get leadStatusList => _leadStatusList;
//
// List<String?> get alphabetList => _alphabetList;
//
// List<String?> get openStatusList => _openStatusList;
//
// Sources? get selectedSource => _selectedSources;
//
// Teams? get selectedTeam => _selectedTeams;
//
// States? get selectedStates => _selectedStates;
//
// Employees? get selectedEmployee => _selectedEmployees;
//
// References? get selectedReference => _selectedReferences;
//
// Segments? get selectedSegment => _selectedSegments;
//
// Districts? get selectedDistricts => _selectedDistricts;
//
// SubLocations? get selectedSubLocations => _selectedSubLocations;
//
// Contacts? get selectedContacts => _selectedContacts;
//
// String? get selectedLeadStatus => _selectedLeadStatus;
//
// String? get selectedOpenStatus => _selectedOpenStatus;
//
// String? get selectedSourceId => _selectedSourceId;
//
// String? get selectedSourceValue => _selectedSourceValue;
//
// String? get selectedTeamId => _selectedTeamId;
//
// String? get selectedTeamValue => _selectedTeamValue;
//
// String? get selectedStateId => _selectedStateId;
//
// String? get selectedStateValue => _selectedStateValue;
//
// String? get selectedEmployeeId => _selectedEmployeeId;
//
// String? get selectedEmployeeValue => _selectedEmployeeValue;
//
// String? get selectedReferenceId => _selectedReferenceId;
//
// String? get selectedReferenceValue => _selectedReferenceValue;
//
// String? get selectedSegmentId => _selectedSegmentId;
//
// String? get selectedSegmentValue => _selectedSegmentValue;
//
// String? get selectedDistrictId => _selectedDistrictId;
//
// String? get selectedDistrictValue => _selectedDistrictValue;
//
// String? get selectedSubLocationId => _selectedSubLocationId;
//
// String? get selectedSubLocationValue => _selectedSubLocationValue;
//
// String? get selectedContactId => _selectedContactId;
//
// String? get selectedContactValue => _selectedContactValue;
//
// String? get selectedAlphabet => _selectedAlphabet;
//
// set isLoading(bool value) {
// _isLoading = value;
// notifyListeners();
// }
//
// set selectedAlphabet(String? value){
// _selectedAlphabet = value;
// notifyListeners();
// }
//
// set selectedSource(Sources? value) {
// _selectedSources = value;
// _selectedSourceId = value!.id!;
// _selectedSourceValue = value!.name!;
// notifyListeners();
// }
//
// set selectedTeam(Teams? value) {
// _selectedTeams = value;
// _selectedTeamId = value!.id!;
// _selectedTeamValue = value.name;
// notifyListeners();
// }
//
// set selectedStates(States? value) {
// _selectedStates = value;
// _selectedStateId = value!.id!;
// _selectedStateValue = value.name;
// notifyListeners();
// }
//
// set selectedEmployee(Employees? value) {
// _selectedEmployees = value;
// _selectedEmployeeId = value!.id!;
// _selectedEmployeeValue = value.name;
// notifyListeners();
// }
//
// set selectedReference(References? value) {
// _selectedReferences = value;
// _selectedReferenceId = value!.id!;
// _selectedReferenceValue = value.name;
// notifyListeners();
// }
//
// set selectedSegment(Segments? value) {
// _selectedSegments = value;
// _selectedSegmentId = value!.id!;
// _selectedSegmentValue = value.name;
// notifyListeners();
// }
//
// set selectedDistricts(Districts? value) {
// _selectedDistricts = value;
// _selectedDistrictId = value!.id!;
// _selectedDistrictValue = value.district;
// notifyListeners();
// }
//
// set selectedSubLocations(SubLocations? value) {
// _selectedSubLocations = value;
// _selectedSubLocationId = value!.id!;
// _selectedSubLocationValue = value.subLocality;
// notifyListeners();
// }
//
// set selectedContacts(Contacts? value) {
// _selectedContacts = value;
// _selectedContactId = value!.id!;
// _selectedContactValue = value.name;
// notifyListeners();
// }
//
// set selectedLeadStatus(String? value) {
// _selectedLeadStatus = value;
// notifyListeners();
// }
//
// set selectedOpenStatus(String? value) {
// _selectedOpenStatus = value;
// notifyListeners();
// }
//
// set selectedSourceId(String? value) {
// _selectedSourceId = value;
// notifyListeners();
// }
//
// set selectedSourceValue(String? value) {
// _selectedSourceValue = value;
// notifyListeners();
// }
//
// set selectedTeamId(String? value) {
// _selectedTeamId = value;
// notifyListeners();
// }
//
// set selectedTeamValue(String? value) {
// _selectedTeamValue = value;
// notifyListeners();
// }
//
// set selectedStateId(String? value) {
// _selectedStateId = value;
// notifyListeners();
// }
//
// set selectedStateValue(String? value) {
// _selectedStateValue = value;
// notifyListeners();
// }
//
// set selectedEmployeeId(String? value) {
// _selectedEmployeeId = value;
// notifyListeners();
// }
//
// set selectedEmployeeValue(String? value) {
// _selectedEmployeeValue = value;
// notifyListeners();
// }
//
// set selectedReferenceId(String? value) {
// _selectedReferenceId = value;
// notifyListeners();
// }
//
// set selectedReferenceValue(String? value) {
// _selectedReferenceValue = value;
// notifyListeners();
// }
//
// set selectedSegmentId(String? value) {
// _selectedSegmentId = value;
// notifyListeners();
// }
//
// set selectedSegmentValue(String? value) {
// _selectedSegmentValue = value;
// notifyListeners();
// }
//
// set selectedDistrictId(String? value) {
// _selectedDistrictId = value;
// notifyListeners();
// }
//
// set selectedDistrictValue(String? value) {
// _selectedDistrictValue = value;
// notifyListeners();
// }
//
// set selectedSubLocationId(String? value) {
// _selectedSubLocationId = value;
// notifyListeners();
// }
//
// set selectedSubLocationValue(String? value) {
// _selectedSubLocationValue = value;
// notifyListeners();
// }
//
// set selectedContactId(String? value) {
// _selectedContactId = value;
// notifyListeners();
// }
//
// set selectedContactValue(String? value) {
// _selectedContactValue = value;
// notifyListeners();
// }
//
// Future<void> crmLeadListViewAPIFunction(context, mode) async {
// try {
// var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmLeadListViewAPI(
// HomeProv.empId,
// HomeProv.session,
// mode,
// );
// if (data != null) {
// print(data.error);
// if (data.error == "0") {
// // print("as");
// _leadStatusList = [
// "all",
// "Hot",
// "Warm",
// "Cold",
// "Order Gain",
// "Order Lost",
// ];
// _alphabetList = List.generate(26, (index) => String.fromCharCode(index + 65));
//
// _openStatusList = ["open", "Closed", "All"];
// _sourcesList = data.sources!;
// _teamsList = data.teams!;
// _statesList = data.states!;
// _employeesList = data.employees!;
// checkDropDownValues();
// // print(_leadStatusList);
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListSourceOnReferenceAPIFunction(
// context,
// mode,
// sourceID,
// ) async {
// try {
// var prov = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmLeadListSourceOnReferenceAPI(
// prov.empId,
// prov.session,
// sourceID,
// );
// if (data != null) {
// if (data.error == "0") {
// _referencesList = data.references!;
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListSegmentOnTeamAPIFunction(
// context,
// mode,
// teamID,
// ) async {
// try {
// var prov = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmLeadListSegmentOnTeamAPI(
// prov.empId,
// prov.session,
// teamID,
// );
// if (data != null) {
// if (data.error == "0") {
// _segmentsList = data.segments!;
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListDistrictsOnStateAPIFunction(
// context,
// mode,
// stateID,
// ) async {
// try {
// var prov = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmDistrictsOnStateAPI(
// prov.empId,
// prov.session,
// stateID,
// );
// if (data != null) {
// if (data.error == "0") {
// _districtsList = data.districts!;
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListSubLocOnDistrictAPIFunction(
// context,
// mode,
// districtID,
// ) async {
// try {
// var prov = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmSubLocOnDistrictAPI(
// prov.empId,
// prov.session,
// districtID,
// );
// if (data != null) {
// if (data.error == "0") {
// _subLocationsList = data.subLocations!;
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListContactPopUpAPIFunction(
// context,
// mode,
// accountID,
// ) async {
// try {
// var prov = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmLeadListContactPopUpAPI(
// prov.empId,
// prov.session,
// accountID,
// );
// if (data != null) {
// if (data.error == "0") {
// _contactsList = data.contacts!;
// notifyListeners();
// }
// }
// } catch (e, s) {}
// }
//
// Future<void> crmLeadListAPIFunction(
// context,
// mode,
// leadStatus,
// openStatus,
// sourceID,
// referenceID,
// teamID,
// segmentID,
// alphabet
// ) async {
// try {
// _isLoading = true;
// _crmLeadList.clear();
// notifyListeners();
// var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
// final data = await ApiCalling.crmLeadListFilterSubmitAPI(
// HomeProv.empId,
// HomeProv.session,
// mode,
// leadStatus,
// openStatus,
// mobileNumberController.text,
// companyNameController.text,
// sourceID,
// referenceID,
// teamID,
// segmentID,
// alphabet
// );
// if (data != null) {
// _isLoading = true;
// notifyListeners();
// if (data.error == "0") {
// _crmLeadList = data.leadList!;
// _isLoading = false;
// checkDropDownValues();
// notifyListeners();
// } else {
// _isLoading = false;
// notifyListeners();
// }
// } else {
// _isLoading = false;
// notifyListeners();
// }
// } catch (e, s) {
// _isLoading = false;
// notifyListeners();
// }
// }
//
// onChangedLeadId(value) {
// notifyListeners();
// }
//
// onChangedMobileNum(value) {
// notifyListeners();
// }
//
// onChangedCompanyName(value) {
// notifyListeners();
// }
//
// void resetForm() {
// _isLoading = false;
// sLeadIDController.clear();
// mobileNumberController.clear();
// companyNameController.clear();
// companyNameController.text = "a";
// _selectedEmployees = null;
// _selectedSources = null;
// _selectedReferences = null;
// _selectedSegments = null;
// _selectedTeams = null;
// _selectedDistricts = null;
// _selectedStates = null;
// _selectedSubLocations = null;
// _selectedLeadStatus = null;
// _selectedOpenStatus = null;
// _selectedEmployeeId = null;
// _selectedSourceId = null;
// _selectedReferenceId = null;
// _selectedTeamId = null;
// _selectedSegmentId = null;
// _selectedStateId = null;
// _selectedDistrictId = null;
// _selectedSubLocationId = null;
//
// _selectedEmployeeValue = null;
// _selectedSourceValue = null;
// _selectedReferenceValue = null;
// _selectedTeamValue = null;
// _selectedSegmentValue = null;
// _selectedDistrictValue = null;
// _selectedStateValue = null;
// _selectedSubLocationValue = null;
// checkDropDownValues();
// notifyListeners();
// }
//
// void checkDropDownValues() {
// if (!_employeesList.contains(_selectedEmployees) &&
// _selectedEmployees != null) {
// _selectedEmployeeId = null;
// _selectedEmployeeValue = null;
// }
// if (!_sourcesList.contains(_selectedSources) && _selectedSources != null) {
// _selectedSourceId = null;
// _selectedSourceValue = null;
// }
// if (!_referencesList.contains(_selectedReferences) &&
// _selectedReferences != null) {
// _selectedReferenceId = null;
// _selectedReferenceValue = null;
// }
// if (!_segmentsList.contains(_selectedSegments) &&
// _selectedSegments != null) {
// _selectedTeamId = null;
// _selectedTeamValue = null;
// }
// if (!_teamsList.contains(_selectedTeams) && _selectedTeams != null) {
// _selectedSegmentId = null;
// _selectedSegmentValue = null;
// }
// if (!_districtsList.contains(_selectedDistricts) &&
// _selectedDistricts != null) {
// _selectedDistrictId = null;
// _selectedDistrictValue = null;
// }
// if (!_statesList.contains(_selectedStates) && _selectedStates != null) {
// _selectedStateId = null;
// _selectedStateValue = null;
// }
// if (!_subLocationsList.contains(_selectedSubLocations) &&
// _selectedSubLocations != null) {
// _selectedSubLocationId = null;
// _selectedSubLocationValue = null;
// }
// notifyListeners();
// }
// }
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:generp/Models/crmModels/GetSegmentOnTeamResponse.dart';
......@@ -697,89 +76,49 @@ class Leadlistprovider extends ChangeNotifier {
bool get isLoading => _isLoading;
List<Sources> get sourcesList => _sourcesList;
List<Teams> get teamsList => _teamsList;
List<States> get statesList => _statesList;
List<Employees> get employeesList => _employeesList;
List<References> get referencesList => _referencesList;
List<Segments> get segmentsList => _segmentsList;
List<Districts> get districtsList => _districtsList;
List<SubLocations> get subLocationsList => _subLocationsList;
List<Contacts> get contactsList => _contactsList;
List<LeadList> get crmLeadList => _crmLeadList;
List<String?> get leadStatusList => _leadStatusList;
List<String?> get alphabetList => _alphabetList;
List<String?> get openStatusList => _openStatusList;
Sources? get selectedSource => _selectedSources;
Teams? get selectedTeam => _selectedTeams;
States? get selectedStates => _selectedStates;
Employees? get selectedEmployee => _selectedEmployees;
References? get selectedReference => _selectedReferences;
Segments? get selectedSegment => _selectedSegments;
Districts? get selectedDistricts => _selectedDistricts;
SubLocations? get selectedSubLocations => _selectedSubLocations;
Contacts? get selectedContacts => _selectedContacts;
String? get selectedLeadStatus => _selectedLeadStatus;
String? get selectedOpenStatus => _selectedOpenStatus;
String? get selectedSourceId => _selectedSourceId;
String? get selectedSourceValue => _selectedSourceValue;
String? get selectedTeamId => _selectedTeamId;
String? get selectedTeamValue => _selectedTeamValue;
String? get selectedStateId => _selectedStateId;
String? get selectedStateValue => _selectedStateValue;
String? get selectedEmployeeId => _selectedEmployeeId;
String? get selectedEmployeeValue => _selectedEmployeeValue;
String? get selectedReferenceId => _selectedReferenceId;
String? get selectedReferenceValue => _selectedReferenceValue;
String? get selectedSegmentId => _selectedSegmentId;
String? get selectedSegmentValue => _selectedSegmentValue;
String? get selectedDistrictId => _selectedDistrictId;
String? get selectedDistrictValue => _selectedDistrictValue;
String? get selectedSubLocationId => _selectedSubLocationId;
String? get selectedSubLocationValue => _selectedSubLocationValue;
String? get selectedContactId => _selectedContactId;
String? get selectedContactValue => _selectedContactValue;
String? get selectedAlphabet => _selectedAlphabet;
set isLoading(bool value) {
......@@ -796,8 +135,9 @@ class Leadlistprovider extends ChangeNotifier {
_selectedSources = value;
_selectedSourceId = value?.id;
_selectedSourceValue = value?.name;
print(
'Setting selectedSource: $value, id: $_selectedSourceId, name: $_selectedSourceValue',
// debug - helpful when debugging filter problems
debugPrint(
'Setting selectedSource: id=$_selectedSourceId, name=$_selectedSourceValue',
);
notifyListeners();
}
......@@ -958,6 +298,9 @@ class Leadlistprovider extends ChangeNotifier {
notifyListeners();
}
// ---------- fetches & rehydrate selection logic ----------
// view API that returns many filter lists
Future<void> crmLeadListViewAPIFunction(context, mode) async {
try {
var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
......@@ -967,8 +310,8 @@ class Leadlistprovider extends ChangeNotifier {
mode,
);
if (data != null) {
print(
'crmLeadListViewAPI error: ${data.error}, sources: ${data.sources}',
debugPrint(
'crmLeadListViewAPI error: ${data.error}, sources length: ${data.sources?.length ?? 0}',
);
if (data.error == "0") {
_leadStatusList = [
......@@ -981,45 +324,69 @@ class Leadlistprovider extends ChangeNotifier {
];
_alphabetList = List.generate(
26,
(index) => String.fromCharCode(index + 65),
(index) => String.fromCharCode(index + 65),
);
_openStatusList = ["open", "Closed", "All"];
_sourcesList =
data.sources
?.where((source) => source.id != null && source.name != null)
.toList() ??
_sourcesList = data.sources
?.where((source) => source.id != null && source.name != null)
.toList() ??
[];
_teamsList =
data.teams
?.where((team) => team.id != null && team.name != null)
.toList() ??
_teamsList = data.teams
?.where((team) => team.id != null && team.name != null)
.toList() ??
[];
_statesList =
data.states
?.where((state) => state.id != null && state.name != null)
.toList() ??
_statesList = data.states
?.where((state) => state.id != null && state.name != null)
.toList() ??
[];
_employeesList =
data.employees
?.where(
(employee) => employee.id != null && employee.name != null,
)
.toList() ??
_employeesList = data.employees
?.where(
(employee) => employee.id != null && employee.name != null,
)
.toList() ??
[];
// --- rehydrate previously selected items by matching IDs ---
_rehydrateSelectionsAfterViewLoad();
checkDropDownValues();
// ensure loading flag set off
_isLoading = false;
notifyListeners();
} else {
// clear lists on non-zero error to avoid stale options
_sourcesList.clear();
_teamsList.clear();
_statesList.clear();
_employeesList.clear();
_isLoading = false;
notifyListeners();
}
} else {
// null response -> clear lists
_sourcesList.clear();
_teamsList.clear();
_statesList.clear();
_employeesList.clear();
_isLoading = false;
notifyListeners();
}
} catch (e, s) {
print('crmLeadListViewAPI error: $e, stack: $s');
debugPrint('crmLeadListViewAPI error: $e, stack: $s');
_sourcesList.clear();
_teamsList.clear();
_statesList.clear();
_employeesList.clear();
_isLoading = false;
notifyListeners();
}
}
Future<void> crmLeadListSourceOnReferenceAPIFunction(
context,
mode,
String? sourceID,
) async {
context,
mode,
String? sourceID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadListSourceOnReferenceAPI(
......@@ -1029,27 +396,51 @@ class Leadlistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_referencesList =
data.references
?.where((ref) => ref.id != null && ref.name != null)
.toList() ??
_referencesList = data.references
?.where((ref) => ref.id != null && ref.name != null)
.toList() ??
[];
// rehydrate selectedReference if id exists
if (_selectedReferenceId != null) {
final found = _referencesList.firstWhere(
(r) => r.id == _selectedReferenceId,
orElse: () => References(id: _selectedReferenceId, name: _selectedReferenceValue),
);
// if found in list use that instance else leave existing selectedReference null/unchanged
if (_referencesList.any((r) => r.id == _selectedReferenceId)) {
_selectedReferences = found;
} else {
// if API returned different list, keep selectedReference null to avoid mismatch
_selectedReferences = null;
_selectedReferenceId = null;
_selectedReferenceValue = null;
}
}
notifyListeners();
} else {
_referencesList.clear();
_selectedReferences = null;
_selectedReferenceId = null;
_selectedReferenceValue = null;
notifyListeners();
}
} else {
_referencesList.clear();
_selectedReferences = null;
_selectedReferenceId = null;
_selectedReferenceValue = null;
notifyListeners();
}
} catch (e, s) {
print('crmLeadListSourceOnReferenceAPI error: $e, stack: $s');
debugPrint('crmLeadListSourceOnReferenceAPI error: $e, stack: $s');
}
}
Future<void> crmLeadListSegmentOnTeamAPIFunction(
context,
mode,
String? teamID,
) async {
context,
mode,
String? teamID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadListSegmentOnTeamAPI(
......@@ -1059,29 +450,51 @@ class Leadlistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_segmentsList =
data.segments
?.where(
(segment) => segment.id != null && segment.name != null,
)
.toList() ??
_segmentsList = data.segments
?.where(
(segment) => segment.id != null && segment.name != null,
)
.toList() ??
[];
// rehydrate selectedSegment if id exists
if (_selectedSegmentId != null) {
final found = _segmentsList.firstWhere(
(s) => s.id == _selectedSegmentId,
orElse: () => Segments(id: _selectedSegmentId, name: _selectedSegmentValue),
);
if (_segmentsList.any((s) => s.id == _selectedSegmentId)) {
_selectedSegments = found;
} else {
_selectedSegments = null;
_selectedSegmentId = null;
_selectedSegmentValue = null;
}
}
notifyListeners();
} else {
_segmentsList.clear();
_selectedSegments = null;
_selectedSegmentId = null;
_selectedSegmentValue = null;
notifyListeners();
}
} else {
_segmentsList.clear();
_selectedSegments = null;
_selectedSegmentId = null;
_selectedSegmentValue = null;
notifyListeners();
}
} catch (e, s) {
print('crmLeadListSegmentOnTeamAPI error: $e, stack: $s');
debugPrint('crmLeadListSegmentOnTeamAPI error: $e, stack: $s');
}
}
Future<void> crmLeadListDistrictsOnStateAPIFunction(
context,
mode,
String? stateID,
) async {
context,
mode,
String? stateID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmDistrictsOnStateAPI(
......@@ -1091,30 +504,52 @@ class Leadlistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_districtsList =
data.districts
?.where(
(district) =>
district.id != null && district.district != null,
)
.toList() ??
_districtsList = data.districts
?.where(
(district) =>
district.id != null && district.district != null,
)
.toList() ??
[];
// rehydrate selection
if (_selectedDistrictId != null) {
final found = _districtsList.firstWhere(
(d) => d.id == _selectedDistrictId,
orElse: () => Districts(id: _selectedDistrictId, district: _selectedDistrictValue),
);
if (_districtsList.any((d) => d.id == _selectedDistrictId)) {
_selectedDistricts = found;
} else {
_selectedDistricts = null;
_selectedDistrictId = null;
_selectedDistrictValue = null;
}
}
notifyListeners();
} else {
_districtsList.clear();
_selectedDistricts = null;
_selectedDistrictId = null;
_selectedDistrictValue = null;
notifyListeners();
}
} else {
_districtsList.clear();
_selectedDistricts = null;
_selectedDistrictId = null;
_selectedDistrictValue = null;
notifyListeners();
}
} catch (e, s) {
print('crmLeadListDistrictsOnStateAPI error: $e, stack: $s');
debugPrint('crmLeadListDistrictsOnStateAPI error: $e, stack: $s');
}
}
Future<void> crmLeadListSubLocOnDistrictAPIFunction(
context,
mode,
String? districtID,
) async {
context,
mode,
String? districtID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmSubLocOnDistrictAPI(
......@@ -1124,29 +559,51 @@ class Leadlistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_subLocationsList =
data.subLocations
?.where(
(subLoc) => subLoc.id != null && subLoc.subLocality != null,
)
.toList() ??
_subLocationsList = data.subLocations
?.where(
(subLoc) => subLoc.id != null && subLoc.subLocality != null,
)
.toList() ??
[];
// rehydrate selection
if (_selectedSubLocationId != null) {
final found = _subLocationsList.firstWhere(
(s) => s.id == _selectedSubLocationId,
orElse: () => SubLocations(id: _selectedSubLocationId, subLocality: _selectedSubLocationValue),
);
if (_subLocationsList.any((s) => s.id == _selectedSubLocationId)) {
_selectedSubLocations = found;
} else {
_selectedSubLocations = null;
_selectedSubLocationId = null;
_selectedSubLocationValue = null;
}
}
notifyListeners();
} else {
_subLocationsList.clear();
_selectedSubLocations = null;
_selectedSubLocationId = null;
_selectedSubLocationValue = null;
notifyListeners();
}
} else {
_subLocationsList.clear();
_selectedSubLocations = null;
_selectedSubLocationId = null;
_selectedSubLocationValue = null;
notifyListeners();
}
} catch (e, s) {
print('crmLeadListSubLocOnDistrictAPI error: $e, stack: $s');
debugPrint('crmLeadListSubLocOnDistrictAPI error: $e, stack: $s');
}
}
Future<void> crmLeadListContactPopUpAPIFunction(
context,
mode,
String? accountID,
) async {
context,
mode,
String? accountID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadListContactPopUpAPI(
......@@ -1156,21 +613,23 @@ class Leadlistprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_contactsList =
data.contacts
?.where(
(contact) => contact.id != null && contact.name != null,
)
.toList() ??
_contactsList = data.contacts
?.where(
(contact) => contact.id != null && contact.name != null,
)
.toList() ??
[];
notifyListeners();
} else {
_contactsList.clear();
notifyListeners();
}
} else {
_contactsList.clear();
notifyListeners();
}
} catch (e, s) {
print('crmLeadListContactPopUpAPI error: $e, stack: $s');
debugPrint('crmLeadListContactPopUpAPI error: $e, stack: $s');
}
}
......@@ -1193,17 +652,17 @@ class Leadlistprovider extends ChangeNotifier {
}
Future<void> crmLeadListAPIFunction(
BuildContext context,
String? mode,
String? leadStatus,
String? openStatus,
String? sourceID,
String? referenceID,
String? teamID,
String? segmentID,
String? alphabet, {
bool append = false,
}) async {
BuildContext context,
String? mode,
String? leadStatus,
String? openStatus,
String? sourceID,
String? referenceID,
String? teamID,
String? segmentID,
String? alphabet, {
bool append = false,
}) async {
try {
var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
......@@ -1235,27 +694,25 @@ class Leadlistprovider extends ChangeNotifier {
'empId: ${HomeProv.empId}, session: ${HomeProv.session}, pageNumber: $_currentPage',
);
if(data != null){
if ( data.error == "0") {
print("Lead List Length ${data.leadList!.length}");
if (data != null) {
if (data.error == "0") {
debugPrint("Lead List Length ${data.leadList?.length ?? 0}");
if (append) {
_crmLeadList.addAll(data.leadList ?? []);
} else {
_crmLeadList = data.leadList ?? [];
}
if (data.leadList!.length < 15) {
if ((data.leadList?.length ?? 0) < 15) {
_hasMoreData = false; // no more pages
}
} else if(data.error=="1") {
} else if (data.error == "1") {
if (!append) _errorMessage = "No leads found!";
_hasMoreData = false;
}
}else{
} else {
_hasMoreData = false;
}
} catch (e) {
_errorMessage = "Error: $e";
}
......@@ -1267,16 +724,16 @@ class Leadlistprovider extends ChangeNotifier {
/// Load next page
Future<void> loadMore(
BuildContext context,
String? mode,
String? leadStatus,
String? openStatus,
String? sourceID,
String? referenceID,
String? teamID,
String? segmentID,
String? alphabet,
) async {
BuildContext context,
String? mode,
String? leadStatus,
String? openStatus,
String? sourceID,
String? referenceID,
String? teamID,
String? segmentID,
String? alphabet,
) async {
if (_isLoadingMore || !_hasMoreData) return;
_currentPage++;
await crmLeadListAPIFunction(
......@@ -1310,7 +767,6 @@ class Leadlistprovider extends ChangeNotifier {
sLeadIDController.clear();
mobileNumberController.clear();
companyNameController.clear();
companyNameController.clear();
_selectedEmployees = null;
_selectedSources = null;
_selectedReferences = null;
......@@ -1390,4 +846,60 @@ class Leadlistprovider extends ChangeNotifier {
}
notifyListeners();
}
// --- helper to rehydrate selected instances by their IDs after lists are loaded ---
// --- helper to rehydrate selected instances by their IDs after lists are loaded ---
void _rehydrateSelectionsAfterViewLoad() {
// sources
if (_selectedSourceId != null) {
final idx = _sourcesList.indexWhere((s) => s.id == _selectedSourceId);
if (idx != -1) {
_selectedSources = _sourcesList[idx];
} else {
_selectedSources = null;
_selectedSourceId = null;
_selectedSourceValue = null;
}
}
// teams
if (_selectedTeamId != null) {
final idx = _teamsList.indexWhere((t) => t.id == _selectedTeamId);
if (idx != -1) {
_selectedTeams = _teamsList[idx];
} else {
_selectedTeams = null;
_selectedTeamId = null;
_selectedTeamValue = null;
}
}
// employees
if (_selectedEmployeeId != null) {
final idx = _employeesList.indexWhere((e) => e.id == _selectedEmployeeId);
if (idx != -1) {
_selectedEmployees = _employeesList[idx];
} else {
_selectedEmployees = null;
_selectedEmployeeId = null;
_selectedEmployeeValue = null;
}
}
// states
if (_selectedStateId != null) {
final idx = _statesList.indexWhere((st) => st.id == _selectedStateId);
if (idx != -1) {
_selectedStates = _statesList[idx];
} else {
_selectedStates = null;
_selectedStateId = null;
_selectedStateValue = null;
}
}
// note: I only handled the lists you used earlier; if you need to rehydrate
// other selections (district/segment/subLocation/reference etc.) add same pattern.
}
}
......@@ -757,14 +757,17 @@ class Addnewleadsandprospectsprovider extends ChangeNotifier {
notifyListeners();
}
onChangeCompanyName(value) {
companynameError = "";
// onChangeCompanyName(value) {
// companynameError = "";
// notifyListeners();
// }
onChangeContactPersonName(value) {
nameError = "";
notifyListeners();
}
onChangeAlternatemobile(value) {
AlternatemobileError = "";
notifyListeners();
......@@ -775,22 +778,22 @@ class Addnewleadsandprospectsprovider extends ChangeNotifier {
notifyListeners();
}
Future<void> onChangeContactPersonName(BuildContext context, String value) async {
nameError = "";
Future<void> onChangeCompanyName(BuildContext context, String value) async {
companynameError = "";
if (value.isEmpty) {
nameError = "Name cannot be empty";
companynameError = "Company Name cannot be empty";
} else if (value.length < 3) {
nameError = "Name must be at least 3 characters";
companynameError = "Company Name must be at least 3 characters";
} else {
final exists = await checkAccountFieldExistence(
context,
type: "name",
type: "company",
typeValue: value,
);
if (!exists) {
nameError = "Name already exists";
companynameError = "Company Name already exists";
} else {
nameError = null;
companynameError = null;
}
}
notifyListeners();
......@@ -997,7 +1000,7 @@ class Addnewleadsandprospectsprovider extends ChangeNotifier {
isValid = false;
}
if(nameError == "Name already exists"){
if(companynameError == "Company Name already exists"){
isValid = false;
}
......
......@@ -34,16 +34,26 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
List<Teams> get teamsList => _teamsList;
List<Segments> get segmentsList => _segmentsList;
Teams? get selectedTeams => _selectedTeams;
Segments? get selectedSegments => _selectedSegments;
// IMPORTANT: Return null if the selected object is not present in the current list.
// This prevents DropdownButton2 from receiving a value that is not part of items.
Teams? get selectedTeams {
if (_selectedTeams == null) return null;
if (!_teamsList.contains(_selectedTeams)) return null;
return _selectedTeams;
}
Segments? get selectedSegments {
if (_selectedSegments == null) return null;
if (!_segmentsList.contains(_selectedSegments)) return null;
return _selectedSegments;
}
AccountDetails get accountsDetails => _accountDetails;
String? get selectedTeamId => _selectedTeamId;
String? get selectedTeamValue => _selectedTeamValue;
String? get selectedSegmentId => _selectedSegmentId;
String? get selectedSegmentValue => _selectedSegmentValue;
set isLoading(bool value) {
......@@ -68,15 +78,25 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
set selectedTeams(Teams? value) {
_selectedTeams = value;
_selectedTeamId = value!.id!;
_selectedTeamValue = value.name;
if (value != null) {
_selectedTeamId = value.id;
_selectedTeamValue = value.name;
} else {
_selectedTeamId = null;
_selectedTeamValue = null;
}
notifyListeners();
}
set selectedSegments(Segments? value) {
_selectedSegments = value;
_selectedSegmentId = value!.id!;
_selectedSegmentValue = value.name;
if (value != null) {
_selectedSegmentId = value.id;
_selectedSegmentValue = value.name;
} else {
_selectedSegmentId = null;
_selectedSegmentValue = null;
}
notifyListeners();
}
......@@ -101,10 +121,10 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
}
Future<void> crmLeadDetailsEditAccountAPIFunction(
context,
leadID,
mode,
) async {
context,
leadID,
mode,
) async {
try {
var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadDetailsEditAccountViewAPI(
......@@ -117,8 +137,21 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
notifyListeners();
if (data != null) {
if (data.error == "0") {
_teamsList = data.teams!;
_accountDetails = data.accountDetails!;
_teamsList = data.teams ?? [];
_accountDetails = data.accountDetails ?? AccountDetails();
// If current selected team/segment are not in the new lists, clear them
if (_selectedTeams != null && !_teamsList.contains(_selectedTeams)) {
_selectedTeams = null;
_selectedTeamId = null;
_selectedTeamValue = null;
}
if (_selectedSegments != null && !_segmentsList.contains(_selectedSegments)) {
_selectedSegments = null;
_selectedSegmentId = null;
_selectedSegmentValue = null;
}
_isLoading = false;
notifyListeners();
} else {
......@@ -136,10 +169,10 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
}
Future<void> crmLeadListSegmentOnTeamAPIFunction(
context,
mode,
teamID,
) async {
context,
mode,
teamID,
) async {
try {
var prov = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadListSegmentOnTeamAPI(
......@@ -149,23 +182,43 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
);
if (data != null) {
if (data.error == "0") {
_segmentsList = data.segments!;
_segmentsList = data.segments ?? [];
// clear selectedSegments if it's not part of new list
if (_selectedSegments != null && !_segmentsList.contains(_selectedSegments)) {
_selectedSegments = null;
_selectedSegmentId = null;
_selectedSegmentValue = null;
}
notifyListeners();
}
}
} catch (e) {}
}
// Fixed: check string emptiness properly (IDs are strings in your model)
bool get isFormValid {
final nameOk = editCompanyNameController.text.trim().isNotEmpty;
final teamOk = _selectedTeamId != null && _selectedTeamId!.isNotEmpty;
final segmentOk = _selectedSegmentId != null && _selectedSegmentId!.isNotEmpty;
return nameOk && teamOk && segmentOk;
}
Future<void> crmLeadDetailsEditAccountSubmitAPIFunction(
context,
accountId,
segmentId,
teamId,
) async {
context,
accountId,
segmentId,
teamId,
) async {
try {
if (!validateForm(context)) {
return;
}
_isLoading = true;
notifyListeners();
var HomeProv = Provider.of<HomescreenNotifier>(context, listen: false);
final data = await ApiCalling.crmLeadDetailsEditAccountSubmitAPI(
HomeProv.empId,
......@@ -175,8 +228,7 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
teamId,
editCompanyNameController.text,
);
_isLoading = true;
notifyListeners();
if (data != null) {
if (data.error == "0") {
Navigator.pop(context, true);
......@@ -200,19 +252,24 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
bool validateForm(BuildContext context) {
// Reset all errors
_companyNameError = null;
_selectedTeamError = null;
_selectedSegmentError = null;
bool isValid = true;
if (_selectedTeams == null || _selectedTeamId!.isEmpty) {
_selectedTeamError = "Please select an account";
// Team checks
if (_selectedTeams == null || _selectedTeamId == null || _selectedTeamId!.isEmpty) {
_selectedTeamError = "Please select a team";
isValid = false;
}
if (_selectedSegments == null || _selectedSegmentId!.isEmpty) {
_selectedSegmentError = "Please select an account";
// Segment checks
if (_selectedSegments == null || _selectedSegmentId == null || _selectedSegmentId!.isEmpty) {
_selectedSegmentError = "Please select a segment";
isValid = false;
}
// Company name check
if (editCompanyNameController.text.trim().isEmpty) {
_companyNameError = "Please enter Company name";
isValid = false;
......@@ -223,7 +280,12 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
}
onChangedCompanyName(value) {
_companyNameError = "";
// Clear the company name error when user types
if (value != null && value.toString().trim().isNotEmpty) {
_companyNameError = null;
} else {
_companyNameError = "Name is required";
}
notifyListeners();
}
......@@ -236,11 +298,13 @@ class Editcrmaccountdetailsprovider extends ChangeNotifier {
_selectedSegmentId = null;
_selectedTeamValue = null;
_selectedSegmentValue = null;
_selectedTeamError = null;
_selectedSegmentError = null;
_companyNameError = null;
}
void checkDropDownValues() {
if (!_segmentsList.contains(_selectedSegments) &&
_selectedSegments != null) {
if (!_segmentsList.contains(_selectedSegments) && _selectedSegments != null) {
_selectedTeamId = null;
_selectedTeamValue = null;
}
......
......@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_ringtone_player/flutter_ringtone_player.dart';
import 'package:generp/Models/hrmModels/leaveApplicationLIstResponse.dart';
import 'package:generp/Notifiers/QrProvider.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/screens/AttendanceScreen.dart';
import 'package:generp/screens/HomeScreen.dart';
......@@ -426,6 +427,7 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(create: (_) => AdvanceListProvider()),
ChangeNotifierProvider(create: (_) => CasualLeaveHistoryProvider()),
ChangeNotifierProvider(create: (_) => ContactProvider()),
ChangeNotifierProvider(create: (_) => QrProvider()),
],
child: Builder(
......
// addcommonpayment.dart
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
......@@ -6,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/Notifiers/commonProvider/accountsListProvider.dart';
import 'package:generp/Notifiers/HomeScreenNotifier.dart';
import 'package:generp/Utils/ShakeWidget.dart';
import 'package:generp/Utils/app_colors.dart';
import 'package:generp/Utils/commonServices.dart';
......@@ -35,7 +37,6 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
@override
void initState() {
// TODO: implement initState
super.initState();
_connectivity.initialise();
_connectivity.myStream.listen((source) {
......@@ -47,48 +48,183 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
});
}
void _nextStep() {
if (_formKey.currentState!.validate()) {
if (_currentStep < 3) {
setState(() {
_currentStep += 1;
});
} else {
// Submit form data
_submitForm();
}
@override
void dispose() {
// correctly dispose focus nodes
for (final fn in focusNodes) {
try {
fn.dispose();
} catch (_) {}
}
_connectivity.disposeStream();
super.dispose();
}
void _previousStep() {
Future<bool> onBackPressed(BuildContext context) async {
if (_currentStep > 0) {
setState(() {
_currentStep -= 1;
});
setState(() => _currentStep -= 1);
return false;
} else {
return true;
}
}
void _submitForm() {
print('Form Submitted:');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Form submitted successfully!')),
);
bool _isIfscValidFormat(String ifsc) {
// Basic IFSC pattern: 4 letters, 0, 6 alphanumeric
final reg = RegExp(r'^[A-Za-z]{4}0[A-Za-z0-9]{6}$');
return reg.hasMatch(ifsc.trim());
}
@override
void dispose() {
focusNodes.map((e) => e.dispose());
super.dispose();
_connectivity.disposeStream();
Future<void> _validateGstIfNeeded(
Accountslistprovider provider) async {
final gst = provider.gstNumberController.text.trim();
if (gst.isEmpty) {
// nothing to validate
return;
}
// get emp/session
final homeProv =
Provider.of<HomescreenNotifier>(context, listen: false);
// set a small loading indicator on provider or local if needed
provider.isLoading = true;
provider.gstNumberError = null;
provider.notifyListeners();
try {
await provider.validateGstNumber(homeProv.empId, homeProv.session, gst);
// After validateGstNumber completes, provider.gstResponse should be set
if (provider.gstResponse != null && provider.gstResponse!.error == "0") {
// autofill address in step 2
final fetchedAddress = provider.gstResponse!.address ?? "";
if (fetchedAddress.isNotEmpty) {
provider.addressController.text = fetchedAddress;
}
// compare company name
final gstName = (provider.gstResponse!.legalNameOfBusiness ?? "")
.trim()
.toLowerCase();
final companyName = provider.nameController.text.trim().toLowerCase();
if (gstName.isNotEmpty &&
companyName.isNotEmpty &&
gstName != companyName) {
provider.nameError =
"Company name doesn't match GST legal name (${provider.gstResponse!.legalNameOfBusiness}). Please correct.";
} else {
provider.nameError = null;
}
provider.gstNumberError = null;
} else {
// API returned error or invalid gst
provider.gstNumberError =
provider.gstResponse?.message ?? "Invalid GST number";
}
} catch (e) {
provider.gstNumberError = "Failed to validate GST: $e";
} finally {
provider.isLoading = false;
provider.notifyListeners();
}
}
Future<bool> onBackPressed(BuildContext context) async {
if (_currentStep > 0) {
_previousStep();
return false;
Future<void> _validateBankIfNeeded(
Accountslistprovider provider) async {
final acc = provider.bankAcNumberController.text.trim();
final ifsc = provider.bankIfscCotroller.text.trim();
// Only validate if both fields have values
if (acc.isEmpty || ifsc.isEmpty) {
return;
}
// IFSC basic format check first
if (!_isIfscValidFormat(ifsc)) {
provider.bankIFSCError = "Invalid IFSC format";
provider.notifyListeners();
return;
} else {
return true;
provider.bankIFSCError = null;
}
final homeProv =
Provider.of<HomescreenNotifier>(context, listen: false);
provider.isLoading = true;
provider.notifyListeners();
try {
await provider.validateBankDetails(homeProv.empId, homeProv.session,
acc); // provider uses bankIfscCotroller.text internally
if (provider.bankResponse != null && provider.bankResponse!.error == "0") {
// autofill fields
final br = provider.bankResponse!;
if (br.bankName != null && br.bankName!.isNotEmpty) {
provider.bankNameController.text = br.bankName!;
}
if (br.branch != null && br.branch!.isNotEmpty) {
provider.branchNameController.text = br.branch!;
}
if (br.nameAtBank != null && br.nameAtBank!.isNotEmpty) {
provider.bankHolderNameController.text = br.nameAtBank!;
}
provider.bankAcNumberError = null;
provider.bankIFSCError = null;
} else {
// show error
provider.bankAcNumberError =
provider.bankResponse?.message ?? "Invalid account / IFSC";
}
} catch (e) {
provider.bankAcNumberError = "Failed to validate bank details: $e";
} finally {
provider.isLoading = false;
provider.notifyListeners();
}
}
/// Determine first step which has an error (0..3). If none returns null.
int? _firstErrorStep(Accountslistprovider provider) {
// Step 0: Account Details
if ((provider.accountError?.isNotEmpty ?? false) ||
(provider.nameError?.isNotEmpty ?? false) ||
(provider.mobileError?.isNotEmpty ?? false) ||
(provider.contactPersonError?.isNotEmpty ?? false)) {
return 0;
}
// Step 1: Address details (optional step but if errors exist, redirect)
if ((provider.stateError?.isNotEmpty ?? false) ||
(provider.districtError?.isNotEmpty ?? false) ||
(provider.localityError?.isNotEmpty ?? false) ||
(provider.addressError?.isNotEmpty ?? false)) {
return 1;
}
// Step 2: Bank / GST related validation errors
if ((provider.gstNumberError?.isNotEmpty ?? false) ||
(provider.bankAcNumberError?.isNotEmpty ?? false) ||
(provider.bankIFSCError?.isNotEmpty ?? false) ||
(provider.banknameError?.isNotEmpty ?? false) ||
(provider.bankBranchError?.isNotEmpty ?? false) ||
(provider.bankHolderNameError?.isNotEmpty ?? false) ||
(provider.upiError?.isNotEmpty ?? false)) {
return 2;
}
// Step 3: Contact Details
if ((provider.desigantionError?.isNotEmpty ?? false) ||
(provider.altMobError?.isNotEmpty ?? false) ||
(provider.teleError?.isNotEmpty ?? false) ||
(provider.mailError?.isNotEmpty ?? false)) {
return 3;
}
return null;
}
@override
......@@ -104,17 +240,14 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
default:
connection = 'Offline';
}
return (connection == "Online")
? Platform.isAndroid
? WillPopScope(
onWillPop: () => onBackPressed(context),
child: SafeArea(
top: false,
bottom: true,
child: _scaffold(context),
),
)
: _scaffold(context)
? WillPopScope(
onWillPop: () => onBackPressed(context),
child: SafeArea(top: false, bottom: true, child: _scaffold(context)),
)
: _scaffold(context)
: NoNetwork(context);
}
......@@ -132,42 +265,112 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
),
backgroundColor: AppColors.scaffold_bg_color,
body: Form(
canPop: _currentStep == 0,
key: _formKey,
child: Stepper(
margin: EdgeInsets.symmetric(horizontal: 0, vertical: 0),
type: StepperType.horizontal,
currentStep: _currentStep,
onStepContinue: _nextStep,
onStepCancel: _previousStep,
onStepTapped: (value) {
print(value);
onStepContinue: () async {
// custom continue logic:
if (_currentStep == 0) {
// validate step1 via provider
if (provider.validateStep1()) {
setState(() => _currentStep = 1);
} else {
// show error (provider sets errors and notifies)
provider.notifyListeners();
// stay on step 0
setState(() => _currentStep = 0);
}
} else if (_currentStep == 1) {
// Step 2 is optional; allow moving to next without forcing validation
// But if the user has entered fields and they are invalid, validate
if (provider.stateSearchController.text.trim().isNotEmpty ||
provider.districtSearchController.text.trim().isNotEmpty ||
provider.addressController.text.trim().isNotEmpty) {
if (provider.validateStep2()) {
setState(() => _currentStep = 2);
} else {
provider.notifyListeners();
}
} else {
// nothing filled, just move forward
setState(() => _currentStep = 2);
}
} else if (_currentStep == 2) {
// Step 3 (bank) is optional; validate only if fields present
if (provider.gstNumberController.text.trim().isNotEmpty) {
// ensure gst validated
await _validateGstIfNeeded(provider);
}
if (provider.bankAcNumberController.text.trim().isNotEmpty ||
provider.bankIfscCotroller.text.trim().isNotEmpty) {
await _validateBankIfNeeded(provider);
}
setState(() {
if (value == 1 && !provider.validateStep1()) {
return;
// if any bank/gst errors exist, stay; else move forward
final bankErrors = (provider.gstNumberError != null &&
provider.gstNumberError!.isNotEmpty) ||
(provider.bankAcNumberError != null &&
provider.bankAcNumberError!.isNotEmpty) ||
(provider.bankIFSCError != null &&
provider.bankIFSCError!.isNotEmpty);
if (bankErrors) {
provider.notifyListeners();
setState(() => _currentStep = 2);
} else {
setState(() => _currentStep = 3);
}
if (value == 2 && !provider.validateStep2()) {
return;
} else if (_currentStep == 3) {
// last step: same as pressing Submit in controls below
}
},
onStepCancel: () {
if (_currentStep > 0) {
setState(() => _currentStep -= 1);
}
},
onStepTapped: (value) async {
// allow tapping to review steps; prevent jumping forward past required failed steps
if (value == 0) {
setState(() => _currentStep = 0);
} else if (value == 1) {
// user wants to jump to step 1 - ensure step 0 valid
if (provider.validateStep1()) {
setState(() => _currentStep = 1);
} else {
provider.notifyListeners();
setState(() => _currentStep = 0);
}
if (value < _currentStep) {
_currentStep = value;
} else if (value > _currentStep) {
_currentStep = value;
} else if (value == 2) {
// allow jump if step0 valid; step1 optional
if (provider.validateStep1()) {
setState(() => _currentStep = 2);
} else {
provider.notifyListeners();
setState(() => _currentStep = 0);
}
} else if (value == 3) {
// final - require step0 valid
if (!provider.validateStep1()) {
provider.notifyListeners();
setState(() => _currentStep = 0);
return;
}
});
// optional steps validated when moving forward by controls
setState(() => _currentStep = 3);
}
},
connectorColor: WidgetStatePropertyAll(AppColors.app_blue),
stepIconBuilder: (stepIndex, stepState) {
return CircleAvatar(
radius: 12,
backgroundColor:
stepIndex <= _currentStep
? AppColors.app_blue
: Colors.grey[300],
backgroundColor: stepIndex <= _currentStep ? AppColors.app_blue : Colors.grey[300],
);
},
steps: [
// Step 0 (Step 1 in your labels)
Step(
label: Text("Step 1", style: TextStyle(fontSize: 12)),
title: const Text(''),
......@@ -187,15 +390,8 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -218,45 +414,20 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
),
],
),
items:
provider.accountTypes
.map(
(act) =>
DropdownMenuItem<String>(
value: act,
child: Text(
act,
style: const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
// value: provider.selectedAccountType,
value:
provider.accountTypes.contains(
provider.selectedAccountType,
)
? provider.selectedAccountType
: null,
items: provider.accountTypes.map((act) => DropdownMenuItem<String>(
value: act,
child: Text(act, style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
)).toList(),
value: provider.accountTypes.contains(provider.selectedAccountType) ? provider.selectedAccountType : null,
onChanged: (value) {
if (value != null) {
provider.selectedAccountType = value;
print(
"statusId:${provider.selectedAccountType}",
);
}
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
......@@ -268,13 +439,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.nameController,
"Company Name",
"Enter Company Name",
(p0) {
(p0) {
provider.updateName(p0);
provider.checkInputsAPI(
context,
"name",
provider.nameController.text,
);
provider.checkInputsAPI(context, "name", provider.nameController.text);
},
TextInputType.text,
false,
......@@ -282,6 +449,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
focusNodes[0],
focusNodes[1],
TextInputAction.next,
null,
// validator for form-level as well
// we won't add Form validator since provider handles it
),
errorWidget(context, provider.nameError),
textControllerWidget(
......@@ -289,13 +459,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.mobileController,
"Mobile Number",
"Enter Mobile",
(p0) {
(p0) {
provider.updateMobile(p0);
provider.checkInputsAPI(
context,
"mob1",
provider.mobileController.text,
);
provider.checkInputsAPI(context, "mob1", provider.mobileController.text);
},
TextInputType.phone,
false,
......@@ -326,6 +492,8 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
],
),
),
// Step 1 (Address / optional)
Step(
label: Text("Step 2", style: TextStyle(fontSize: 12)),
title: const Text(''),
......@@ -337,22 +505,12 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
margin: EdgeInsets.only(bottom: 10),
child: Text(
"Address Details",
style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
fontFamily: "JakartaMedium",
),
style: TextStyle(color: AppColors.app_blue, fontSize: 16, fontFamily: "JakartaMedium"),
),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -363,114 +521,47 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
Expanded(
child: DropdownButton2<States>(
focusNode: focusNodes[2],
autofocus:
focusNodes[2].hasFocus ? true : false,
autofocus: focusNodes[2].hasFocus ? true : false,
isExpanded: true,
hint: Text(
'Select State',
style: TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
items:
provider.states
.map(
(states) =>
DropdownMenuItem<States>(
value: states,
child: Text(
states.name ?? '',
style: const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
value:
provider.states.contains(
provider.selectedState,
)
? provider.selectedState
: null,
// value: provider.selectedState,
hint: Text('Select State', style: TextStyle(fontSize: 14)),
items: provider.states.map((states) => DropdownMenuItem<States>(
value: states,
child: Text(states.name ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
)).toList(),
value: provider.states.contains(provider.selectedState) ? provider.selectedState : null,
onChanged: (States? value) {
if (value != null) {
if (provider.states.isNotEmpty) {
provider.selectedState = value;
print(
"Selected Complaint Type: ${value.name}, ID: ${value.id}",
);
provider.selectedStateID =
value.id!;
print(
"hfjkshfg${provider.selectedStateID}",
);
if (provider.selectedDistricts !=
null) {
provider.districts.clear();
// provider.selectedDistricts = null;
provider.selectedDistrictId =
null;
provider.selectedDistrictValue =
null;
}
provider.getDistrictAPI(
context,
provider.selectedStateID,
);
provider.selectedState = value;
provider.selectedStateID = value.id!;
if (provider.selectedDistricts != null) {
provider.districts.clear();
provider.selectedDistrictId = null;
provider.selectedDistrictValue = null;
}
provider.getDistrictAPI(context, provider.selectedStateID);
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider.stateSearchController,
searchController: provider.stateSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller:
provider.stateSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText: 'Search States...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(8),
),
),
controller: provider.stateSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search States...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.name
?.toLowerCase()
.contains(
searchValue.toLowerCase(),
) ??
false;
return item.value?.name?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) {
provider.stateSearchController
.clear();
}
if (!isOpen) provider.stateSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
......@@ -485,110 +576,45 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
child: DropdownButton2<Districts>(
focusNode: focusNodes[3],
isExpanded: true,
hint: Text(
'Select District',
style: TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
items:
provider.districts
.map(
(dist) =>
DropdownMenuItem<Districts>(
value: dist,
child: Text(
dist.district ?? '',
style: const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
value:
provider.districts.contains(
provider.selectedDistricts,
)
? provider.selectedDistricts
: null,
// value: provider.selectedDistricts,
hint: Text('Select District', style: TextStyle(fontSize: 14)),
items: provider.districts.map((dist) => DropdownMenuItem<Districts>(
value: dist,
child: Text(dist.district ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
)).toList(),
value: provider.districts.contains(provider.selectedDistricts) ? provider.selectedDistricts : null,
onChanged: (Districts? value) {
if (value != null) {
if (provider.districts.isNotEmpty) {
provider.selectedDistricts = value;
print("Selected ID: ${value.id}");
provider.selectedDistrictId =
value.id!;
provider.selectedDistrictValue =
value.district!;
print(
"hfjkshfg${provider.selectedDistrictId}",
);
if (provider.selectedSubLocations !=
null) {
provider.subLocations.clear();
// provider.selectedSubLocations =
// null;
provider.selectedSubLocID = null;
provider.selectedSubLocValue =
null;
}
provider.getSubLocationAPI(
context,
provider.selectedDistrictId,
);
provider.selectedDistricts = value;
provider.selectedDistrictId = value.id!;
provider.selectedDistrictValue = value.district!;
if (provider.selectedSubLocations != null) {
provider.selectedSubLocID = null;
provider.selectedSubLocValue = null;
}
provider.getSubLocationAPI(context, provider.selectedDistrictId);
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider.districtSearchController,
searchController: provider.districtSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller:
provider
.districtSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText: 'Search Districts...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(8),
),
),
controller: provider.districtSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search Districts...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.district
?.toLowerCase()
.contains(
searchValue.toLowerCase(),
) ??
false;
return item.value?.district?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) {
provider.districtSearchController
.clear();
}
if (!isOpen) provider.districtSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
......@@ -603,100 +629,40 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
child: DropdownButton2<SubLocations>(
focusNode: focusNodes[4],
isExpanded: true,
hint: Text(
'Select Sub Locality',
style: TextStyle(fontSize: 14),
overflow: TextOverflow.ellipsis,
),
items:
provider.subLocations
.map(
(subloc) => DropdownMenuItem<
SubLocations
>(
value: subloc,
child: Text(
subloc.subLocality ?? '',
style: const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow.ellipsis,
),
),
)
.toList(),
// value: provider.selectedSubLocations,
value:
provider.subLocations.contains(
provider.selectedSubLocations,
)
? provider.selectedSubLocations
: null,
hint: Text('Select Sub Locality', style: TextStyle(fontSize: 14)),
items: provider.subLocations.map((subloc) => DropdownMenuItem<SubLocations>(
value: subloc,
child: Text(subloc.subLocality ?? '', style: const TextStyle(fontSize: 14), overflow: TextOverflow.ellipsis),
)).toList(),
value: provider.subLocations.contains(provider.selectedSubLocations) ? provider.selectedSubLocations : null,
onChanged: (SubLocations? value) {
if (value != null) {
if (provider
.subLocations
.isNotEmpty) {
provider.selectedSubLocations =
value;
print("Selected ID: ${value.id}");
provider.selectedSubLocID =
value.id!;
provider.selectedSubLocValue =
value.subLocality!;
print(
"hfjkshfg${provider.selectedSubLocID}",
);
}
provider.selectedSubLocations = value;
provider.selectedSubLocID = value.id!;
provider.selectedSubLocValue = value.subLocality!;
}
},
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider.subLocSearchController,
searchController: provider.subLocSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(8),
child: TextFormField(
controller:
provider.subLocSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText:
'Search Sub Locality...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(8),
),
),
controller: provider.subLocSearchController,
decoration: InputDecoration(isDense: true, contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), hintText: 'Search Sub Locality...', border: OutlineInputBorder(borderRadius: BorderRadius.circular(8))),
),
),
searchMatchFn: (item, searchValue) {
return item.value?.subLocality
?.toLowerCase()
.contains(
searchValue.toLowerCase(),
) ??
false;
return item.value?.subLocality?.toLowerCase().contains(searchValue.toLowerCase()) ?? false;
},
),
onMenuStateChange: (isOpen) {
if (!isOpen) {
provider.subLocSearchController
.clear();
}
if (!isOpen) provider.subLocSearchController.clear();
},
buttonStyleData: ddtheme.buttonStyleData,
iconStyleData: ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
menuItemStyleData: ddtheme.menuItemStyleData,
dropdownStyleData: ddtheme.dropdownStyleData,
),
),
],
......@@ -723,37 +689,100 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
],
),
),
// Step 2 (Bank & GST)
Step(
label: Text("Step 3", style: TextStyle(fontSize: 12)),
title: const Text(''),
isActive: _currentStep >= 1,
isActive: _currentStep >= 2,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(bottom: 10),
child: Text(
"Bank Details",
style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
fontFamily: "JakartaMedium",
),
),
child: Text("Bank Details", style: TextStyle(color: AppColors.app_blue, fontSize: 16, fontFamily: "JakartaMedium")),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// GST field - if provided, validate and autofill address; compare company name
textControllerWidget(
context,
provider.gstNumberController,
"GST Number",
"Enter GST Number",
(val) async {
// when user types, just update; validation on field submit
provider.gstNumberController.text = val;
provider.notifyListeners();
},
TextInputType.text,
false,
null,
focusNodes[9],
focusNodes[10],
TextInputAction.next,
null,
// onEditingComplete / onFieldSubmitted, we will validate
),
// Validate GST when user leaves the field (on editing complete)
// To do that we use FocusNode
const SizedBox(height: 4),
errorWidget(context, provider.gstNumberError),
// Bank Account Number
textControllerWidget(
context,
provider.bankAcNumberController,
"Bank Account Number",
"Enter Bank Account Number",
(p0) {
provider.updateNumber(p0);
// attempt validation only when IFSC present
if (provider.bankIfscCotroller.text.trim().isNotEmpty) {
_validateBankIfNeeded(provider);
}
},
TextInputType.number,
false,
FilteringTextInputFormatter.digitsOnly,
focusNodes[10],
focusNodes[11],
TextInputAction.next,
),
errorWidget(context, provider.bankAcNumberError),
// IFSC
textControllerWidget(
context,
provider.bankIfscCotroller,
"Bank IFSC",
"Enter Bank IFSC",
(p0) {
provider.updateIFSC(p0);
// validate IFSC format locally; if good and account present, trigger server validation
if (_isIfscValidFormat(p0) && provider.bankAcNumberController.text.trim().isNotEmpty) {
_validateBankIfNeeded(provider);
} else {
if (p0.trim().isNotEmpty && !_isIfscValidFormat(p0)) {
provider.bankIFSCError = "Invalid IFSC format";
provider.notifyListeners();
} else {
provider.bankIFSCError = null;
provider.notifyListeners();
}
}
},
TextInputType.text,
false,
null,
focusNodes[8],
focusNodes[9],
TextInputAction.next,
),
errorWidget(context, provider.bankIFSCError),
// Bank Name (autofill)
textControllerWidget(
context,
provider.bankNameController,
......@@ -768,6 +797,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
TextInputAction.next,
),
errorWidget(context, provider.banknameError),
// Branch
textControllerWidget(
context,
provider.branchNameController,
......@@ -782,20 +812,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
TextInputAction.next,
),
errorWidget(context, provider.bankBranchError),
textControllerWidget(
context,
provider.bankIfscCotroller,
"Bank IFSC",
"Enter Bank IFSC",
provider.updateIFSC,
TextInputType.text,
false,
null,
focusNodes[8],
focusNodes[9],
TextInputAction.next,
),
errorWidget(context, provider.bankIFSCError),
// Holder
textControllerWidget(
context,
provider.bankHolderNameController,
......@@ -810,20 +827,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
TextInputAction.next,
),
errorWidget(context, provider.bankHolderNameError),
textControllerWidget(
context,
provider.bankAcNumberController,
"Bank Account Number",
"Enter Bank Account Number",
provider.updateNumber,
TextInputType.number,
false,
FilteringTextInputFormatter.digitsOnly,
focusNodes[10],
focusNodes[11],
TextInputAction.next,
),
errorWidget(context, provider.bankAcNumberError),
// UPI
textControllerWidget(
context,
provider.bankUpiController,
......@@ -844,33 +848,22 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
],
),
),
// Step 3 (Contact Details)
Step(
label: Text("Step 4", style: TextStyle(fontSize: 12)),
title: const Text(''),
isActive: _currentStep >= 2,
isActive: _currentStep >= 3,
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(bottom: 10),
child: Text(
"Contact Details",
style: TextStyle(
color: AppColors.app_blue,
fontSize: 16,
fontFamily: "JakartaMedium",
),
),
child: Text("Contact Details", style: TextStyle(color: AppColors.app_blue, fontSize: 16, fontFamily: "JakartaMedium")),
),
Container(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16)),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
......@@ -893,13 +886,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.contectPersonAltMobController,
"Alternative Mobile Number",
"Enter Alternative Mobile Number",
(p0) {
(p0) {
provider.updateAltMobile(p0);
provider.checkInputsAPI(
context,
"mob2",
provider.contectPersonAltMobController.text,
);
provider.checkInputsAPI(context, "mob2", provider.contectPersonAltMobController.text);
},
TextInputType.number,
false,
......@@ -950,43 +939,54 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
children: [
if (_currentStep == 3) ...[
InkResponse(
onTap:
provider.submitClickced
? null
: () {
if (provider.validateStep4()) {
provider.submitClickced = true;
provider.submitCommonAccountsAPI(
context,
widget.from,
);
}
onTap: provider.submitClickced
? null
: () async {
// Before submit final: validate all required rules:
// - Step 0 required
// - Step 3 required
// - Step 2 & Step 1 optional (but if fields present then validate)
// Also ensure gst/bank validations triggered if filled
// Trigger validations
// If GST present -> validate
if (provider.gstNumberController.text.trim().isNotEmpty) {
await _validateGstIfNeeded(provider);
}
if (provider.bankAcNumberController.text.trim().isNotEmpty &&
provider.bankIfscCotroller.text.trim().isNotEmpty) {
await _validateBankIfNeeded(provider);
}
// Now run provider overall validation
final ok = provider.validatereceiptForm(context);
details.onStepContinue;
},
if (!ok) {
// move to first error step
final errStep = _firstErrorStep(provider);
if (errStep != null) {
setState(() {
_currentStep = errStep;
});
}
// ensure UI updated
provider.notifyListeners();
return;
}
// All good => submit
provider.submitClickced = true;
provider.notifyListeners();
await provider.submitCommonAccountsAPI(context, widget.from);
},
child: Container(
height: 45,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: BoxDecoration(
color: AppColors.app_blue,
borderRadius: BorderRadius.circular(15),
),
child: Text(
"Submit",
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(color: AppColors.app_blue, borderRadius: BorderRadius.circular(15)),
child: provider.submitClickced
? CircularProgressIndicator.adaptive(valueColor: AlwaysStoppedAnimation(AppColors.white))
: Text("Submit", style: TextStyle(fontSize: 15, fontFamily: "JakartaMedium", color: Colors.white)),
),
),
] else ...[
......@@ -996,45 +996,48 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
if (_currentStep == 0) {
if (provider.validateStep1()) {
_currentStep = 1;
} else {
provider.notifyListeners();
_currentStep = 0;
}
} else if (_currentStep == 1) {
if (provider.validateStep2()) {
if (provider.stateSearchController.text.trim().isNotEmpty ||
provider.districtSearchController.text.trim().isNotEmpty ||
provider.addressController.text.trim().isNotEmpty) {
// user has filled something - validate
if (provider.validateStep2()) {
_currentStep = 2;
} else {
provider.notifyListeners();
}
} else {
_currentStep = 2;
}
} else if (_currentStep == 2) {
if (provider.validateStep3()) {
_currentStep = 3;
// trigger GST/Bank validate if fields present
if (provider.gstNumberController.text.trim().isNotEmpty) {
_validateGstIfNeeded(provider);
}
if (provider.bankAcNumberController.text.trim().isNotEmpty &&
provider.bankIfscCotroller.text.trim().isNotEmpty) {
_validateBankIfNeeded(provider);
}
// after async validations we just try to move forward;
// if errors exist they will be shown and user stays after next tap
_currentStep = 3;
} else {
_currentStep = 0;
}
});
details.onStepContinue;
},
child: Container(
height: 45,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
decoration: BoxDecoration(
color: AppColors.app_blue,
borderRadius: BorderRadius.circular(15),
),
child: Text(
"Proceed to Next Step",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
margin: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(color: AppColors.app_blue, borderRadius: BorderRadius.circular(15)),
child: Text("Proceed to Next Step", textAlign: TextAlign.start, style: TextStyle(fontSize: 15, fontFamily: "JakartaMedium", color: Colors.white)),
),
),
],
......@@ -1044,24 +1047,16 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
setState(() {
if (_currentStep == 3) {
_currentStep = 2;
}
if (_currentStep == 2) {
} else if (_currentStep == 2) {
_currentStep = 1;
} else if (_currentStep == 1) {
_currentStep = 0;
} else {
_currentStep = 3;
_currentStep = 0;
}
});
details.onStepCancel;
},
child: Text(
'Back',
style: TextStyle(
color: AppColors.app_blue,
fontSize: 14,
),
),
child: Text('Back', style: TextStyle(color: AppColors.app_blue, fontSize: 14)),
),
],
],
......@@ -1074,6 +1069,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
);
}
// --- Not used UI variant kept for parity with your original file ---
Widget _scaffold1(BuildContext context) {
return Consumer<Accountslistprovider>(
builder: (context, provider, child) {
......@@ -1129,27 +1125,27 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
],
),
items:
provider.accountTypes
.map(
(act) => DropdownMenuItem<String>(
value: act,
child: Text(
act,
style: const TextStyle(
fontSize: 14,
),
overflow: TextOverflow.ellipsis,
),
),
)
.toList(),
provider.accountTypes
.map(
(act) => DropdownMenuItem<String>(
value: act,
child: Text(
act,
style: const TextStyle(
fontSize: 14,
),
overflow: TextOverflow.ellipsis,
),
),
)
.toList(),
// value: provider.selectedAccountType,
value:
provider.accountTypes.contains(
provider.selectedAccountType,
)
? provider.selectedAccountType
: null,
provider.accountTypes.contains(
provider.selectedAccountType,
)
? provider.selectedAccountType
: null,
onChanged: (value) {
if (value != null) {
provider.selectedAccountType = value;
......@@ -1173,7 +1169,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.nameController,
"Company Name",
"Enter Company Name",
(p0) {
(p0) {
provider.updateName(p0);
provider.checkInputsAPI(
context,
......@@ -1194,7 +1190,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.mobileController,
"Mobile Number",
"Enter Mobile",
(p0) {
(p0) {
provider.updateMobile(p0);
provider.checkInputsAPI(
context,
......@@ -1291,9 +1287,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
child: DropdownButton2<States>(
focusNode: focusNodes[2],
autofocus:
focusNodes[2].hasFocus
? true
: false,
focusNodes[2].hasFocus
? true
: false,
isExpanded: true,
hint: Text(
'Select State',
......@@ -1301,33 +1297,33 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
overflow: TextOverflow.ellipsis,
),
items:
provider.states
.map(
(
states,
) => DropdownMenuItem<
States
>(
value: states,
child: Text(
states.name ?? '',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
provider.states
.map(
(
states,
) => DropdownMenuItem<
States
>(
value: states,
child: Text(
states.name ?? '',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
value:
provider.states.contains(
provider.selectedState,
)
? provider.selectedState
: null,
provider.states.contains(
provider.selectedState,
)
? provider.selectedState
: null,
// value: provider.selectedState,
onChanged: (States? value) {
if (value != null) {
......@@ -1340,19 +1336,19 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
"Selected Complaint Type: ${value.name}, ID: ${value.id}",
);
provider.selectedStateID =
value.id!;
value.id!;
print(
"hfjkshfg${provider.selectedStateID}",
);
if (provider
.selectedDistricts !=
.selectedDistricts !=
null) {
provider.states.clear();
// provider.selectedDistricts = null;
provider.selectedDistrictId =
null;
null;
provider.selectedDistrictValue =
null;
null;
}
provider.getDistrictAPI(
......@@ -1366,44 +1362,44 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider
.stateSearchController,
provider
.stateSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(
8,
),
child: TextFormField(
controller:
provider
.stateSearchController,
provider
.stateSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText:
'Search States...',
'Search States...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(
8,
),
BorderRadius.circular(
8,
),
),
),
),
),
searchMatchFn: (
item,
searchValue,
) {
item,
searchValue,
) {
return item.value?.name
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
false;
},
),
......@@ -1414,13 +1410,13 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
}
},
buttonStyleData:
ddtheme.buttonStyleData,
ddtheme.buttonStyleData,
iconStyleData:
ddtheme.iconStyleData,
ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
ddtheme.dropdownStyleData,
),
),
],
......@@ -1441,34 +1437,34 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
overflow: TextOverflow.ellipsis,
),
items:
provider.districts
.map(
(
dist,
) => DropdownMenuItem<
Districts
>(
value: dist,
child: Text(
dist.district ?? '',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
provider.districts
.map(
(
dist,
) => DropdownMenuItem<
Districts
>(
value: dist,
child: Text(
dist.district ?? '',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
value:
provider.districts.contains(
provider
.selectedDistricts,
)
? provider.selectedDistricts
: null,
provider.districts.contains(
provider
.selectedDistricts,
)
? provider.selectedDistricts
: null,
// value: provider.selectedDistricts,
onChanged: (Districts? value) {
if (value != null) {
......@@ -1481,21 +1477,21 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
"Selected ID: ${value.id}",
);
provider.selectedDistrictId =
value.id!;
value.id!;
provider.selectedDistrictValue =
value.district!;
value.district!;
print(
"hfjkshfg${provider.selectedDistrictId}",
);
if (provider
.selectedSubLocations !=
.selectedSubLocations !=
null) {
// provider.selectedSubLocations =
// null;
provider.selectedSubLocID =
null;
null;
provider.selectedSubLocValue =
null;
null;
}
provider.getSubLocationAPI(
context,
......@@ -1507,44 +1503,44 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider
.districtSearchController,
provider
.districtSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(
8,
),
child: TextFormField(
controller:
provider
.districtSearchController,
provider
.districtSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText:
'Search Districts...',
'Search Districts...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(
8,
),
BorderRadius.circular(
8,
),
),
),
),
),
searchMatchFn: (
item,
searchValue,
) {
item,
searchValue,
) {
return item.value?.district
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
false;
},
),
......@@ -1556,13 +1552,13 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
}
},
buttonStyleData:
ddtheme.buttonStyleData,
ddtheme.buttonStyleData,
iconStyleData:
ddtheme.iconStyleData,
ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
ddtheme.dropdownStyleData,
),
),
],
......@@ -1583,37 +1579,37 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
overflow: TextOverflow.ellipsis,
),
items:
provider.subLocations
.map(
(
subloc,
) => DropdownMenuItem<
SubLocations
>(
value: subloc,
child: Text(
subloc.subLocality ??
'',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
provider.subLocations
.map(
(
subloc,
) => DropdownMenuItem<
SubLocations
>(
value: subloc,
child: Text(
subloc.subLocality ??
'',
style:
const TextStyle(
fontSize: 14,
),
overflow:
TextOverflow
.ellipsis,
),
),
)
.toList(),
// value: provider.selectedSubLocations,
value:
provider.subLocations.contains(
provider
.selectedSubLocations,
)
? provider
.selectedSubLocations
: null,
provider.subLocations.contains(
provider
.selectedSubLocations,
)
? provider
.selectedSubLocations
: null,
onChanged: (SubLocations? value) {
if (value != null) {
if (provider
......@@ -1625,9 +1621,9 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
"Selected ID: ${value.id}",
);
provider.selectedSubLocID =
value.id!;
value.id!;
provider.selectedSubLocValue =
value.subLocality!;
value.subLocality!;
print(
"hfjkshfg${provider.selectedSubLocID}",
);
......@@ -1637,44 +1633,44 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
dropdownSearchData: DropdownSearchData(
searchInnerWidgetHeight: 50,
searchController:
provider
.subLocSearchController,
provider
.subLocSearchController,
searchInnerWidget: Padding(
padding: const EdgeInsets.all(
8,
),
child: TextFormField(
controller:
provider
.subLocSearchController,
provider
.subLocSearchController,
decoration: InputDecoration(
isDense: true,
contentPadding:
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
const EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
),
hintText:
'Search Sub Locality...',
'Search Sub Locality...',
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(
8,
),
BorderRadius.circular(
8,
),
),
),
),
),
searchMatchFn: (
item,
searchValue,
) {
item,
searchValue,
) {
return item.value?.subLocality
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
?.toLowerCase()
.contains(
searchValue
.toLowerCase(),
) ??
false;
},
),
......@@ -1685,13 +1681,13 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
}
},
buttonStyleData:
ddtheme.buttonStyleData,
ddtheme.buttonStyleData,
iconStyleData:
ddtheme.iconStyleData,
ddtheme.iconStyleData,
menuItemStyleData:
ddtheme.menuItemStyleData,
ddtheme.menuItemStyleData,
dropdownStyleData:
ddtheme.dropdownStyleData,
ddtheme.dropdownStyleData,
),
),
],
......@@ -1863,7 +1859,7 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
provider.contectPersonAltMobController,
"Alternative Mobile Number",
"Enter Alternative Mobile Number",
(p0) {
(p0) {
provider.updateAltMobile(p0);
provider.checkInputsAPI(
context,
......@@ -1925,12 +1921,12 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
bottomNavigationBar: InkResponse(
onTap:
provider.submitClickced
? null
: () {
provider.submitClickced = true;
provider.submitCommonAccountsAPI(context, widget.from);
},
provider.submitClickced
? null
: () {
provider.submitClickced = true;
provider.submitCommonAccountsAPI(context, widget.from);
},
child: Container(
height: 45,
alignment: Alignment.center,
......@@ -1941,18 +1937,18 @@ class _AddcommonpaymentState extends State<Addcommonpayment> {
borderRadius: BorderRadius.circular(15),
),
child:
provider.submitClickced
? CircularProgressIndicator.adaptive(
valueColor: AlwaysStoppedAnimation(AppColors.white),
)
: Text(
"Submit",
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
provider.submitClickced
? CircularProgressIndicator.adaptive(
valueColor: AlwaysStoppedAnimation(AppColors.white),
)
: Text(
"Submit",
style: TextStyle(
fontSize: 15,
fontFamily: "JakartaMedium",
color: Colors.white,
),
),
),
),
);
......
......@@ -103,7 +103,7 @@ class _TransactiondetailsState extends State<Transactiondetails> {
),
),
),
SizedBox(width: 10),
SizedBox(width: 8),
Expanded(
flex: 3,
child: Container(
......@@ -117,15 +117,15 @@ class _TransactiondetailsState extends State<Transactiondetails> {
),
child: Center(
child: Text(
"₹ ${details.amount ?? "-"}",
" ${details.amount ?? "-"}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontFamily: "JakartaRegular",
fontSize: 14,
fontSize: 12.8,
color:
widget.type == "Credited"
widget.type == "Credit"
? Color(0xFFEF3739)
: Color(0xFF0D9C00),
),
......@@ -359,6 +359,7 @@ class _TransactiondetailsState extends State<Transactiondetails> {
details
.attachmentDirFilePath ??
"",
downloadEnable: false,
),
),
);
......
......@@ -151,6 +151,11 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
InkResponse(
onTap: () async {
HapticFeedback.selectionClick();
final provider = Provider.of<Leadlistprovider>(context, listen: false);
// If lists are empty, fetch required data first (this will populate sources, teams, states, employees)
if (provider.sourcesList.isEmpty || provider.teamsList.isEmpty || provider.employeesList.isEmpty) {
await provider.crmLeadListViewAPIFunction(context, widget.mode);
}
_showFilterSheetNew(context);
},
child: SvgPicture.asset(
......@@ -158,6 +163,7 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
height: 25,
),
),
],
),
0xFFFFFFFF,
......@@ -206,7 +212,36 @@ class _LeadlistbymodeState extends State<Leadlistbymode> {
provider.crmLeadList[index].leadid,
),
),
);
).then((_) async {
final provider = Provider.of<Leadlistprovider>(context, listen: false);
provider.resetPagination();
if (widget.filter != null) {
provider.crmLeadListAPIFunction(
context,
widget.mode,
widget.filter!.status,
widget.filter!.openStatus,
"",
"",
"",
"",
"",
);
} else {
provider.crmLeadListAPIFunction(
context,
widget.mode,
"",
"",
"",
"",
"",
"",
"",
);
}
});
},
child: Container(
padding: EdgeInsets.symmetric(
......
......@@ -291,7 +291,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
provider.companyNameController,
"Company Name",
"Enter Company Name",
provider.onChangeCompanyName,
(value) => provider.onChangeCompanyName(context, value),
TextInputType.name,
false,
null,
......@@ -301,21 +301,34 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
),
errorWidget(context, provider.companynameError),
textControllerWidget(
context,
provider.contactPersonNameController,
"Contact Person Name",
"Enter Name",
(value) => provider.onChangeContactPersonName(context, value),
TextInputType.name,
false,
null,
focusNodes[1],
focusNodes[2],
TextInputAction.next,
),
// textControllerWidget(
// context,
// provider.contactPersonNameController,
// "Contact Person Name",
// "Enter Name",
//
// TextInputType.name,
// false,
// null,
// focusNodes[1],
// focusNodes[2],
// TextInputAction.next,
// ),
errorWidget(context, provider.nameError),
textControllerWidget(
context,
provider.contactPersonNameController,
"Contact Person Name",
"Enter Name",
provider.onChangeContactPersonName,
TextInputType.name,
false,
null,
focusNodes[1],
focusNodes[2],
TextInputAction.next
),
errorWidget(context, provider.nameError),
textControllerWidget(
context,
......@@ -980,7 +993,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
),
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 8,
vertical: 6,
),
decoration: BoxDecoration(
color: Color(0xFFE6F6FF),
......@@ -1040,7 +1053,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
color: AppColors.grey_semi,
),
),
SizedBox(height: 5),
SizedBox(height: 2),
DottedLine(
dashGapLength: 4,
dashGapColor: Colors.white,
......@@ -1048,7 +1061,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
dashLength: 2,
lineThickness: 0.5,
),
SizedBox(height: 5),
SizedBox(height: 2),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
......@@ -1062,7 +1075,7 @@ class _AddleadsprospectsscreenState extends State<Addleadsprospectsscreen> {
),
],
),
SizedBox(height: 5),
SizedBox(height: 2),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
......
......@@ -16,6 +16,7 @@ import 'package:generp/screens/crm/universalSearchScreen.dart';
import 'package:provider/provider.dart';
import 'package:generp/screens/screensExports.dart';
import '../../Notifiers/crmProvider/LeadListProvider.dart';
import '../../Notifiers/crmProvider/crmDashboardProvider.dart';
import '../../Utils/app_colors.dart';
import '../../Utils/commonServices.dart';
......@@ -272,19 +273,48 @@ class _CrmdashboardScreenState extends State<CrmdashboardScreen> {
await Navigator.push(
context,
MaterialPageRoute(
builder:
(context) => Leadlistbymode(
pageTitleName: leadTitles[jndex],
mode:
provider
.allLeads[jndex]
.filter!
.mode,
filter:
provider.allLeads[jndex].filter!,
),
builder: (context) => Leadlistbymode(
pageTitleName: leadTitles[jndex],
mode: provider.allLeads[jndex].filter!.mode,
filter: provider.allLeads[jndex].filter!,
),
),
);
).then((_) async {
// get the Leadlistprovider and reset filters + reload list
final leadProv = Provider.of<Leadlistprovider>(context, listen: false);
// Clear selected filters
leadProv.resetForm();
// Reset pagination and reload the list (same as your existing pattern)
leadProv.resetPagination();
if (provider.allLeads[jndex].filter != null) {
leadProv.crmLeadListAPIFunction(
context,
provider.allLeads[jndex].filter!.mode,
provider.allLeads[jndex].filter!.status,
provider.allLeads[jndex].filter!.openStatus,
"",
"",
"",
"",
"",
);
} else {
leadProv.crmLeadListAPIFunction(
context,
provider.allLeads[jndex].filter!.mode,
"",
"",
"",
"",
"",
"",
"",
);
}
});
}
}
if (leadTitles[jndex] == "Pending Tasks") {
......
......@@ -179,49 +179,45 @@ class _EditAccountDetailsState extends State<EditAccountDetails> {
),
),
SizedBox(height: 25),
InkWell(
onTap:
provider.isLoading
? null
: () {
provider.isLoading = true;
provider
.crmLeadDetailsEditAccountSubmitAPIFunction(
context,
widget.accountID,
provider.selectedSegmentId,
provider.selectedTeamId,
);
},
child: Container(
alignment: Alignment.center,
height: 45,
margin: EdgeInsets.only(
left: 5.0,
right: 5.0,
top: 5.0,
bottom: 5.0,
),
decoration: BoxDecoration(
color: AppColors.app_blue, //1487C9
borderRadius: BorderRadius.circular(14.0),
// Use ElevatedButton so we can easily disable it when invalid or loading
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: (provider.isLoading || !provider.isFormValid)
? null
: () {
// DO NOT set provider.isLoading here — provider will manage it.
provider.crmLeadDetailsEditAccountSubmitAPIFunction(
context,
widget.accountID,
provider.selectedSegmentId,
provider.selectedTeamId,
);
},
style: ElevatedButton.styleFrom(
padding: EdgeInsets.symmetric(vertical: 12.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14.0),
),
backgroundColor: AppColors.app_blue,
disabledBackgroundColor: AppColors.app_blue.withOpacity(0.5),
),
child: Center(
child:
provider.isLoading
? CircularProgressIndicator.adaptive(
valueColor: AlwaysStoppedAnimation(
AppColors.white,
),
)
: Text(
"Submit",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
child: provider.isLoading
? SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator.adaptive(
valueColor: AlwaysStoppedAnimation(Colors.white),
strokeWidth: 2,
),
)
: Text(
"Submit",
style: TextStyle(color: Colors.white),
),
),
),
],
),
),
......
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:open_filex/open_filex.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_svg/svg.dart';
import 'package:generp/Utils/commonWidgets.dart';
import 'package:path_provider/path_provider.dart';
import 'package:syncfusion_flutter_pdfviewer/pdfviewer.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_pdfview/flutter_pdfview.dart';
......@@ -17,7 +21,8 @@ import '../../Utils/app_colors.dart';
class Fileviewer extends StatefulWidget {
final String fileName;
final String fileUrl;
const Fileviewer({super.key, required this.fileName, required this.fileUrl});
final bool downloadEnable;// use this to show download button or not at
const Fileviewer({super.key, required this.fileName, required this.fileUrl, required this.downloadEnable});
@override
State<Fileviewer> createState() => _FileviewerState();
......@@ -60,36 +65,70 @@ class _FileviewerState extends State<Fileviewer> {
@override
void initState() {
pullToRefreshController =
kIsWeb
? null
: PullToRefreshController(
settings: pullToRefreshSettings,
onRefresh: () async {
if (defaultTargetPlatform == TargetPlatform.android) {
webViewController?.reload();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
webViewController?.loadUrl(
urlRequest: URLRequest(
url: await webViewController?.getUrl(),
),
);
}
},
);
super.initState();
// Create pullToRefreshController only for non-web platforms
if (!kIsWeb) {
pullToRefreshController = PullToRefreshController(
settings: pullToRefreshSettings,
onRefresh: () async {
// Guard: don't run if widget disposed
if (!mounted) return;
try {
if (defaultTargetPlatform == TargetPlatform.android) {
await webViewController?.reload();
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
final currentUrl = await webViewController?.getUrl();
if (currentUrl != null) {
await webViewController?.loadUrl(
urlRequest: URLRequest(url: currentUrl),
);
}
}
} catch (e) {
// ignore errors during refresh
} finally {
// ensure refresh animation stops even if reload fails
safeEndRefreshing();
}
},
);
} else {
pullToRefreshController = null;
}
// Initialize photo view controllers
_photoViewController = PhotoViewController();
_scaleStateController = PhotoViewScaleStateController();
super.initState();
}
/// Safe helper to end pull-to-refresh only when mounted and controller exists.
void safeEndRefreshing() {
if (!mounted) return;
final ctrl = pullToRefreshController;
if (ctrl != null) {
try {
ctrl.endRefreshing();
} catch (e) {
// In some rare cases controller may already be disposed — ignore.
}
}
}
@override
void dispose() {
// Dispose photo controllers first
_photoViewController.dispose();
_scaleStateController.dispose();
pullToRefreshController?.dispose();
// Dispose pullToRefreshController and null it to avoid later use
try {
pullToRefreshController?.dispose();
} catch (e) {
// ignore dispose errors
}
pullToRefreshController = null;
webViewController = null;
super.dispose();
}
......@@ -97,7 +136,50 @@ class _FileviewerState extends State<Fileviewer> {
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: appbarNew(context, "File Viewer", 0xFFFFFFFF),
appBar: AppBar(
backgroundColor: Color(0xFFFFFFFF),
automaticallyImplyLeading: false,
title: SizedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
InkResponse(
onTap: () => Navigator.pop(context, true),
child: SvgPicture.asset(
"assets/svg/appbar_back_button.svg",
height: 25,
),
),
SizedBox(width: 10),
InkResponse(
onTap: () => Navigator.pop(context, true),
child: Text(
"File Viewer",
style: TextStyle(
fontSize: 16,
height: 1.1,
fontFamily: "JakartaSemiBold",
color: AppColors.semi_black,
),
),
),
Spacer(),
/// DOWNLOAD BUTTON (NEW)
/// -----------------------------
if (widget.downloadEnable)
IconButton(
icon: Icon(Icons.download_rounded, color: AppColors.app_blue),
onPressed: () async {
await _downloadFile(widget.fileUrl, widget.fileName);
},
),
],
),
),
),
body: SafeArea(
child: Center(
child: fileWidget(context)
......@@ -106,6 +188,190 @@ class _FileviewerState extends State<Fileviewer> {
);
}
Future<void> _downloadFile(String url, String fileName) async {
try {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Downloading...")),
);
final http.Response response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final bytes = response.bodyBytes;
// Get storage directory
final directory = await getApplicationDocumentsDirectory();
final filePath = "${directory.path}/$fileName";
final file = File(filePath);
await file.writeAsBytes(bytes);
// Download success
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("File downloaded successfully \n At this: ${directory.path}")),
);
// ASK USER TO OPEN THE DOWNLOADED FILE
// -------------------------------------
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
elevation: 0,
child: Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 20,
offset: const Offset(0, 4),
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Success Icon
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: Colors.green.withOpacity(0.1),
shape: BoxShape.circle,
),
child: const Icon(
Icons.check_circle_rounded,
color: Colors.green,
size: 36,
),
),
const SizedBox(height: 16),
// Title
const Text(
"Download Complete",
style: TextStyle(
fontSize: 18,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w700,
color: Colors.black87,
),
),
const SizedBox(height: 8),
// Description
Text(
"Do you want to open the file?",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 14,
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w400,
color: Colors.grey[600],
height: 1.4,
),
),
const SizedBox(height: 24),
// Buttons Row
Row(
children: [
// Cancel Button
Expanded(
child: OutlinedButton(
onPressed: () => Navigator.pop(context),
style: OutlinedButton.styleFrom(
backgroundColor: Colors.transparent,
foregroundColor: Colors.grey[600],
side: BorderSide(
color: Colors.grey[300]!,
width: 1.5,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
),
child: Text(
"Cancel",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.grey[700],
),
),
),
),
const SizedBox(width: 12),
// Open Button
Expanded(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
OpenFilex.open(filePath);
},
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF008CDE),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
padding: const EdgeInsets.symmetric(vertical: 14),
elevation: 0,
shadowColor: Colors.transparent,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.open_in_new, size: 18),
const SizedBox(width: 6),
Text(
"Open",
style: TextStyle(
fontFamily: "Plus Jakarta Sans",
fontWeight: FontWeight.w600,
fontSize: 14,
color: Colors.white,
),
),
],
),
),
),
],
),
],
),
),
);
},
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Failed to download file")),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Error: $e")),
);
}
}
Widget fileWidget(BuildContext context) {
final extension = getFileExtension(widget.fileName);
switch (extension) {
......
......@@ -350,6 +350,7 @@ class _PaymentrequestionlistdetailsState
reqDet
.attachmentDirFilePath ??
"",
downloadEnable: true,
),
),
);
......@@ -920,7 +921,7 @@ class _PaymentrequestionlistdetailsState
textAlign: TextAlign.center,
style: TextStyle(
fontFamily: "JakartaMedium",
fontSize: 14,
fontSize: 13,
color: getTextColor(reqDet.status),
),
),
......@@ -1011,6 +1012,7 @@ class _PaymentrequestionlistdetailsState
reqDet
.attachmentDirFilePath ??
"",
downloadEnable: true,
),
),
);
......@@ -1092,11 +1094,11 @@ class _PaymentrequestionlistdetailsState
decoration: BoxDecoration(color: Colors.white),
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
alignment: Alignment.bottomCenter,
height: 80,
height: 78,
child: Container(
margin: EdgeInsets.only(bottom: 10),
alignment: Alignment.center,
height: 45,
height: 42,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
......@@ -1131,7 +1133,7 @@ class _PaymentrequestionlistdetailsState
"Reject",
style: TextStyle(
color: AppColors.semi_black,
fontSize: 14,
fontSize: 12,
),
),
),
......@@ -1140,7 +1142,7 @@ class _PaymentrequestionlistdetailsState
),
),
),
SizedBox(width: 6),
SizedBox(width: 4),
if ([
"apr_lvl1",
......@@ -1150,7 +1152,7 @@ class _PaymentrequestionlistdetailsState
SvgPicture.asset(
"assets/svg/crm/vertical_line_ic.svg",
),
SizedBox(width: 6),
SizedBox(width: 4),
Expanded(
child: InkResponse(
onTap: () {
......@@ -1186,7 +1188,7 @@ class _PaymentrequestionlistdetailsState
"Approve",
style: TextStyle(
color: AppColors.semi_black,
fontSize: 14,
fontSize: 12,
),
),
),
......@@ -1195,12 +1197,12 @@ class _PaymentrequestionlistdetailsState
),
),
),
SizedBox(width: 6),
SizedBox(width: 4),
] else if (widget.mode == "process") ...[
SvgPicture.asset(
"assets/svg/crm/vertical_line_ic.svg",
),
SizedBox(width: 6),
SizedBox(width: 5),
Expanded(
child: InkResponse(
onTap: () {
......@@ -1231,13 +1233,13 @@ class _PaymentrequestionlistdetailsState
SvgPicture.asset(
"assets/svg/finance/level_add_payment_ic.svg",
),
SizedBox(width: 5),
SizedBox(width: 2),
Center(
child: Text(
"Add Payment",
style: TextStyle(
color: AppColors.semi_black,
fontSize: 14,
fontSize: 12,
),
),
),
......@@ -1280,7 +1282,7 @@ class _PaymentrequestionlistdetailsState
"Edit Amount",
style: TextStyle(
color: AppColors.semi_black,
fontSize: 14,
fontSize: 12,
),
),
),
......
......@@ -487,11 +487,11 @@ class _FinancedashboardState extends State<Financedashboard> {
},
child: Container(
padding: EdgeInsets.symmetric(
vertical: 5,
vertical: 4.6,
horizontal: 15,
),
margin: EdgeInsets.symmetric(
vertical: 7,
vertical: 5,
horizontal: 5,
),
decoration: BoxDecoration(
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment