import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:frontend/services/apiService.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import '../../config/apiUrl.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_field.dart'; class MailSetting extends StatefulWidget { bool isDesktop; final Function(Map) onMailDataChanged; final Map initialMailData; MailSetting({ super.key, required this.isDesktop, required this.initialMailData, required this.onMailDataChanged, }); @override _MailSettingState createState() => _MailSettingState(); } class _MailSettingState extends State { final ApiService apiService = ApiService(); String? orgId; String? userId; Map errorMessages = {}; final Map controllers = {}; bool _obscurePassword = true; Map focusNodes = {}; Map focusStates = {}; List dataHeader = [ "host", "userName", "mail_password", "port", "senderEmail", "toEmail", ]; @override void dispose() { for (var node in focusNodes.values) { node.dispose(); } super.dispose(); } Map getMailData() => mailData; Map get mailData { final data = { "mail_host": controllers["host"]?.text, "mail_user_name": controllers["userName"]?.text, "mail_password": controllers["mail_password"]?.text, "mail_port": controllers["port"]?.text, "sender_email": controllers["senderEmail"]?.text, "to_mail": controllers["toEmail"]?.text, // "org_id": orgId, // "created_by": userId, }; // Only add group_id if it's an edit operation // if (widget.group != null && widget.group!.containsKey('group_id')) { // data["group_id"] = selectedGroupId; // } return data; } void _initControllers() { for (var field in dataHeader) { controllers[field] = TextEditingController(); controllers[field]!.addListener(() { widget.onMailDataChanged(mailData); // Notify parent }); } for (var field in dataHeader) { focusNodes["${field}FocusNode"] = FocusNode(); focusStates["${field}Focused"] = false; } for (var key in focusNodes.keys) { _addFocusListener(focusNodes[key]!, (focus) { setState(() { focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; }); }); } } @override void initState() { super.initState(); // loadAllServices(); for (var field in dataHeader) { controllers[field] = TextEditingController(); } _initControllers(); updateData(); print("Updata 1"); print(widget.initialMailData['sender_email']); } void updateData() { print("Updata 2"); if (widget.initialMailData.isNotEmpty) { controllers["senderEmail"]?.text = widget.initialMailData['sender_email'] ?? ''; controllers["userName"]?.text = widget.initialMailData['mail_user_name'] ?? ''; controllers["mail_password"]?.text = widget.initialMailData['mail_password'] ?? ''; controllers["host"]?.text = widget.initialMailData['mail_host'] ?? ''; controllers["port"]?.text = widget.initialMailData['mail_port']?.toString() ?? ''; } } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } void handleTestMailSubmit() { print("TEStMailData- $mailData"); if (!isValidData(mailData)) { print("USERDETAILS : $mailData"); print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails } else { print("MailSuccessDETAILS : $mailData"); // orgId = await getOrgId(); sendTestMail(mailData); } } Future sendTestMail(Map mailData) async { final token = await getToken(); // Fetch token // Build correct API URL final String apiUrlData = '$apiUrl/api/organizations/testMail'; if (token == null) { throw Exception('Token not found. Please log in.'); } try { final response = await http.post( Uri.parse(apiUrlData), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', 'app-signature': 'ts-traveltool-2025-signature-123456', }, body: jsonEncode(mailData), // Convert map to JSON ); if (response.statusCode == 200 || response.statusCode == 201) { print("Plan submitted successfully!"); print("Response: ${response.body}"); } else if (response.statusCode == 403) { print("403-FORB"); await apiService.logout(context); return null; // throw Exception('Failed to load users'); } else { print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); } } catch (e) { print(" Error submitting plan: $e"); } } bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = [ "mail_host", "mail_user_name", "mail_password", "mail_port", "sender_email", "to_mail", ]; // if (apiselectedUser == null) { // requiredFields.add("password"); // } // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } } // Email validation if (data["to_mail"] != null && data["to_mail"].toString().isNotEmpty) { if (!RegExp( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ).hasMatch(data["to_mail"].toString())) { errorMessages["to_mail"] = "Invalid email format"; // Invalid email format } } // Email validation if (data["sender_email"] != null && data["sender_email"].toString().isNotEmpty) { if (!RegExp( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ).hasMatch(data["sender_email"].toString())) { errorMessages["sender_email"] = "Invalid email format"; // Invalid email format } } return errorMessages.isEmpty; // Valid if there are no errors } void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { errorMessages.remove(field); }); } } @override Widget build(BuildContext context) { return Expanded( child: Container( padding: const EdgeInsets.all(8.0), // color: Colors.amber.shade100, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ widget.isDesktop ? Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.start, children: _buildMailFirstRow(widget.isDesktop), ) : Column( crossAxisAlignment: CrossAxisAlignment.center, children: _buildMailFirstRow(widget.isDesktop), ), SizedBox(height: 10), widget.isDesktop ? Row( // mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.start, children: _buildMailSecondRow(widget.isDesktop), ) : Column(children: _buildMailSecondRow(widget.isDesktop)), SizedBox(height: 10), Container( // color: Color(0xFFF7F7FB), padding: const EdgeInsets.only(top: 5, bottom: 5), child: Row( children: [ Text( "Test Mail", style: GoogleFonts.poppins( color: Color(0xFF114D8B), fontWeight: FontWeight.w600, ), ), ], ), ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: _buildTestMail(widget.isDesktop), ), ], ), ), ); } List _buildMailSecondRow(bool isDesktop) { return [ Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // ✅ This works crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "User Name", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["userNameFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.23 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], focusNode: focusNodes["userNameFocusNode"], controller: controllers["userName"], onChanged: (value) { _clearError("mail_user_name"); }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( labelText: "user name", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["mail_user_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["mail_user_name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) SizedBox(width: 20), Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // ✅ This works crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Password", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["mail_passwordFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.23 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, focusNode: focusNodes["mail_passwordFocusNode"], controller: controllers["mail_password"], onChanged: (value) { _clearError("mail_password"); }, // obscureText: _obscurePassword, autofillHints: const [ // AutofillHints.newPassword, ], // <--- This is key enableSuggestions: false, // <--- Disable suggestions autocorrect: false, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Access Key", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), // suffixIcon: IconButton( // icon: Icon( // _obscurePassword // ? Icons.visibility_off // : Icons.visibility, // size: 16, // ), // onPressed: () { // setState(() { // _obscurePassword = !_obscurePassword; // }); // }, // ), ), ), ), ), if (errorMessages["mail_password"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["mail_password"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), ]; } List _buildMailFirstRow(bool isDesktop) { return [ Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // ✅ This works crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Sender Email", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["senderEmailFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.23 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, focusNode: focusNodes["senderEmailFocusNode"], controller: controllers["senderEmail"], onChanged: (value) { _clearError("sender_email"); // Validate just this one field if (value.trim().isEmpty) { errorMessages["sender_email"] = "Required"; } else if (!RegExp( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ).hasMatch(value)) { errorMessages["sender_email"] = "Invalid email format"; } }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( labelText: "sender email", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["sender_email"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["sender_email"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) SizedBox(width: 20), Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // ✅ This works crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Host", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["hostFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.23 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, focusNode: focusNodes["hostFocusNode"], controller: controllers["host"], onChanged: (value) { _clearError("mail_host"); }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( labelText: "host", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["mail_host"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["mail_host"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) SizedBox(width: 20), Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, // ✅ This works crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Port", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["portFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.23 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, focusNode: focusNodes["portFocusNode"], inputFormatters: [FilteringTextInputFormatter.digitsOnly], controller: controllers["port"], onChanged: (value) { _clearError("mail_port"); }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( labelText: "port", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["mail_port"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["mail_port"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), ]; } List _buildTestMail(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Enter Your Mail Id", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["toEmailFocused"] ?? false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( // focusNode: _destinationFocusNode, focusNode: focusNodes["toEmailFocusNode"], controller: controllers["toEmail"], onChanged: (value) { _clearError("to_mail"); // Validate just this one field if (value.trim().isEmpty) { errorMessages["to_mail"] = "Required"; } else if (!RegExp( r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", ).hasMatch(value)) { errorMessages["to_mail"] = "Invalid email format"; } }, style: GoogleFonts.poppins(fontSize: 12), decoration: InputDecoration( labelText: "To mail", labelStyle: GoogleFonts.poppins( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (errorMessages["to_mail"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["to_mail"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), SizedBox(width: 10), Column( children: [ ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Keep original color foregroundColor: Colors.white, // Keep original color disabledBackgroundColor: Color( 0xFF114D8B, ), // Ensure color remains when disabled disabledForegroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), side: BorderSide(color: Color(0xFF114D8B), width: 2), ), padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12), ), onPressed: () { handleTestMailSubmit(); }, child: Text("Test Email", style: GoogleFonts.poppins(fontSize: 12)), ), ], ), ]; } }