OrgLevel Data, UserDetails
This commit is contained in:
parent
1f28816021
commit
886bef2baa
@ -4,23 +4,27 @@ import 'package:flutter/material.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../data/models/Searchtraveller.dart';
|
||||
import '../../data/models/searchUser.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_text_traveller.dart';
|
||||
|
||||
class UserSelectionDialog extends StatefulWidget{
|
||||
class UserSelectionDialog extends StatefulWidget {
|
||||
final String title;
|
||||
final void Function(String,String, bool) onSubmit;
|
||||
final void Function(String, String, bool) onSubmit;
|
||||
|
||||
UserSelectionDialog({Key? key, required this.title, required this.onSubmit,}) : super(key: key);
|
||||
UserSelectionDialog({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.onSubmit,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_UserSelectionDialogState createState() => _UserSelectionDialogState();
|
||||
}
|
||||
|
||||
class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
TextEditingController _controller = TextEditingController();
|
||||
TextEditingController _searchController = TextEditingController();
|
||||
// List<String> _filteredUsers = [];
|
||||
@ -31,21 +35,23 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
List<Map<String, dynamic>> _filteredList = [];
|
||||
List<SearchTraveler> _filteredTraveller = [];
|
||||
String userIdSelected = " ";
|
||||
bool isTraveller = false;
|
||||
|
||||
bool isTraveller = false;
|
||||
bool _showTravellerForm = false;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
String? orgId;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
|
||||
Future<void> fetchUsers() async {
|
||||
final String apiUrldata = '$apiUrl/api/users';
|
||||
orgId = await getOrgId();
|
||||
// final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
|
||||
final String apiUrldata = '$apiUrl/api/users?org_id=$orgId';
|
||||
|
||||
try {
|
||||
final token = await getToken();
|
||||
@ -62,8 +68,7 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String,dynamic> responseBody = json.decode(response.body);
|
||||
|
||||
final Map<String, dynamic> responseBody = json.decode(response.body);
|
||||
|
||||
print("API Response: $responseBody"); // Debugging
|
||||
|
||||
@ -88,18 +93,69 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
for (var user in _users) {
|
||||
print("${user.firstName} ${user.lastName}");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
|
||||
throw Exception(
|
||||
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load users. Status Code: ${response.statusCode}');
|
||||
throw Exception(
|
||||
'Failed to load users. Status Code: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching users: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchTraveller() async {
|
||||
orgId = await getOrgId();
|
||||
final String apiUrldata = '$apiUrl/api/travellers?org_id=$orgId';
|
||||
|
||||
try {
|
||||
final token = await getToken();
|
||||
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',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String, dynamic> responseBody = json.decode(response.body);
|
||||
|
||||
print("API Response: $responseBody"); // Debugging
|
||||
|
||||
if (responseBody.containsKey('data') && responseBody['data'] is List) {
|
||||
List<dynamic> travellerList = responseBody['data'];
|
||||
|
||||
setState(() {
|
||||
_traveller = travellerList
|
||||
.map((user) => SearchTraveler.fromJson(user))
|
||||
.toList();
|
||||
_filteredTraveller = List.from(_traveller);
|
||||
});
|
||||
|
||||
print("Users fetched: ${_users.length}");
|
||||
for (var travvelr in _traveller) {
|
||||
print("${travvelr.firstName} ${travvelr.lastName}");
|
||||
}
|
||||
} else {
|
||||
throw Exception(
|
||||
"Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
|
||||
}
|
||||
} else {
|
||||
throw Exception(
|
||||
'Failed to load users. Status Code: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching traveller: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void _filterUsers1(String query) {
|
||||
print("Filtering users...");
|
||||
setState(() {
|
||||
@ -115,7 +171,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
return searchFields
|
||||
.any((field) => field.contains(query.toLowerCase()));
|
||||
}).toList();
|
||||
}
|
||||
});
|
||||
@ -126,7 +183,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _filterUsers(String query) {
|
||||
print("Filtering _filterUsersTravellers...");
|
||||
setState(() {
|
||||
@ -146,9 +202,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
user.mobileNo ?? "",
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
return searchFields
|
||||
.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((user) => {"type": "user", "data": user}),
|
||||
|
||||
];
|
||||
}
|
||||
});
|
||||
@ -156,7 +212,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
print("Filtered List:");
|
||||
for (var item in _filteredList) {
|
||||
var user = item["data"];
|
||||
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
print(
|
||||
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +225,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
if (query.isEmpty) {
|
||||
_filteredList = [
|
||||
..._users.map((user) => {"type": "user", "data": user}),
|
||||
..._traveller.map((traveller) => {"type": "traveller", "data": traveller}),
|
||||
..._traveller
|
||||
.map((traveller) => {"type": "traveller", "data": traveller}),
|
||||
];
|
||||
} else {
|
||||
_filteredList = [
|
||||
@ -180,9 +238,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
user.mobileNo ?? "",
|
||||
user.alternateMobileNo ?? ""
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
return searchFields
|
||||
.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((user) => {"type": "user", "data": user}),
|
||||
|
||||
..._traveller.where((traveller) {
|
||||
List<String> searchFields = [
|
||||
"${traveller.firstName} ${traveller.lastName}".toLowerCase(),
|
||||
@ -190,7 +248,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
traveller.travellerId.toLowerCase() ?? "",
|
||||
traveller.mobileNo ?? "",
|
||||
];
|
||||
return searchFields.any((field) => field.contains(query.toLowerCase()));
|
||||
return searchFields
|
||||
.any((field) => field.contains(query.toLowerCase()));
|
||||
}).map((traveller) => {"type": "traveller", "data": traveller}),
|
||||
];
|
||||
}
|
||||
@ -199,58 +258,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
print("Filtered List:");
|
||||
for (var item in _filteredList) {
|
||||
var user = item["data"];
|
||||
print("${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
print(
|
||||
"${item["type"].toUpperCase()} - ${user.firstName} ${user.lastName}");
|
||||
}
|
||||
}
|
||||
Future<void> fetchTraveller() async {
|
||||
final String apiUrldata = '$apiUrl/api/travellers';
|
||||
|
||||
try {
|
||||
final token = await getToken();
|
||||
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',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String,dynamic> responseBody = json.decode(response.body);
|
||||
|
||||
|
||||
print("API Response: $responseBody"); // Debugging
|
||||
|
||||
if (responseBody.containsKey('data') && responseBody['data'] is List) {
|
||||
List<dynamic> travellerList = responseBody['data'];
|
||||
|
||||
|
||||
setState(() {
|
||||
_traveller = travellerList.map((user) => SearchTraveler.fromJson(user)).toList();
|
||||
_filteredTraveller = List.from(_traveller);
|
||||
});
|
||||
|
||||
print("Users fetched: ${_users.length}");
|
||||
for (var travvelr in _traveller) {
|
||||
print("${travvelr.firstName} ${travvelr.lastName}");
|
||||
}
|
||||
|
||||
} else {
|
||||
throw Exception("Unexpected response format: Expected a List but got ${responseBody.runtimeType}");
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load users. Status Code: ${response.statusCode}');
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching traveller: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -268,7 +279,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
width: 400, // Adjust width as needed
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min, // Ensures content doesn't expand unnecessarily
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content doesn't expand unnecessarily
|
||||
children: [
|
||||
Text("Please Select User", style: TextStyle(fontSize: 14)),
|
||||
SizedBox(height: 10),
|
||||
@ -280,15 +292,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
setState(() {
|
||||
_showTravellerForm = false;
|
||||
});
|
||||
widget.title == "Others"? _filterUsersTravellers(query):
|
||||
_filterUsers(query);
|
||||
|
||||
widget.title == "Others"
|
||||
? _filterUsersTravellers(query)
|
||||
: _filterUsers(query);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a user",
|
||||
hintStyle: TextStyle(fontSize: 14),
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
border:
|
||||
OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
@ -301,7 +314,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("or create a new traveler", style: TextStyle(fontSize: 14, color: Color(0xFF575A74))),
|
||||
Text("or create a new traveler",
|
||||
style: TextStyle(fontSize: 14, color: Color(0xFF575A74))),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -309,7 +323,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
_searchController.clear();
|
||||
});
|
||||
},
|
||||
child: Text("Create", style: TextStyle(fontSize: 14, color: Colors.blueAccent)),
|
||||
child: Text("Create",
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: Colors.blueAccent)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -320,42 +336,51 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
// User List or Message
|
||||
_searchController.text.isNotEmpty
|
||||
? SizedBox(
|
||||
height: 300, // Limit height to avoid overflow
|
||||
// child: _filteredUsers.isEmpty
|
||||
child: _filteredList.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No users found",
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
// itemCount: _filteredUsers.length,
|
||||
itemCount: _filteredList.length,
|
||||
itemBuilder: (context, index) {
|
||||
// final user = _filteredUsers[index];
|
||||
height: 300, // Limit height to avoid overflow
|
||||
// child: _filteredUsers.isEmpty
|
||||
child: _filteredList.isEmpty
|
||||
? Center(
|
||||
child: Text(
|
||||
"No users found",
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: Colors.grey),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
// itemCount: _filteredUsers.length,
|
||||
itemCount: _filteredList.length,
|
||||
itemBuilder: (context, index) {
|
||||
// final user = _filteredUsers[index];
|
||||
|
||||
final item = _filteredList[index];
|
||||
final user = item["data"]; // Extract user object
|
||||
final userType = item["type"]; // "user" or "traveller"
|
||||
final item = _filteredList[index];
|
||||
final user = item["data"]; // Extract user object
|
||||
final userType =
|
||||
item["type"]; // "user" or "traveller"
|
||||
|
||||
return ListTile(
|
||||
title: Text("${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
|
||||
subtitle: Text("ID: ${userType == "user" ? user.userId : user.travellerId}"),
|
||||
onTap: () {
|
||||
String selectedUser = "${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
|
||||
setState(() {
|
||||
_searchController.text = selectedUser;
|
||||
userIdSelected = userType == "user" ? user.userId : user.travellerId;
|
||||
isTraveller = userType == "traveller";
|
||||
});
|
||||
print("Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
|
||||
" isTraveller: $userIdSelected");
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
) : SizedBox.shrink(),
|
||||
return ListTile(
|
||||
title: Text(
|
||||
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
|
||||
subtitle: Text(
|
||||
"ID: ${userType == "user" ? user.userId : user.travellerId}"),
|
||||
onTap: () {
|
||||
String selectedUser =
|
||||
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
|
||||
setState(() {
|
||||
_searchController.text = selectedUser;
|
||||
userIdSelected = userType == "user"
|
||||
? user.userId
|
||||
: user.travellerId;
|
||||
isTraveller = userType == "traveller";
|
||||
});
|
||||
print(
|
||||
"Selected: $selectedUser, ID: ${userType == "user" ? user.userId : user.travellerId},"
|
||||
" isTraveller: $userIdSelected");
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: SizedBox.shrink(),
|
||||
|
||||
// Traveler Form
|
||||
if (_showTravellerForm)
|
||||
@ -366,13 +391,16 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: TravelerForm(
|
||||
formKey: _formKey,
|
||||
onSubmit: (String fullName, String travellerId, bool isTraveller) {
|
||||
widget.onSubmit(fullName, travellerId,isTraveller); // Pass the data up
|
||||
onSubmit: (String fullName, String travellerId,
|
||||
bool isTraveller) {
|
||||
widget.onSubmit(fullName, travellerId,
|
||||
isTraveller); // Pass the data up
|
||||
},
|
||||
firstNameController: TextEditingController(),
|
||||
lastNameController: TextEditingController(),
|
||||
emailController: TextEditingController(),
|
||||
mobileController: TextEditingController(),
|
||||
orgId: orgId,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -392,7 +420,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text("Cancel",),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
@ -406,8 +436,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
print("Submitting: ${_searchController.text}, ID: $userIdSelected");
|
||||
widget.onSubmit(_searchController.text,userIdSelected,isTraveller);
|
||||
print(
|
||||
"Submitting: ${_searchController.text}, ID: $userIdSelected");
|
||||
widget.onSubmit(
|
||||
_searchController.text, userIdSelected, isTraveller);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text("Submit"),
|
||||
@ -419,7 +451,6 @@ class _UserSelectionDialogState extends State<UserSelectionDialog>{
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TravelerForm extends StatefulWidget {
|
||||
@ -427,25 +458,24 @@ class TravelerForm extends StatefulWidget {
|
||||
final TextEditingController lastNameController;
|
||||
final TextEditingController emailController;
|
||||
final TextEditingController mobileController;
|
||||
final String? orgId;
|
||||
final GlobalKey<FormState> formKey;
|
||||
final void Function(String, String, bool) onSubmit;
|
||||
|
||||
TravelerForm({
|
||||
required this.formKey,
|
||||
required this.firstNameController,
|
||||
required this.lastNameController,
|
||||
required this.emailController,
|
||||
required this.mobileController,
|
||||
required this.onSubmit
|
||||
});
|
||||
TravelerForm(
|
||||
{required this.formKey,
|
||||
required this.orgId,
|
||||
required this.firstNameController,
|
||||
required this.lastNameController,
|
||||
required this.emailController,
|
||||
required this.mobileController,
|
||||
required this.onSubmit});
|
||||
|
||||
@override
|
||||
_TravelerFormState createState() => _TravelerFormState();
|
||||
}
|
||||
|
||||
class _TravelerFormState extends State<TravelerForm> {
|
||||
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
@ -472,27 +502,29 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Email is required';
|
||||
}
|
||||
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$').hasMatch(value)) {
|
||||
if (!RegExp(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
||||
.hasMatch(value)) {
|
||||
return 'Enter a valid email address';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _onSubmit(BuildContext context) {
|
||||
Future<void> _onSubmit(BuildContext context) async {
|
||||
bool isValid = _validateForm();
|
||||
print("Form Validation Result: $isValid");
|
||||
|
||||
if (isValid) {
|
||||
print("Validation Success");
|
||||
|
||||
_submitForm(context);
|
||||
} else {
|
||||
print("Validation Failed"); // This should now print if validation fails
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _submitForm(BuildContext context) async {
|
||||
Map<String, String> requestBody = {
|
||||
"org_id": widget.orgId!,
|
||||
"first_name": widget.firstNameController.text,
|
||||
"last_name": widget.lastNameController.text,
|
||||
"email": widget.emailController.text,
|
||||
@ -516,24 +548,24 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
body: jsonEncode(requestBody),
|
||||
);
|
||||
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
final Map<String, dynamic> responseData = jsonDecode(response.body); // Parse response
|
||||
if (responseData["success"] == true && responseData.containsKey("data")) {
|
||||
final Map<String, dynamic> responseData =
|
||||
jsonDecode(response.body); // Parse response
|
||||
if (responseData["success"] == true &&
|
||||
responseData.containsKey("data")) {
|
||||
final travellerData = responseData["data"];
|
||||
|
||||
String travellerId = travellerData["traveller_id"];
|
||||
String firstName = travellerData["first_name"];
|
||||
String lastName = travellerData["last_name"];
|
||||
|
||||
print("Traveller Added: ID: $travellerId, Name: $firstName $lastName");
|
||||
print(
|
||||
"Traveller Added: ID: $travellerId, Name: $firstName $lastName");
|
||||
|
||||
// Pass data to callback
|
||||
widget.onSubmit("$firstName $lastName", travellerId, true);
|
||||
|
||||
|
||||
// Close the dialog
|
||||
Navigator.pop(context);
|
||||
}
|
||||
@ -542,10 +574,11 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Traveller added successfully!",
|
||||
style: TextStyle(color: Colors.white), // ✅ Set text color
|
||||
"Traveller added successfully!",
|
||||
style: TextStyle(color: Colors.white), // ✅ Set text color
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
backgroundColor: Colors.green,),
|
||||
);
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
@ -564,7 +597,8 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
double widthFactor;
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
bool isDesktop =
|
||||
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
if (sizingInfo.deviceScreenType == DeviceScreenType.desktop) {
|
||||
widthFactor = 0.23;
|
||||
@ -599,7 +633,8 @@ class _TravelerFormState extends State<TravelerForm> {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => _onSubmit(context),
|
||||
child: Text("Add", style: TextStyle(color: Colors.blueAccent)),
|
||||
child:
|
||||
Text("Add", style: TextStyle(color: Colors.blueAccent)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -6,15 +6,16 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class AccomodationScreen extends StatefulWidget {
|
||||
|
||||
final Function(bool) onClose; // Callback function
|
||||
final Function(Map<String,dynamic>) onSaveAccomadation;
|
||||
final Function(Map<String, dynamic>) onSaveAccomadation;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
|
||||
AccomodationScreen({
|
||||
required this.onClose, required this.onSaveAccomadation, required this.selectedItem, required this.loginUser});
|
||||
AccomodationScreen(
|
||||
{required this.onClose,
|
||||
required this.onSaveAccomadation,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_AccomodationScreenState createState() => _AccomodationScreenState();
|
||||
@ -31,7 +32,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
final FocusNode _checkOutTimeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
|
||||
late TextEditingController _destinationController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
late TextEditingController _checkInController = TextEditingController();
|
||||
@ -59,11 +59,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get accomadationData {
|
||||
|
||||
Map<String,dynamic> data ={
|
||||
Map<String, dynamic> data = {
|
||||
"destination_city": _destinationController.text,
|
||||
"hotel_name": _hotelNameController.text,
|
||||
"checkin_date": _checkInController.text ,
|
||||
"checkin_date": _checkInController.text,
|
||||
"checkin_time": _checkInTimeController.text,
|
||||
"checkout_date": _checkOutController.text,
|
||||
"checkout_time": _checkOutTimeController.text,
|
||||
@ -73,9 +72,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["accomodation_id"] != null && widget.selectedItem?["accomodation_id"] != 0) {
|
||||
} else if (widget.selectedItem?["accomodation_id"] != null &&
|
||||
widget.selectedItem?["accomodation_id"] != 0) {
|
||||
data["accomodation_id"] = widget.selectedItem!["accomodation_id"];
|
||||
}
|
||||
}
|
||||
@ -86,16 +87,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocused = focus);
|
||||
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(
|
||||
_destinationFocusNode, (focus) => _destinationFocused = focus);
|
||||
_addFocusListener(
|
||||
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
|
||||
_addFocusListener(_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
|
||||
_addFocusListener(
|
||||
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
|
||||
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
|
||||
_addFocusListener(_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
|
||||
_addFocusListener(
|
||||
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
@ -106,7 +110,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
_checkOutTimeController = initController("checkout_time");
|
||||
_commentsController = initController("comments");
|
||||
|
||||
|
||||
_destinationController.addListener(() => _clearError("destination_city"));
|
||||
_hotelNameController.addListener(() => _clearError("hotel_name"));
|
||||
_checkInController.addListener(() => _clearError("checkin_date"));
|
||||
@ -115,8 +118,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
_checkOutTimeController.addListener(() => _clearError("checkout_time"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_destinationFocusNode.dispose();
|
||||
@ -137,12 +138,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["destination_city", "hotel_name","checkin_date","checkin_time","checkout_date",
|
||||
"checkout_time"];
|
||||
List<String> requiredFields = [
|
||||
"destination_city",
|
||||
"hotel_name",
|
||||
"checkin_date",
|
||||
"checkin_time",
|
||||
"checkout_date",
|
||||
"checkout_time"
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -154,25 +162,22 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save accomadationData $accomadationData");
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save accomadationData $accomadationData");
|
||||
|
||||
Map<String,dynamic> data = accomadationData;
|
||||
Map<String, dynamic> data = accomadationData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSaveAccomadation(accomadationData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -201,10 +206,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Text("Accomodation Booking",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -245,11 +251,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
isDesktop
|
||||
? Row(
|
||||
children: _buildThirdRow(isDesktop),
|
||||
)
|
||||
children: _buildThirdRow(isDesktop),
|
||||
)
|
||||
: Column(
|
||||
children: _buildThirdRow(isDesktop),
|
||||
),
|
||||
children: _buildThirdRow(isDesktop),
|
||||
),
|
||||
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
@ -275,6 +281,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _destinationFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -291,14 +300,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["destination_city"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["destination_city"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -320,6 +329,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -336,15 +348,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
if (errorMessages["hotel_name"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["hotel_name"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["hotel_name"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["hotel_name"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -359,9 +370,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
|
||||
? _selectedCheckInDate!
|
||||
: today,
|
||||
initialDate:
|
||||
_selectedCheckInDate != null && _selectedCheckInDate!.isAfter(today)
|
||||
? _selectedCheckInDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
@ -395,7 +407,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
}
|
||||
//-------------------------------Check-In End
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -405,9 +416,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -417,7 +427,8 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
_checkOutController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
_checkOutController.text =
|
||||
DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -442,9 +453,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -483,14 +491,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["checkin_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkin_date"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["checkin_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkin_date"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -535,14 +543,14 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["checkin_time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkin_time"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["checkin_time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkin_time"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -566,7 +574,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
@ -586,17 +593,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["checkout_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkout_date"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["checkout_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
errorMessages["checkout_date"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -634,12 +640,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon:
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["checkout_time"] != null) ...[
|
||||
@ -671,7 +676,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
|
||||
@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class BusScreen extends StatefulWidget {
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>)onSaveBus;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
final Function(Map<String, dynamic>) onSaveBus;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
BusScreen({
|
||||
required this.onClose, this.apiData, required this.onSaveBus, required this.selectedItem, required this.loginUser});
|
||||
BusScreen(
|
||||
{required this.onClose,
|
||||
this.apiData,
|
||||
required this.onSaveBus,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_BusScreenState createState() => _BusScreenState();
|
||||
@ -52,27 +55,26 @@ class _BusScreenState extends State<BusScreen> {
|
||||
bool _timeFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
|
||||
Map<String, dynamic> get busData{
|
||||
Map<String,dynamic> data = {
|
||||
"from": _fromController.text,
|
||||
"to": _toController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _buscommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
Map<String, dynamic> get busData {
|
||||
Map<String, dynamic> data = {
|
||||
"from": _fromController.text,
|
||||
"to": _toController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
"comments": _buscommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["bus_id"] != null && widget.selectedItem?["bus_id"] != 0) {
|
||||
data["bus_id"] = widget.selectedItem!["bus_id"];
|
||||
}
|
||||
}
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["bus_id"] != null &&
|
||||
widget.selectedItem?["bus_id"] != 0) {
|
||||
data["bus_id"] = widget.selectedItem!["bus_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
@ -91,41 +93,39 @@ class _BusScreenState extends State<BusScreen> {
|
||||
});
|
||||
});
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
_fromFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_fromFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_fromFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_fromFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_toFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_toFocus = _toFocusNode.hasFocus;
|
||||
});
|
||||
_toFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_toFocus = _toFocusNode.hasFocus;
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_timeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_timeFocus = _timeFocusNode.hasFocus;
|
||||
});
|
||||
_timeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_timeFocus = _timeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
_buscommentsController = initController("comments");
|
||||
_fromController = initController("from");
|
||||
@ -137,7 +137,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
_toController.addListener(() => _clearError("to"));
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
|
||||
}
|
||||
|
||||
@override
|
||||
@ -153,7 +152,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -161,11 +159,12 @@ class _BusScreenState extends State<BusScreen> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["from", "to","date","time"];
|
||||
List<String> requiredFields = ["from", "to", "date", "time"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -177,26 +176,22 @@ class _BusScreenState extends State<BusScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save accomadationData $busData");
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save accomadationData $busData");
|
||||
|
||||
Map<String,dynamic> data = busData;
|
||||
Map<String, dynamic> data = busData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSaveBus(busData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -225,8 +220,10 @@ class _BusScreenState extends State<BusScreen> {
|
||||
),
|
||||
),
|
||||
Text("Bus Booking List",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -244,7 +241,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -258,7 +255,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
// ...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
@ -274,10 +270,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -290,14 +283,9 @@ class _BusScreenState extends State<BusScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
)
|
||||
|
||||
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop))
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -306,34 +294,32 @@ class _BusScreenState extends State<BusScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
String? selectedPurpose =
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
@ -341,37 +327,33 @@ class _BusScreenState extends State<BusScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedPurpose,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
} : null,
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -381,9 +363,8 @@ class _BusScreenState extends State<BusScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -419,7 +400,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -436,7 +416,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
child: TextField(
|
||||
focusNode: _fromFocusNode,
|
||||
controller: _fromController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
@ -446,7 +426,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -482,7 +461,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _toFocusNode,
|
||||
controller: _toController,
|
||||
@ -528,7 +506,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
@ -548,7 +525,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["date"] != null) ...[
|
||||
@ -596,12 +572,11 @@ class _BusScreenState extends State<BusScreen> {
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon:
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
@ -613,8 +588,6 @@ class _BusScreenState extends State<BusScreen> {
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -635,7 +608,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
@ -644,7 +617,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -662,7 +635,7 @@ class _BusScreenState extends State<BusScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
widget.onClose(false);// Close the dialog or screen
|
||||
widget.onClose(false); // Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -697,4 +670,4 @@ class _BusScreenState extends State<BusScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,17 +6,18 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class InsuranceScreen extends StatefulWidget {
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveInsurance;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
|
||||
InsuranceScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveInsurance,
|
||||
required this.selectedItem,required this.loginUser});
|
||||
InsuranceScreen(
|
||||
{required this.onClose,
|
||||
required this.apiData,
|
||||
required this.onSaveInsurance,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_InsuranceScreenState createState() => _InsuranceScreenState();
|
||||
@ -33,72 +34,79 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _startdateController = TextEditingController();
|
||||
late TextEditingController _endDateController = TextEditingController();
|
||||
late TextEditingController _insuranceCommentsController = TextEditingController();
|
||||
late TextEditingController _insuranceCommentsController =
|
||||
TextEditingController();
|
||||
|
||||
bool _isHotelNameFocused = false;
|
||||
bool _dateFocus = false;
|
||||
bool _commentsFocus = false;
|
||||
|
||||
|
||||
String? selectedTripType;
|
||||
String? selectedInsuranceType;
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String, dynamic> get InsuranceData{
|
||||
Map<String, dynamic> get InsuranceData {
|
||||
Map<String, dynamic> data = {
|
||||
|
||||
"type_of_insurance": selectedInsuranceType,
|
||||
"start_date": _startdateController.text,
|
||||
"end_date": _endDateController.text,
|
||||
"comments": _insuranceCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) {
|
||||
} else if (widget.selectedItem?["insurance_id"] != null &&
|
||||
widget.selectedItem?["insurance_id"] != 0) {
|
||||
data["insurance_id"] = widget.selectedItem!["insurance_id"];
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {_isHotelNameFocused = _hotelNameFocusNode.hasFocus;});});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {_dateFocus = _fromFocusNode.hasFocus;});});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {_commentsFocus = _commentsFocusNode.hasFocus;});});
|
||||
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
|
||||
_insuranceCommentsController =
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
_startdateController =
|
||||
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
|
||||
_endDateController =
|
||||
TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
|
||||
TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) {
|
||||
selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString();
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["type_of_insurance"] != null) {
|
||||
selectedInsuranceType =
|
||||
widget.selectedItem!["type_of_insurance"].toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
@ -107,15 +115,15 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["type_of_insurance", "start_date","end_date"];
|
||||
List<String> requiredFields = [
|
||||
"type_of_insurance",
|
||||
"start_date",
|
||||
"end_date"
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -127,22 +135,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save InsuranceData $InsuranceData");
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save InsuranceData $InsuranceData");
|
||||
|
||||
Map<String,dynamic> data = InsuranceData;
|
||||
Map<String, dynamic> data = InsuranceData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSaveInsurance(InsuranceData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
DateTime? _parseDate(String date) {
|
||||
@ -153,9 +159,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
@ -165,8 +168,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -195,8 +196,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
),
|
||||
),
|
||||
Text("Insurance Booking List",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -214,7 +217,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -228,7 +231,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
@ -244,10 +246,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -260,14 +259,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
)
|
||||
|
||||
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop))
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -276,81 +270,74 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList =
|
||||
widget.apiData?['insurance_type_of_insurance'] ?? [];
|
||||
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
selectedInsuranceType ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedInsuranceType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedInsuranceType = newValue;
|
||||
if (selectedInsuranceType!.isNotEmpty) {
|
||||
errorMessages.remove("type_of_insurance");
|
||||
}
|
||||
setState(() {
|
||||
selectedInsuranceType = newValue;
|
||||
if (selectedInsuranceType!.isNotEmpty) {
|
||||
errorMessages.remove("type_of_insurance");
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
print(selectedInsuranceType);
|
||||
|
||||
}
|
||||
print(selectedInsuranceType);
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -360,9 +347,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -372,21 +358,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
||||
setState(() {
|
||||
_selectedCheckOutDate = pickedDate;
|
||||
_startdateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
_startdateController.text =
|
||||
DateFormat('yyyy-MM-dd').format(pickedDate);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _selectEndCheckOutDate(BuildContext context) async {
|
||||
DateTime now = DateTime.now();
|
||||
DateTime today = DateTime(now.year, now.month, now.day);
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -402,7 +387,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -417,15 +401,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () async{
|
||||
onTap: () async {
|
||||
await _selectCheckOutDate(context);
|
||||
if(_startdateController.text.isNotEmpty){
|
||||
if (_startdateController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
errorMessages.remove("start_date"); // Removes the key completely
|
||||
errorMessages
|
||||
.remove("start_date"); // Removes the key completely
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -446,11 +433,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
if (errorMessages["start_date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
@ -458,7 +442,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -466,7 +450,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -481,9 +464,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
await _selectEndCheckOutDate(context);
|
||||
@ -492,9 +477,12 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
DateTime? startDate = _parseDate(_startdateController.text);
|
||||
DateTime? endDate = _parseDate(_endDateController.text);
|
||||
|
||||
if (startDate != null && endDate != null && endDate.isBefore(startDate)) {
|
||||
if (startDate != null &&
|
||||
endDate != null &&
|
||||
endDate.isBefore(startDate)) {
|
||||
setState(() {
|
||||
errorMessages["end_date"] = "End date cannot be earlier than start date";
|
||||
errorMessages["end_date"] =
|
||||
"End date cannot be earlier than start date";
|
||||
});
|
||||
} else {
|
||||
setState(() {
|
||||
@ -503,7 +491,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -521,7 +508,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["end_date"] != null) ...[
|
||||
@ -532,7 +518,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -541,8 +526,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -563,7 +546,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
@ -609,7 +592,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSave();
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
@ -625,4 +608,4 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,6 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class MiscellaneousScreen extends StatefulWidget {
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String, dynamic>) onSaveMiscellaneous;
|
||||
@ -14,9 +13,13 @@ class MiscellaneousScreen extends StatefulWidget {
|
||||
final int? selectedIndex;
|
||||
final String? loginUser;
|
||||
|
||||
MiscellaneousScreen({
|
||||
required this.onClose, required this.apiData, required this.onSaveMiscellaneous,
|
||||
this.selectedItem, this.selectedIndex,required this.loginUser});
|
||||
MiscellaneousScreen(
|
||||
{required this.onClose,
|
||||
required this.apiData,
|
||||
required this.onSaveMiscellaneous,
|
||||
this.selectedItem,
|
||||
this.selectedIndex,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_MiscellaneousScreenState createState() => _MiscellaneousScreenState();
|
||||
@ -49,14 +52,14 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
"comments": _commentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
|
||||
} else if (widget.selectedItem?["miscellaneous_id"] != null && widget.selectedItem?["miscellaneous_id"] != 0) {
|
||||
} else if (widget.selectedItem?["miscellaneous_id"] != null &&
|
||||
widget.selectedItem?["miscellaneous_id"] != 0) {
|
||||
data["miscellaneous_id"] = widget.selectedItem!["miscellaneous_id"];
|
||||
}
|
||||
}
|
||||
@ -64,9 +67,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -90,13 +90,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["special_request"] != null) {
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["special_request"] != null) {
|
||||
selectedSpecialType = widget.selectedItem!["special_request"].toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_tripTypeFocusNode.dispose();
|
||||
@ -105,16 +104,12 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["special_request", "comments"];
|
||||
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
@ -125,30 +120,26 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save miscellaneousData $miscellaneousData");
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save miscellaneousData $miscellaneousData");
|
||||
|
||||
Map<String,dynamic> data = miscellaneousData;
|
||||
Map<String, dynamic> data = miscellaneousData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSaveMiscellaneous(miscellaneousData); // Send object to parent
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
// Clear only if this is a new entry
|
||||
// if (widget.selectedItem == null) {
|
||||
// _commentsController.clear();
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -178,8 +169,10 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
),
|
||||
),
|
||||
Text("Miscellaneous Booking List",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -197,7 +190,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -205,9 +198,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
...buildResponsiveRow(_buildThirdRow(isDesktop)),
|
||||
@ -220,10 +211,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -236,14 +224,9 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
)
|
||||
|
||||
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop))
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -252,66 +235,64 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<dynamic> purposeList = widget.apiData?['miscellaneous_special_request'] ?? [];
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList =
|
||||
widget.apiData?['miscellaneous_special_request'] ?? [];
|
||||
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedSpecialType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
|
||||
|
||||
selectedSpecialType ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : "No options";
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedSpecialType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedSpecialType = newValue;
|
||||
});
|
||||
setState(() {
|
||||
selectedSpecialType = newValue;
|
||||
});
|
||||
|
||||
print(selectedSpecialType);
|
||||
|
||||
}
|
||||
print(selectedSpecialType);
|
||||
}
|
||||
: null,
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["special_request"] != null) ...[
|
||||
@ -324,8 +305,6 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildThirdRow(bool isDesktop) {
|
||||
return [
|
||||
Column(
|
||||
@ -343,7 +322,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
@ -352,7 +331,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -377,9 +356,8 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
|
||||
_commentsController.clear();
|
||||
widget.onClose(false);// Close the dialog or screen
|
||||
widget.onClose(false); // Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -398,7 +376,7 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSave();
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
@ -414,4 +392,4 @@ class _MiscellaneousScreenState extends State<MiscellaneousScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,13 +9,16 @@ import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
class TaxiScreen extends StatefulWidget {
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String,dynamic>) onSavetaxi;
|
||||
final Map<String,dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
final Function(Map<String, dynamic>) onSavetaxi;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
TaxiScreen({
|
||||
required this.onClose, this.apiData, required this.onSavetaxi,
|
||||
required this.selectedItem,required this.loginUser});
|
||||
TaxiScreen(
|
||||
{required this.onClose,
|
||||
this.apiData,
|
||||
required this.onSavetaxi,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_TaxiScreenState createState() => _TaxiScreenState();
|
||||
@ -55,11 +58,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
String? selectedReqTaxi;
|
||||
String? selectedCarType;
|
||||
|
||||
|
||||
Map<String , dynamic> get taxiData {
|
||||
Map<String, dynamic> data ={
|
||||
|
||||
|
||||
Map<String, dynamic> get taxiData {
|
||||
Map<String, dynamic> data = {
|
||||
"destination_city": _destinationController.text,
|
||||
"date": _dateController.text,
|
||||
"time": _timeController.text,
|
||||
@ -72,13 +72,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
"updated_by": widget.loginUser,
|
||||
// "updated_on": ,
|
||||
// "updated_by": ,
|
||||
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["taxi_id"] != null && widget.selectedItem?["taxi_id"] != 0) {
|
||||
} else if (widget.selectedItem?["taxi_id"] != null &&
|
||||
widget.selectedItem?["taxi_id"] != 0) {
|
||||
data["taxi_id"] = widget.selectedItem!["taxi_id"];
|
||||
}
|
||||
}
|
||||
@ -94,17 +95,16 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
|
||||
_addFocusListener(_destinationFocusNode, (focus) => _destinationFocus = focus);
|
||||
_addFocusListener(
|
||||
_destinationFocusNode, (focus) => _destinationFocus = focus);
|
||||
_addFocusListener(_locationFocusNode, (focus) => _locationFocus = focus);
|
||||
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
|
||||
_addFocusListener(_timeFocusNode, (focus) => _timeFocus = focus);
|
||||
_addFocusListener(_taxiReqFocusNode, (focus) => _taxiReqFocused = focus);
|
||||
_addFocusListener(_numPassengerFocusNode, (focus) => _numPassengerFocus = focus);
|
||||
_addFocusListener(
|
||||
_numPassengerFocusNode, (focus) => _numPassengerFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
_dateController = initController("date");
|
||||
_timeController = initController("time");
|
||||
@ -112,14 +112,15 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
_numPassengerController = initController("no_of_passengers");
|
||||
_taxiCommentsController = initController("comments");
|
||||
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["car_required_for"] != null) {
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["car_required_for"] != null) {
|
||||
selectedReqTaxi = widget.selectedItem!["car_required_for"].toString();
|
||||
}
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["car_type"] != null) {
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["car_type"] != null) {
|
||||
selectedCarType = widget.selectedItem!["car_type"].toString();
|
||||
}
|
||||
|
||||
@ -128,8 +129,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
_numPassengerController.addListener(() => _clearError("no_of_passengers"));
|
||||
|
||||
|
||||
}
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
@ -140,8 +139,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_destinationFocusNode.dispose();
|
||||
@ -153,7 +150,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -162,12 +158,17 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["destination_city", "location_of_pickup","no_of_passengers","date","time"];
|
||||
List<String> requiredFields = [
|
||||
"destination_city",
|
||||
"location_of_pickup",
|
||||
"no_of_passengers",
|
||||
"date",
|
||||
"time"
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -179,27 +180,22 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save taxiData $taxiData");
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save taxiData $taxiData");
|
||||
|
||||
Map<String,dynamic> data = taxiData;
|
||||
Map<String, dynamic> data = taxiData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSavetaxi(taxiData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -228,8 +224,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
Text("Taxi Booking List",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -247,7 +245,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -261,7 +259,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
...rowBuilders.expand((row) => buildResponsiveRow(row)),
|
||||
|
||||
@ -277,30 +274,29 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
List<dynamic> purposeList = widget.apiData?['taxt_car_type'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedCarType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
selectedCarType ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
Column(
|
||||
@ -314,12 +310,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
)
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop))
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -328,7 +321,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -345,13 +337,14 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
child: TextField(
|
||||
focusNode: _numPassengerFocusNode,
|
||||
controller: _numPassengerController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
|
||||
FilteringTextInputFormatter.allow(RegExp(
|
||||
r'^\d*\.?\d*$')), // Allow only positive numbers with optional decimal
|
||||
],
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Number of Passenger",
|
||||
@ -359,7 +352,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -371,7 +363,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -396,24 +387,23 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _toFocusNode, // Assign the correct focus node
|
||||
value: selectedCarType,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedCarType = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
}
|
||||
setState(() {
|
||||
selectedCarType = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
@ -428,73 +418,69 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiData?['taxi_car_required_for'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedReqTaxi ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
selectedReqTaxi ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _taxiReqFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _taxiReqFocusNode, // Assign the correct focus node
|
||||
value: selectedReqTaxi,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedReqTaxi = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
}
|
||||
setState(() {
|
||||
selectedReqTaxi = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -504,9 +490,8 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -542,7 +527,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -559,7 +543,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
child: TextField(
|
||||
focusNode: _destinationFocusNode,
|
||||
controller: _destinationController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
@ -569,19 +553,18 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -605,7 +588,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _locationFocusNode,
|
||||
controller: _locationController,
|
||||
@ -620,13 +602,13 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["location_of_pickup"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
if (errorMessages["location_of_pickup"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -651,7 +633,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
@ -671,17 +652,16 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -719,22 +699,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon:
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
@ -756,8 +735,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
controller: _taxiCommentsController,
|
||||
@ -783,7 +763,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
// Close Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
widget.onClose(false);// Close the dialog or screen
|
||||
widget.onClose(false); // Close the dialog or screen
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
@ -818,4 +798,4 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,15 +6,18 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class TrainScreen extends StatefulWidget {
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(Map<String, dynamic>)onSavetrain;
|
||||
final Function(Map<String, dynamic>) onSavetrain;
|
||||
final Function(bool) onClose;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
TrainScreen({
|
||||
required this.onClose, this.apiData, required this.onSavetrain, required this.selectedItem, required this.loginUser});
|
||||
TrainScreen(
|
||||
{required this.onClose,
|
||||
this.apiData,
|
||||
required this.onSavetrain,
|
||||
required this.selectedItem,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_TrainScreenState createState() => _TrainScreenState();
|
||||
@ -33,7 +36,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
final FocusNode _timeFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
|
||||
late TextEditingController _trainNoController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
late TextEditingController _fromController = TextEditingController();
|
||||
@ -54,11 +56,10 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
Map<String , dynamic> get trainData {
|
||||
Map<String, dynamic> data ={
|
||||
|
||||
"train_no": _trainNoController.text,
|
||||
"class": selectedClass,
|
||||
Map<String, dynamic> get trainData {
|
||||
Map<String, dynamic> data = {
|
||||
"train_no": _trainNoController.text,
|
||||
"class": selectedClass,
|
||||
"from_station": _fromController.text,
|
||||
"to_station": _toController.text,
|
||||
"date": _dateController.text,
|
||||
@ -69,9 +70,11 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["train_id"] != null && widget.selectedItem?["train_id"] != 0) {
|
||||
} else if (widget.selectedItem?["train_id"] != null &&
|
||||
widget.selectedItem?["train_id"] != 0) {
|
||||
data["train_id"] = widget.selectedItem!["train_id"];
|
||||
}
|
||||
}
|
||||
@ -83,47 +86,45 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
return TextEditingController(text: widget.selectedItem?[key] ?? "");
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_trainNoFocusNode.addListener(() {
|
||||
_trainNoFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_trainNoFocused = _trainNoFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
_hotelNameFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_isHotelNameFocused = _hotelNameFocusNode.hasFocus;
|
||||
});
|
||||
_fromFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_fromFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_fromFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_fromFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
_toFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_toFocus = _toFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_toFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_toFocus = _toFocusNode.hasFocus;
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_dateFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_dateFocus = _fromFocusNode.hasFocus;
|
||||
});
|
||||
_timeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_timeFocus = _timeFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_timeFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_timeFocus = _timeFocusNode.hasFocus;
|
||||
});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
});
|
||||
_commentsFocusNode.addListener(() {
|
||||
setState(() {
|
||||
_commentsFocus = _commentsFocusNode.hasFocus;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
_trainCommentsController = initController("comments");
|
||||
_trainNoController = initController("train_no");
|
||||
@ -134,7 +135,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["class"] != null) {
|
||||
selectedClass = widget.selectedItem!["class"].toString();
|
||||
selectedClass = widget.selectedItem!["class"].toString();
|
||||
}
|
||||
|
||||
_trainNoController.addListener(() => _clearError("train_no"));
|
||||
@ -142,10 +143,8 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
_toController.addListener(() => _clearError("to_station"));
|
||||
_dateController.addListener(() => _clearError("date"));
|
||||
_timeController.addListener(() => _clearError("time"));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_trainNoFocusNode.dispose();
|
||||
@ -159,7 +158,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -168,12 +166,18 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["train_no", "class","from_station", "to_station","date","time"];
|
||||
List<String> requiredFields = [
|
||||
"train_no",
|
||||
"class",
|
||||
"from_station",
|
||||
"to_station",
|
||||
"date",
|
||||
"time"
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -185,28 +189,22 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save accomadationData $trainData");
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save accomadationData $trainData");
|
||||
|
||||
Map<String,dynamic> data = trainData;
|
||||
Map<String, dynamic> data = trainData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSavetrain(trainData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -235,8 +233,10 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
),
|
||||
),
|
||||
Text("Train Booking List",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -254,7 +254,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -268,7 +268,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
@ -284,10 +283,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -300,20 +296,16 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
),
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop)),
|
||||
if (errorMessages["train_no"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -322,39 +314,40 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_value'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
String? selectedPurpose = dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
String? selectedPurpose =
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _trainNoFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
@ -369,36 +362,34 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _builClassType(bool isDesktop){
|
||||
|
||||
List<Widget> _builClassType(bool isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiData?['train_class'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedClass ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
selectedClass ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
Column(
|
||||
@ -415,9 +406,11 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _hotelNameFocusNode, // Assign the correct focus node
|
||||
// controller: _hotelNameController,
|
||||
@ -425,38 +418,33 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedClass = newValue;
|
||||
});
|
||||
}
|
||||
setState(() {
|
||||
selectedClass = newValue;
|
||||
});
|
||||
}
|
||||
: null,
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["class"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["class"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
|
||||
DateTime? _selectedCheckOutDate;
|
||||
TimeOfDay? _selectedCheckOutTime;
|
||||
|
||||
@ -466,9 +454,8 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -504,7 +491,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -521,7 +507,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
child: TextField(
|
||||
focusNode: _fromFocusNode,
|
||||
controller: _fromController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
@ -531,19 +517,18 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["from_station"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["from_station"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -567,7 +552,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: TextField(
|
||||
focusNode: _toFocusNode,
|
||||
controller: _toController,
|
||||
@ -582,14 +566,14 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["to_station"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["to_station"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -613,7 +597,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: () => _selectCheckOutDate(context),
|
||||
child: AbsorbPointer(
|
||||
@ -633,17 +616,16 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
if (errorMessages["date"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
@ -681,12 +663,11 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon:
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
@ -698,8 +679,6 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -720,7 +699,7 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
@ -782,4 +761,4 @@ class _TrainScreenState extends State<TrainScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,20 +7,21 @@ import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_text_itnerary_sub.dart';
|
||||
|
||||
class VisaScreen extends StatefulWidget {
|
||||
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
|
||||
final Function(bool) onClose;
|
||||
final Function(Map<String,dynamic>) onSaveVisa;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
final Function(Map<String, dynamic>) onSaveVisa;
|
||||
final Map<String, dynamic>? selectedItem;
|
||||
final String? loginUser;
|
||||
|
||||
|
||||
VisaScreen({
|
||||
required this.onClose,required this.onSaveVisa, this.apiData, required this.selectedItem,
|
||||
required this.apiCountryData, required this.loginUser});
|
||||
VisaScreen(
|
||||
{required this.onClose,
|
||||
required this.onSaveVisa,
|
||||
this.apiData,
|
||||
required this.selectedItem,
|
||||
required this.apiCountryData,
|
||||
required this.loginUser});
|
||||
|
||||
@override
|
||||
_VisaScreenState createState() => _VisaScreenState();
|
||||
@ -38,7 +39,6 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
final FocusNode _dateFocusNode = FocusNode();
|
||||
final FocusNode _commentsFocusNode = FocusNode();
|
||||
|
||||
|
||||
late TextEditingController _tripTypeController = TextEditingController();
|
||||
late TextEditingController _hotelNameController = TextEditingController();
|
||||
late TextEditingController _fromController = TextEditingController();
|
||||
@ -57,36 +57,36 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
|
||||
|
||||
Map<String, dynamic> get visaData{
|
||||
Map<String, dynamic> data ={
|
||||
"type_of_visa" :selectedPurpose,
|
||||
// "country": selectedCountry,
|
||||
Map<String, dynamic> get visaData {
|
||||
Map<String, dynamic> data = {
|
||||
"type_of_visa": selectedPurpose,
|
||||
// "country": selectedCountry,
|
||||
"country_code": selectedCountry,
|
||||
"start_date": _dateController.text,
|
||||
"comments":_visaCommentsController.text,
|
||||
"start_date": _dateController.text,
|
||||
"comments": _visaCommentsController.text,
|
||||
"created_by": widget.loginUser,
|
||||
"updated_by": widget.loginUser,
|
||||
};
|
||||
|
||||
if (widget.selectedItem != null) {
|
||||
if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) {
|
||||
if (widget.selectedItem?["indx"] != null &&
|
||||
widget.selectedItem?["indx"] != 0) {
|
||||
data["indx"] = widget.selectedItem!["indx"];
|
||||
} else if (widget.selectedItem?["visa_id"] != null && widget.selectedItem?["visa_id"] != 0) {
|
||||
} else if (widget.selectedItem?["visa_id"] != null &&
|
||||
widget.selectedItem?["visa_id"] != 0) {
|
||||
data["visa_id"] = widget.selectedItem!["visa_id"];
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_addFocusListener(_tripTypeFocusNode, (focus) => _tripTypeFocused = focus);
|
||||
_addFocusListener(_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(
|
||||
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
|
||||
_addFocusListener(_dateFocusNode, (focus) => _dateFocus = focus);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
@ -96,20 +96,17 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
|
||||
|
||||
// Set the selected value if available
|
||||
if (widget.selectedItem != null && widget.selectedItem!["type_of_visa"] != null) {
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["type_of_visa"] != null) {
|
||||
selectedPurpose = widget.selectedItem!["type_of_visa"].toString();
|
||||
}
|
||||
if (widget.selectedItem != null && widget.selectedItem!["country_code"] != null) {
|
||||
if (widget.selectedItem != null &&
|
||||
widget.selectedItem!["country_code"] != null) {
|
||||
// selectedPurpose = widget.selectedItem!["selectedCountry"].toString();
|
||||
selectedCountry = widget.selectedItem!["country_code"] as String?;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
||||
node.addListener(() {
|
||||
setState(() {
|
||||
@ -131,12 +128,15 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["type_of_visa", "country_code","start_date"];
|
||||
List<String> requiredFields = [
|
||||
"type_of_visa",
|
||||
"country_code",
|
||||
"start_date"
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
@ -148,29 +148,22 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void handleSave() {
|
||||
print("Handle Save visaData $visaData");
|
||||
|
||||
|
||||
void handleSave(){
|
||||
|
||||
print( "Handle Save visaData $visaData");
|
||||
|
||||
Map<String,dynamic> data = visaData;
|
||||
Map<String, dynamic> data = visaData;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
}else {
|
||||
} else {
|
||||
widget.onSaveVisa(visaData);
|
||||
}
|
||||
|
||||
widget.onClose(false);// Close screen after saving
|
||||
widget.onClose(false); // Close screen after saving
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -199,8 +192,10 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
),
|
||||
),
|
||||
Text("Visa Registration",
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF575A74))),
|
||||
SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
@ -218,7 +213,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm (bool isDesktop) {
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
List<Widget> buildResponsiveRow(List<Widget> children) {
|
||||
return [
|
||||
isDesktop ? Row(children: children) : Column(children: children),
|
||||
@ -226,12 +221,9 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
List<List<Widget>> rowBuilders = [
|
||||
_buildSecondRow(isDesktop)
|
||||
];
|
||||
List<List<Widget>> rowBuilders = [_buildSecondRow(isDesktop)];
|
||||
|
||||
return [
|
||||
|
||||
...buildResponsiveRow(_buildFirstRow(isDesktop)),
|
||||
|
||||
// Iterate over rowBuilders and wrap each in a responsive container
|
||||
@ -247,10 +239,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildFirstRow(isDesktop) {
|
||||
|
||||
return [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@ -263,20 +252,16 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
color: Color(0xFF575A74)),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
|
||||
isDesktop ? Row(children: _buildTripType(isDesktop)
|
||||
) :
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
),
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop)),
|
||||
if (errorMessages["type_of_visa"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
@ -285,75 +270,69 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop){
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
List<dynamic> purposeList = widget.apiData?['visa_type_of_visa'] ?? [];
|
||||
|
||||
List<DropdownMenuItem<String>> dropdownItems = purposeList
|
||||
.map((item)=>DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
)).toList();
|
||||
.map((item) => DropdownMenuItem<String>(
|
||||
value: item['dropdown_key'],
|
||||
child: Text(item['dropdown_value']),
|
||||
))
|
||||
.toList();
|
||||
|
||||
if (dropdownItems.isEmpty) {
|
||||
dropdownItems.add(
|
||||
DropdownMenuItem<String>(
|
||||
value: null,
|
||||
child: Text("No options available", style: TextStyle(color: Colors.grey)),
|
||||
child: Text("No options available",
|
||||
style: TextStyle(color: Colors.grey)),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Default selected value
|
||||
selectedPurpose ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
selectedPurpose ??=
|
||||
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
||||
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _tripTypeFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: DropdownButtonFormField<String>(
|
||||
focusNode: _tripTypeFocusNode, // Assign the correct focus node
|
||||
value: selectedPurpose,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10), // Proper padding
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(horizontal: 10), // Proper padding
|
||||
),
|
||||
onChanged: purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print("Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
|
||||
|
||||
}
|
||||
setState(() {
|
||||
selectedPurpose = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}");
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
List<Widget> _buildSecondRow(bool isDesktop) {
|
||||
|
||||
// List<dynamic> countryList = widget.apiCountryData ?? [];
|
||||
|
||||
//
|
||||
@ -380,12 +359,12 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
||||
late List<String> countryCodes; // List of country codes
|
||||
|
||||
|
||||
countryList = widget.apiCountryData ?? [];
|
||||
|
||||
// Map country codes to country names
|
||||
countryMap = {
|
||||
for (var item in countryList) item['country_code'] as String: item['country_name'] as String
|
||||
for (var item in countryList)
|
||||
item['country_code'] as String: item['country_name'] as String
|
||||
};
|
||||
|
||||
// Extract only country codes for processing
|
||||
@ -403,9 +382,8 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
|
||||
|
||||
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
@ -420,9 +398,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -437,12 +413,15 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _isHotelNameFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: countryMap[selectedCountry],
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true, // Enables search functionality
|
||||
showSearchBox: true, // Enables search functionality
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Country...",
|
||||
@ -450,14 +429,17 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
items: countryMap.values.toList(),
|
||||
items: countryMap.values.toList(),
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1,),
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item
|
||||
dropdownBuilder: (context, selectedItem) => Align(
|
||||
// Center-align selected item
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Country",
|
||||
@ -474,22 +456,20 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
if (selectedCountry!.isNotEmpty) {
|
||||
errorMessages.remove("country_code");
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
if (errorMessages["country_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
@ -510,19 +490,20 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _dateFocus,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
|
||||
child: GestureDetector(
|
||||
onTap: ()async{
|
||||
onTap: () async {
|
||||
await _selectCheckOutDate(context);
|
||||
if (_dateController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
errorMessages.remove("start_date");
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
if (_dateController.text.isNotEmpty) {
|
||||
setState(() {
|
||||
errorMessages.remove("start_date");
|
||||
});
|
||||
}
|
||||
},
|
||||
child: AbsorbPointer(
|
||||
child: TextField(
|
||||
focusNode: _dateFocusNode,
|
||||
@ -540,7 +521,6 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
),
|
||||
),
|
||||
if (errorMessages["start_date"] != null) ...[
|
||||
@ -552,9 +532,6 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
],
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@ -575,7 +552,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: TextField(
|
||||
focusNode: _commentsFocusNode,
|
||||
@ -584,7 +561,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelText: "Comments",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -621,7 +598,7 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
handleSave();
|
||||
handleSave();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue, // Primary color for save
|
||||
@ -637,4 +614,4 @@ class _VisaScreenState extends State<VisaScreen> {
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -11,80 +11,87 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
|
||||
|
||||
class ListPlans extends StatefulWidget{
|
||||
class ListPlans extends StatefulWidget {
|
||||
const ListPlans({super.key});
|
||||
|
||||
@override
|
||||
_ListPlansState createState() => _ListPlansState();
|
||||
}
|
||||
|
||||
|
||||
class _ListPlansState extends State<ListPlans>{
|
||||
|
||||
class _ListPlansState extends State<ListPlans> {
|
||||
late Future<List<Plan>> futurePlans;
|
||||
String? userId;
|
||||
String? orgId;
|
||||
String? token;
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
void initState() {
|
||||
super.initState();
|
||||
getToken();
|
||||
initializeData();
|
||||
|
||||
|
||||
// futurePlans = fetchPlans();
|
||||
|
||||
}
|
||||
|
||||
Future<void> initializeData ()async{
|
||||
Future<void> initializeData() async {
|
||||
token = await getToken();
|
||||
userId = await getUserId();
|
||||
orgId = await getOrgId();
|
||||
|
||||
if(token == null || userId == null){
|
||||
if (token == null || userId == null) {
|
||||
print("Token or USerId missing");
|
||||
return;
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
setState(() {
|
||||
futurePlans = fetchPlans();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if(userDataString != null){
|
||||
try{
|
||||
final Map<String,dynamic> userData = jsonDecode(userDataString);
|
||||
if (userDataString != null) {
|
||||
try {
|
||||
final Map<String, dynamic> userData = jsonDecode(userDataString);
|
||||
return userData["user_id"]?.toString();
|
||||
}catch(e){
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
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<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Fetch API Data
|
||||
Future<List<Plan>> fetchPlans() async {
|
||||
// final String apiUrldata = '$apiUrl/api/plans';
|
||||
final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId';
|
||||
// api/plans?org_id=1&user_id=1
|
||||
// final token = await getToken();
|
||||
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
@ -92,7 +99,7 @@ class _ListPlansState extends State<ListPlans>{
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
@ -106,8 +113,7 @@ class _ListPlansState extends State<ListPlans>{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future <Map<String,dynamic>> getViewPlan(String planId) async{
|
||||
Future<Map<String, dynamic>> getViewPlan(String planId) async {
|
||||
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
||||
print("API URL: $apiUrldata");
|
||||
// final token = await getToken();
|
||||
@ -119,36 +125,32 @@ class _ListPlansState extends State<ListPlans>{
|
||||
final response = await http.put(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Authorization': 'Bearer $token', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final Map<String,dynamic>? resData = json.decode(response.body);
|
||||
final Map<String, dynamic>? resData = json.decode(response.body);
|
||||
|
||||
return resData?["data"];
|
||||
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void viewPlan(String planId, {bool isViewMode = false}) async{
|
||||
void viewPlan(String planId, {bool isViewMode = false}) async {
|
||||
try {
|
||||
Map<String, dynamic> planData = await getViewPlan(planId);
|
||||
print("ViewAAA - $planData");
|
||||
|
||||
context.go('/createPlan',extra: {'planData': planData, 'isViewMode': isViewMode} );
|
||||
context.go('/createPlan',
|
||||
extra: {'planData': planData, 'isViewMode': isViewMode});
|
||||
} catch (e) {
|
||||
print("Error fetching plan: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
@ -192,13 +194,22 @@ class _ListPlansState extends State<ListPlans>{
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.blueAccent),
|
||||
onPressed: () {
|
||||
context.go('/createPlan');
|
||||
context.go('/createPlan', extra: {
|
||||
// 'apiCountryData': apiCountryData,
|
||||
'orgId': orgId,
|
||||
});
|
||||
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.add_circle,color: Colors.white,),
|
||||
SizedBox(width: 5,),
|
||||
Icon(
|
||||
Icons.add_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text('NewPlan'),
|
||||
],
|
||||
),
|
||||
@ -206,220 +217,290 @@ class _ListPlansState extends State<ListPlans>{
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<Plan>>(
|
||||
future: futurePlans, // Use the futurePlans variable
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text("Error: ${snapshot.error}"));
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Center(child: Text("No plans available"));
|
||||
}
|
||||
FutureBuilder<List<Plan>>(
|
||||
future: futurePlans, // Use the futurePlans variable
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
color: Colors.redAccent,
|
||||
size: 60,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
"Oops!",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
"No Plans Available For This User",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
" Please Create Plan",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// ElevatedButton.icon(
|
||||
// onPressed: () {
|
||||
// // Optional: retry logic or navigation
|
||||
// },
|
||||
// icon: Icon(Icons.refresh),
|
||||
// label: Text("Try Again"),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.blueAccent,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Center(child: Text("No plans available"));
|
||||
}
|
||||
|
||||
List<Plan> plans = snapshot.data!; // Extract the list of plans
|
||||
List<Plan> plans = snapshot.data!; // Extract the list of plans
|
||||
|
||||
// Ensure planId is sorted in descending order
|
||||
plans.sort((a, b) => int.parse(b.planId.toString())
|
||||
.compareTo(int.parse(a.planId.toString())));
|
||||
|
||||
// Ensure planId is sorted in descending order
|
||||
plans.sort((a, b) => int.parse(b.planId.toString()).compareTo(int.parse(a.planId.toString())));
|
||||
// return ResponsiveBuilder(
|
||||
// builder: (context, sizingInfo) {
|
||||
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
||||
//
|
||||
// return SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// child: Container(
|
||||
// color: Colors.grey,
|
||||
// child: SizedBox(
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
|
||||
//
|
||||
// // constraints: isTabletOrDesktop
|
||||
// // ? const BoxConstraints(maxWidth: double.infinity)
|
||||
// // : BoxConstraints.tightFor(width: 600),
|
||||
//
|
||||
//
|
||||
// child: DataTable(
|
||||
// // columnSpacing: 50.0,
|
||||
// dividerThickness: 0.5, // Reduce the thickness of row dividers
|
||||
// border: TableBorder(
|
||||
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
|
||||
// ),
|
||||
// columns: const [
|
||||
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// //
|
||||
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// ],
|
||||
// rows: plans.map((plan) {
|
||||
// return DataRow(cells: [
|
||||
// DataCell(Text(plan.planId)),
|
||||
// // DataCell(Text(plan.tripTitle)),
|
||||
// DataCell(Row(
|
||||
// children: [
|
||||
// Flexible(
|
||||
// child: Text(
|
||||
// plan.tripTitle,
|
||||
// softWrap: true,
|
||||
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )),
|
||||
//
|
||||
//
|
||||
// DataCell(Text(plan.tripType)),
|
||||
// DataCell(Text(plan.costCenter)),
|
||||
// // DataCell(Text(plan.functionalDepartment)),
|
||||
// // DataCell(Text(plan.purposeOfTravel)),
|
||||
// // DataCell(Text(plan.description)),
|
||||
// //
|
||||
// DataCell(Text(plan.isBillable)),
|
||||
// DataCell(Text(plan.status)),
|
||||
// DataCell(
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// viewPlan(plan.planId);
|
||||
// print("View button clicked for ${plan.planId}");
|
||||
// },
|
||||
// child: const Text('View',
|
||||
// style: TextStyle(color: Colors.blueAccent)),
|
||||
// ),
|
||||
// ),
|
||||
// ]);
|
||||
// }).toList(),
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// },
|
||||
// );
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 1.5,
|
||||
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen
|
||||
// width: double.infinity , // Ensure table is wider than screen
|
||||
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
|
||||
scrollDirection: Axis
|
||||
.horizontal, // Inner wrapper for vertical scrolling
|
||||
|
||||
// return ResponsiveBuilder(
|
||||
// builder: (context, sizingInfo) {
|
||||
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
||||
//
|
||||
// return SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// child: Container(
|
||||
// color: Colors.grey,
|
||||
// child: SizedBox(
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
|
||||
//
|
||||
// // constraints: isTabletOrDesktop
|
||||
// // ? const BoxConstraints(maxWidth: double.infinity)
|
||||
// // : BoxConstraints.tightFor(width: 600),
|
||||
//
|
||||
//
|
||||
// child: DataTable(
|
||||
// // columnSpacing: 50.0,
|
||||
// dividerThickness: 0.5, // Reduce the thickness of row dividers
|
||||
// border: TableBorder(
|
||||
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
|
||||
// ),
|
||||
// columns: const [
|
||||
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// //
|
||||
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// ],
|
||||
// rows: plans.map((plan) {
|
||||
// return DataRow(cells: [
|
||||
// DataCell(Text(plan.planId)),
|
||||
// // DataCell(Text(plan.tripTitle)),
|
||||
// DataCell(Row(
|
||||
// children: [
|
||||
// Flexible(
|
||||
// child: Text(
|
||||
// plan.tripTitle,
|
||||
// softWrap: true,
|
||||
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )),
|
||||
//
|
||||
//
|
||||
// DataCell(Text(plan.tripType)),
|
||||
// DataCell(Text(plan.costCenter)),
|
||||
// // DataCell(Text(plan.functionalDepartment)),
|
||||
// // DataCell(Text(plan.purposeOfTravel)),
|
||||
// // DataCell(Text(plan.description)),
|
||||
// //
|
||||
// DataCell(Text(plan.isBillable)),
|
||||
// DataCell(Text(plan.status)),
|
||||
// DataCell(
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// viewPlan(plan.planId);
|
||||
// print("View button clicked for ${plan.planId}");
|
||||
// },
|
||||
// child: const Text('View',
|
||||
// style: TextStyle(color: Colors.blueAccent)),
|
||||
// ),
|
||||
// ),
|
||||
// ]);
|
||||
// }).toList(),
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// },
|
||||
// );
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 1300),
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 1.5,
|
||||
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen
|
||||
// width: double.infinity , // Ensure table is wider than screen
|
||||
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
|
||||
scrollDirection: Axis.horizontal, // Inner wrapper for vertical scrolling
|
||||
|
||||
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 1300),
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
|
||||
child: Container(
|
||||
// color: Colors.amber,
|
||||
child: DataTable(
|
||||
columnSpacing: 50.0, // Adjust spacing between columns
|
||||
dividerThickness: 0.5,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('Plan ID', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Trip Title', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Trip Type', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Cost Center', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Is Billable', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Status', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
DataColumn(label: Text('Actions', style: TextStyle(fontWeight: FontWeight.bold))),
|
||||
],
|
||||
|
||||
|
||||
|
||||
rows: plans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId)),
|
||||
DataCell(Text(plan.tripTitle, softWrap: true, overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(plan.tripType)),
|
||||
DataCell(Text(plan.costCenter)),
|
||||
DataCell(Text(plan.isBillable)),
|
||||
DataCell(
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10), // Padding for better look
|
||||
decoration: BoxDecoration(
|
||||
color: plan.status == "Active" ? Colors.green.shade50 : Colors.grey.shade50, // Background color
|
||||
borderRadius: BorderRadius.circular(10), // Rounded corners
|
||||
),
|
||||
child: Text(
|
||||
plan.status,
|
||||
style: TextStyle(
|
||||
color: plan.status == "Active" ? Colors.green : Colors.grey, // Text color
|
||||
fontWeight: FontWeight.bold, // Optional: Make text bold
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Row(
|
||||
children:[
|
||||
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: true);
|
||||
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: false);
|
||||
},
|
||||
),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// deletePlan(plan.planId);
|
||||
// },
|
||||
// ),
|
||||
|
||||
|
||||
]
|
||||
)
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList(),
|
||||
child: Container(
|
||||
// color: Colors.amber,
|
||||
child: DataTable(
|
||||
columnSpacing:
|
||||
50.0, // Adjust spacing between columns
|
||||
dividerThickness: 0.5,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(
|
||||
label: Text('Plan ID',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Title',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Type',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Cost Center',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Is Billable',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Status',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Actions',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
],
|
||||
|
||||
rows: plans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId)),
|
||||
DataCell(Text(plan.tripTitle,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(plan.tripType)),
|
||||
DataCell(Text(plan.costCenter)),
|
||||
DataCell(Text(plan.isBillable)),
|
||||
DataCell(
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal:
|
||||
10), // Padding for better look
|
||||
decoration: BoxDecoration(
|
||||
color: plan.status == "Active"
|
||||
? Colors.green.shade50
|
||||
: Colors
|
||||
.grey.shade50, // Background color
|
||||
borderRadius: BorderRadius.circular(
|
||||
10), // Rounded corners
|
||||
),
|
||||
child: Text(
|
||||
plan.status,
|
||||
style: TextStyle(
|
||||
color: plan.status == "Active"
|
||||
? Colors.green
|
||||
: Colors.grey, // Text color
|
||||
fontWeight: FontWeight
|
||||
.bold, // Optional: Make text bold
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(Row(children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye,
|
||||
color: Colors.blue),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: true);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: false);
|
||||
},
|
||||
),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// deletePlan(plan.planId);
|
||||
// },
|
||||
// ),
|
||||
])),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,8 @@ import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_user_form.dart';
|
||||
|
||||
class Policy extends StatefulWidget {
|
||||
const Policy({super.key});
|
||||
@ -18,6 +20,7 @@ class _PolicyState extends State<Policy> {
|
||||
late String policyType = "domestic";
|
||||
int? selectedServiceIndex = 1;
|
||||
late String selectedService = "Train";
|
||||
String? _selectedTripType;
|
||||
|
||||
bool showClass = true;
|
||||
bool showCost = true;
|
||||
@ -42,136 +45,206 @@ class _PolicyState extends State<Policy> {
|
||||
}
|
||||
|
||||
Widget buildPolicyLayout(bool isDesktop) {
|
||||
return Container(
|
||||
margin: isDesktop
|
||||
? EdgeInsets.all(20.0)
|
||||
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||
height: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
border: isDesktop
|
||||
? Border.all(
|
||||
width: 3,
|
||||
color: Color(0xFFF7F7FB),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Color(0xFFF7F7FB),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
|
||||
// color: Colors.white, // Background to avoid overlapping
|
||||
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"Choose Policy Type",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.black,
|
||||
return SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Container(
|
||||
margin: isDesktop
|
||||
? EdgeInsets.all(20.0)
|
||||
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||
height: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
border: isDesktop
|
||||
? Border.all(
|
||||
width: 2,
|
||||
color: Color(0xFFF7F7FB),
|
||||
)
|
||||
: null,
|
||||
color: Color(0xFFF7F7FB),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
// color: Color(0xFFF7F7FB),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
|
||||
// color: Colors.white, // Background to avoid overlapping
|
||||
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
"Choose Policy Type",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Container(
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "domestic";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Colors.blue.shade300,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "Domestic",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "international";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// // color: Color(0xFFEBEBF7),
|
||||
// color: policyType == "international"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Color(0xFFE3F2FD),
|
||||
//
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "International",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "international"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
],
|
||||
// Container(
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "domestic";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Colors.blue.shade300,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "Domestic",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "international";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// // color: Color(0xFFEBEBF7),
|
||||
// color: policyType == "international"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Color(0xFFE3F2FD),
|
||||
//
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "International",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "international"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
isDesktop
|
||||
? Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
_buildPolicyCategoryList(isDesktop),
|
||||
_buildPolicyCategory(isDesktop),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildPolicyCategoryList(isDesktop),
|
||||
_buildPolicyCategory(isDesktop),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
isDesktop ? SizedBox(height: 0) : SizedBox(height: 5),
|
||||
Container(
|
||||
// color: Colors.amber,
|
||||
// color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
||||
padding: isDesktop ? const EdgeInsets.only(left: 35) : null,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Policy Name",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
// controller: controllers["Fname"],
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Policy Name",
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior:
|
||||
FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Policy Type",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: _buildTripType(isDesktop),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
isDesktop
|
||||
? Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
_buildPolicyCategoryList(isDesktop),
|
||||
_buildPolicyCategory(isDesktop),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildPolicyCategoryList(isDesktop),
|
||||
_buildPolicyCategory(isDesktop),
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -224,8 +297,10 @@ class _PolicyState extends State<Policy> {
|
||||
return SizedBox(
|
||||
width: isDesktop ? 180 : null,
|
||||
height: isDesktop
|
||||
? max((MediaQuery.of(context).size.height * 0.09), 10)
|
||||
? max((MediaQuery.of(context).size.height * 0.075), 10)
|
||||
: 45,
|
||||
|
||||
// max((MediaQuery.of(context).size.height * 0.09), 10)
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
print("Selected Services - $service - $index");
|
||||
@ -288,4 +363,64 @@ class _PolicyState extends State<Policy> {
|
||||
selectedTab: selectedService)),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
width: isDesktop ? 170 : 140,
|
||||
isFocused: _selectedTripType == "1",
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile<String>(
|
||||
activeColor: Colors.blueAccent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: true,
|
||||
title: Text("Domestic"),
|
||||
value: "1",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
isDesktop ? SizedBox(width: 28) : SizedBox(width: 15),
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
width: isDesktop ? 180 : 180,
|
||||
isFocused: _selectedTripType == "2",
|
||||
isDesktop: isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile<String>(
|
||||
activeColor: Colors.blueAccent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: true,
|
||||
title: Text("International"),
|
||||
value: "2",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
widget.isDesktop ? SizedBox(height: 10) : SizedBox(height: 5),
|
||||
Row(
|
||||
@ -56,22 +57,13 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
)
|
||||
],
|
||||
),
|
||||
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
|
||||
Padding(
|
||||
padding: widget.isDesktop
|
||||
? const EdgeInsets.all(8.0)
|
||||
: const EdgeInsets.all(1.0),
|
||||
child: Row(
|
||||
children: _buildTripType(widget.isDesktop),
|
||||
),
|
||||
),
|
||||
if (widget.isClass!)
|
||||
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color:Colors.grey,
|
||||
// color: Colors.grey,
|
||||
padding:
|
||||
const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5),
|
||||
child: Column(
|
||||
@ -181,19 +173,20 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: Colors.grey.shade100,
|
||||
width: widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.63
|
||||
: 600,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(right: 20),
|
||||
margin: const EdgeInsets.only(right: 0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade50,
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade50,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
color: Colors.grey.shade100,
|
||||
border: Border.all(
|
||||
color: Colors.grey.shade100,
|
||||
),
|
||||
),
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10, bottom: 10, left: 35, right: 35),
|
||||
child: Row(
|
||||
@ -232,7 +225,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
child: Container(
|
||||
// color: Colors.grey,
|
||||
margin: const EdgeInsets.only(right: 20),
|
||||
color: Colors.grey.shade50,
|
||||
color: Colors.grey.shade100,
|
||||
child: Column(children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
@ -511,57 +504,4 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTripType(bool isMobile) {
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
padding: EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
width: 120,
|
||||
isFocused: _selectedTripType == "1",
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 35,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: RadioListTile<String>(
|
||||
activeColor: Colors.blueAccent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
visualDensity: VisualDensity.compact,
|
||||
dense: true,
|
||||
title: Text("Domestic"),
|
||||
value: "1",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
width: 150,
|
||||
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
isFocused: _selectedTripType == "2",
|
||||
isDesktop: widget.isDesktop,
|
||||
child: RadioListTile<String>(
|
||||
activeColor: Colors.blueAccent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
title: Text("International"),
|
||||
value: "2",
|
||||
groupValue: _selectedTripType,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:html' as html;
|
||||
import 'dart:typed_data'; // Import for Uint8List
|
||||
@ -8,11 +9,14 @@ import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:frontend/utils/auth_utils.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_parser/http_parser.dart' as http_parser;
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
|
||||
import '../../../config/apiUrl.dart';
|
||||
import '../../../routes/custom_appBar.dart';
|
||||
@ -31,6 +35,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
String? userId;
|
||||
String? orgId;
|
||||
|
||||
String? token;
|
||||
|
||||
@ -67,8 +72,11 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
|
||||
String? selectedFileNames;
|
||||
Uint8List? passportDocumentBytes;
|
||||
String? passportFileUrlFromApi;
|
||||
String? base64PDF;
|
||||
|
||||
html.File? passportFile;
|
||||
|
||||
List<String> dataHeader = [
|
||||
"Fname",
|
||||
"Lname",
|
||||
@ -109,12 +117,13 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
"address": controllers["address"]?.text,
|
||||
"gender": selectedGender,
|
||||
"postal_code": controllers["postalCode"]?.text,
|
||||
"country": selectedCountry,
|
||||
"country_code": selectedCountry,
|
||||
"employee_code": controllers["employeeCode"]?.text,
|
||||
|
||||
"user_type": selectedUserType,
|
||||
"role_id": selectedRole,
|
||||
"department_id": selectedDepartment,
|
||||
|
||||
"group_id": selectedLevel,
|
||||
|
||||
"first_approver": selectedFirstApprover,
|
||||
@ -122,21 +131,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
"third_approver": selectedThirdApprover,
|
||||
"passport_number": controllers["passportNumber"]?.text,
|
||||
"place_of_issue": controllers["placeOfIssue"]?.text,
|
||||
"passport_document": base64PDF,
|
||||
"passport_document": passportFile,
|
||||
|
||||
"date_of_issue": controllers["dateOfIssue"]?.text,
|
||||
"date_of_expiry": controllers["dateOfExpiry"]?.text,
|
||||
"created_by": userId,
|
||||
"is_active": "1",
|
||||
// "passport_fileData": base64PDF,
|
||||
"org_id": orgId,
|
||||
// "passport_fileData": passportFile,
|
||||
};
|
||||
return data;
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
Future<void> updateData() async {
|
||||
// Ensure apiselectedUser is not null before printing
|
||||
if (apiselectedUser != null) {
|
||||
print("API Selected User Has Data - $apiselectedUser");
|
||||
print("API Selected User Has Data - $widget.apiselectedUser");
|
||||
setState(() {
|
||||
// ✅ Wrap in setState to update the UI
|
||||
|
||||
@ -165,15 +175,22 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
apiselectedUser?["date_of_expiry"] ?? "";
|
||||
|
||||
selectedCountry = apiselectedUser?["country_code"]?.toString() ?? "";
|
||||
|
||||
selectedGender = apiselectedUser?["gender"]?.toString().trim() ?? "";
|
||||
|
||||
base64PDF =
|
||||
apiselectedUser?["passport_document"]?.toString().trim() ?? "";
|
||||
selectedUserType =
|
||||
selectedUserType = selectedUserType =
|
||||
apiselectedUser?["user_type"]?.toString().trim() ?? "";
|
||||
|
||||
selectedRole = apiselectedUser?["role_id"]?.toString().trim() ?? "";
|
||||
selectedDepartment =
|
||||
apiselectedUser?["department_id"]?.toString().trim() ?? "";
|
||||
// selectedDepartment =
|
||||
// apiselectedUser?["department_id"]?.toString().trim() ?? "";
|
||||
|
||||
if (apiselectedUser?["department_id"] != null) {
|
||||
selectedDepartment = apiselectedUser!["department_id"].toString();
|
||||
}
|
||||
// print(
|
||||
// "selectedDepartment - $selectedDepartment - ${apiselectedUser?["department_id"]} ");
|
||||
|
||||
selectedLevel = apiselectedUser?["level_id"]?.toString().trim() ?? "";
|
||||
|
||||
selectedFirstApprover =
|
||||
@ -184,6 +201,19 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
apiselectedUser?["third_approver"]?.toString() ?? "";
|
||||
|
||||
print("Updated selectedGender: $selectedGender"); // Debugging
|
||||
|
||||
// ✅ Load passport document from API
|
||||
String? apiDocPath = apiselectedUser?["passport_document"];
|
||||
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
||||
passportFileUrlFromApi = apiDocPath;
|
||||
selectedFileNames =
|
||||
apiDocPath.split('/').last; // Extract filename from path
|
||||
passportFile = null; // No local file selected yet
|
||||
} else {
|
||||
passportFileUrlFromApi = null;
|
||||
selectedFileNames = null;
|
||||
passportFile = null;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
print("API Selected User Has Data - No data available yet");
|
||||
@ -195,37 +225,29 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
super.initState();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Step 1: Set 'reloaded' flag before page unload
|
||||
html.window.onBeforeUnload.listen((event) {
|
||||
html.window.localStorage['reloaded'] = 'true';
|
||||
});
|
||||
|
||||
// apiCountryData = extraData['apiCountryData']; // Extract apiCountryData
|
||||
// futureUsers = extraData['apiUserData']; // Extract futureUsers (Future<List<dynamic>>)
|
||||
apiCountryData = null;
|
||||
apiUserData = null;
|
||||
apiselectedUser = null;
|
||||
// apiselectedUser = null;
|
||||
apiCostData = null;
|
||||
apiRoleData = null;
|
||||
|
||||
// Delay accessing context until the widget is fully initialized
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// setState(() {
|
||||
// apiCountryData = (GoRouterState.of(context).extra as Map<String, dynamic>)['apiCountryData'];
|
||||
// apiUserData = (GoRouterState.of(context).extra as Map<String, dynamic>)['apiUserData'];
|
||||
// apiselectedUser = (GoRouterState.of(context).extra as Map<String, dynamic>)['selectedUser'];
|
||||
//
|
||||
//
|
||||
// userList = apiUserData ?? [];
|
||||
// userMap = {
|
||||
// for (var user in userList)
|
||||
// user['user_id'] as String: "${user['first_name']} ${user['last_name']}"
|
||||
// };
|
||||
//
|
||||
// userIdsApi = userMap.keys.toList();
|
||||
//
|
||||
//
|
||||
// });
|
||||
// });
|
||||
|
||||
// Initialize controllers for each field
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
// final wasReloaded = html.window.localStorage['reloaded'] == 'true';
|
||||
//
|
||||
// if (wasReloaded) {
|
||||
// html.window.localStorage.remove('reloaded'); // Clear it
|
||||
// context.go('/listUser'); // Navigate using go_router
|
||||
// }
|
||||
|
||||
final extraData =
|
||||
GoRouterState.of(context).extra as Map<String, dynamic>?;
|
||||
|
||||
@ -250,6 +272,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
isEditProfile = extraData['isEditProfile'] ?? false;
|
||||
});
|
||||
|
||||
print("selectedUser: $apiselectedUser");
|
||||
|
||||
// Add another post-frame callback to check after setState
|
||||
await Future.delayed(Duration(
|
||||
milliseconds: 100)); // Optional delay to ensure UI has updated
|
||||
@ -296,7 +320,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
setState(() {
|
||||
// apiUserData = users;
|
||||
|
||||
apiUserData = users.where((user) => user["role_id"] == "3").toList();
|
||||
apiUserData = users.where((user) => user["role_id"] == "4").toList();
|
||||
|
||||
print("APIUSerDATa - $apiUserData");
|
||||
|
||||
@ -383,7 +407,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void handleSubmit() {
|
||||
void handleSubmit() async {
|
||||
print("USR Detail Submit");
|
||||
printFormData();
|
||||
|
||||
@ -396,6 +420,8 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
return; // Stop execution if validation fails
|
||||
} else {
|
||||
print("USERDETAILS : $userDetials");
|
||||
orgId = await getOrgId();
|
||||
|
||||
createUserData(userDetials);
|
||||
}
|
||||
}
|
||||
@ -464,133 +490,102 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
|
||||
uploadInput.onChange.listen((e) {
|
||||
final file = uploadInput.files!.first;
|
||||
final reader = html.FileReader();
|
||||
|
||||
reader.readAsArrayBuffer(file);
|
||||
reader.onLoadEnd.listen((event) {
|
||||
print('File picked: ${file.name}');
|
||||
print('File size: ${file.size} bytes');
|
||||
// Ensure the file is a PDF
|
||||
if (!file.type.contains("pdf")) {
|
||||
print("Error: Not a PDF file");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure the file is a PDF
|
||||
if (!file.type.contains("pdf")) {
|
||||
print("Error: Not a PDF file");
|
||||
return;
|
||||
}
|
||||
// 🔹 File size check: Ensure it does not exceed 3MB
|
||||
int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
|
||||
if (file.size > maxFileSize) {
|
||||
print('Error: File size exceeds 3MB');
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
selectedFileNames = file.name; // Store file name
|
||||
passportDocumentBytes = reader.result as Uint8List; // Store file data
|
||||
|
||||
// 🔹 Convert to Base64 properly
|
||||
base64PDF = base64Encode(passportDocumentBytes!);
|
||||
|
||||
print('Base64 Length: ${base64PDF!.length}');
|
||||
print('Base64 (first 50 chars): ${base64PDF!.substring(0, 50)}');
|
||||
|
||||
// Ensure Base64 starts with "JVBERi0x"
|
||||
if (!base64PDF!.startsWith("JVBERi0x")) {
|
||||
print("Error: Base64 does not start with 'JVBERi0x'");
|
||||
return;
|
||||
}
|
||||
|
||||
// 🔹 File size check: Ensure it does not exceed 3MB
|
||||
int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
|
||||
if (file.size > maxFileSize) {
|
||||
print('Error: File size exceeds 3MB');
|
||||
return;
|
||||
}
|
||||
});
|
||||
setState(() {
|
||||
selectedFileNames = file.name;
|
||||
passportFile = file;
|
||||
passportFileUrlFromApi = null;
|
||||
});
|
||||
|
||||
print('PDF File selected: ${file.name}');
|
||||
});
|
||||
}
|
||||
|
||||
// void pickPDFWeb() {
|
||||
// html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
|
||||
// uploadInput.accept = '.pdf';
|
||||
// uploadInput.click();
|
||||
//
|
||||
// uploadInput.onChange.listen((e) {
|
||||
// final file = uploadInput.files!.first;
|
||||
// final reader = html.FileReader();
|
||||
//
|
||||
// reader.readAsArrayBuffer(file);
|
||||
// reader.onLoadEnd.listen((event) {
|
||||
// print('File picked: ${file.name}');
|
||||
// print('File size: ${file.size} bytes');
|
||||
//
|
||||
//
|
||||
// // Update the state with the selected file name
|
||||
// setState(() {
|
||||
// selectedFileNames = file.name; // Store only one file name
|
||||
// passportDocumentBytes = reader.result as Uint8List; // Store file data
|
||||
//
|
||||
// // 🔹 Convert file to Base64
|
||||
// base64PDF = base64Encode(passportDocumentBytes! as List<int>);
|
||||
// // File size check: Ensure the file size does not exceed 3MB
|
||||
// int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
|
||||
//
|
||||
// if (file.size > maxFileSize) {
|
||||
// print('Error: File size exceeds 3MB');
|
||||
// // You can show an error message here if necessary
|
||||
// // For example:
|
||||
// // showError('File size cannot exceed 3MB');
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// print('File size: ${file.size} bytes');
|
||||
// print('Base64 Data: $base64PDF'); // Debugging
|
||||
// });
|
||||
//
|
||||
//
|
||||
//
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
Future<void> createUserData(Map<String, dynamic> userData) async {
|
||||
bool isUpdating = apiselectedUser != null && apiselectedUser!.isNotEmpty;
|
||||
final String apiUrldata = isUpdating
|
||||
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}'
|
||||
: '$apiUrl/api/users/create';
|
||||
final bool isUpdating =
|
||||
apiselectedUser != null && apiselectedUser!.isNotEmpty;
|
||||
final uri = Uri.parse(
|
||||
isUpdating
|
||||
? '$apiUrl/api/users/update/${apiselectedUser?["user_id"]}'
|
||||
: '$apiUrl/api/users/create',
|
||||
);
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
// Add user_id only if updating
|
||||
// Use MultipartRequest (POST only)
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
request.headers['Authorization'] = 'Bearer $token';
|
||||
|
||||
// If updating, spoof the method Laravel-style
|
||||
if (isUpdating) {
|
||||
userData['user_id'] = apiselectedUser?["user_id"];
|
||||
request.fields['_method'] = 'PUT';
|
||||
request.fields['user_id'] = apiselectedUser!["user_id"].toString();
|
||||
}
|
||||
|
||||
// Add all non-null and non-empty user data fields
|
||||
userData.forEach((key, value) {
|
||||
if (value != null && value.toString().trim().isNotEmpty) {
|
||||
request.fields[key] = value.toString();
|
||||
}
|
||||
});
|
||||
|
||||
// Attach file if selected
|
||||
if (passportFile != null) {
|
||||
try {
|
||||
final reader = html.FileReader();
|
||||
reader.readAsArrayBuffer(passportFile!);
|
||||
await reader.onLoad.first;
|
||||
|
||||
final data = reader.result as Uint8List;
|
||||
|
||||
final multipartFile = http.MultipartFile.fromBytes(
|
||||
'passport_document',
|
||||
data,
|
||||
filename: passportFile!.name,
|
||||
);
|
||||
|
||||
request.files.add(multipartFile);
|
||||
print("📎 File attached: ${passportFile!.name}");
|
||||
} catch (e) {
|
||||
print("❌ Failed to read file: $e");
|
||||
}
|
||||
} else {
|
||||
print("⚠️ No passport file selected.");
|
||||
}
|
||||
|
||||
print("🚀 Sending request with fields: ${request.fields}");
|
||||
|
||||
try {
|
||||
final response = isUpdating
|
||||
? await http.put(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode(userData),
|
||||
)
|
||||
: await http.post(
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode(userData),
|
||||
);
|
||||
final streamedResponse = await request.send();
|
||||
final response = await http.Response.fromStream(streamedResponse);
|
||||
print("Response status: ${response.statusCode}");
|
||||
print("Response body: ${response.body}");
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
print("Plan submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
print("✅ User submitted successfully!");
|
||||
print("📨 Response: ${response.body}");
|
||||
context.go('/listUser');
|
||||
} else {
|
||||
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
print("❌ Submission failed. Status: ${response.statusCode}");
|
||||
print("📨 Body: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting plan: $e");
|
||||
print("🔥 Error submitting user: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@ -755,6 +750,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
child: isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Expanded(child: _buildFirstRowLeftColumn(isDesktop)),
|
||||
// SizedBox(width: 20),
|
||||
@ -1136,7 +1132,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
_selectedDateOfBirth != null && _selectedDateOfBirth!.isAfter(today)
|
||||
? _selectedDateOfBirth!
|
||||
: today,
|
||||
firstDate: today,
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
@ -1436,21 +1432,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
),
|
||||
SizedBox(height: 3),
|
||||
apiselectedUser != null
|
||||
? Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Change Password",
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
? SizedBox()
|
||||
: Row(
|
||||
children: [
|
||||
Column(
|
||||
@ -1876,7 +1858,7 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
),
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
if (base64PDF != null)
|
||||
if (passportFile != null || passportFileUrlFromApi != null)
|
||||
|
||||
// Centers the text
|
||||
Container(
|
||||
@ -1890,57 +1872,46 @@ class _CreateUserFormState extends State<CreateUserForm> {
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print('DOWLOAS- $base64PDF ');
|
||||
print('DOWNLOAD - $passportFile');
|
||||
|
||||
if (base64PDF != null && base64PDF!.isNotEmpty) {
|
||||
if (passportFile != null) {
|
||||
try {
|
||||
// ✅ Step 1: Clean the Base64 string
|
||||
String cleanedBase64 = base64PDF!
|
||||
.replaceAll("\n", "") // Remove newlines
|
||||
.replaceAll(
|
||||
"\r", "") // Remove carriage returns
|
||||
.replaceAll(" ", "") // Remove spaces
|
||||
.trim(); // Trim any whitespace
|
||||
// ✅ Step 1: Create a Blob directly from the file
|
||||
final blob = html.Blob(
|
||||
[passportFile!], 'application/pdf');
|
||||
|
||||
// ✅ Step 2: Ensure valid Base64 length (multiple of 4)
|
||||
while (cleanedBase64.length % 4 != 0) {
|
||||
cleanedBase64 += "_"; // Add '=' padding
|
||||
}
|
||||
|
||||
// ✅ Step 3: Decode the cleaned Base64
|
||||
Uint8List bytes;
|
||||
try {
|
||||
bytes = base64Decode(cleanedBase64);
|
||||
} catch (e) {
|
||||
print("Base64 decoding failed: $e");
|
||||
return;
|
||||
}
|
||||
|
||||
// ✅ Step 4: Create a Blob for download
|
||||
final blob =
|
||||
html.Blob([bytes], 'application/pdf');
|
||||
// ✅ Step 2: Generate a download URL from the Blob
|
||||
final url =
|
||||
html.Url.createObjectUrlFromBlob(blob);
|
||||
|
||||
// ✅ Step 5: Trigger the file download
|
||||
// ✅ Step 3: Create an invisible anchor to trigger download
|
||||
final anchor = html.AnchorElement(href: url)
|
||||
..setAttribute("download",
|
||||
selectedFileNames ?? "document.pdf")
|
||||
..style.display = "none";
|
||||
|
||||
// ✅ Step 4: Add anchor to DOM and click it
|
||||
html.document.body!.append(anchor);
|
||||
anchor.click();
|
||||
|
||||
// ✅ Step 6: Clean up
|
||||
// ✅ Step 5: Clean up
|
||||
anchor.remove();
|
||||
html.Url.revokeObjectUrl(url);
|
||||
|
||||
print("Download successful!");
|
||||
print("Download triggered successfully!");
|
||||
} catch (e) {
|
||||
print("Error downloading file: $e");
|
||||
print("Error during download: $e");
|
||||
}
|
||||
} else if (passportFileUrlFromApi != null) {
|
||||
// Trigger file download from the server path
|
||||
final anchor = html.AnchorElement(
|
||||
href: passportFileUrlFromApi!)
|
||||
..target = 'blank'
|
||||
..download =
|
||||
selectedFileNames ?? "document.pdf"
|
||||
..click();
|
||||
} else {
|
||||
print("No file to download.");
|
||||
print("No file available to download.");
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/utils/auth_utils.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
@ -18,6 +19,7 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
late Future<List<dynamic>> futureUsers;
|
||||
List<dynamic>? apiCountryData;
|
||||
String? selectedUserId;
|
||||
String? orgId;
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@ -25,7 +27,8 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
final String apiUrlData = '$apiUrl/api/users';
|
||||
orgId = await getOrgId();
|
||||
final String apiUrlData = '$apiUrl/api/users?org_id=$orgId';
|
||||
final String? token = await getToken();
|
||||
|
||||
print("Fetch Users");
|
||||
@ -110,7 +113,59 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
print("handDel - $userId");
|
||||
}
|
||||
|
||||
void handleToggleUserStatus(String userId, String currentStatus) async {
|
||||
Future<void> createUserData(
|
||||
Map<String, dynamic> userData, String userId, String newStatus) async {
|
||||
final uri = Uri.parse('$apiUrl/api/users/update/$userId');
|
||||
|
||||
final String? token = await getToken();
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
// Use MultipartRequest (POST only)
|
||||
final request = http.MultipartRequest('POST', uri);
|
||||
request.headers['Authorization'] = 'Bearer $token';
|
||||
|
||||
// If updating, spoof the method Laravel-style
|
||||
|
||||
request.fields['_method'] = 'PUT';
|
||||
request.fields['user_id'] = userId;
|
||||
|
||||
print("STatus 2 - $newStatus");
|
||||
|
||||
// Add all non-null and non-empty user data fields
|
||||
userData.forEach((key, value) {
|
||||
if (value != null && value.toString().trim().isNotEmpty) {
|
||||
request.fields[key] = value.toString();
|
||||
}
|
||||
});
|
||||
|
||||
request.fields['is_active'] = newStatus;
|
||||
|
||||
print("🚀 Sending request with fields: ${request.fields}");
|
||||
|
||||
try {
|
||||
final streamedResponse = await request.send();
|
||||
final response = await http.Response.fromStream(streamedResponse);
|
||||
print("Response status: ${response.statusCode}");
|
||||
print("Response body: ${response.body}");
|
||||
|
||||
if (response.statusCode == 200 || response.statusCode == 201) {
|
||||
print("✅ User Status submitted successfully! ");
|
||||
print("📨 Response: ${response.body}");
|
||||
|
||||
refreshUserList();
|
||||
} else {
|
||||
print("❌ Submission failed. Status: ${response.statusCode}");
|
||||
print("📨 Body: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print("🔥 Error submitting user: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void handleToggleUserStatus(String userId, String currentStatus,
|
||||
Map<String, dynamic> userData) async {
|
||||
print("Toggling user status - $userId (Current: $currentStatus)");
|
||||
|
||||
final String apiUrlData =
|
||||
@ -125,28 +180,32 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
// Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1")
|
||||
String newStatus = (currentStatus == "1") ? "0" : "1";
|
||||
|
||||
try {
|
||||
final response = await http.put(
|
||||
Uri.parse(apiUrlData),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode({
|
||||
"is_active": newStatus // Set new status dynamically
|
||||
}),
|
||||
);
|
||||
print("STatus 1 - $newStatus");
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("User status updated successfully to $newStatus!");
|
||||
refreshUserList(); // Refresh users list after update
|
||||
} else {
|
||||
print("Failed to update user status. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error updating user status: $e");
|
||||
}
|
||||
createUserData(userData, userId, newStatus);
|
||||
|
||||
// try {
|
||||
// final response = await http.put(
|
||||
// Uri.parse(apiUrlData),
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer $token',
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// body: jsonEncode({
|
||||
// "is_active": newStatus // Set new status dynamically
|
||||
// }),
|
||||
// );
|
||||
//
|
||||
// if (response.statusCode == 200) {
|
||||
// print("User status updated successfully to $newStatus!");
|
||||
// refreshUserList(); // Refresh users list after update
|
||||
// } else {
|
||||
// print("Failed to update user status. Status: ${response.statusCode}");
|
||||
// print("Error: ${response.body}");
|
||||
// }
|
||||
// } catch (e) {
|
||||
// print("Error updating user status: $e");
|
||||
// }
|
||||
}
|
||||
|
||||
// Refresh user list after update
|
||||
@ -207,10 +266,12 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
// Print the resolved value
|
||||
print("CREATELIAS - $users");
|
||||
|
||||
context.go("/CreateUserDetails", extra: {
|
||||
// 'apiCountryData': apiCountryData,
|
||||
'apiUserData': users,
|
||||
});
|
||||
context.go("/CreateUserDetails"
|
||||
// extra: {
|
||||
// // 'apiCountryData': apiCountryData,
|
||||
// 'apiUserData': users,
|
||||
// }
|
||||
);
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
@ -235,7 +296,60 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text("Error: ${snapshot.error}"));
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
color: Colors.redAccent,
|
||||
size: 60,
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
"Oops!",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
"No User Available",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
" Please Create NewUser",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// ElevatedButton.icon(
|
||||
// onPressed: () {
|
||||
// // Optional: retry logic or navigation
|
||||
// },
|
||||
// icon: Icon(Icons.refresh),
|
||||
// label: Text("Try Again"),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.blueAccent,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Center(child: Text("No users found"));
|
||||
}
|
||||
@ -467,8 +581,8 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
)),
|
||||
DataCell(GestureDetector(
|
||||
onTap: () {
|
||||
handleToggleUserStatus(
|
||||
user['user_id'], user['is_active']);
|
||||
handleToggleUserStatus(user['user_id'],
|
||||
user['is_active'], user);
|
||||
},
|
||||
child: Text(
|
||||
user['is_active'] == "1"
|
||||
@ -519,6 +633,13 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
? null
|
||||
: () {
|
||||
print("USER: $user");
|
||||
|
||||
// final userJson = jsonEncode(
|
||||
// user); // Convert user map to string
|
||||
// final encodedUser =
|
||||
// Uri.encodeComponent(
|
||||
// userJson);
|
||||
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
@ -529,25 +650,6 @@ class _UserListScreenState extends State<UserListScreen> {
|
||||
},
|
||||
),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: user['is_active'] == "0"
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.delete,
|
||||
color: user['is_active'] == "0"
|
||||
? Colors.grey
|
||||
: Colors.redAccent),
|
||||
onPressed: user['is_active'] == "0"
|
||||
? null
|
||||
: () {
|
||||
print(
|
||||
"USER ID: ${user['user_id']}");
|
||||
var userId = user['user_id'];
|
||||
handleDelete(userId);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -5,86 +5,77 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
class CustomDrawer extends StatefulWidget{
|
||||
|
||||
final bool isDesktop;
|
||||
const CustomDrawer({super.key, required this.isDesktop});
|
||||
|
||||
@override
|
||||
_CustomDrawerState createState() => _CustomDrawerState();
|
||||
|
||||
}
|
||||
|
||||
class _CustomDrawerState extends State<CustomDrawer>{
|
||||
|
||||
String? token;
|
||||
Map<String,dynamic>? userData;
|
||||
Map<String, dynamic>? fetchedUserData;
|
||||
Map<String, dynamic> userDetails = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeData();
|
||||
}
|
||||
|
||||
Future<void> initializeData() async{
|
||||
|
||||
print("initializeDatainitializeData");
|
||||
token = await getToken();
|
||||
fetchedUserData = await getUserData();
|
||||
|
||||
if(token == null || fetchedUserData == null)
|
||||
{
|
||||
print("Token or USerId missing");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
userData = fetchedUserData;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<String?> getToken() async{
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("auth_token");
|
||||
}
|
||||
|
||||
Future <Map<String,dynamic>?> getUserData() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if(userDataString != null){
|
||||
try{
|
||||
userDetails = jsonDecode(userDataString);
|
||||
|
||||
return{
|
||||
"user_id" : userDetails["user_id"].toString(),
|
||||
"name" : "${userDetails["first_name"]} ${userDetails["last_name"]}",
|
||||
"email" : userDetails["email"] ?? "",
|
||||
};
|
||||
}catch (e) {
|
||||
print("Error decoding user data: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
class CustomDrawer extends StatefulWidget {
|
||||
final bool isDesktop;
|
||||
const CustomDrawer({super.key, required this.isDesktop});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context){
|
||||
_CustomDrawerState createState() => _CustomDrawerState();
|
||||
}
|
||||
|
||||
class _CustomDrawerState extends State<CustomDrawer> {
|
||||
String? token;
|
||||
Map<String, dynamic>? userData;
|
||||
Map<String, dynamic>? fetchedUserData;
|
||||
Map<String, dynamic> userDetails = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeData();
|
||||
}
|
||||
|
||||
Future<void> initializeData() async {
|
||||
print("initializeDatainitializeData");
|
||||
token = await getToken();
|
||||
fetchedUserData = await getUserData();
|
||||
|
||||
if (token == null || fetchedUserData == null) {
|
||||
print("Token or USerId missing");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
userData = fetchedUserData;
|
||||
});
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("auth_token");
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> getUserData() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if (userDataString != null) {
|
||||
try {
|
||||
userDetails = jsonDecode(userDataString);
|
||||
|
||||
return {
|
||||
"user_id": userDetails["user_id"].toString(),
|
||||
"name": "${userDetails["first_name"]} ${userDetails["last_name"]}",
|
||||
"email": userDetails["email"] ?? "",
|
||||
};
|
||||
} catch (e) {
|
||||
print("Error decoding user data: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget drawerContent = Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:(){
|
||||
onTap: () {
|
||||
print("ONTAP Custom");
|
||||
print("ONTAP Custom- $userDetails ");
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
@ -94,69 +85,71 @@ class _CustomDrawerState extends State<CustomDrawer>{
|
||||
},
|
||||
);
|
||||
},
|
||||
child :SizedBox(
|
||||
height: 80,
|
||||
child: Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
padding: EdgeInsets.all(16),
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration:BoxDecoration(
|
||||
color: Colors.blueAccent,
|
||||
shape: BoxShape.circle
|
||||
) ,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [ Text(
|
||||
userData?["name"]?.isNotEmpty == true
|
||||
? userData!["name"]![0].toUpperCase()
|
||||
: "N/A",
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),],),
|
||||
),
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style: TextStyle(color: Colors.black87, fontSize: 11),
|
||||
),
|
||||
Text(
|
||||
userData?["email"] ?? "N/A",
|
||||
style: TextStyle(color: Colors.black45, fontSize: 10),
|
||||
),
|
||||
|
||||
],)
|
||||
],)
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
child: Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
padding: EdgeInsets.all(16),
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueAccent, shape: BoxShape.circle),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"]?.isNotEmpty == true
|
||||
? userData!["name"]![0].toUpperCase()
|
||||
: "N/A",
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontSize: 25),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style:
|
||||
TextStyle(color: Colors.black87, fontSize: 11),
|
||||
),
|
||||
Text(
|
||||
userData?["email"] ?? "N/A",
|
||||
style:
|
||||
TextStyle(color: Colors.black45, fontSize: 10),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildDrawerItem(context, Icons.home,'Home', '/home'),
|
||||
_buildExpandableItem(context,Icons.assessment,'Plans',[
|
||||
_buildSubDrawerItem(context,'My Travel Request','/listPlan'),
|
||||
_buildDrawerItem(context, Icons.home, 'Home', '/home'),
|
||||
_buildExpandableItem(context, Icons.assessment, 'Plans', [
|
||||
_buildSubDrawerItem(context, 'My Travel Request', '/listPlan'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildExpandableItem(context,Icons.account_circle_outlined,'User ',[
|
||||
_buildSubDrawerItem(context,'User List','/listUser'),
|
||||
_buildExpandableItem(
|
||||
context, Icons.account_circle_outlined, 'User ', [
|
||||
_buildSubDrawerItem(context, 'User List', '/listUser'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
|
||||
_buildExpandableItem(context,Icons.policy,'Policy ',[
|
||||
_buildSubDrawerItem(context,'Policy','/Policy'),
|
||||
_buildExpandableItem(context, Icons.policy, 'Policy ', [
|
||||
_buildSubDrawerItem(context, 'Policy', '/Policy'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
||||
],
|
||||
_buildDrawerItem(context, Icons.logout, 'Logout', '/')
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@ -169,17 +162,17 @@ class _CustomDrawerState extends State<CustomDrawer>{
|
||||
);
|
||||
} else {
|
||||
// Drawer for Mobile & Tablet**
|
||||
return Drawer(child: ListView(padding: EdgeInsets.zero, children: [drawerContent]));
|
||||
return Drawer(
|
||||
child: ListView(padding: EdgeInsets.zero, children: [drawerContent]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// **Reusable Drawer Item**
|
||||
Widget _buildDrawerItem(BuildContext context, IconData icon, String title, String route)
|
||||
{
|
||||
Widget _buildDrawerItem(
|
||||
BuildContext context, IconData icon, String title, String route) {
|
||||
return ListTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
onTap: () async {
|
||||
if (route == '/') {
|
||||
// Handle logout separately
|
||||
@ -189,11 +182,11 @@ class _CustomDrawerState extends State<CustomDrawer>{
|
||||
} else {
|
||||
context.go(route);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildExpandableItem(BuildContext context, IconData icon, String title, List<Widget>children){
|
||||
Widget _buildExpandableItem(BuildContext context, IconData icon, String title,
|
||||
List<Widget> children) {
|
||||
return ExpansionTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
@ -203,16 +196,13 @@ class _CustomDrawerState extends State<CustomDrawer>{
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubDrawerItem(BuildContext context, String title, String route)
|
||||
{
|
||||
Widget _buildSubDrawerItem(BuildContext context, String title, String route) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
onTap: (){
|
||||
onTap: () {
|
||||
context.go(route);
|
||||
if (!widget.isDesktop) Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:frontend/Screens/authentication/login/login_page.dart';
|
||||
import 'package:frontend/Screens/authentication/loginPage1.dart';
|
||||
@ -11,7 +12,6 @@ import 'package:go_router/go_router.dart';
|
||||
|
||||
final GoRouter router = GoRouter(
|
||||
routes: [
|
||||
|
||||
GoRoute(
|
||||
path: '/',
|
||||
builder: (context, state) => LoginPage(),
|
||||
@ -35,11 +35,27 @@ final GoRouter router = GoRouter(
|
||||
GoRoute(
|
||||
path: '/CreateUserDetails',
|
||||
builder: (context, state) => CreateUserForm(),
|
||||
// builder: (context, state) {
|
||||
// final userParam = state.uri.queryParameters['user'];
|
||||
//
|
||||
// final isEditProfile =
|
||||
// state.uri.queryParameters['isEditProfile'] == 'true';
|
||||
// final isViewMode = state.uri.queryParameters['isViewMode'] == 'true';
|
||||
//
|
||||
// final user = userParam != null
|
||||
// ? jsonDecode(Uri.decodeComponent(userParam))
|
||||
// : null;
|
||||
//
|
||||
// return CreateUserForm(
|
||||
// apiselectedUser: user,
|
||||
// isEditProfile: isEditProfile,
|
||||
// isViewMode: isViewMode,
|
||||
// );
|
||||
// }
|
||||
),
|
||||
GoRoute(
|
||||
path: '/Policy',
|
||||
builder: (context,state) => Policy(),
|
||||
path: '/Policy',
|
||||
builder: (context, state) => Policy(),
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
);
|
||||
|
||||
@ -3,11 +3,8 @@ import 'package:frontend/utils/auth_utils.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../../config/apiUrl.dart';
|
||||
|
||||
|
||||
class ApiService {
|
||||
|
||||
Future<List<dynamic>> fetchCountryList() async {
|
||||
|
||||
final String apiUrldata = '$apiUrl/api/getcountryMaster';
|
||||
final token = await getToken();
|
||||
|
||||
@ -29,7 +26,8 @@ class ApiService {
|
||||
print("Country - $data");
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is! List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
throw Exception(
|
||||
"Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
return data['data'];
|
||||
@ -42,7 +40,8 @@ class ApiService {
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
final String apiUrlData = '$apiUrl/api/users';
|
||||
String? ordId = await getOrgId();
|
||||
final String apiUrlData = '$apiUrl/api/users?org_id=$ordId';
|
||||
final String? token = await getToken();
|
||||
|
||||
print("Fetch Users");
|
||||
@ -68,7 +67,6 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<List> fetchCostCenter() async {
|
||||
final String apiUrldata = '$apiUrl/api/getCostCenterMaster';
|
||||
|
||||
@ -95,27 +93,26 @@ class ApiService {
|
||||
final data = json.decode(response.body);
|
||||
print(data);
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is!List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
if (!data.containsKey('data') || data['data'] is! List) {
|
||||
throw Exception(
|
||||
"Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
List <dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
List<dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
// setState(() {
|
||||
// apiCostData = plansJson; // Store API response in state
|
||||
// if(apiCostData!.isNotEmpty){
|
||||
// selectedCostCenterId =apiCostData?.first['department_id'];
|
||||
// }
|
||||
// if(apiCostData!.isNotEmpty){
|
||||
// selectedCostCenterId =apiCostData?.first['department_id'];
|
||||
// }
|
||||
|
||||
// if (apiCostData != null && apiCostData!.isNotEmpty) {
|
||||
// selectedCostCenterId ??= apiCostData!.first['department_id']?.toString();
|
||||
// }
|
||||
// if (apiCostData != null && apiCostData!.isNotEmpty) {
|
||||
// selectedCostCenterId ??= apiCostData!.first['department_id']?.toString();
|
||||
// }
|
||||
// });
|
||||
|
||||
print('plansJSON');
|
||||
|
||||
return plansJson;
|
||||
|
||||
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
@ -137,20 +134,23 @@ class ApiService {
|
||||
Uri.parse(apiUrldata),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',},);
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
try {
|
||||
final data = json.decode(response.body);
|
||||
print(data);
|
||||
if (!data.containsKey('data') || data['data'] is! Map) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a Map");
|
||||
throw Exception(
|
||||
"Invalid response format: 'data' field is missing or not a Map");
|
||||
}
|
||||
|
||||
Map<String, dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
Map<String, dynamic> plansJson =
|
||||
data['data']; // 'data' is a Map, not a List
|
||||
|
||||
return plansJson;
|
||||
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
@ -158,6 +158,4 @@ class ApiService {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
Future<String?> getToken() async {
|
||||
@ -9,3 +11,18 @@ Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('userId');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
|
||||
return Container(
|
||||
width: widget.width ?? // Use custom width if provided, else default
|
||||
(widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.4
|
||||
? MediaQuery.of(context).size.width * 0.3
|
||||
: MediaQuery.of(context).size.width * 0.85),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
@ -42,14 +42,13 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
|
||||
),
|
||||
]
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: widget.child,
|
||||
|
||||
@ -21,16 +21,18 @@ class CustomTextFieldForexWrapper extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
_CustomTextFieldForexWrapperState createState() => _CustomTextFieldForexWrapperState();
|
||||
_CustomTextFieldForexWrapperState createState() =>
|
||||
_CustomTextFieldForexWrapperState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrapper> {
|
||||
class _CustomTextFieldForexWrapperState
|
||||
extends State<CustomTextFieldForexWrapper> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width ?? // Use custom width if provided, else default
|
||||
(widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.25
|
||||
? MediaQuery.of(context).size.width * 0.2
|
||||
: MediaQuery.of(context).size.width * 0.8),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
@ -43,14 +45,13 @@ class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrappe
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
|
||||
),
|
||||
]
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: widget.child,
|
||||
|
||||
44
pubspec.lock
44
pubspec.lock
@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
bcrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bcrypt
|
||||
sha256: "9dc3f234d5935a76917a6056613e1a6d9b53f7fa56f98e24cd49b8969307764b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -49,6 +57,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -105,6 +121,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "36a1652d99cb6bf8ccc8b9f43aded1fd60b234d23ce78af422c07f950a436ef7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -118,6 +142,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.27"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -145,7 +177,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
@ -453,6 +485,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.12.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -462,5 +502,5 @@ packages:
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.7.0 <4.0.0"
|
||||
flutter: ">=3.27.0"
|
||||
|
||||
@ -43,6 +43,7 @@ dependencies:
|
||||
dropdown_search: ^5.0.6
|
||||
file_picker: ^10.0.0
|
||||
bcrypt: ^1.1.3
|
||||
http_parser: ^4.1.2
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user