APP-SIGNATURE

This commit is contained in:
venbaittech 2025-08-20 17:45:27 +05:30
parent d29caf66c5
commit f7342a14b4
39 changed files with 1371 additions and 981 deletions

View File

@ -221,6 +221,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -253,6 +254,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);
@ -308,6 +310,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);

View File

@ -29,7 +29,8 @@ class _CommentModalListState extends State<CommentModalList> {
Future<List<Map<String, dynamic>>> fetchComments1() async {
final response = await http.get(
Uri.parse(
'$apiUrl/api/plans/getRemarksByPlanId?plan_id=${widget.planId}'),
'$apiUrl/api/plans/getRemarksByPlanId?plan_id=${widget.planId}',
),
);
if (response.statusCode == 200) {
@ -58,6 +59,7 @@ class _CommentModalListState extends State<CommentModalList> {
Uri.parse(apiUrldata),
headers: {
'Authorization': 'Bearer $token',
'app-signature': 'ts-traveltool-2025-signature-123456',
'Content-Type': 'application/json',
},
);
@ -81,10 +83,7 @@ class _CommentModalListState extends State<CommentModalList> {
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
title: Text(
'Comments',
style: GoogleFonts.poppins(color: Colors.black),
),
title: Text('Comments', style: GoogleFonts.poppins(color: Colors.black)),
content: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500, // You can adjust this width
@ -119,64 +118,86 @@ class _CommentModalListState extends State<CommentModalList> {
child: DataTable(
columns: [
DataColumn(
label: Text(
'Name',
style:
GoogleFonts.poppins(fontSize: 11, color: Colors.black),
)),
label: Text(
'Name',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.black,
),
),
),
DataColumn(
label: Text(
'Comment',
style:
GoogleFonts.poppins(fontSize: 11, color: Colors.black),
)),
label: Text(
'Comment',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.black,
),
),
),
DataColumn(
label: Text(
'Updated On',
style:
GoogleFonts.poppins(fontSize: 11, color: Colors.black),
)),
label: Text(
'Updated On',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.black,
),
),
),
],
rows: comments.map((comment) {
final name = comment['created_by_name'] ?? 'Unknown';
final remark = comment['remarks'] ?? '';
final rawDateStr = comment['updated_on'];
String updatedOn = '';
rows:
comments.map((comment) {
final name = comment['created_by_name'] ?? 'Unknown';
final remark = comment['remarks'] ?? '';
final rawDateStr = comment['updated_on'];
String updatedOn = '';
if (rawDateStr != null && rawDateStr.isNotEmpty) {
try {
final parsedDate = DateTime.parse(rawDateStr);
updatedOn = DateFormat('d MMM yyyy')
.format(parsedDate); // e.g., 15 May 2025
} catch (e) {
updatedOn = rawDateStr.split(' ').first; // fallback
}
}
if (rawDateStr != null && rawDateStr.isNotEmpty) {
try {
final parsedDate = DateTime.parse(rawDateStr);
updatedOn = DateFormat(
'd MMM yyyy',
).format(parsedDate); // e.g., 15 May 2025
} catch (e) {
updatedOn = rawDateStr.split(' ').first; // fallback
}
}
return DataRow(cells: [
DataCell(Text(
name,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500),
)),
DataCell(Text(
remark,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500),
)),
DataCell(Text(
updatedOn,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500),
)),
]);
}).toList(),
return DataRow(
cells: [
DataCell(
Text(
name,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
),
DataCell(
Text(
remark,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
),
DataCell(
Text(
updatedOn,
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
fontWeight: FontWeight.w500,
),
),
),
],
);
}).toList(),
),
);
},
@ -185,11 +206,14 @@ class _CommentModalListState extends State<CommentModalList> {
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text('Close',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: widget.layoutColorForUser)),
child: Text(
'Close',
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: widget.layoutColorForUser,
),
),
),
],
);

View File

@ -211,6 +211,7 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -243,6 +244,7 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);

View File

@ -200,6 +200,7 @@ class _ApprovalListState extends State<ApprovalList> {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -233,6 +234,7 @@ class _ApprovalListState extends State<ApprovalList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);
@ -293,6 +295,7 @@ class _ApprovalListState extends State<ApprovalList> {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -525,6 +528,7 @@ class _ApprovalListState extends State<ApprovalList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);

View File

@ -117,6 +117,7 @@ class _LoginWidgetState extends State<LoginWidget> {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode({
'email': _emailController.text.trim(),
@ -214,11 +215,14 @@ class _LoginWidgetState extends State<LoginWidget> {
if (_isForgotPassword && !_showOtpResetFields) {
print('11');
// Step 1: Send OTP
final url = '$apiUrl/forgotPassword/verifyUser';
final url = '$apiUrl/api/forgotPassword/verifyUser';
try {
final response = await http.post(
Uri.parse(url),
headers: {'Content-Type': 'application/json'},
headers: {
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode({'email': _emailController.text.trim()}),
);
@ -273,12 +277,15 @@ class _LoginWidgetState extends State<LoginWidget> {
} else if (!_isForgotPassword && _showOtpResetFields) {
print('22');
// Step 2: Verify OTP & Reset Password
final url = '$apiUrl/forgotPassword/changePassword';
final url = '$apiUrl/api/forgotPassword/changePassword';
try {
print('21');
final response = await http.post(
Uri.parse(url),
headers: {'Content-Type': 'application/json'},
headers: {
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode({
'email': _emailController.text.trim(),
'otp': _otpController.text.trim(),
@ -1111,12 +1118,15 @@ class _LoginWidgetState extends State<LoginWidget> {
}
Future<void> handleMS() async {
final url = '$apiUrl/auth/mslogin';
final url = '$apiUrl/api/auth/mslogin';
print(url);
try {
final response = await http.get(
Uri.parse(url),
headers: {'Content-Type': 'application/json'},
headers: {
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
print("inside try method");
if (response.statusCode == 200) {

View File

@ -105,6 +105,7 @@ class CostCenterDataState extends State<CostCenterData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -168,7 +169,7 @@ class CostCenterDataState extends State<CostCenterData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postCostCenterData();
}else{
} else {
isDisable = false;
}
});
@ -212,6 +213,7 @@ class CostCenterDataState extends State<CostCenterData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(costcenterData);
@ -426,22 +428,23 @@ class CostCenterDataState extends State<CostCenterData> {
// ),
SizedBox(
child: ElevatedButton(
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// onPressed: () {
// handleSubmit();
// // You can get text from commentController.text

View File

@ -149,6 +149,7 @@ class CostCenterListState extends State<CostCenterList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
print("called api : $apiUrlData");
@ -286,15 +287,13 @@ class CostCenterListState extends State<CostCenterList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Cost Center Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Cost Center Details'),
],
),
),
@ -310,8 +309,8 @@ class CostCenterListState extends State<CostCenterList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.08),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -458,7 +457,7 @@ class CostCenterListState extends State<CostCenterList> {
builder: (context, snapshot) {
final adjHgt = MediaQuery.of(context).size.height;
if (futureCostCenter == null) {
return CircularProgressIndicator();
return const Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {

View File

@ -205,6 +205,7 @@ class StatusDashboardState extends State<StatusDashboard> {
Uri.parse(apiUrlData),
headers: {
'Authorization': 'Bearer $token',
'app-signature': 'ts-traveltool-2025-signature-123456',
'Content-Type': 'application/json',
},
);

View File

@ -105,6 +105,7 @@ class DepartmentDataState extends State<DepartmentData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -168,7 +169,7 @@ class DepartmentDataState extends State<DepartmentData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postDepartmentData();
}else{
} else {
isDisable = false;
}
});
@ -212,6 +213,7 @@ class DepartmentDataState extends State<DepartmentData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(departmentData);
@ -428,22 +430,23 @@ class DepartmentDataState extends State<DepartmentData> {
// ),
SizedBox(
child: ElevatedButton(
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -149,6 +149,7 @@ class DepartmentListState extends State<DepartmentList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
print("called api : $apiUrlData");
@ -181,11 +182,10 @@ class DepartmentListState extends State<DepartmentList> {
allDepartment.where((object) {
final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['id']?.toLowerCase().contains(
lowerQuery,
) ??
return (object['id']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['dropdown_value']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['dropdown_value']?.toLowerCase().contains(lowerQuery) ?? false) ||
// (object['description']?.toLowerCase().contains(lowerQuery) ??
// false) ||
(isActiveStatus.contains(lowerQuery));
@ -285,15 +285,13 @@ class DepartmentListState extends State<DepartmentList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Department Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Department Details'),
],
),
),
@ -309,8 +307,8 @@ class DepartmentListState extends State<DepartmentList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.08),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -584,8 +582,7 @@ class DepartmentListState extends State<DepartmentList> {
rows:
paginatedDepartment.map((tableObject) {
String departmentId =
tableObject['id']
.toString(); // Get user ID
tableObject['id'].toString(); // Get user ID
bool isSelected =
selectedDepartmentId == departmentId;
@ -648,8 +645,7 @@ class DepartmentListState extends State<DepartmentList> {
// final usersData = await getUserDetails(userId);
//
final departmentId = int.tryParse(
tableObject['id']
.toString(),
tableObject['id'].toString(),
);
if (departmentId != null) {
@ -741,8 +737,7 @@ class DepartmentListState extends State<DepartmentList> {
// final usersData = await getUserDetails(userId);
//
final departmentId = int.tryParse(
cardObject['id']
.toString(),
cardObject['id'].toString(),
);
if (departmentId != null) {

View File

@ -71,6 +71,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -95,7 +96,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
_users = userList.map((user) => SearchUser.fromJson(user)).toList();
_filteredUsers = List.from(_users);
_filterUsers("");
});
print("filtered user === ${_filteredUsers.length}");
print("Users fetched: ${_users.length}");
@ -132,6 +132,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -211,10 +212,15 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
if (query.isEmpty) {
// Return all users excluding self and roleId == 5
_filteredList = _users.where((user) =>
user.userId.toString() != excludedUserId && user.roleId.toString() != "5")
.map((user) => {"type": "user", "data": user})
.toList();
_filteredList =
_users
.where(
(user) =>
user.userId.toString() != excludedUserId &&
user.roleId.toString() != "5",
)
.map((user) => {"type": "user", "data": user})
.toList();
// _filteredList = [
// // ..._users.map((user) => {"type": "user", "data": user}),
@ -277,9 +283,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
setState(() {
_filteredList.clear(); // Reset the list before filtering
if (query.isEmpty) {
_filteredList = _traveller
.map((traveller) => {"type": "traveller", "data": traveller})
.toList();
_filteredList =
_traveller
.map((traveller) => {"type": "traveller", "data": traveller})
.toList();
// _filteredList = [
// ..._users.map((user) => {"type": "user", "data": user}),
// ];
@ -377,19 +384,21 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
@override
void initState() {
super.initState();
if (widget.title == "Others (Non Employee)"){ fetchTraveller(); }
else{ fetchUsers(); }
if (widget.title == "Others (Non Employee)") {
fetchTraveller();
} else {
fetchUsers();
}
print("SelffsdfcurrentUser - ${widget.currentUser}");
_filterByTitle("");
}
@override
Widget build(BuildContext context) {
final isValid = _searchController.text.trim().isNotEmpty &&
final isValid =
_searchController.text.trim().isNotEmpty &&
userIdSelected.trim().isNotEmpty;
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
child: Container(
@ -482,81 +491,100 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
// _searchController.text.isNotEmpty
// ?
if (!_showTravellerForm)
SizedBox(
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child:
_filteredList.isEmpty
? Center(
child: Text(
"No users found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
SizedBox(
height: 300, // Limit height to avoid overflow
// child: _filteredUsers.isEmpty
child:
_filteredList.isEmpty
? Center(
child: Text(
"No users found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
)
: ListView.builder(
// itemCount: _filteredUsers.length,
itemCount: _filteredList.length,
itemBuilder: (context, index) {
// final user = _filteredUsers[index];
final item = _filteredList[index];
final user = item["data"]; // Extract user object
final userType =
item["type"]; // "user" or "traveller"
if (user is Map<String, dynamic>) {
print("userLsirer - ${jsonEncode(user)}"); // pretty JSON-like string
} else {
print("userLsirer - $user"); // fallback
}
final isSelected = userIdSelected == (userType == "user" ? user.userId : user.travellerId);
return ListTile(
// hoverColor: ,
title: Text(
"${user.firstName ?? "Unknown"}" "${(user.lastName?.isNotEmpty ?? false) ? " ${user.lastName}" : ""}",
style: GoogleFonts.poppins(fontSize: 11 , color: isSelected ? widget.layoutColorForUser : Colors.black),
),
subtitle:
userType == "user"
? Text(
"Employee ID: ${user.empCode ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color: isSelected ? widget.layoutColorForUser : Colors.black,
),
)
: Text(
"Mobile : ${user.mobileNo ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color: isSelected ? widget.layoutColorForUser : Colors.black,
),
),
onTap: () {
String selectedUser =
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
userIdSelected =
userType == "user"
? user.userId
: user.travellerId;
isTraveller = userType == "traveller";
});
print(
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected",
);
},
);
},
),
),
// : SizedBox.shrink(),
)
: ListView.builder(
// itemCount: _filteredUsers.length,
itemCount: _filteredList.length,
itemBuilder: (context, index) {
// final user = _filteredUsers[index];
final item = _filteredList[index];
final user = item["data"]; // Extract user object
final userType =
item["type"]; // "user" or "traveller"
if (user is Map<String, dynamic>) {
print(
"userLsirer - ${jsonEncode(user)}",
); // pretty JSON-like string
} else {
print("userLsirer - $user"); // fallback
}
final isSelected =
userIdSelected ==
(userType == "user"
? user.userId
: user.travellerId);
return ListTile(
// hoverColor: ,
title: Text(
"${user.firstName ?? "Unknown"}"
"${(user.lastName?.isNotEmpty ?? false) ? " ${user.lastName}" : ""}",
style: GoogleFonts.poppins(
fontSize: 11,
color:
isSelected
? widget.layoutColorForUser
: Colors.black,
),
),
subtitle:
userType == "user"
? Text(
"Employee ID: ${user.empCode ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color:
isSelected
? widget.layoutColorForUser
: Colors.black,
),
)
: Text(
"Mobile : ${user.mobileNo ?? "-"} ",
// "Employee ID: ${userType == "user" ? user.userId : user.travellerId}",
style: GoogleFonts.poppins(
fontSize: 10,
color:
isSelected
? widget.layoutColorForUser
: Colors.black,
),
),
onTap: () {
String selectedUser =
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
setState(() {
_searchController.text = selectedUser;
userIdSelected =
userType == "user"
? user.userId
: user.travellerId;
isTraveller = userType == "traveller";
});
print(
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
" isTraveller: $userIdSelected",
);
},
);
},
),
),
// : SizedBox.shrink(),
// Traveler Form
if (_showTravellerForm)
@ -616,32 +644,35 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
SizedBox(width: 10),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: isValid ? widget.layoutColorForUser : Colors.grey,
backgroundColor:
isValid ? widget.layoutColorForUser : Colors.grey,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(
// color: widget.layoutColorForUser,
color: isValid ? widget.layoutColorForUser : Colors.grey,
color:
isValid ? widget.layoutColorForUser : Colors.grey,
width: isValid ? 2 : 0,
),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
onPressed: isValid ? () {
print("Submitting: ${_searchController.text}, ID: $userIdSelected");
widget.onSubmit(
_searchController.text,
userIdSelected,
isTraveller,
);
Navigator.pop(context);
}
: null,
child: Text(
"Save",
style: GoogleFonts.poppins(fontSize: 11),
),
onPressed:
isValid
? () {
print(
"Submitting: ${_searchController.text}, ID: $userIdSelected",
);
widget.onSubmit(
_searchController.text,
userIdSelected,
isTraveller,
);
Navigator.pop(context);
}
: null,
child: Text("Save", style: GoogleFonts.poppins(fontSize: 11)),
),
],
),
@ -745,6 +776,7 @@ class _TravelerFormState extends State<TravelerForm> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(requestBody),
);

View File

@ -247,6 +247,7 @@ class _groupState extends State<Group> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(groupData),
)
@ -255,6 +256,7 @@ class _groupState extends State<Group> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(groupData),
));

View File

@ -232,7 +232,7 @@ class GroupDataState extends State<GroupData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postGroupData();
}else{
} else {
isDisable = false;
}
});
@ -270,6 +270,7 @@ class GroupDataState extends State<GroupData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(groupData);
@ -308,12 +309,12 @@ class GroupDataState extends State<GroupData> {
behavior: SnackBarBehavior.floating,
),
);
}
else {
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
final message = jsonDecode(response.body);
final errorMessage = message['messages']?['error'] ?? 'Unknown error occurred';
final errorMessage =
message['messages']?['error'] ?? 'Unknown error occurred';
if (errorMessage.contains("Duplicate entry")) {
_clearError();
@ -464,7 +465,11 @@ class GroupDataState extends State<GroupData> {
),
),
IconButton(
icon: Icon(Icons.remove_circle_sharp, size: 12, color: Colors.redAccent),
icon: Icon(
Icons.remove_circle_sharp,
size: 12,
color: Colors.redAccent,
),
tooltip: "Reset",
onPressed: () {
setState(() {
@ -506,15 +511,15 @@ class GroupDataState extends State<GroupData> {
constraints: BoxConstraints(maxHeight: 250),
itemBuilder:
(context, object, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
object,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Text(
object,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
@ -533,40 +538,47 @@ class GroupDataState extends State<GroupData> {
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["domestic_policy_nameFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
(focusStates["domestic_policy_nameFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["domestic_policy_nameFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
(focusStates["domestic_policy_nameFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
borderSide: BorderSide(
color: widget.layoutColor!,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
@ -574,7 +586,7 @@ class GroupDataState extends State<GroupData> {
DomesticMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
)
.key;
selectedDomesticPolicyName = newValue;
});
@ -602,7 +614,11 @@ class GroupDataState extends State<GroupData> {
),
),
IconButton(
icon: Icon(Icons.remove_circle_sharp, size: 12, color: Colors.redAccent),
icon: Icon(
Icons.remove_circle_sharp,
size: 12,
color: Colors.redAccent,
),
tooltip: "Reset",
onPressed: () {
setState(() {
@ -673,28 +689,35 @@ class GroupDataState extends State<GroupData> {
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["international_policy_nameFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
(focusStates["international_policy_nameFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["international_policy_nameFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
(focusStates["international_policy_nameFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
borderSide: BorderSide(
color: widget.layoutColor!,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
@ -833,21 +856,22 @@ class GroupDataState extends State<GroupData> {
// // You can get text from commentController.text
// // Navigator.of(context).pop(); // Close the modal
// },
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -190,6 +190,7 @@ class _GroupListState extends State<GroupList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode({
"is_active": newStatus, // Set new status dynamically
@ -304,18 +305,17 @@ class _GroupListState extends State<GroupList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Group ',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Group '),
],
),
),
// Text(
// 'Group',
// style: GoogleFonts.poppins(
@ -324,13 +324,12 @@ class _GroupListState extends State<GroupList> {
// color: Colors.black,
// ),
// ),
],
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.145),
// SizedBox(width: MediaQuery.of(context).size.width * 0.28),
// SizedBox(width: MediaQuery.of(context).size.width * 0.28),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -625,7 +624,9 @@ class _GroupListState extends State<GroupList> {
// ),
DataCell(
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200), // limit description width
constraints: BoxConstraints(
maxWidth: 200,
), // limit description width
child: Text(
"${group['name'] ?? ''}",
style: TextStyle(
@ -633,7 +634,8 @@ class _GroupListState extends State<GroupList> {
fontFamily: "Inter",
),
overflow: TextOverflow.ellipsis,
maxLines: 1, // optional: show only 1 line
maxLines:
1, // optional: show only 1 line
softWrap: false,
),
),
@ -667,7 +669,9 @@ class _GroupListState extends State<GroupList> {
// ),
DataCell(
ConstrainedBox(
constraints: BoxConstraints(maxWidth: 200), // limit description width
constraints: BoxConstraints(
maxWidth: 200,
), // limit description width
child: Text(
"${group['description'] ?? 'N/A'}",
style: TextStyle(

View File

@ -120,6 +120,7 @@ class HotelsDataState extends State<HotelsData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -201,7 +202,7 @@ class HotelsDataState extends State<HotelsData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postHotelsData();
}else{
} else {
isDisable = false;
}
});
@ -247,6 +248,7 @@ class HotelsDataState extends State<HotelsData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(hotelsData);
@ -282,7 +284,6 @@ class HotelsDataState extends State<HotelsData> {
setState(() {
isDisable = false;
});
} else {
print("Failed to submit plan. Status: ${response.statusCode}");
print("Error: ${response.body}");
@ -291,14 +292,12 @@ class HotelsDataState extends State<HotelsData> {
isDisable = false;
});
}
}
catch (e) {
} catch (e) {
print("Error submitting plan: $e");
setState(() {
isDisable = false;
});
}
}
@override
@ -318,7 +317,7 @@ class HotelsDataState extends State<HotelsData> {
countryMap = {
for (var country in countryList)
(country['country_code'] ?? ''):
'${country['country_name'] ?? ''} (${country['country_code'] ?? ''})',
'${country['country_name'] ?? ''} (${country['country_code'] ?? ''})',
};
// Extract only country codes for processing
@ -455,7 +454,9 @@ class HotelsDataState extends State<HotelsData> {
focusNode: focusNodes["categoryFocusNode"],
controller: controllers["category"],
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 ]')),
FilteringTextInputFormatter.allow(
RegExp(r'[a-zA-Z0-9 ]'),
),
],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
@ -489,119 +490,144 @@ class HotelsDataState extends State<HotelsData> {
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) {
print("contryItem - $item");
final match = RegExp(r'^(.*)\s\((.*)\)$',).firstMatch(item);
final countryName = match?.group(1) ?? '';
final countryCode = match?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.02,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
countryName,
style: GoogleFonts.poppins(fontSize: 11.5),
),
Text(
countryCode,
style: GoogleFonts.poppins(fontSize: 11.5,color:Colors.grey),
),
],
),
),
);},
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
final match = RegExp(r'^(.*)\s\((.*)\)$').firstMatch(newValue!);
final countryName = match?.group(1) ?? newValue; // --> "Ascension Islands"
// final countryCode = match?.group(2) ?? "";
selectedCountryName = countryName;
});
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
)
)
constraints: BoxConstraints(maxHeight: 200),
itemBuilder: (context, item, isSelected) {
print("contryItem - $item");
final match = RegExp(
r'^(.*)\s\((.*)\)$',
).firstMatch(item);
final countryName = match?.group(1) ?? '';
final countryCode = match?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.02,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
countryName,
style: GoogleFonts.poppins(
fontSize: 11.5,
),
),
Text(
countryCode,
style: GoogleFonts.poppins(
fontSize: 11.5,
color: Colors.grey,
),
),
],
),
),
);
},
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
vertical: 1,
),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: widget.layoutColor!,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
final match = RegExp(
r'^(.*)\s\((.*)\)$',
).firstMatch(newValue!);
final countryName =
match?.group(1) ??
newValue; // --> "Ascension Islands"
// final countryCode = match?.group(2) ?? "";
selectedCountryName = countryName;
});
},
),
),
),
),
),
if (errorMessages["country_code"] != null) ...[
@ -712,22 +738,23 @@ class HotelsDataState extends State<HotelsData> {
// ),
SizedBox(
child: ElevatedButton(
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -152,6 +152,7 @@ class HotelsDataListState extends State<HotelsDataList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -177,6 +178,7 @@ class HotelsDataListState extends State<HotelsDataList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -338,15 +340,13 @@ class HotelsDataListState extends State<HotelsDataList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Hotel Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Hotel Details'),
],
),
),
@ -508,7 +508,7 @@ class HotelsDataListState extends State<HotelsDataList> {
builder: (context, snapshot) {
final adjHgt = MediaQuery.of(context).size.height;
if (futureHotels == null) {
return CircularProgressIndicator();
return const Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
@ -892,7 +892,7 @@ class HotelsDataListState extends State<HotelsDataList> {
children: [
Column(
crossAxisAlignment:
CrossAxisAlignment.start,
CrossAxisAlignment.start,
children: [
Text(
"${hotels['category'] ?? ''} ",

View File

@ -207,6 +207,7 @@ class _ForexScreenState extends State<ForexScreen> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(forexData), // Convert map to JSON
);

View File

@ -1,4 +1,6 @@
import 'dart:convert';
import 'dart:html' as html;
import 'dart:async';
import 'dart:io' as io show Directory, File;
import 'package:delta_to_html/delta_to_html.dart';
import 'package:flutter/cupertino.dart' as dom;
@ -15,6 +17,9 @@ import 'package:frontend/Screens/myTemplates/templateForex.dart'
as _editorScrollController;
import 'package:frontend/Screens/myTemplates/templateForex.dart' as _controller;
import 'package:html2md/html2md.dart' as html2md;
import 'package:http_parser/http_parser.dart';
import 'package:image_picker/image_picker.dart';
import 'package:mime/mime.dart';
import 'package:vsc_quill_delta_to_html/vsc_quill_delta_to_html.dart';
import 'package:flutter_quill/flutter_quill.dart' as quill;
@ -59,6 +64,8 @@ class TemplateForex extends StatefulWidget {
class TemplateForexState extends State<TemplateForex> {
final ApiService apiService = ApiService();
Uint8List? _imageBytes;
String? selectedOrglogo;
// final QuillController _controller = QuillController.basic();
String? orgId;
String? userId;
@ -91,11 +98,6 @@ class TemplateForexState extends State<TemplateForex> {
"body_html": DeltaToHTML.encodeJson(
_controller.document.toDelta().toJson(),
),
// "body_html": jsonEncode(_controller.document.toDelta().toJson()),
// "body_html": _controller,
// "body_html": convertQuillDocToHtml(_controller.document),
// convert delta to HTML
"placeholder": jsonEncode(placeholderList),
// "created_by": userId
};
@ -363,7 +365,7 @@ class TemplateForexState extends State<TemplateForex> {
print(
"API Selected User Has - ${widget.templateData?["templateData"]?["subject"]}",
);
setState(() {
setState(() async {
// Wrap in setState to update the UI
controllers["templateName"]?.text =
widget.templateData?["templateData"]?["template_name"] ?? "";
@ -432,7 +434,7 @@ class TemplateForexState extends State<TemplateForex> {
) ??
0;
print("Fetched template_id: $templateId");
fetchSignature();
// if (widget.group?["international_policy_id"] != null) {
// selectedInternational =
// widget.group!["international_policy_id"].toString();
@ -443,6 +445,34 @@ class TemplateForexState extends State<TemplateForex> {
}
}
Future<void> fetchSignature() async {
final uri = Uri.parse('$apiUrl/api/getForexSignaturePath');
final token = await getToken();
final response = await http.get(
uri,
headers: {'Authorization': 'Bearer $token'},
);
if (response.statusCode == 200) {
print("ERS - $response");
final json = jsonDecode(response.body);
print("ERSjson - $json");
String? rawLogoPath = json['url']?.toString();
if (rawLogoPath != null && rawLogoPath.isNotEmpty) {
print("ERSrawLogoPath - $rawLogoPath");
setState(() {
selectedOrglogo = rawLogoPath;
});
}
} else {
print("❌ Failed to fetch signature: ${response.statusCode}");
}
}
Future<void> handleSubmit() async {
Map<String, dynamic> data = TemplateData;
@ -492,6 +522,7 @@ class TemplateForexState extends State<TemplateForex> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(policyData), // Convert map to JSON
);
@ -519,6 +550,70 @@ class TemplateForexState extends State<TemplateForex> {
}
}
Future<void> _pickImage() async {
final picker = ImagePicker();
final XFile? pickedFile = await picker.pickImage(
source: ImageSource.gallery,
);
if (pickedFile != null && kIsWeb) {
try {
final bytes = await pickedFile.readAsBytes();
print('✅ Image loaded, size: ${bytes.length} bytes');
setState(() {
_imageBytes = bytes;
});
await uploadSignature();
} catch (e) {
print('❌ Error reading image bytes: $e');
}
} else {
print('⚠️ Image picking canceled or not on web.');
}
}
Future<void> uploadSignature() async {
if (_imageBytes == null) {
print('⚠️ No image selected');
return;
}
final token = await getToken(); // Fetch token
if (token == null) {
throw Exception('Token not found. Please log in.');
}
final uri = Uri.parse('$apiUrl/api/forex_signature_upload');
final request = http.MultipartRequest('POST', uri);
// Add auth header
request.headers['Authorization'] = 'Bearer $token';
// Add the image as multipart with the key "signature"
request.files.add(
http.MultipartFile.fromBytes(
'signature', // <-- key name
_imageBytes!, // <-- image bytes
filename: 'signature.png', // <-- filename (can be png/jpg)
contentType: MediaType('image', 'png'),
),
);
try {
final response = await request.send();
final respStr = await response.stream.bytesToString();
if (response.statusCode == 200 || response.statusCode == 201) {
print('✅ Upload successful: $respStr');
} else {
print('❌ Upload failed (${response.statusCode}): $respStr');
}
} catch (e) {
print('❌ Error uploading signature: $e');
}
}
@override
Widget build(BuildContext context) {
return ResponsiveBuilder(
@ -683,56 +778,65 @@ class TemplateForexState extends State<TemplateForex> {
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
child: IconTheme(
data: IconThemeData(size: 18), // Set icon size here
child: QuillSimpleToolbar(
controller: _controller,
config: QuillSimpleToolbarConfig(
embedButtons: FlutterQuillEmbeds.toolbarButtons(),
showClipboardPaste: true,
customButtons: [
QuillToolbarCustomButtonOptions(
icon: const Icon(Icons.add_alarm_rounded),
onPressed: () {
_controller.document.insert(
_controller.selection.extentOffset,
TimeStampEmbed(DateTime.now().toString()),
);
_controller.updateSelection(
TextSelection.collapsed(
offset: _controller.selection.extentOffset + 1,
),
ChangeSource.local,
);
},
),
],
buttonOptions: QuillSimpleToolbarButtonOptions(
base: QuillToolbarBaseButtonOptions(
afterButtonPressed: () {
final isDesktop = {
TargetPlatform.linux,
TargetPlatform.windows,
TargetPlatform.macOS,
}.contains(defaultTargetPlatform);
// if (isDesktop) {
// _editorFocusNode.requestFocus();
// }
},
),
linkStyle: QuillToolbarLinkStyleButtonOptions(
validateLink: (link) {
// Treats all links as valid. When launching the URL,
// `https://` is prefixed if the link is incomplete (e.g., `google.com` `https://google.com`)
// however this happens only within the editor.
return true;
},
),
),
child: Container(
color: Color(0xFFFFFEF0),
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
child: IconTheme(
data: IconThemeData(size: 18), // Set icon size here
child: QuillSimpleToolbar(controller: _controller),
),
),
// child: QuillSimpleToolbar(
// controller: _controller,
// config: QuillSimpleToolbarConfig(
// embedButtons: FlutterQuillEmbeds.toolbarButtons(),
// showClipboardPaste: true,
// customButtons: [
// QuillToolbarCustomButtonOptions(
// icon: const Icon(Icons.add_alarm_rounded),
// onPressed: () {
// _controller.document.insert(
// _controller.selection.extentOffset,
// TimeStampEmbed(DateTime.now().toString()),
// );
//
// _controller.updateSelection(
// TextSelection.collapsed(
// offset: _controller.selection.extentOffset + 1,
// ),
// ChangeSource.local,
// );
// },
// ),
// ],
// buttonOptions: QuillSimpleToolbarButtonOptions(
// base: QuillToolbarBaseButtonOptions(
// afterButtonPressed: () {
// final isDesktop = {
// TargetPlatform.linux,
// TargetPlatform.windows,
// TargetPlatform.macOS,
// }.contains(defaultTargetPlatform);
// // if (isDesktop) {
// // _editorFocusNode.requestFocus();
// // }
// },
// ),
// linkStyle: QuillToolbarLinkStyleButtonOptions(
// validateLink: (link) {
// // Treats all links as valid. When launching the URL,
// // `https://` is prefixed if the link is incomplete (e.g., `google.com` `https://google.com`)
// // however this happens only within the editor.
// return true;
// },
// ),
// ),
// ),
// ),
),
),
SizedBox(height: 2),
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
@ -782,9 +886,10 @@ class TemplateForexState extends State<TemplateForex> {
],
),
),
Container(
padding: const EdgeInsets.all(16),
height: MediaQuery.of(context).size.height * 0.45,
height: MediaQuery.of(context).size.height * 0.38,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Color(0xFFD6D5E6), width: 0.5),
@ -799,15 +904,16 @@ class TemplateForexState extends State<TemplateForex> {
padding: const EdgeInsets.all(16),
embedBuilders: [
...FlutterQuillEmbeds.editorBuilders(
imageEmbedConfig: QuillEditorImageEmbedConfig(
imageProviderBuilder: (context, imageUrl) {
// https://pub.dev/packages/flutter_quill_extensions#-image-assets
if (imageUrl.startsWith('assets/')) {
return AssetImage(imageUrl);
}
return null;
},
),
// imageEmbedConfig: QuillEditorImageEmbedConfig(
// imageProviderBuilder: (context, imageUrl) {
// if (imageUrl.startsWith('data:image')) {
// return MemoryImage(
// base64Decode(imageUrl.split(',').last),
// );
// }
// return null;
// },
// ),
videoEmbedConfig: QuillEditorVideoEmbedConfig(
customVideoBuilder: (videoUrl, readOnly) {
// To load YouTube videos https://github.com/singerdmx/flutter-quill/releases/tag/v10.8.0
@ -820,6 +926,58 @@ class TemplateForexState extends State<TemplateForex> {
),
),
),
SizedBox(height: 4),
Container(
child: Row(
// mainAxisAlignment: MainAxisAlignment.end,
children: [
Text(
"Upload Signature",
style: GoogleFonts.poppins(fontSize: 11.5),
),
SizedBox(width: 5),
GestureDetector(
onTap: _pickImage,
child:
_imageBytes != null
? ClipOval(
child: Image.memory(
_imageBytes!,
// width: 50,
// height: 50,
width: 50, // Use responsive width
height: 50,
fit: BoxFit.cover,
),
)
: selectedOrglogo != null
? ClipRect(
child: Image.network(
selectedOrglogo!,
width: 50, // Use responsive width
height: 50,
// width: 250,
// height: 55,
fit: BoxFit.contain,
errorBuilder: (context, error, stackTrace) {
return const CircleAvatar(
radius: 20,
backgroundColor: Colors.redAccent,
child: Icon(Icons.error, size: 10),
);
},
),
)
: const CircleAvatar(
radius: 20,
backgroundColor: Colors.amber,
child: Icon(Icons.add_a_photo, size: 10),
),
),
],
),
),
],
);
}

View File

@ -178,6 +178,7 @@ class TemplatesListState extends State<TemplatesList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -210,6 +211,7 @@ class TemplatesListState extends State<TemplatesList> {
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456';
// If updating, spoof the method Laravel-style
@ -447,24 +449,22 @@ class TemplatesListState extends State<TemplatesList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Templates',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Templates'),
],
),
),
],
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.13),
// SizedBox(width: MediaQuery.of(context).size.width * 0.23),
SizedBox(width: MediaQuery.of(context).size.width * 0.13),
// SizedBox(width: MediaQuery.of(context).size.width * 0.23),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -597,7 +597,7 @@ class TemplatesListState extends State<TemplatesList> {
builder: (context, snapshot) {
final adjHgt = MediaQuery.of(context).size.height;
if (futureTemplates == null) {
return CircularProgressIndicator();
return const Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());

View File

@ -50,6 +50,7 @@ class _MailSettingState extends State<MailSetting> {
super.dispose();
}
Map<String, dynamic> getMailData() => mailData;
Map<String, dynamic> get mailData {
@ -169,6 +170,7 @@ class _MailSettingState extends State<MailSetting> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(mailData), // Convert map to JSON
);
@ -353,7 +355,7 @@ class _MailSettingState extends State<MailSetting> {
],
],
),
if(isDesktop) SizedBox(width:20),
if (isDesktop) SizedBox(width: 20),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // This works
crossAxisAlignment: CrossAxisAlignment.start,
@ -447,9 +449,9 @@ class _MailSettingState extends State<MailSetting> {
isFocused: focusStates["senderEmailFocused"] ?? false,
isDesktop: widget.isDesktop,
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.23
: MediaQuery.of(context).size.width * 0.85,
widget.isDesktop
? MediaQuery.of(context).size.width * 0.23
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
height: 40,
child: TextField(
@ -465,8 +467,7 @@ class _MailSettingState extends State<MailSetting> {
} else if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(value)) {
errorMessages["sender_email"] =
"Invalid email format";
errorMessages["sender_email"] = "Invalid email format";
}
},
style: GoogleFonts.poppins(fontSize: 12),
@ -492,7 +493,7 @@ class _MailSettingState extends State<MailSetting> {
],
],
),
if(isDesktop) SizedBox(width:20),
if (isDesktop) SizedBox(width: 20),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // This works
crossAxisAlignment: CrossAxisAlignment.start,
@ -545,7 +546,7 @@ class _MailSettingState extends State<MailSetting> {
],
],
),
if(isDesktop) SizedBox(width:20),
if (isDesktop) SizedBox(width: 20),
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, // This works
crossAxisAlignment: CrossAxisAlignment.start,

View File

@ -333,6 +333,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456';
// If updating, spoof the method Laravel-style
if (isUpdating) {

View File

@ -130,6 +130,7 @@ class ForexDataState extends State<ForexData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -247,7 +248,7 @@ class ForexDataState extends State<ForexData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postForexData();
}else{
} else {
isDisable = false;
}
});
@ -256,7 +257,6 @@ class ForexDataState extends State<ForexData> {
print("ForexDAta - $forexData1");
}
Future<void> postForexData({int isActive = 1}) async {
// final remarksData = getData();
@ -296,6 +296,7 @@ class ForexDataState extends State<ForexData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(forexData);
@ -375,7 +376,7 @@ class ForexDataState extends State<ForexData> {
countryMap = {
for (var country in countryList)
(country['country_code'] ?? ''):
'${country['country_name'] ?? ''} (${country['country_code'] ?? ''})',
'${country['country_name'] ?? ''} (${country['country_code'] ?? ''})',
};
// Extract only country codes for processing
@ -435,105 +436,130 @@ class ForexDataState extends State<ForexData> {
isDesktop: widget.isDesktop,
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(backgroundColor: Colors.white),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder:
(context, item, isSelected) {
print("contryItem - $item");
final match = RegExp(r'^(.*)\s\((.*)\)$',).firstMatch(item);
final countryName = match?.group(1) ?? '';
final countryCode = match?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.02,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
countryName,
style: GoogleFonts.poppins(fontSize: 11.5),
),
Text(
countryCode,
style: GoogleFonts.poppins(fontSize: 11.5,color:Colors.grey),
),
],
width: double.infinity,
child: Focus(
focusNode: focusNodes["country_codeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["country_codeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["country_codeFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedCountry],
popupProps: PopupProps.menu(
showSearchBox: true, // Enables search functionality
menuProps: const MenuProps(
backgroundColor: Colors.white,
),
constraints: BoxConstraints(maxHeight: 200),
itemBuilder: (context, item, isSelected) {
print("contryItem - $item");
final match = RegExp(
r'^(.*)\s\((.*)\)$',
).firstMatch(item);
final countryName = match?.group(1) ?? '';
final countryCode = match?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 0.02,
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
countryName,
style: GoogleFonts.poppins(
fontSize: 11.5,
),
),
),
);},
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(horizontal: 3,vertical: 3),
Text(
countryCode,
style: GoogleFonts.poppins(
fontSize: 11.5,
color: Colors.grey,
),
),
],
),
),
),
items: countryMap.values.toList(),
filterFn: (item, filter) {
final lowerFilter = filter.toLowerCase();
final match = RegExp(r'^(.*)\s\((.*)\)$').firstMatch(item);
final countryName = match?.group(1)?.toLowerCase() ?? '';
final countryCode = match?.group(2)?.toLowerCase() ?? '';
// Prioritize country code match, fallback to country name
return countryCode.contains(lowerFilter) || countryName.contains(lowerFilter);
},
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ?? false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
vertical: 8.0,),
);
},
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
hintStyle: GoogleFonts.poppins(fontSize: 11),
contentPadding: EdgeInsets.symmetric(
horizontal: 3,
vertical: 3,
),
),
dropdownBuilder:
(context, selectedItem) => Align(
),
),
items: countryMap.values.toList(),
filterFn: (item, filter) {
final lowerFilter = filter.toLowerCase();
final match = RegExp(
r'^(.*)\s\((.*)\)$',
).firstMatch(item);
final countryName =
match?.group(1)?.toLowerCase() ?? '';
final countryCode =
match?.group(2)?.toLowerCase() ?? '';
// Prioritize country code match, fallback to country name
return countryCode.contains(lowerFilter) ||
countryName.contains(lowerFilter);
},
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// width: 0.5,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(focusStates["country_codeFocused"] ??
false)
? widget.layoutColor!
: Colors.white,
// : const Color(0xFFD6D5E6),
// width: 0.5,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: widget.layoutColor!,
width: 1,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10.0,
vertical: 8.0,
),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
// Center-align selected item
alignment: Alignment.centerLeft,
child: Text(
@ -541,100 +567,105 @@ class ForexDataState extends State<ForexData> {
style: GoogleFonts.poppins(fontSize: 11),
),
),
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
final setMatch = RegExp(r'^(.*)\s\((.*)\)$').firstMatch(newValue!);
final setCountryName = setMatch?.group(1)?.toLowerCase() ?? '';
selectedCountryName = setCountryName;
setCurrencyFromSelectedCountry(selectedCountry!);
});
},
),
// child: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// popupProps: PopupProps.menu(
// showSearchBox: true, // Enables search functionality
// menuProps: const MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 250),
// itemBuilder:
// (context, item, isSelected) => Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 10.0,
// vertical: 8.0,
// ),
// child: Text(
// item,
// style: GoogleFonts.poppins(fontSize: 11.5),
// ),
// ),
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// hintStyle: GoogleFonts.poppins(fontSize: 11),
// contentPadding: EdgeInsets.symmetric(horizontal: 4),
// ),
// ),
// ),
// items: countryMap.values.toList(),
// dropdownDecoratorProps: DropDownDecoratorProps(
// dropdownSearchDecoration: InputDecoration(
// // border: InputBorder.none,
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide: BorderSide(
// color:
// (focusStates["country_codeFocused"] ?? false)
// ? widget.layoutColor!
// : Colors.white,
// // width: 0.5,
// ),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(
// color:
// (focusStates["country_codeFocused"] ?? false)
// ? widget.layoutColor!
// : Colors.white,
// // : const Color(0xFFD6D5E6),
// // width: 0.5,
// // const Color(0xFFD6D5E6),
// ),
// ),
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: widget.layoutColor!, width: 1),
// ),
// contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
// vertical: 8.0,),
// ),
// ),
// dropdownBuilder:
// (context, selectedItem) => Align(
// // Center-align selected item
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select ",
// style: GoogleFonts.poppins(fontSize: 11),
// ),
// ),
// onChanged: (String? newValue) {
// setState(() {
// // Find the country_code based on selected country_name
// selectedCountry =
// countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
// selectedCountryName = newValue;
// setCurrencyFromSelectedCountry(selectedCountry!);
// });
// },
// ),
)
)
onChanged: (String? newValue) {
setState(() {
// Find the country_code based on selected country_name
selectedCountry =
countryMap.entries
.firstWhere(
(entry) => entry.value == newValue,
)
.key;
final setMatch = RegExp(
r'^(.*)\s\((.*)\)$',
).firstMatch(newValue!);
final setCountryName =
setMatch?.group(1)?.toLowerCase() ?? '';
selectedCountryName = setCountryName;
setCurrencyFromSelectedCountry(selectedCountry!);
});
},
),
// child: DropdownSearch<String>(
// selectedItem: countryMap[selectedCountry],
// popupProps: PopupProps.menu(
// showSearchBox: true, // Enables search functionality
// menuProps: const MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 250),
// itemBuilder:
// (context, item, isSelected) => Padding(
// padding: const EdgeInsets.symmetric(
// horizontal: 10.0,
// vertical: 8.0,
// ),
// child: Text(
// item,
// style: GoogleFonts.poppins(fontSize: 11.5),
// ),
// ),
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// hintStyle: GoogleFonts.poppins(fontSize: 11),
// contentPadding: EdgeInsets.symmetric(horizontal: 4),
// ),
// ),
// ),
// items: countryMap.values.toList(),
// dropdownDecoratorProps: DropDownDecoratorProps(
// dropdownSearchDecoration: InputDecoration(
// // border: InputBorder.none,
// border: OutlineInputBorder(
// borderRadius: BorderRadius.circular(8),
// borderSide: BorderSide(
// color:
// (focusStates["country_codeFocused"] ?? false)
// ? widget.layoutColor!
// : Colors.white,
// // width: 0.5,
// ),
// ),
// enabledBorder: OutlineInputBorder(
// borderSide: BorderSide(
// color:
// (focusStates["country_codeFocused"] ?? false)
// ? widget.layoutColor!
// : Colors.white,
// // : const Color(0xFFD6D5E6),
// // width: 0.5,
// // const Color(0xFFD6D5E6),
// ),
// ),
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: widget.layoutColor!, width: 1),
// ),
// contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
// vertical: 8.0,),
// ),
// ),
// dropdownBuilder:
// (context, selectedItem) => Align(
// // Center-align selected item
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select ",
// style: GoogleFonts.poppins(fontSize: 11),
// ),
// ),
// onChanged: (String? newValue) {
// setState(() {
// // Find the country_code based on selected country_name
// selectedCountry =
// countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
// selectedCountryName = newValue;
// setCurrencyFromSelectedCountry(selectedCountry!);
// });
// },
// ),
),
),
),
),
if (errorMessages["country_code"] != null) ...[
@ -913,22 +944,23 @@ class ForexDataState extends State<ForexData> {
// // You can get text from commentController.text
// // Navigator.of(context).pop(); // Close the modal
// },
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -168,6 +168,7 @@ class ForexDataListState extends State<ForexDataList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -193,6 +194,7 @@ class ForexDataListState extends State<ForexDataList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -368,9 +370,10 @@ class ForexDataListState extends State<ForexDataList> {
return input
.toLowerCase()
.split(' ')
.map((word) => word.isNotEmpty
? word[0].toUpperCase() + word.substring(1)
: '')
.map(
(word) =>
word.isNotEmpty ? word[0].toUpperCase() + word.substring(1) : '',
)
.join(' ');
}
@ -464,15 +467,13 @@ class ForexDataListState extends State<ForexDataList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Perdiem Amount Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Perdiem Amount Details'),
],
),
),
@ -488,8 +489,8 @@ class ForexDataListState extends State<ForexDataList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.1),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -634,7 +635,7 @@ class ForexDataListState extends State<ForexDataList> {
future: futureForex,
builder: (context, snapshot) {
if (futureForex == null) {
return const CircularProgressIndicator();
return const Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
@ -790,7 +791,8 @@ class ForexDataListState extends State<ForexDataList> {
),
DataCell(
Text(
toTitleCase(forex['country_name']) ?? '',
toTitleCase(forex['country_name']) ??
'',
style: TextStyle(
fontSize: 13,
fontFamily: "Inter",
@ -921,7 +923,8 @@ class ForexDataListState extends State<ForexDataList> {
MainAxisAlignment.spaceBetween,
children: [
Text(
toTitleCase(forex['country_name']) ?? 'N/A',
toTitleCase(forex['country_name']) ??
'N/A',
style: GoogleFonts.poppins(
fontSize: 10,
color: Colors.black87,

View File

@ -824,6 +824,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -964,6 +965,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1010,6 +1012,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1081,6 +1084,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1139,6 +1143,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1289,6 +1294,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(data),
);
@ -1500,6 +1506,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);

View File

@ -300,6 +300,7 @@ class _ListPlansState extends State<ListPlans> {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -332,6 +333,7 @@ class _ListPlansState extends State<ListPlans> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(planData), // Convert map to JSON
);
@ -446,6 +448,7 @@ class _ListPlansState extends State<ListPlans> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);

View File

@ -595,6 +595,7 @@ class _PolicyState extends State<Policy> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(policyData), // Convert map to JSON
);

View File

@ -139,10 +139,12 @@ class _PolicyListState extends State<PolicyList> {
filteredPolicy =
allPolicy.where((object) {
final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['policy_id']?.toLowerCase().contains(lowerQuery) ?? false) ||
object['is_active'] == "1" ? "active" : "inactive";
return (object['policy_id']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['name']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['policy_type']?.toLowerCase().contains(lowerQuery) ?? false) ||
(object['policy_type']?.toLowerCase().contains(lowerQuery) ??
false) ||
(isActiveStatus.contains(lowerQuery));
}).toList();
currentPage = 0;
@ -185,6 +187,7 @@ class _PolicyListState extends State<PolicyList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(policyData), // Convert map to JSON
);
@ -339,15 +342,13 @@ class _PolicyListState extends State<PolicyList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Policy',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Policy'),
],
),
),
@ -363,8 +364,8 @@ class _PolicyListState extends State<PolicyList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.145),
// SizedBox(width: MediaQuery.of(context).size.width * 0.28),
// SizedBox(width: MediaQuery.of(context).size.width * 0.28),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -1029,32 +1030,34 @@ class _PolicyListState extends State<PolicyList> {
children: [
Expanded(
child:
isDesktop
? (searchController.text.isNotEmpty && filteredPolicy.isEmpty
? Center(
child: Text( "No Matches Found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
)
)
: (searchController.text.isNotEmpty && filteredPolicy.isEmpty
? Center(
child: Text(
isDesktop
? (searchController.text.isNotEmpty &&
filteredPolicy.isEmpty
? Center(
child: Text(
"No Matches Found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: buildMobileCardView(paginatedUser)
),
),
)
: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: table,
))
: (searchController.text.isNotEmpty &&
filteredPolicy.isEmpty
? Center(
child: Text(
"No Matches Found",
style: GoogleFonts.poppins(
fontSize: 14,
color: Colors.grey,
),
),
)
: buildMobileCardView(paginatedUser)),
),
PaginationControls(
currentPage: currentPage,

View File

@ -105,6 +105,7 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -168,7 +169,7 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postPurposeOfTravelData();
}else{
} else {
isDisable = false;
}
});
@ -212,6 +213,7 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(purposeOfTravelData);
@ -429,22 +431,23 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
// ),
SizedBox(
child: ElevatedButton(
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -132,7 +132,8 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
}
Future<List<dynamic>> fetchGetPurposeOfTravel() async {
final String apiUrlData = '$apiUrl/api/getPurposeOfTravelList?for=table_view';
final String apiUrlData =
'$apiUrl/api/getPurposeOfTravelList?for=table_view';
final String? token = await getToken();
@ -148,6 +149,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
print("called api : $apiUrlData");
@ -180,11 +182,10 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
allPurposeOfTravel.where((object) {
final isActiveStatus =
object['is_active'] == "1" ? "active" : "inactive";
return (object['id']?.toLowerCase().contains(
lowerQuery,
) ??
return (object['id']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['dropdown_value']?.toLowerCase().contains(lowerQuery) ??
false) ||
(object['dropdown_value']?.toLowerCase().contains(lowerQuery) ?? false) ||
// (object['description']?.toLowerCase().contains(lowerQuery) ??
// false) ||
(isActiveStatus.contains(lowerQuery));
@ -284,15 +285,13 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Purpose Of Travel Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Purpose Of Travel Details'),
],
),
),
@ -308,8 +307,8 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.08),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -583,10 +582,10 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
rows:
paginatedPurposeOfTravel.map((tableObject) {
String purposeOfTravelId =
tableObject['id']
.toString(); // Get user ID
tableObject['id'].toString(); // Get user ID
bool isSelected =
selectedPurposeOfTravelId == purposeOfTravelId;
selectedPurposeOfTravelId ==
purposeOfTravelId;
return DataRow(
cells: [
@ -635,7 +634,8 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
// ),
GestureDetector(
child: Tooltip(
message: 'Edit Purpose Of Travel Details',
message:
'Edit Purpose Of Travel Details',
child: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
@ -646,10 +646,10 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
// final userId = getUserId(user['user_id']);
// final usersData = await getUserDetails(userId);
//
final purposeOfTravelId = int.tryParse(
tableObject['id']
.toString(),
);
final purposeOfTravelId =
int.tryParse(
tableObject['id'].toString(),
);
if (purposeOfTravelId != null) {
print(
@ -664,7 +664,9 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
showDialog(
context: context,
builder:
(context) => PurposeOfTravelData(
(
context,
) => PurposeOfTravelData(
isDesktop: isDesktop,
purposeOfTravelId:
purposeOfTravelId, // Pass the ID
@ -728,7 +730,8 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
GestureDetector(
child: Tooltip(
message: 'Edit Purpose Of Travel Details',
message:
'Edit Purpose Of Travel Details',
child: Image.asset(
'assets/images/IconsImg/edit.png',
width: 20,
@ -740,8 +743,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
// final usersData = await getUserDetails(userId);
//
final purposeOfTravelId = int.tryParse(
cardObject['id']
.toString(),
cardObject['id'].toString(),
);
if (purposeOfTravelId != null) {
@ -757,7 +759,9 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
showDialog(
context: context,
builder:
(context) => PurposeOfTravelData(
(
context,
) => PurposeOfTravelData(
isDesktop: isDesktop,
purposeOfTravelId:
purposeOfTravelId, // Pass the ID

View File

@ -106,6 +106,7 @@ class TravellerDataState extends State<TravellerData> {
}
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
@ -188,7 +189,7 @@ class TravellerDataState extends State<TravellerData> {
// This triggers UI rebuild with error messages
if (validateData()) {
postTravellerData();
}else{
} else {
isDisable = false;
}
});
@ -235,6 +236,7 @@ class TravellerDataState extends State<TravellerData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode(travellerData);
@ -550,22 +552,23 @@ class TravellerDataState extends State<TravellerData> {
// ),
SizedBox(
child: ElevatedButton(
onPressed: isDisable
? null
: () async {
setState(() {
isDisable = true;
});
onPressed:
isDisable
? null
: () async {
setState(() {
isDisable = true;
});
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
await handleSubmit();
// You can get text from commentController.text
// Navigator.of(context).pop(); // Close the modal
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
// Optional: re-enable only on error
// setState(() {
// isDisable = false;
// });
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColor,
shape: RoundedRectangleBorder(

View File

@ -151,6 +151,7 @@ class TravellerListState extends State<TravellerList> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
print("called api : $apiUrlData");
@ -294,15 +295,13 @@ class TravellerListState extends State<TravellerList> {
isDesktop: isDesktop,
breadcrumbItems: [
BreadcrumbItem(
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
}
),
BreadcrumbItem(
title: 'Traveller Details',
title: 'Org Settings',
tooltip: 'Go To Organization Settings',
onTap: (context) {
context.go("/OrganizationSettings");
},
),
BreadcrumbItem(title: 'Traveller Details'),
],
),
),
@ -318,8 +317,8 @@ class TravellerListState extends State<TravellerList> {
),
if (isDesktop)
SizedBox(width: MediaQuery.of(context).size.width * 0.10),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
// SizedBox(width: MediaQuery.of(context).size.width * 0.16),
if (isDesktop)
Container(
width: MediaQuery.of(context).size.width * 0.2,
@ -466,7 +465,7 @@ class TravellerListState extends State<TravellerList> {
builder: (context, snapshot) {
final adjHgt = MediaQuery.of(context).size.height;
if (futureTraveller == null) {
return CircularProgressIndicator();
return const Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState == ConnectionState.waiting) {

View File

@ -990,6 +990,7 @@ class _CreateTravelAgentFormDetialsState
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456';
// If updating, spoof the method Laravel-style
if (isUpdating) {

View File

@ -12,43 +12,35 @@ import '../../../services/apiService.dart';
import '../../../utils/auth_utils.dart';
import '../../../widgets/custom_user_form.dart';
class ChangePasswordDialogData extends StatefulWidget {
final dynamic isDesktop;
final dynamic layoutColor;
final dynamic updaterUserId;
final dynamic updaterEmail;
const ChangePasswordDialogData({
super.key,
this.isDesktop,
this.layoutColor,
this.updaterUserId,
this.updaterEmail
super.key,
this.isDesktop,
this.layoutColor,
this.updaterUserId,
this.updaterEmail,
});
@override
ChangePasswordDialogDataState createState() => ChangePasswordDialogDataState();
}
@override
ChangePasswordDialogDataState createState() =>
ChangePasswordDialogDataState();
}
class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
final ApiService apiService = ApiService();
final Map<String, TextEditingController> controllers = {};
Map<String, String> errorMessages = {};
String? loggeduserId;
String? updaterUserIdForAPI;
List<String> dataHeader = [
"email",
"changePassword",
"confirmPassword"
];
List<String> dataHeader = ["email", "changePassword", "confirmPassword"];
// @override
// void initState() {
@ -75,15 +67,13 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
}
setState(() {
controllers['email']?.text = widget.updaterEmail ;
controllers['email']?.text = widget.updaterEmail;
controllers['changePassword']?.text = '';
controllers['confirmPassword']?.text = '';
updaterUserIdForAPI = widget.updaterUserId;
});
}
void _clearError() {
setState(() {
errorMessages.clear();
@ -98,40 +88,37 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
super.dispose();
}
bool validateData() {
errorMessages.clear();
final String? email = controllers["email"]?.text;
final String? changePassword = controllers["changePassword"]?.text;
final String? confirmPassword = controllers["confirmPassword"]?.text;
final String? email = controllers["email"]?.text;
final String? changePassword = controllers["changePassword"]?.text;
final String? confirmPassword = controllers["confirmPassword"]?.text;
// Required fields check
if (email == null || email.trim().isEmpty) {
errorMessages["email"] = "Required";
}
if (changePassword == null || changePassword.trim().isEmpty) {
errorMessages["changePassword"] = "Required";
}
if (confirmPassword == null || confirmPassword.trim().isEmpty) {
errorMessages["confirmPassword"] = "Required";
}
// Password match check
if ((changePassword?.isNotEmpty ?? false) &&
(confirmPassword?.isNotEmpty ?? false) &&
changePassword != confirmPassword) {
errorMessages["changePassword"] = "Passwords do not match";
errorMessages["confirmPassword"] = "Passwords do not match";
}
// setState(() {}); // Update UI with any error messages
return errorMessages.isEmpty;
// Required fields check
if (email == null || email.trim().isEmpty) {
errorMessages["email"] = "Required";
}
if (changePassword == null || changePassword.trim().isEmpty) {
errorMessages["changePassword"] = "Required";
}
if (confirmPassword == null || confirmPassword.trim().isEmpty) {
errorMessages["confirmPassword"] = "Required";
}
// Password match check
if ((changePassword?.isNotEmpty ?? false) &&
(confirmPassword?.isNotEmpty ?? false) &&
changePassword != confirmPassword) {
errorMessages["changePassword"] = "Passwords do not match";
errorMessages["confirmPassword"] = "Passwords do not match";
}
// setState(() {}); // Update UI with any error messages
return errorMessages.isEmpty;
}
Future<void> handleSubmit() async {
loggeduserId = await getUserId();
@ -142,7 +129,6 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
postData();
}
});
}
Future<void> postData() async {
@ -152,12 +138,14 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
final password = controllers["changePassword"]?.text ?? '';
final confirmPassword = controllers["confirmPassword"]?.text ?? '';
final String apiUrldata = '$apiUrl/api/user/user-password/$updaterUserIdForAPI';
final String apiUrldata =
'$apiUrl/api/user/user-password/$updaterUserIdForAPI';
final token = await getToken();
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
try {
@ -165,6 +153,7 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
final headers = {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
};
final body = jsonEncode({
"password": password,
@ -200,7 +189,6 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: Colors.white,
contentPadding: const EdgeInsets.fromLTRB(34, 30, 34, 30),
@ -220,10 +208,7 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
],
),
const SizedBox(height: 2),
Divider(
thickness: 0.2,
color: Colors.blueGrey.shade100,
),
Divider(thickness: 0.2, color: Colors.blueGrey.shade100),
const SizedBox(height: 5),
Column(
@ -232,9 +217,10 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
Text(
"Email",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
@ -245,18 +231,19 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
// ? MediaQuery.of(context).size.width * 0.330
// : MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["email"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Email",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
height: 40,
child: TextField(
controller: controllers["email"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Email",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["email"] != null) ...[
SizedBox(height: 5), // Space before error message
@ -267,18 +254,17 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
],
],
),
SizedBox(
height: 10,
),
SizedBox(height: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Change Password",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
@ -286,18 +272,19 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["changePassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Change Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
height: 40,
child: TextField(
controller: controllers["changePassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Change Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["changePassword"] != null) ...[
SizedBox(height: 5), // Space before error message
@ -308,18 +295,17 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
],
],
),
SizedBox(
height: 15,
),
SizedBox(height: 15),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Confirm Password",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldUserWrapper(
@ -327,18 +313,19 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
isDesktop: widget.isDesktop,
color: Colors.transparent,
child: SizedBox(
height: 40,
child: TextField(
controller: controllers["confirmPassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Confirm Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
)),
height: 40,
child: TextField(
controller: controllers["confirmPassword"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Confirm Password",
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["confirmPassword"] != null) ...[
SizedBox(height: 5), // Space before error message
@ -349,10 +336,8 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
],
],
),
SizedBox(
height: 15,
),
SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@ -367,17 +352,20 @@ class ChangePasswordDialogDataState extends State<ChangePasswordDialogData> {
borderRadius: BorderRadius.circular(8),
),
),
child: Text('Save',
style: GoogleFonts.poppins(
fontSize: 11, color: Colors.white)),
child: Text(
'Save',
style: GoogleFonts.poppins(
fontSize: 11,
color: Colors.white,
),
),
),
),
],
)
),
// : SizedBox.shrink(),
],
),
);
}
}

View File

@ -1259,6 +1259,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456';
// If updating, spoof the method Laravel-style
if (isUpdating) {

View File

@ -161,6 +161,7 @@ class _UserListScreenState extends State<UserListScreen> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -186,6 +187,7 @@ class _UserListScreenState extends State<UserListScreen> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -587,6 +589,9 @@ class _UserListScreenState extends State<UserListScreen> {
// Attach the file to the request
// Set authorization token in headers
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] =
'ts-traveltool-2025-signature-123456';
// request.files.add(http.MultipartFile.fromBytes('file', fileBytes, filename: fileName));
request.files.add(
@ -718,6 +723,7 @@ class _UserListScreenState extends State<UserListScreen> {
// Use MultipartRequest (POST only)
final request = http.MultipartRequest('POST', uri);
request.headers['Authorization'] = 'Bearer $token';
request.headers['app-signature'] = 'ts-traveltool-2025-signature-123456';
// If updating, spoof the method Laravel-style

View File

@ -34,8 +34,8 @@ class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
SemanticsBinding.instance
.ensureSemantics(); // -only for testing uncomment, Otherwise Email Template wont allow to type
// SemanticsBinding.instance
// .ensureSemantics(); // -only for testing uncomment, Otherwise Email Template wont allow to type
if (kIsWeb) {
final uri = Uri.parse(html.window.location.href);
print("URI - $uri");
@ -87,7 +87,7 @@ class _MyAppState extends State<MyApp> {
print("handleTokenUsingMS- $authCode");
try {
// final url = 'http://localhost:43627/tstat/auth/verifyMSAuthUser?code=$authCode';
final url = '$apiUrl/auth/verifyMSAuthUser?code=$authCode';
final url = '$apiUrl/api/auth/verifyMSAuthUser?code=$authCode';
print("Microsoft BE URL - $url");
final response = await http.get(
Uri.parse(url),

View File

@ -44,6 +44,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -80,6 +81,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -116,6 +118,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -156,6 +159,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -185,6 +189,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -240,6 +245,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -291,6 +297,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -330,6 +337,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -367,6 +375,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -404,6 +413,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -441,6 +451,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -476,6 +487,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -518,6 +530,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -559,6 +572,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -583,6 +597,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token', // Add token here
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -664,6 +679,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -710,6 +726,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -747,6 +764,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -786,6 +804,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -850,6 +869,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -915,6 +935,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
// 'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -965,6 +986,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1010,6 +1032,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1054,6 +1077,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1100,6 +1124,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1146,6 +1171,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1200,6 +1226,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1346,6 +1373,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1392,6 +1420,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1438,6 +1467,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1480,6 +1510,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -1534,6 +1565,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
if (response.statusCode == 200) {
@ -1573,6 +1605,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: body,
);
@ -1616,6 +1649,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: body,
);
@ -1673,6 +1707,7 @@ class ApiService {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);

View File

@ -122,6 +122,7 @@ class CommentModalState extends State<CommentModal> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
);
@ -143,7 +144,8 @@ class CommentModalState extends State<CommentModal> {
print("Res1 - $data");
if (!data.containsKey('data') || data['data'] is! Map) {
throw Exception(
"Invalid response format: 'data' field is missing or not a Map");
"Invalid response format: 'data' field is missing or not a Map",
);
}
return data['data'];
} else {
@ -186,6 +188,7 @@ class CommentModalState extends State<CommentModal> {
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
'app-signature': 'ts-traveltool-2025-signature-123456',
},
body: jsonEncode(remarksData), // Convert map to JSON
);
@ -255,7 +258,8 @@ class CommentModalState extends State<CommentModal> {
icon: const Icon(Icons.delete, size: 20),
onPressed: () async {
await postRemarksData(
isActive: 0); // Marks the remark as deleted
isActive: 0,
); // Marks the remark as deleted
Navigator.of(context).pop();
},
),
@ -280,27 +284,30 @@ class CommentModalState extends State<CommentModal> {
const SizedBox(height: 20),
// Row 3: OK button
editRemarks
? SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
postRemarksData();
// You can get text from commentController.text
Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColorForUser,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
width: double.infinity,
child: ElevatedButton(
onPressed: () {
postRemarksData();
// You can get text from commentController.text
Navigator.of(context).pop(); // Close the modal
},
style: ElevatedButton.styleFrom(
backgroundColor: widget.layoutColorForUser,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Text('OK',
style: GoogleFonts.poppins(
fontSize: 13, color: Colors.white)),
),
)
child: Text(
'OK',
style: GoogleFonts.poppins(
fontSize: 13,
color: Colors.white,
),
),
),
)
: SizedBox.shrink(),
],
),

View File

@ -609,10 +609,10 @@ packages:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
version: "1.0.6"
nested:
dependency: transitive
description: