ts-tat/lib/Screens/organization/mailSettings.dart
2025-10-27 17:35:57 +05:30

704 lines
23 KiB
Dart

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<String, dynamic>) onMailDataChanged;
final Map<String, dynamic> initialMailData;
MailSetting({
super.key,
required this.isDesktop,
required this.initialMailData,
required this.onMailDataChanged,
});
@override
_MailSettingState createState() => _MailSettingState();
}
class _MailSettingState extends State<MailSetting> {
final ApiService apiService = ApiService();
String? orgId;
String? userId;
Map<String, String> errorMessages = {};
final Map<String, TextEditingController> controllers = {};
bool _obscurePassword = true;
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
List<String> dataHeader = [
"host",
"userName",
"mail_password",
"port",
"senderEmail",
"toEmail",
];
@override
void dispose() {
for (var node in focusNodes.values) {
node.dispose();
}
super.dispose();
}
Map<String, dynamic> getMailData() => mailData;
Map<String, dynamic> 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<void> sendTestMail(Map<String, dynamic> 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<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> 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<Widget> _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<Widget> _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<Widget> _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)),
),
],
),
];
}
}