user management -TravelDetails
This commit is contained in:
parent
4435eeadd3
commit
75592f9879
@ -15,6 +15,7 @@ import '../../routes/custom_drawer.dart';
|
|||||||
import '../../services/apiService.dart';
|
import '../../services/apiService.dart';
|
||||||
import '../../utils/auth_utils.dart';
|
import '../../utils/auth_utils.dart';
|
||||||
import '../../utils/pagination.dart';
|
import '../../utils/pagination.dart';
|
||||||
|
import '../../utils/travelAgent_remarks.dart';
|
||||||
import '../../widgets/custom_popup.dart';
|
import '../../widgets/custom_popup.dart';
|
||||||
import '../../widgets/popup_listPlan_action.dart';
|
import '../../widgets/popup_listPlan_action.dart';
|
||||||
|
|
||||||
@ -530,20 +531,17 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.error_outline,
|
// const Icon(Icons.error_outline,
|
||||||
color: Colors.redAccent, size: 60),
|
// color: Colors.redAccent, size: 60),
|
||||||
const SizedBox(height: 1),
|
// const SizedBox(height: 1),
|
||||||
Text(
|
// Text(
|
||||||
"Oops!",
|
// "Oops!",
|
||||||
style: GoogleFonts.poppins(
|
// style: GoogleFonts.poppins(
|
||||||
fontSize: 12,
|
// fontSize: 12,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Colors.redAccent),
|
// color: Colors.redAccent),
|
||||||
// style: TextStyle(
|
//
|
||||||
// fontSize: 22,
|
// ),
|
||||||
// fontWeight: FontWeight.bold,
|
|
||||||
// color: Colors.redAccent)
|
|
||||||
),
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
Text(
|
Text(
|
||||||
"No Trips",
|
"No Trips",
|
||||||
@ -703,73 +701,95 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
PopupMenuButton<int>(
|
Row(
|
||||||
color: Colors.white,
|
children: [
|
||||||
padding: EdgeInsets.zero,
|
PopupMenuButton<int>(
|
||||||
offset: Offset(0, 30),
|
color: Colors.white,
|
||||||
icon: Icon(
|
padding: EdgeInsets.zero,
|
||||||
Icons.more_vert,
|
offset: Offset(0, 30),
|
||||||
color: Color(0xFF475569),
|
icon: Icon(
|
||||||
size: 14,
|
Icons.more_vert,
|
||||||
),
|
color: Color(0xFF475569),
|
||||||
itemBuilder: (context) => [
|
size: 14,
|
||||||
CustomPopupMenuEntry(
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.remove_red_eye,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(
|
|
||||||
context); // Close popup manually
|
|
||||||
ApiService.viewPlan(
|
|
||||||
context, plan.planId,
|
|
||||||
isViewMode: true);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
ApiService.viewPlan(
|
|
||||||
context, plan.planId,
|
|
||||||
isViewMode: false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/delete.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
deletePlan(plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.download,
|
|
||||||
color: Color(0xFF114D8B),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
apiService.getPdfDownload(
|
|
||||||
plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
CustomPopupMenuEntry(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.remove_red_eye,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(
|
||||||
|
context); // Close popup manually
|
||||||
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/edit.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/delete.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
deletePlan(plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.download,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
apiService.getPdfDownload(
|
||||||
|
plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Admin"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -839,6 +859,25 @@ class _ListAllPlansState extends State<ListAllPlans> {
|
|||||||
deletePlan: deletePlan,
|
deletePlan: deletePlan,
|
||||||
apiService: apiService,
|
apiService: apiService,
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId
|
||||||
|
.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Admin"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'package:frontend/data/models/plan.dart';
|
import 'package:frontend/data/models/plan.dart';
|
||||||
|
import 'package:frontend/utils/travelAgent_remarks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
@ -456,17 +457,24 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
|
|||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: const [
|
children: [
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
Icon(Icons.error_outline,
|
// Icon(Icons.error_outline,
|
||||||
color: Colors.redAccent, size: 60),
|
// color: Colors.redAccent, size: 60),
|
||||||
SizedBox(height: 1),
|
// SizedBox(height: 1),
|
||||||
Text("Oops!",
|
// Text("Oops!",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontSize: 22,
|
// fontSize: 22,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Colors.redAccent)),
|
// color: Colors.redAccent)),
|
||||||
SizedBox(height: 20),
|
SizedBox(height: 20),
|
||||||
|
isDesktop
|
||||||
|
? SizedBox(
|
||||||
|
width:
|
||||||
|
MediaQuery.of(context).size.width * 5.5,
|
||||||
|
)
|
||||||
|
: SizedBox.shrink(),
|
||||||
|
|
||||||
Text("No Trips Pending For Your Approvals",
|
Text("No Trips Pending For Your Approvals",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -605,6 +613,9 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
|
|||||||
))),
|
))),
|
||||||
DataCell(
|
DataCell(
|
||||||
Container(
|
Container(
|
||||||
|
padding: const EdgeInsets.all(3),
|
||||||
|
// width:
|
||||||
|
// 150, // Set your desired fixed size (equal width and height)
|
||||||
width: double
|
width: double
|
||||||
.infinity, // Set your desired fixed size (equal width and height)
|
.infinity, // Set your desired fixed size (equal width and height)
|
||||||
height: 25,
|
height: 25,
|
||||||
@ -628,70 +639,92 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
DataCell(
|
DataCell(
|
||||||
PopupMenuButton<int>(
|
Row(
|
||||||
color: Colors.white,
|
children: [
|
||||||
padding: EdgeInsets.zero,
|
PopupMenuButton<int>(
|
||||||
offset: Offset(0, 30),
|
color: Colors.white,
|
||||||
icon: Icon(Icons.more_vert,
|
padding: EdgeInsets.zero,
|
||||||
color: Color(0xFF475569)),
|
offset: Offset(0, 30),
|
||||||
itemBuilder: (context) => [
|
icon: Icon(Icons.more_vert,
|
||||||
CustomPopupMenuEntry(
|
color: Color(0xFF475569)),
|
||||||
child: Container(
|
itemBuilder: (context) => [
|
||||||
padding: EdgeInsets.symmetric(
|
CustomPopupMenuEntry(
|
||||||
horizontal: 8, vertical: 8),
|
child: Container(
|
||||||
child: Row(
|
padding: EdgeInsets.symmetric(
|
||||||
mainAxisSize: MainAxisSize.min,
|
horizontal: 8, vertical: 8),
|
||||||
mainAxisAlignment:
|
child: Row(
|
||||||
MainAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
mainAxisAlignment:
|
||||||
IconButton(
|
MainAxisAlignment.center,
|
||||||
icon: Icon(Icons.remove_red_eye,
|
children: [
|
||||||
color: Color(0xFF475569),
|
IconButton(
|
||||||
size: 14),
|
icon: Icon(
|
||||||
onPressed: () {
|
Icons.remove_red_eye,
|
||||||
Navigator.pop(
|
color: Color(0xFF475569),
|
||||||
context); // Close popup manually
|
size: 16),
|
||||||
ApiService.viewPlan(
|
onPressed: () {
|
||||||
context, plan.planId,
|
Navigator.pop(
|
||||||
isViewMode: true);
|
context); // Close popup manually
|
||||||
},
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/edit.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/delete.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
deletePlan(plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.download,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
apiService.getPdfDownload(
|
||||||
|
plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
IconButton(
|
),
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
ApiService.viewPlan(
|
|
||||||
context, plan.planId,
|
|
||||||
isViewMode: false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/delete.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
deletePlan(plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.download,
|
|
||||||
color: Color(0xFF114D8B),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
apiService.getPdfDownload(
|
|
||||||
plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Travel Agent"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -787,6 +820,23 @@ class _TravelAgentListPlansState extends State<TravelAgentListPlans> {
|
|||||||
deletePlan: deletePlan,
|
deletePlan: deletePlan,
|
||||||
apiService: apiService,
|
apiService: apiService,
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Travel Agent"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import '../../routes/custom_drawer.dart';
|
|||||||
import '../../services/apiService.dart';
|
import '../../services/apiService.dart';
|
||||||
import '../../utils/auth_utils.dart';
|
import '../../utils/auth_utils.dart';
|
||||||
import '../../utils/pagination.dart';
|
import '../../utils/pagination.dart';
|
||||||
|
import '../../utils/travelAgent_remarks.dart';
|
||||||
import '../../widgets/custom_popup.dart';
|
import '../../widgets/custom_popup.dart';
|
||||||
import '../../widgets/popup_listPlan_action.dart';
|
import '../../widgets/popup_listPlan_action.dart';
|
||||||
|
|
||||||
@ -505,14 +506,14 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: const [
|
children: const [
|
||||||
Icon(Icons.error_outline,
|
// Icon(Icons.error_outline,
|
||||||
color: Colors.redAccent, size: 60),
|
// color: Colors.redAccent, size: 60),
|
||||||
SizedBox(height: 1),
|
// SizedBox(height: 1),
|
||||||
Text("Oops!",
|
// Text("Oops!",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontSize: 22,
|
// fontSize: 22,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Colors.redAccent)),
|
// color: Colors.redAccent)),
|
||||||
SizedBox(height: 15),
|
SizedBox(height: 15),
|
||||||
Text("No Trips Pending For Your Approvals",
|
Text("No Trips Pending For Your Approvals",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -670,74 +671,99 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
),
|
),
|
||||||
|
|
||||||
DataCell(
|
DataCell(
|
||||||
PopupMenuButton<int>(
|
Row(
|
||||||
color: Colors.white,
|
children: [
|
||||||
padding: EdgeInsets.zero,
|
PopupMenuButton<int>(
|
||||||
offset: Offset(0, 30),
|
color: Colors.white,
|
||||||
icon: Icon(
|
padding: EdgeInsets.zero,
|
||||||
Icons.more_vert,
|
offset: Offset(0, 30),
|
||||||
color: Color(0xFF475569),
|
icon: Icon(
|
||||||
size: 14,
|
Icons.more_vert,
|
||||||
),
|
color: Color(0xFF475569),
|
||||||
itemBuilder: (context) => [
|
size: 14,
|
||||||
CustomPopupMenuEntry(
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.remove_red_eye,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(
|
|
||||||
context); // Close popup manually
|
|
||||||
viewPlanforApprover(
|
|
||||||
plan.planId,
|
|
||||||
isViewMode: true,
|
|
||||||
isApprover: true);
|
|
||||||
}),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
ApiService.viewPlanForApprover(
|
|
||||||
context, plan.planId,
|
|
||||||
isViewMode: false,
|
|
||||||
isApprover: true);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/delete.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
deletePlan(plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.download,
|
|
||||||
color: Color(0xFF114D8B),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
apiService.getPdfDownload(
|
|
||||||
plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
CustomPopupMenuEntry(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 8, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.remove_red_eye,
|
||||||
|
color:
|
||||||
|
Color(0xFF475569),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(
|
||||||
|
context); // Close popup manually
|
||||||
|
viewPlanforApprover(
|
||||||
|
plan.planId,
|
||||||
|
isViewMode: true,
|
||||||
|
isApprover: true);
|
||||||
|
}),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/edit.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
ApiService
|
||||||
|
.viewPlanForApprover(
|
||||||
|
context,
|
||||||
|
plan.planId,
|
||||||
|
isViewMode: false,
|
||||||
|
isApprover: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/delete.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
deletePlan(plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.download,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
apiService.getPdfDownload(
|
||||||
|
plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Approver"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -911,6 +937,23 @@ class _ApprovalListState extends State<ApprovalList> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "Approver"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:frontend/config/apiUrl.dart';
|
import 'package:frontend/config/apiUrl.dart';
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -115,11 +116,28 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||||||
context.go('/listPlan');
|
context.go('/listPlan');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
final body = jsonDecode(response.body);
|
||||||
|
final messages = body['messages'];
|
||||||
|
|
||||||
|
String errorMessage = 'Unknown error';
|
||||||
|
|
||||||
|
if (messages is Map && messages.isNotEmpty) {
|
||||||
|
errorMessage = messages.values.first.toString();
|
||||||
|
}
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text("Login Failed: $errorMessage"),
|
||||||
"Login Failed: ${jsonDecode(response.body)['message']}")),
|
backgroundColor: Colors.red,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
// Fluttertoast.showToast(
|
||||||
|
// msg: "Login Failed: $errorMessage",
|
||||||
|
// toastLength: Toast.LENGTH_LONG,
|
||||||
|
// gravity: ToastGravity.TOP_RIGHT,
|
||||||
|
// backgroundColor: Colors.red,
|
||||||
|
// textColor: Colors.white,
|
||||||
|
// fontSize: 16.0,
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
|||||||
@ -369,8 +369,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(
|
title: Text(
|
||||||
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
|
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}"),
|
||||||
subtitle: Text(
|
// subtitle: Text(
|
||||||
"ID: ${userType == "user" ? user.userId : user.travellerId}"),
|
// "ID: ${userType == "user" ? user.userId : user.travellerId}"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
String selectedUser =
|
String selectedUser =
|
||||||
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
|
"${user.firstName ?? "Unknown"} ${user.lastName ?? ""}";
|
||||||
|
|||||||
@ -423,6 +423,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
// Handle field changes
|
// Handle field changes
|
||||||
void _onFieldChanged() {
|
void _onFieldChanged() {
|
||||||
if (_isForexDataDurationComplete()) {
|
if (_isForexDataDurationComplete()) {
|
||||||
|
print("Calculate 1");
|
||||||
CalculateDuration();
|
CalculateDuration();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,16 +433,24 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CalculateDuration() {
|
void CalculateDuration() {
|
||||||
|
print("Calculate 2");
|
||||||
final data = getForexData;
|
final data = getForexData;
|
||||||
final startDateString = data["start_date"];
|
final startDateString = data["start_date"];
|
||||||
final endDateString = data["end_date"];
|
final endDateString = data["end_date"];
|
||||||
|
|
||||||
if (startDateString == null || endDateString == null) return;
|
if (startDateString == null || endDateString == null) return;
|
||||||
|
print(
|
||||||
|
"Calculate 3 - StarrtDAte: $startDateString --EndDate: $endDateString");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Parse the dates from string
|
// Parse the dates from string
|
||||||
final startDate = DateFormat('d/M/yyyy').parse(startDateString);
|
// final startDate = DateFormat('dd/mm/yyyy').parse(startDateString);
|
||||||
final endDate = DateFormat('d/M/yyyy').parse(endDateString);
|
// final endDate = DateFormat('dd/mm/yyyy').parse(endDateString);
|
||||||
|
|
||||||
|
final startDate = DateFormat('dd-MM-yyyy').parse(startDateString);
|
||||||
|
final endDate = DateFormat('dd-MM-yyyy').parse(endDateString);
|
||||||
|
|
||||||
|
print("Calculate 4");
|
||||||
|
|
||||||
// Calculate difference
|
// Calculate difference
|
||||||
final durationInDays =
|
final durationInDays =
|
||||||
@ -450,8 +459,10 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
// You can now use durationInDays however you want:
|
// You can now use durationInDays however you want:
|
||||||
print("Duration: $durationInDays days");
|
print("Duration: $durationInDays days");
|
||||||
|
|
||||||
// Optionally, you might want to update some data:
|
// Convert int to String
|
||||||
selectedDuration = durationInDays as String?;
|
setState(() {
|
||||||
|
selectedDuration = durationInDays.toString();
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error parsing dates: $e');
|
print('Error parsing dates: $e');
|
||||||
}
|
}
|
||||||
@ -501,8 +512,23 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
(quotedAmount * 15) ~/ 100; // Calculate 15% (integer division)
|
(quotedAmount * 15) ~/ 100; // Calculate 15% (integer division)
|
||||||
remainingAmount = quotedAmount - fifteenPercent; // Subtract from total
|
remainingAmount = quotedAmount - fifteenPercent; // Subtract from total
|
||||||
|
|
||||||
textControllers["_cash"]?.text = fifteenPercent.toString();
|
// Only set text if the field is empty (user hasn't typed)
|
||||||
textControllers["_card"]?.text = remainingAmount.toString();
|
if (textControllers["_cash"] != null &&
|
||||||
|
textControllers["_cash"]!.text.trim().isEmpty) {
|
||||||
|
textControllers["_cash"]!.text = fifteenPercent.toString();
|
||||||
|
} else {
|
||||||
|
print("_cash already has user input, not overwriting");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (textControllers["_card"] != null &&
|
||||||
|
textControllers["_card"]!.text.trim().isEmpty) {
|
||||||
|
textControllers["_card"]!.text = remainingAmount.toString();
|
||||||
|
} else {
|
||||||
|
print("_card already has user input, not overwriting");
|
||||||
|
}
|
||||||
|
|
||||||
|
// textControllers["_cash"]?.text = fifteenPercent.toString();
|
||||||
|
// textControllers["_card"]?.text = remainingAmount.toString();
|
||||||
print("15% Amount: $fifteenPercent");
|
print("15% Amount: $fifteenPercent");
|
||||||
print("Remaining Amount: $remainingAmount");
|
print("Remaining Amount: $remainingAmount");
|
||||||
} else {
|
} else {
|
||||||
@ -518,11 +544,19 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0");
|
int? qouteAmount = int.tryParse(selectedQuotedAmount ?? "0");
|
||||||
int? calculateAmnt = cashAmount! + enteredAmount!;
|
int? calculateAmnt = cashAmount! + enteredAmount!;
|
||||||
|
|
||||||
|
int checkValidAmount = cashAmount + enteredAmount;
|
||||||
|
|
||||||
|
print(
|
||||||
|
"checkValidAmount - $checkValidAmount - $enteredAmount - $cashAmount");
|
||||||
|
|
||||||
print(
|
print(
|
||||||
"CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount");
|
"CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount");
|
||||||
|
|
||||||
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
||||||
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
||||||
|
} else if (checkValidAmount < qouteAmount) {
|
||||||
|
errorMessages["deposit_on_card"] =
|
||||||
|
"Enter Valid Amount"; // Clear error if valid
|
||||||
} else {
|
} else {
|
||||||
errorMessages["deposit_on_card"] = ""; // Clear error if valid
|
errorMessages["deposit_on_card"] = ""; // Clear error if valid
|
||||||
}
|
}
|
||||||
@ -532,18 +566,29 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _validateCashAmount(String value) {
|
void _validateCashAmount(String value) {
|
||||||
|
errorMessages["deposit_on_card"] = " ";
|
||||||
print("_validateCashAmount - $value - $fifteenPercent");
|
print("_validateCashAmount - $value - $fifteenPercent");
|
||||||
|
|
||||||
int? enteredAmount = int.tryParse(value);
|
int? enteredAmount = int.tryParse(value);
|
||||||
|
|
||||||
int? quotedAmount = int.tryParse(selectedQuotedAmount!);
|
int? quotedAmount = int.tryParse(selectedQuotedAmount!);
|
||||||
int? difference = quotedAmount! - enteredAmount!;
|
int? difference = quotedAmount! - enteredAmount!;
|
||||||
|
int? cashAmount = int.tryParse(textControllers["_cash"]!.text ?? "0");
|
||||||
|
int? cardAmount = int.tryParse(textControllers["_card"]!.text ?? "0");
|
||||||
|
|
||||||
|
int checkValidAmount = cardAmount! + enteredAmount;
|
||||||
|
|
||||||
|
print(
|
||||||
|
"checkValidAmountCash - $checkValidAmount -cash- $enteredAmount -Card - $cardAmount - quotedAmount- $quotedAmount");
|
||||||
|
|
||||||
print("Difference - $difference");
|
print("Difference - $difference");
|
||||||
|
print('CardAmount - $cardAmount');
|
||||||
textControllers["_card"]?.text = difference.toString();
|
textControllers["_card"]?.text = difference.toString();
|
||||||
|
|
||||||
if (enteredAmount == null || enteredAmount > fifteenPercent) {
|
if (enteredAmount == null || enteredAmount > fifteenPercent) {
|
||||||
errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
|
errorMessages["deposit_on_cash"] = "Amount cannot exceed $fifteenPercent";
|
||||||
|
} else if (checkValidAmount == quotedAmount) {
|
||||||
|
errorMessages["deposit_on_card"] = " "; // Clear error if valid
|
||||||
} else {
|
} else {
|
||||||
errorMessages["deposit_on_cash"] = ""; // Clear error if valid
|
errorMessages["deposit_on_cash"] = ""; // Clear error if valid
|
||||||
}
|
}
|
||||||
@ -682,8 +727,8 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
if (flightFirstTripDateNotifier.value != null) {
|
if (flightFirstTripDateNotifier.value != null) {
|
||||||
try {
|
try {
|
||||||
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
initialDate = DateTime.parse(flightFirstTripDateNotifier.value!);
|
||||||
textControllers["_forexStartDate"]?.text =
|
// textControllers["_forexStartDate"]?.text =
|
||||||
DateFormat('yyyy-MM-dd').format(initialDate);
|
// DateFormat('yyyy-MM-dd').format(initialDate);
|
||||||
textControllers["_forexStartDate"]?.text =
|
textControllers["_forexStartDate"]?.text =
|
||||||
DateFormat('dd-MM-yyyy').format(initialDate);
|
DateFormat('dd-MM-yyyy').format(initialDate);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -938,7 +983,7 @@ class _ForexScreenState extends State<ForexScreen> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Durartion",
|
"Duration",
|
||||||
style: GoogleFonts.poppins(
|
style: GoogleFonts.poppins(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import 'package:http/http.dart' as http;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
import '../../services/apiService.dart';
|
||||||
|
|
||||||
class ForexListWidget extends StatelessWidget {
|
class ForexListWidget extends StatelessWidget {
|
||||||
final List<Map<String, dynamic>> forexList;
|
final List<Map<String, dynamic>> forexList;
|
||||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||||
@ -27,6 +29,7 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final isDesktop = MediaQuery.of(context).size.width > 1024;
|
final isDesktop = MediaQuery.of(context).size.width > 1024;
|
||||||
|
final ApiService apiService = ApiService();
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
@ -133,7 +136,11 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildData(BuildContext context, bool isDesktop) {
|
Widget _buildData(
|
||||||
|
BuildContext context,
|
||||||
|
bool isDesktop,
|
||||||
|
) {
|
||||||
|
final ApiService apiService = ApiService();
|
||||||
List<Map<String, dynamic>> filteredList =
|
List<Map<String, dynamic>> filteredList =
|
||||||
forexList.where((item) => item["is_active"] == "1").toList();
|
forexList.where((item) => item["is_active"] == "1").toList();
|
||||||
print("filteredList- $filteredList");
|
print("filteredList- $filteredList");
|
||||||
@ -227,6 +234,23 @@ class ForexListWidget extends StatelessWidget {
|
|||||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||||
width: 20, height: 15),
|
width: 20, height: 15),
|
||||||
),
|
),
|
||||||
|
item['forex_id'] != null
|
||||||
|
? IconButton(
|
||||||
|
icon: Icon(Icons.download,
|
||||||
|
color: Color(0xFF114D8B), size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
print("PDF- ${item['forex_id']}");
|
||||||
|
final forexIdString = item['forex_id'];
|
||||||
|
final id = int.tryParse(forexIdString ?? '');
|
||||||
|
|
||||||
|
if (id != null) {
|
||||||
|
apiService.getForexPdfDownload(id);
|
||||||
|
} else {
|
||||||
|
print("Invalid forex_id: $forexIdString");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: SizedBox.shrink(), // or Container()
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Divider(
|
Divider(
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import '../../routes/custom_drawer.dart';
|
|||||||
import '../../services/apiService.dart';
|
import '../../services/apiService.dart';
|
||||||
import '../../utils/auth_utils.dart';
|
import '../../utils/auth_utils.dart';
|
||||||
import '../../utils/pagination.dart';
|
import '../../utils/pagination.dart';
|
||||||
|
import '../../utils/travelAgent_remarks.dart';
|
||||||
import '../../widgets/custom_popup.dart';
|
import '../../widgets/custom_popup.dart';
|
||||||
import '../../widgets/popup_listPlan_action.dart';
|
import '../../widgets/popup_listPlan_action.dart';
|
||||||
|
|
||||||
@ -557,14 +558,14 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: const [
|
children: const [
|
||||||
Icon(Icons.error_outline,
|
// Icon(Icons.error_outline,
|
||||||
color: Colors.redAccent, size: 60),
|
// color: Colors.redAccent, size: 60),
|
||||||
SizedBox(height: 1),
|
// SizedBox(height: 1),
|
||||||
Text("Oops!",
|
// Text("Oops!",
|
||||||
style: TextStyle(
|
// style: TextStyle(
|
||||||
fontSize: 22,
|
// fontSize: 22,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Colors.redAccent)),
|
// color: Colors.redAccent)),
|
||||||
SizedBox(height: 15),
|
SizedBox(height: 15),
|
||||||
Text("No Trips",
|
Text("No Trips",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -743,70 +744,92 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
PopupMenuButton<int>(
|
Row(
|
||||||
color: Colors.white,
|
children: [
|
||||||
padding: EdgeInsets.zero,
|
PopupMenuButton<int>(
|
||||||
offset: Offset(0, 30),
|
color: Colors.white,
|
||||||
icon: Icon(Icons.more_vert,
|
padding: EdgeInsets.zero,
|
||||||
size: 14, color: Color(0xFF475569)),
|
offset: Offset(0, 30),
|
||||||
itemBuilder: (context) => [
|
icon: Icon(Icons.more_vert,
|
||||||
CustomPopupMenuEntry(
|
size: 14, color: Color(0xFF475569)),
|
||||||
child: Container(
|
itemBuilder: (context) => [
|
||||||
padding: EdgeInsets.symmetric(
|
CustomPopupMenuEntry(
|
||||||
horizontal: 8, vertical: 8),
|
child: Container(
|
||||||
child: Row(
|
padding: EdgeInsets.symmetric(
|
||||||
mainAxisSize: MainAxisSize.min,
|
horizontal: 8, vertical: 8),
|
||||||
mainAxisAlignment:
|
child: Row(
|
||||||
MainAxisAlignment.center,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
mainAxisAlignment:
|
||||||
IconButton(
|
MainAxisAlignment.center,
|
||||||
icon: Icon(Icons.remove_red_eye,
|
children: [
|
||||||
color: Color(0xFF475569),
|
IconButton(
|
||||||
size: 18),
|
icon: Icon(
|
||||||
onPressed: () {
|
Icons.remove_red_eye,
|
||||||
Navigator.pop(
|
color: Color(0xFF475569),
|
||||||
context); // Close popup manually
|
size: 18),
|
||||||
ApiService.viewPlan(
|
onPressed: () {
|
||||||
context, plan.planId,
|
Navigator.pop(
|
||||||
isViewMode: true);
|
context); // Close popup manually
|
||||||
},
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: true);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/edit.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
ApiService.viewPlan(
|
||||||
|
context, plan.planId,
|
||||||
|
isViewMode: false);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Image.asset(
|
||||||
|
'assets/images/IconsImg/delete.png',
|
||||||
|
width: 20,
|
||||||
|
height: 15),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
deletePlan(plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(Icons.download,
|
||||||
|
color: Color(0xFF114D8B),
|
||||||
|
size: 18),
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
apiService.getPdfDownload(
|
||||||
|
plan.planId);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
IconButton(
|
),
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
ApiService.viewPlan(
|
|
||||||
context, plan.planId,
|
|
||||||
isViewMode: false);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/delete.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
deletePlan(plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.download,
|
|
||||||
color: Color(0xFF114D8B),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
apiService.getPdfDownload(
|
|
||||||
plan.planId);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "User"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -910,6 +933,23 @@ class _ListPlansState extends State<ListPlans> {
|
|||||||
deletePlan: deletePlan,
|
deletePlan: deletePlan,
|
||||||
apiService: apiService,
|
apiService: apiService,
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.comment,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 11,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CommentModal(
|
||||||
|
// planId: plan.planId,
|
||||||
|
planId: plan.planId.toString(),
|
||||||
|
layoutColorForUser:
|
||||||
|
layoutColor!,
|
||||||
|
role: "User"),
|
||||||
|
);
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,10 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
|
|
||||||
final GlobalKey<PersonalDetailsState> personalDetailsKey =
|
final GlobalKey<PersonalDetailsState> personalDetailsKey =
|
||||||
GlobalKey<PersonalDetailsState>();
|
GlobalKey<PersonalDetailsState>();
|
||||||
|
final GlobalKey<TravellerDetailsState> travellerDetailsKey =
|
||||||
|
GlobalKey<TravellerDetailsState>();
|
||||||
|
|
||||||
|
Map<String, dynamic>? travelDetailsData;
|
||||||
|
|
||||||
late TabController _tabController;
|
late TabController _tabController;
|
||||||
|
|
||||||
@ -124,6 +128,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
Color? layoutColor;
|
Color? layoutColor;
|
||||||
Color? bodyColor;
|
Color? bodyColor;
|
||||||
|
|
||||||
|
bool setSelectesUserType = false;
|
||||||
|
|
||||||
Map<String, dynamic> get userDetials {
|
Map<String, dynamic> get userDetials {
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
"first_name": controllers["Fname"]?.text,
|
"first_name": controllers["Fname"]?.text,
|
||||||
@ -159,11 +165,18 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
"created_by": userId,
|
"created_by": userId,
|
||||||
"is_active": "1",
|
"is_active": "1",
|
||||||
"org_id": orgId,
|
"org_id": orgId,
|
||||||
|
"travel_details": travelDetailsData ?? {},
|
||||||
// "passport_fileData": passportFile,
|
// "passport_fileData": passportFile,
|
||||||
};
|
};
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleUserTypeChange(bool isTravelAgent) {
|
||||||
|
setState(() {
|
||||||
|
setSelectesUserType = isTravelAgent;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateData() async {
|
Future<void> updateData() async {
|
||||||
// Ensure apiselectedUser is not null before printing
|
// Ensure apiselectedUser is not null before printing
|
||||||
if (apiselectedUser != null) {
|
if (apiselectedUser != null) {
|
||||||
@ -474,8 +487,14 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
print("USR Detail Submit");
|
print("USR Detail Submit");
|
||||||
printFormData();
|
printFormData();
|
||||||
|
|
||||||
|
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
|
||||||
|
|
||||||
|
print("TRAVEL DETAILS FROM CHILD: $travelDetailsData");
|
||||||
|
|
||||||
Map<String, dynamic> data = userDetials;
|
Map<String, dynamic> data = userDetials;
|
||||||
|
|
||||||
|
print("USERDETAILS : $data");
|
||||||
|
|
||||||
if (!isValidData(data)) {
|
if (!isValidData(data)) {
|
||||||
print("USERDETAILS : $userDetials");
|
print("USERDETAILS : $userDetials");
|
||||||
print("Validation Failed: Required fields are missing.");
|
print("Validation Failed: Required fields are missing.");
|
||||||
@ -487,7 +506,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
final tabs = {
|
final tabs = {
|
||||||
"personal": "Personal Details",
|
"personal": "Personal Details",
|
||||||
"office": "Office Details",
|
"office": "Office Details",
|
||||||
// "travel": "Travel Details",
|
"travel": "Travel Details",
|
||||||
};
|
};
|
||||||
|
|
||||||
final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"]
|
final tabKeys = tabs.keys.toList(); // ["personal", "office", "travel"]
|
||||||
@ -511,8 +530,16 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
print("USR Detail Submit");
|
print("USR Detail Submit");
|
||||||
printFormData();
|
printFormData();
|
||||||
|
|
||||||
|
travelDetailsData = travellerDetailsKey.currentState?.travel_Detials;
|
||||||
|
|
||||||
|
print("TRAVEL DETAILS FROM CHILD: $travelDetailsData");
|
||||||
|
|
||||||
Map<String, dynamic> data = userDetials;
|
Map<String, dynamic> data = userDetials;
|
||||||
|
|
||||||
|
print("USERDETAILS : $data");
|
||||||
|
|
||||||
|
// Map<String, dynamic> data = userDetials;
|
||||||
|
|
||||||
if (!isValidData(data)) {
|
if (!isValidData(data)) {
|
||||||
print("USERDETAILS : $userDetials");
|
print("USERDETAILS : $userDetials");
|
||||||
print("Validation Failed: Required fields are missing.");
|
print("Validation Failed: Required fields are missing.");
|
||||||
@ -760,11 +787,9 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
height: isDesktop
|
height: isDesktop
|
||||||
? MediaQuery.of(context).size.height * 0.98
|
? MediaQuery.of(context).size.height * 0.98
|
||||||
: MediaQuery.of(context).size.height,
|
: MediaQuery.of(context).size.height,
|
||||||
child: SingleChildScrollView(
|
child: Padding(
|
||||||
child: Padding(
|
padding: EdgeInsets.all(0.0),
|
||||||
padding: EdgeInsets.all(0.0),
|
child: _buildUserDetails(isDesktop),
|
||||||
child: _buildUserDetails(isDesktop),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -775,13 +800,15 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
child: isDesktop
|
child: isDesktop
|
||||||
? Row(
|
? Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: selectedTab == "office"
|
children: (selectedTab == "travel" ||
|
||||||
|
setSelectesUserType == true)
|
||||||
? _buildSubmit(isDesktop, layoutColor!)
|
? _buildSubmit(isDesktop, layoutColor!)
|
||||||
: _buildNext(isDesktop, layoutColor!),
|
: _buildNext(isDesktop, layoutColor!),
|
||||||
)
|
)
|
||||||
: Row(
|
: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: selectedTab == "office"
|
children: (selectedTab == "travel" ||
|
||||||
|
setSelectesUserType == true)
|
||||||
? _buildSubmit(isDesktop, layoutColor!)
|
? _buildSubmit(isDesktop, layoutColor!)
|
||||||
: _buildNext(isDesktop, layoutColor!),
|
: _buildNext(isDesktop, layoutColor!),
|
||||||
)),
|
)),
|
||||||
@ -819,7 +846,8 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
// color: Colors.yellow.shade50,
|
// color: Colors.yellow.shade50,
|
||||||
height: MediaQuery.of(context).size.height * 0.67,
|
|
||||||
|
height: MediaQuery.of(context).size.height * 0.64,
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: buildTabContents(isDesktop, isViewMode)),
|
Expanded(child: buildTabContents(isDesktop, isViewMode)),
|
||||||
@ -846,6 +874,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
selectedCountry: selectedCountry,
|
selectedCountry: selectedCountry,
|
||||||
selectedRole: selectedRole,
|
selectedRole: selectedRole,
|
||||||
apiselectedUser: isapiselectedUser,
|
apiselectedUser: isapiselectedUser,
|
||||||
|
onUserTypeChanged: handleUserTypeChange,
|
||||||
onGenderChanged: (gender) {
|
onGenderChanged: (gender) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedGender = gender;
|
selectedGender = gender;
|
||||||
@ -900,17 +929,25 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
case "travel":
|
case "travel":
|
||||||
return TravellerDetails();
|
return TravellerDetails(
|
||||||
|
key: travellerDetailsKey,
|
||||||
|
isDesktop: isDesktop,
|
||||||
|
isViewMode: isViewMode,
|
||||||
|
controllers: controllers,
|
||||||
|
errorMessages: errorMessages,
|
||||||
|
);
|
||||||
default:
|
default:
|
||||||
return PersonalDetails(
|
return PersonalDetails(
|
||||||
personalDetailsKey: personalDetailsKey,
|
personalDetailsKey: personalDetailsKey,
|
||||||
isDesktop: isDesktop, // pass isDesktop as a named argument
|
isDesktop: isDesktop, // pass isDesktop as a named argument
|
||||||
isViewMode: isViewMode,
|
isViewMode: isViewMode,
|
||||||
controllers: controllers,
|
controllers: controllers,
|
||||||
errorMessages: errorMessages,
|
errorMessages: errorMessages,
|
||||||
selectedGender: selectedGender,
|
selectedGender: selectedGender,
|
||||||
selectedCountry: selectedCountry,
|
selectedCountry: selectedCountry,
|
||||||
apiselectedUser: isapiselectedUser);
|
apiselectedUser: isapiselectedUser,
|
||||||
|
onUserTypeChanged: handleUserTypeChange,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,7 +955,7 @@ class _CreateUserFormDetialsState extends State<CreateUserFormDetials> {
|
|||||||
final tabs = {
|
final tabs = {
|
||||||
"personal": "Personal Details",
|
"personal": "Personal Details",
|
||||||
"office": "Office Details",
|
"office": "Office Details",
|
||||||
// "travel": "Travel Details",
|
"travel": "Travel Details",
|
||||||
};
|
};
|
||||||
|
|
||||||
return Row(
|
return Row(
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import 'package:dropdown_search/dropdown_search.dart';
|
|||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import '../../../services/apiService.dart';
|
import '../../../services/apiService.dart';
|
||||||
@ -294,11 +295,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Employee Code",
|
Text(
|
||||||
style: TextStyle(
|
"Employee Code",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -306,12 +307,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["employeeCode"],
|
controller: widget.controllers["employeeCode"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Employee Code",
|
labelText: "Employee Code",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -327,11 +329,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Department",
|
Text(
|
||||||
style: TextStyle(
|
"Department",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false, // Dropdown doesn't use focus
|
isFocused: false, // Dropdown doesn't use focus
|
||||||
@ -340,7 +342,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
height: 45, // Set appropriate height
|
height: 45, // Set appropriate height
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
value: widget.isViewMode ? null : selectedDepartment,
|
value: widget.isViewMode ? null : selectedDepartment,
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding:
|
contentPadding:
|
||||||
@ -363,7 +365,7 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
hint: Text("Select"),
|
hint: Text("Select"),
|
||||||
disabledHint: Text(
|
disabledHint: Text(
|
||||||
selectedDepartment ?? "Select Department",
|
selectedDepartment ?? "Select Department",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -376,11 +378,11 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Group",
|
Text(
|
||||||
style: TextStyle(
|
"Group",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
// CustomTextFieldUserWrapper(
|
// CustomTextFieldUserWrapper(
|
||||||
// isFocused: false,
|
// isFocused: false,
|
||||||
@ -474,7 +476,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedItem ?? "Select",
|
selectedItem ?? "Select",
|
||||||
style: TextStyle(fontSize: 12),
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -512,11 +516,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("First Approver",
|
Text(
|
||||||
style: TextStyle(
|
"First Approver",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12,
|
||||||
color: Colors.black)),
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -561,7 +567,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedItem ?? "Select",
|
selectedItem ?? "Select",
|
||||||
style: TextStyle(fontSize: 12),
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// onChanged: (String? newValue) {
|
// onChanged: (String? newValue) {
|
||||||
@ -620,11 +628,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Second Approver",
|
Text(
|
||||||
style: TextStyle(
|
"Second Approver",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12,
|
||||||
color: Colors.black)),
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -669,7 +679,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedItem ?? "Select",
|
selectedItem ?? "Select",
|
||||||
style: TextStyle(fontSize: 12),
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -727,11 +739,13 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Third Approver",
|
Text(
|
||||||
style: TextStyle(
|
"Third Approver",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12,
|
||||||
color: Colors.black)),
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.black),
|
||||||
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -776,7 +790,9 @@ class _OfficeDetailsState extends State<OfficeDetails> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedItem ?? "Select",
|
selectedItem ?? "Select",
|
||||||
style: TextStyle(fontSize: 12),
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onChanged: (String? newValue) {
|
onChanged: (String? newValue) {
|
||||||
|
|||||||
@ -1,12 +1,15 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io' as html;
|
import 'dart:io' as html;
|
||||||
|
|
||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
import '../../../services/apiService.dart';
|
import '../../../services/apiService.dart';
|
||||||
|
import '../../../utils/auth_utils.dart';
|
||||||
import '../../../widgets/custom_user_form.dart';
|
import '../../../widgets/custom_user_form.dart';
|
||||||
|
|
||||||
class PersonalDetails extends StatefulWidget {
|
class PersonalDetails extends StatefulWidget {
|
||||||
@ -20,27 +23,29 @@ class PersonalDetails extends StatefulWidget {
|
|||||||
final ValueChanged<String?>? onGenderChanged;
|
final ValueChanged<String?>? onGenderChanged;
|
||||||
final ValueChanged<String?>? onCountryChanged;
|
final ValueChanged<String?>? onCountryChanged;
|
||||||
final ValueChanged<String?>? onRoleChanged;
|
final ValueChanged<String?>? onRoleChanged;
|
||||||
|
final void Function(bool)? onUserTypeChanged;
|
||||||
|
|
||||||
final String? selectedGender;
|
final String? selectedGender;
|
||||||
final String? selectedCountry;
|
final String? selectedCountry;
|
||||||
final String? selectedRole;
|
final String? selectedRole;
|
||||||
|
|
||||||
// const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},);
|
// const PersonalDetails(this.isDesktop, this.isViewMode, {super.key},);
|
||||||
const PersonalDetails({
|
const PersonalDetails(
|
||||||
Key? key,
|
{Key? key,
|
||||||
this.selectedGender,
|
this.selectedGender,
|
||||||
this.selectedCountry,
|
this.selectedCountry,
|
||||||
this.selectedRole,
|
this.selectedRole,
|
||||||
required this.personalDetailsKey,
|
required this.personalDetailsKey,
|
||||||
required this.controllers,
|
required this.controllers,
|
||||||
required this.errorMessages,
|
required this.errorMessages,
|
||||||
required this.isDesktop,
|
required this.isDesktop,
|
||||||
required this.isViewMode,
|
required this.isViewMode,
|
||||||
required this.apiselectedUser,
|
required this.apiselectedUser,
|
||||||
this.onGenderChanged,
|
this.onGenderChanged,
|
||||||
this.onCountryChanged,
|
this.onCountryChanged,
|
||||||
this.onRoleChanged,
|
this.onRoleChanged,
|
||||||
}) : super(key: key);
|
this.onUserTypeChanged})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
PersonalDetailsState createState() => PersonalDetailsState();
|
PersonalDetailsState createState() => PersonalDetailsState();
|
||||||
@ -53,11 +58,16 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
late List<String> countryCodes; // List of country codes
|
late List<String> countryCodes; // List of country codes
|
||||||
late List<dynamic>? apiCountryData;
|
late List<dynamic>? apiCountryData;
|
||||||
late List<dynamic>? apiRoleData;
|
late List<dynamic>? apiRoleData;
|
||||||
|
List<dynamic>? apiAllServices;
|
||||||
|
Map<String, dynamic>? selectedOrg;
|
||||||
|
List<Map<String, dynamic>> selectedServiceIds = [];
|
||||||
|
|
||||||
String? get selectedGenderValue => selectedGender;
|
String? get selectedGenderValue => selectedGender;
|
||||||
String? get selectedCountryValue => selectedCountry;
|
String? get selectedCountryValue => selectedCountry;
|
||||||
String? get selectedRoleValue => selectedRole;
|
String? get selectedRoleValue => selectedRole;
|
||||||
|
|
||||||
|
late bool isTravelAgent = false;
|
||||||
|
|
||||||
String? selectedTab;
|
String? selectedTab;
|
||||||
|
|
||||||
String? selectedCountry;
|
String? selectedCountry;
|
||||||
@ -121,6 +131,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
selectedRole = widget.selectedRole;
|
selectedRole = widget.selectedRole;
|
||||||
fetchRoles();
|
fetchRoles();
|
||||||
fetchCountries();
|
fetchCountries();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
loadAllServices();
|
||||||
|
getOrganizationData();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _clearError(String field) {
|
void _clearError(String field) {
|
||||||
@ -158,6 +173,47 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> getOrganizationData() async {
|
||||||
|
try {
|
||||||
|
print("getUpdatedServices");
|
||||||
|
|
||||||
|
final result = await apiService.fetchOrganization();
|
||||||
|
print("UUPdatedServices - $result");
|
||||||
|
setState(() {
|
||||||
|
final servicesRaw = selectedOrg?['services_ids'];
|
||||||
|
|
||||||
|
List<dynamic> services;
|
||||||
|
|
||||||
|
if (servicesRaw is String) {
|
||||||
|
try {
|
||||||
|
services = jsonDecode(servicesRaw);
|
||||||
|
} catch (e) {
|
||||||
|
print('❌ Failed to decode services_ids: $e');
|
||||||
|
services = [];
|
||||||
|
}
|
||||||
|
} else if (servicesRaw is List) {
|
||||||
|
services = servicesRaw;
|
||||||
|
} else {
|
||||||
|
services = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedServiceIds = services.map<Map<String, dynamic>>((item) {
|
||||||
|
// force cast or copy to a regular map
|
||||||
|
final map = Map<String, dynamic>.from(item);
|
||||||
|
return {
|
||||||
|
"service_id": map['service_id'].toString(),
|
||||||
|
};
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
|
||||||
|
// orgId = await getOrgId();
|
||||||
|
|
||||||
|
print("selectedOrg - $selectedOrg");
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching updatedServices list: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
@ -169,30 +225,215 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: Column(
|
child: SingleChildScrollView(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
scrollDirection: Axis.vertical,
|
||||||
children: [
|
child: Column(
|
||||||
SizedBox(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
height: 10,
|
children: [
|
||||||
),
|
SizedBox(
|
||||||
_buildFirstRow(widget.isDesktop),
|
height: 10,
|
||||||
SizedBox(
|
),
|
||||||
height: 10,
|
_buildFirstRow(widget.isDesktop),
|
||||||
),
|
SizedBox(
|
||||||
_buildSecondRow(widget.isDesktop),
|
height: 10,
|
||||||
SizedBox(
|
),
|
||||||
height: 10,
|
_buildSecondRow(widget.isDesktop),
|
||||||
),
|
SizedBox(
|
||||||
_buildThirdRow(widget.isDesktop),
|
height: 10,
|
||||||
SizedBox(
|
),
|
||||||
height: 10,
|
_buildThirdRow(widget.isDesktop),
|
||||||
),
|
SizedBox(
|
||||||
_buildForthRow(widget.isDesktop),
|
height: 10,
|
||||||
],
|
),
|
||||||
|
_buildForthRow(widget.isDesktop),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
if (selectedRole == "5") _buildServices(widget.isDesktop)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> loadAllServices() async {
|
||||||
|
try {
|
||||||
|
final result = await apiService.fetchAllServices();
|
||||||
|
setState(() {
|
||||||
|
apiAllServices = result;
|
||||||
|
});
|
||||||
|
print("Fetched services: $apiAllServices");
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching role list: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildServices(bool isDesktop) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Services",
|
||||||
|
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 14, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontFamily: "Archivo",
|
||||||
|
// fontSize: 14,
|
||||||
|
// fontWeight: FontWeight.w600,
|
||||||
|
// color: Color(0xFF212121)),
|
||||||
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Color(0xFFF4F4FB)),
|
||||||
|
borderRadius: BorderRadius.circular(1),
|
||||||
|
// color: bodyColor,
|
||||||
|
// color: Color(0xFFF5F5F5),
|
||||||
|
color: Colors.white),
|
||||||
|
padding: EdgeInsets.only(left: 5, right: 5, top: 15, bottom: 5),
|
||||||
|
child: isDesktop
|
||||||
|
? Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
children: _buildOptions(),
|
||||||
|
)
|
||||||
|
: Expanded(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Row(
|
||||||
|
children: _buildOptions(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildOptions() {
|
||||||
|
if (apiAllServices == null) return [];
|
||||||
|
|
||||||
|
return apiAllServices!.map((service) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 20.0),
|
||||||
|
child: _buildOption(service),
|
||||||
|
);
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildOption(Map<String, dynamic> service) {
|
||||||
|
String name = service['name'];
|
||||||
|
String iconUrl = service['icon']; // Can be empty string
|
||||||
|
|
||||||
|
IconData fallbackIcon = _getLocalIconForService(name);
|
||||||
|
|
||||||
|
String serviceId = service['service_id'].toString();
|
||||||
|
// bool isSelected = selectedServiceIds.contains(serviceId);
|
||||||
|
bool isSelected =
|
||||||
|
selectedServiceIds.any((item) => item["service_id"] == serviceId);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
String serviceId = service['service_id'].toString();
|
||||||
|
|
||||||
|
// Check if already selected
|
||||||
|
int existingIndex = selectedServiceIds
|
||||||
|
.indexWhere((item) => item["service_id"] == serviceId);
|
||||||
|
|
||||||
|
if (existingIndex != -1) {
|
||||||
|
selectedServiceIds.removeAt(existingIndex);
|
||||||
|
} else {
|
||||||
|
selectedServiceIds.add({"service_id": serviceId});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: Row(children: [
|
||||||
|
iconUrl.isNotEmpty
|
||||||
|
? Image.network(
|
||||||
|
iconUrl,
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Icon(fallbackIcon,
|
||||||
|
size: 18,
|
||||||
|
color: isSelected == name
|
||||||
|
? Color(0xFF114D8B)
|
||||||
|
: Color(0xFF475569));
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: Icon(fallbackIcon,
|
||||||
|
size: 18,
|
||||||
|
color:
|
||||||
|
isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569)),
|
||||||
|
|
||||||
|
SizedBox(width: 5),
|
||||||
|
|
||||||
|
SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
name,
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
|
||||||
|
fontWeight:
|
||||||
|
isSelected == name ? FontWeight.bold : FontWeight.w500),
|
||||||
|
// style: TextStyle(
|
||||||
|
// fontSize: 13,
|
||||||
|
// color: isSelected == name ? Color(0xFF114D8B) : Color(0xFF475569),
|
||||||
|
// fontFamily: "Archivo",
|
||||||
|
// fontWeight:
|
||||||
|
// isSelected == name ? FontWeight.bold : FontWeight.w500),
|
||||||
|
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(width: 5),
|
||||||
|
// if (selectedListOption == title && widget.isViewMode == false)
|
||||||
|
Container(
|
||||||
|
height: 15,
|
||||||
|
width: 15,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
border: Border.all(
|
||||||
|
color: isSelected ? Colors.green : Colors.grey, width: 1),
|
||||||
|
),
|
||||||
|
child: Icon(
|
||||||
|
Icons.check_circle,
|
||||||
|
size: 10,
|
||||||
|
color: isSelected ? Colors.green : Colors.grey,
|
||||||
|
// color: Colors.grey,
|
||||||
|
)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconData _getLocalIconForService(String name) {
|
||||||
|
switch (name.toLowerCase()) {
|
||||||
|
case 'flight':
|
||||||
|
return Icons.flight_takeoff_outlined;
|
||||||
|
case 'train':
|
||||||
|
return Icons.train_outlined;
|
||||||
|
case 'bus':
|
||||||
|
return Icons.bus_alert_outlined;
|
||||||
|
case 'taxi':
|
||||||
|
return Icons.local_taxi_outlined;
|
||||||
|
case 'accomodation':
|
||||||
|
return Icons.local_hotel_outlined;
|
||||||
|
case 'forex':
|
||||||
|
return Icons.attach_money_outlined;
|
||||||
|
case 'insurance':
|
||||||
|
return Icons.list_alt_outlined;
|
||||||
|
case 'visa':
|
||||||
|
return Icons.badge_outlined;
|
||||||
|
case 'miscellaneous':
|
||||||
|
return Icons.card_giftcard_outlined;
|
||||||
|
default:
|
||||||
|
return Icons.circle_notifications;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildFirstRow(bool isDesktop) {
|
Widget _buildFirstRow(bool isDesktop) {
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@ -227,11 +468,6 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSecondRow(bool isDesktop) {
|
Widget _buildSecondRow(bool isDesktop) {
|
||||||
List<Widget> fields = [
|
|
||||||
buildEmailField(),
|
|
||||||
buildMobileField(),
|
|
||||||
buildAlternateMobileField(),
|
|
||||||
];
|
|
||||||
return Container(
|
return Container(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
child: widget.isDesktop
|
child: widget.isDesktop
|
||||||
@ -318,11 +554,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("First Name",
|
Text(
|
||||||
style: TextStyle(
|
"First Name",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -330,7 +566,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["Fname"],
|
controller: widget.controllers["Fname"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -338,7 +574,10 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "FirstName",
|
labelText: "FirstName",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -404,11 +643,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Gender",
|
Text(
|
||||||
style: TextStyle(
|
"Gender",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
width: widget.isDesktop
|
width: widget.isDesktop
|
||||||
@ -435,18 +674,18 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
enabled: !widget.isViewMode, // Disables input when in view mode
|
enabled: !widget.isViewMode, // Disables input when in view mode
|
||||||
),
|
),
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
items: [
|
items: [
|
||||||
DropdownMenuItem(value: "Male", child: Text("Male")),
|
DropdownMenuItem(value: "Male", child: Text("Male")),
|
||||||
DropdownMenuItem(value: "Female", child: Text("Female")),
|
DropdownMenuItem(value: "Female", child: Text("Female")),
|
||||||
],
|
],
|
||||||
hint: Text(
|
hint: Text(
|
||||||
selectedGender ?? "Select Gender",
|
selectedGender ?? "Select Gender",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
),
|
),
|
||||||
disabledHint: Text(
|
disabledHint: Text(
|
||||||
selectedGender ?? "Select Gender",
|
selectedGender ?? "Select Gender",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -522,11 +761,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Date of Birth",
|
Text(
|
||||||
style: TextStyle(
|
"Date of Birth",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
width: widget.isDesktop
|
width: widget.isDesktop
|
||||||
@ -550,10 +789,13 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: AbsorbPointer(
|
child: AbsorbPointer(
|
||||||
child: TextField(
|
child: TextField(
|
||||||
controller: widget.controllers["dob"],
|
controller: widget.controllers["dob"],
|
||||||
style: const TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
decoration: const InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Select Date",
|
labelText: "Select Date",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -573,11 +815,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Email",
|
Text(
|
||||||
style: TextStyle(
|
"Email",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -585,7 +827,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["email"],
|
controller: widget.controllers["email"],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_clearError("email");
|
_clearError("email");
|
||||||
@ -593,7 +835,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Email",
|
labelText: "Email",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -616,11 +859,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Mobile Number",
|
Text(
|
||||||
style: TextStyle(
|
"Mobile Number",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -628,7 +871,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["mobileNumber"],
|
controller: widget.controllers["mobileNumber"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -641,7 +884,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Mobile Number",
|
labelText: "Mobile Number",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -665,9 +909,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Alternate Mobile Number",
|
Text("Alternate Mobile Number",
|
||||||
style: TextStyle(
|
style: GoogleFonts.poppins(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w200,
|
fontWeight: FontWeight.w400,
|
||||||
color: Colors.black)),
|
color: Colors.black)),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
@ -676,7 +920,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["alternateMobile"],
|
controller: widget.controllers["alternateMobile"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||||
@ -686,7 +930,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Alternate Number",
|
labelText: "Alternate Number",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -726,11 +971,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Country",
|
Text(
|
||||||
style: TextStyle(
|
"Country",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -765,7 +1010,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
selectedItem ?? "Select Country",
|
selectedItem ?? "Select Country",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// onChanged: widget.isViewMode
|
// onChanged: widget.isViewMode
|
||||||
@ -811,11 +1056,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Postal Code",
|
Text(
|
||||||
style: TextStyle(
|
"Postal Code",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -823,7 +1068,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["postalCode"],
|
controller: widget.controllers["postalCode"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
keyboardType: TextInputType.numberWithOptions(decimal: true),
|
||||||
@ -833,7 +1078,10 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
],
|
],
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Postal Code",
|
labelText: "Postal Code",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle: GoogleFonts.poppins(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -849,11 +1097,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Role ",
|
Text(
|
||||||
style: TextStyle(
|
"Role ",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false, // Dropdown doesn't use focus
|
isFocused: false, // Dropdown doesn't use focus
|
||||||
@ -862,7 +1110,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
height: 45, // Set appropriate height
|
height: 45, // Set appropriate height
|
||||||
child: DropdownButtonFormField<String>(
|
child: DropdownButtonFormField<String>(
|
||||||
value: widget.isViewMode ? null : selectedRole,
|
value: widget.isViewMode ? null : selectedRole,
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding:
|
contentPadding:
|
||||||
@ -873,6 +1121,9 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
: (newValue) {
|
: (newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedRole = newValue;
|
selectedRole = newValue;
|
||||||
|
isTravelAgent = selectedRole == "Travel Agent";
|
||||||
|
// Pass the result back to parent
|
||||||
|
widget.onUserTypeChanged?.call(isTravelAgent);
|
||||||
});
|
});
|
||||||
widget.onRoleChanged?.call(newValue);
|
widget.onRoleChanged?.call(newValue);
|
||||||
},
|
},
|
||||||
@ -884,7 +1135,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
hint: Text("Select"),
|
hint: Text("Select"),
|
||||||
disabledHint: Text(
|
disabledHint: Text(
|
||||||
selectedRole ?? "Select Role",
|
selectedRole ?? "Select Role",
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -897,11 +1148,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Address",
|
Text(
|
||||||
style: TextStyle(
|
"Address",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -909,14 +1160,15 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 80,
|
height: 80,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["address"],
|
controller: widget.controllers["address"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Address",
|
labelText: "Address",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
@ -933,11 +1185,11 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text("Password",
|
Text(
|
||||||
style: TextStyle(
|
"Password",
|
||||||
fontSize: 12,
|
style: GoogleFonts.poppins(
|
||||||
fontWeight: FontWeight.w200,
|
fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black),
|
||||||
color: Colors.black)),
|
),
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
CustomTextFieldUserWrapper(
|
CustomTextFieldUserWrapper(
|
||||||
isFocused: false,
|
isFocused: false,
|
||||||
@ -945,7 +1197,7 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 40,
|
height: 40,
|
||||||
child: TextField(
|
child: TextField(
|
||||||
style: TextStyle(fontSize: 12, color: Colors.black),
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
controller: widget.controllers["password"],
|
controller: widget.controllers["password"],
|
||||||
enabled: !widget.isViewMode,
|
enabled: !widget.isViewMode,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
@ -953,7 +1205,8 @@ class PersonalDetailsState extends State<PersonalDetails> {
|
|||||||
},
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: "Password",
|
labelText: "Password",
|
||||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
labelStyle:
|
||||||
|
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,10 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import '../../config/apiUrl.dart';
|
import '../../config/apiUrl.dart';
|
||||||
import '../../routes/custom_appBar.dart';
|
import '../../routes/custom_appBar.dart';
|
||||||
import '../../routes/custom_drawer.dart';
|
import '../../routes/custom_drawer.dart';
|
||||||
|
import '../../services/apiService.dart';
|
||||||
import '../../utils/pagination.dart';
|
import '../../utils/pagination.dart';
|
||||||
import '../../widgets/custom_popup.dart';
|
import '../../widgets/custom_popup.dart';
|
||||||
|
import '../../widgets/popup_userList_action.dart';
|
||||||
|
|
||||||
class UserListScreen extends StatefulWidget {
|
class UserListScreen extends StatefulWidget {
|
||||||
@override
|
@override
|
||||||
@ -19,7 +21,10 @@ class UserListScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _UserListScreenState extends State<UserListScreen> {
|
class _UserListScreenState extends State<UserListScreen> {
|
||||||
|
final ApiService apiService = ApiService();
|
||||||
late Future<List<dynamic>> futureUsers;
|
late Future<List<dynamic>> futureUsers;
|
||||||
|
|
||||||
|
late Map<String, dynamic> userSingleData;
|
||||||
List<dynamic>? apiCountryData;
|
List<dynamic>? apiCountryData;
|
||||||
String? selectedUserId;
|
String? selectedUserId;
|
||||||
String? orgId;
|
String? orgId;
|
||||||
@ -542,16 +547,16 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.error_outline,
|
// const Icon(Icons.error_outline,
|
||||||
color: Colors.redAccent, size: 60),
|
// color: Colors.redAccent, size: 60),
|
||||||
const SizedBox(height: 16),
|
// const SizedBox(height: 16),
|
||||||
Text(
|
// Text(
|
||||||
"Oops!",
|
// "Oops!",
|
||||||
style: GoogleFonts.poppins(
|
// style: GoogleFonts.poppins(
|
||||||
fontSize: 20,
|
// fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
// fontWeight: FontWeight.bold,
|
||||||
color: Colors.redAccent),
|
// color: Colors.redAccent),
|
||||||
),
|
// ),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
Text(
|
Text(
|
||||||
"No Plans Available For This User",
|
"No Plans Available For This User",
|
||||||
@ -683,61 +688,104 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
fontWeight: FontWeight.w400),
|
fontWeight: FontWeight.w400),
|
||||||
))),
|
))),
|
||||||
DataCell(
|
DataCell(
|
||||||
PopupMenuButton<int>(
|
UserActionsMenu(
|
||||||
color: Colors.white,
|
user: user,
|
||||||
padding: EdgeInsets.zero,
|
getUserDetails: (id) =>
|
||||||
offset: Offset(0, 30),
|
apiService.getSingleUser(id),
|
||||||
icon: Icon(
|
|
||||||
Icons.more_vert,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
CustomPopupMenuEntry(
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.remove_red_eye,
|
|
||||||
color:
|
|
||||||
Color(0xFF475569),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser": user,
|
|
||||||
"isViewMode": true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser": user,
|
|
||||||
"isViewMode": false
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
// PopupMenuButton<int>(
|
||||||
|
// color: Colors.white,
|
||||||
|
// padding: EdgeInsets.zero,
|
||||||
|
// offset: Offset(0, 30),
|
||||||
|
// icon: Icon(
|
||||||
|
// Icons.more_vert,
|
||||||
|
// color: Color(0xFF475569),
|
||||||
|
// size: 14,
|
||||||
|
// ),
|
||||||
|
// itemBuilder: (context) => [
|
||||||
|
// CustomPopupMenuEntry(
|
||||||
|
// child: Container(
|
||||||
|
// padding: EdgeInsets.symmetric(
|
||||||
|
// horizontal: 8, vertical: 8),
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize: MainAxisSize.min,
|
||||||
|
// mainAxisAlignment:
|
||||||
|
// MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// IconButton(
|
||||||
|
// icon: Icon(
|
||||||
|
// Icons.remove_red_eye,
|
||||||
|
// color:
|
||||||
|
// Color(0xFF475569),
|
||||||
|
// size: 18),
|
||||||
|
// onPressed: () async {
|
||||||
|
// print(
|
||||||
|
// "USerDAta1 - $user");
|
||||||
|
// // Fetch the user data properly with await
|
||||||
|
// Map<String, dynamic>
|
||||||
|
// usersData =
|
||||||
|
// await apiService
|
||||||
|
// .getSingleUser(user[
|
||||||
|
// 'user_id']
|
||||||
|
// is String
|
||||||
|
// ? int.parse(user[
|
||||||
|
// 'user_id'])
|
||||||
|
// : user[
|
||||||
|
// 'user_id']);
|
||||||
|
//
|
||||||
|
// print(
|
||||||
|
// "USerDAta2 - $usersData");
|
||||||
|
//
|
||||||
|
// // userSingleData =
|
||||||
|
// // await apiService
|
||||||
|
// // .getSingleUser(user[
|
||||||
|
// // 'user_id']);
|
||||||
|
//
|
||||||
|
// context.go(
|
||||||
|
// "/CreateUserDetails",
|
||||||
|
// extra: {
|
||||||
|
// "selectedUser":
|
||||||
|
// usersData,
|
||||||
|
// "isViewMode": true
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
// IconButton(
|
||||||
|
// icon: Image.asset(
|
||||||
|
// 'assets/images/IconsImg/edit.png',
|
||||||
|
// width: 20,
|
||||||
|
// height: 15),
|
||||||
|
// onPressed: () async {
|
||||||
|
// // Fetch the user data properly with await
|
||||||
|
// Map<String, dynamic>
|
||||||
|
// usersData =
|
||||||
|
// await apiService
|
||||||
|
// .getSingleUser(user[
|
||||||
|
// 'user_id']
|
||||||
|
// is String
|
||||||
|
// ? int.parse(user[
|
||||||
|
// 'user_id'])
|
||||||
|
// : user[
|
||||||
|
// 'user_id']);
|
||||||
|
//
|
||||||
|
// print(
|
||||||
|
// "USerDAta2 - $usersData");
|
||||||
|
// context.go(
|
||||||
|
// "/CreateUserDetails",
|
||||||
|
// extra: {
|
||||||
|
// "selectedUser":
|
||||||
|
// usersData,
|
||||||
|
// "isViewMode": false
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
// Row(
|
// Row(
|
||||||
// children: [
|
// children: [
|
||||||
// MouseRegion(
|
// MouseRegion(
|
||||||
@ -858,65 +906,85 @@ class _UserListScreenState extends State<UserListScreen> {
|
|||||||
color: Colors.black87,
|
color: Colors.black87,
|
||||||
fontWeight: FontWeight.w700),
|
fontWeight: FontWeight.w700),
|
||||||
),
|
),
|
||||||
PopupMenuButton<int>(
|
|
||||||
color: Colors.white,
|
UserActionsMenu(
|
||||||
padding: EdgeInsets.zero,
|
user: user,
|
||||||
offset: Offset(0, 30),
|
getUserDetails: (id) =>
|
||||||
icon: Icon(
|
apiService.getSingleUser(id),
|
||||||
Icons.more_vert,
|
|
||||||
color: Color(0xFF475569),
|
|
||||||
size: 14,
|
|
||||||
),
|
|
||||||
itemBuilder: (context) => [
|
|
||||||
CustomPopupMenuEntry(
|
|
||||||
child: Container(
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 8, vertical: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize:
|
|
||||||
MainAxisSize.min,
|
|
||||||
mainAxisAlignment:
|
|
||||||
MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(
|
|
||||||
Icons
|
|
||||||
.remove_red_eye,
|
|
||||||
color: Color(
|
|
||||||
0xFF475569),
|
|
||||||
size: 18),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser":
|
|
||||||
user,
|
|
||||||
"isViewMode": true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
IconButton(
|
|
||||||
icon: Image.asset(
|
|
||||||
'assets/images/IconsImg/edit.png',
|
|
||||||
width: 20,
|
|
||||||
height: 15),
|
|
||||||
onPressed: () {
|
|
||||||
context.go(
|
|
||||||
"/CreateUserDetails",
|
|
||||||
extra: {
|
|
||||||
"selectedUser":
|
|
||||||
user,
|
|
||||||
"isViewMode": false
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
// PopupMenuButton<int>(
|
||||||
|
// color: Colors.white,
|
||||||
|
// padding: EdgeInsets.zero,
|
||||||
|
// offset: Offset(0, 30),
|
||||||
|
// icon: Icon(
|
||||||
|
// Icons.more_vert,
|
||||||
|
// color: Color(0xFF475569),
|
||||||
|
// size: 14,
|
||||||
|
// ),
|
||||||
|
// itemBuilder: (context) => [
|
||||||
|
// CustomPopupMenuEntry(
|
||||||
|
// child: Container(
|
||||||
|
// padding: EdgeInsets.symmetric(
|
||||||
|
// horizontal: 8, vertical: 8),
|
||||||
|
// child: Row(
|
||||||
|
// mainAxisSize:
|
||||||
|
// MainAxisSize.min,
|
||||||
|
// mainAxisAlignment:
|
||||||
|
// MainAxisAlignment.center,
|
||||||
|
// children: [
|
||||||
|
// IconButton(
|
||||||
|
// icon: Icon(
|
||||||
|
// Icons
|
||||||
|
// .remove_red_eye,
|
||||||
|
// color: Color(
|
||||||
|
// 0xFF475569),
|
||||||
|
// size: 18),
|
||||||
|
// onPressed: () {
|
||||||
|
// print(
|
||||||
|
// "USerDAta - $user");
|
||||||
|
// // dynamic usersData = apiService
|
||||||
|
// // .getSingleUser(user[
|
||||||
|
// // 'user_id']
|
||||||
|
// // is String
|
||||||
|
// // ? int.parse(user[
|
||||||
|
// // 'user_id'])
|
||||||
|
// // : user[
|
||||||
|
// // 'user_id']);
|
||||||
|
// //
|
||||||
|
// // print(
|
||||||
|
// // "USerDAta - $usersData");
|
||||||
|
//
|
||||||
|
// context.go(
|
||||||
|
// "/CreateUserDetails",
|
||||||
|
// extra: {
|
||||||
|
// "selectedUser":
|
||||||
|
// user,
|
||||||
|
// "isViewMode": true
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// }),
|
||||||
|
// IconButton(
|
||||||
|
// icon: Image.asset(
|
||||||
|
// 'assets/images/IconsImg/edit.png',
|
||||||
|
// width: 20,
|
||||||
|
// height: 15),
|
||||||
|
// onPressed: () {
|
||||||
|
// context.go(
|
||||||
|
// "/CreateUserDetails",
|
||||||
|
// extra: {
|
||||||
|
// "selectedUser":
|
||||||
|
// user,
|
||||||
|
// "isViewMode": false
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
|
|||||||
@ -394,31 +394,54 @@ class _CustomAppBarState extends State<CustomAppBar> {
|
|||||||
buildNavItem("All Trips", _myTravelRequestColor,
|
buildNavItem("All Trips", _myTravelRequestColor,
|
||||||
() => context.go('/listAllPlan'),
|
() => context.go('/listAllPlan'),
|
||||||
isSelected: selectedTab == "All Trips"),
|
isSelected: selectedTab == "All Trips"),
|
||||||
if (userDetails["role"] == "Travel Agent")
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
if (userDetails["role"] == "Travel Agent")
|
|
||||||
buildNavItem("My Trips", _myTravelRequestColor,
|
|
||||||
() => context.go('/listTravelAgentPlan'),
|
|
||||||
isSelected: selectedTab == "My Trips"),
|
|
||||||
...[
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
buildNavItem("My Trips", _myTravelRequestColor,
|
|
||||||
() => context.go('/listPlan'),
|
|
||||||
isSelected: selectedTab == "My Trips"),
|
|
||||||
],
|
|
||||||
if (userDetails["role"] != "Travel Agent")
|
if (userDetails["role"] != "Travel Agent")
|
||||||
const SizedBox(width: 20),
|
const SizedBox(width: 20),
|
||||||
buildNavItem("My Approvals", _myApprovalsColor, () {
|
|
||||||
if (userData?["role"] == "Travel Agent") {
|
if (userData?["role"] == "Travel Agent")
|
||||||
isSelected:
|
buildNavItem(
|
||||||
selectedTab == "My Approvals";
|
"My Trips",
|
||||||
context.go('/listTravelAgentPlan');
|
_myTravelRequestColor,
|
||||||
} else {
|
() => context.go('/listTravelAgentPlan'),
|
||||||
|
isSelected: selectedTab == "My Trips",
|
||||||
|
),
|
||||||
|
|
||||||
|
if (userData?["role"] != "Travel Agent") // for others
|
||||||
|
buildNavItem(
|
||||||
|
"My Trips",
|
||||||
|
_myTravelRequestColor,
|
||||||
|
() => context.go('/listPlan'),
|
||||||
|
isSelected: selectedTab == "My Trips",
|
||||||
|
),
|
||||||
|
// if (userDetails["role"] == "Travel Agent")
|
||||||
|
// buildNavItem("My Trips", _myTravelRequestColor,
|
||||||
|
// () => context.go('/listTravelAgentPlan'),
|
||||||
|
// isSelected: selectedTab == "My Trips"),
|
||||||
|
// ...[
|
||||||
|
// const SizedBox(width: 20),
|
||||||
|
// buildNavItem("My Trips", _myTravelRequestColor,
|
||||||
|
// () => context.go('/listPlan'),
|
||||||
|
// isSelected: selectedTab == "My Trips"),
|
||||||
|
// ],
|
||||||
|
//
|
||||||
|
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
if (userData?["role"] != "Travel Agent")
|
||||||
|
buildNavItem("My Approvals", _myApprovalsColor, () {
|
||||||
isSelected:
|
isSelected:
|
||||||
selectedTab == "My Approvals";
|
selectedTab == "My Approvals";
|
||||||
context.go('/ApprovalList');
|
context.go('/ApprovalList');
|
||||||
}
|
})
|
||||||
}),
|
|
||||||
|
// if (userData?["role"] == "Travel Agent") {
|
||||||
|
// isSelected:
|
||||||
|
// selectedTab == "My Approvals";
|
||||||
|
// context.go('/listTravelAgentPlan');
|
||||||
|
// } else {
|
||||||
|
// isSelected:
|
||||||
|
// selectedTab == "My Approvals";
|
||||||
|
// context.go('/ApprovalList');
|
||||||
|
// }
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -623,4 +623,111 @@ class ApiService {
|
|||||||
throw Exception('Failed to load plans');
|
throw Exception('Failed to load plans');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> getForexPdfDownload(forexId) async {
|
||||||
|
final String apiUrldata =
|
||||||
|
'$apiUrl/api/plans/forexDownload?forex_id=$forexId';
|
||||||
|
|
||||||
|
// final String apiUrldata = '$apiUrl/auth/googlelogin';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
try {
|
||||||
|
print("PDf Dowloaded");
|
||||||
|
|
||||||
|
// Create a blob from the response body
|
||||||
|
final blob = html.Blob([response.bodyBytes]);
|
||||||
|
|
||||||
|
// Generate a download URL for the blob
|
||||||
|
final url = html.Url.createObjectUrlFromBlob(blob);
|
||||||
|
|
||||||
|
// Create a link element to trigger the download
|
||||||
|
final anchor = html.AnchorElement(href: url)
|
||||||
|
..setAttribute('download', 'Forex_$forexId.pdf')
|
||||||
|
..click();
|
||||||
|
|
||||||
|
// Revoke the download URL to free up resources
|
||||||
|
html.Url.revokeObjectUrl(url);
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else if (response.statusCode == 404) {
|
||||||
|
// showDialog(
|
||||||
|
// context: context,
|
||||||
|
// builder: (BuildContext context) {
|
||||||
|
// return AlertDialog(
|
||||||
|
// title: Text('File not found.'),
|
||||||
|
// // content: Text('File not found.'),
|
||||||
|
// actions: [
|
||||||
|
// TextButton(
|
||||||
|
// child: Text('OK'),
|
||||||
|
// onPressed: () {
|
||||||
|
// Navigator.of(context).pop(); // Close the dialog
|
||||||
|
// },
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load plans');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------------------------User Management-----------------------------------
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getSingleUser(int userId) async {
|
||||||
|
print('Single USer 1 - $userId');
|
||||||
|
|
||||||
|
final String apiUrldata = '$apiUrl/api/users/find/$userId';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
try {
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
print(data);
|
||||||
|
if (!data.containsKey('data') || data['data'] is! Map) {
|
||||||
|
throw Exception(
|
||||||
|
"Invalid response format: 'data' field is missing or not a Map");
|
||||||
|
}
|
||||||
|
print('Single USer 1');
|
||||||
|
|
||||||
|
Map<String, dynamic> plansJson =
|
||||||
|
data['data']; // 'data' is a Map, not a List
|
||||||
|
|
||||||
|
print('Single USer 2');
|
||||||
|
return plansJson;
|
||||||
|
} catch (e) {
|
||||||
|
throw Exception('Error parsing response: $e');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load plans');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
267
lib/utils/travelAgent_remarks.dart
Normal file
267
lib/utils/travelAgent_remarks.dart
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:frontend/utils/auth_utils.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../config/apiUrl.dart';
|
||||||
|
|
||||||
|
class CommentModal extends StatefulWidget {
|
||||||
|
final String planId;
|
||||||
|
final Color layoutColorForUser;
|
||||||
|
final String role;
|
||||||
|
const CommentModal({
|
||||||
|
Key? key,
|
||||||
|
required this.planId,
|
||||||
|
required this.layoutColorForUser,
|
||||||
|
required this.role,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
CommentModalState createState() => CommentModalState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class CommentModalState extends State<CommentModal> {
|
||||||
|
Map<String, dynamic>? remarksData;
|
||||||
|
int? remarksId;
|
||||||
|
dynamic userId;
|
||||||
|
bool isLoading = true;
|
||||||
|
String? errorMessage;
|
||||||
|
bool editRemarks = false;
|
||||||
|
TextEditingController commentController = TextEditingController();
|
||||||
|
|
||||||
|
Map<String, dynamic> getData() {
|
||||||
|
final map = {
|
||||||
|
"plan_id": int.parse(widget.planId),
|
||||||
|
"remarks": commentController.text,
|
||||||
|
"created_by": userId,
|
||||||
|
"is_active": 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (remarksId != null) {
|
||||||
|
map["id"] = remarksId; // Add 'id' only if available
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
loadRemarks();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> loadRemarks() async {
|
||||||
|
try {
|
||||||
|
final userIdString = await getUserId(); // getUserId returns String?
|
||||||
|
if (userIdString == null) {
|
||||||
|
throw Exception('User ID not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
userId = int.tryParse(userIdString);
|
||||||
|
if (userId == null) {
|
||||||
|
throw Exception('Invalid user ID format.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final data = await getRemarks(userId);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
remarksData = data;
|
||||||
|
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
print("Remarks -");
|
||||||
|
print("Remarks - ${jsonEncode(remarksData)}");
|
||||||
|
} catch (e) {
|
||||||
|
setState(() {
|
||||||
|
errorMessage = e.toString();
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Map<String, dynamic>> getRemarks(int userId) async {
|
||||||
|
// final String apiUrldata =
|
||||||
|
// '$apiUrl/getRemarksByPlanId?plan_id=${widget.planId}&user_id=$userId';
|
||||||
|
|
||||||
|
final String apiUrldata =
|
||||||
|
'$apiUrl/api/plans/getRemarksByPlanId?plan_id=${widget.planId}&user_id=$userId';
|
||||||
|
|
||||||
|
final token = await getToken();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final response = await http.get(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print("Res1 ....");
|
||||||
|
final data = json.decode(response.body);
|
||||||
|
|
||||||
|
// Check if 'data' is empty or doesn't contain valid remarks
|
||||||
|
if (!data.containsKey('data') ||
|
||||||
|
data['data'] == null ||
|
||||||
|
data['data'].isEmpty) {
|
||||||
|
editRemarks = true; // Set the flag to true if data is empty
|
||||||
|
} else {
|
||||||
|
print("DAta available ");
|
||||||
|
updateData(data);
|
||||||
|
editRemarks = false; // Otherwise, no need to create remarks
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Res1 - $data");
|
||||||
|
if (!data.containsKey('data') || data['data'] is! Map) {
|
||||||
|
throw Exception(
|
||||||
|
"Invalid response format: 'data' field is missing or not a Map");
|
||||||
|
}
|
||||||
|
return data['data'];
|
||||||
|
} else {
|
||||||
|
throw Exception('Failed to load remarks');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> postRemarksData() async {
|
||||||
|
final remarksData = getData();
|
||||||
|
|
||||||
|
print("Remarks Data - remarksData");
|
||||||
|
|
||||||
|
final String apiUrldata = '$apiUrl/api/plans/addOrEditPlanRemark';
|
||||||
|
// final String apiUrldata = '$apiUrl/api/plans/createOrEditPlan';
|
||||||
|
|
||||||
|
final token = await getToken(); // Fetch token
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw Exception('Token not found. Please log in.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (selectedPlanId != null && selectedPlanId!.isNotEmpty) {
|
||||||
|
// planData['plan_id'] = selectedPlanId; // Add plan_id for update
|
||||||
|
// }
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await http.post(
|
||||||
|
Uri.parse(apiUrldata),
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: jsonEncode(remarksData), // Convert map to JSON
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
print("Plan submitted successfully!");
|
||||||
|
print("Response: ${response.body}");
|
||||||
|
} else {
|
||||||
|
print("Failed to submit plan. Status: ${response.statusCode}");
|
||||||
|
print("Error: ${response.body}");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(" Error submitting plan: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateData(dynamic data) {
|
||||||
|
if (data['data'] != null && data['data'].isNotEmpty) {
|
||||||
|
final remarksData = data['data'][0]; // Take the first item from the list
|
||||||
|
|
||||||
|
// Now update your local fields
|
||||||
|
commentController.text = remarksData['remarks'] ?? '';
|
||||||
|
|
||||||
|
// If you need to update other fields like created_by, you can do that too
|
||||||
|
userId = remarksData['created_by'] ?? userId;
|
||||||
|
remarksId = remarksData['id'];
|
||||||
|
// If plan_id needs to be updated (usually it doesn't change), you can do it too
|
||||||
|
// widget.planId = remarksData['plan_id'].toString(); // If widget.planId is mutable
|
||||||
|
|
||||||
|
setState(() {}); // Refresh the UI if needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
// You can also make a TextEditingController if you want to collect input
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
contentPadding: const EdgeInsets.fromLTRB(24, 20, 24, 10),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
// Row 1: Title + Edit + Delete buttons
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'Comment',
|
||||||
|
style: GoogleFonts.poppins(fontSize: 18, color: Colors.black),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
if (widget.role == "Travel Agent")
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.edit, size: 20),
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
editRemarks = !editRemarks;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.delete, size: 20),
|
||||||
|
onPressed: () {
|
||||||
|
// Handle delete pressed
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
|
||||||
|
// Row 2: TextField for comment
|
||||||
|
TextField(
|
||||||
|
controller: commentController,
|
||||||
|
maxLines: 5,
|
||||||
|
enabled: editRemarks,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: 'Enter your comment...',
|
||||||
|
hintStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
|
||||||
|
// Row 3: OK button
|
||||||
|
|
||||||
|
editRemarks
|
||||||
|
? SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
postRemarksData();
|
||||||
|
// You can get text from commentController.text
|
||||||
|
Navigator.of(context).pop(); // Close the modal
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: widget.layoutColorForUser,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text('OK',
|
||||||
|
style: GoogleFonts.poppins(
|
||||||
|
fontSize: 13, color: Colors.white)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox.shrink(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
61
lib/widgets/custom_user_travel.dart
Normal file
61
lib/widgets/custom_user_travel.dart
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class CustomTextFieldUserTravellerWrapper extends StatefulWidget {
|
||||||
|
final Widget child;
|
||||||
|
final bool isFocused;
|
||||||
|
final bool isDesktop;
|
||||||
|
final double? width;
|
||||||
|
final Color? color;
|
||||||
|
final VoidCallback? onFocusChange; // Callback for focus handling
|
||||||
|
final EdgeInsetsGeometry padding;
|
||||||
|
|
||||||
|
const CustomTextFieldUserTravellerWrapper({
|
||||||
|
super.key,
|
||||||
|
required this.child,
|
||||||
|
required this.isFocused,
|
||||||
|
required this.isDesktop,
|
||||||
|
this.width,
|
||||||
|
this.color = Colors.white,
|
||||||
|
this.onFocusChange,
|
||||||
|
this.padding = const EdgeInsets.symmetric(horizontal: 12),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
_CustomTextFieldUserTravellerWrapperState createState() =>
|
||||||
|
_CustomTextFieldUserTravellerWrapperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CustomTextFieldUserTravellerWrapperState
|
||||||
|
extends State<CustomTextFieldUserTravellerWrapper> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: widget.width ?? // Use custom width if provided, else default
|
||||||
|
(widget.isDesktop
|
||||||
|
? MediaQuery.of(context).size.width * 0.24
|
||||||
|
: MediaQuery.of(context).size.width * 0.8),
|
||||||
|
padding: widget.padding,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
// color: widget.color,
|
||||||
|
// color: Color(0xFFF7F7FB),
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
border: Border.all(
|
||||||
|
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||||
|
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||||
|
width: widget.isFocused ? 2.0 : 0.5,
|
||||||
|
),
|
||||||
|
boxShadow: widget.isFocused
|
||||||
|
? [
|
||||||
|
BoxShadow(
|
||||||
|
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||||
|
blurRadius: 10,
|
||||||
|
spreadRadius: 2,
|
||||||
|
offset: Offset(0, 4),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
77
lib/widgets/popup_userList_action.dart
Normal file
77
lib/widgets/popup_userList_action.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../services/apiService.dart';
|
||||||
|
import 'custom_popup.dart';
|
||||||
|
|
||||||
|
class UserActionsMenu extends StatelessWidget {
|
||||||
|
final ApiService apiService = ApiService();
|
||||||
|
final Map<String, dynamic> user;
|
||||||
|
final Future<Map<String, dynamic>> Function(int userId) getUserDetails;
|
||||||
|
|
||||||
|
UserActionsMenu({
|
||||||
|
super.key,
|
||||||
|
required this.user,
|
||||||
|
required this.getUserDetails,
|
||||||
|
});
|
||||||
|
|
||||||
|
int getUserId(dynamic value) {
|
||||||
|
if (value is String) return int.parse(value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return PopupMenuButton<int>(
|
||||||
|
color: Colors.white,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
offset: Offset(0, 30),
|
||||||
|
icon: Icon(
|
||||||
|
Icons.more_vert,
|
||||||
|
color: Color(0xFF475569),
|
||||||
|
size: 14,
|
||||||
|
),
|
||||||
|
itemBuilder: (context) => [
|
||||||
|
CustomPopupMenuEntry(
|
||||||
|
child: Container(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
GestureDetector(
|
||||||
|
child: Icon(Icons.remove_red_eye,
|
||||||
|
color: Color(0xFF475569), size: 18),
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.pop(context); // Close the menu
|
||||||
|
final userId = getUserId(user['user_id']);
|
||||||
|
final usersData = await getUserDetails(userId);
|
||||||
|
context.go("/CreateUserDetails", extra: {
|
||||||
|
"selectedUser": usersData,
|
||||||
|
"isViewMode": true,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
GestureDetector(
|
||||||
|
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||||
|
width: 20, height: 15),
|
||||||
|
onTap: () async {
|
||||||
|
Navigator.pop(context); // Close the menu
|
||||||
|
final userId = getUserId(user['user_id']);
|
||||||
|
final usersData = await getUserDetails(userId);
|
||||||
|
context.go("/CreateUserDetails", extra: {
|
||||||
|
"selectedUser": usersData,
|
||||||
|
"isViewMode": false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -208,6 +208,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
fluttertoast:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: fluttertoast
|
||||||
|
sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.2.12"
|
||||||
go_router:
|
go_router:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -49,6 +49,7 @@ dependencies:
|
|||||||
universal_html: ^2.2.4
|
universal_html: ^2.2.4
|
||||||
super_tooltip: ^2.0.9
|
super_tooltip: ^2.0.9
|
||||||
google_fonts: ^6.2.1
|
google_fonts: ^6.2.1
|
||||||
|
fluttertoast: ^8.2.12
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user