user mangement bugs
This commit is contained in:
parent
7c9968c159
commit
b246c79789
File diff suppressed because it is too large
Load Diff
@ -156,6 +156,8 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
_emailController.clear();
|
||||
_passwordController.clear();
|
||||
// Fluttertoast.showToast(
|
||||
// msg: "Login Failed: $errorMessage",
|
||||
// toastLength: Toast.LENGTH_LONG,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -15,8 +15,6 @@ import '../../utils/auth_utils.dart';
|
||||
import '../../utils/pagination.dart';
|
||||
import 'groupDetails.dart';
|
||||
|
||||
|
||||
|
||||
class GroupList extends StatefulWidget {
|
||||
@override
|
||||
_GroupListState createState() => _GroupListState();
|
||||
@ -39,7 +37,6 @@ class _GroupListState extends State<GroupList> {
|
||||
List filteredGroups = [];
|
||||
TextEditingController searchController = TextEditingController();
|
||||
|
||||
|
||||
int currentPage = 0;
|
||||
int itemsPerPage = 10;
|
||||
|
||||
@ -71,14 +68,14 @@ class _GroupListState extends State<GroupList> {
|
||||
|
||||
setState(() {
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor =
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
@ -87,15 +84,11 @@ class _GroupListState extends State<GroupList> {
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Future<List<dynamic>> fetchGroups() async {
|
||||
final result = await apiService.fetchAllGroup();
|
||||
return result; // Returning raw JSON list
|
||||
}
|
||||
|
||||
|
||||
Future<void> loadAllGroups() async {
|
||||
try {
|
||||
final result = await apiService.fetchAllGroup();
|
||||
@ -116,14 +109,21 @@ class _GroupListState extends State<GroupList> {
|
||||
filteredGroups =
|
||||
allGroups.where((group) {
|
||||
return (group['name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(group['domestic_policy_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false)
|
||||
(group['international_policy_name']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(group['domestic_policy_name']?.toLowerCase().contains(
|
||||
lowerQuery,
|
||||
) ??
|
||||
false)(
|
||||
group['international_policy_name']?.toLowerCase().contains(
|
||||
lowerQuery,
|
||||
) ??
|
||||
false,
|
||||
) ||
|
||||
(group['description']?.toLowerCase().contains(lowerQuery) ??
|
||||
false) ||
|
||||
(group['is_active']?.toLowerCase().contains(lowerQuery) ?? false);}).toList();
|
||||
(group['is_active']?.toLowerCase().contains(lowerQuery) ??
|
||||
false);
|
||||
}).toList();
|
||||
currentPage = 0;
|
||||
});
|
||||
|
||||
@ -131,10 +131,10 @@ class _GroupListState extends State<GroupList> {
|
||||
}
|
||||
|
||||
void handleActiveStatus(
|
||||
Map<String, dynamic> groupData,
|
||||
String groupId,
|
||||
String currentStatus,
|
||||
) async {
|
||||
Map<String, dynamic> groupData,
|
||||
String groupId,
|
||||
String currentStatus,
|
||||
) async {
|
||||
print("Toggling group status - $groupId (Current: $currentStatus)");
|
||||
|
||||
final String apiUrlData =
|
||||
@ -159,7 +159,7 @@ class _GroupListState extends State<GroupList> {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode({
|
||||
"is_active": newStatus // Set new status dynamically
|
||||
"is_active": newStatus, // Set new status dynamically
|
||||
}),
|
||||
);
|
||||
|
||||
@ -189,7 +189,6 @@ class _GroupListState extends State<GroupList> {
|
||||
loadAllGroups();
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(
|
||||
@ -203,21 +202,17 @@ class _GroupListState extends State<GroupList> {
|
||||
drawer: CustomDrawer(isDesktop: false),
|
||||
body: Padding(
|
||||
padding:
|
||||
isDesktop
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical:
|
||||
MediaQuery.of(context).size.height *
|
||||
0, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: buildGroupList(isDesktop)),
|
||||
],
|
||||
),
|
||||
isDesktop
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical:
|
||||
MediaQuery.of(context).size.height *
|
||||
0, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(0),
|
||||
child: Row(children: [Expanded(child: buildGroupList(isDesktop))]),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -250,9 +245,9 @@ class _GroupListState extends State<GroupList> {
|
||||
// : EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||
// padding: const EdgeInsets.all(10),
|
||||
height:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
@ -342,23 +337,22 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
onPressed: () async {
|
||||
// List<dynamic> groups = await futureGroups;
|
||||
// context.go('/CreateGroup');
|
||||
showDialog(
|
||||
// List<dynamic> groups = await futureGroups;
|
||||
// context.go('/CreateGroup');
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: null,
|
||||
layoutColor: layoutColor!,
|
||||
fetchGetGroup: refreshData
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
builder:
|
||||
(context) => GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: null,
|
||||
layoutColor: layoutColor!,
|
||||
fetchGetGroup: refreshData,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New Group",
|
||||
@ -382,49 +376,49 @@ class _GroupListState extends State<GroupList> {
|
||||
isDesktop
|
||||
? SizedBox.shrink()
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
height: 35,
|
||||
child: TextField(
|
||||
controller: searchController,
|
||||
onChanged: filterGroups,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a Group",
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF9E9DBD),
|
||||
),
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
color: Color(0xFF9E9DBD),
|
||||
size: 18,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade200,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade300,
|
||||
width: 1,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.8,
|
||||
height: 35,
|
||||
child: TextField(
|
||||
controller: searchController,
|
||||
onChanged: filterGroups,
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a Group",
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF9E9DBD),
|
||||
),
|
||||
prefixIcon: Icon(
|
||||
Icons.search,
|
||||
color: Color(0xFF9E9DBD),
|
||||
size: 18,
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade200,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade300,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
style: GoogleFonts.poppins(fontSize: 12),
|
||||
),
|
||||
),
|
||||
style: GoogleFonts.poppins(fontSize: 12),
|
||||
),
|
||||
// SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
// SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureGroups,
|
||||
@ -467,7 +461,7 @@ class _GroupListState extends State<GroupList> {
|
||||
}
|
||||
|
||||
List<dynamic> groups =
|
||||
filteredGroups.isNotEmpty ? filteredGroups : allGroups;
|
||||
filteredGroups.isNotEmpty ? filteredGroups : allGroups;
|
||||
|
||||
groups.sort((a, b) {
|
||||
DateTime dateA = DateTime.parse(a['created_on']);
|
||||
@ -477,10 +471,10 @@ class _GroupListState extends State<GroupList> {
|
||||
});
|
||||
|
||||
List paginatedGroup =
|
||||
groups
|
||||
.skip(currentPage * itemsPerPage)
|
||||
.take(itemsPerPage)
|
||||
.toList();
|
||||
groups
|
||||
.skip(currentPage * itemsPerPage)
|
||||
.take(itemsPerPage)
|
||||
.toList();
|
||||
|
||||
Widget table = LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
@ -554,128 +548,146 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
],
|
||||
rows:
|
||||
paginatedGroup.map((group) {
|
||||
String groupId =
|
||||
group['group_id'].toString(); // Get group ID
|
||||
bool isSelected = selectedGroupId == groupId;
|
||||
paginatedGroup.map((group) {
|
||||
String groupId =
|
||||
group['group_id']
|
||||
.toString(); // Get group ID
|
||||
bool isSelected = selectedGroupId == groupId;
|
||||
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['name'] ?? ''}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
return DataRow(
|
||||
cells: [
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['name'] ?? ''}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text("${group['domestic_policy_name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['domestic_policy_name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['international_policy_name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['international_policy_name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['description'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
DataCell(
|
||||
Text(
|
||||
"${group['description'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Text(
|
||||
group['is_active'] == "1"
|
||||
? "Active"
|
||||
: "Inactive",
|
||||
style: TextStyle(
|
||||
color:
|
||||
DataCell(
|
||||
Text(
|
||||
group['is_active'] == "1"
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.w400,
|
||||
? "Active"
|
||||
: "Inactive",
|
||||
style: TextStyle(
|
||||
color:
|
||||
group['is_active'] == "1"
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (group['group_id'] != null) {
|
||||
final newGroupID = int.tryParse(
|
||||
group['group_id'].toString());
|
||||
if (newGroupID != null) {
|
||||
final data = await apiService.getGroupDetailsFind(
|
||||
newGroupID); // ✅ Always an int
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: newGroupID,
|
||||
// Pass the ID
|
||||
groupData: data,
|
||||
layoutColor: layoutColor!,
|
||||
fetchGetGroup: refreshData
|
||||
),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
if (group['group_id'] != null) {
|
||||
final newGroupID = int.tryParse(
|
||||
group['group_id'].toString(),
|
||||
);
|
||||
if (newGroupID != null) {
|
||||
final data = await apiService
|
||||
.getGroupDetailsFind(
|
||||
newGroupID,
|
||||
); // ✅ Always an int
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: newGroupID,
|
||||
// Pass the ID
|
||||
groupData: data,
|
||||
layoutColor:
|
||||
layoutColor!,
|
||||
fetchGetGroup:
|
||||
refreshData,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
print(
|
||||
"something went wrong check properly",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// onTap: () {
|
||||
// context.go("/CreateGroup", extra: group);
|
||||
// },
|
||||
child: Tooltip(
|
||||
message: 'Edit Group Details',
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/edit.png',
|
||||
width: 20,
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final idStr = group['group_id'];
|
||||
final id = int.tryParse(
|
||||
idStr.toString(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
print("something went wrong check properly");
|
||||
}
|
||||
},
|
||||
|
||||
// onTap: () {
|
||||
// context.go("/CreateGroup", extra: group);
|
||||
// },
|
||||
child: Tooltip(
|
||||
message: 'Edit Group Details',
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15), ),
|
||||
if (id == null) {
|
||||
print("group_id is null");
|
||||
return;
|
||||
}
|
||||
final status = group['is_active'];
|
||||
// print("GroupId : ${group['group_id']} ");
|
||||
deleteGroup(group, id, status);
|
||||
},
|
||||
child: Tooltip(
|
||||
message: 'Delete Group Details',
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/delete.png',
|
||||
width: 20,
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final idStr = group['group_id'];
|
||||
final id = int.tryParse(idStr.toString());
|
||||
|
||||
if (id == null) {
|
||||
print("group_id is null");
|
||||
return;
|
||||
}
|
||||
final status = group['is_active'];
|
||||
// print("GroupId : ${group['group_id']} ");
|
||||
deleteGroup(group, id, status);
|
||||
},
|
||||
child: Tooltip(
|
||||
message: 'Delete Group Details',
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
@ -688,7 +700,10 @@ class _GroupListState extends State<GroupList> {
|
||||
|
||||
return Card(
|
||||
color: Colors.white,
|
||||
margin: EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 6,
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
@ -700,7 +715,8 @@ class _GroupListState extends State<GroupList> {
|
||||
children: [
|
||||
// Row 1: Policy Name and Actions
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: RichText(
|
||||
@ -718,11 +734,12 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "${object['name'] ?? 'N/A'}",
|
||||
text:
|
||||
"${object['name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -736,42 +753,54 @@ class _GroupListState extends State<GroupList> {
|
||||
onTap: () async {
|
||||
if (object['group_id'] != null) {
|
||||
final newGroupID = int.tryParse(
|
||||
object['group_id'].toString());
|
||||
object['group_id'].toString(),
|
||||
);
|
||||
if (newGroupID != null) {
|
||||
final data = await apiService.getGroupDetailsFind(
|
||||
newGroupID); // ✅ Always an int
|
||||
final data = await apiService
|
||||
.getGroupDetailsFind(
|
||||
newGroupID,
|
||||
); // ✅ Always an int
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) =>
|
||||
GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: newGroupID,
|
||||
// Pass the ID
|
||||
groupData: data,
|
||||
layoutColor: layoutColor!,
|
||||
fetchGetGroup: refreshData
|
||||
builder:
|
||||
(context) => GroupData(
|
||||
isDesktop: isDesktop,
|
||||
groupId: newGroupID,
|
||||
// Pass the ID
|
||||
groupData: data,
|
||||
layoutColor:
|
||||
layoutColor!,
|
||||
fetchGetGroup:
|
||||
refreshData,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
print("something went wrong check properly");
|
||||
print(
|
||||
"something went wrong check properly",
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// onTap: () {
|
||||
// context.go("/CreateGroup", extra: group);
|
||||
// },
|
||||
child: Tooltip( message: 'Edit Group Details',
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),),
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
child: Tooltip(
|
||||
message: 'Edit Group Details',
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/edit.png',
|
||||
width: 20,
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final idStr = object['group_id'];
|
||||
final id = int.tryParse(idStr.toString());
|
||||
final id = int.tryParse(
|
||||
idStr.toString(),
|
||||
);
|
||||
|
||||
if (id == null) {
|
||||
print("group_id is null");
|
||||
@ -783,8 +812,12 @@ class _GroupListState extends State<GroupList> {
|
||||
},
|
||||
child: Tooltip(
|
||||
message: 'Edit Group Details',
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),),
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/delete.png',
|
||||
width: 20,
|
||||
height: 15,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -792,7 +825,6 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
|
||||
SizedBox(height: 8), // Spacing
|
||||
|
||||
// Row 2:
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
@ -809,18 +841,19 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "${object['domestic_policy_name'] ?? 'N/A'}",
|
||||
text:
|
||||
"${object['domestic_policy_name'] ?? 'N/A'}",
|
||||
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8), // Spacing
|
||||
|
||||
// Row 3:
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
@ -837,18 +870,18 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "${object['international_policy_name']}",
|
||||
text:
|
||||
"${object['international_policy_name'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8), // Spacing
|
||||
|
||||
// Row 4:
|
||||
RichText(
|
||||
text: TextSpan(
|
||||
@ -865,11 +898,12 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: "${object['description'] ?? 'N/A'}",
|
||||
text:
|
||||
"${object['description'] ?? 'N/A'}",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal
|
||||
fontSize: 13,
|
||||
fontFamily: "Inter",
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -889,12 +923,12 @@ class _GroupListState extends State<GroupList> {
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
isDesktop
|
||||
? SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table, // <-- your existing table
|
||||
)
|
||||
: buildMobileCardView(paginatedGroup),
|
||||
isDesktop
|
||||
? SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table, // <-- your existing table
|
||||
)
|
||||
: buildMobileCardView(paginatedGroup),
|
||||
),
|
||||
PaginationControls(
|
||||
currentPage: currentPage,
|
||||
@ -924,4 +958,4 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -14,12 +14,13 @@ class TaxiScreen extends StatefulWidget {
|
||||
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();
|
||||
@ -97,13 +98,17 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
super.initState();
|
||||
|
||||
_addFocusListener(
|
||||
_destinationFocusNode, (focus) => _destinationFocus = focus);
|
||||
_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);
|
||||
_numPassengerFocusNode,
|
||||
(focus) => _numPassengerFocus = focus,
|
||||
);
|
||||
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
|
||||
|
||||
_destinationController = initController("destination_city");
|
||||
@ -168,7 +173,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
"location_of_pickup",
|
||||
"no_of_passengers",
|
||||
"date",
|
||||
"time"
|
||||
"time",
|
||||
];
|
||||
|
||||
// Check validation for each field
|
||||
@ -199,30 +204,33 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isMobile = sizingInfo.isMobile;
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
bool isMobile = sizingInfo.isMobile;
|
||||
bool isDesktop =
|
||||
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Container(
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
child: Column(children: _buildAccomadtionForm(isDesktop)),
|
||||
return Container(
|
||||
// color: Color(0xFFF4F4FB),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(28.0),
|
||||
child: Center(
|
||||
child: Column(children: _buildAccomadtionForm(isDesktop)),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildAccomadtionForm(bool isDesktop) {
|
||||
@ -235,7 +243,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
List<List<Widget>> rowBuilders = [
|
||||
// _builClassType(isDesktop),
|
||||
_buildSecondRow(isDesktop)
|
||||
_buildSecondRow(isDesktop),
|
||||
];
|
||||
|
||||
return [
|
||||
@ -253,19 +261,24 @@ 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();
|
||||
List<DropdownMenuItem<String>> dropdownItems =
|
||||
purposeList
|
||||
.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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -281,31 +294,28 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
Text(
|
||||
"Taxi Required For",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
isDesktop
|
||||
? Row(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop))
|
||||
: Column(children: _buildTripType(isDesktop)),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) Spacer() else SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Number of Passenger",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -319,8 +329,9 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
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",
|
||||
@ -334,28 +345,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
if (errorMessages["no_of_passengers"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) Spacer() else SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Car Type",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -369,18 +373,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
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 ?? ""}");
|
||||
}
|
||||
: null,
|
||||
onChanged:
|
||||
purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedCarType = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
|
||||
);
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
@ -388,31 +395,31 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
SizedBox.shrink()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) SizedBox.shrink() else SizedBox(height: 8),
|
||||
];
|
||||
}
|
||||
|
||||
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();
|
||||
List<DropdownMenuItem<String>> dropdownItems =
|
||||
purposeList
|
||||
.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),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -425,9 +432,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _taxiReqFocused,
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownButtonFormField<String>(
|
||||
@ -436,18 +444,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
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 ?? ""}");
|
||||
}
|
||||
: null,
|
||||
onChanged:
|
||||
purposeList.isNotEmpty
|
||||
? (newValue) {
|
||||
setState(() {
|
||||
selectedReqTaxi = newValue;
|
||||
});
|
||||
print(
|
||||
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
|
||||
);
|
||||
}
|
||||
: null,
|
||||
|
||||
items: dropdownItems,
|
||||
),
|
||||
@ -466,10 +477,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
|
||||
DateTime? pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
initialDate:
|
||||
_selectedCheckOutDate != null &&
|
||||
_selectedCheckOutDate!.isAfter(today)
|
||||
? _selectedCheckOutDate!
|
||||
: today,
|
||||
firstDate: today,
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
@ -494,8 +506,13 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
// Formatting time to HH:mm (24-hour format)
|
||||
final now = DateTime.now();
|
||||
final formattedTime = DateFormat('HH:mm').format(
|
||||
DateTime(now.year, now.month, now.day, pickedTime.hour,
|
||||
pickedTime.minute),
|
||||
DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
pickedTime.hour,
|
||||
pickedTime.minute,
|
||||
),
|
||||
);
|
||||
_timeController.text = formattedTime;
|
||||
});
|
||||
@ -509,9 +526,10 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
Text(
|
||||
"City",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -535,28 +553,21 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
if (errorMessages["destination_city"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) Spacer() else SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Location of Pickup",
|
||||
"Pickup Location",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -569,7 +580,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
controller: _locationController,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Location of Pickup",
|
||||
labelText: "Pickup Location",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
@ -580,28 +591,21 @@ 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),
|
||||
),
|
||||
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) Spacer() else SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Date",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -622,8 +626,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: Icon(Icons.calendar_today,
|
||||
size: 16, color: Colors.grey),
|
||||
suffixIcon: Icon(
|
||||
Icons.calendar_today,
|
||||
size: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -632,28 +639,21 @@ 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),
|
||||
),
|
||||
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
Spacer()
|
||||
else
|
||||
SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (isDesktop) Spacer() else SizedBox(height: 8),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Time",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldItnerarySubWrapper(
|
||||
@ -674,8 +674,11 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon:
|
||||
Icon(Icons.access_time, size: 16, color: Colors.grey),
|
||||
suffixIcon: Icon(
|
||||
Icons.access_time,
|
||||
size: 16,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -684,10 +687,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
if (errorMessages["time"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
"Required",
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
@ -702,17 +702,19 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
Text(
|
||||
"Comments",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74)),
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldWrapper(
|
||||
isFocused: _commentsFocus, // Dropdown doesn't use focus
|
||||
isDesktop: isDesktop,
|
||||
width: isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
width:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.34
|
||||
: MediaQuery.of(context).size.width * 0.66,
|
||||
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
@ -733,9 +735,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
],
|
||||
),
|
||||
if (isDesktop) Spacer(),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Column(
|
||||
children: [
|
||||
Row(
|
||||
@ -756,9 +756,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey[400], // Light grey color
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
child: Text(
|
||||
@ -767,7 +765,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: 10), // Space between buttons
|
||||
|
||||
// Save Changes Button
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
@ -775,9 +772,7 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B), // Primary color for save
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
child: Text(
|
||||
|
||||
@ -85,7 +85,8 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
return AlertDialog(
|
||||
title: const Text('Select Trip Type'),
|
||||
content: const Text(
|
||||
'Please select a trip type before adding a flight.'),
|
||||
'Please select a trip type before adding a flight.',
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
@ -118,16 +119,18 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
MouseRegion(
|
||||
cursor: widget.isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
cursor:
|
||||
widget.isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: GestureDetector(
|
||||
onTap: widget.isViewMode
|
||||
? null
|
||||
: () {
|
||||
checkClass();
|
||||
print("New data");
|
||||
},
|
||||
onTap:
|
||||
widget.isViewMode
|
||||
? null
|
||||
: () {
|
||||
checkClass();
|
||||
print("New data");
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
@ -141,7 +144,7 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
Icons.add_circle_sharp,
|
||||
size: 30,
|
||||
color: Color(0xFF114D8B),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -149,9 +152,9 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Scroll behavior based on device
|
||||
|
||||
_buildData(context, isDesktop)
|
||||
// Scroll behavior based on device
|
||||
_buildData(context, isDesktop),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -224,8 +227,11 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
|
||||
if (hour == 24 && minute == 0) {
|
||||
// 24:00 is treated as 00:00 on the next day
|
||||
dateTime = DateTime(now.year, now.month, now.day)
|
||||
.add(const Duration(days: 1));
|
||||
dateTime = DateTime(
|
||||
now.year,
|
||||
now.month,
|
||||
now.day,
|
||||
).add(const Duration(days: 1));
|
||||
} else {
|
||||
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
|
||||
throw FormatException("Invalid hour or minute");
|
||||
@ -245,7 +251,21 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
itemCount: filteredList.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = filteredList[index];
|
||||
|
||||
String? tripTypeName = '';
|
||||
switch (item["trip_type"]?.toString()) {
|
||||
case "Roundtrip":
|
||||
tripTypeName = "Round Trip";
|
||||
break;
|
||||
case "Multitrip":
|
||||
tripTypeName = "Multi-Trip";
|
||||
break;
|
||||
case "Oneway":
|
||||
tripTypeName = "One-Way";
|
||||
break;
|
||||
default:
|
||||
tripTypeName = item["trip_type"]?.toString();
|
||||
break;
|
||||
}
|
||||
return Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
@ -281,7 +301,7 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
item["trip_type"]?.toString() ?? "N/A",
|
||||
tripTypeName ?? "N/A",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
@ -313,59 +333,53 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
),
|
||||
],
|
||||
),
|
||||
Divider(
|
||||
color: Colors.blueGrey.shade50,
|
||||
),
|
||||
Divider(color: Colors.blueGrey.shade50),
|
||||
SizedBox(height: 4),
|
||||
if (isDesktop)
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Class",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11,
|
||||
),
|
||||
)),
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Class",
|
||||
style: GoogleFonts.poppins(fontSize: 11),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
"Sector",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11,
|
||||
),
|
||||
)),
|
||||
flex: 4,
|
||||
child: Text(
|
||||
"Sector",
|
||||
style: GoogleFonts.poppins(fontSize: 11),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Date",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11,
|
||||
),
|
||||
)),
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Date",
|
||||
style: GoogleFonts.poppins(fontSize: 11),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Time",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11,
|
||||
),
|
||||
)),
|
||||
flex: 2,
|
||||
child: Text(
|
||||
"Time",
|
||||
style: GoogleFonts.poppins(fontSize: 11),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
// Trip Rows
|
||||
|
||||
// Trip Rows
|
||||
if (isDesktop)
|
||||
if (item["trips"] != null && item["trips"].isNotEmpty)
|
||||
...item["trips"].map<Widget>((trip) {
|
||||
String fromPlaceCountry =
|
||||
countryMap[trip["from_place"]?.toString()] ??
|
||||
"Unknown Country";
|
||||
"Unknown Country";
|
||||
String toPlaceCountry =
|
||||
countryMap[trip["to_place"]?.toString()] ??
|
||||
"Unknown Country";
|
||||
"Unknown Country";
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||
@ -385,7 +399,7 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: Text(
|
||||
"$fromPlaceCountry (from) - (to) $toPlaceCountry",
|
||||
"$fromPlaceCountry (From) - (To) $toPlaceCountry",
|
||||
// "${trip["from_place"]?.toString()} - ${trip["to_place"]?.toString()}",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
@ -435,15 +449,22 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildKeyValueRow(
|
||||
"Class",
|
||||
getRequestForClass(trip["class"].toString()) ??
|
||||
"N/A"),
|
||||
_buildKeyValueRow("Sector",
|
||||
"${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}"),
|
||||
"Class",
|
||||
getRequestForClass(trip["class"].toString()) ??
|
||||
"N/A",
|
||||
),
|
||||
_buildKeyValueRow(
|
||||
"Date", formatDate(trip["date"] ?? "")),
|
||||
"Sector",
|
||||
"${trip["from_place"] ?? "N/A"} - ${trip["to_place"] ?? "N/A"}",
|
||||
),
|
||||
_buildKeyValueRow(
|
||||
"Time", formatTime(trip["time"] ?? "")),
|
||||
"Date",
|
||||
formatDate(trip["date"] ?? ""),
|
||||
),
|
||||
_buildKeyValueRow(
|
||||
"Time",
|
||||
formatTime(trip["time"] ?? ""),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -474,12 +495,7 @@ class _FlightListWidgetState extends State<FlightListWidget> {
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
child: Text(value, style: GoogleFonts.poppins(fontSize: 12)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -131,7 +131,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"delegationEndDate",
|
||||
// "dateOfIssue",
|
||||
// "dateOfExpiry",
|
||||
"changePassword"
|
||||
"changePassword",
|
||||
];
|
||||
|
||||
Color? layoutColor;
|
||||
@ -202,7 +202,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
print("API Selected User Has Data - $apiselectedUser");
|
||||
}
|
||||
userIdApi = apiselectedUser?["user_id"] ?? "";
|
||||
userIdApi = apiselectedUser?["user_id"] ?? "";
|
||||
controllers["Fname"]?.text = apiselectedUser?["first_name"] ?? "";
|
||||
controllers["Lname"]?.text = apiselectedUser?["last_name"] ?? "";
|
||||
controllers["email"]?.text = apiselectedUser?["email"] ?? "";
|
||||
@ -286,7 +286,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
if (apiselectedUser?["delegated_to_user_id"] != null) {
|
||||
print(
|
||||
"UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}");
|
||||
"UPDADele- ${apiselectedUser?["delegated_to_user_id"]?.toString()}",
|
||||
);
|
||||
selectedSubstituteApprover =
|
||||
apiselectedUser?["delegated_to_user_id"]?.toString() ?? "";
|
||||
|
||||
@ -303,12 +304,11 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
List<dynamic> decodedList = jsonDecode(fixedJson);
|
||||
|
||||
selectedServiceIds = decodedList.map<Map<String, dynamic>>((item) {
|
||||
final map = Map<String, dynamic>.from(item);
|
||||
return {
|
||||
"service_id": map['service_id'].toString(),
|
||||
};
|
||||
}).toList();
|
||||
selectedServiceIds =
|
||||
decodedList.map<Map<String, dynamic>>((item) {
|
||||
final map = Map<String, dynamic>.from(item);
|
||||
return {"service_id": map['service_id'].toString()};
|
||||
}).toList();
|
||||
} catch (e) {
|
||||
print("❌ Error decoding fixed agent_supported_service_ids: $e");
|
||||
selectedServiceIds = [];
|
||||
@ -380,8 +380,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
// userIdsApi = userMap.keys.toList();
|
||||
|
||||
// Handle selectedUser as a Map (not a List)
|
||||
apiselectedUser = extraData['selectedUser']
|
||||
as Map<String, dynamic>?; // Cast it as a Map
|
||||
apiselectedUser =
|
||||
extraData['selectedUser']
|
||||
as Map<String, dynamic>?; // Cast it as a Map
|
||||
isViewMode = extraData['isViewMode'] ?? false;
|
||||
isEditProfile = extraData['isEditProfile'] ?? false;
|
||||
});
|
||||
@ -443,7 +444,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
userMap = {
|
||||
for (var user in userList)
|
||||
user['user_id'].toString():
|
||||
"${user['first_name']} ${user['last_name']}"
|
||||
"${user['first_name']} ${user['last_name']}",
|
||||
};
|
||||
userIdsApi = userMap.keys.toList();
|
||||
});
|
||||
@ -481,13 +482,15 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
layoutColor =
|
||||
layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
bodyColor =
|
||||
bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
@ -538,6 +541,11 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
}
|
||||
}
|
||||
|
||||
void handleGoBack() async {
|
||||
print("hello, please Go Back");
|
||||
printFormData();
|
||||
}
|
||||
|
||||
void handleNext() async {
|
||||
print("USR Detail Next");
|
||||
printFormData();
|
||||
@ -552,35 +560,67 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
print("USERDETAILS : $data");
|
||||
|
||||
if (!isValidData(data)) {
|
||||
print("USERDETAILS : $userDetials");
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
return; // Stop execution if validation fails
|
||||
final tabs = {
|
||||
"personal": "Personal Details",
|
||||
"office": "Office Details",
|
||||
"travel": "Travel Details",
|
||||
};
|
||||
|
||||
final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"]
|
||||
|
||||
final currentIndex = tabKeys.indexOf(selectedTab ?? "personal");
|
||||
|
||||
print("currentIndex - $currentIndex");
|
||||
|
||||
bool isValid = false;
|
||||
|
||||
final currentTab = tabKeys[currentIndex];
|
||||
if (currentTab == "personal") {
|
||||
isValid = isValidData(userDetials);
|
||||
} else if (currentTab == "office") {
|
||||
isValid = isValidDataTwo(userDetials);
|
||||
} else {
|
||||
print("USERDETAILS : $userDetials");
|
||||
|
||||
final tabs = {
|
||||
"personal": "Personal Details",
|
||||
"office": "Office Details",
|
||||
"travel": "Travel Details",
|
||||
};
|
||||
|
||||
final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"]
|
||||
|
||||
final currentIndex = tabKeys.indexOf(selectedTab ?? "personal");
|
||||
|
||||
if (currentIndex < tabKeys.length - 1) {
|
||||
// Move to next tab
|
||||
setState(() {
|
||||
selectedTab = tabKeys[currentIndex + 1];
|
||||
});
|
||||
} else {
|
||||
// Already at last tab (travel), maybe submit form or show done message
|
||||
print("All tabs completed!");
|
||||
// You can trigger full form submit here
|
||||
}
|
||||
isValid = true; // Travel tab might not need validation at this point
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
print("Validation Failed on $currentTab: $userDetials");
|
||||
setState(() {}); // To trigger UI update showing errors
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentIndex < tabKeys.length - 1) {
|
||||
// Move to next tab
|
||||
setState(() {
|
||||
selectedTab = tabKeys[currentIndex + 1];
|
||||
});
|
||||
} else {
|
||||
// Final step — submit or show done
|
||||
print("All tabs completed!");
|
||||
// Submit the full form here
|
||||
}
|
||||
|
||||
// if (!isValidData(data))
|
||||
// {
|
||||
// print("USERDETAILS : $userDetials");
|
||||
// print("Validation Failed: Required fields are missing.");
|
||||
// setState(() {});
|
||||
// return; // Stop execution if validation fails
|
||||
// } else
|
||||
// {
|
||||
// print("USERDETAILS : $userDetials");
|
||||
//
|
||||
// if (currentIndex < tabKeys.length - 1) {
|
||||
// // Move to next tab
|
||||
// setState(() {
|
||||
// selectedTab = tabKeys[currentIndex + 1];
|
||||
// });
|
||||
// } else {
|
||||
// // Already at last tab (travel), maybe submit form or show done message
|
||||
// print("All tabs completed!");
|
||||
// // You can trigger full form submit here
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void handleSubmit() async {
|
||||
@ -601,7 +641,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
// Map<String, dynamic> data = userDetials;
|
||||
|
||||
if (!isValidData(data)) {
|
||||
if (!isValidData(data) && isValidDataTwo(data)) {
|
||||
print("USERDETAILS : $userDetials");
|
||||
print("Validation Failed: Required fields are missing.");
|
||||
setState(() {});
|
||||
@ -623,7 +663,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
"last_name",
|
||||
"email",
|
||||
"mobile_no",
|
||||
// "employeeCode"
|
||||
// "employeeCode",
|
||||
];
|
||||
|
||||
if (apiselectedUser == null) {
|
||||
@ -647,8 +687,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
if (data["alternate_mobile_no"] != null &&
|
||||
data["alternate_mobile_no"].toString().isNotEmpty) {
|
||||
if (!RegExp(r"^\d{10}$")
|
||||
.hasMatch(data["alternate_mobile_no"].toString())) {
|
||||
if (!RegExp(
|
||||
r"^\d{10}$",
|
||||
).hasMatch(data["alternate_mobile_no"].toString())) {
|
||||
errorMessages["alternate_mobile_no"] =
|
||||
"Enter 10 digits"; // Invalid mobile number format
|
||||
}
|
||||
@ -656,14 +697,31 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
// Email validation
|
||||
if (data["email"] != null && data["email"].toString().isNotEmpty) {
|
||||
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
|
||||
.hasMatch(data["email"].toString())) {
|
||||
if (!RegExp(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
).hasMatch(data["email"].toString())) {
|
||||
errorMessages["email"] = "Invalid email format"; // Invalid email format
|
||||
}
|
||||
}
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
bool isValidDataTwo(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
// Required fields that must not be empty
|
||||
List<String> requiredFields = ["employee_code"];
|
||||
|
||||
// Check validation for each field
|
||||
for (String field in requiredFields) {
|
||||
if (data[field] == null || data[field].toString().trim().isEmpty) {
|
||||
errorMessages[field] = "Required";
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.isEmpty; // Valid if there are no errors
|
||||
}
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -814,8 +872,10 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
print("enteredPassword - $enteredPassword ");
|
||||
|
||||
if (hashedPassword != null && hashedPassword.isNotEmpty) {
|
||||
bool isMatch =
|
||||
BCrypt.checkpw(enteredPassword, hashedPassword); // Compare passwords
|
||||
bool isMatch = BCrypt.checkpw(
|
||||
enteredPassword,
|
||||
hashedPassword,
|
||||
); // Compare passwords
|
||||
|
||||
setState(() {
|
||||
// ✅ Ensure UI updates
|
||||
@ -825,8 +885,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
print(" Password match!");
|
||||
} else {
|
||||
print(" Password NOT match!");
|
||||
errorMessages
|
||||
.remove("password"); // Clear error if password is different
|
||||
errorMessages.remove(
|
||||
"password",
|
||||
); // Clear error if password is different
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -836,32 +897,36 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
return ResponsiveBuilder(
|
||||
builder: (context, sizingInfo) {
|
||||
bool isDesktop =
|
||||
sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Color(0xFFf5f5f5),
|
||||
// appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '),
|
||||
// drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
appBar: CustomAppBar(isDesktop: isDesktop),
|
||||
drawer: CustomDrawer(isDesktop: false),
|
||||
body: Padding(
|
||||
padding: isDesktop
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal: MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical: MediaQuery.of(context).size.height *
|
||||
0, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: buildData(isDesktop, context)),
|
||||
],
|
||||
return Scaffold(
|
||||
backgroundColor: Color(0xFFf5f5f5),
|
||||
// appBar: isDesktop ? null : const CustomAppBar(title: 'Create User '),
|
||||
// drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
appBar: CustomAppBar(isDesktop: isDesktop),
|
||||
drawer: CustomDrawer(isDesktop: false),
|
||||
body: Padding(
|
||||
padding:
|
||||
isDesktop
|
||||
? EdgeInsets.symmetric(
|
||||
horizontal:
|
||||
MediaQuery.of(context).size.width *
|
||||
0.1, // 30% of screen width as horizontal padding
|
||||
vertical:
|
||||
MediaQuery.of(context).size.height *
|
||||
0, // 5% of screen height as vertical padding
|
||||
)
|
||||
: EdgeInsets.all(0),
|
||||
child: Row(
|
||||
children: [Expanded(child: buildData(isDesktop, context))],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildData(bool isDesktop, context) {
|
||||
@ -875,9 +940,10 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: isDesktop
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
height:
|
||||
isDesktop
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(0.0),
|
||||
child: _buildUserDetails(isDesktop),
|
||||
@ -887,25 +953,32 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: isDesktop
|
||||
? Row(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child:
|
||||
isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: (selectedTab == "travel" ||
|
||||
selectedRole == "5" ||
|
||||
setSelectesUserType == true)
|
||||
? _buildSubmit(isDesktop, layoutColor!)
|
||||
: _buildNext(isDesktop, layoutColor!),
|
||||
children:
|
||||
(selectedTab == "travel" ||
|
||||
selectedRole == "5" ||
|
||||
setSelectesUserType == true)
|
||||
? _buildSubmit(isDesktop, layoutColor!)
|
||||
: _buildNext(
|
||||
isDesktop,
|
||||
layoutColor!,
|
||||
), // _buildGoBack(isDesktop, layoutColor!),
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: (selectedTab == "travel" ||
|
||||
selectedRole == "5" ||
|
||||
setSelectesUserType == true)
|
||||
? _buildSubmit(isDesktop, layoutColor!)
|
||||
: _buildNext(isDesktop, layoutColor!),
|
||||
)),
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children:
|
||||
(selectedTab == "travel" ||
|
||||
selectedRole == "5" ||
|
||||
setSelectesUserType == true)
|
||||
? _buildSubmit(isDesktop, layoutColor!)
|
||||
: _buildNext(isDesktop, layoutColor!),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -944,25 +1017,22 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
// })
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 18,
|
||||
),
|
||||
SizedBox(height: 18),
|
||||
isDesktop
|
||||
? buildTabsForUser()
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: buildTabsForUser(),
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: buildTabsForUser(),
|
||||
),
|
||||
Container(
|
||||
// color: Colors.yellow.shade50,
|
||||
|
||||
height: MediaQuery.of(context).size.height * 0.64,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: buildTabContents(isDesktop, isViewMode)),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -977,7 +1047,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
personalDetailsKey: personalDetailsKey,
|
||||
isDesktop: isDesktop, // pass isDesktop as a named argument
|
||||
isViewMode: isViewMode,
|
||||
userIdApi:userIdApi,
|
||||
userIdApi: userIdApi,
|
||||
controllers: controllers,
|
||||
errorMessages: errorMessages,
|
||||
selectedGender: selectedGender,
|
||||
@ -1012,7 +1082,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
return OfficeDetails(
|
||||
isDesktop: isDesktop, // pass isDesktop as a named argument
|
||||
isViewMode: isViewMode,
|
||||
userIdApi:userIdApi,
|
||||
userIdApi: userIdApi,
|
||||
controllers: controllers,
|
||||
errorMessages: errorMessages,
|
||||
selectedLevel: selectedLevel,
|
||||
@ -1063,14 +1133,14 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
errorMessages: errorMessages,
|
||||
travelDetails: travelDetailsDataFromAPI, // 👈 Pass this down
|
||||
passportFileUrlFromApi: passportFileUrlFromApi,
|
||||
userIdApi:userIdApi,
|
||||
userIdApi: userIdApi,
|
||||
);
|
||||
default:
|
||||
return PersonalDetails(
|
||||
personalDetailsKey: personalDetailsKey,
|
||||
isDesktop: isDesktop, // pass isDesktop as a named argument
|
||||
isViewMode: isViewMode,
|
||||
userIdApi:userIdApi,
|
||||
userIdApi: userIdApi,
|
||||
controllers: controllers,
|
||||
errorMessages: errorMessages,
|
||||
selectedGender: selectedGender,
|
||||
@ -1101,9 +1171,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
Map<String, String> getTabs(bool setSelectesUserType) {
|
||||
if (setSelectesUserType || selectedRole == "5") {
|
||||
return {
|
||||
"personal": "Personal Details",
|
||||
};
|
||||
return {"personal": "Personal Details"};
|
||||
} else {
|
||||
return allTabs;
|
||||
}
|
||||
@ -1113,50 +1181,102 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.end, // important
|
||||
children: tabs.entries.map((entry) {
|
||||
final isSelected = selectedTab == entry.key;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedTab = entry.key;
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 24.0), // space between tabs
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
entry.value,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isSelected ? Color(0xFF114D8B) : Color(0xFF475569),
|
||||
),
|
||||
children:
|
||||
tabs.entries.map((entry) {
|
||||
final isSelected = selectedTab == entry.key;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
bool isValid = false;
|
||||
|
||||
final currentTab = selectedTab;
|
||||
if (currentTab == "personal") {
|
||||
isValid = isValidData(userDetials);
|
||||
|
||||
if (isValid) {
|
||||
selectedTab = entry.key;
|
||||
}
|
||||
} else if (currentTab == "office") {
|
||||
isValid = isValidDataTwo(userDetials);
|
||||
if (isValid) {
|
||||
selectedTab = entry.key;
|
||||
}
|
||||
} else {
|
||||
isValid =
|
||||
true; // Travel tab might not need validation at this point
|
||||
selectedTab = entry.key;
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
right: 24.0,
|
||||
), // space between tabs
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
entry.value,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color:
|
||||
isSelected ? Color(0xFF114D8B) : Color(0xFF475569),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
height: 2,
|
||||
width: isSelected ? 50 : 0, // small line
|
||||
color: Color(0xFF114D8B),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
height: 2,
|
||||
width: isSelected ? 50 : 0, // small line
|
||||
color: Color(0xFF114D8B),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
// ---- Submit ---------------------------------------
|
||||
// ---- Submit ---------------------------------------
|
||||
|
||||
List<Widget> _buildGoBack(isDesktop, Color layoutColor) {
|
||||
return [
|
||||
MouseRegion(
|
||||
cursor:
|
||||
isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
isViewMode ? layoutColor : layoutColor, // Keep original color
|
||||
foregroundColor:
|
||||
isViewMode ? Colors.white : Colors.white, // Keep original color
|
||||
disabledBackgroundColor:
|
||||
layoutColor, // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: handleGoBack,
|
||||
child: Text("Back"),
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildNext(isDesktop, Color layoutColor) {
|
||||
return [
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
cursor:
|
||||
isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
@ -1177,41 +1297,42 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
// isViewMode ? null : handleNext, // Disable when in view mode
|
||||
child: Text("Next"),
|
||||
),
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildSubmit(isDesktop, Color layoutColor) {
|
||||
return [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
onPressed: () {
|
||||
isEditProfile ? context.go('/listPlan') : context.go('/listUser');
|
||||
},
|
||||
child: Text("Cancel")),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
isEditProfile ? context.go('/listPlan') : context.go('/listUser');
|
||||
},
|
||||
child: Text("Cancel"),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
if (!isViewMode)
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
cursor:
|
||||
isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
isViewMode ? layoutColor : layoutColor, // Keep original color
|
||||
foregroundColor: isViewMode
|
||||
? Colors.white
|
||||
: Colors.white, // Keep original color
|
||||
foregroundColor:
|
||||
isViewMode
|
||||
? Colors.white
|
||||
: Colors.white, // Keep original color
|
||||
disabledBackgroundColor:
|
||||
layoutColor, // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
@ -1225,7 +1346,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
||||
isViewMode ? null : handleSubmit, // Disable when in view mode
|
||||
child: Text("Submit"),
|
||||
),
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ class OfficeDetails extends StatefulWidget {
|
||||
final bool isViewMode;
|
||||
final String? userIdApi;
|
||||
|
||||
|
||||
final ValueChanged<String?>? onLevelChanged;
|
||||
final ValueChanged<String?>? onDepartmentChanged;
|
||||
final ValueChanged<String?>? onFirstApproverChanged;
|
||||
@ -157,17 +156,25 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
fetchFindGroup();
|
||||
}
|
||||
|
||||
Future<void> apiCheckDuplicate(String label, String field, String value, String? userId) async {
|
||||
Future<void> apiCheckDuplicate(
|
||||
String label,
|
||||
String field,
|
||||
String value,
|
||||
String? userId,
|
||||
) async {
|
||||
try {
|
||||
// Basic validation: Check mobile number length
|
||||
|
||||
if (field == "employeeCode") {
|
||||
field = "employee_code";
|
||||
}
|
||||
|
||||
final response = await apiService.CheckDuplicate(label, field, value, userId);
|
||||
final response = await apiService.CheckDuplicate(
|
||||
label,
|
||||
field,
|
||||
value,
|
||||
userId,
|
||||
);
|
||||
if (response.isNotEmpty) {
|
||||
_clearError(field);
|
||||
widget.errorMessages[field] = response['message'] ?? "$label already exists";
|
||||
widget.errorMessages[field] =
|
||||
response['message'] ?? "$label Already Exists";
|
||||
print("Duplicate found: ${response['message']}");
|
||||
return;
|
||||
} else {
|
||||
@ -290,7 +297,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
SizedBox(height: 20),
|
||||
_buildFirstRow(widget.isDesktop),
|
||||
if (widget.isDesktop) SizedBox(height: 10),
|
||||
_buildSecondRow(widget.isDesktop),
|
||||
@ -407,7 +414,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Employee Code",
|
||||
"Employee Code *",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -425,8 +432,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
controller: widget.controllers["employeeCode"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("employeeCode");
|
||||
apiCheckDuplicate("Employee Code", "employeeCode",value,widget.userIdApi);
|
||||
_clearError("employee_code");
|
||||
apiCheckDuplicate(
|
||||
"Employee Code",
|
||||
"employee_code",
|
||||
value,
|
||||
widget.userIdApi,
|
||||
);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Employee Code",
|
||||
@ -441,10 +453,10 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.errorMessages["employeeCode"] != null) ...[
|
||||
if (widget.errorMessages["employee_code"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
widget.errorMessages["employeeCode"]!,
|
||||
widget.errorMessages["employee_code"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
@ -452,7 +464,71 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildDepartmentFieldOld() {
|
||||
// return Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Department",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF575A74),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 5),
|
||||
// CustomTextFieldUserWrapper(
|
||||
// isFocused: false, // Dropdown doesn't use focus
|
||||
// isDesktop: widget.isDesktop,
|
||||
// child: SizedBox(
|
||||
// height: 45, // Set appropriate height
|
||||
// child: DropdownButtonFormField<String>(
|
||||
// value: selectedDepartment,
|
||||
// // value: widget.isViewMode ? null : selectedDepartment,
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// decoration: InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// horizontal: 10,
|
||||
// ), // Proper padding
|
||||
// ),
|
||||
// onChanged:
|
||||
// widget.isViewMode
|
||||
// ? null
|
||||
// : (newValue) {
|
||||
// setState(() {
|
||||
// selectedDepartment = newValue;
|
||||
// });
|
||||
// widget.onDepartmentChanged?.call(newValue);
|
||||
// },
|
||||
//
|
||||
// items:
|
||||
// apiCostData?.map<DropdownMenuItem<String>>((item) {
|
||||
// return DropdownMenuItem(
|
||||
//
|
||||
// value: item['department_id'], // ID as value
|
||||
// child: Text(item['name'] ?? "Unknown"),
|
||||
// );
|
||||
// }).toList(),
|
||||
// hint: Text("Select"),
|
||||
// disabledHint: Text(
|
||||
// selectedDepartment ?? "Select Department",
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildDepartmentField() {
|
||||
// Map department_id to department_name
|
||||
Map<String, String> departmentMap = {
|
||||
for (var item in apiCostData ?? [])
|
||||
item['department_id'] as String: item['name'] as String,
|
||||
};
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -466,41 +542,74 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false, // Dropdown doesn't use focus
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 45, // Set appropriate height
|
||||
child: DropdownButtonFormField<String>(
|
||||
value: selectedDepartment,
|
||||
// value: widget.isViewMode ? null : selectedDepartment,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
), // Proper padding
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: departmentMap[selectedDepartment],
|
||||
enabled: !widget.isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose,
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Department...",
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
items: departmentMap.values.toList(),
|
||||
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
(context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Department",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
widget.isViewMode
|
||||
? null
|
||||
: (newValue) {
|
||||
: (String? newValue) {
|
||||
if (newValue == null) return;
|
||||
|
||||
final departmentId =
|
||||
departmentMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
|
||||
setState(() {
|
||||
selectedDepartment = newValue;
|
||||
selectedDepartment = departmentId;
|
||||
});
|
||||
widget.onDepartmentChanged?.call(newValue);
|
||||
|
||||
widget.onDepartmentChanged?.call(
|
||||
departmentId,
|
||||
); // Send department_id
|
||||
},
|
||||
items:
|
||||
apiCostData?.map<DropdownMenuItem<String>>((item) {
|
||||
return DropdownMenuItem(
|
||||
value: item['department_id'], // ID as value
|
||||
child: Text(item['name'] ?? "Unknown"),
|
||||
);
|
||||
}).toList(),
|
||||
hint: Text("Select"),
|
||||
disabledHint: Text(
|
||||
selectedDepartment ?? "Select Department",
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -585,6 +694,17 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
@ -686,6 +806,22 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
menuProps: const MenuProps(
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
@ -806,6 +942,22 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
menuProps: const MenuProps(
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
@ -925,6 +1077,22 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
menuProps: const MenuProps(
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
@ -1047,6 +1215,22 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
menuProps: const MenuProps(
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 11.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
|
||||
@ -157,7 +157,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> apiCheckDuplicate(String label, String field, String value, String? userId) async {
|
||||
Future<void> apiCheckDuplicate(
|
||||
String label,
|
||||
String field,
|
||||
String value,
|
||||
String? userId,
|
||||
) async {
|
||||
try {
|
||||
// Basic validation: Check mobile number length
|
||||
if (field == "mobile_no" && value.length != 10) {
|
||||
@ -167,10 +172,16 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
return; // Skip the API call if input is invalid
|
||||
}
|
||||
|
||||
final response = await apiService.CheckDuplicate(label, field, value, userId);
|
||||
final response = await apiService.CheckDuplicate(
|
||||
label,
|
||||
field,
|
||||
value,
|
||||
userId,
|
||||
);
|
||||
if (response.isNotEmpty) {
|
||||
_clearError(field);
|
||||
widget.errorMessages[field] = response['message'] ?? "$label already exists";
|
||||
widget.errorMessages[field] =
|
||||
response['message'] ?? "$label Already Exists";
|
||||
print("Duplicate found: ${response['message']}");
|
||||
return;
|
||||
} else {
|
||||
@ -182,9 +193,6 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
setState(() {
|
||||
widget.errorMessages.remove(field);
|
||||
@ -305,11 +313,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(height: 10),
|
||||
SizedBox(height: 20),
|
||||
_buildFirstRow(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
_buildSecondRow(widget.isDesktop),
|
||||
// SizedBox(height: 10),
|
||||
SizedBox(height: 10),
|
||||
_buildThirdRow(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
_buildForthRow(widget.isDesktop),
|
||||
@ -731,7 +739,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"First Name",
|
||||
"First Name *",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -781,7 +789,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Last Name",
|
||||
"Last Name*",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -822,7 +830,107 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildGenderFieldOld() {
|
||||
// return Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Gender",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF575A74),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 5),
|
||||
// CustomTextFieldUserWrapper(
|
||||
// width:
|
||||
// widget.isDesktop
|
||||
// ? MediaQuery.of(context).size.width * 0.12
|
||||
// : null,
|
||||
// isFocused: false,
|
||||
// isDesktop: widget.isDesktop,
|
||||
// child: SizedBox(
|
||||
// height: 40,
|
||||
// child: DropdownButtonFormField<String>(
|
||||
// // value: selectedGender,
|
||||
// value: widget.isViewMode ? null : selectedGender,
|
||||
// onChanged:
|
||||
// widget.isViewMode
|
||||
// ? null
|
||||
// : (String? newValue) {
|
||||
// setState(() {
|
||||
// selectedGender = newValue;
|
||||
// print("selectedGender - $selectedGender");
|
||||
// });
|
||||
//
|
||||
// widget.onGenderChanged?.call(newValue);
|
||||
// },
|
||||
// decoration: InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// enabled: !widget.isViewMode, // Disables input when in view mode
|
||||
// ),
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// items: [
|
||||
// DropdownMenuItem(value: "Male", child: Text("Male")),
|
||||
// DropdownMenuItem(value: "Female", child: Text("Female")),
|
||||
// ],
|
||||
// hint: Text(
|
||||
// selectedGender ?? "Select Gender",
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// ),
|
||||
// disabledHint: Text(
|
||||
// selectedGender ?? "Select Gender",
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// // child: DropdownSearch<String>(
|
||||
// // selectedItem: selectedGender,
|
||||
// // // key: ValueKey(selectedGender),
|
||||
// // popupProps: PopupProps.menu(
|
||||
// // fit: FlexFit.loose, // Allows flexible height
|
||||
// // constraints: BoxConstraints(maxHeight: 250),
|
||||
// //
|
||||
// // ),
|
||||
// // items: ["Male", "Female", "Others"],
|
||||
// // dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
// // dropdownSearchDecoration: InputDecoration(
|
||||
// // border: InputBorder.none,
|
||||
// // contentPadding: EdgeInsets.symmetric(horizontal: 1,),
|
||||
// // ),
|
||||
// // ),
|
||||
// // dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item
|
||||
// // alignment: Alignment.centerLeft,
|
||||
// // child: Text(
|
||||
// // selectedItem ?? "Select",
|
||||
// // style: TextStyle(fontSize: 12),
|
||||
// // ),
|
||||
// // ),
|
||||
// // onChanged: isViewMode
|
||||
// // ? null
|
||||
// // : (String? newValue) {
|
||||
// // setState(() {
|
||||
// // // Find the country_code based on selected country_name
|
||||
// // selectedGender = newValue;
|
||||
// // print("selectedGender - $selectedGender");
|
||||
// // // if (selectedCountry!.isNotEmpty) {
|
||||
// // // errorMessages.remove("country_code");
|
||||
// // // }
|
||||
// // });
|
||||
// // },
|
||||
// //
|
||||
// //
|
||||
// // ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildGenderField() {
|
||||
List<String> genderOptions = ["Male", "Female"];
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -844,9 +952,45 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownButtonFormField<String>(
|
||||
// value: selectedGender,
|
||||
value: widget.isViewMode ? null : selectedGender,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: selectedGender,
|
||||
enabled: !widget.isViewMode,
|
||||
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: false, // Set true if you want search
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
),
|
||||
items: genderOptions,
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
(context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Gender",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
widget.isViewMode
|
||||
? null
|
||||
@ -855,65 +999,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
selectedGender = newValue;
|
||||
print("selectedGender - $selectedGender");
|
||||
});
|
||||
|
||||
widget.onGenderChanged?.call(newValue);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
enabled: !widget.isViewMode, // Disables input when in view mode
|
||||
),
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
items: [
|
||||
DropdownMenuItem(value: "Male", child: Text("Male")),
|
||||
DropdownMenuItem(value: "Female", child: Text("Female")),
|
||||
],
|
||||
hint: Text(
|
||||
selectedGender ?? "Select Gender",
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
disabledHint: Text(
|
||||
selectedGender ?? "Select Gender",
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
),
|
||||
|
||||
// child: DropdownSearch<String>(
|
||||
// selectedItem: selectedGender,
|
||||
// // key: ValueKey(selectedGender),
|
||||
// popupProps: PopupProps.menu(
|
||||
// fit: FlexFit.loose, // Allows flexible height
|
||||
// constraints: BoxConstraints(maxHeight: 250),
|
||||
//
|
||||
// ),
|
||||
// items: ["Male", "Female", "Others"],
|
||||
// dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
// dropdownSearchDecoration: InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(horizontal: 1,),
|
||||
// ),
|
||||
// ),
|
||||
// dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item
|
||||
// alignment: Alignment.centerLeft,
|
||||
// child: Text(
|
||||
// selectedItem ?? "Select",
|
||||
// style: TextStyle(fontSize: 12),
|
||||
// ),
|
||||
// ),
|
||||
// onChanged: isViewMode
|
||||
// ? null
|
||||
// : (String? newValue) {
|
||||
// setState(() {
|
||||
// // Find the country_code based on selected country_name
|
||||
// selectedGender = newValue;
|
||||
// print("selectedGender - $selectedGender");
|
||||
// // if (selectedCountry!.isNotEmpty) {
|
||||
// // errorMessages.remove("country_code");
|
||||
// // }
|
||||
// });
|
||||
// },
|
||||
//
|
||||
//
|
||||
// ),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1014,7 +1102,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Email",
|
||||
"Email *",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -1032,7 +1120,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
controller: widget.controllers["email"],
|
||||
onChanged: (value) {
|
||||
_clearError("email");
|
||||
apiCheckDuplicate("Email", "email",value,widget.userIdApi);
|
||||
apiCheckDuplicate("Email", "email", value, widget.userIdApi);
|
||||
},
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
@ -1064,7 +1152,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Mobile Number",
|
||||
"Mobile Number *",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -1083,7 +1171,12 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("mobile_no");
|
||||
apiCheckDuplicate("Mobile Number", "mobile_no",value,widget.userIdApi);
|
||||
apiCheckDuplicate(
|
||||
"Mobile Number",
|
||||
"mobile_no",
|
||||
value,
|
||||
widget.userIdApi,
|
||||
);
|
||||
},
|
||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||
inputFormatters: [
|
||||
@ -1208,6 +1301,18 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true, // Enables search functionality
|
||||
fit: FlexFit.loose, // Allows flexible height
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
@ -1323,12 +1428,79 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildRoleOld() {
|
||||
// return Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// children: [
|
||||
// Text(
|
||||
// "Role ",
|
||||
// style: GoogleFonts.poppins(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF575A74),
|
||||
// ),
|
||||
// ),
|
||||
// SizedBox(height: 5),
|
||||
// CustomTextFieldUserWrapper(
|
||||
// isFocused: false, // Dropdown doesn't use focus
|
||||
// isDesktop: widget.isDesktop,
|
||||
// child: SizedBox(
|
||||
// height: 45, // Set appropriate height
|
||||
// child: DropdownButtonFormField<String>(
|
||||
// // value: widget.isViewMode ? null : selectedRole,
|
||||
// value: selectedRole,
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// decoration: InputDecoration(
|
||||
// border: InputBorder.none,
|
||||
// contentPadding: EdgeInsets.symmetric(
|
||||
// horizontal: 10,
|
||||
// ), // Proper padding
|
||||
// ),
|
||||
// onChanged:
|
||||
// widget.isViewMode
|
||||
// ? null
|
||||
// : (newValue) {
|
||||
// setState(() {
|
||||
// selectedRole = newValue;
|
||||
// isTravelAgent = selectedRole == "5";
|
||||
// // Pass the result back to parent
|
||||
// widget.onUserTypeChanged?.call(isTravelAgent);
|
||||
// });
|
||||
// widget.onRoleChanged?.call(newValue);
|
||||
// },
|
||||
// items:
|
||||
// apiRoleData?.map<DropdownMenuItem<String>>((item) {
|
||||
// return DropdownMenuItem(
|
||||
// value: item['dropdown_key'], // ID as value
|
||||
// child: Text(item['dropdown_value'] ?? "Select Role"),
|
||||
// );
|
||||
// }).toList(),
|
||||
// hint: Text("Select Role"),
|
||||
// disabledHint: Text(
|
||||
// selectedRole ?? "Select Role",
|
||||
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildRole() {
|
||||
// Map dropdown_key (ID) -> dropdown_value (Name)
|
||||
Map<String, String> roleMap = {
|
||||
for (var item in apiRoleData ?? [])
|
||||
item['dropdown_key'] as String: item['dropdown_value'] as String,
|
||||
};
|
||||
|
||||
List<String> roleNames = roleMap.values.toList();
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Role ",
|
||||
"Role",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
@ -1337,44 +1509,73 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false, // Dropdown doesn't use focus
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 45, // Set appropriate height
|
||||
child: DropdownButtonFormField<String>(
|
||||
// value: widget.isViewMode ? null : selectedRole,
|
||||
value: selectedRole,
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
), // Proper padding
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: selectedRole != null ? roleMap[selectedRole] : null,
|
||||
enabled: !widget.isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose,
|
||||
constraints: BoxConstraints(maxHeight: 250),
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
color: Colors.white,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Role...",
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
items: roleNames,
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
(context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Role",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
widget.isViewMode
|
||||
? null
|
||||
: (newValue) {
|
||||
: (String? newValue) {
|
||||
if (newValue == null) return;
|
||||
final selectedKey =
|
||||
roleMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
|
||||
setState(() {
|
||||
selectedRole = newValue;
|
||||
isTravelAgent = selectedRole == "5";
|
||||
// Pass the result back to parent
|
||||
widget.onUserTypeChanged?.call(isTravelAgent);
|
||||
selectedRole = selectedKey;
|
||||
isTravelAgent = selectedKey == "5";
|
||||
});
|
||||
widget.onRoleChanged?.call(newValue);
|
||||
|
||||
widget.onUserTypeChanged?.call(isTravelAgent);
|
||||
widget.onRoleChanged?.call(selectedKey);
|
||||
},
|
||||
items:
|
||||
apiRoleData?.map<DropdownMenuItem<String>>((item) {
|
||||
return DropdownMenuItem(
|
||||
value: item['dropdown_key'], // ID as value
|
||||
child: Text(item['dropdown_value'] ?? "Select Role"),
|
||||
);
|
||||
}).toList(),
|
||||
hint: Text("Select Role"),
|
||||
disabledHint: Text(
|
||||
selectedRole ?? "Select Role",
|
||||
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -1429,7 +1630,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Password",
|
||||
"Password *",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@ -33,7 +33,6 @@ class TravellerDetails extends StatefulWidget {
|
||||
final String? passportFileUrlFromApi;
|
||||
final String? userIdApi;
|
||||
|
||||
|
||||
const TravellerDetails({
|
||||
Key? key,
|
||||
required this.controllers,
|
||||
@ -149,15 +148,24 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> apiCheckDuplicate(String label, String field, String value, String? userId) async {
|
||||
Future<void> apiCheckDuplicate(
|
||||
String label,
|
||||
String field,
|
||||
String value,
|
||||
String? userId,
|
||||
) async {
|
||||
var newField = "";
|
||||
try {
|
||||
if(field == "forex_card_num"){
|
||||
field = "forex_pre_paid_card_number";
|
||||
}
|
||||
final response = await apiService.CheckDuplicate(label, field, value, userId);
|
||||
final response = await apiService.CheckDuplicate(
|
||||
label,
|
||||
newField,
|
||||
value,
|
||||
userId,
|
||||
);
|
||||
if (response.isNotEmpty) {
|
||||
_clearError(field);
|
||||
widget.errorMessages[field] = response['message'] ?? "$label already exists";
|
||||
widget.errorMessages[field] =
|
||||
response['message'] ?? "$label already exists";
|
||||
print("Duplicate found: ${response['message']}");
|
||||
return;
|
||||
} else {
|
||||
@ -705,6 +713,7 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
@ -1107,7 +1116,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
controller: controllers["passportNumber"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("last_name");
|
||||
_clearError("passport_number");
|
||||
apiCheckDuplicate(
|
||||
"Passport Number",
|
||||
"passport_number",
|
||||
value,
|
||||
widget.userIdApi,
|
||||
);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Passport Number",
|
||||
@ -1122,6 +1137,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.errorMessages["passport_number"] != null) ...[
|
||||
SizedBox(height: 5), // Space before error message
|
||||
Text(
|
||||
widget.errorMessages["passport_number"]!,
|
||||
style: TextStyle(color: Colors.red, fontSize: 12),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -1181,7 +1203,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
_selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today)
|
||||
? _selectedDateOfIssue!
|
||||
: today,
|
||||
firstDate: today,
|
||||
// firstDate: today,
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
@ -1271,7 +1294,8 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
_selectedDateOfExpiry!.isAfter(today)
|
||||
? _selectedDateOfExpiry!
|
||||
: today,
|
||||
firstDate: today,
|
||||
// firstDate: today,
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
@ -2330,8 +2354,13 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
controller: controllers["forex_card_num"],
|
||||
enabled: !widget.isViewMode,
|
||||
onChanged: (value) {
|
||||
_clearError("forex_card_num");
|
||||
apiCheckDuplicate("Forex Pre-Paid Card Number", "forex_card_num",value,widget.userIdApi);
|
||||
_clearError("forex_pre_paid_card_number");
|
||||
apiCheckDuplicate(
|
||||
"Forex Pre-Paid Card Number",
|
||||
"forex_pre_paid_card_number",
|
||||
value,
|
||||
widget.userIdApi,
|
||||
);
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Card Number",
|
||||
@ -2467,9 +2496,16 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
height: 60,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here
|
||||
padding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 8,
|
||||
), // 👈 Add top padding here
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -2480,7 +2516,6 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
@ -2491,7 +2526,11 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
buildFrequentFlierInformation(entry),
|
||||
SizedBox(width: 15),
|
||||
IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -2773,30 +2812,37 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
child:
|
||||
widget.isDesktop
|
||||
? Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildHotel(entry),
|
||||
SizedBox(width: 25),
|
||||
buildHotelMembershipNum(entry),
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
removeHotelLoyaltyEntry(entry);
|
||||
});
|
||||
},
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
buildHotel(entry),
|
||||
SizedBox(width: 25),
|
||||
buildHotelMembershipNum(entry),
|
||||
SizedBox(
|
||||
height: 60,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 8,
|
||||
), // 👈 Add top padding here
|
||||
child: IconButton(
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
removeHotelLoyaltyEntry(entry);
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -2804,7 +2850,11 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
SizedBox(height: 8),
|
||||
buildHotelMembershipNum(entry),
|
||||
IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -3035,9 +3085,16 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
height: 60,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 20, left: 8), // 👈 Add top padding here
|
||||
padding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 8,
|
||||
), // 👈 Add top padding here
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -3061,7 +3118,11 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
SizedBox(height: 8),
|
||||
buildVisaValidUpTo(entry),
|
||||
IconButton(
|
||||
icon: Icon(Icons.horizontal_rule, color: Colors.red, size: 15),
|
||||
icon: Icon(
|
||||
Icons.remove_circle_sharp,
|
||||
color: Colors.red,
|
||||
size: 15,
|
||||
),
|
||||
tooltip: 'Cancel the Details',
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
@ -3187,6 +3248,116 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
);
|
||||
}
|
||||
|
||||
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
||||
|
||||
// Map for id => name
|
||||
Map<String, String> visaTypeMap = {
|
||||
for (var item in purposeList)
|
||||
item['dropdown_key'].toString(): item['dropdown_value'].toString(),
|
||||
};
|
||||
|
||||
// Selected value from entry
|
||||
String? selectedPurpose = entry['visa_type_id']?.toString();
|
||||
|
||||
if (!visaTypeMap.containsKey(selectedPurpose)) {
|
||||
selectedPurpose = null;
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Visa Type",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF575A74),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserTravellerWrapper(
|
||||
width:
|
||||
widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.17
|
||||
: null,
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem: visaTypeMap[selectedPurpose],
|
||||
enabled: purposeList.isNotEmpty,
|
||||
popupProps: PopupProps.menu(
|
||||
showSearchBox: true,
|
||||
fit: FlexFit.loose,
|
||||
menuProps: const MenuProps(backgroundColor: Colors.white),
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
itemBuilder:
|
||||
(context, item, isSelected) => Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 6,
|
||||
),
|
||||
child: Text(
|
||||
item,
|
||||
style: GoogleFonts.poppins(fontSize: 11.5),
|
||||
),
|
||||
),
|
||||
searchFieldProps: TextFieldProps(
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search Visa Type...",
|
||||
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
||||
),
|
||||
),
|
||||
),
|
||||
items: visaTypeMap.values.toList(),
|
||||
|
||||
dropdownDecoratorProps: DropDownDecoratorProps(
|
||||
dropdownSearchDecoration: InputDecoration(
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
||||
),
|
||||
),
|
||||
dropdownBuilder:
|
||||
(context, selectedItem) => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
selectedItem ?? "Select Visa Type",
|
||||
style: GoogleFonts.poppins(
|
||||
fontSize: 12,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
onChanged: (newValue) {
|
||||
if (newValue == null) return;
|
||||
|
||||
final selectedId =
|
||||
visaTypeMap.entries
|
||||
.firstWhere((entry) => entry.value == newValue)
|
||||
.key;
|
||||
|
||||
setState(() {
|
||||
entry['visa_type_id'] = selectedId;
|
||||
});
|
||||
|
||||
print("Updating form data: visa_type_id -> $selectedId");
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildVisaType3(entry) {
|
||||
if (apiData == null || apiData?['visa_type_of_visa'] == null) {
|
||||
return Center(
|
||||
child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
// List<dynamic> purposeList = apiData?['visa_type_of_visa'] ?? [];
|
||||
// List<dynamic> purposeList = [];
|
||||
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
||||
@ -3402,8 +3573,9 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
|
||||
final pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
// initialDate: initialDate,
|
||||
// firstDate: initialDate,
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
@ -3490,8 +3662,9 @@ class TravellerDetailsState extends State<TravellerDetails> {
|
||||
|
||||
final pickedDate = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: initialDate,
|
||||
firstDate: initialDate,
|
||||
// initialDate: initialDate,
|
||||
// firstDate: initialDate,
|
||||
firstDate: DateTime(1900),
|
||||
lastDate: DateTime(2100),
|
||||
);
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'dart:html' as html;
|
||||
import 'package:frontend/config/apiUrl.dart'; // 1 newly added
|
||||
import 'package:frontend/services/apiService.dart';
|
||||
@ -31,7 +31,7 @@ class _MyAppState extends State<MyApp> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here
|
||||
SemanticsBinding.instance.ensureSemantics(); // ✅ Safe here
|
||||
if (kIsWeb) {
|
||||
final uri = Uri.parse(html.window.location.href);
|
||||
if (uri.path == '/authredirection' &&
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
//api url
|
||||
const String apiUrl = 'http://apitest.tripapprovaltool.com';
|
||||
const String apiUrl = 'http://apitest.tripapprovaltool.com/tstat_be';
|
||||
// const String apiUrl = 'https://uat.tripapprovaltool.com';
|
||||
|
||||
Loading…
Reference in New Issue
Block a user