july 1 to july 15
This commit is contained in:
parent
3be15a71ef
commit
35229fa755
BIN
assets/images/IconsImg/Domestic.png
Normal file
BIN
assets/images/IconsImg/Domestic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
BIN
assets/images/IconsImg/Domestic_new.png
Normal file
BIN
assets/images/IconsImg/Domestic_new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/images/IconsImg/Domestic_old.png
Normal file
BIN
assets/images/IconsImg/Domestic_old.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
assets/images/IconsImg/International.png
Normal file
BIN
assets/images/IconsImg/International.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
BIN
assets/images/IconsImg/International_new.png
Normal file
BIN
assets/images/IconsImg/International_new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/images/IconsImg/International_old.png
Normal file
BIN
assets/images/IconsImg/International_old.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@ -723,12 +723,12 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
||||
// ),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
" No Trips Found",
|
||||
" No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1187,11 +1187,156 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
||||
),
|
||||
Column(
|
||||
children: [
|
||||
PlanPopupMenu(
|
||||
plan: plan,
|
||||
deletePlan: deletePlan,
|
||||
apiService: apiService,
|
||||
layoutColor: layoutColor,
|
||||
// PlanPopupMenu(
|
||||
// plan: plan,
|
||||
// deletePlan: deletePlan,
|
||||
// apiService: apiService,
|
||||
// layoutColor: layoutColor,
|
||||
// ),
|
||||
PopupMenuButton<int>(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.zero,
|
||||
offset: Offset(0, 30),
|
||||
icon: Icon(
|
||||
Icons.more_vert,
|
||||
color: Color(0xFF475569),
|
||||
size: 14,
|
||||
),
|
||||
itemBuilder:
|
||||
(context) => [
|
||||
CustomPopupMenuEntry(
|
||||
child: Container(
|
||||
padding:
|
||||
EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 8,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons
|
||||
.remove_red_eye,
|
||||
color: Color(
|
||||
0xFF475569,
|
||||
),
|
||||
size: 18,
|
||||
),
|
||||
tooltip:
|
||||
'View The Trip Details',
|
||||
onPressed: () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
); // Close popup manually
|
||||
ApiService.viewPlan(
|
||||
context,
|
||||
plan.planId,
|
||||
isViewMode:
|
||||
true,
|
||||
);
|
||||
},
|
||||
),
|
||||
if (plan.statusValue ==
|
||||
"Pending Approval" ||
|
||||
plan.statusValue ==
|
||||
"Partially Approved")
|
||||
IconButton(
|
||||
icon: Image.asset(
|
||||
'assets/images/IconsImg/edit.png',
|
||||
width: 20,
|
||||
height: 15,
|
||||
),
|
||||
tooltip:
|
||||
'Edit The Trip Details',
|
||||
onPressed: () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
);
|
||||
ApiService.viewPlan(
|
||||
context,
|
||||
plan.planId,
|
||||
isViewMode:
|
||||
false,
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons
|
||||
.cancel_rounded,
|
||||
size: 18,
|
||||
),
|
||||
tooltip:
|
||||
'Cancellation The Trip Details',
|
||||
onPressed: () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
);
|
||||
deletePlan(
|
||||
plan.planId,
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.download,
|
||||
color: Color(
|
||||
0xFF114D8B,
|
||||
),
|
||||
size: 18,
|
||||
),
|
||||
tooltip:
|
||||
'Download The PDF',
|
||||
onPressed: () {
|
||||
Navigator.pop(
|
||||
context,
|
||||
);
|
||||
apiService
|
||||
.getPdfDownload(
|
||||
plan.planId,
|
||||
);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.comment,
|
||||
color: Color(
|
||||
0xFF475569,
|
||||
),
|
||||
size: 11,
|
||||
),
|
||||
tooltip:
|
||||
'Trip Comments',
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context:
|
||||
context,
|
||||
builder:
|
||||
(
|
||||
context,
|
||||
) => CommentModalList(
|
||||
// planId: plan.planId,
|
||||
planId:
|
||||
plan.planId
|
||||
.toString(),
|
||||
layoutColorForUser:
|
||||
layoutColor!,
|
||||
role:
|
||||
"Admin",
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -1311,7 +1456,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1326,7 +1471,7 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -858,15 +858,12 @@ class _ApprovalListState extends State<ApprovalList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent)),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
" No Trips Pending For Your Approvals",
|
||||
|
||||
Text("No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
// fontFamily: "Inter",
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1688,7 +1685,7 @@ class _ApprovalListState extends State<ApprovalList> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1703,7 +1700,7 @@ class _ApprovalListState extends State<ApprovalList> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -50,6 +50,7 @@ class CostCenterDataState extends State<CostCenterData> {
|
||||
String? userId;
|
||||
int? costcenterDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = ["name", "description"];
|
||||
|
||||
@ -163,9 +164,12 @@ class CostCenterDataState extends State<CostCenterData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postCostCenterData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -221,22 +225,38 @@ class CostCenterDataState extends State<CostCenterData> {
|
||||
print("Update - Response: ${response.body}");
|
||||
_clearError();
|
||||
widget.fetchGetCostCenter();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
break;
|
||||
|
||||
case 201:
|
||||
print("Save - Response: ${response.body}");
|
||||
_clearError();
|
||||
await widget.fetchGetCostCenter();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
print("Failed to submit costcenter. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,11 +426,27 @@ class CostCenterDataState extends State<CostCenterData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
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;
|
||||
// });
|
||||
},
|
||||
// onPressed: () {
|
||||
// handleSubmit();
|
||||
// // You can get text from commentController.text
|
||||
// // Navigator.of(context).pop(); // Close the modal
|
||||
// },
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
|
||||
@ -132,6 +132,7 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGetCostCenter() async {
|
||||
// return [];
|
||||
final String apiUrlData = '$apiUrl/api/getCostCenterMaster?for=table_view';
|
||||
|
||||
final String? token = await getToken();
|
||||
@ -285,7 +286,7 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -455,6 +456,7 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureCostCenter,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureCostCenter == null) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
@ -480,9 +482,9 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No CostCenter Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -491,15 +493,15 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create CostCenter Details",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create CostCenter Details",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -906,7 +908,7 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
filteredCostCenter.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -921,7 +923,7 @@ class CostCenterListState extends State<CostCenterList> {
|
||||
filteredCostCenter.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
423
lib/Screens/dashboard/status_dashboard (copy).dart
Normal file
423
lib/Screens/dashboard/status_dashboard (copy).dart
Normal file
@ -0,0 +1,423 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/routes/custom_router.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../utils/pagination.dart';
|
||||
|
||||
class StatusDashboard extends StatefulWidget {
|
||||
const StatusDashboard({super.key});
|
||||
|
||||
@override
|
||||
StatusDashboardState createState() => StatusDashboardState();
|
||||
}
|
||||
|
||||
class StatusDashboardState extends State<StatusDashboard> {
|
||||
Map<String, dynamic>? apiData;
|
||||
String? organizationId;
|
||||
late bool _dialogShown = false;
|
||||
late StreamSubscription<html.PopStateEvent> _popStateListener;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkAuthAndLoadData();
|
||||
|
||||
checkbackbutton();
|
||||
|
||||
// loadDashboardData();
|
||||
}
|
||||
|
||||
void checkbackbutton() async {
|
||||
// Push a dummy state so back button triggers popstate instead of navigating
|
||||
html.window.history.pushState(null, 'home', html.window.location.href);
|
||||
|
||||
_popStateListener = html.window.onPopState.listen((event) {
|
||||
if (!_dialogShown && mounted) {
|
||||
_showBackConfirmationDialog();
|
||||
}
|
||||
|
||||
// Re-push to prevent leaving
|
||||
html.window.history.pushState(null, 'home', html.window.location.href);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_popStateListener.cancel(); // ✅ Remove the browser popstate listener
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _showBackConfirmationDialog() {
|
||||
if (!mounted) return;
|
||||
|
||||
_dialogShown = true;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => AlertDialog(
|
||||
title: Text("Confirm"),
|
||||
content: Text("Do you want to logout?"),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context); // Close dialog
|
||||
_dialogShown = false;
|
||||
},
|
||||
child: Text("Cancel"),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(context); // Close dialog
|
||||
_dialogShown = false;
|
||||
await _logoutAndRedirect(context);
|
||||
},
|
||||
child: Text("Logout"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _logoutAndRedirect(BuildContext context) async {
|
||||
print("logue 0");
|
||||
|
||||
// Example: clear session or shared preferences
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.clear();
|
||||
|
||||
context.go("/");
|
||||
|
||||
print("logue 1");
|
||||
}
|
||||
|
||||
void _checkAuthAndLoadData() async {
|
||||
final String? token = await getToken(); // Your async function to get token
|
||||
|
||||
if (token == null || token.isEmpty) {
|
||||
// Token doesn't exist → redirect to login
|
||||
context.go(
|
||||
"/",
|
||||
); // or use: router.go("/") if you're using `GoRouter` directly
|
||||
return;
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
checkbackbutton();
|
||||
});
|
||||
// If token exists, load the dashboard data
|
||||
loadDashboardData();
|
||||
}
|
||||
|
||||
Future<void> loadDashboardData() async {
|
||||
try {
|
||||
final data = await fetchStatusDashboard();
|
||||
setState(() {
|
||||
apiData = data;
|
||||
});
|
||||
} catch (e) {
|
||||
print("Error loading dashboard: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
Future<String?> getOrgId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if (userDataString != null) {
|
||||
try {
|
||||
final Map<String, dynamic> userData = jsonDecode(userDataString);
|
||||
return userData["org_id"]?.toString();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchStatusDashboard() async {
|
||||
organizationId = await getOrgId();
|
||||
|
||||
final String apiUrlData =
|
||||
'$apiUrl/api/plans/statusDashboard?org_id=$organizationId';
|
||||
final String? token = await getToken();
|
||||
|
||||
print("Fetch StatusDashboard -- 2KN Here : $token");
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrlData),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
print("called api : $apiUrlData");
|
||||
if (response.statusCode == 200) {
|
||||
final apiData = json.decode(response.body);
|
||||
print("Fetch StatusDashboard -- Reponse Here : $apiData");
|
||||
return apiData; // Returning raw JSON list
|
||||
} else {
|
||||
throw Exception('Failed to load users');
|
||||
}
|
||||
}
|
||||
|
||||
Color getStatusColor(String status) {
|
||||
if (status == "Domestic") return Colors.white;
|
||||
if (status == "International") return Colors.white;
|
||||
if (status == "Partially Approved") return Colors.yellow.shade100;
|
||||
if (status == "Pending Approval") return Colors.greenAccent.shade100;
|
||||
if (status == "Approved") return Colors.green.shade100;
|
||||
if (status == "Completed") return Colors.green.shade500;
|
||||
if (status == "Rejected") return Colors.red.shade100;
|
||||
if (status == "Cancelled") return Colors.red.shade200;
|
||||
return Colors.grey.shade100;
|
||||
}
|
||||
|
||||
IconData getStatusIcon(String title) {
|
||||
switch (title) {
|
||||
case "Domestic":
|
||||
return Icons.directions_bus_filled_outlined;
|
||||
case "International":
|
||||
return Icons.airplanemode_active;
|
||||
case "Partially Approved":
|
||||
return Icons.pending;
|
||||
case "Approved":
|
||||
return Icons.check_circle_outline;
|
||||
case "Completed":
|
||||
return Icons.done_all;
|
||||
case "Rejected":
|
||||
return Icons.cancel_outlined;
|
||||
case "Cancelled":
|
||||
return Icons.cancel_schedule_send_outlined;
|
||||
|
||||
default:
|
||||
return Icons.info_outline;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final typeBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['typeBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
print("apiData - => $apiData");
|
||||
print("typeBasedCount => $typeBasedCount");
|
||||
|
||||
final statusBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusBasedCount']) ?? [],
|
||||
);
|
||||
print("statusBasedCount - => $statusBasedCount");
|
||||
// 👇 Local function to create the card widget
|
||||
Widget buildInfoCard(String title, int count, double width) {
|
||||
print('title $title');
|
||||
final color = getStatusColor(title);
|
||||
final icon = getStatusIcon(title);
|
||||
return Card(
|
||||
elevation: 3,
|
||||
color: color,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: Container(
|
||||
width: width,
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
icon,
|
||||
size: 32,
|
||||
color:
|
||||
(title == 'Domestic' || title == 'International')
|
||||
? Colors.green
|
||||
: Colors.black54,
|
||||
), // 👈 Add Icon here
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize:
|
||||
(title == 'Domestic' || title == 'International')
|
||||
? 16
|
||||
: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
count.toString(),
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
// color: Colors.blue,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleBackButton() {
|
||||
final location = GoRouterState.of(context).uri.toString();
|
||||
|
||||
print("location - $location");
|
||||
if (location.contains('/StatusDashboard')) {
|
||||
// Do nothing or show "Press again to exit" toast
|
||||
print("Blocked back on dashboard");
|
||||
} else {
|
||||
print("dashboard ..");
|
||||
}
|
||||
}
|
||||
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
bool isDesktop =
|
||||
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return PopScope(
|
||||
canPop: false, // Allow back navigation only if not login screen
|
||||
onPopInvokedWithResult: (didPop, result) {
|
||||
if (didPop) return;
|
||||
_handleBackButton(); // Show exit confirmation dialog
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: const Color(0xFFf5f5f5),
|
||||
appBar: CustomAppBar(isDesktop: isDesktop),
|
||||
drawer: CustomDrawer(isDesktop: false),
|
||||
body: Padding(
|
||||
padding:
|
||||
isDesktop
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical: 10, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(0),
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
// Only needed if child layout depends on height
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isDesktop
|
||||
? Colors.white
|
||||
: const Color(0xFFFCFCFC),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(30.0),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children:
|
||||
typeBasedCount.map((item) {
|
||||
double cardWidth =
|
||||
isDesktop
|
||||
? (MediaQuery.of(
|
||||
context,
|
||||
).size.width *
|
||||
0.75 -
|
||||
10) /
|
||||
2 // 80% width padding adjusted
|
||||
: MediaQuery.of(
|
||||
context,
|
||||
).size.width -
|
||||
24; // full width with padding
|
||||
|
||||
return SizedBox(
|
||||
width: cardWidth,
|
||||
child: buildInfoCard(
|
||||
item['value'],
|
||||
item['count'],
|
||||
cardWidth,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children:
|
||||
statusBasedCount.map((item) {
|
||||
double cardWidth =
|
||||
isDesktop
|
||||
? (MediaQuery.of(
|
||||
context,
|
||||
).size.width *
|
||||
0.90 -
|
||||
50) /
|
||||
6 // desktop layout: 6 cards per row
|
||||
: MediaQuery.of(
|
||||
context,
|
||||
).size.width -
|
||||
24; // mobile: full width
|
||||
|
||||
return SizedBox(
|
||||
width: cardWidth,
|
||||
child: buildInfoCard(
|
||||
item['value'],
|
||||
item['count'],
|
||||
cardWidth,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -31,6 +31,34 @@ class StatusDashboardState extends State<StatusDashboard> {
|
||||
String? organizationId;
|
||||
late bool _dialogShown = false;
|
||||
late StreamSubscription<html.PopStateEvent> _popStateListener;
|
||||
String selectedView = 'Today';
|
||||
String AccessKeyFlag = "Both";
|
||||
Map<String, dynamic>? currentData;
|
||||
List typeData = [];
|
||||
List statusData = [];
|
||||
List<int> flightData = [];
|
||||
List<int> accommodationData = [];
|
||||
List<int> forexData = [];
|
||||
|
||||
final categoryIcons = {
|
||||
"flight": Icons.flight,
|
||||
"accommodation": Icons.hotel_outlined,
|
||||
"forex": Icons.attach_money,
|
||||
};
|
||||
|
||||
List<String> statusLabels = [
|
||||
"Pending Approval",
|
||||
"Partially Approved",
|
||||
"Approved",
|
||||
"Rejected",
|
||||
"Cancelled"
|
||||
];
|
||||
|
||||
// Extract counts for "Both" section
|
||||
List<int> getStatusValues(Map<String, dynamic> serviceData) {
|
||||
final both = serviceData['Both'] ?? {};
|
||||
return statusLabels.map((label) => (both[label] ?? 0) as int).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -231,7 +259,82 @@ class StatusDashboardState extends State<StatusDashboard> {
|
||||
final statusBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
final statusBasedTodayCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusBasedTodayCount']) ?? [],
|
||||
);
|
||||
|
||||
final statusBasedWeeklyCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusBasedWeeklyCount']) ?? [],
|
||||
);
|
||||
|
||||
final typeBasedTodayCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['typeBasedTodayCount']) ?? [],
|
||||
);
|
||||
|
||||
final typeBasedWeeklyCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['typeBasedWeeklyCount']) ?? [],
|
||||
);
|
||||
|
||||
final typeTodayBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['typeTodayBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
final typeWeeklyBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['typeWeeklyBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
final statusTodayBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusTodayBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
final statusWeeklyBasedCount = List<Map<String, dynamic>>.from(
|
||||
(apiData?['data']?['statusWeeklyBasedCount']) ?? [],
|
||||
);
|
||||
|
||||
final List todayList = List<Map<String, dynamic>>.from(
|
||||
apiData?['data']?['todayTripCounts'] ?? [],
|
||||
);
|
||||
|
||||
final List weekList = List<Map<String, dynamic>>.from(
|
||||
apiData?['data']?['weekTripCounts'] ?? [],
|
||||
);
|
||||
|
||||
// Merge all maps in the list into one map
|
||||
Map<String, dynamic> todayBasedCount = {};
|
||||
Map<String, dynamic> weekBasedCount = {};
|
||||
|
||||
for (final item in todayList) {
|
||||
todayBasedCount.addAll(item);
|
||||
}
|
||||
|
||||
for (final item in weekList) {
|
||||
weekBasedCount.addAll(item);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
// currentData = selectedView == "Today" ? todayBasedCount : weekBasedCount;
|
||||
typeData = selectedView == "Today"
|
||||
? typeBasedTodayCount
|
||||
: typeBasedWeeklyCount ;
|
||||
statusData = selectedView == "Today"
|
||||
? statusBasedTodayCount
|
||||
: statusBasedWeeklyCount ;
|
||||
|
||||
flightData = selectedView == "Today"
|
||||
? getStatusValues(todayBasedCount['flight'] ?? {})
|
||||
: getStatusValues(weekBasedCount['flight'] ?? {});
|
||||
accommodationData = selectedView == "Today"
|
||||
? getStatusValues(todayBasedCount['acomodation'] ?? {})
|
||||
: getStatusValues(weekBasedCount['acomodation'] ?? {});
|
||||
forexData = selectedView == "Today"
|
||||
? getStatusValues(todayBasedCount['forex'] ?? {})
|
||||
: getStatusValues(weekBasedCount['forex'] ?? {});
|
||||
});
|
||||
|
||||
|
||||
print("statusBasedCount - => $statusBasedCount");
|
||||
print("Current Data - => $currentData");
|
||||
// 👇 Local function to create the card widget
|
||||
Widget buildInfoCard(String title, int count, double width) {
|
||||
print('title $title');
|
||||
@ -265,6 +368,7 @@ class StatusDashboardState extends State<StatusDashboard> {
|
||||
(title == 'Domestic' || title == 'International')
|
||||
? 16
|
||||
: 14,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@ -293,6 +397,317 @@ class StatusDashboardState extends State<StatusDashboard> {
|
||||
print("dashboard ..");
|
||||
}
|
||||
}
|
||||
String capitalize(String s) => s[0].toUpperCase() + s.substring(1);
|
||||
|
||||
Widget toggleChip(String label) {
|
||||
bool isSelected = selectedView == label;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedView = label;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? const Color(0xFF003A78) : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
height: 36,
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 10,
|
||||
color: isSelected ? Colors.white : Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTopCard({
|
||||
// required IconData icon,
|
||||
required Color color,
|
||||
required String label,
|
||||
required int count,
|
||||
required Color bgColor,
|
||||
}) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(1),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Tooltip(
|
||||
message: label ,
|
||||
child: Image.asset(
|
||||
label == 'Domestic'
|
||||
? 'assets/images/IconsImg/Domestic_new.png'
|
||||
: 'assets/images/IconsImg/International_new.png',
|
||||
// width: 65,
|
||||
// height: label == 'Domestic' ? 37 : 40,
|
||||
),
|
||||
),
|
||||
// Icon(icon, color: color, size: 40),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
count.toString(),
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 28,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: bgColor,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Text(
|
||||
label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 10,
|
||||
color: color,
|
||||
// color: const Color(0xFFEAF3FB),
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget statusCard(String label, int count) {
|
||||
return Column(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEAF3FB),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
count.toString(),
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 18,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
label,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildCategoryCard(String title, IconData icon, List<String> statusLabels,List<int> values) {
|
||||
|
||||
final statusIcons = [
|
||||
Icons.calendar_today,
|
||||
Icons.assignment,
|
||||
Icons.verified,
|
||||
Icons.block,
|
||||
Icons.cancel,
|
||||
];
|
||||
|
||||
final statusColors = [
|
||||
Color(0xFFFBFFCA),
|
||||
Color(0xFFFFF1CD),// Colors.yellow.shade100, // Color(0xFFCAE77B),
|
||||
Color(0xFFDAFFE8),// Colors.green.shade100, // Color(0xFF72D480),
|
||||
Color(0xFFFFD6D3),// Colors.red.shade100, // Color(0xFFF88C8C),
|
||||
Color(0xFFFFA8A8), // Colors.red.shade200, // Color(0xFFE94B4B),
|
||||
];
|
||||
|
||||
final statusIconColors = [
|
||||
Color(0xFFB1BC1A),
|
||||
Color(0xFFCC9300),
|
||||
Color(0xFF509269),
|
||||
Color(0xFFD25E55),
|
||||
Color(0xFFAE0303),
|
||||
];
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
// decoration: BoxDecoration(
|
||||
// color: Colors.white,
|
||||
// borderRadius: BorderRadius.circular(12),
|
||||
// ),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.blueGrey,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: const BoxDecoration(
|
||||
// color: Colors.blue,
|
||||
color: Color(0xFF004A8E),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
icon,
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(title, style: GoogleFonts.poppins(
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold
|
||||
),),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...List.generate(values.length, (index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
// Icon(statusIcons[index], size: 15, color: statusIconColors[index]),
|
||||
Container(
|
||||
width: 28,
|
||||
height: 28,
|
||||
decoration: BoxDecoration(
|
||||
color: statusColors[index], // background color
|
||||
borderRadius: BorderRadius.circular(4), // square with slight rounding
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Icon(
|
||||
statusIcons[index],
|
||||
size: 15,
|
||||
color: statusIconColors[index], // icon color
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(statusLabels[index], style: GoogleFonts.poppins(
|
||||
color: Colors.black87,
|
||||
),),
|
||||
],
|
||||
),
|
||||
Text(values[index].toString(), style: GoogleFonts.poppins(
|
||||
fontSize: 10,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildCategoryCard(String title, IconData icon,statusLabels,statusvalue) {
|
||||
//
|
||||
//
|
||||
// return Container(
|
||||
// padding: const EdgeInsets.all(16),
|
||||
// // decoration: BoxDecoration(
|
||||
// // color: Colors.white,
|
||||
// // borderRadius: BorderRadius.circular(12),
|
||||
// // ),
|
||||
//
|
||||
// child: Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Row(
|
||||
// children: [
|
||||
// Container(
|
||||
// padding: const EdgeInsets.all(8),
|
||||
// decoration: const BoxDecoration(
|
||||
// color: Colors.blue,
|
||||
// shape: BoxShape.circle,
|
||||
// ),
|
||||
// child: Icon(
|
||||
// icon,
|
||||
// color: Colors.white,
|
||||
// size: 20,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 10),
|
||||
// Text(title, style: GoogleFonts.poppins(
|
||||
// color: Colors.black87,
|
||||
// fontWeight: FontWeight.bold
|
||||
// ),),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 12),
|
||||
// ...List.generate(statusvalue.length, (index) {
|
||||
// return Padding(
|
||||
// padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Row(
|
||||
// children: [
|
||||
// // Icon(statusIcons[index], size: 20, color: statusColors[index]),
|
||||
// Container(
|
||||
// padding: const EdgeInsets.all(8),
|
||||
// decoration: BoxDecoration(
|
||||
// color: statusColors[index], // Background color behind the icon
|
||||
// borderRadius: BorderRadius.circular(10), // Rounded square
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Colors.grey, // Shadow color
|
||||
// spreadRadius: 1,
|
||||
// blurRadius: 5,
|
||||
// offset: const Offset(0, 2), // Shadow position
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Icon(
|
||||
// statusIcons[index],
|
||||
// color: statusIconColors[index],
|
||||
// size: 20,
|
||||
// // color: statusColors[index],
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: 6),
|
||||
// Text(statusLabels[index]),
|
||||
// ],
|
||||
// ),
|
||||
//
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
@ -322,94 +737,137 @@ class StatusDashboardState extends State<StatusDashboard> {
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return SingleChildScrollView(
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: constraints.maxHeight,
|
||||
),
|
||||
child: IntrinsicHeight(
|
||||
// Only needed if child layout depends on height
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isDesktop
|
||||
? Colors.white
|
||||
: const Color(0xFFFCFCFC),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(30.0),
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
// child: ConstrainedBox(
|
||||
// constraints: BoxConstraints(
|
||||
// minHeight: constraints.maxHeight,
|
||||
// ),
|
||||
// ),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// First Row: Toggle Buttons Row
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFEAEAEA),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
toggleChip("Today"),
|
||||
toggleChip("This Week"),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Second Row: Domestic & International and Status
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
// LEFT SIDE
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.blueGrey,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Row(
|
||||
children: [
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children:
|
||||
typeBasedCount.map((item) {
|
||||
double cardWidth =
|
||||
isDesktop
|
||||
? (MediaQuery.of(
|
||||
context,
|
||||
).size.width *
|
||||
0.75 -
|
||||
10) /
|
||||
2 // 80% width padding adjusted
|
||||
: MediaQuery.of(
|
||||
context,
|
||||
).size.width -
|
||||
24; // full width with padding
|
||||
|
||||
return SizedBox(
|
||||
width: cardWidth,
|
||||
child: buildInfoCard(
|
||||
item['value'],
|
||||
item['count'],
|
||||
cardWidth,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children:
|
||||
statusBasedCount.map((item) {
|
||||
double cardWidth =
|
||||
isDesktop
|
||||
? (MediaQuery.of(
|
||||
context,
|
||||
).size.width *
|
||||
0.90 -
|
||||
50) /
|
||||
6 // desktop layout: 6 cards per row
|
||||
: MediaQuery.of(
|
||||
context,
|
||||
).size.width -
|
||||
24; // mobile: full width
|
||||
|
||||
return SizedBox(
|
||||
width: cardWidth,
|
||||
child: buildInfoCard(
|
||||
item['value'],
|
||||
item['count'],
|
||||
cardWidth,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
...typeData.map((item) {
|
||||
return Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
buildTopCard(
|
||||
// icon: item['value'] == "Domestic"
|
||||
// ? Icons.home
|
||||
// : Icons.travel_explore,
|
||||
color: item['value'] == "Domestic"
|
||||
? Color(0xFF0DB04B)
|
||||
: Color(0xFF004A8E),
|
||||
label: item['value'],
|
||||
count: item['count'],
|
||||
bgColor: item['value'] == "Domestic"
|
||||
? const Color(0xFFD6FBE4)
|
||||
: const Color(0xFFD9E8FF),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
// RIGHT SIDE
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.blueGrey,
|
||||
blurRadius: 5,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Status",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 19,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 23),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
...statusData.map((item) => statusCard(item['value'], item['count'])).toList(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
|
||||
// Third Row: Flight, Accommodation, Forex
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(child: buildCategoryCard("Flight", Icons.flight,statusLabels,flightData)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: buildCategoryCard("Accomodation", Icons.hotel_outlined,statusLabels,accommodationData)),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: buildCategoryCard("Forex", Icons.attach_money,statusLabels,forexData)),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@ -50,6 +50,7 @@ class DepartmentDataState extends State<DepartmentData> {
|
||||
String? userId;
|
||||
int? departmentDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = ["dropdown_value", "description"];
|
||||
|
||||
@ -163,9 +164,12 @@ class DepartmentDataState extends State<DepartmentData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postDepartmentData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -222,11 +226,20 @@ class DepartmentDataState extends State<DepartmentData> {
|
||||
_clearError();
|
||||
widget.fetchGetDepartment();
|
||||
// dispose();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
} else if (response.statusCode == 404) {
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
|
||||
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
@ -237,9 +250,15 @@ class DepartmentDataState extends State<DepartmentData> {
|
||||
} else {
|
||||
print("Failed to submit. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,10 +428,21 @@ class DepartmentDataState extends State<DepartmentData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -132,6 +132,7 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGetDepartment() async {
|
||||
// return [];
|
||||
final String apiUrlData = '$apiUrl/api/getDepartmentList?for=table_view';
|
||||
|
||||
final String? token = await getToken();
|
||||
@ -284,7 +285,7 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -454,6 +455,7 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureDepartment,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureDepartment == null) {
|
||||
CircularProgressIndicator();
|
||||
}
|
||||
@ -479,9 +481,9 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Department Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -490,15 +492,15 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Department Details",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Department Details",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -905,7 +907,7 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
filteredDepartment.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -920,7 +922,7 @@ class DepartmentListState extends State<DepartmentList> {
|
||||
filteredDepartment.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -66,6 +66,7 @@ class GroupDataState extends State<GroupData> {
|
||||
int? groupDataId;
|
||||
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = [
|
||||
"name",
|
||||
@ -227,9 +228,12 @@ class GroupDataState extends State<GroupData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postGroupData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -280,11 +284,21 @@ class GroupDataState extends State<GroupData> {
|
||||
// _clearError();
|
||||
_clearError();
|
||||
await widget.fetchGetGroup();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
|
||||
// dispose();
|
||||
Navigator.of(context).pop();
|
||||
} else if (response.statusCode == 404) {
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -294,12 +308,33 @@ 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';
|
||||
|
||||
if (errorMessage.contains("Duplicate entry")) {
|
||||
_clearError();
|
||||
await widget.fetchGetGroup();
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text("Group Already Exists!"),
|
||||
backgroundColor: Colors.orange,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,7 +412,6 @@ class GroupDataState extends State<GroupData> {
|
||||
CustomTextFieldForexWrapper(
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["nameFocused"] ?? false,
|
||||
|
||||
isDesktop: widget.isDesktop,
|
||||
color: Colors.transparent,
|
||||
child: SizedBox(
|
||||
@ -444,6 +478,7 @@ class GroupDataState extends State<GroupData> {
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldForexWrapper(
|
||||
isFocused: focusStates["domestic_policy_nameFocused"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -498,7 +533,7 @@ class GroupDataState extends State<GroupData> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["country_codeFocused"] ?? false)
|
||||
(focusStates["domestic_policy_nameFocused"] ?? false)
|
||||
? widget.layoutColor!
|
||||
: Colors.white,
|
||||
// width: 0.5,
|
||||
@ -507,7 +542,7 @@ class GroupDataState extends State<GroupData> {
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["country_codeFocused"] ?? false)
|
||||
(focusStates["domestic_policy_nameFocused"] ?? false)
|
||||
? widget.layoutColor!
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
@ -518,9 +553,8 @@ class GroupDataState extends State<GroupData> {
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: widget.layoutColor!, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
)
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0,
|
||||
vertical: 8.0,),
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
@ -583,7 +617,7 @@ class GroupDataState extends State<GroupData> {
|
||||
CustomTextFieldForexWrapper(
|
||||
isFocused:
|
||||
focusStates["international_policy_nameFocused"] ?? false,
|
||||
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -639,20 +673,20 @@ class GroupDataState extends State<GroupData> {
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["country_codeFocused"] ?? false)
|
||||
(focusStates["international_policy_nameFocused"] ?? false)
|
||||
? widget.layoutColor!
|
||||
: Colors.white,
|
||||
// width: 0.5,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["country_codeFocused"] ?? false)
|
||||
(focusStates["international_policy_nameFocused"] ?? false)
|
||||
? widget.layoutColor!
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
// width: 0.5,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
@ -794,10 +828,25 @@ class GroupDataState extends State<GroupData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
// onPressed: () {
|
||||
// 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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -117,6 +117,7 @@ class _GroupListState extends State<GroupList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGroups() async {
|
||||
// return [];
|
||||
final result = await apiService.fetchAllGroup();
|
||||
return result; // Returning raw JSON list
|
||||
}
|
||||
@ -303,7 +304,7 @@ class _GroupListState extends State<GroupList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -475,6 +476,7 @@ class _GroupListState extends State<GroupList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureGroups,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureGroups == null) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
@ -490,9 +492,9 @@ class _GroupListState extends State<GroupList> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Group Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -501,15 +503,15 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Group",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Group",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -612,12 +614,27 @@ class _GroupListState extends State<GroupList> {
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
// DataCell(
|
||||
// Text(
|
||||
// "${group['name'] ?? ''}",
|
||||
// style: TextStyle(
|
||||
// fontSize: 13,
|
||||
// fontFamily: "Inter",
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['name'] ?? ''}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: 200), // limit description width
|
||||
child: Text(
|
||||
"${group['name'] ?? ''}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1, // optional: show only 1 line
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -639,12 +656,27 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// DataCell(
|
||||
// Text(
|
||||
// "${group['description'] ?? 'N/A'}",
|
||||
// style: TextStyle(
|
||||
// fontSize: 13,
|
||||
// fontFamily: "Inter",
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['description'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: 200), // limit description width
|
||||
child: Text(
|
||||
"${group['description'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
softWrap: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -984,7 +1016,7 @@ class _GroupListState extends State<GroupList> {
|
||||
filteredGroups.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -999,7 +1031,7 @@ class _GroupListState extends State<GroupList> {
|
||||
paginatedGroup.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -54,6 +54,7 @@ class HotelsDataState extends State<HotelsData> {
|
||||
String? userId;
|
||||
int? hotelsDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = [
|
||||
"hotel_name",
|
||||
@ -196,9 +197,12 @@ class HotelsDataState extends State<HotelsData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postHotelsData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -250,20 +254,24 @@ class HotelsDataState extends State<HotelsData> {
|
||||
hotelsDataId != null
|
||||
? await http.put(uri, headers: headers, body: body)
|
||||
: await http.post(uri, headers: headers, body: body);
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
print("Hotels Details Created successfully!");
|
||||
print("Response: ${response.body}");
|
||||
// _clearError();
|
||||
_clearError();
|
||||
await widget.fetchGetHotels();
|
||||
|
||||
// dispose();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
// Do NOT re-enable here if success
|
||||
} else if (response.statusCode == 404) {
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
@ -271,13 +279,26 @@ class HotelsDataState extends State<HotelsData> {
|
||||
behavior: SnackBarBehavior.floating,
|
||||
),
|
||||
);
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
|
||||
} else {
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
}
|
||||
catch (e) {
|
||||
print("Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@ -691,10 +712,21 @@ class HotelsDataState extends State<HotelsData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -135,6 +135,7 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGetHotels() async {
|
||||
// return [];
|
||||
orgId = await getOrgId();
|
||||
final String apiUrlData = '$apiUrl/api/getHotels?for=table_view';
|
||||
|
||||
@ -335,7 +336,7 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -503,6 +504,7 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureHotels,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureHotels == null) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
@ -528,9 +530,9 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Hotels Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -539,15 +541,15 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Hotels",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Hotels",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -922,7 +924,7 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
filteredHotels.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -937,7 +939,7 @@ class HotelsDataListState extends State<HotelsDataList> {
|
||||
filteredHotels.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_confirmation_dialog.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
@ -76,6 +77,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
bool _checkOutTimeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
Color layoutColor = Colors.grey;
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) onFocusChange) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
@ -100,6 +103,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
print(":(");
|
||||
print(_checkInController.text);
|
||||
print(_checkInTimeController.text);
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
@ -120,7 +126,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
_addFocusListener(
|
||||
_destinationFocusNode,
|
||||
(focus) => _destinationFocused = focus,
|
||||
@ -129,6 +137,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
_hotelNameFocusNode,
|
||||
(focus) => _isHotelNameFocused = focus,
|
||||
);
|
||||
_addFocusListener( _CategoryFocusNode, (focus) => _CategoryFocused = focus);
|
||||
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
|
||||
_addFocusListener(
|
||||
_checkInTimeFocusNode,
|
||||
@ -160,6 +169,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
flightLastTripDateNotifier = ValueNotifier<String?>(null);
|
||||
flightFirstToDestinationNotifier = ValueNotifier<String?>(null);
|
||||
|
||||
// // ✅ Check and set default times if empty
|
||||
// if (_checkInTimeController.text.isEmpty) {
|
||||
// _checkInTimeController.text = '14:00'; // 2 PM
|
||||
// }
|
||||
// if (_checkOutTimeController.text.isEmpty) {
|
||||
// _checkOutTimeController.text = '12:00'; // 12 PM
|
||||
// }
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadCountryList();
|
||||
|
||||
@ -176,12 +193,23 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
widget.selectedItem?["destination_city"] ??
|
||||
result['firstToDestination'];
|
||||
|
||||
// if (checkinDate != null) {
|
||||
// flightFirstTripDateNotifier.value = checkinDate;
|
||||
// }
|
||||
//
|
||||
// if (checkoutDate != null) {
|
||||
// flightLastTripDateNotifier.value = checkoutDate;
|
||||
// }
|
||||
|
||||
final tripType = result['tripType'] ;
|
||||
|
||||
if (checkinDate != null) {
|
||||
flightFirstTripDateNotifier.value = checkinDate;
|
||||
}
|
||||
|
||||
if (checkoutDate != null) {
|
||||
flightLastTripDateNotifier.value = checkoutDate;
|
||||
if (tripType == "Oneway") {
|
||||
flightLastTripDateNotifier.value = checkinDate;
|
||||
} else {
|
||||
flightLastTripDateNotifier.value = checkoutDate ?? checkinDate;
|
||||
}
|
||||
}
|
||||
|
||||
if (destinationCity != null) {
|
||||
@ -199,24 +227,25 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
// ✅ Only set controller after value is updated
|
||||
// final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
|
||||
final parsedDate = DateFormat(
|
||||
"dd-MM-yyyy",
|
||||
).parse(flightFirstTripDateNotifier.value ?? '');
|
||||
if (parsedDate != null) {
|
||||
_checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
|
||||
final checkInDateInput = flightFirstTripDateNotifier.value;
|
||||
if (checkInDateInput != null && checkInDateInput.isNotEmpty) {
|
||||
try {
|
||||
final parsedDate = DateFormat("dd-MM-yyyy").parseStrict(checkInDateInput);
|
||||
_checkInController.text = DateFormat("dd-MM-yyyy").format(parsedDate);
|
||||
} catch (e) {
|
||||
_checkInController.text = '';
|
||||
}
|
||||
}
|
||||
|
||||
// final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
|
||||
final parsedEndDate = DateFormat(
|
||||
"dd-MM-yyyy",
|
||||
).parse(flightLastTripDateNotifier.value ?? '');
|
||||
|
||||
if (parsedEndDate != null) {
|
||||
// _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate);
|
||||
|
||||
_checkOutController.text = DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(parsedEndDate);
|
||||
final checkOutDateInput = flightLastTripDateNotifier.value;
|
||||
if (checkOutDateInput != null && checkOutDateInput.isNotEmpty) {
|
||||
try {
|
||||
final parsedDate = DateFormat("dd-MM-yyyy").parseStrict(checkOutDateInput);
|
||||
_checkOutController.text = DateFormat("dd-MM-yyyy").format(parsedDate);
|
||||
} catch (e) {
|
||||
_checkOutController.text = '';
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Check and set default times if empty
|
||||
@ -239,6 +268,24 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
// setState(() {
|
||||
// layoutColor =
|
||||
// layoutString != null
|
||||
// ? Color(int.parse(layoutString))
|
||||
// : Colors.redAccent;
|
||||
// });
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_destinationFocusNode.dispose();
|
||||
@ -272,8 +319,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
Map<String, String?> getFlightTripDateRange(
|
||||
List<Map<String, dynamic>> flightData,
|
||||
) {
|
||||
print("FlightDAta - ${widget.flightData}");
|
||||
|
||||
String tt = "";
|
||||
if (widget.flightData.isNotEmpty) {
|
||||
print("Trip Type - ${widget.flightData[0]['trip_type']}");
|
||||
tt = widget.flightData[0]['trip_type'];
|
||||
} else {
|
||||
print("No flight data available");
|
||||
}
|
||||
final allTrips =
|
||||
flightData
|
||||
.expand((flight) => flight['trips'] ?? [])
|
||||
@ -298,6 +350,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
print("res1 - ${firstTrip['date']},");
|
||||
print("res11 - $firstTripDate");
|
||||
print("res22 - $tt");
|
||||
|
||||
// Use `List.where` instead of `firstWhere` so you can handle an empty result
|
||||
|
||||
@ -319,6 +372,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
// 'lastTripDate': nextTrip['date'],
|
||||
// 'lastTripDate': lastTrip['date'],
|
||||
'firstToDestination': firstTripToDestination['to_place'],
|
||||
'tripType': tt, // trip_type: Oneway
|
||||
};
|
||||
}
|
||||
|
||||
@ -503,6 +557,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiDataForClass?['hotel_class'] ?? [];
|
||||
print("hotel_classhotel_class");
|
||||
print(purposeList);
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems =
|
||||
purposeList
|
||||
@ -655,6 +711,20 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _CategoryFocusNode,
|
||||
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_CategoryFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
//
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_CategoryFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key:
|
||||
selectedClass == null
|
||||
@ -697,10 +767,34 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_CategoryFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_CategoryFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
),
|
||||
@ -764,7 +858,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
}
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
if (errorMessages["class"] != null) ...[
|
||||
|
||||
@ -113,7 +113,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
_dateFocus = _dateFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
@ -144,6 +144,9 @@ class _BusScreenState extends State<BusScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_dateFocusNode.dispose();
|
||||
_timeFocusNode.dispose();
|
||||
|
||||
_tripTypeController.dispose();
|
||||
_hotelNameController.dispose();
|
||||
_fromController.dispose();
|
||||
@ -536,7 +539,11 @@ class _BusScreenState extends State<BusScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
// onTap: () => _selectCheckOutDate(context),
|
||||
onTap: () {
|
||||
_dateFocusNode.requestFocus();
|
||||
_selectCheckOutDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -588,6 +595,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
onTap: () {
|
||||
// _selectedCheckOutTime = null; // ✅ Reset time variable
|
||||
_timeController.text = ""; // ✅ Clear text field
|
||||
_timeFocusNode.requestFocus();
|
||||
_selectCheckOutTime(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
|
||||
@ -279,7 +279,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
if (cardNumber.length < 15) {
|
||||
// Show error or return false
|
||||
print("Card number must be 15 digits");
|
||||
errorMessages["card_number"] = "Card number must be atleat 15 digits";
|
||||
errorMessages["card_number"] = "Card number must be atleast 15 digits";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1091,8 +1091,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
//
|
||||
// },
|
||||
onTap: () async {
|
||||
focusNodes["_forexStartDate"]?.requestFocus();
|
||||
await _selectCheckOutDate(context);
|
||||
|
||||
if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
|
||||
DateTime? startDate = _parseDate(
|
||||
textControllers["_forexStartDate"]!.text,
|
||||
@ -1189,6 +1189,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
child: GestureDetector(
|
||||
// onTap: () => _selectForexEndDate(context),
|
||||
onTap: () async {
|
||||
focusNodes["_forexEndDate"]?.requestFocus();
|
||||
await _selectForexEndDate(context);
|
||||
|
||||
if (textControllers["_forexEndDate"]!.text.isNotEmpty) {
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
@ -44,10 +45,10 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
|
||||
late TextEditingController _commentsController = TextEditingController();
|
||||
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _hotelNameFocused = false;
|
||||
bool _commentsFocus = false;
|
||||
String? selectedSpecialType;
|
||||
|
||||
Color layoutColor = Colors.grey;
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get miscellaneousData {
|
||||
@ -77,10 +78,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
if (widget.selectedItem == null) {
|
||||
_commentsController.clear();
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
_hotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
@ -104,11 +107,30 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_hotelNameFocusNode.dispose();
|
||||
_tripTypeController.dispose();
|
||||
_commentsController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
// setState(() {
|
||||
// layoutColor =
|
||||
// layoutString != null
|
||||
// ? Color(int.parse(layoutString))
|
||||
// : Colors.redAccent;
|
||||
// });
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
@ -281,8 +303,9 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
// ),
|
||||
// ),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isFocused: _hotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
width:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
@ -290,6 +313,19 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _hotelNameFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_hotelNameFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
//
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_hotelNameFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
// focusNode: _taxiReqFocusNode,
|
||||
items: purposeList.cast<Map<String, dynamic>>(),
|
||||
@ -318,9 +354,33 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_hotelNameFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_hotelNameFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
@ -348,7 +408,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
print("Updating form data: -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),)
|
||||
),
|
||||
),
|
||||
if (errorMessages["special_request"] != null) ...[
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
@ -41,6 +42,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _numPassengerFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
final FocusNode _carTypeFocusNode = FocusNode();
|
||||
|
||||
late TextEditingController _destinationController = TextEditingController();
|
||||
late TextEditingController _locationController = TextEditingController();
|
||||
@ -54,12 +56,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
bool _toFocus = false;
|
||||
bool _dateFocus = false;
|
||||
bool _taxiReqFocused = false;
|
||||
bool _carTypeFocused = false;
|
||||
bool _numPassengerFocus = false;
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
String? selectedReqTaxi;
|
||||
String? selectedCarType;
|
||||
Color layoutColor = Colors.grey;
|
||||
|
||||
Map<String, dynamic> get taxiData {
|
||||
Map<String, dynamic> data = {
|
||||
@ -98,6 +102,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
_addFocusListener(
|
||||
_destinationFocusNode,
|
||||
(focus) => _destinationFocus = focus,
|
||||
@ -106,6 +114,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
|
||||
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
|
||||
_addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus);
|
||||
_addFocusListener(_carTypeFocusNode, (focus) => _carTypeFocused = focus);
|
||||
_addFocusListener(
|
||||
_numPassengerFocusNode,
|
||||
(focus) => _numPassengerFocus = focus,
|
||||
@ -146,10 +155,22 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_destinationFocusNode.dispose();
|
||||
_locationFocusNode.dispose();
|
||||
_carTypeFocusNode.dispose();
|
||||
_destinationController.dispose();
|
||||
_dateController.dispose();
|
||||
_timeController.dispose();
|
||||
@ -397,9 +418,22 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _toFocus,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _carTypeFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_carTypeFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_carTypeFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
// focusNode: _taxiReqFocusNode,
|
||||
items: purposeList.cast<Map<String, dynamic>>(),
|
||||
@ -428,9 +462,33 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_carTypeFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_carTypeFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
@ -464,7 +522,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
print("Updating form data: -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -541,6 +599,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _taxiReqFocused,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
width:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
@ -548,6 +607,17 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _taxiReqFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_taxiReqFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_taxiReqFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
// focusNode: _taxiReqFocusNode,
|
||||
items: purposeList.cast<Map<String, dynamic>>(),
|
||||
@ -576,12 +646,36 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_taxiReqFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_taxiReqFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -606,7 +700,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
print("Updating form data: -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
];
|
||||
@ -817,7 +911,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
// onTap: () => _selectCheckOutDate(context),
|
||||
onTap: () {
|
||||
_dateFocusNode.requestFocus();
|
||||
_selectCheckOutDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -868,6 +966,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_timeController.text = "";
|
||||
_timeFocusNode.requestFocus();
|
||||
_selectCheckOutTime(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_confirmation_dialog.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
@ -44,7 +45,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
Map<String, String?> selectedValues = {};
|
||||
|
||||
final FocusNode _trainNoFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _typeOfClassFocusNode = FocusNode();
|
||||
final FocusNode _fromFocusNode = FocusNode();
|
||||
final FocusNode _toFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
@ -60,7 +61,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
late TextEditingController _trainCommentsController = TextEditingController();
|
||||
|
||||
bool _trainNoFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _typeOfClassFocus = false;
|
||||
bool _fromFocus = false;
|
||||
bool _toFocus = false;
|
||||
bool _dateFocus = false;
|
||||
@ -71,6 +72,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
String? exceptionalClass = "0";
|
||||
String? selectedFrom;
|
||||
String? selectedTo;
|
||||
Color layoutColor = Colors.grey;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
@ -110,15 +112,17 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
_trainNoFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_trainNoFocused = _trainNoFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
_typeOfClassFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
_typeOfClassFocus = _typeOfClassFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_fromFocusNode.addListener(() {
|
||||
@ -133,7 +137,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
_dateFocus = _dateFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_timeFocusNode.addListener(() {
|
||||
@ -213,6 +217,13 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
_trainNoFocusNode.dispose();
|
||||
_dateFocusNode.dispose();
|
||||
_timeFocusNode.dispose();
|
||||
_typeOfClassFocusNode.dispose();
|
||||
_fromFocusNode.dispose();
|
||||
_toFocusNode.dispose();
|
||||
_commentsFocusNode.dispose();
|
||||
|
||||
_trainNoController.dispose();
|
||||
_hotelNameController.dispose();
|
||||
_fromController.dispose();
|
||||
@ -223,6 +234,17 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -632,12 +654,24 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
// ),
|
||||
// ),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isFocused: _typeOfClassFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _typeOfClassFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_typeOfClassFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_typeOfClassFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
key:
|
||||
selectedClass == null
|
||||
@ -678,9 +712,33 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_typeOfClassFocus ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_typeOfClassFocus ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
@ -759,7 +817,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),)
|
||||
),
|
||||
),
|
||||
if (errorMessages["class"] != null) ...[
|
||||
@ -874,15 +932,27 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
// ),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _fromFocus ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: _fromFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_fromFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_fromFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedFrom],
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
constraints: BoxConstraints(maxHeight: 150),// maxHeight: 250
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
@ -906,8 +976,32 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_fromFocus ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_fromFocus ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -929,7 +1023,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
.key;
|
||||
});
|
||||
},
|
||||
),
|
||||
),),)
|
||||
),
|
||||
),
|
||||
if (errorMessages["from_station"] != null) ...[
|
||||
@ -1008,14 +1102,26 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _toFocus ?? false,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: _toFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_toFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_toFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedTo],
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
constraints: BoxConstraints(maxHeight: 150),
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
@ -1039,8 +1145,32 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_toFocus ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_toFocus ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1062,7 +1192,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
.key;
|
||||
});
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
if (errorMessages["to_station"] != null) ...[
|
||||
@ -1094,7 +1224,11 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
// onTap: () => _selectCheckOutDate(context),
|
||||
onTap: () {
|
||||
_dateFocusNode.requestFocus();
|
||||
_selectCheckOutDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -1146,6 +1280,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_timeController.text = "";
|
||||
_timeFocusNode.requestFocus();
|
||||
_selectCheckOutTime(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
@ -49,6 +50,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
List<dynamic> countryList = [];
|
||||
|
||||
final FocusNode _tripTypeFocusNode = FocusNode();
|
||||
final FocusNode _countryFocusNode = FocusNode();
|
||||
final FocusNode _hotelNameFocusNode = FocusNode();
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
@ -62,12 +64,14 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
late TextEditingController _visaCommentsController = TextEditingController();
|
||||
|
||||
bool _tripTypeFocused = false;
|
||||
bool _countryFocused = false;
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _dateFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
String? selectedPurpose;
|
||||
String? selectedCountry;
|
||||
Color layoutColor = Colors.grey;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
@ -97,8 +101,11 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
|
||||
_addFocusListener(_countryFocusNode, (focus) => _countryFocused = focus);
|
||||
_addFocusListener(
|
||||
_hotelNameFocusNode,
|
||||
(focus) => _isHotelNameFocused = focus,
|
||||
@ -144,6 +151,24 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
// setState(() {
|
||||
// layoutColor =
|
||||
// layoutString != null
|
||||
// ? Color(int.parse(layoutString))
|
||||
// : Colors.redAccent;
|
||||
// });
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
@ -217,6 +242,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
_countryFocusNode.dispose();
|
||||
_tripTypeController.dispose();
|
||||
_hotelNameController.dispose();
|
||||
_fromController.dispose();
|
||||
@ -519,12 +545,27 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
width:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: _countryFocusNode,
|
||||
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_countryFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
//
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_countryFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
popupProps: PopupProps.menu(
|
||||
@ -575,8 +616,32 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_countryFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_countryFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -598,7 +663,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
.key;
|
||||
});
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
@ -659,10 +724,24 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _tripTypeFocused,
|
||||
isDesktop: isDesktop,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _tripTypeFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_tripTypeFocused = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
//
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_tripTypeFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
items: purposeList.cast<Map<String, dynamic>>(),
|
||||
selectedItem: purposeList.firstWhere(
|
||||
@ -690,9 +769,33 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_tripTypeFocused ?? false)
|
||||
? layoutColor!
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_tripTypeFocused ?? false)
|
||||
? layoutColor
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: layoutColor, width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 10,
|
||||
@ -734,7 +837,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
});
|
||||
}
|
||||
: null,
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
if (errorMessages["type_of_visa"] != null) ...[
|
||||
@ -764,6 +867,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
height: 40,
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
_dateFocusNode.requestFocus();
|
||||
await _selectCheckOutDate(context);
|
||||
if (_dateController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
|
||||
@ -34,6 +34,7 @@ import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_breadcrumb_navigation.dart';
|
||||
import '../../widgets/custom_user_form.dart';
|
||||
import '../../widgets/custom_user_travel.dart';
|
||||
import 'dialog_placeholders.dart';
|
||||
|
||||
@ -63,6 +64,8 @@ class TemplateState extends State<Template> {
|
||||
List<String> placeholders = [];
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
bool isDisable = false;
|
||||
|
||||
|
||||
late QuillController _controller = QuillController.basic();
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
@ -449,6 +452,7 @@ class TemplateState extends State<Template> {
|
||||
// print('doc $doc');
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
updateTemplateData(data);
|
||||
// This triggers UI rebuild with error messages
|
||||
// if (validateData()) {
|
||||
@ -478,14 +482,19 @@ class TemplateState extends State<Template> {
|
||||
if (response.statusCode == 200) {
|
||||
print("policyData submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
setState(() {
|
||||
isDisable = false; });
|
||||
context.go('/templateList');
|
||||
} else {
|
||||
print("Failed to submit policyData. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false; });
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting policyData: $e");
|
||||
setState(() {
|
||||
isDisable = false; });
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,7 +571,7 @@ class TemplateState extends State<Template> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -608,7 +617,7 @@ class TemplateState extends State<Template> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
CustomTextFieldUserWrapper(
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["subjectFocused"] ?? false,
|
||||
color: Colors.white,
|
||||
@ -618,7 +627,7 @@ class TemplateState extends State<Template> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["subjectNode"],
|
||||
focusNode: focusNodes["subjectFocusNode"],
|
||||
controller: controllers["subject"],
|
||||
onChanged: (value) {
|
||||
// _clearError("local_id_num");
|
||||
@ -762,8 +771,14 @@ class TemplateState extends State<Template> {
|
||||
SizedBox(width: 10),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
onPressed: isDisable
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
});
|
||||
|
||||
await handleSubmit();
|
||||
// You can get text from commentController.text
|
||||
// Navigator.of(context).pop(); // Close the modal
|
||||
},
|
||||
|
||||
@ -74,6 +74,10 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
late int templateId = 0;
|
||||
late String templateName = "";
|
||||
late List<Map<String, dynamic>> placeholderList = [];
|
||||
bool isDisable = false;
|
||||
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
|
||||
Map<String, dynamic> get TemplateData {
|
||||
final data = {
|
||||
@ -118,6 +122,18 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
// updateData();
|
||||
// loadinitializeData();
|
||||
// loadInitialData();
|
||||
for (var field in dataHeader) {
|
||||
focusNodes["${field}FocusNode"] = FocusNode();
|
||||
focusStates["${field}Focused"] = false;
|
||||
}
|
||||
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _checkAuthAndLoadData() async {
|
||||
@ -151,6 +167,22 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
// _editorFocusNode.dispose();
|
||||
// super.dispose();
|
||||
// }
|
||||
void dispose() {
|
||||
for (var node in focusNodes.values) {
|
||||
node.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void loadinitializeData() async {
|
||||
orgId = await getOrgId();
|
||||
userId = await getUserId();
|
||||
@ -437,6 +469,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
// print('doc $doc');
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
updateTemplateData(data);
|
||||
// This triggers UI rebuild with error messages
|
||||
// if (validateData()) {
|
||||
@ -466,14 +499,23 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
if (response.statusCode == 200) {
|
||||
print("policyData submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
context.go('/templateList');
|
||||
// context.go('/templateList');
|
||||
context.go('/OrganizationSettings');
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
} else {
|
||||
print("Failed to submit policyData. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting policyData: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -545,7 +587,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -596,7 +638,8 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["subjectFocused"] ?? false,
|
||||
color: Colors.white,
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.8 : null,
|
||||
isDesktop: isDesktop,
|
||||
@ -604,6 +647,7 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["subjectFocusNode"],
|
||||
controller: controllers["subject"],
|
||||
onChanged: (value) {
|
||||
// _clearError("local_id_num");
|
||||
@ -818,8 +862,14 @@ class TemplateForexState extends State<TemplateForex> {
|
||||
SizedBox(width: 10),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
onPressed: isDisable
|
||||
? null
|
||||
: () async {
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
});
|
||||
|
||||
await handleSubmit();
|
||||
// You can get text from commentController.text
|
||||
// Navigator.of(context).pop(); // Close the modal
|
||||
},
|
||||
|
||||
@ -159,6 +159,7 @@ class TemplatesListState extends State<TemplatesList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGetForex() async {
|
||||
// return [];
|
||||
orgId = await getOrgId();
|
||||
// final String apiUrlData = '$apiUrl/api/getForexPerdiemList';
|
||||
final String apiUrlData = '$apiUrl/api/template?org_id=${orgId}';
|
||||
@ -446,7 +447,7 @@ class TemplatesListState extends State<TemplatesList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -594,6 +595,7 @@ class TemplatesListState extends State<TemplatesList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureTemplates,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureTemplates == null) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
@ -618,9 +620,9 @@ class TemplatesListState extends State<TemplatesList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Templates Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -629,15 +631,15 @@ class TemplatesListState extends State<TemplatesList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Templates",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Templates",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -30,6 +30,8 @@ class _MailSettingState extends State<MailSetting> {
|
||||
Map<String, String> errorMessages = {};
|
||||
final Map<String, TextEditingController> controllers = {};
|
||||
bool _obscurePassword = true;
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
|
||||
List<String> dataHeader = [
|
||||
"host",
|
||||
@ -40,6 +42,14 @@ class _MailSettingState extends State<MailSetting> {
|
||||
"toEmail",
|
||||
];
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
for (var node in focusNodes.values) {
|
||||
node.dispose();
|
||||
}
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
Map<String, dynamic> getMailData() => mailData;
|
||||
|
||||
Map<String, dynamic> get mailData {
|
||||
@ -70,6 +80,18 @@ class _MailSettingState extends State<MailSetting> {
|
||||
widget.onMailDataChanged(mailData); // Notify parent
|
||||
});
|
||||
}
|
||||
for (var field in dataHeader) {
|
||||
focusNodes["${field}FocusNode"] = FocusNode();
|
||||
focusStates["${field}Focused"] = false;
|
||||
}
|
||||
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -105,6 +127,14 @@ class _MailSettingState extends State<MailSetting> {
|
||||
}
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void handleTestMailSubmit() {
|
||||
print("TEStMailData- $mailData");
|
||||
|
||||
@ -285,7 +315,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["userNameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -295,6 +325,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["userNameFocusNode"],
|
||||
controller: controllers["userName"],
|
||||
onChanged: (value) {
|
||||
_clearError("mail_user_name");
|
||||
@ -337,7 +368,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["mail_passwordFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -347,6 +378,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["mail_passwordFocusNode"],
|
||||
controller: controllers["mail_password"],
|
||||
onChanged: (value) {
|
||||
_clearError("mail_password");
|
||||
@ -412,7 +444,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["senderEmailFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -422,6 +454,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["senderEmailFocusNode"],
|
||||
controller: controllers["senderEmail"],
|
||||
onChanged: (value) {
|
||||
_clearError("sender_email");
|
||||
@ -474,7 +507,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["hostFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -484,6 +517,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["hostFocusNode"],
|
||||
controller: controllers["host"],
|
||||
onChanged: (value) {
|
||||
_clearError("mail_host");
|
||||
@ -526,7 +560,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["portFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -536,6 +570,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["portFocusNode"],
|
||||
controller: controllers["port"],
|
||||
onChanged: (value) {
|
||||
_clearError("mail_port");
|
||||
@ -581,7 +616,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["toEmailFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -591,6 +626,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
// focusNode: _destinationFocusNode,
|
||||
focusNode: focusNodes["toEmailFocusNode"],
|
||||
controller: controllers["toEmail"],
|
||||
onChanged: (value) {
|
||||
_clearError("to_mail");
|
||||
|
||||
@ -591,7 +591,7 @@ class _OrgSetUpState extends State<OrgSetUp> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
|
||||
@ -150,7 +150,7 @@ class _OrganizationListState extends State<OrganizationList> {
|
||||
|
||||
Widget buildGroupListView(bool isDesktop) {
|
||||
if (apiAllOrganization == null || apiAllOrganization!.isEmpty) {
|
||||
return Center(child: Text("No groups found."));
|
||||
return Center(child: Text("No Data Found."));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
|
||||
@ -54,6 +54,7 @@ class ForexDataState extends State<ForexData> {
|
||||
String? userId;
|
||||
int? forexDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
bool isCashEditing = false;
|
||||
bool isCardEditing = false;
|
||||
@ -242,9 +243,12 @@ class ForexDataState extends State<ForexData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postForexData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -252,6 +256,7 @@ class ForexDataState extends State<ForexData> {
|
||||
print("ForexDAta - $forexData1");
|
||||
}
|
||||
|
||||
|
||||
Future<void> postForexData({int isActive = 1}) async {
|
||||
// final remarksData = getData();
|
||||
|
||||
@ -316,9 +321,19 @@ class ForexDataState extends State<ForexData> {
|
||||
await widget.fetchGetForex();
|
||||
|
||||
// dispose();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
} else if (response.statusCode == 404) {
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -331,9 +346,15 @@ class ForexDataState extends State<ForexData> {
|
||||
} else {
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -887,10 +908,26 @@ class ForexDataState extends State<ForexData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
// onPressed: () {
|
||||
// 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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -364,6 +364,16 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
print("filteredForex: $filteredForex");
|
||||
}
|
||||
|
||||
String toTitleCase(String input) {
|
||||
return input
|
||||
.toLowerCase()
|
||||
.split(' ')
|
||||
.map((word) => word.isNotEmpty
|
||||
? word[0].toUpperCase() + word.substring(1)
|
||||
: '')
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
@ -454,7 +464,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -649,7 +659,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
"No Perdiem Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -658,15 +668,15 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Perdiem Amount",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Perdiem Amount",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -780,7 +790,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
forex['country_name'] ?? '',
|
||||
toTitleCase(forex['country_name']) ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
@ -911,7 +921,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
forex['country_name'] ?? 'N/A',
|
||||
toTitleCase(forex['country_name']) ?? 'N/A',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 10,
|
||||
color: Colors.black87,
|
||||
@ -1113,7 +1123,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
filteredForex.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1128,7 +1138,7 @@ class ForexDataListState extends State<ForexDataList> {
|
||||
filteredForex.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -44,6 +44,7 @@ class _CreatePlansState extends State<CreatePlan> {
|
||||
|
||||
final FocusNode focusNode = FocusNode();
|
||||
bool isFocused = false;
|
||||
late FocusNode _tripTitleFocusNode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -571,6 +572,10 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
||||
focusStates["${field}Focused"] = false;
|
||||
}
|
||||
|
||||
// trip_titleFocusNode.addListener(() {
|
||||
// setState(() {}); // Rebuild when focus changes
|
||||
// });
|
||||
|
||||
print("Focus Nodes KeysII: ${focusNodes.keys.toList()}");
|
||||
print("Focus States KeysII: ${focusStates.keys.toList()}");
|
||||
|
||||
@ -1356,9 +1361,10 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
||||
print("hasExceptionalClass: $hasExceptionalClass");
|
||||
}
|
||||
|
||||
void handleSubmit() {
|
||||
setState(() async {
|
||||
await checkExceptionalClass();
|
||||
void handleSubmit() async {
|
||||
await checkExceptionalClass(); // Do async work first
|
||||
setState(() {
|
||||
// await checkExceptionalClass();
|
||||
|
||||
print("Handle Submit....after check");
|
||||
|
||||
@ -3719,7 +3725,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildTripName(bool isDesktop) {
|
||||
Widget _buildTN(bool isDesktop) {
|
||||
return SizedBox(
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.7443333 : 180,
|
||||
height: 35,
|
||||
@ -3751,6 +3757,49 @@ class CreateNewPlansState extends State<CreateNewPlan> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTripName(bool isDesktop) {
|
||||
return SizedBox(
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.5 : 180,
|
||||
height: 35,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
reverse: false, // show right-end on overflow
|
||||
physics: BouncingScrollPhysics(),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: isDesktop ? MediaQuery.of(context).size.width * 0.5 : 180),
|
||||
child: IntrinsicWidth(
|
||||
child: TextField(
|
||||
controller: _tripTitleController,
|
||||
maxLines: 1,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: isDesktop ? 16 : 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
labelText: "Trip Name *",
|
||||
labelStyle: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.grey,
|
||||
),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1, vertical: 16),
|
||||
errorText: validationErrors["trip_title"],
|
||||
errorStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.red),
|
||||
),
|
||||
keyboardType: TextInputType.text,
|
||||
scrollPhysics: BouncingScrollPhysics(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildApprovalItem(String title, String name) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@ -278,6 +278,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
|
||||
// Fetch API Data
|
||||
Future<List<Plan>> fetchPlans() async {
|
||||
// return [];
|
||||
// final String apiUrldata = '$apiUrl/api/plans';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
@ -869,24 +870,29 @@ class _ListPlansState extends State<ListPlans> {
|
||||
// color: Colors.redAccent)),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Trips Found",
|
||||
" No Trips Found",
|
||||
textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 20,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please Create Trip",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please Create Trip",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1627,7 +1633,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1642,7 +1648,7 @@ class _ListPlansState extends State<ListPlans> {
|
||||
filteredPlans.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -5,7 +5,7 @@ import 'dart:ui' as html;
|
||||
import 'dart:async';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:typed_data';
|
||||
import 'dart:html' as html;
|
||||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'dart:ui' as web;
|
||||
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
@ -50,12 +50,14 @@ class _PolicyState extends State<Policy> {
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
late String policyType = "domestic";
|
||||
// int? selectedServiceIndex = 1;
|
||||
String selectedServiceDBValue = '1';
|
||||
ValueNotifier<String> selectedServiceIndex = ValueNotifier("1");
|
||||
ValueNotifier<String> selectedServicePriorityIndex = ValueNotifier("1");
|
||||
String selectedService = "train";
|
||||
// ValueNotifier<String> selectedService = ValueNotifier("Train");
|
||||
bool isViewMode = false;
|
||||
bool isDisable = false;
|
||||
bool isSubmitting = false;
|
||||
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
@ -118,6 +120,15 @@ class _PolicyState extends State<Policy> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_checkAuthAndLoadData();
|
||||
focusNodes["policyFocusNode"] = FocusNode();
|
||||
focusStates["policyFocused"] = false;
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
||||
});
|
||||
});
|
||||
}
|
||||
// for (var field in dataHeader) {
|
||||
// focusNodes["${field}FocusNode"] = FocusNode();
|
||||
// focusStates["${field}Focused"] = false;
|
||||
@ -142,7 +153,13 @@ class _PolicyState extends State<Policy> {
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
void _checkAuthAndLoadData() async {
|
||||
final String? token = await getToken(); // Your async function to get token
|
||||
|
||||
@ -366,9 +383,38 @@ class _PolicyState extends State<Policy> {
|
||||
}
|
||||
}
|
||||
|
||||
// void handleSubmit() async {
|
||||
// setState(() { isDisable = true; });
|
||||
// print("Services - $services");
|
||||
// print("USR Detail Submit - $policyData");
|
||||
//
|
||||
// policyCriteriaKey.currentState?.saveCurrentPolicy(services);
|
||||
//
|
||||
// // Now the full data is ready in policyDataFromChild
|
||||
// print("Submitting full policyData: $policyData");
|
||||
//
|
||||
// Map<String, dynamic> data = policyData;
|
||||
//
|
||||
// if (!isValidData(data)) {
|
||||
// print("USERDETAILS : $policyData");
|
||||
// print("Validation Failed: Required fields are missing.");
|
||||
// setState(() { isDisable = false; });
|
||||
// return; // Stop execution if validation fails
|
||||
// } else {
|
||||
// // print("USERDETAILS : $policyData");
|
||||
// // orgId = await getOrgId();
|
||||
//
|
||||
// if(isDisable == true){ createPolicyData(data); }
|
||||
// setState(() { isDisable = false; });
|
||||
// }
|
||||
// }
|
||||
|
||||
void handleSubmit() async {
|
||||
print("Services - $services");
|
||||
print("USR Detail Submit - $policyData");
|
||||
if (isSubmitting || isDisable) return;
|
||||
isSubmitting = true;
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
});
|
||||
|
||||
policyCriteriaKey.currentState?.saveCurrentPolicy(services);
|
||||
|
||||
@ -378,18 +424,44 @@ class _PolicyState extends State<Policy> {
|
||||
Map<String, dynamic> data = policyData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("USERDETAILS : $policyData");
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
} else {
|
||||
// print("USERDETAILS : $policyData");
|
||||
// orgId = await getOrgId();
|
||||
|
||||
createPolicyData(data);
|
||||
print("Validation Failed");
|
||||
isSubmitting = false;
|
||||
setState(() => isDisable = false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
try {
|
||||
result = await createPolicyData(data);
|
||||
} catch (e) {
|
||||
print("Unexpected error during policy creation: $e");
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// ✅ Only navigate on success
|
||||
context.go('/PolicyList');
|
||||
} else {
|
||||
// ❌ Show error, allow retry
|
||||
Fluttertoast.showToast(
|
||||
msg: "Failed to submit policy. Please try again.",
|
||||
toastLength: Toast.LENGTH_SHORT,
|
||||
gravity: ToastGravity.CENTER,
|
||||
timeInSecForIosWeb: 2,
|
||||
backgroundColor: Colors.redAccent,
|
||||
textColor: Colors.white,
|
||||
fontSize: 18.0,
|
||||
webBgColor: "linear-gradient(to right, #28a745, #28a745)",
|
||||
);
|
||||
}
|
||||
|
||||
isSubmitting = false;
|
||||
setState(() {
|
||||
isDisable = false; // Allow retry
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset previous errors
|
||||
|
||||
@ -444,12 +516,14 @@ class _PolicyState extends State<Policy> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> createPolicyData(Map<String, dynamic> policyData) async {
|
||||
Future<bool> createPolicyData(Map<String, dynamic> policyData) async {
|
||||
final String apiUrldata = '$apiUrl/api/policy/createOrUpdate';
|
||||
final token = await getToken(); // Fetch token
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
print('Token not found. Please log in.');
|
||||
// throw Exception('Token not found. Please log in.');
|
||||
return false;
|
||||
}
|
||||
|
||||
final int? policyId;
|
||||
@ -472,14 +546,15 @@ class _PolicyState extends State<Policy> {
|
||||
if (response.statusCode == 200) {
|
||||
print("policyData submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
context.go('/PolicyList');
|
||||
return true;
|
||||
} else {
|
||||
print("Failed to submit policyData. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting policyData: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -792,12 +867,13 @@ class _PolicyState extends State<Policy> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["policyFocused"] ?? false,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12),
|
||||
focusNode: focusNodes["policyFocusNode"],
|
||||
controller: _policyController,
|
||||
onChanged: (value) => _clearError("name"),
|
||||
decoration: InputDecoration(
|
||||
@ -1073,6 +1149,13 @@ class _PolicyState extends State<Policy> {
|
||||
List<String> services =
|
||||
ServicesChoosed!.map((service) => service['name'].toString()).toList();
|
||||
|
||||
List<String> serviceDBvalue =
|
||||
ServicesChoosed!.map((service) => service['service_id'].toString()).toList();
|
||||
|
||||
print("1093");
|
||||
print(services);
|
||||
print(serviceDBvalue);
|
||||
|
||||
return Container(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -1102,12 +1185,17 @@ class _PolicyState extends State<Policy> {
|
||||
print("Selected Services - $service - $index");
|
||||
setState(() {
|
||||
selectedServiceIndex.value = index.toString();
|
||||
selectedServiceDBValue = serviceDBvalue[entry.key];
|
||||
selectedService = service;
|
||||
|
||||
print(
|
||||
" selectedServiceIndex.value - ${selectedServiceIndex.value}",
|
||||
);
|
||||
|
||||
print(
|
||||
" selectedServiceDBValue.value - ${selectedServiceDBValue}",
|
||||
);
|
||||
|
||||
// policyCriteriaKey.currentState?.fieldForPolicy();
|
||||
// policyCriteriaKey.currentState
|
||||
// ?.addOrUpdatePolicy(selectedServiceIndex.value);
|
||||
@ -1308,6 +1396,7 @@ class _PolicyState extends State<Policy> {
|
||||
isCost: showCost,
|
||||
selectedTabNotifier: selectedServiceIndex,
|
||||
selectedService: selectedService,
|
||||
selectedServiceDBValue: selectedServiceDBValue,
|
||||
userId: userId,
|
||||
onPolicyDataChanged: (List<Map<String, dynamic>>? policyData) {
|
||||
print("🟢 policyData received from child: $policyData");
|
||||
@ -1493,7 +1582,8 @@ class _PolicyState extends State<Policy> {
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed:
|
||||
isViewMode ? null : handleSubmit, // Disable when in view mode
|
||||
// isViewMode ? null : handleSubmit, // Disable when in view mode
|
||||
isViewMode || isDisable ? null : handleSubmit,
|
||||
child: Text("Submit", style: GoogleFonts.poppins(fontSize: 10)),
|
||||
),
|
||||
),
|
||||
|
||||
@ -14,6 +14,7 @@ import '../../widgets/custom_user_form.dart';
|
||||
class PolicyCriteria extends StatefulWidget {
|
||||
bool isDesktop;
|
||||
String selectedService;
|
||||
String selectedServiceDBValue;
|
||||
final ValueNotifier<String> selectedTabNotifier;
|
||||
String? userId;
|
||||
bool isClass = true;
|
||||
@ -26,6 +27,7 @@ class PolicyCriteria extends StatefulWidget {
|
||||
required this.isDesktop,
|
||||
required this.selectedService,
|
||||
required this.selectedTabNotifier,
|
||||
required this.selectedServiceDBValue,
|
||||
required this.userId,
|
||||
required this.isClass,
|
||||
required this.isCost,
|
||||
@ -149,7 +151,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
|
||||
// Step 4: Initialize any missing controllers or data
|
||||
costController.putIfAbsent(id, () => TextEditingController());
|
||||
classAction.putIfAbsent(id, () => "1");
|
||||
classAction.putIfAbsent(id, () => "1"); // working fine
|
||||
FirstApproverAction.putIfAbsent(id, () => "None");
|
||||
SecondApproverAction.putIfAbsent(id, () => "None");
|
||||
ThirdApproverAction.putIfAbsent(id, () => "None");
|
||||
@ -170,7 +172,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
policyData?.add({
|
||||
"service_id": int.parse(id),
|
||||
"cost": "",
|
||||
"class": classAction[id],
|
||||
"class": classAction[id], // working fine
|
||||
"a1_action": FirstApproverAction[id],
|
||||
"a2_action": SecondApproverAction[id],
|
||||
"a3_action": ThirdApproverAction[id],
|
||||
@ -204,7 +206,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
|
||||
// classAction[serviceId] = classAction[serviceId]?.toString();
|
||||
|
||||
classAction[serviceId] = item['class']?.toString() ?? '';
|
||||
classAction[serviceId] = item['class']?.toString() ?? ''; // working fine
|
||||
|
||||
FirstApproverAction[serviceId] = item['a1_action']?.toString();
|
||||
SecondApproverAction[serviceId] = item['a2_action']?.toString();
|
||||
@ -266,7 +268,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
"service_id": serviceId,
|
||||
"cost": costController[serviceId]?.text,
|
||||
// "class": classController[serviceId]?.text,
|
||||
"class": classAction[serviceId] ?? "",
|
||||
"class": classAction[serviceId] ?? "", // working fine
|
||||
"a1_action": FirstApproverAction[serviceId],
|
||||
"a2_action": SecondApproverAction[serviceId],
|
||||
"a3_action": ThirdApproverAction[serviceId],
|
||||
@ -288,7 +290,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
final cost = costController[serviceId]?.text ?? "";
|
||||
|
||||
// final travelClass = classController[serviceId]?.text ?? "";
|
||||
final travelClass = classAction[serviceId];
|
||||
final travelClass = classAction[serviceId]; // working fine
|
||||
final a1 = FirstApproverAction[serviceId];
|
||||
final a2 = SecondApproverAction[serviceId];
|
||||
final a3 = ThirdApproverAction[serviceId];
|
||||
@ -456,10 +458,11 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
); // 👈 Save current values for existing service
|
||||
}
|
||||
setState(() {
|
||||
ServiceId = widget.selectedTabNotifier.value ?? "1";
|
||||
// ServiceId = widget.selectedTabNotifier.value ?? "1";
|
||||
ServiceId = widget.selectedServiceDBValue;
|
||||
// Initialize controllers and variables if not present
|
||||
costController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||
classAction.putIfAbsent(ServiceId!, () => "1");
|
||||
classAction.putIfAbsent((widget.selectedServiceDBValue), () => "1");
|
||||
// classController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||
|
||||
FirstApproverAction.putIfAbsent(ServiceId!, () => "None");
|
||||
@ -2370,6 +2373,7 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
|
||||
Widget buildClassWidget(isDesktop) {
|
||||
List<dynamic> purposeList = [];
|
||||
String textLabel = "Class";
|
||||
// if (ServiceId != null) {
|
||||
// purposeList = (ServiceId == "1")
|
||||
// ? (apiDataForClass?['flight_class'] ?? [])
|
||||
@ -2377,20 +2381,33 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
// }
|
||||
|
||||
if (ServiceIndxId != null) {
|
||||
purposeList =
|
||||
(ServiceIndxId == "1")
|
||||
? (apiDataForClass?['flight_class'] ?? [])
|
||||
: (ServiceIndxId == "2")
|
||||
? (apiDataForClass?['hotel_class'] ?? [])
|
||||
: (apiDataForClass?['train_class'] ?? []);
|
||||
}
|
||||
String textLabel = "Class";
|
||||
if (ServiceIndxId != null) {
|
||||
textLabel = (ServiceIndxId == "2") ? "Category" : "Class";
|
||||
switch (ServiceIndxId) {
|
||||
case "1":
|
||||
textLabel = "Class";
|
||||
purposeList = apiDataForClass?['flight_class'] ?? [];
|
||||
break;
|
||||
|
||||
case "2":
|
||||
textLabel = "Category";
|
||||
purposeList = apiDataForClass?['hotel_class'] ?? [];
|
||||
break;
|
||||
|
||||
case "9":
|
||||
textLabel = "Class";
|
||||
purposeList = apiDataForClass?['train_class'] ?? [];
|
||||
break;
|
||||
|
||||
default:
|
||||
textLabel = "Class";
|
||||
purposeList = [];
|
||||
}
|
||||
}
|
||||
|
||||
print("CLASSServiceId- $ServiceId");
|
||||
print("CLASSpurposeList - $purposeList");
|
||||
print("CLASS-ServiceId- $ServiceId");
|
||||
print("CLASS-ServiceIndxId- $ServiceIndxId");
|
||||
print("CLASS-purposeList - $purposeList");
|
||||
print("CLASS-DB Service Id - ${widget.selectedServiceDBValue}");
|
||||
print("CLASS-Action - ${classAction[widget.selectedServiceDBValue]}");
|
||||
|
||||
// ✅ Sort by dropdown_key
|
||||
purposeList.sort((a, b) {
|
||||
@ -2409,7 +2426,6 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
)
|
||||
.toList();
|
||||
|
||||
print("CLASS 2 - ");
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
@ -2436,13 +2452,13 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
// }
|
||||
|
||||
// 1. First, when dropdownItems are ready, assign default value:
|
||||
if (ServiceId != null &&
|
||||
classAction[ServiceId!] == null &&
|
||||
if ((widget.selectedServiceDBValue) != null &&
|
||||
classAction[(widget.selectedServiceDBValue)] == null &&
|
||||
dropdownItems.isNotEmpty) {
|
||||
// Important to wrap inside setState if async
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
classAction[ServiceId!] = dropdownItems.first.value!;
|
||||
classAction[(widget.selectedServiceDBValue)] = dropdownItems.first.value!;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -2502,13 +2518,13 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child:
|
||||
ServiceId != null && (isLoading || dropdownItems.isEmpty)
|
||||
(widget.selectedServiceDBValue) != null && (isLoading || dropdownItems.isEmpty)
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: DropdownSearch<Map<String, dynamic>>(
|
||||
key:
|
||||
classAction[ServiceId!] == null
|
||||
classAction[(widget.selectedServiceDBValue)] == null
|
||||
? UniqueKey()
|
||||
: ValueKey(classAction[ServiceId!]),
|
||||
: ValueKey(classAction[(widget.selectedServiceDBValue)]),
|
||||
items: purposeList.cast<Map<String, dynamic>>(),
|
||||
|
||||
// selectedItem: purposeList.firstWhere(
|
||||
@ -2516,11 +2532,11 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
// orElse: () => {},
|
||||
// ),
|
||||
selectedItem:
|
||||
classAction[ServiceId!] != null
|
||||
classAction[(widget.selectedServiceDBValue)] != null
|
||||
? purposeList.firstWhere(
|
||||
(item) =>
|
||||
item['dropdown_key'] ==
|
||||
classAction[ServiceId!],
|
||||
classAction[(widget.selectedServiceDBValue)],
|
||||
orElse: () => {},
|
||||
)
|
||||
: null,
|
||||
@ -2583,10 +2599,10 @@ class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
purposeList.isNotEmpty
|
||||
? (Map<String, dynamic>? newValue) async {
|
||||
setState(() {
|
||||
classAction[ServiceId!] =
|
||||
classAction[(widget.selectedServiceDBValue)] =
|
||||
newValue?['dropdown_key'];
|
||||
print(
|
||||
"Selected Purpose: ${classAction[ServiceId!]}",
|
||||
"Selected Purpose: ${classAction[(widget.selectedServiceDBValue)]}",
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -133,17 +133,16 @@ class _PolicyListState extends State<PolicyList> {
|
||||
}
|
||||
|
||||
void filterPolicy(String query) {
|
||||
print("all before filtering: $query");
|
||||
final lowerQuery = query.toLowerCase();
|
||||
setState(() {
|
||||
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;
|
||||
@ -340,7 +339,7 @@ class _PolicyListState extends State<PolicyList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -504,6 +503,7 @@ class _PolicyListState extends State<PolicyList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futurePolicy,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futurePolicy == null) {
|
||||
return const CircularProgressIndicator();
|
||||
}
|
||||
@ -519,9 +519,9 @@ class _PolicyListState extends State<PolicyList> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Policy Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -530,15 +530,15 @@ class _PolicyListState extends State<PolicyList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Policy",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Policy",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1029,12 +1029,32 @@ class _PolicyListState extends State<PolicyList> {
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
isDesktop
|
||||
? SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table, // <-- your existing table
|
||||
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,
|
||||
|
||||
@ -50,6 +50,7 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
|
||||
String? userId;
|
||||
int? purposeOfTravelDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = ["dropdown_value", "description"];
|
||||
|
||||
@ -163,9 +164,12 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postPurposeOfTravelData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -222,9 +226,19 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
|
||||
_clearError();
|
||||
widget.fetchGetPurposeOfTravel();
|
||||
// dispose();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
} else if (response.statusCode == 404) {
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
final message = jsonDecode(response.body)['message'] ?? 'Unknown error';
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -237,9 +251,15 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
|
||||
} else {
|
||||
print("Failed to submit. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,10 +429,21 @@ class PurposeOfTravelDataState extends State<PurposeOfTravelData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -284,7 +284,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -454,6 +454,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futurePurposeOfTravel,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futurePurposeOfTravel == null) {
|
||||
CircularProgressIndicator();
|
||||
}
|
||||
@ -479,9 +480,9 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Purpose Of Travel Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -490,15 +491,15 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Purpose Of Travel Details",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Purpose Of Travel Details",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -797,7 +798,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
filteredPurposeOfTravel.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -812,7 +813,7 @@ class PurposeOfTravelListState extends State<PurposeOfTravelList> {
|
||||
filteredPurposeOfTravel.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -449,8 +449,9 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
false) ||
|
||||
(object['Service']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(newToDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['created_on']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['created_time']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['FirstTravelDt']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['AdvPurch']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage1 = 0;
|
||||
@ -505,8 +506,9 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
false) ||
|
||||
(object['Service']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(newToDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['created_on']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['created_time']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['FirstTravelDt']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['AdvPurch']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage2 = 0;
|
||||
@ -662,24 +664,24 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -895,7 +897,7 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -903,16 +905,16 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1031,6 +1033,18 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Created Time',
|
||||
style:
|
||||
GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'First Travel Date',
|
||||
@ -1060,22 +1074,8 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
paginatedDomestic.map((
|
||||
entry,
|
||||
) {
|
||||
final FromDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['created_on'],
|
||||
),
|
||||
);
|
||||
final ToDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['FirstTravelDt'],
|
||||
),
|
||||
);
|
||||
final FromDate = entry['created_on'];
|
||||
final ToDate = entry['FirstTravelDt'];
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -1142,6 +1142,15 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
'${entry['created_time']}',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
12,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
// '${entry['FirstTravelDt']}',
|
||||
@ -1210,7 +1219,7 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1219,15 +1228,15 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1473,7 +1482,7 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1482,15 +1491,15 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1638,22 +1647,8 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
paginatedInternational.map((
|
||||
entry,
|
||||
) {
|
||||
final FromDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['created_on'],
|
||||
),
|
||||
);
|
||||
final ToDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['FirstTravelDt'],
|
||||
),
|
||||
);
|
||||
final FromDate = entry['created_on'];
|
||||
final ToDate = entry['FirstTravelDt'];
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -1786,23 +1781,44 @@ class _AdvancePurchaseState extends State<AdvancePurchase>
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildTitle(bool isDesktop) {
|
||||
// return // Left side: Breadcrumb inside a Container (optional)
|
||||
// Container(
|
||||
// child: BreadcrumbNavigation(
|
||||
// isDesktop: isDesktop,
|
||||
// breadcrumbItems: [
|
||||
// BreadcrumbItem(
|
||||
// title: 'Report List',
|
||||
// tooltip: 'Go To Report List',
|
||||
// onTap: (context) {
|
||||
// // Navigator.pushNamed(context, '/report');
|
||||
// context.go("/report");
|
||||
// },
|
||||
// ),
|
||||
// BreadcrumbItem(title: 'Advance Purchase'),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildTitle(bool isDesktop) {
|
||||
return // Left side: Breadcrumb inside a Container (optional)
|
||||
Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Advance Purchase'),
|
||||
],
|
||||
),
|
||||
return Row(
|
||||
children: [
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Advance Purchase'),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -420,7 +420,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
false) ||
|
||||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['sector']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage1 = 0;
|
||||
@ -469,7 +469,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
false) ||
|
||||
(object['flight_class']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(formattedDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['flight_travel_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['sector']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage2 = 0;
|
||||
@ -626,24 +626,24 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -859,7 +859,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -868,15 +868,15 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1024,14 +1024,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
paginatedDomestic.map((
|
||||
entry,
|
||||
) {
|
||||
final TravelDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['flight_travel_date'],
|
||||
),
|
||||
);
|
||||
final TravelDate = entry['flight_travel_date'];
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -1394,7 +1387,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
filteredInternational.isEmpty)
|
||||
? Center(
|
||||
child: Text(
|
||||
"No data found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
@ -1544,14 +1537,7 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
paginatedInternational.map((
|
||||
entry,
|
||||
) {
|
||||
final TravelDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['flight_travel_date'],
|
||||
),
|
||||
);
|
||||
final TravelDate = entry['flight_travel_date'];
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -1682,23 +1668,44 @@ class _MISairState extends State<MISair> with SingleTickerProviderStateMixin {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildTitle(bool isDesktop) {
|
||||
// return // Left side: Breadcrumb inside a Container (optional)
|
||||
// Container(
|
||||
// child: BreadcrumbNavigation(
|
||||
// isDesktop: isDesktop,
|
||||
// breadcrumbItems: [
|
||||
// BreadcrumbItem(
|
||||
// title: 'Report List',
|
||||
// tooltip: 'Go To Report List',
|
||||
// onTap: (context) {
|
||||
// // Navigator.pushNamed(context, '/report');
|
||||
// context.go("/report");
|
||||
// },
|
||||
// ),
|
||||
// BreadcrumbItem(title: 'MIS Air Report '),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildTitle(bool isDesktop) {
|
||||
return // Left side: Breadcrumb inside a Container (optional)
|
||||
Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Air Report '),
|
||||
],
|
||||
),
|
||||
return Row(
|
||||
children: [
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Air Report '),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -518,24 +518,24 @@ class _MISforexState extends State<MISforex>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -660,7 +660,7 @@ class _MISforexState extends State<MISforex>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -669,15 +669,15 @@ class _MISforexState extends State<MISforex>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -822,22 +822,22 @@ class _MISforexState extends State<MISforex>
|
||||
paginatedInternational.map((
|
||||
entry,
|
||||
) {
|
||||
final forexFromDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['forex_from_date'],
|
||||
),
|
||||
);
|
||||
final forexToDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['forex_to_date'],
|
||||
),
|
||||
);
|
||||
final forexFromDate = entry['forex_from_date'];
|
||||
// DateFormat(
|
||||
// 'dd-MM-yyyy',
|
||||
// ).format(
|
||||
// DateTime.parse(
|
||||
// entry['forex_from_date'],
|
||||
// ),
|
||||
// );
|
||||
final forexToDate = entry['forex_to_date'];
|
||||
// DateFormat(
|
||||
// 'dd-MM-yyyy',
|
||||
// ).format(
|
||||
// DateTime.parse(
|
||||
// entry['forex_to_date'],
|
||||
// ),
|
||||
// );
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -973,22 +973,23 @@ class _MISforexState extends State<MISforex>
|
||||
}
|
||||
|
||||
Widget buildTitle(bool isDesktop) {
|
||||
return // Left side: Breadcrumb inside a Container (optional)
|
||||
Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Forex Report '),
|
||||
],
|
||||
),
|
||||
return Row(
|
||||
children: [
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Forex Report '),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -429,8 +429,8 @@ class _MIShotelState extends State<MIShotel>
|
||||
false) ||
|
||||
(object['hotel_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(newToDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['check_in_date'].toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['check_out_date'].toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['hotel_city']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage1 = 0;
|
||||
@ -487,8 +487,8 @@ class _MIShotelState extends State<MIShotel>
|
||||
false) ||
|
||||
(object['hotel_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(newFromDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(newToDate.toLowerCase().contains(lowerQuery)) ||
|
||||
(object['check_in_date']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['check_out_date'].toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(object['hotel_city']?.toLowerCase().contains(lowerQuery));
|
||||
}).toList();
|
||||
currentPage2 = 0;
|
||||
@ -644,24 +644,24 @@ class _MIShotelState extends State<MIShotel>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -877,7 +877,7 @@ class _MIShotelState extends State<MIShotel>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -886,15 +886,15 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1002,18 +1002,18 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Sector',
|
||||
style:
|
||||
GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
// DataColumn(
|
||||
// label: Text(
|
||||
// 'Sector',
|
||||
// style:
|
||||
// GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight:
|
||||
// FontWeight
|
||||
// .w600,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Hotel Name',
|
||||
@ -1067,22 +1067,9 @@ class _MIShotelState extends State<MIShotel>
|
||||
paginatedDomestic.map((
|
||||
entry,
|
||||
) {
|
||||
final checkInDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['check_in_date'],
|
||||
),
|
||||
);
|
||||
final checkOutDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['check_out_date'],
|
||||
),
|
||||
);
|
||||
final checkInDate = entry['check_in_date'];
|
||||
|
||||
final checkOutDate = entry['check_out_date'];
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
@ -1140,15 +1127,15 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
'${entry['sector']}',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
12,
|
||||
),
|
||||
),
|
||||
),
|
||||
// DataCell(
|
||||
// Text(
|
||||
// '${entry['sector']}',
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize:
|
||||
// 12,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataCell(
|
||||
Text(
|
||||
'${entry['hotel_name']}',
|
||||
@ -1234,7 +1221,7 @@ class _MIShotelState extends State<MIShotel>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1243,15 +1230,15 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1497,7 +1484,7 @@ class _MIShotelState extends State<MIShotel>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1506,15 +1493,15 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1622,18 +1609,18 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Sector',
|
||||
style:
|
||||
GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
// DataColumn(
|
||||
// label: Text(
|
||||
// 'Sector',
|
||||
// style:
|
||||
// GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight:
|
||||
// FontWeight
|
||||
// .w600,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Hotel Name',
|
||||
@ -1687,22 +1674,22 @@ class _MIShotelState extends State<MIShotel>
|
||||
paginatedInternational.map((
|
||||
entry,
|
||||
) {
|
||||
final checkInDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['check_in_date'],
|
||||
),
|
||||
);
|
||||
final checkOutDate =
|
||||
DateFormat(
|
||||
'dd-MM-yyyy',
|
||||
).format(
|
||||
DateTime.parse(
|
||||
entry['check_out_date'],
|
||||
),
|
||||
);
|
||||
final checkInDate = entry['check_in_date'];
|
||||
// DateFormat(
|
||||
// 'dd-MM-yyyy',
|
||||
// ).format(
|
||||
// DateTime.parse(
|
||||
// entry['check_in_date'],
|
||||
// ),
|
||||
// );
|
||||
final checkOutDate = entry['check_out_date'];
|
||||
// DateFormat(
|
||||
// 'dd-MM-yyyy',
|
||||
// ).format(
|
||||
// DateTime.parse(
|
||||
// entry['check_out_date'],
|
||||
// ),
|
||||
// );
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
@ -1759,15 +1746,15 @@ class _MIShotelState extends State<MIShotel>
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
'${entry['sector']}',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize:
|
||||
12,
|
||||
),
|
||||
),
|
||||
),
|
||||
// DataCell(
|
||||
// Text(
|
||||
// '${entry['sector']}',
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize:
|
||||
// 12,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DataCell(
|
||||
Text(
|
||||
'${entry['hotel_name']}',
|
||||
@ -1853,22 +1840,24 @@ class _MIShotelState extends State<MIShotel>
|
||||
|
||||
Widget buildTitle(bool isDesktop) {
|
||||
return // Left side: Breadcrumb inside a Container (optional)
|
||||
Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
Row(
|
||||
children: [
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Hotel Report '),
|
||||
],
|
||||
),
|
||||
BreadcrumbItem(title: 'MIS Hotel Report '),
|
||||
],
|
||||
),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildExports(bool isDesktop) {
|
||||
|
||||
@ -567,24 +567,24 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
// const SizedBox(height: 10),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -800,7 +800,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -809,15 +809,15 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -971,7 +971,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
MediaQuery.of(context).size.height / 4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -980,15 +980,15 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1234,7 +1234,7 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
4,
|
||||
),
|
||||
Text(
|
||||
"No Records Found",
|
||||
"No Matches Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1243,15 +1243,15 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
"Please ensure the selected dates contain report data.",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black54,
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// "Please ensure the selected dates contain report data.",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Colors.black54,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1403,23 +1403,44 @@ class _ServicesAnalysisState extends State<ServicesAnalysis>
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildTitle(bool isDesktop) {
|
||||
// return // Left side: Breadcrumb inside a Container (optional)
|
||||
// Container(
|
||||
// child: BreadcrumbNavigation(
|
||||
// isDesktop: isDesktop,
|
||||
// breadcrumbItems: [
|
||||
// BreadcrumbItem(
|
||||
// title: 'Report List',
|
||||
// tooltip: 'Go To Report List',
|
||||
// onTap: (context) {
|
||||
// // Navigator.pushNamed(context, '/report');
|
||||
// context.go("/report");
|
||||
// },
|
||||
// ),
|
||||
// BreadcrumbItem(title: 'Service Analysis Report'),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildTitle(bool isDesktop) {
|
||||
return // Left side: Breadcrumb inside a Container (optional)
|
||||
Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Service Analysis Report'),
|
||||
],
|
||||
),
|
||||
return Row(
|
||||
children: [
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Report List',
|
||||
tooltip: 'Go To Report List',
|
||||
onTap: (context) {
|
||||
// Navigator.pushNamed(context, '/report');
|
||||
context.go("/report");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Service Analysis Report'),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -50,6 +50,7 @@ class TravellerDataState extends State<TravellerData> {
|
||||
String? userId;
|
||||
int? travellerDataId;
|
||||
late String isActive = "1";
|
||||
bool isDisable = false;
|
||||
|
||||
List<String> dataHeader = ["first_name", "last_name", "email", "mobile"];
|
||||
|
||||
@ -183,9 +184,12 @@ class TravellerDataState extends State<TravellerData> {
|
||||
userId = await getUserId();
|
||||
|
||||
setState(() {
|
||||
isDisable = true;
|
||||
// This triggers UI rebuild with error messages
|
||||
if (validateData()) {
|
||||
postTravellerData();
|
||||
}else{
|
||||
isDisable = false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -244,22 +248,38 @@ class TravellerDataState extends State<TravellerData> {
|
||||
print("Update - Response: ${response.body}");
|
||||
_clearError();
|
||||
widget.fetchGetTraveller();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
break;
|
||||
|
||||
case 201:
|
||||
print("Save - Response: ${response.body}");
|
||||
_clearError();
|
||||
await widget.fetchGetTraveller();
|
||||
Navigator.of(context).pop();
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop(); // Close modal only if mounted
|
||||
}
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
print("Failed to submit traveller. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
setState(() {
|
||||
isDisable = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -530,10 +550,21 @@ class TravellerDataState extends State<TravellerData> {
|
||||
// ),
|
||||
SizedBox(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSubmit();
|
||||
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;
|
||||
// });
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: widget.layoutColor,
|
||||
|
||||
@ -132,6 +132,7 @@ class TravellerListState extends State<TravellerList> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchGetTraveller() async {
|
||||
// return [];
|
||||
String? ordId = await getOrgId();
|
||||
final String apiUrlData =
|
||||
'$apiUrl/api/travellers?for=table_view&org_id=$ordId';
|
||||
@ -293,7 +294,7 @@ class TravellerListState extends State<TravellerList> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -463,6 +464,7 @@ class TravellerListState extends State<TravellerList> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureTraveller,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureTraveller == null) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
@ -478,9 +480,9 @@ class TravellerListState extends State<TravellerList> {
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Traveller Available ",
|
||||
"No Data Found ",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -489,15 +491,15 @@ class TravellerListState extends State<TravellerList> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Traveller Details",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Traveller Details",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -840,7 +842,7 @@ class TravellerListState extends State<TravellerList> {
|
||||
filteredTraveller.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -855,7 +857,7 @@ class TravellerListState extends State<TravellerList> {
|
||||
filteredTraveller.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No matches found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -112,6 +112,7 @@ class _CreateTravelAgentFormDetialsState
|
||||
List<String> dataHeader = [
|
||||
"Fname",
|
||||
"Lname",
|
||||
"Cname",
|
||||
"email",
|
||||
"password",
|
||||
"dob",
|
||||
@ -148,6 +149,7 @@ class _CreateTravelAgentFormDetialsState
|
||||
Map<String, dynamic> data = {
|
||||
"first_name": controllers["Fname"]?.text,
|
||||
"last_name": controllers["Lname"]?.text,
|
||||
"company_name": controllers["Cname"]?.text,
|
||||
"email": controllers["email"]?.text,
|
||||
"password": controllers["password"]?.text,
|
||||
"mobile_no": controllers["mobileNumber"]?.text,
|
||||
@ -212,6 +214,7 @@ class _CreateTravelAgentFormDetialsState
|
||||
userIdApi = apiselectedUser?["user_id"] ?? "";
|
||||
controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? "";
|
||||
controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? "";
|
||||
controllers["Cname"]?.text = apiselectedUser?["company_name"] ?? "";
|
||||
controllers["email"]?.text = apiselectedUser?["email"] ?? "";
|
||||
controllers["password"]?.text = "";
|
||||
// controllers["password"]?.text = apiselectedUser?["password"] ?? "";
|
||||
@ -1251,7 +1254,7 @@ class _CreateTravelAgentFormDetialsState
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
|
||||
@ -142,6 +142,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
// }
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
// return [];
|
||||
orgId = await getOrgId();
|
||||
final String apiUrlData = '$apiUrl/api/agentList?org_id=$orgId&for=table_view';
|
||||
final String? token = await getToken();
|
||||
@ -355,6 +356,8 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
false) ||
|
||||
(user['last_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(user['company_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(user['email']?.toLowerCase().contains(lowerQuery) ?? false) ||
|
||||
(user['role_value']?.toLowerCase().contains(lowerQuery) ??
|
||||
false);
|
||||
@ -456,7 +459,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Organization Settings',
|
||||
title: 'Org Settings',
|
||||
tooltip: 'Go To Organization Settings',
|
||||
onTap: (context) {
|
||||
context.go("/OrganizationSettings");
|
||||
@ -490,7 +493,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
controller: searchController,
|
||||
onChanged: filterUsers,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a Travel Agent",
|
||||
hintText: "Search ...",
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF9E9DBD),
|
||||
@ -626,6 +629,10 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureUsers,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (futureUsers == null) {
|
||||
return CircularProgressIndicator();
|
||||
}
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError ||
|
||||
@ -647,7 +654,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No Travel Agent Found",
|
||||
textAlign: TextAlign.center,
|
||||
@ -658,15 +665,15 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create Travel Agent",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create Travel Agent",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -714,6 +721,15 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Company Name',
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Text(
|
||||
'Email ID',
|
||||
@ -768,6 +784,15 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
user['company_name'] ?? '',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
user['email'] ?? '',
|
||||
@ -1014,6 +1039,25 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"${user['company_name'] ?? ''}",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black87,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
|
||||
// Name
|
||||
Row(
|
||||
@ -1093,7 +1137,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
filteredUsers.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No Travel Agents found",
|
||||
"No Travel Agents Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1108,7 +1152,7 @@ class _TravelAgentListScreenState extends State<TravelAgentListScreen> {
|
||||
filteredUsers.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No Travel Agents found",
|
||||
"No Data Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -104,6 +104,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
List<String> dataHeader = [
|
||||
"Fname",
|
||||
"Lname",
|
||||
"Cname",
|
||||
"email",
|
||||
"password",
|
||||
"dob",
|
||||
@ -351,8 +352,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
SizedBox(height: 10),
|
||||
_buildFirstRow(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
_buildSecondRow(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
// _buildSecondRow(widget.isDesktop),
|
||||
// SizedBox(height: 10),
|
||||
_buildThirdRow(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
_buildForthRow(widget.isDesktop),
|
||||
@ -586,6 +587,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
Spacer(),
|
||||
buildLastNameField(),
|
||||
Spacer(), // Space after Last Name
|
||||
buildCompanyNameField(),
|
||||
Spacer(),
|
||||
// buildGenderField(),
|
||||
// SizedBox(width: 15),
|
||||
// buildDobField(),
|
||||
@ -601,6 +604,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
SizedBox(height: 8), // Vertical space
|
||||
buildLastNameField(),
|
||||
SizedBox(height: 8),
|
||||
buildCompanyNameField(),
|
||||
SizedBox(height: 8),
|
||||
// buildGenderField(),
|
||||
// SizedBox(height: 8),
|
||||
// buildDobField(),
|
||||
@ -612,33 +617,33 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSecondRow(bool isDesktop) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
child:
|
||||
widget.isDesktop
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// buildEmailField(),
|
||||
// Spacer(),
|
||||
// buildMobileField(),
|
||||
// Spacer(), // Space after Last Name
|
||||
// buildAlternateMobileField(),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// buildMobileField(),
|
||||
// SizedBox(height: 8), // Vertical space
|
||||
// buildAlternateMobileField(),
|
||||
// SizedBox(height: 8),
|
||||
// buildEmailField(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
// Widget _buildSecondRow(bool isDesktop) {
|
||||
// return Container(
|
||||
// color: Colors.white,
|
||||
// child:
|
||||
// widget.isDesktop
|
||||
// ? Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // buildEmailField(),
|
||||
// // Spacer(),
|
||||
// // buildMobileField(),
|
||||
// // Spacer(), // Space after Last Name
|
||||
// // buildAlternateMobileField(),
|
||||
// ],
|
||||
// )
|
||||
// : Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// // buildMobileField(),
|
||||
// // SizedBox(height: 8), // Vertical space
|
||||
// // buildAlternateMobileField(),
|
||||
// // SizedBox(height: 8),
|
||||
// // buildEmailField(),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget _buildThirdRow(bool isDesktop) {
|
||||
void _openPopup() {
|
||||
@ -668,11 +673,15 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
children: [
|
||||
if (!widget.apiselectedUser) ...[
|
||||
buildEmailField(),
|
||||
// SizedBox(
|
||||
// width: MediaQuery.of(context).size.width * 0.014,
|
||||
// ),
|
||||
Spacer(),
|
||||
buildPassword(),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.015,
|
||||
),
|
||||
// SizedBox(
|
||||
// width: MediaQuery.of(context).size.width * 0.015,
|
||||
// ),
|
||||
Spacer(),
|
||||
buildRole(),
|
||||
],
|
||||
],
|
||||
@ -797,6 +806,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: focusStates["FnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -848,6 +858,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: focusStates["LnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -879,6 +890,43 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildCompanyNameField() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Company Name",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: focusStates["CnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["CnameFocusNode"],
|
||||
controller: widget.controllers["Cname"],
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
labelText: "CompanyName",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildGenderField() {
|
||||
List<String> genderOptions = ["Male", "Female"];
|
||||
|
||||
@ -1109,11 +1157,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["emailFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["emailFocusNode"],
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: widget.controllers["email"],
|
||||
onChanged: (value) {
|
||||
@ -1159,13 +1208,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["mobileNumberFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["mobileNumberFocusNode"],
|
||||
controller: widget.controllers["mobileNumber"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1221,12 +1271,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["alternateMobileFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width: MediaQuery.of(context).size.width * 0.12,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["alternateMobileFocusNode"],
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: widget.controllers["alternateMobile"],
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1297,11 +1349,25 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["countryFocusNode"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: focusNodes["countryFocusNode"],
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
focusStates["countryFocused"] = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
focusNodes["countryFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
enabled: !widget.isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
@ -1365,8 +1431,33 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["countryFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
// width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["countryFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
// width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 1),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0,),
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1416,7 +1507,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
countryCode,
|
||||
); // ✅ send code not name
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1437,11 +1528,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["postalCodeFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["postalCodeFocusNode"],
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: widget.controllers["postalCode"],
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1586,11 +1678,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["addressFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["addressFocusNode"],
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: widget.controllers["address"],
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1628,11 +1721,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["passwordFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
focusNode: focusNodes["passwordFocusNode"],
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: widget.controllers["password"],
|
||||
enabled: !widget.isViewMode,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
|
||||
import 'package:bcrypt/bcrypt.dart';
|
||||
@ -59,6 +58,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
String? userIdApi;
|
||||
|
||||
bool isCheckingToken = false;
|
||||
bool isDisable = false;
|
||||
|
||||
String? token;
|
||||
|
||||
@ -912,17 +912,17 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
// }
|
||||
|
||||
// valid_from: 20-06-2025, valid_upto: 19-06-2025
|
||||
DateTime? valid_from = data?['valid_from'];
|
||||
DateTime? valid_upto = data?['valid_upto'];
|
||||
DateTime? validFrom = data['valid_from'];
|
||||
DateTime? validUpto = data['valid_upto'];
|
||||
|
||||
if (valid_from != null &&
|
||||
valid_upto != null &&
|
||||
valid_from.toString().isNotEmpty &&
|
||||
valid_upto.toString().isNotEmpty) {
|
||||
if (validFrom != null &&
|
||||
validUpto != null &&
|
||||
validFrom.toString().isNotEmpty &&
|
||||
validUpto.toString().isNotEmpty) {
|
||||
try {
|
||||
final format = DateFormat("dd-MM-yyyy");
|
||||
final checkValidFrom = format.parse("$valid_from");
|
||||
final checkValidUpto = format.parse("$valid_upto");
|
||||
final checkValidFrom = format.parse("$validFrom");
|
||||
final checkValidUpto = format.parse("$validUpto");
|
||||
|
||||
if (checkValidUpto.isBefore(checkValidFrom)) {
|
||||
// return "valid upto cannot be earlier than valid from";
|
||||
@ -1157,7 +1157,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
print("Response body: ${response.body}");
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
dispose();
|
||||
// dispose();
|
||||
print("✅ User submitted successfully!");
|
||||
|
||||
print("📨 Response: ${response.body}");
|
||||
@ -1334,52 +1334,65 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Text(
|
||||
// apiselectedUser != null ? "Profile" : "Create New User",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: isDesktop ? 15 : 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Colors.black,
|
||||
// ),
|
||||
// ), //old very first
|
||||
|
||||
// if (isEditProfile)
|
||||
// IconButton(
|
||||
// icon: Icon(
|
||||
// Icons.edit,
|
||||
// color: Color(0xFF114D8B),
|
||||
// size: 14,
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// setState(() {
|
||||
// isViewMode = !isViewMode;
|
||||
// });
|
||||
// })
|
||||
Row(
|
||||
children: [
|
||||
apiselectedUser != null
|
||||
? Text(
|
||||
"Profile",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: isDesktop ? 15 : 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
if (apiselectedUser != null)
|
||||
isEditProfile
|
||||
? Text( "Profile",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: isDesktop ? 15 : 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
)
|
||||
: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Users',
|
||||
tooltip: 'Go To Users',
|
||||
onTap: (context) {
|
||||
context.go("/listUser");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Edit User'),
|
||||
],
|
||||
)
|
||||
else
|
||||
BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Users',
|
||||
tooltip: 'Go To Users',
|
||||
onTap: (context) {
|
||||
context.go("/listUser");
|
||||
},
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
child: BreadcrumbNavigation(
|
||||
isDesktop: isDesktop,
|
||||
breadcrumbItems: [
|
||||
BreadcrumbItem(
|
||||
title: 'Users',
|
||||
tooltip: 'Go To Users',
|
||||
onTap: (context) {
|
||||
context.go("/listUser");
|
||||
},
|
||||
),
|
||||
BreadcrumbItem(title: 'Create New User'),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Text(
|
||||
// apiselectedUser != null ? "Profile" : "Create New User",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: isDesktop ? 15 : 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Colors.black,
|
||||
// ),
|
||||
// ),
|
||||
// if (isEditProfile)
|
||||
// IconButton(
|
||||
// icon: Icon(
|
||||
// Icons.edit,
|
||||
// color: Color(0xFF114D8B),
|
||||
// size: 14,
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// setState(() {
|
||||
// isViewMode = !isViewMode;
|
||||
// });
|
||||
// })
|
||||
BreadcrumbItem(title: 'Create User'),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
|
||||
@ -8,6 +8,7 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../../../services/apiService.dart';
|
||||
import '../../../utils/auth_utils.dart';
|
||||
import '../../../widgets/custom_user_form.dart';
|
||||
|
||||
class OfficeDetails extends StatefulWidget {
|
||||
@ -108,6 +109,28 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
DateTime? _selectedEndDate;
|
||||
html.File? passportFile;
|
||||
|
||||
final FocusNode _employeeCodeFocusNode = FocusNode();
|
||||
final FocusNode _departmentFocusNode = FocusNode();
|
||||
final FocusNode _groupFocusNode = FocusNode();
|
||||
final FocusNode _firstApprovalFocusNode = FocusNode();
|
||||
final FocusNode _secondApprovalFocusNode = FocusNode();
|
||||
final FocusNode _thirdApprovalFocusNode = FocusNode();
|
||||
final FocusNode _exceptionalApprovalFocusNode = FocusNode();
|
||||
final FocusNode _delegateFocusNode = FocusNode();
|
||||
final FocusNode _startDateFocusNode = FocusNode();
|
||||
final FocusNode _endDateFocusNode = FocusNode();
|
||||
|
||||
bool _employeeCodeFocus = false;
|
||||
bool _departmentFocus = false;
|
||||
bool _groupFocus = false;
|
||||
bool _firstApprovalFocus = false;
|
||||
bool _secondApprovalFocus = false;
|
||||
bool _thirdApprovalFocus = false;
|
||||
bool _exceptionalApprovalFocus = false;
|
||||
bool _delegateFocus = false;
|
||||
bool _startDateFocus = false;
|
||||
bool _endDateFocus = false;
|
||||
|
||||
List<String> dataHeader = [
|
||||
"Fname",
|
||||
"Lname",
|
||||
@ -137,12 +160,16 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
"changePassword",
|
||||
];
|
||||
|
||||
Color? layoutColor;
|
||||
// Color? layoutColor;
|
||||
Color layoutColor = Colors.grey;
|
||||
Color? bodyColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
apiCountryData = null;
|
||||
apiUserData = null;
|
||||
apiCostData = null;
|
||||
@ -162,6 +189,44 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
fetchDepartment();
|
||||
fetchUsers();
|
||||
fetchFindGroup();
|
||||
|
||||
|
||||
_addFocusListener(_employeeCodeFocusNode, (focus) => _employeeCodeFocus = focus);
|
||||
_addFocusListener(_departmentFocusNode, (focus) => _departmentFocus = focus);
|
||||
_addFocusListener(_groupFocusNode, (focus) => _groupFocus = focus);
|
||||
_addFocusListener(_firstApprovalFocusNode, (focus) => _firstApprovalFocus = focus);
|
||||
_addFocusListener(_secondApprovalFocusNode, (focus) => _secondApprovalFocus = focus);
|
||||
_addFocusListener(_thirdApprovalFocusNode, (focus) => _thirdApprovalFocus = focus);
|
||||
_addFocusListener(_exceptionalApprovalFocusNode, (focus) => _exceptionalApprovalFocus = focus);
|
||||
_addFocusListener(_delegateFocusNode, (focus) => _delegateFocus = focus);
|
||||
_addFocusListener(_startDateFocusNode, (focus) => _startDateFocus = focus);
|
||||
_addFocusListener(_endDateFocusNode, (focus) => _endDateFocus = focus);
|
||||
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor =
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> apiCheckDuplicate(
|
||||
@ -459,14 +524,14 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _employeeCodeFocus,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: _employeeCodeFocusNode,
|
||||
controller: widget.controllers["employeeCode"],
|
||||
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("employee_code");
|
||||
@ -585,10 +650,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _departmentFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _departmentFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_departmentFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_departmentFocusNode?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: departmentMap[selectedDepartment],
|
||||
enabled: !widget.isViewMode,
|
||||
@ -619,8 +698,32 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_departmentFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_departmentFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -653,7 +756,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
departmentId,
|
||||
); // Send department_id
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -718,10 +821,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
// ),
|
||||
// ),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _groupFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _groupFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_groupFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_groupFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiAllGroups == null
|
||||
? Center(
|
||||
@ -774,8 +891,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_groupFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_groupFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -805,6 +945,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
); // ✅ pass the ID not the name
|
||||
},
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -834,10 +975,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _firstApprovalFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _firstApprovalFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_firstApprovalFocus= hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_firstApprovalFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiUserData == null
|
||||
? Center(
|
||||
@ -884,10 +1039,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
}).toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_firstApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_firstApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -934,6 +1110,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
); // ✅ not newValue, but approverId
|
||||
},
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -970,10 +1147,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _secondApprovalFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _secondApprovalFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_secondApprovalFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_secondApprovalFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiUserData == null
|
||||
? Center(
|
||||
@ -1020,10 +1211,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
}).toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_secondApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_secondApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1069,6 +1281,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
// });
|
||||
// },
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1105,10 +1318,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _thirdApprovalFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _thirdApprovalFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_thirdApprovalFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_thirdApprovalFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiUserData == null
|
||||
? Center(
|
||||
@ -1155,10 +1382,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
}).toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_thirdApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_thirdApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1203,6 +1451,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
// });
|
||||
// },
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1239,10 +1488,24 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _exceptionalApprovalFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _exceptionalApprovalFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_exceptionalApprovalFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_exceptionalApprovalFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiUserData == null
|
||||
? Center(
|
||||
@ -1289,10 +1552,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
}).toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_exceptionalApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_exceptionalApprovalFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.white), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1326,6 +1610,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
); // ✅ not newValue, but approverId
|
||||
},
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1363,10 +1648,23 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _delegateFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _delegateFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_delegateFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
_delegateFocusNode?.requestFocus();
|
||||
},
|
||||
child:
|
||||
apiUserData == null
|
||||
? Center(
|
||||
@ -1417,10 +1715,31 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
}).toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_delegateFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_delegateFocus ?? false)
|
||||
? (layoutColor ?? Colors.white)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1467,6 +1786,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
); // ✅ not newValue, but approverId
|
||||
},
|
||||
),
|
||||
),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1544,7 +1864,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: _startDateFocus,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -1554,10 +1874,12 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
//
|
||||
// },
|
||||
onTap: () async {
|
||||
_startDateFocusNode?.requestFocus();
|
||||
await _selectCheckOutDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _startDateFocusNode,
|
||||
controller: widget.controllers["delegationStartDate"],
|
||||
readOnly: true,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
@ -1649,7 +1971,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : null,
|
||||
isFocused: false,
|
||||
isFocused: _endDateFocus,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -1659,10 +1981,12 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
//
|
||||
// },
|
||||
onTap: () async {
|
||||
_endDateFocusNode?.requestFocus();
|
||||
await _selectForexEndDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _endDateFocusNode,
|
||||
controller: widget.controllers["delegationEndDate"],
|
||||
readOnly: true,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
|
||||
@ -75,6 +75,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
String? get selectedGenderValue => selectedGender;
|
||||
String? get selectedCountryValue => selectedCountry;
|
||||
String? get selectedRoleValue => selectedRole;
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
|
||||
late bool isTravelAgent = false;
|
||||
|
||||
@ -135,7 +137,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
apiCountryData = null;
|
||||
|
||||
apiRoleData = null;
|
||||
@ -150,6 +154,19 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
|
||||
print("selectedServiceIdsAPI - $selectedServiceIds");
|
||||
|
||||
for (var field in dataHeader) {
|
||||
focusNodes["${field}FocusNode"] = FocusNode();
|
||||
focusStates["${field}Focused"] = false;
|
||||
}
|
||||
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadAllServices();
|
||||
getOrganizationData();
|
||||
@ -157,6 +174,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
});
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> apiCheckDuplicate(
|
||||
String label,
|
||||
String field,
|
||||
@ -777,13 +802,15 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["FnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["FnameFocusNode"],
|
||||
controller: widget.controllers["Fname"],
|
||||
autofocus: true,
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("first_name");
|
||||
@ -827,12 +854,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["LnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["LnameFocusNode"],
|
||||
autofocus: true,
|
||||
controller: widget.controllers["Lname"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -977,10 +1006,22 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.12
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["genderFocused"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: focusNodes["genderFocusNode"],
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
focusStates["genderFocused"] = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
focusNodes["genderFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: selectedGender,
|
||||
enabled: !widget.isViewMode,
|
||||
@ -990,51 +1031,51 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
itemBuilder:
|
||||
// (context, item, isSelected) => Container(
|
||||
// color: Colors.white,
|
||||
// padding: EdgeInsets.symmetric(
|
||||
// horizontal: 10,
|
||||
// vertical: 6,
|
||||
// ),
|
||||
// child: Text(
|
||||
// item,
|
||||
// style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
// ),
|
||||
// ),
|
||||
(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: 6.0,
|
||||
),
|
||||
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),
|
||||
|
||||
),
|
||||
],
|
||||
(context, item, isSelected) => Container(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
);},
|
||||
),
|
||||
items: genderOptions,
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["genderFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["genderFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
// vertical: 1,
|
||||
),
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1058,7 +1099,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
});
|
||||
widget.onGenderChanged?.call(newValue);
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1111,7 +1152,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.12
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["dobFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -1125,11 +1166,16 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
setState(() {
|
||||
// errorMessages.remove("start_date");
|
||||
});
|
||||
// Delay re-focusing the DOB field slightly
|
||||
Future.delayed(Duration(milliseconds: 100), () {
|
||||
focusNodes["dobFocusNode"]?.requestFocus();
|
||||
});
|
||||
}
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
controller: widget.controllers["dob"],
|
||||
focusNode: focusNodes["dobFocusNode"],
|
||||
readOnly: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
@ -1171,12 +1217,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["emailFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["emailFocusNode"],
|
||||
autofocus: true,
|
||||
controller: widget.controllers["email"],
|
||||
onChanged: (value) {
|
||||
_clearError("email");
|
||||
@ -1221,12 +1269,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["mobileNumberFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["mobileNumberFocusNode"],
|
||||
autofocus: true,
|
||||
controller: widget.controllers["mobileNumber"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1282,12 +1332,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["alternateMobileFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["alternateMobileFocusNode"],
|
||||
autofocus: true,
|
||||
controller: widget.controllers["alternateMobile"],
|
||||
enabled: !widget.isViewMode,
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
@ -1356,10 +1408,24 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["countryFocused"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: focusNodes["countryFocusNode"],
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
focusStates["countryFocused"] = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
focusNodes["countryFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1389,8 +1455,33 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["countryFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["countryFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 8.0),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1440,7 +1531,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
countryCode,
|
||||
); // ✅ send code not name
|
||||
},
|
||||
),
|
||||
),),)
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1461,12 +1552,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["postalCodeFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["postalCodeFocusNode"],
|
||||
controller: widget.controllers["postalCode"],
|
||||
enabled: !widget.isViewMode,
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
@ -1579,10 +1672,24 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["roleIdFocusNode"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: focusNodes["roleIdFocusNode"],
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
focusStates["roleIdFocused"] = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
focusNodes["roleIdFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: selectedRole != null ? roleMap[selectedRole] : null,
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1613,8 +1720,36 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
items: roleNames,
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["roleIdFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["roleIdFocused"] ?? false)
|
||||
? (layoutColor ?? Colors.blue)
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color : (layoutColor ?? Colors.grey), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1647,7 +1782,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
widget.onUserTypeChanged?.call(isTravelAgent);
|
||||
widget.onRoleChanged?.call(selectedKey);
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
if (widget.errorMessages["role_id"] != null) ...[
|
||||
@ -1675,12 +1810,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["addressFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
child: TextField(
|
||||
autofocus: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["addressFocusNode"],
|
||||
controller: widget.controllers["address"],
|
||||
enabled: !widget.isViewMode,
|
||||
maxLines: 2,
|
||||
@ -1717,12 +1854,14 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["passwordFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["passwordFocusNode"],
|
||||
autofocus: true,
|
||||
controller: widget.controllers["password"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
|
||||
@ -91,6 +91,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
late List<dynamic>? apiAirlineCountryData;
|
||||
Map<String, dynamic>? apiData;
|
||||
final Map<String, TextEditingController> controllers = {};
|
||||
Map<String, FocusNode> focusNodes = {};
|
||||
Map<String, bool> focusStates = {};
|
||||
|
||||
bool _iSeatPrefFocus = false;
|
||||
bool _dSeatPrefFocus = false;
|
||||
|
||||
final FocusNode _iSeatPrefFocusNode = FocusNode();
|
||||
final FocusNode _dSeatPrefFocusNode = FocusNode();
|
||||
|
||||
final List<String> headers = [
|
||||
"Passport Details",
|
||||
@ -126,6 +134,11 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
"forex_expiry_date",
|
||||
];
|
||||
|
||||
// Color? layoutColor;
|
||||
Color layoutColor = Colors.grey;
|
||||
Color? bodyColor;
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -138,7 +151,18 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
apiData = null;
|
||||
for (var field in dataHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
focusNodes["${field}FocusNode"] = FocusNode();
|
||||
focusStates["${field}Focused"] = false;
|
||||
}
|
||||
|
||||
for (var key in focusNodes.keys) {
|
||||
_addFocusListener(focusNodes[key]!, (focus) {
|
||||
setState(() {
|
||||
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fetchCountries();
|
||||
fetchHotels();
|
||||
loadCountryList();
|
||||
@ -149,9 +173,46 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
addHotelLoyaltyEntry();
|
||||
addFrequentFlierEntry();
|
||||
addVisaEntry();
|
||||
loadInitialData();
|
||||
});
|
||||
updateTravel();
|
||||
});
|
||||
|
||||
_addFocusListener(_iSeatPrefFocusNode, (focus) => _iSeatPrefFocus = focus);
|
||||
_addFocusListener(_dSeatPrefFocusNode, (focus) => _dSeatPrefFocus = focus);
|
||||
// _addFocusListener(_groupFocusNode, (focus) => _groupFocus = focus);
|
||||
// _addFocusListener(_firstApprovalFocusNode, (focus) => _firstApprovalFocus = focus);
|
||||
// _addFocusListener(_secondApprovalFocusNode, (focus) => _secondApprovalFocus = focus);
|
||||
// _addFocusListener(_thirdApprovalFocusNode, (focus) => _thirdApprovalFocus = focus);
|
||||
// _addFocusListener(_exceptionalApprovalFocusNode, (focus) => _exceptionalApprovalFocus = focus);
|
||||
// _addFocusListener(_delegateFocusNode, (focus) => _delegateFocus = focus);
|
||||
// _addFocusListener(_startDateFocusNode, (focus) => _startDateFocus = focus);
|
||||
// _addFocusListener(_endDateFocusNode, (focus) => _endDateFocus = focus);
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
updateState(node.hasFocus);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor =
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
bool boolValidation() {
|
||||
@ -1145,12 +1206,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["FnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["FnameFocusNode"],
|
||||
controller: controllers["Fname"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1187,12 +1250,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["MnameFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["MnameFocusNode"],
|
||||
controller: controllers["Mname"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1229,7 +1293,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["LnameFocused"] ?? false,
|
||||
width:
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.13
|
||||
@ -1239,6 +1303,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["LnameFocusNode"],
|
||||
controller: controllers["Lname"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1280,7 +1345,9 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["nationality"] ?? false,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
width:
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.13
|
||||
@ -1288,6 +1355,18 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Focus(
|
||||
focusNode: focusNodes["nationalityFocusNode"],
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
focusStates["nationalityFocused"] = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
focusNodes["nationalityFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: _selectedTripType,
|
||||
enabled: !widget.isViewMode,
|
||||
@ -1312,8 +1391,36 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
items: nationalityOptions,
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["nationalityFocused"] ?? false)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(focusStates["nationalityFocused"] ?? false)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
// : const Color(0xFFD6D5E6),
|
||||
width: 1,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color : (layoutColor), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
@ -1337,7 +1444,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
print("_selectedTripType - $_selectedTripType");
|
||||
});
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1358,12 +1465,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["passportNumberFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["passportNumberFocusNode"],
|
||||
controller: controllers["passportNumber"],
|
||||
enabled: !widget.isViewMode,
|
||||
inputFormatters: [
|
||||
@ -1421,12 +1529,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["placeOfIssueFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["placeOfIssueFocusNode"],
|
||||
controller: controllers["placeOfIssue"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -1504,7 +1613,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.13
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["dateOfIssueFocused"] ?? false,
|
||||
// isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -1519,10 +1629,12 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
// errorMessages.remove("start_date");
|
||||
});
|
||||
}
|
||||
focusNodes["dateOfIssueFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
controller: controllers["dateOfIssue"],
|
||||
focusNode: focusNodes["dateOfIssueFocusNode"],
|
||||
readOnly: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
@ -1638,7 +1750,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["dateOfExpiryFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
width:
|
||||
widget.isDesktop
|
||||
@ -1657,10 +1770,12 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
// errorMessages.remove("start_date");
|
||||
});
|
||||
}
|
||||
focusNodes["dateOfExpiryFocusNode"]?.requestFocus();
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
controller: controllers["dateOfExpiry"],
|
||||
focusNode: focusNodes["dateOfExpiryFocusNode"],
|
||||
readOnly: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
@ -2388,11 +2503,24 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.15
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: _dSeatPrefFocus,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _dSeatPrefFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_dSeatPrefFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_dSeatPrefFocusNode.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: false,
|
||||
@ -2416,7 +2544,31 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_dSeatPrefFocus)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_dSeatPrefFocus)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor), width: 0.5),
|
||||
),
|
||||
hintText: "Select Seat",
|
||||
hintStyle: GoogleFonts.poppins(
|
||||
fontSize: 11.5,
|
||||
@ -2463,7 +2615,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -2488,12 +2640,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.15
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["d_meal_prefFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
focusNode: focusNodes["d_meal_prefFocusNode"],
|
||||
controller: controllers["d_meal_pref"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
@ -2532,13 +2685,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
width:
|
||||
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["d_additonal_InfoFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: controllers["d_additonal_Info"],
|
||||
focusNode: focusNodes["d_additonal_InfoFocusNode"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("d_additonal_Info");
|
||||
@ -2625,11 +2779,23 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.15
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: _iSeatPrefFocus,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
width: double.infinity,
|
||||
child: Focus(
|
||||
focusNode: _iSeatPrefFocusNode,
|
||||
onFocusChange: (hasFocus) {
|
||||
setState(() {
|
||||
_iSeatPrefFocus = hasFocus;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
// Request focus when user taps
|
||||
_iSeatPrefFocusNode.requestFocus();
|
||||
},
|
||||
child: DropdownSearch<Map<String, dynamic>>(
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: false,
|
||||
@ -2651,12 +2817,36 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
);
|
||||
},
|
||||
),
|
||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
// border: InputBorder.none,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_iSeatPrefFocus)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color:
|
||||
(_iSeatPrefFocus)
|
||||
? (layoutColor)
|
||||
: Colors.white,
|
||||
width: 0.5,
|
||||
// const Color(0xFFD6D5E6),
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color : (layoutColor), width: 0.5),
|
||||
),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 5,
|
||||
vertical: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -2701,7 +2891,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),),),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -2726,13 +2916,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.15
|
||||
: null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["i_meal_prefFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: controllers["i_meal_pref"],
|
||||
focusNode: focusNodes["i_meal_prefFocusNode"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("i_meal_pref");
|
||||
@ -2770,13 +2961,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
width:
|
||||
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
|
||||
isFocused: false,
|
||||
isFocused: focusStates["i_additonal_InfoFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: controllers["i_additonal_Info"],
|
||||
focusNode: focusNodes["i_additonal_InfoFocusNode"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("i_additonal_Info");
|
||||
@ -2821,13 +3013,15 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
// isFocused: false,
|
||||
isFocused: focusStates["emergency_contactFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: controllers["emergency_contact"],
|
||||
focusNode: focusNodes["emergency_contactFocusNode"],
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
|
||||
],
|
||||
@ -2900,13 +3094,14 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["forex_card_numFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
controller: controllers["forex_card_num"],
|
||||
focusNode: focusNodes["forex_card_numFocusNode"],
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
|
||||
LengthLimitingTextInputFormatter(16),
|
||||
@ -2988,7 +3183,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
isFocused: false,
|
||||
isFocused: focusStates["forex_expiry_dateFocused"] ?? false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -2997,11 +3192,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
widget.isViewMode
|
||||
? null
|
||||
: () async {
|
||||
focusNodes["forex_expiry_dateFocusNode"]?.requestFocus();
|
||||
await _selectExpiryDate(context);
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
controller: controllers["forex_expiry_date"],
|
||||
focusNode: focusNodes["forex_expiry_dateFocusNode"],
|
||||
readOnly: true,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
|
||||
@ -144,6 +144,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
// }
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
// return [];
|
||||
orgId = await getOrgId();
|
||||
final String apiUrlData = '$apiUrl/api/users?org_id=$orgId&for=table_view';
|
||||
final String? token = await getToken();
|
||||
@ -767,7 +768,8 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
return
|
||||
ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
bool isDesktop =
|
||||
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
@ -795,6 +797,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
// if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
// const Expanded(child: Center(child: Text("User Page Content"))),
|
||||
Expanded(child: buildGroupList(isDesktop)),
|
||||
// child: buildGroupList(isDesktop),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -872,7 +875,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
controller: searchController,
|
||||
onChanged: filterUsers,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a User",
|
||||
hintText: "Search ...",
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF9E9DBD),
|
||||
@ -896,6 +899,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade300,
|
||||
// color: layoutColor!,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
@ -1082,9 +1086,10 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<dynamic>>(
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureUsers,
|
||||
builder: (context, snapshot) {
|
||||
final adjHgt = MediaQuery.of(context).size.height;
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError ||
|
||||
@ -1106,9 +1111,10 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.redAccent),
|
||||
// ),
|
||||
const SizedBox(height: 8),
|
||||
// const SizedBox(height: 8),
|
||||
SizedBox(height: adjHgt / 4),
|
||||
Text(
|
||||
"No User Found",
|
||||
"No Data Found",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 20,
|
||||
@ -1117,15 +1123,15 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Text(
|
||||
"Please Create User",
|
||||
textAlign: TextAlign.center,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
// Text(
|
||||
// "Please Create User",
|
||||
// textAlign: TextAlign.center,
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 16,
|
||||
// color: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -1732,7 +1738,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
filteredUsers.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No users found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
@ -1747,7 +1753,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
filteredUsers.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No users found",
|
||||
"No Matches Found",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
color: Colors.grey,
|
||||
|
||||
@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'dart:html' as html;
|
||||
import 'package:frontend/config/apiUrl.dart'; // 1 newly added
|
||||
import 'package:frontend/services/apiService.dart';
|
||||
@ -31,7 +31,7 @@ class _MyAppState extends State<MyApp> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here
|
||||
SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here - for testing uncomment this
|
||||
if (kIsWeb) {
|
||||
final uri = Uri.parse(html.window.location.href);
|
||||
print("URI - $uri");
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
//api url
|
||||
const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be';
|
||||
// const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be';
|
||||
// const String apiUrl = 'http://localhost/tstat_be';
|
||||
// const String apiUrl = 'https://uat.tripapprovaltool.com/tstat_be';
|
||||
const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be'; // look at this
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../utils/auth_utils.dart';
|
||||
|
||||
class CustomTextFieldUserWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
@ -27,6 +28,28 @@ class CustomTextFieldUserWrapper extends StatefulWidget {
|
||||
|
||||
class _CustomTextFieldUserWrapperState
|
||||
extends State<CustomTextFieldUserWrapper> {
|
||||
Color? layoutColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -41,9 +64,9 @@ class _CustomTextFieldUserWrapperState
|
||||
// color: Color(0xFFF7F7FB),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
|
||||
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 2.0 : 0.5,
|
||||
width: widget.isFocused ? 1.0 : 0.5,
|
||||
),
|
||||
// boxShadow:
|
||||
// widget.isFocused
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../utils/auth_utils.dart';
|
||||
|
||||
class CustomTextFieldUserTravellerWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
@ -27,6 +28,27 @@ class CustomTextFieldUserTravellerWrapper extends StatefulWidget {
|
||||
|
||||
class _CustomTextFieldUserTravellerWrapperState
|
||||
extends State<CustomTextFieldUserTravellerWrapper> {
|
||||
Color? layoutColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadInitialData();
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
});
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -40,20 +62,21 @@ class _CustomTextFieldUserTravellerWrapperState
|
||||
// color: Color(0xFFF7F7FB),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
|
||||
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 2.0 : 0.5,
|
||||
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 1.0 : 0.5,
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
// boxShadow: widget.isFocused
|
||||
// ? [
|
||||
// BoxShadow(
|
||||
// color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
// blurRadius: 10,
|
||||
// spreadRadius: 2,
|
||||
// offset: Offset(0, 4),
|
||||
// ),
|
||||
// ]
|
||||
// : [],
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
|
||||
@ -110,6 +110,13 @@ flutter:
|
||||
- assets/images/IconsImg/upload.png
|
||||
- assets/images/IconsImg/download.png
|
||||
- assets/images/IconsImg/planPdf_icon.png
|
||||
- assets/images/IconsImg/Domestic.png
|
||||
- assets/images/IconsImg/Domestic_old.png
|
||||
- assets/images/IconsImg/Domestic_new.png
|
||||
- assets/images/IconsImg/International.png
|
||||
- assets/images/IconsImg/International_old.png
|
||||
- assets/images/IconsImg/International_new.png
|
||||
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
|
||||
Loading…
Reference in New Issue
Block a user