july 1 to july 15

This commit is contained in:
venba-Inspriron-3558 2025-07-15 15:58:38 +05:30
parent 3be15a71ef
commit 35229fa755
60 changed files with 4133 additions and 1120 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -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,

View File

@ -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,

View File

@ -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(

View File

@ -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,

View 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(),
),
],
),
),
),
],
),
),
),
),
);
},
),
),
),
);
},
);
}
}

View File

@ -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)),
],
),
],
),
);
},

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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) ...[

View File

@ -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(

View File

@ -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) {

View File

@ -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) ...[

View File

@ -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(

View File

@ -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(

View File

@ -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(() {

View File

@ -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
},

View File

@ -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
},

View File

@ -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),
],
),
),

View File

@ -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");

View File

@ -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");

View File

@ -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(

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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)),
),
),

View File

@ -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)]}",
);
});
}

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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'),
],
),
],
);
}

View File

@ -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 '),
],
),
],
);
}

View File

@ -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 '),
],
),
],
);
}

View File

@ -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) {

View File

@ -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'),
],
),
],
);
}

View File

@ -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,

View File

@ -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,

View File

@ -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");

View File

@ -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,

View File

@ -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,

View File

@ -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),

View File

@ -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),

View File

@ -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) {

View File

@ -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(

View File

@ -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,

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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,
);

View File

@ -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