import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_download_manager/flutter_download_manager.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:generp/Utils/commonServices.dart'; import 'package:get/get.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../Models/crmModels/LeadDetailsResponse.dart'; import '../../Utils/app_colors.dart'; import '../../services/api_calling.dart'; import '../HomeScreenNotifier.dart'; import 'package:http/http.dart' as http; class crmLeadDetailsProvider extends ChangeNotifier { final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); static const platform = MethodChannel('in.webgrid.generp/download'); final GlobalKey webViewKey = GlobalKey(); var dl = DownloadManager(); bool _isLoading = false; bool _showMoreDetails = false; bool _leadDetailsLoading = true; var _showEditButton; TextEditingController nameController = TextEditingController(); TextEditingController designationController = TextEditingController(); TextEditingController mobileNumberController = TextEditingController(); TextEditingController alternativeMobileController = TextEditingController(); TextEditingController telephoneController = TextEditingController(); TextEditingController emailController = TextEditingController(); TextEditingController editNameController = TextEditingController(); TextEditingController editDesignationController = TextEditingController(); TextEditingController editMobileNumberController = TextEditingController(); TextEditingController editAlternativeMobileController = TextEditingController(); TextEditingController editTelephoneController = TextEditingController(); TextEditingController editEmailController = TextEditingController(); TextEditingController dateController = TextEditingController(); TextEditingController noteController = TextEditingController(); String? _nameError; String? _designationError; String? _mobileNumError; String? _altMobError; String? _teleError; String? _emailError; String? _noteError; String? _editNameError; String? _editDesignationError; String? _editMobileNumError; String? _editAltMobError; String? _editTeleError; String? _editEmailError; String? _dateError; LeadDetails _leadDetails = LeadDetails(); AccountDetails _accountDetails = AccountDetails(); List _leadProducts = []; List _contactDetails = []; List _followupDetails = []; List _appointmentDetails = []; List _quotationsDetails = []; List _visitTypes = []; String? _selectedVisitType; List _headings = []; List _subHeadings = []; DateTime? _date; String? _formattedDate; String? _formattedDateToSend; String? _visitTypeError = ""; String? get formattedDateToSend => _formattedDateToSend; get showEditButton => _showEditButton; String? get nameError => _nameError; String? get designationError => _designationError; String? get mobileNumError => _mobileNumError; String? get altMobError => _altMobError; String? get teleError => _teleError; String? get emailError => _emailError; String? get noteError => _noteError; String? get visitTypeError => _visitTypeError; String? get editNameError => _editNameError; String? get editDesignationError => _editDesignationError; String? get editMobileNumError => _editMobileNumError; String? get editAltMobError => _editAltMobError; String? get editTeleError => _editTeleError; String? get editEmailError => _editEmailError; String? get dateError => _dateError; bool get isLoading => _isLoading; bool get leadDetailsLoading => _leadDetailsLoading; bool get showMoreDetails => _showMoreDetails; List get visitTypes => _visitTypes; String? get selectedVisitType => _selectedVisitType; LeadDetails get leadDetails => _leadDetails; AccountDetails get accountDetails => _accountDetails; List get leadProducts => _leadProducts; List get contactDetails => _contactDetails; List get followupDetails => _followupDetails; List get appointmentDetails => _appointmentDetails; List get quotationsDetails => _quotationsDetails; // List get Headings => _headings; // // List get subHeadings => _subHeadings; String? get formattedDate => _formattedDate; set selectedVisitType(String? value) { _selectedVisitType = value; _visitTypeError = ""; notifyListeners(); } set nameError(String? value) { _nameError = value; notifyListeners(); } set designationError(String? value) { _designationError = value; notifyListeners(); } set mobileNumError(String? value) { _mobileNumError = value; notifyListeners(); } set altMobError(String? value) { _altMobError = value; notifyListeners(); } set teleError(String? value) { _teleError = value; notifyListeners(); } set emailError(String? value) { _emailError = value; notifyListeners(); } set noteError(String? value) { _noteError = value; notifyListeners(); } set editNameError(String? value) { _editNameError = value; notifyListeners(); } set editDesignationError(String? value) { _editDesignationError = value; notifyListeners(); } set editMobileNumError(String? value) { _editMobileNumError = value; notifyListeners(); } set editAltMobError(String? value) { _editAltMobError = value; notifyListeners(); } set editTeleError(String? value) { _editTeleError = value; notifyListeners(); } set editEmailError(String? value) { _editEmailError = value; notifyListeners(); } set isLoading(bool value) { _isLoading = value; notifyListeners(); } set leadDetailsLoading(bool value){ _leadDetailsLoading = value; notifyListeners(); } set showMoreDetails(bool value) { _showMoreDetails = value; notifyListeners(); } set formattedDate(String? value) { _formattedDate = value; dateController.text = _formattedDate!; _dateError = null; notifyListeners(); } set dateError(value) { _dateError = value; notifyListeners(); } void setDate(DateTime newDate) { _date = newDate; _formattedDate = DateFormat('d MMM yyyy').format(newDate); _formattedDateToSend = DateFormat('yyyy-MM-dd').format(newDate); dateController.text = _formattedDate!; _dateError = null; notifyListeners(); } Future crmLeadDetailsAPIFunction(context, leadID, mode) async { try { var HomeProv = Provider.of(context, listen: false); final data = await ApiCalling.crmLeadDetailsAPI( HomeProv.empId, HomeProv.session, leadID, mode, ); _leadDetailsLoading = true; notifyListeners(); if (data != null) { if (data.sessionExists == 1) { if (data.error == "0") { print("calling"); _leadDetails = data.leadDetails!; _accountDetails = data.accountDetails!; _leadProducts = data.leadProducts!; _contactDetails = data.contactDetails!; _followupDetails = data.followupDetails!; _appointmentDetails = data.appointmentDetails!; _quotationsDetails = data.quotationsDetails!; _showEditButton = data.showEditAccountButton; _visitTypes = ["Phone", "Visit"]; _leadDetailsLoading = false; notifyListeners(); } else { _leadDetailsLoading = false; notifyListeners(); } } else { sessionDoesNotExist(context); } } else { _leadDetailsLoading = false; notifyListeners(); } } catch (e, s) { _leadDetailsLoading = false; notifyListeners(); } } Future crmLeadDetailsAddContactAPIFunction(context, accID) async { try { if(!AddContactValidation()){ return; } _isLoading = true; notifyListeners(); var prov = Provider.of(context, listen: false); final data = await ApiCalling.crmLeadDetailsAddContactAPI( prov.empId, prov.session, accID, nameController.text, mobileNumberController.text, designationController.text, alternativeMobileController.text, telephoneController.text, emailController.text, ); if (data != null) { if (data.error == "0") { _leadDetailsLoading = false; nameController.clear(); mobileNumberController.clear(); designationController.clear(); alternativeMobileController.clear(); telephoneController.clear(); emailController.clear(); Navigator.pop(context, true); _isLoading = false; notifyListeners(); } else { _isLoading = false; notifyListeners(); } } else { _isLoading = false; notifyListeners(); } } catch (e, s) { _isLoading = false; notifyListeners(); } } Future crmLeadDetailsEditContactAPIFunction(context, contactID) async { try { if(!editContactValidation()){ return; } _isLoading = true; notifyListeners(); var prov = Provider.of(context, listen: false); final data = await ApiCalling.crmLeadDetailsEditContactAPI( prov.empId, prov.session, contactID, editNameController.text, editMobileNumberController.text, editDesignationController.text, editAlternativeMobileController.text, editTelephoneController.text, editEmailController.text, ); if (data != null) { if (data.error == "0") { _isLoading = false; _leadDetailsLoading = false; editNameController.clear(); editMobileNumberController.clear(); editDesignationController.clear(); editAlternativeMobileController.clear(); editTelephoneController.clear(); editEmailController.clear(); Navigator.pop(context, true); notifyListeners(); } else { _isLoading = false; notifyListeners(); } } else { _isLoading = false; notifyListeners(); } } catch (e, s) { _isLoading = false; notifyListeners(); } } Future crmDownloadQuotationFunction( context, leadId, quoteId, quotationType, ) async { try { var prov = Provider.of(context, listen: false); final data = await ApiCalling.crmDownloadGenQuoteAPI( prov.empId, prov.session, leadId, quoteId, quotationType, ); if (data != null) { if (data.sessionExists == 1) { if (data.error == "0") { String suggestedFilename = getUniqueFilename('quotation', 'pdf'); String contentDisposition = 'attachment; filename="$suggestedFilename"'; // _handleDownload(data.quoteFilepath!); _handleDownload( context, data.quoteFilepath!, contentDisposition, 'application/octet-stream', '', ); notifyListeners(); } else { notifyListeners(); } } else { sessionDoesNotExist(context); } } else { notifyListeners(); } } catch (e, s) { _isLoading = false; notifyListeners(); } } Future crmCheckFields(context, value, type) async { try { var prov = Provider.of(context, listen: false); final data = await ApiCalling.crmCheckAccountFieldsApi( prov.empId, prov.session, value, type, ); if (data != null) { if (data.error == "0") { nameError = null; mobileNumError = null; altMobError = null; notifyListeners(); } else if (data.error == "1") { if (type == "name") { nameError = data.message!; } else if (type == 'mob1') { mobileNumError = data.message!; } else if (type == 'mob2') { altMobError = data.message!; } notifyListeners(); } } else { notifyListeners(); } } catch (e, s) { notifyListeners(); } } Future crmLeadDetailsAddAppointmentAPIFunction( context, leadID, appointmentDate, appointmentType, note, ) async { if(!validAppointment()){ return; } _isLoading = true; notifyListeners(); try { var prov = Provider.of(context, listen: false); final data = await ApiCalling.crmLeadDetailsAddAppointmentAPI( prov.empId, prov.session, leadID, appointmentDate, appointmentType, note, ); if (data != null) { if (data.error == "0") { _isLoading = false; _leadDetailsLoading = false; dateController.clear(); noteController.clear(); selectedVisitType = null; _selectedVisitType = null; Navigator.pop(context, true); _isLoading = false; notifyListeners(); } else { _isLoading = false; notifyListeners(); } } else { _isLoading = false; notifyListeners(); } } catch (e, s) { _isLoading = false; notifyListeners(); } } void showDatePickerDialog(BuildContext context) { showCupertinoModalPopup( context: context, builder: (BuildContext context) => Container( height: 216, padding: const EdgeInsets.only(top: 6.0), margin: EdgeInsets.only( bottom: MediaQuery.of(context).viewInsets.bottom, ), color: CupertinoColors.systemBackground.resolveFrom(context), child: SafeArea( top: false, child: Column( children: [ Expanded( flex: 2, child: SizedBox( height: 40, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ CupertinoButton( child: Text( 'Cancel', style: TextStyle( fontFamily: "JakartaMedium", color: AppColors.app_blue, ), ), onPressed: () { Navigator.pop(context); }, ), CupertinoButton( child: Text( 'Done', style: TextStyle( fontFamily: "JakartaMedium", color: AppColors.app_blue, ), ), onPressed: () { setDate(_date ?? DateTime.now()); Navigator.pop(context); }, ), ], ), ), ), Expanded( flex: 4, child: CupertinoDatePicker( dateOrder: DatePickerDateOrder.dmy, initialDateTime: _date ?? DateTime.now(), minimumDate: DateTime(DateTime.now().year, DateTime.now().month, DateTime.now().day), mode: CupertinoDatePickerMode.date, use24hFormat: true, showDayOfWeek: true, onDateTimeChanged: (DateTime newDate) { setDate(newDate); }, ), ), ], ), ), ), ); } List> prepareExportData() { final headers = [ 'Company Name:', 'Contact Person Name:', 'Mobile Number:', 'Alternate Mobile Number:', 'Email ID:', 'Address:', 'Product:', 'Lead Received Date:', ]; final rows = [ leadDetails.name ?? "-", leadDetails.contName ?? "-", leadDetails.mob1 ?? "-", leadDetails.mob2 ?? "-", leadDetails.email ?? "-", leadDetails.address ?? "-", leadDetails.prod ?? "-", leadDetails.createdDatetime ?? "-", ].toList(); return [headers, rows]; } void copyToClipboard(BuildContext context) async { try { if (leadDetails == null) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("No data to copy"))); return; } final data = prepareExportData(); // if (data.isEmpty || data[1].every((element) => element == "-")) { // ScaffoldMessenger.of(context).showSnackBar( // SnackBar(content: Text("No valid data to copy")), // ); // return; // } String raw = data.map((row) => row.join('\t')).join('\n'); print('Clipboard data: $raw'); await Clipboard.setData(ClipboardData(text: raw)); ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("Copied to Clipboard"))); } catch (e) { print('Error copying to clipboard: $e'); ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text("Failed to copy to clipboard"))); } } Future _handleDownload( context, String url, String contentDisposition, String mimeType, String suggestedFilename, ) async { // Request notification permission for Android 13+ if (Platform.isIOS) { _handleIOSDownload(context, url, suggestedFilename); } else if (Platform.isAndroid) { if (await Permission.notification.request().isGranted) { try { // Show custom notification (optional, since DownloadManager shows its own) if (Platform.isAndroid) { // Call native Android Download Manager final userAgent = 'Flutter InAppWebView'; await platform.invokeMethod('startDownload', { 'url': url, 'userAgent': userAgent, 'contentDisposition': contentDisposition, 'mimeType': mimeType, 'suggestedFilename': suggestedFilename, }); await launchUrl( Uri.parse(url), mode: LaunchMode.externalApplication, ); } else if (Platform.isIOS) { _handleIOSDownload(context, url, suggestedFilename); } } catch (e) { print("Download Error $e"); } } else { toast(context, "Notification Permission Denied"); } } } Future _handleIOSDownload( context, String url, String suggestedFilename, ) async { try { // Show initial download notification await _showDownloadNotification(0, suggestedFilename, isComplete: false); // Get the temporary directory for iOS final tempDir = await getTemporaryDirectory(); final fileName = suggestedFilename.isNotEmpty ? suggestedFilename : url.split('/').last; final filePath = '${tempDir.path}/$fileName'; // Download the file using http final response = await http.get(Uri.parse(url)); if (response.statusCode == 200) { // Save the file final file = File(filePath); await file.writeAsBytes(response.bodyBytes); // Show completion notification await _showDownloadNotification(100, fileName, isComplete: true); // Optionally, open the file or notify the user toast(context, "File downloaded to $filePath"); } else { throw Exception("Failed to download file: HTTP ${response.statusCode}"); } } catch (e) { print("iOS Download Error: $e"); await _showDownloadNotification( 0, suggestedFilename, isComplete: false, isError: true, ); toast(context, "Failed to download file: $e"); } } String getUniqueFilename(String baseName, [String ext = 'pdf']) { final now = DateTime.now(); final formattedDate = DateFormat('yyyyMMdd_HHmmss').format(now); return '${baseName}_$formattedDate.$ext'; } Future _showDownloadNotification( int progress, String fileName, { bool isComplete = false, bool isError = false, }) async { final androidDetails = AndroidNotificationDetails( 'download_channel', 'Downloads', channelDescription: 'Notifications for file downloads', importance: Importance.high, priority: Priority.high, showProgress: !isComplete && !isError, maxProgress: 100, progress: progress, ongoing: !isComplete && !isError, playSound: isComplete || isError, styleInformation: BigTextStyleInformation( isError ? 'Download failed for $fileName' : isComplete ? 'Download complete: $fileName' : 'Downloading $fileName...', ), ); final iosDetails = DarwinNotificationDetails( presentAlert: true, presentBadge: true, presentSound: isComplete || isError, subtitle: isError ? 'Download failed' : isComplete ? 'Download complete' : 'Downloading...', threadIdentifier: 'download_thread', ); final notificationDetails = NotificationDetails( android: androidDetails, iOS: iosDetails, ); await _notificationsPlugin.show( fileName.hashCode, // Unique ID for the notification isError ? 'Download Failed' : isComplete ? 'Download Complete' : 'Downloading File', isError ? 'Failed to download $fileName' : isComplete ? 'Successfully downloaded $fileName' : 'Downloading $fileName ($progress%)', notificationDetails, ); } void onChangeName(value) { _nameError = ""; notifyListeners(); } void onChangeDesignation(value) { _designationError = ""; notifyListeners(); } void onChangeMobile(value) { _mobileNumError = ""; notifyListeners(); } void onChangeAltMobile(value) { if(mobileNumberController.text.trim() == alternativeMobileController.text.trim()){ _altMobError = "Mobile Number and Alternate Mobile Number Should not be same"; }else{ _altMobError = ""; } notifyListeners(); } void onChangeTelephone(value) { _teleError = ""; notifyListeners(); } void onChangeEmailId(value) { _emailError = ""; notifyListeners(); } void onChangeEditName(value) { _editNameError = ""; notifyListeners(); } void onChangeEditDesignation(value) { _editDesignationError = ""; notifyListeners(); } void onChangeEditMobile(value) { _editMobileNumError = ""; notifyListeners(); } void onChangeEditAltMobile(value) { if(editMobileNumberController.text.trim() == editAlternativeMobileController.text.trim()){ _editAltMobError = "Mobile Number and Alternate Mobile Number Should not be same"; }else{ _editAltMobError = ""; } notifyListeners(); } void onChangeEditTelephone(value) { _editTeleError = ""; notifyListeners(); } void onChangeEditEmailId(value) { _editEmailError = ""; notifyListeners(); } void onChangeNote(value) { _noteError = ""; notifyListeners(); } bool AddContactValidation(){ bool isValid = true; _nameError = null; _designationError = null; _mobileNumError = null; _altMobError = null; _teleError = null; _emailError = null; _noteError = null; if(nameController.text.trim().isEmpty){ _nameError = "Please Enter Name"; isValid = false; } if(mobileNumberController.text.trim().isEmpty || mobileNumberController.text.length<10){ _mobileNumError = "Please Enter Mobile Number 10 Digits"; isValid = false; } if(alternativeMobileController.text.trim().isNotEmpty){ if(alternativeMobileController.text.length<10){ _altMobError = "Please Enter Mobile Number 10 Digits"; isValid = false; } } if(mobileNumberController.text.trim() == alternativeMobileController.text.trim()){ _altMobError = "Mobile Number and Alternate Mobile Number Should not be same"; isValid = false; } if(emailController.text.isNotEmpty){ final RegExp emailRegex = RegExp( r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', ); isValid = emailRegex.hasMatch(emailController.text.trim()); if(!isValid){ _emailError = "Please Enter Proper Email ID"; } } _isLoading = false; notifyListeners(); return isValid; } bool editContactValidation(){ bool isValid = true; _editNameError = null; _editDesignationError = null; _editMobileNumError = null; _editAltMobError = null; _editTeleError = null; _editEmailError = null; _noteError = null; if(editNameController.text.trim().isEmpty){ _editNameError = "Please Enter Name"; isValid = false; } if(editMobileNumberController.text.trim().isEmpty || editMobileNumberController.text.length<10){ _editMobileNumError = "Please Enter Mobile Number 10 Digits"; isValid = false; } if(editAlternativeMobileController.text.trim().isNotEmpty){ if(editAlternativeMobileController.text.length<10){ _editAltMobError = "Please Enter Mobile Number 10 Digits"; isValid = false; } } if(editMobileNumberController.text.trim() == editAlternativeMobileController.text.trim()){ _editAltMobError = "Mobile Number and Alternate Mobile Number Should not be same"; isValid = false; } if(editEmailController.text.isNotEmpty){ final RegExp emailRegex = RegExp( r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', ); isValid = emailRegex.hasMatch(editEmailController.text.trim()); if(!isValid){ _editEmailError = "Please Enter Proper Email ID"; } } _isLoading = false; notifyListeners(); return isValid; } bool validAppointment(){ bool isValid = true; _dateError = null; _visitTypeError = null; _noteError = null; if(dateController.text.trim().isEmpty){ _dateError = "Please Enter Name"; isValid = false; } if(_selectedVisitType==null || _selectedVisitType == ""){ _visitTypeError = "Please Select Appointment Type"; isValid = false; } if(noteController.text.trim().isEmpty){ _noteError = "Please Enter Note"; isValid = false; } _isLoading = false; notifyListeners(); return isValid; } void resetAll() { _formattedDateToSend = null; _formattedDate = null; _date = null; nameController.clear(); designationController.clear(); mobileNumberController.clear(); alternativeMobileController.clear(); telephoneController.clear(); emailController.clear(); editNameController.clear(); editDesignationController.clear(); editMobileNumberController.clear(); editAlternativeMobileController.clear(); editTelephoneController.clear(); editEmailController.clear(); _nameError = ""; _designationError = ""; _mobileNumError = ""; _altMobError = ""; _teleError = ""; _emailError = ""; _editNameError = ""; _editDesignationError = ""; _editMobileNumError = ""; _editAltMobError = ""; _editTeleError = ""; _editEmailError = ""; _isLoading = false; _showMoreDetails = false; _leadDetailsLoading = true; notifyListeners(); } }