Policy, Approval Screens
This commit is contained in:
parent
a91166727a
commit
e0ed41ad35
BIN
assets/images/IconsImg/delete.png
Normal file
BIN
assets/images/IconsImg/delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 242 B |
BIN
assets/images/IconsImg/edit.png
Normal file
BIN
assets/images/IconsImg/edit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 488 B |
BIN
assets/images/login/TravelSpendsLogo1.png
Normal file
BIN
assets/images/login/TravelSpendsLogo1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
assets/images/login/VectorG.png
Normal file
BIN
assets/images/login/VectorG.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 476 B |
BIN
assets/images/login/login_img1.png
Normal file
BIN
assets/images/login/login_img1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 863 KiB |
BIN
assets/images/login/travelSpend_Logo.png
Normal file
BIN
assets/images/login/travelSpend_Logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
119
lib/Screens/approvals/approval_dialogs.dart
Normal file
119
lib/Screens/approvals/approval_dialogs.dart
Normal file
@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Show confirm dialog for approval
|
||||
Future<bool?> showApproveDialog(BuildContext context, Color layoutColor) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
backgroundColor: Colors.white,
|
||||
title: const Text(
|
||||
"Confirm Approval",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
content: const Text("Are you sure you want to approve this plan?"),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: layoutColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context, true),
|
||||
child: const Text("OK"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Show confirm dialog for rejection with remarks input
|
||||
Future<String?> showRejectDialog(
|
||||
BuildContext context, Color layoutColor) async {
|
||||
String remarks = "";
|
||||
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) => AlertDialog(
|
||||
backgroundColor: Colors.white,
|
||||
contentPadding: const EdgeInsets.all(36),
|
||||
// title: const Text("Confirm Rejection"),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"Confirm Rejection",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Text("Please enter remarks to reject the plan."),
|
||||
const SizedBox(height: 10),
|
||||
TextField(
|
||||
maxLines: 3,
|
||||
onChanged: (value) => remarks = value,
|
||||
decoration: const InputDecoration(
|
||||
hintText: "Remarks...",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () => Navigator.pop(context, false),
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: layoutColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
if (remarks.trim().isEmpty) return;
|
||||
Navigator.pop(context, true);
|
||||
},
|
||||
child: const Text("OK"),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return confirmed == true ? remarks : null;
|
||||
}
|
||||
510
lib/Screens/approvals/approval_list.dart
Normal file
510
lib/Screens/approvals/approval_list.dart
Normal file
@ -0,0 +1,510 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'package:frontend/data/models/plan.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/config/apiUrl.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
|
||||
class ApprovalList extends StatefulWidget {
|
||||
const ApprovalList({super.key});
|
||||
|
||||
@override
|
||||
_ApprovalListState createState() => _ApprovalListState();
|
||||
}
|
||||
|
||||
class _ApprovalListState extends State<ApprovalList> {
|
||||
final ApiService apiService = ApiService();
|
||||
late Future<List<Plan>> futurePlans;
|
||||
String? userId;
|
||||
String? orgId;
|
||||
String? token;
|
||||
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
late List<dynamic> plansJson;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getToken();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initializeData();
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
// futurePlans = fetchPlans();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
// Future<void> loadAllGroups() async {
|
||||
// try {
|
||||
// final result = await apiService.fetchUserApprovalList();
|
||||
// // setState(() {
|
||||
// // apiAllGroups = result;
|
||||
// // });
|
||||
// print("Fetched services: $result");
|
||||
// } catch (e) {
|
||||
// print('Error fetching role list: $e');
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> initializeData() async {
|
||||
token = await getToken();
|
||||
userId = await getUserId();
|
||||
orgId = await getOrgId();
|
||||
|
||||
if (token == null || userId == null) {
|
||||
print("Token or USerId missing");
|
||||
return;
|
||||
} else {
|
||||
setState(() {
|
||||
futurePlans = fetchPlans();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if (userDataString != null) {
|
||||
try {
|
||||
final Map<String, dynamic> userData = jsonDecode(userDataString);
|
||||
return userData["user_id"]?.toString();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String?> getOrgId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if (userDataString != null) {
|
||||
try {
|
||||
final Map<String, dynamic> userData = jsonDecode(userDataString);
|
||||
return userData["org_id"]?.toString();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
}
|
||||
|
||||
// Fetch API Data
|
||||
Future<List<Plan>> fetchPlans() async {
|
||||
// final String apiUrldata = '$apiUrl/api/plans';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
// final String apiUrldata = '$apiUrl/api/plans?org_id=$orgId&user_id=$userId';
|
||||
|
||||
final String apiUrldata =
|
||||
'$apiUrl/api/plans/findApprovalList?user_id=$userId&org_id=$orgId';
|
||||
|
||||
// api/plans?org_id=1&user_id=1
|
||||
// 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', // Add token here
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
plansJson = data['data'];
|
||||
return plansJson.map((json) => Plan.fromJson(json)).toList();
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
|
||||
// Future<Map<String, dynamic>> getViewPlan(String planId) async {
|
||||
// final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
||||
// print("API URL: $apiUrldata");
|
||||
// // final token = await getToken();
|
||||
//
|
||||
// if (token == null) {
|
||||
// throw Exception('Token not found. Please log in.');
|
||||
// }
|
||||
//
|
||||
// final response = await http.put(
|
||||
// Uri.parse(apiUrldata),
|
||||
// headers: {
|
||||
// 'Authorization': 'Bearer $token', // Add token here
|
||||
// 'Content-Type': 'application/json',
|
||||
// },
|
||||
// );
|
||||
//
|
||||
// if (response.statusCode == 200) {
|
||||
// final Map<String, dynamic>? resData = json.decode(response.body);
|
||||
//
|
||||
// return resData?["data"];
|
||||
// } else {
|
||||
// throw Exception('Failed to load plans');
|
||||
// }
|
||||
// }
|
||||
|
||||
// Future<Map<String, dynamic>> getViewPlan(String planId, List plansJson) async {
|
||||
// try {
|
||||
// final plan = plansJson.firstWhere(
|
||||
// (item) => item["plan_id"].toString() == planId,
|
||||
// orElse: () => null,
|
||||
// );
|
||||
//
|
||||
// if (plan == null) {
|
||||
// throw Exception("Plan with ID $planId not found.");
|
||||
// }
|
||||
//
|
||||
// return Map<String, dynamic>.from(plan);
|
||||
// } catch (e) {
|
||||
// throw Exception("Error finding plan: $e");
|
||||
// }
|
||||
// }
|
||||
|
||||
void viewPlanforApprover(String planId,
|
||||
{bool isViewMode = false, bool isApprover = true}) async {
|
||||
try {
|
||||
Map<String, dynamic> planData =
|
||||
await apiService.getViewPlan(planId, plansJson);
|
||||
print("ViewAAA - $planData");
|
||||
|
||||
context.go('/createPlan', extra: {
|
||||
'planData': planData,
|
||||
'isViewMode': isViewMode,
|
||||
'isApprover': isApprover
|
||||
});
|
||||
} catch (e) {
|
||||
print("Error fetching plan: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
||||
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
body: Row(
|
||||
children: [
|
||||
if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
Expanded(child: buildTableLayout(isDesktop))
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildTableLayout(isDesktop) {
|
||||
return Container(
|
||||
color: bodyColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Text('List For Approval',
|
||||
style: TextStyle(
|
||||
fontFamily: "Archivo",
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF212121))),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Divider(
|
||||
thickness: 0.2, // how "thick" the line is
|
||||
color: Colors.grey, // optional
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.2,
|
||||
// or use Flexible
|
||||
child: TextField(
|
||||
onChanged: (query) {},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a plan",
|
||||
hintStyle:
|
||||
TextStyle(fontSize: 14, color: Color(0xFF9E9DBD)),
|
||||
prefixIcon:
|
||||
Icon(Icons.search, color: Color(0xFF9E9DBD)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade300, width: 0.5),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
borderSide:
|
||||
BorderSide(color: Colors.blueAccent, width: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// SizedBox(width: 16),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<Plan>>(
|
||||
future: futurePlans,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError ||
|
||||
!snapshot.hasData ||
|
||||
snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Icon(Icons.error_outline,
|
||||
color: Colors.redAccent, size: 60),
|
||||
SizedBox(height: 16),
|
||||
Text("Oops!",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.redAccent)),
|
||||
SizedBox(height: 8),
|
||||
Text("No Plans Available For This User",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey)),
|
||||
SizedBox(height: 20),
|
||||
Text("Please Create Plan",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16, color: Colors.grey)),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Plan> plans = snapshot.data!;
|
||||
plans.sort((a, b) =>
|
||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||
|
||||
Widget table = LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: DataTable(
|
||||
dividerThickness: 0.5,
|
||||
columnSpacing: isDesktop ? 24.0 : 16.0,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(
|
||||
label: Text('Plan Id',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontSize: 14,
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Planned User',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Title',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Type',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Created On',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Status',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Actions',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
],
|
||||
rows: plans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(
|
||||
plan.userName.isNotEmpty
|
||||
? plan.userName
|
||||
: plan.travellerName,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.tripTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(plan.tripType,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.createdOn,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: plan.status == "Active"
|
||||
? layoutColor
|
||||
: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
plan.statusValue,
|
||||
style: TextStyle(
|
||||
color: plan.status == "Active"
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
)),
|
||||
DataCell(Row(children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.remove_red_eye,
|
||||
color: Color(0xFF475569),
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () => viewPlanforApprover(
|
||||
plan.planId,
|
||||
isViewMode: true,
|
||||
isApprover: true)),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () => viewPlanforApprover(plan.planId,
|
||||
isViewMode: false, isApprover: true),
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/edit.png',
|
||||
width: 20,
|
||||
height: 15),
|
||||
),
|
||||
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.edit,
|
||||
// color: Colors.green),
|
||||
// onPressed: () => viewPlanforApprover(plan.planId,
|
||||
// isViewMode: false) ),
|
||||
])),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: isDesktop
|
||||
? table
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,8 @@ class LoginWidget extends StatefulWidget {
|
||||
final bool isDesktop;
|
||||
final bool isTablet;
|
||||
|
||||
const LoginWidget({Key? key, required this.isDesktop, required this.isTablet}) : super(key: key);
|
||||
const LoginWidget({Key? key, required this.isDesktop, required this.isTablet})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_LoginWidgetState createState() => _LoginWidgetState();
|
||||
@ -21,35 +22,31 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
final TextEditingController _passwordController = TextEditingController();
|
||||
bool _obscureText = true;
|
||||
|
||||
Future<void> storeUserDetails(String token) async{
|
||||
try{
|
||||
final parts = token.split('.');
|
||||
if (parts.length != 3) throw Exception('Invalid token format');
|
||||
Future<void> storeUserDetails(String token) async {
|
||||
try {
|
||||
final parts = token.split('.');
|
||||
if (parts.length != 3) throw Exception('Invalid token format');
|
||||
|
||||
final payload = json.decode(
|
||||
utf8.decode(base64Url.decode(base64Url.normalize(parts[1])))
|
||||
);
|
||||
final payload = json
|
||||
.decode(utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))));
|
||||
|
||||
final userData = payload['data'];
|
||||
final userData = payload['data'];
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('auth_token', token);
|
||||
await prefs.setString('user_data', jsonEncode(userData)); // Store full user data
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('auth_token', token);
|
||||
await prefs.setString(
|
||||
'user_data', jsonEncode(userData)); // Store full user data
|
||||
|
||||
if(userData != null){
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.setString('auth_token', token);
|
||||
await pref.setString('user_data', jsonEncode(userData));
|
||||
if (userData != null) {
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.setString('auth_token', token);
|
||||
await pref.setString('user_data', jsonEncode(userData));
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error decoding token: $e');
|
||||
}
|
||||
|
||||
}
|
||||
catch(e){
|
||||
print('Error decoding token: $e');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _login(BuildContext context) async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
const String url = '$apiUrl/auth/login';
|
||||
@ -57,7 +54,10 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
try {
|
||||
final response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
},
|
||||
body: jsonEncode({
|
||||
'email': _emailController.text.trim(),
|
||||
'password': _passwordController.text.trim(),
|
||||
@ -73,7 +73,6 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
|
||||
await storeUserDetails(token);
|
||||
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text("Login Successful"),
|
||||
@ -83,7 +82,9 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
context.go('/home'); // Navigate to home
|
||||
} else {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text("Login Failed: ${jsonDecode(response.body)['message']}")),
|
||||
SnackBar(
|
||||
content: Text(
|
||||
"Login Failed: ${jsonDecode(response.body)['message']}")),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
@ -95,39 +96,69 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
/// Layout
|
||||
Widget build(BuildContext context) {
|
||||
double formWidth = widget.isTablet ? 400 : 300;
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
if (widget.isDesktop)
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
color: Colors.blueAccent,
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Welcome Back!",
|
||||
style: TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
return Container(
|
||||
color: Color(0xFF114D8B),
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.isDesktop)
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/login/login_img1.png'),
|
||||
fit: BoxFit.fill
|
||||
// fit: BoxFit.cover, // or BoxFit.contain, BoxFit.fill, etc.
|
||||
),
|
||||
color: Colors.blueAccent,
|
||||
borderRadius: BorderRadius.only(
|
||||
topRight: Radius.circular(25), // Rounded top-left corner
|
||||
bottomRight:
|
||||
Radius.circular(25), // Rounded bottom-left corner
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 30),
|
||||
child: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: Image.asset(
|
||||
'assets/images/login/TravelSpendsLogo1.png',
|
||||
width: 200, // Optional: control size
|
||||
height: 100,
|
||||
fit: BoxFit.contain,
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(25), // Rounded top-left corner
|
||||
bottomLeft: Radius.circular(25), // Rounded bottom-left corner
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: _buildForm(width: formWidth), // Fixed form width
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(40),
|
||||
child: _buildForm(width: formWidth), // Fixed form width
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// **Reusable Login Form**
|
||||
Widget _buildForm({required double width}) {
|
||||
return SizedBox(
|
||||
@ -138,47 +169,140 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"Hello! Welcome Back",
|
||||
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blueAccent),
|
||||
"Sign In",
|
||||
style: TextStyle(
|
||||
fontSize: 26,
|
||||
fontFamily: "Nunito",
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF212121)),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 2),
|
||||
const Text(
|
||||
"Welcome to trip system management",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontFamily: "Nunito",
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF212121)),
|
||||
),
|
||||
const SizedBox(height: 18),
|
||||
|
||||
/// **Email Field**
|
||||
_buildLabel("EMAIL"),
|
||||
_buildLabel("Email Address"),
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
decoration: _inputDecoration("Enter your email"),
|
||||
validator: (value) => value == null || value.isEmpty ? 'Required Email' : null,
|
||||
decoration: _inputDecoration("Enter your email address").copyWith(
|
||||
prefixIcon: Icon(
|
||||
Icons.email_outlined,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
validator: (value) =>
|
||||
value == null || value.isEmpty ? 'Required Email' : null,
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
/// **Password Field**
|
||||
_buildLabel("PASSWORD"),
|
||||
_buildLabel("Password"),
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: _obscureText,
|
||||
decoration: _inputDecoration("Enter your password").copyWith(
|
||||
prefixIcon: Icon(
|
||||
Icons.key,
|
||||
size: 16,
|
||||
),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_obscureText ? Icons.visibility_off : Icons.visibility, color: Colors.blueAccent),
|
||||
icon: Icon(
|
||||
_obscureText ? Icons.visibility_off : Icons.visibility,
|
||||
color: Color(0xFF12B24B),
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () => setState(() => _obscureText = !_obscureText),
|
||||
),
|
||||
),
|
||||
validator: (value) => value == null || value.isEmpty ? 'Required Password' : null,
|
||||
validator: (value) =>
|
||||
value == null || value.isEmpty ? 'Required Password' : null,
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
/// **Login Button**
|
||||
ElevatedButton(
|
||||
onPressed: () => _login(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blueAccent, // Button color
|
||||
foregroundColor: Colors.white, // Text color
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _login(context),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF12B24B), // Button color
|
||||
foregroundColor: Colors.white, // Text color
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 24, vertical: 10),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
"Sign In",
|
||||
style: TextStyle(
|
||||
fontFamily: "Nunito",
|
||||
fontWeight: FontWeight.w800,
|
||||
fontSize: 15),
|
||||
),
|
||||
SizedBox(
|
||||
width: 3,
|
||||
),
|
||||
Icon(
|
||||
Icons.arrow_forward_sharp,
|
||||
color: Colors.white,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Center(
|
||||
child: Text(
|
||||
"Forgot Password",
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w300,
|
||||
fontFamily: "Nunito",
|
||||
color: Color(0xFF212121), // Text color
|
||||
decoration: TextDecoration.underline, // Underline the text
|
||||
),
|
||||
),
|
||||
child: const Text("Login"),
|
||||
),
|
||||
|
||||
const SizedBox(height: 20),
|
||||
|
||||
Container(
|
||||
width: 45, // Adjust size
|
||||
height: 45,
|
||||
decoration: BoxDecoration(
|
||||
// Background color
|
||||
shape: BoxShape.rectangle,
|
||||
borderRadius: BorderRadius.all(Radius.circular(15)),
|
||||
border: Border.all(
|
||||
color: Color(0xFF9E9DBD), width: 1), // Grey outline
|
||||
),
|
||||
|
||||
child: Center(
|
||||
child: Image.asset(
|
||||
'assets/images/login/VectorG.png',
|
||||
width: 20, // Optional: control size
|
||||
height: 10,
|
||||
fit: BoxFit.contain,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -194,7 +318,11 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.blueAccent),
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
fontFamily: "Nunito",
|
||||
color: Color(0xFF212121)),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -205,11 +333,16 @@ class _LoginWidgetState extends State<LoginWidget> {
|
||||
return InputDecoration(
|
||||
labelText: hint,
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 1.0, horizontal: 0.0),
|
||||
filled: true,
|
||||
fillColor: Colors.white,
|
||||
labelStyle: TextStyle(fontSize: 11, color: Colors.grey),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xFF212121), // Border color
|
||||
width: 1, // Optional: adjust the width of the border
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,11 +12,13 @@ import '../../widgets/custom_text_traveller.dart';
|
||||
|
||||
class UserSelectionDialog extends StatefulWidget {
|
||||
final String title;
|
||||
final Color layoutColorForUser;
|
||||
final void Function(String, String, bool) onSubmit;
|
||||
|
||||
UserSelectionDialog({
|
||||
Key? key,
|
||||
required this.title,
|
||||
required this.layoutColorForUser,
|
||||
required this.onSubmit,
|
||||
}) : super(key: key);
|
||||
|
||||
@ -302,6 +304,10 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
prefixIcon: Icon(Icons.search),
|
||||
border:
|
||||
OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.grey, width: 1),
|
||||
// borderSide: BorderSide(color: Color(0xFFF5F5F5), width: 2),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
@ -309,8 +315,9 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(height: 10),
|
||||
|
||||
if (widget.title == "Others") ...[
|
||||
SizedBox(height: 10),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@ -324,8 +331,8 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
});
|
||||
},
|
||||
child: Text("Create",
|
||||
style:
|
||||
TextStyle(fontSize: 14, color: Colors.blueAccent)),
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: widget.layoutColorForUser)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -412,10 +419,11 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.blueAccent,
|
||||
foregroundColor: widget.layoutColorForUser,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
side: BorderSide(
|
||||
color: widget.layoutColorForUser, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
@ -427,11 +435,12 @@ class _UserSelectionDialogState extends State<UserSelectionDialog> {
|
||||
SizedBox(width: 10),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blueAccent,
|
||||
backgroundColor: widget.layoutColorForUser,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
side: BorderSide(
|
||||
color: widget.layoutColorForUser, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
|
||||
@ -36,6 +36,9 @@ class Group extends StatefulWidget {
|
||||
class _groupState extends State<Group> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
String? orgId;
|
||||
String? userId;
|
||||
String? selectedGroupId;
|
||||
@ -72,7 +75,11 @@ class _groupState extends State<Group> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadAllServices();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadAllServices();
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
for (var field in dataHeader) {
|
||||
controllers[field] = TextEditingController();
|
||||
@ -81,6 +88,21 @@ class _groupState extends State<Group> {
|
||||
updateData();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> updateData() async {
|
||||
// Ensure apiselectedUser is not null before printing
|
||||
if (widget.group != null) {
|
||||
@ -247,7 +269,8 @@ class _groupState extends State<Group> {
|
||||
|
||||
Widget buildOrganizationLayout(isDesktop) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
color: bodyColor,
|
||||
// color: Colors.white,
|
||||
width: double.infinity,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
margin: const EdgeInsets.all(8),
|
||||
@ -258,7 +281,8 @@ class _groupState extends State<Group> {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Colors.white,
|
||||
// color: Colors.white,
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
@ -269,7 +293,9 @@ class _groupState extends State<Group> {
|
||||
Container(
|
||||
color: Color(0xFFE9EBF6),
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.close),
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
),
|
||||
onPressed: () {
|
||||
context.go('/group');
|
||||
},
|
||||
@ -278,12 +304,14 @@ class _groupState extends State<Group> {
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 30,
|
||||
),
|
||||
// SizedBox(
|
||||
// height: 5,
|
||||
// ),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(10),
|
||||
padding: const EdgeInsets.all(20),
|
||||
color: Colors.grey.shade100,
|
||||
color: Colors.white,
|
||||
height: MediaQuery.of(context).size.height * 0.8,
|
||||
child: Column(
|
||||
children: [
|
||||
// Row(
|
||||
@ -324,7 +352,9 @@ class _groupState extends State<Group> {
|
||||
children: _buildSecondRow(isDesktop),
|
||||
),
|
||||
|
||||
SizedBox(height: 15),
|
||||
// SizedBox(height: 15),
|
||||
|
||||
Spacer(),
|
||||
isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
@ -587,10 +617,10 @@ class _groupState extends State<Group> {
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.blueAccent,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
side: BorderSide(color: layoutColor ?? Colors.green, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
@ -607,14 +637,14 @@ class _groupState extends State<Group> {
|
||||
// : SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blueAccent, // Keep original color
|
||||
backgroundColor: layoutColor, // Keep original color
|
||||
foregroundColor: Colors.white, // Keep original color
|
||||
disabledBackgroundColor:
|
||||
Colors.blueAccent, // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
// disabledBackgroundColor:
|
||||
// Colors.blueAccent, // Ensure color remains when disabled
|
||||
// disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
// side: BorderSide(color: Colors.blueAccent, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
|
||||
@ -6,6 +6,7 @@ import 'package:responsive_builder/responsive_builder.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
|
||||
class GroupList extends StatefulWidget {
|
||||
@override
|
||||
@ -16,11 +17,32 @@ class _GroupListState extends State<GroupList> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
List<dynamic>? apiAllGroups;
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadAllGroups();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadAllGroups();
|
||||
loadInitialData();
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loadAllGroups() async {
|
||||
@ -71,10 +93,12 @@ class _GroupListState extends State<GroupList> {
|
||||
|
||||
Widget buildGroupListLayout(bool isDesktop) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
color: bodyColor,
|
||||
// color: Colors.white,
|
||||
width: double.infinity,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
margin: const EdgeInsets.all(8),
|
||||
// margin: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
@ -93,33 +117,44 @@ class _GroupListState extends State<GroupList> {
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.blueAccent),
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// side: BorderSide(color: , width: 1),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () async {
|
||||
// List<dynamic> users = await futureUsers;
|
||||
context.go('/CreateGroup');
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text('New Group'),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text('New Group'),
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.88,
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
height: MediaQuery.of(context).size.height * 0.899,
|
||||
padding: const EdgeInsets.all(10),
|
||||
// margin: const EdgeInsets.only(bottom: 10),
|
||||
color: Colors.white,
|
||||
// color: Colors.red.shade100,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
@ -156,6 +191,8 @@ class _GroupListState extends State<GroupList> {
|
||||
itemBuilder: (context, index) {
|
||||
final group = apiAllGroups![index];
|
||||
return Card(
|
||||
// color: bodyColor,
|
||||
color: Color(0xFFF5F5F5),
|
||||
margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
|
||||
@ -1,34 +1,87 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AccomodationListWidget extends StatelessWidget {
|
||||
|
||||
final List<Map<String,dynamic>> accommodationList;
|
||||
final List<Map<String, dynamic>> accommodationList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteAccommodation;
|
||||
final Function(Map<String, dynamic>) onDeleteAccommodation;
|
||||
|
||||
const AccomodationListWidget({super.key, required this.accommodationList, required this.onOpen, required this.onDeleteAccommodation});
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const AccomodationListWidget(
|
||||
{super.key,
|
||||
required this.accommodationList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteAccommodation,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return
|
||||
Padding(
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Accomodation Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Accomodation Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Accomodation", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(label: Text('#')),
|
||||
@ -48,45 +101,65 @@ class AccomodationListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = accommodationList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
accommodationList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return filteredList.asMap() .entries.map((entry) {
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
DataCell(Text(item["destination_city"]!)),
|
||||
DataCell(Text(item["hotel_name"]!)),
|
||||
DataCell(Text(item["checkin_date"]!)),
|
||||
DataCell(Text(item["checkout_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Accomodation"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteAccommodation(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Accomodation");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteAccommodation(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
))
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Accomodation");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteAccommodation(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )),
|
||||
//
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -11,11 +11,17 @@ class BusListWidget extends StatelessWidget {
|
||||
final List<Map<String, dynamic>> busList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteBus;
|
||||
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const BusListWidget(
|
||||
{super.key,
|
||||
required this.busList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteBus});
|
||||
required this.onDeleteBus,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -24,9 +30,54 @@ class BusListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Bus Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Bus Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Bus", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
@ -83,26 +134,51 @@ class BusListWidget extends StatelessWidget {
|
||||
DataCell(Text(item["time"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Bus"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteBus(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Bus");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteBus(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Bus");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteBus(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FlightListWidget extends StatelessWidget {
|
||||
final List<Map<String,dynamic>> flightList;
|
||||
final Function( bool,Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteFlight;
|
||||
final List<Map<String, dynamic>> flightList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteFlight;
|
||||
|
||||
const FlightListWidget({super.key, required this.flightList, required this.onOpen, required this.onDeleteFlight});
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const FlightListWidget(
|
||||
{super.key,
|
||||
required this.flightList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteFlight,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -14,80 +23,151 @@ class FlightListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Flight Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Flight Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Flight", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
// width: 1000,
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
// width: 1000,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
DataColumn(label: Text('Trip Type')),
|
||||
DataColumn(label: Text('From')),
|
||||
DataColumn(label: Text('To')),
|
||||
DataColumn(label: Text('Actions')),
|
||||
],
|
||||
rows: _buildDataRows(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = flightList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
flightList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
Map<String,dynamic> item = entry.value;
|
||||
Map<String, dynamic> item = entry.value;
|
||||
print("Trip Type: ${item["trip_type"]}");
|
||||
|
||||
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["trip_type"]?.toString() ?? "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty ? item["trips"][0]["from_place"]?.toString() ?? "N/A" : "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty ? item["trips"][0]["to_place"]?.toString() ?? "N/A" : "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty
|
||||
? item["trips"][0]["from_place"]?.toString() ?? "N/A"
|
||||
: "N/A")),
|
||||
DataCell(Text(item["trips"].isNotEmpty
|
||||
? item["trips"][0]["to_place"]?.toString() ?? "N/A"
|
||||
: "N/A")),
|
||||
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Flight");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteFlight(item);
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Flight"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteFlight(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
//
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Flight");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteFlight(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
//
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -8,12 +8,17 @@ class ForexListWidget extends StatelessWidget {
|
||||
final Function(Map<String, dynamic>) onDeleteForex;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const ForexListWidget(
|
||||
{super.key,
|
||||
required this.forexList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteForex,
|
||||
required this.apiCountryData});
|
||||
required this.apiCountryData,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -22,9 +27,54 @@ class ForexListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Forex List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Forex List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Forex", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
@ -92,26 +142,51 @@ class ForexListWidget extends StatelessWidget {
|
||||
DataCell(Text(item["perdiem_amount"] ?? "N/A")),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Forex"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteForex(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Forex");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteForex(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Forex");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteForex(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -3,12 +3,22 @@ import 'dart:js_interop';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class InsuranceListWidget extends StatelessWidget {
|
||||
final List<Map<String,dynamic>> insuranceList;
|
||||
final Function(bool, Map<String,dynamic>, String)onOpen;
|
||||
final Function(Map<String,dynamic>)onDeleteInsurance;
|
||||
final List<Map<String, dynamic>> insuranceList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteInsurance;
|
||||
final Map<String, dynamic>? apiData;
|
||||
const InsuranceListWidget({super.key, required this.insuranceList, required this.onOpen,required this.apiData,
|
||||
required this.onDeleteInsurance});
|
||||
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const InsuranceListWidget(
|
||||
{super.key,
|
||||
required this.insuranceList,
|
||||
required this.onOpen,
|
||||
required this.apiData,
|
||||
required this.onDeleteInsurance,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -18,20 +28,67 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Insurance Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Insurance Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Insurance", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
@ -52,58 +109,81 @@ class InsuranceListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
|
||||
List<Map<String, dynamic>> filteredList = insuranceList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
insuranceList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
List<dynamic> insurancetypeList = apiData?['insurance_type_of_insurance'] ?? [];
|
||||
|
||||
List<dynamic> insurancetypeList =
|
||||
apiData?['insurance_type_of_insurance'] ?? [];
|
||||
|
||||
String getRequestForInsuranceType(String? specialRequestKey) {
|
||||
if (specialRequestKey == null) return "N/A";
|
||||
|
||||
return insurancetypeList
|
||||
.firstWhere(
|
||||
(element) => element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
(element) =>
|
||||
element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
Map<String,dynamic> item = entry.value;
|
||||
Map<String, dynamic> item = entry.value;
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(item["type_of_insurance"]!)),
|
||||
DataCell( Text(getRequestForInsuranceType( item["type_of_insurance"]!.toString()))),
|
||||
DataCell(Text(
|
||||
getRequestForInsuranceType(item["type_of_insurance"]!.toString()))),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Text(item["end_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Insurance"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteInsurance(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Insurance");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteInsurance(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Insurance");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteInsurance(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -1,14 +1,21 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MiscellaneousListWidget extends StatelessWidget {
|
||||
|
||||
final List<Map<String,dynamic>> miscellaneousList;
|
||||
final List<Map<String, dynamic>> miscellaneousList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const MiscellaneousListWidget({super.key, required this.miscellaneousList, required this.onOpen,
|
||||
required this.onDeleteMiscellaneous,required this.apiData});
|
||||
const MiscellaneousListWidget(
|
||||
{super.key,
|
||||
required this.miscellaneousList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteMiscellaneous,
|
||||
required this.apiData,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -18,20 +25,67 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Miscellaneous Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Miscellaneous Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Miscellaneous", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
@ -62,23 +116,22 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
|
||||
return purposeList
|
||||
.firstWhere(
|
||||
(element) => element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
(element) =>
|
||||
element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
List<Map<String, dynamic>> filteredList = miscellaneousList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
miscellaneousList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return filteredList.asMap().entries.map( (entry) {
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
int index = entry.key + 1; // To start index from 1
|
||||
Map<String, dynamic> item = entry.value;
|
||||
print(item);
|
||||
|
||||
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")), // Index column
|
||||
// DataCell(Text(specialRequestValue)),
|
||||
@ -87,28 +140,53 @@ class MiscellaneousListWidget extends StatelessWidget {
|
||||
// DataCell(Text(item["created_on"] ?? "N/A")),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Miscellaneous"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Miscellaneous"),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Miscellaneous");
|
||||
// Edit action
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteMiscellaneous(item);
|
||||
// Delete action
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Miscellaneous");
|
||||
// // Edit action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteMiscellaneous(item);
|
||||
// // Delete action
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TaxiListWidget extends StatelessWidget {
|
||||
final List<Map<String,dynamic>> taxiList;
|
||||
final Function(bool, Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteTaxi;
|
||||
final List<Map<String, dynamic>> taxiList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteTaxi;
|
||||
|
||||
const TaxiListWidget({super.key, required this.taxiList, required this.onOpen, required this.onDeleteTaxi});
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const TaxiListWidget(
|
||||
{super.key,
|
||||
required this.taxiList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteTaxi,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -14,20 +23,66 @@ class TaxiListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Taxi Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Taxi Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Taxi", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
@ -48,14 +103,11 @@ class TaxiListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
List<Map<String, dynamic>> filteredList = taxiList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
taxiList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
@ -66,26 +118,52 @@ class TaxiListWidget extends StatelessWidget {
|
||||
DataCell(Text(item["car_required_for"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Taxi"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteTaxi(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Taxi");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteTaxi(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Taxi");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteTaxi(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
//
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class TrainListWidget extends StatelessWidget {
|
||||
final List<Map<String,dynamic>> trainList;
|
||||
final Function(bool, Map<String,dynamic>, String) onOpen;
|
||||
final Function(Map<String,dynamic>) onDeleteTrain;
|
||||
const TrainListWidget({super.key, required this.trainList, required this.onOpen, required this.onDeleteTrain});
|
||||
final List<Map<String, dynamic>> trainList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteTrain;
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const TrainListWidget({
|
||||
super.key,
|
||||
required this.trainList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteTrain,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -14,20 +24,67 @@ class TrainListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Train Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Train Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Train", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
@ -49,16 +106,12 @@ class TrainListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
|
||||
|
||||
List<Map<String, dynamic>> filteredList = trainList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
trainList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
final Map<String,dynamic> item = entry.value;
|
||||
final Map<String, dynamic> item = entry.value;
|
||||
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
@ -69,26 +122,52 @@ class TrainListWidget extends StatelessWidget {
|
||||
// DataCell(Text(item["to_station"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Train"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteTrain(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Train");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteTrain(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Train");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteTrain(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
//
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -3,12 +3,22 @@ import 'package:flutter/material.dart';
|
||||
class VisaListWidget extends StatelessWidget {
|
||||
final List<Map<String, dynamic>> visaList;
|
||||
final Function(bool, Map<String, dynamic>, String) onOpen;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Function(Map<String, dynamic>) onDeleteMiscellaneous;
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
|
||||
const VisaListWidget({super.key, required this.visaList, required this.onOpen,
|
||||
required this.onDeleteMiscellaneous, required this.apiData, required this.apiCountryData});
|
||||
final Function(String, bool) onAddNew;
|
||||
final bool isViewMode;
|
||||
|
||||
const VisaListWidget(
|
||||
{super.key,
|
||||
required this.visaList,
|
||||
required this.onOpen,
|
||||
required this.onDeleteMiscellaneous,
|
||||
required this.apiData,
|
||||
required this.apiCountryData,
|
||||
required this.onAddNew,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -18,20 +28,67 @@ class VisaListWidget extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text(
|
||||
"Visa Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Visa Booking List",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: isViewMode
|
||||
? null
|
||||
: () {
|
||||
print("New data");
|
||||
onAddNew("Visa", true);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width ,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: DataTable(
|
||||
border: TableBorder(
|
||||
bottom: BorderSide(color: Colors.black12),
|
||||
horizontalInside: BorderSide(color: Colors.black12), // Only horizontal lines
|
||||
horizontalInside: BorderSide(
|
||||
color: Colors.black12), // Only horizontal lines
|
||||
),
|
||||
columns: const [
|
||||
// DataColumn(label: Text('#')),
|
||||
@ -52,22 +109,22 @@ class VisaListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
List<DataRow> _buildDataRows() {
|
||||
List<Map<String, dynamic>> filteredList = visaList
|
||||
.where((item) => item["is_active"] == "1")
|
||||
.toList();
|
||||
List<Map<String, dynamic>> filteredList =
|
||||
visaList.where((item) => item["is_active"] == "1").toList();
|
||||
print("filteredList- $filteredList");
|
||||
|
||||
List<dynamic> visatypeList = apiData?['visa_type_of_visa'] ?? [];
|
||||
List<dynamic> countryList = apiCountryData ?? [];
|
||||
List<dynamic> countryList = apiCountryData ?? [];
|
||||
|
||||
String getRequestForVisa(String? specialRequestKey) {
|
||||
if (specialRequestKey == null) return "N/A";
|
||||
|
||||
return visatypeList
|
||||
.firstWhere(
|
||||
(element) => element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
(element) =>
|
||||
element["dropdown_key"].toString() == specialRequestKey,
|
||||
orElse: () => {"dropdown_value": "N/A"},
|
||||
)["dropdown_value"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
@ -77,47 +134,70 @@ class VisaListWidget extends StatelessWidget {
|
||||
return countryList
|
||||
.firstWhere(
|
||||
(element) => element["country_code"].toString() == countryCode,
|
||||
orElse: () => {"country_name": "N/A"},
|
||||
)["country_name"]
|
||||
orElse: () => {"country_name": "N/A"},
|
||||
)["country_name"]
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return filteredList.asMap().entries.map((entry){
|
||||
|
||||
return filteredList.asMap().entries.map((entry) {
|
||||
int index = entry.key + 1; // To start index from 1
|
||||
Map<String, dynamic> item = entry.value;
|
||||
print(item);
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(item["indx"]?.toString() ?? "N/A")),
|
||||
// DataCell(Text(item["type_of_visa"]!)),
|
||||
DataCell( Text(getRequestForVisa( item["type_of_visa"]!.toString()))),
|
||||
DataCell( Text(getRequestForCountry( item["country_code"]!.toString()))),
|
||||
DataCell(Text(getRequestForVisa(item["type_of_visa"]!.toString()))),
|
||||
DataCell(Text(getRequestForCountry(item["country_code"]!.toString()))),
|
||||
// DataCell(Text(item["country_code"]!)),
|
||||
DataCell(Text(item["start_date"]!)),
|
||||
DataCell(Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
onPressed: () {
|
||||
// View action
|
||||
},
|
||||
GestureDetector(
|
||||
onTap: () => onOpen(true, item, "Visa"),
|
||||
child: Image.asset('assets/images/IconsImg/edit.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
GestureDetector(
|
||||
onTap: () => onDeleteMiscellaneous(item),
|
||||
child: Image.asset('assets/images/IconsImg/delete.png',
|
||||
width: 20, height: 15),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
icon: Icon(Icons.keyboard_arrow_down_outlined,
|
||||
size: 28, color: Color(0xFF475569)),
|
||||
onPressed: () {
|
||||
onOpen(true, item, "Visa");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.delete, color: Colors.red),
|
||||
onPressed: () {
|
||||
onDeleteMiscellaneous(item);
|
||||
// Expand logic
|
||||
},
|
||||
),
|
||||
],
|
||||
)),
|
||||
)
|
||||
|
||||
//
|
||||
// Row(
|
||||
// children: [
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.remove_red_eye, color: Colors.blue),
|
||||
// onPressed: () {
|
||||
// // View action
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.edit, color: Colors.green),
|
||||
// onPressed: () {
|
||||
// onOpen(true, item, "Visa");
|
||||
// },
|
||||
// ),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// onDeleteMiscellaneous(item);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
|
||||
),
|
||||
]);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@ -9,10 +9,12 @@ import '../../widgets/custom_text_field.dart';
|
||||
class MailSetting extends StatefulWidget {
|
||||
bool isDesktop;
|
||||
final Function(Map<String, dynamic>) onMailDataChanged;
|
||||
final Map<String, dynamic> initialMailData;
|
||||
|
||||
MailSetting({
|
||||
super.key,
|
||||
required this.isDesktop,
|
||||
required this.initialMailData,
|
||||
required this.onMailDataChanged,
|
||||
});
|
||||
|
||||
@ -26,6 +28,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
|
||||
Map<String, String> errorMessages = {};
|
||||
final Map<String, TextEditingController> controllers = {};
|
||||
bool _obscurePassword = true;
|
||||
|
||||
List<String> dataHeader = [
|
||||
"host",
|
||||
@ -78,6 +81,27 @@ class _MailSettingState extends State<MailSetting> {
|
||||
}
|
||||
|
||||
_initControllers();
|
||||
updateData();
|
||||
|
||||
print("Updata 1");
|
||||
|
||||
print(widget.initialMailData['sender_email']);
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
print("Updata 2");
|
||||
|
||||
if (widget.initialMailData.isNotEmpty) {
|
||||
controllers["senderEmail"]?.text =
|
||||
widget.initialMailData['sender_email'] ?? '';
|
||||
controllers["userName"]?.text =
|
||||
widget.initialMailData['mail_user_name'] ?? '';
|
||||
controllers["password"]?.text =
|
||||
widget.initialMailData['mail_password'] ?? '';
|
||||
controllers["host"]?.text = widget.initialMailData['mail_host'] ?? '';
|
||||
controllers["port"]?.text =
|
||||
widget.initialMailData['mail_port']?.toString() ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
void handleTestMailSubmit() {
|
||||
@ -219,6 +243,16 @@ class _MailSettingState extends State<MailSetting> {
|
||||
controller: controllers["senderEmail"],
|
||||
onChanged: (value) {
|
||||
_clearError("sender_email");
|
||||
|
||||
// Validate just this one field
|
||||
if (value.trim().isEmpty) {
|
||||
errorMessages["sender_email"] = "Required";
|
||||
} else if (!RegExp(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
|
||||
.hasMatch(value)) {
|
||||
errorMessages["sender_email"] =
|
||||
"Invalid email format";
|
||||
}
|
||||
},
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
@ -281,7 +315,8 @@ class _MailSettingState extends State<MailSetting> {
|
||||
children: [
|
||||
Text(
|
||||
"Test Mail",
|
||||
style: TextStyle(color: Colors.blueAccent),
|
||||
style: TextStyle(
|
||||
color: Color(0xFF114D8B), fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -370,6 +405,7 @@ class _MailSettingState extends State<MailSetting> {
|
||||
onChanged: (value) {
|
||||
_clearError("mail_password");
|
||||
},
|
||||
obscureText: _obscurePassword,
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
labelText: "password",
|
||||
@ -377,6 +413,19 @@ class _MailSettingState extends State<MailSetting> {
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_obscurePassword = !_obscurePassword;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -514,6 +563,15 @@ class _MailSettingState extends State<MailSetting> {
|
||||
controller: controllers["toEmail"],
|
||||
onChanged: (value) {
|
||||
_clearError("to_mail");
|
||||
|
||||
// Validate just this one field
|
||||
if (value.trim().isEmpty) {
|
||||
errorMessages["to_mail"] = "Required";
|
||||
} else if (!RegExp(
|
||||
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
|
||||
.hasMatch(value)) {
|
||||
errorMessages["to_mail"] = "Invalid email format";
|
||||
}
|
||||
},
|
||||
style: TextStyle(fontSize: 12),
|
||||
decoration: InputDecoration(
|
||||
@ -541,6 +599,18 @@ class _MailSettingState extends State<MailSetting> {
|
||||
Column(
|
||||
children: [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B), // Keep original color
|
||||
foregroundColor: Colors.white, // Keep original color
|
||||
disabledBackgroundColor:
|
||||
Color(0xFF114D8B), // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
handleTestMailSubmit();
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
201
lib/Screens/organization/org_List.dart
Normal file
201
lib/Screens/organization/org_List.dart
Normal file
@ -0,0 +1,201 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/Screens/group/group.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
|
||||
class OrganizationList extends StatefulWidget {
|
||||
const OrganizationList({super.key});
|
||||
|
||||
@override
|
||||
_OrganizationListState createState() => _OrganizationListState();
|
||||
}
|
||||
|
||||
class _OrganizationListState extends State<OrganizationList> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
Map<String, dynamic>? apiAllOrganization;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
loadAllOrganization();
|
||||
}
|
||||
|
||||
Future<void> loadAllOrganization() async {
|
||||
try {
|
||||
final result = await apiService.fetchOrganization();
|
||||
setState(() {
|
||||
apiAllOrganization = result;
|
||||
});
|
||||
print("Fetched services: $apiAllOrganization");
|
||||
} catch (e) {
|
||||
print('Error fetching organization list: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// void deleteGroup(int groupId) {
|
||||
// setState(() {
|
||||
// apiAllGroups?.removeWhere((group) => group['group_id'] == groupId);
|
||||
// });
|
||||
// }
|
||||
|
||||
// Future<void> deleteGroupFromApi(int groupId) async {
|
||||
// try {
|
||||
// await apiService.deleteGroup(groupId); // your delete API call
|
||||
// deleteGroup(groupId); // remove from UI list
|
||||
// } catch (e) {
|
||||
// print('Error deleting group: $e');
|
||||
// }
|
||||
// }'
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
||||
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
body: Row(
|
||||
children: [
|
||||
if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
Expanded(child: buildGroupListLayout(isDesktop))
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildGroupListLayout(bool isDesktop) {
|
||||
return Container(
|
||||
color: Colors.white,
|
||||
width: double.infinity,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
margin: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Text('Organization List',
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.keyboard_arrow_down),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.blueAccent),
|
||||
onPressed: () async {
|
||||
// List<dynamic> users = await futureUsers;
|
||||
// context.go('/CreateGroup');
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text('Create Organization'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.88,
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
// color: Colors.red.shade100,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
buildGroupListView(isDesktop),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildGroupListView(bool isDesktop) {
|
||||
// return Container(
|
||||
// child: Text("DAta"),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildGroupListView(bool isDesktop) {
|
||||
if (apiAllOrganization == null || apiAllOrganization!.isEmpty) {
|
||||
return Center(child: Text("No groups found."));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: apiAllOrganization!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final group = apiAllOrganization![index];
|
||||
return Card(
|
||||
margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text("Organization Name: ${group['name']}",
|
||||
style: TextStyle(
|
||||
fontSize: 13, fontWeight: FontWeight.bold)),
|
||||
Text("Organization Name: ${group['name']}",
|
||||
style: TextStyle(
|
||||
fontSize: 13, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Text("Services: ${group['description'] ?? 'N/A'}"),
|
||||
Text("Created By : ${group['created_by']}"),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go("/CreateGroup", extra: group);
|
||||
|
||||
print("Edit ${group['group_id']} $group");
|
||||
},
|
||||
child: Text("Edit"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,16 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class ColorThemePickerWidget extends StatefulWidget {
|
||||
final Color? initialLayoutColor;
|
||||
final Color? initialBodyColor;
|
||||
|
||||
final Function(Color) onLayoutColorSelected;
|
||||
final Function(Color) onBodyColorSelected;
|
||||
|
||||
const ColorThemePickerWidget({
|
||||
Key? key,
|
||||
required this.initialBodyColor,
|
||||
required this.initialLayoutColor,
|
||||
required this.onLayoutColorSelected,
|
||||
required this.onBodyColorSelected,
|
||||
}) : super(key: key);
|
||||
@ -15,27 +20,37 @@ class ColorThemePickerWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _ColorThemePickerWidgetState extends State<ColorThemePickerWidget> {
|
||||
Color? selectedLayoutColor;
|
||||
Color? selectedBodyColor;
|
||||
late Color selectedLayoutColor;
|
||||
late Color selectedBodyColor;
|
||||
|
||||
// Layout colors
|
||||
final List<Color> layoutThemeColors = [
|
||||
Color(0xFF448AFF), // BlueAccent
|
||||
Color(0xFFF44336), // Red
|
||||
Color(0xFF4CAF50), // Green
|
||||
Color(0xFF12B24B), // Green
|
||||
Color(0xFFFF9800), // Orange
|
||||
Color(0xFF9C27B0), // Purple
|
||||
];
|
||||
|
||||
// Body colors
|
||||
final List<Color> bodyThemeColors = [
|
||||
Colors.grey,
|
||||
Colors.grey.shade300,
|
||||
Colors.blue.shade50,
|
||||
Colors.grey.shade100,
|
||||
Colors.blueGrey.shade50,
|
||||
Color(0xFFD9EAFF),
|
||||
Color(0xFFB0BEC5), // Grey
|
||||
Color(0xFFE0E0E0), // Grey Shade 300
|
||||
Color(0xFFE1F5FE), // Blue Shade 50
|
||||
Color(0xFFF5F5F5), // Grey Shade 100
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
selectedLayoutColor = widget.initialLayoutColor ?? Color(0xFFB0BEC5);
|
||||
selectedBodyColor = widget.initialBodyColor ?? Color(0xFFF44336);
|
||||
|
||||
print("Colors - ${widget.initialLayoutColor} -${widget.initialBodyColor}");
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
@ -63,7 +78,7 @@ class _ColorThemePickerWidgetState extends State<ColorThemePickerWidget> {
|
||||
colors: bodyThemeColors,
|
||||
onColorSelected: (color) {
|
||||
setState(() {
|
||||
selectedBodyColor = color.withOpacity(0.3); // low opacity
|
||||
selectedBodyColor = color; // low opacity
|
||||
});
|
||||
widget.onBodyColorSelected(selectedBodyColor!);
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ import 'package:frontend/Screens/itnerary_list/taxi_list.dart';
|
||||
import 'package:frontend/Screens/itnerary_list/train_list.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../services/apiService.dart';
|
||||
import '../itnerary/accomodations.dart';
|
||||
import '../itnerary/bus.dart';
|
||||
import '../itnerary/flights.dart';
|
||||
@ -25,19 +26,26 @@ class DynamicItinerary extends StatefulWidget {
|
||||
final Map<String, dynamic>? apiData;
|
||||
final List<dynamic>? apiCountryData;
|
||||
final String? loginUser;
|
||||
final Function(String, List<Map<String, dynamic>>) onItineraryUpdate; // Updated Signature
|
||||
final Map<String,dynamic> selectedPlanData;
|
||||
final bool isViewMode ;
|
||||
|
||||
const DynamicItinerary({super.key, required this.apiData, required this.onItineraryUpdate,
|
||||
required this.apiCountryData, required this.loginUser,required this.selectedPlanData, required this.isViewMode});
|
||||
final Function(String, List<Map<String, dynamic>>)
|
||||
onItineraryUpdate; // Updated Signature
|
||||
final Map<String, dynamic> selectedPlanData;
|
||||
final bool isViewMode;
|
||||
|
||||
const DynamicItinerary(
|
||||
{super.key,
|
||||
required this.apiData,
|
||||
required this.onItineraryUpdate,
|
||||
required this.apiCountryData,
|
||||
required this.loginUser,
|
||||
required this.selectedPlanData,
|
||||
required this.isViewMode});
|
||||
|
||||
@override
|
||||
_DynamicItineraryState createState() => _DynamicItineraryState();
|
||||
}
|
||||
|
||||
class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
String selectedOption = "";
|
||||
String selectedListOption = "";
|
||||
@ -46,13 +54,15 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
Map<String, dynamic>? selectedItem;
|
||||
int? selectedIndex;
|
||||
|
||||
List<dynamic>? apiAllServices;
|
||||
|
||||
// List<Map<String, dynamic>> miscellaneousList = [];
|
||||
|
||||
Map<String, List<Map<String, dynamic>>> itineraryData = {
|
||||
"Train": [],
|
||||
"Bus": [],
|
||||
"Taxi": [],
|
||||
"Miscellaneous": [],
|
||||
"Miscellaneous": [],
|
||||
"Flight": [],
|
||||
"Accomodation": [],
|
||||
"Insurance": [],
|
||||
@ -60,70 +70,93 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
"Forex": [],
|
||||
};
|
||||
|
||||
|
||||
// Store form values for each tab
|
||||
final Map<String, Map<String, String>> formData = {
|
||||
"Flight": {}, // Stores data for the Flight tab
|
||||
"Train": {}, // Stores data for the Train tab
|
||||
"Taxi": {}, // Stores data for the Car tab
|
||||
"Bus": {}, // Stores data for the Bus tab
|
||||
"Insurance": {}, // Stores data for the Bus tab
|
||||
"Accomodation": {},// Stores data for the Accomodation tab
|
||||
"Flight": {}, // Stores data for the Flight tab
|
||||
"Train": {}, // Stores data for the Train tab
|
||||
"Taxi": {}, // Stores data for the Car tab
|
||||
"Bus": {}, // Stores data for the Bus tab
|
||||
"Insurance": {}, // Stores data for the Bus tab
|
||||
"Accomodation": {}, // Stores data for the Accomodation tab
|
||||
"Miscellaneous": {},
|
||||
"Forex": {},
|
||||
"Visa": {},
|
||||
};
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
handleSelectedPlan();
|
||||
loadAllServices();
|
||||
}
|
||||
|
||||
void handleSelectedPlan(){
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
|
||||
void handleSelectedPlan() {
|
||||
// Check if selectedPlanData has itinerary data
|
||||
if (hasAnyItineraryData()) {
|
||||
print("selectedPlanData HAS DATA");
|
||||
|
||||
setState(() {
|
||||
itineraryData = {
|
||||
"Train": List<Map<String, dynamic>>.from(widget.selectedPlanData['train'] ?? []),
|
||||
"Bus": List<Map<String, dynamic>>.from(widget.selectedPlanData['bus'] ?? []),
|
||||
"Taxi": List<Map<String, dynamic>>.from(widget.selectedPlanData['taxi'] ?? []),
|
||||
"Miscellaneous": List<Map<String, dynamic>>.from(widget.selectedPlanData['miscellaneous'] ?? []),
|
||||
"Flight": List<Map<String, dynamic>>.from(widget.selectedPlanData['flight'] ?? []),
|
||||
"Accomodation": List<Map<String, dynamic>>.from(widget.selectedPlanData['accomodation'] ?? []),
|
||||
"Insurance": List<Map<String, dynamic>>.from(widget.selectedPlanData['insurance'] ?? []),
|
||||
"Visa": List<Map<String, dynamic>>.from(widget.selectedPlanData['visa'] ?? []),
|
||||
"Forex": List<Map<String, dynamic>>.from(widget.selectedPlanData['forex'] ?? []),
|
||||
"Train": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['train'] ?? []),
|
||||
"Bus": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['bus'] ?? []),
|
||||
"Taxi": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['taxi'] ?? []),
|
||||
"Miscellaneous": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['miscellaneous'] ?? []),
|
||||
"Flight": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['flight'] ?? []),
|
||||
"Accomodation": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['accomodation'] ?? []),
|
||||
"Insurance": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['insurance'] ?? []),
|
||||
"Visa": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['visa'] ?? []),
|
||||
"Forex": List<Map<String, dynamic>>.from(
|
||||
widget.selectedPlanData['forex'] ?? []),
|
||||
};
|
||||
});
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
print("No itinerary data available");
|
||||
}
|
||||
}
|
||||
|
||||
bool hasAnyItineraryData() {
|
||||
List<String> keys = [
|
||||
"train", "bus", "taxi", "miscellaneous", "flight",
|
||||
"accomodation", "insurance", "visa", "forex"
|
||||
"train",
|
||||
"bus",
|
||||
"taxi",
|
||||
"miscellaneous",
|
||||
"flight",
|
||||
"accomodation",
|
||||
"insurance",
|
||||
"visa",
|
||||
"forex"
|
||||
];
|
||||
|
||||
for (String key in keys) {
|
||||
if (widget.selectedPlanData.containsKey(key) &&
|
||||
widget.selectedPlanData[key] is List &&
|
||||
(widget.selectedPlanData[key] as List).isNotEmpty) {
|
||||
return true; // At least one list has data
|
||||
return true; // At least one list has data
|
||||
}
|
||||
}
|
||||
return false; // No itinerary data available
|
||||
}
|
||||
|
||||
|
||||
void handleClose(bool value) {
|
||||
setState(() {
|
||||
selectedOption = "";
|
||||
@ -131,23 +164,27 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
selectedIndex = null;
|
||||
selectedItem = null;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
void handleEdit(bool value,selectedItem, title) {
|
||||
void handleEdit(bool value, selectedItem, title) {
|
||||
setState(() {
|
||||
selectedOption = title;
|
||||
isSelected = value;
|
||||
this.selectedItem = selectedItem;
|
||||
|
||||
});
|
||||
|
||||
print(selectedItem);
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
void handlecreateNewPlan(name, bool value) {
|
||||
setState(() {
|
||||
selectedOption = name;
|
||||
isSelected = value;
|
||||
this.selectedItem = null;
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
Widget selectedWidget;
|
||||
Widget selectedListWidget;
|
||||
|
||||
@ -157,17 +194,15 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
// });
|
||||
// }
|
||||
|
||||
|
||||
void updateFormData(String tab, String key, String value) {
|
||||
setState(() {
|
||||
if (!formData.containsKey(tab)) {
|
||||
formData[tab] = {}; // Initialize if null
|
||||
formData[tab] = {}; // Initialize if null
|
||||
}
|
||||
formData[tab]![key] = value; // Update the stored data
|
||||
formData[tab]![key] = value; // Update the stored data
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
||||
setState(() {
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
@ -181,9 +216,8 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
// int? existingId = newData["id"];
|
||||
// String? existingId = newData["id"];
|
||||
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = newData[idKey];
|
||||
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = newData[idKey];
|
||||
|
||||
print("Looking for ID using key: $idKey, Found ID: $existingId");
|
||||
|
||||
@ -193,7 +227,8 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
// CASE 2: Update using id if available
|
||||
if (existingId != null && existingId != 0) {
|
||||
// int itemId = itemList.indexWhere((item) => item["id"] == existingId);
|
||||
int itemId = itemList.indexWhere((item) => item[idKey]?.toString() == existingId.toString());
|
||||
int itemId = itemList.indexWhere(
|
||||
(item) => item[idKey]?.toString() == existingId.toString());
|
||||
|
||||
if (itemId != -1) {
|
||||
print(" Updating existing item with id: $existingId");
|
||||
@ -205,7 +240,8 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
// CASE 1: Update if indx exists in list
|
||||
if (existingIndex != null && existingIndex != 0) {
|
||||
int itemIndex = itemList.indexWhere((item) => item["indx"] == existingIndex);
|
||||
int itemIndex =
|
||||
itemList.indexWhere((item) => item["indx"] == existingIndex);
|
||||
if (itemIndex != -1) {
|
||||
print("Updating existing item with indx: $existingIndex");
|
||||
newData["is_active"] = "1";
|
||||
@ -221,62 +257,57 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
print(" Updated $type List: ${itineraryData[type]}");
|
||||
});
|
||||
print( " onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
print(" onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
void handleItinerarydelete(String type, Map<String, dynamic> data){
|
||||
setState(() {
|
||||
// if(!itineraryData.containsKey(type)){
|
||||
// return;
|
||||
// }
|
||||
void handleItinerarydelete(String type, Map<String, dynamic> data) {
|
||||
setState(() {
|
||||
// if(!itineraryData.containsKey(type)){
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
itineraryData[type] = []; // Initialize if null
|
||||
}
|
||||
|
||||
if (!itineraryData.containsKey(type)) {
|
||||
itineraryData[type] = []; // Initialize if null
|
||||
}
|
||||
List<Map<String, dynamic>> itemList = itineraryData[type]!;
|
||||
|
||||
List<Map<String, dynamic>> itemList = itineraryData[type]!;
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = data[idKey];
|
||||
// int? existingId = data["id"];
|
||||
// String? existingId = data["id"];
|
||||
int? existingIndex = data["indx"];
|
||||
|
||||
print("🗑️ Deleting item -> ID: $existingId, Index: $existingIndex");
|
||||
|
||||
// Delete by ID
|
||||
if (existingId != null && existingId != 0) {
|
||||
// itemList.removeWhere((item) => item[idKey]?.toString() == existingId.toString());
|
||||
|
||||
String? idKey = "${type.toLowerCase()}_id";
|
||||
String? existingId = data[idKey];
|
||||
// int? existingId = data["id"];
|
||||
// String? existingId = data["id"];
|
||||
int? existingIndex = data["indx"];
|
||||
for (var item in itemList) {
|
||||
if (item[idKey]?.toString() == existingId.toString()) {
|
||||
item["is_active"] = 0; // Soft delete
|
||||
print("Updated is_active to 0 for ID: $existingId");
|
||||
}
|
||||
}
|
||||
itineraryData[type] = List.from(itemList);
|
||||
print("Deleted by ID: $existingId");
|
||||
}
|
||||
//Delete by Index
|
||||
else if (existingIndex != null && existingId != 0) {
|
||||
itemList.removeWhere((item) => item["indx"] == existingIndex);
|
||||
print("Deleted by ID: $existingIndex");
|
||||
} else {
|
||||
print("No Valid Deletion");
|
||||
}
|
||||
|
||||
print("🗑️ Deleting item -> ID: $existingId, Index: $existingIndex");
|
||||
|
||||
// Delete by ID
|
||||
if(existingId != null && existingId != 0){
|
||||
// itemList.removeWhere((item) => item[idKey]?.toString() == existingId.toString());
|
||||
|
||||
for (var item in itemList) {
|
||||
if (item[idKey]?.toString() == existingId.toString()) {
|
||||
item["is_active"] = 0; // Soft delete
|
||||
print("Updated is_active to 0 for ID: $existingId");
|
||||
}
|
||||
}
|
||||
itineraryData[type] = List.from(itemList);
|
||||
print("Deleted by ID: $existingId");
|
||||
}
|
||||
//Delete by Index
|
||||
else if(existingIndex != null && existingId !=0){
|
||||
itemList.removeWhere((item) => item["indx"] == existingIndex);
|
||||
print("Deleted by ID: $existingIndex");
|
||||
}
|
||||
else{
|
||||
print("No Valid Deletion");
|
||||
}
|
||||
|
||||
itineraryData[type]= List.from(itemList);
|
||||
});
|
||||
print( " onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
itineraryData[type] = List.from(itemList);
|
||||
});
|
||||
print(" onItineraryUpdate - $type - ${itineraryData[type]!} ");
|
||||
widget.onItineraryUpdate(type, itineraryData[type]!); // Notify parent
|
||||
}
|
||||
|
||||
|
||||
// void handleItineraryUpdate(String type, Map<String, dynamic> newData) {
|
||||
// setState(() {
|
||||
// if (!itineraryData.containsKey(type)) {
|
||||
@ -294,265 +325,416 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
// print("Updated $type List: ${itineraryData[type]}");
|
||||
// }
|
||||
|
||||
|
||||
|
||||
switch (selectedListOption) {
|
||||
case "Train":
|
||||
selectedListWidget = TrainListWidget( trainList : itineraryData["Train"]!,
|
||||
selectedListWidget = TrainListWidget(
|
||||
trainList: itineraryData["Train"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteTrain: (data)=> handleItinerarydelete("Train", data),
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteTrain: (data) => handleItinerarydelete("Train", data),
|
||||
);
|
||||
break;
|
||||
case "Taxi":
|
||||
selectedListWidget = TaxiListWidget( taxiList : itineraryData["Taxi"]! ,
|
||||
onOpen: handleEdit,
|
||||
onDeleteTaxi: (data)=> handleItinerarydelete("Taxi", data),);
|
||||
break;
|
||||
case "Bus":
|
||||
selectedListWidget = BusListWidget(busList : itineraryData["Bus"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteBus: (data) => handleItinerarydelete("Bus", data),);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedListWidget = InsuranceListWidget(insuranceList : itineraryData["Insurance"]!,
|
||||
onOpen: handleEdit, apiData: widget.apiData,
|
||||
onDeleteInsurance:(data) => handleItinerarydelete("Insurance", data),);
|
||||
break;
|
||||
case "Visa":
|
||||
selectedListWidget = VisaListWidget(visaList : itineraryData["Visa"]!,
|
||||
apiData: widget.apiData, apiCountryData : widget.apiCountryData,
|
||||
selectedListWidget = TaxiListWidget(
|
||||
taxiList: itineraryData["Taxi"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Visa", data),);
|
||||
break;
|
||||
case "Forex":
|
||||
selectedListWidget = ForexListWidget(forexList: itineraryData["Forex"]!,
|
||||
apiCountryData : widget.apiCountryData,
|
||||
onOpen: handleEdit,
|
||||
onDeleteForex: (data) => handleItinerarydelete("Forex", data),);
|
||||
break;
|
||||
case "Accomodation":
|
||||
selectedListWidget = AccomodationListWidget(accommodationList: itineraryData["Accomodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteAccommodation:(data) => handleItinerarydelete("Accomodation", data));
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedListWidget = MiscellaneousListWidget(miscellaneousList: itineraryData["Miscellaneous"]!,
|
||||
onOpen: handleEdit,apiData: widget.apiData,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Miscellaneous", data),
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteTaxi: (data) => handleItinerarydelete("Taxi", data),
|
||||
);
|
||||
break;
|
||||
case "Flight":
|
||||
case "Bus":
|
||||
selectedListWidget = BusListWidget(
|
||||
busList: itineraryData["Bus"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteBus: (data) => handleItinerarydelete("Bus", data),
|
||||
);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedListWidget = InsuranceListWidget(
|
||||
insuranceList: itineraryData["Insurance"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
apiData: widget.apiData,
|
||||
onDeleteInsurance: (data) => handleItinerarydelete("Insurance", data),
|
||||
);
|
||||
break;
|
||||
case "Visa":
|
||||
selectedListWidget = VisaListWidget(
|
||||
visaList: itineraryData["Visa"]!,
|
||||
apiData: widget.apiData,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteMiscellaneous: (data) => handleItinerarydelete("Visa", data),
|
||||
);
|
||||
break;
|
||||
case "Forex":
|
||||
selectedListWidget = ForexListWidget(
|
||||
forexList: itineraryData["Forex"]!,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteForex: (data) => handleItinerarydelete("Forex", data),
|
||||
);
|
||||
break;
|
||||
case "Accomodation":
|
||||
selectedListWidget = AccomodationListWidget(
|
||||
accommodationList: itineraryData["Accomodation"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteAccommodation: (data) =>
|
||||
handleItinerarydelete("Accomodation", data));
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedListWidget = MiscellaneousListWidget(
|
||||
miscellaneousList: itineraryData["Miscellaneous"]!,
|
||||
onOpen: handleEdit,
|
||||
isViewMode: widget.isViewMode,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
apiData: widget.apiData,
|
||||
onDeleteMiscellaneous: (data) =>
|
||||
handleItinerarydelete("Miscellaneous", data),
|
||||
);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
selectedListWidget = FlightListWidget(flightList : itineraryData["Flight"]!,
|
||||
onOpen: handleEdit,
|
||||
onDeleteFlight: (data) => handleItinerarydelete("Flight", data)
|
||||
|
||||
);
|
||||
selectedListWidget = FlightListWidget(
|
||||
flightList: itineraryData["Flight"]!,
|
||||
onOpen: handleEdit,
|
||||
onAddNew: handlecreateNewPlan,
|
||||
isViewMode: widget.isViewMode,
|
||||
onDeleteFlight: (data) => handleItinerarydelete("Flight", data));
|
||||
break;
|
||||
}
|
||||
|
||||
switch (selectedOption) {
|
||||
case "Train":
|
||||
selectedWidget = TrainScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSavetrain :(data) => handleItineraryUpdate("Train", data),
|
||||
selectedWidget = TrainScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSavetrain: (data) => handleItineraryUpdate("Train", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Taxi":
|
||||
selectedWidget = TaxiScreen(onClose: handleClose,apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSavetaxi: (data)=> handleItineraryUpdate("Taxi", data),
|
||||
selectedWidget = TaxiScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSavetaxi: (data) => handleItineraryUpdate("Taxi", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Bus":
|
||||
selectedWidget = BusScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveBus: (data)=> handleItineraryUpdate("Bus", data),
|
||||
selectedWidget = BusScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveBus: (data) => handleItineraryUpdate("Bus", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Insurance":
|
||||
selectedWidget = InsuranceScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveInsurance:(data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem);
|
||||
selectedWidget = InsuranceScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveInsurance: (data) => handleItineraryUpdate("Insurance", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
|
||||
case "Visa":
|
||||
selectedWidget = VisaScreen(onClose: handleClose,apiData: widget.apiData, apiCountryData : widget.apiCountryData, loginUser : widget.loginUser,
|
||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||
selectedItem: selectedItem,);
|
||||
break;
|
||||
case "Visa":
|
||||
selectedWidget = VisaScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveVisa: (data) => handleItineraryUpdate("Visa", data),
|
||||
selectedItem: selectedItem,
|
||||
);
|
||||
break;
|
||||
case "Miscellaneous":
|
||||
selectedWidget = MiscellaneousScreen(onClose: handleClose, apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
onSaveMiscellaneous: (data) => handleItineraryUpdate("Miscellaneous", data),
|
||||
selectedItem: selectedItem, selectedIndex: selectedIndex, );
|
||||
break;
|
||||
case "Accomodation":
|
||||
selectedWidget = AccomodationScreen( onClose: handleClose, loginUser : widget.loginUser,
|
||||
onSaveAccomadation: (data)=>handleItineraryUpdate("Accomodation", data),
|
||||
selectedItem: selectedItem, );
|
||||
selectedWidget = MiscellaneousScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveMiscellaneous: (data) =>
|
||||
handleItineraryUpdate("Miscellaneous", data),
|
||||
selectedItem: selectedItem,
|
||||
selectedIndex: selectedIndex,
|
||||
);
|
||||
break;
|
||||
case "Accomodation":
|
||||
selectedWidget = AccomodationScreen(
|
||||
onClose: handleClose,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveAccomadation: (data) =>
|
||||
handleItineraryUpdate("Accomodation", data),
|
||||
selectedItem: selectedItem,
|
||||
);
|
||||
break;
|
||||
case "Forex":
|
||||
selectedWidget = ForexScreen( onClose: handleClose,apiData: widget.apiData, loginUser : widget.loginUser,
|
||||
apiCountryData : widget.apiCountryData,
|
||||
onSaveForex: (data)=>handleItineraryUpdate("Forex", data),
|
||||
selectedWidget = ForexScreen(
|
||||
onClose: handleClose,
|
||||
apiData: widget.apiData,
|
||||
loginUser: widget.loginUser,
|
||||
apiCountryData: widget.apiCountryData,
|
||||
onSaveForex: (data) => handleItineraryUpdate("Forex", data),
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
case "Flight":
|
||||
default:
|
||||
selectedWidget = FlightScreen(onClose: handleClose,loginUser : widget.loginUser,
|
||||
onSaveFlight: (data)=>handleItineraryUpdate("Flight", data), apiData: widget.apiData,
|
||||
selectedItem: selectedItem
|
||||
);
|
||||
selectedWidget = FlightScreen(
|
||||
onClose: handleClose,
|
||||
loginUser: widget.loginUser,
|
||||
onSaveFlight: (data) => handleItineraryUpdate("Flight", data),
|
||||
apiData: widget.apiData,
|
||||
selectedItem: selectedItem);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isMobile = sizingInfo.isMobile;
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
|
||||
return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text("Itinerary",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
),),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Color(0xFFF4F4FB)),
|
||||
border: Border(
|
||||
bottom: BorderSide(color: Color(0xFFF4F4FB), width: 2)),
|
||||
borderRadius: BorderRadius.circular(1),
|
||||
color: Color(0xFFF4F4FB),
|
||||
// color: Color(0xFFF4F4FB),
|
||||
),
|
||||
padding: EdgeInsets.all(5),
|
||||
child: isMobile ?
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: _buildOptions(),
|
||||
),
|
||||
),
|
||||
)
|
||||
padding: EdgeInsets.all(10),
|
||||
child: isMobile
|
||||
? Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: _buildOptions(),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: _buildOptions(),
|
||||
),
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: _buildOptions(),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 2),
|
||||
isSelected ? selectedWidget : selectedListWidget,
|
||||
// TrainScreen()
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
|
||||
Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 2),
|
||||
isSelected ? selectedWidget : selectedListWidget,
|
||||
// TrainScreen()
|
||||
],
|
||||
),
|
||||
|
||||
],
|
||||
|
||||
);
|
||||
});
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
List<Widget> _buildOptions() {
|
||||
return [
|
||||
_buildOption("Flight", itineraryData["Flight"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Taxi", itineraryData["Taxi"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Train", itineraryData["Train"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Bus", itineraryData["Bus"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Accomodation", itineraryData["Accomodation"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Forex", itineraryData["Forex"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Insurance", itineraryData["Insurance"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Visa", itineraryData["Visa"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Miscellaneous", itineraryData["Miscellaneous"]?.isNotEmpty ?? false),
|
||||
];
|
||||
if (apiAllServices == null) return [];
|
||||
|
||||
return apiAllServices!.map((service) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 20.0),
|
||||
child: _buildOption(
|
||||
service, itineraryData[service['name']]?.isNotEmpty ?? false),
|
||||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Widget _buildOption(
|
||||
Map<String, dynamic> service,
|
||||
bool hasData,
|
||||
) {
|
||||
String name = service['name'];
|
||||
String iconUrl = service['icon']; // Can be empty string
|
||||
// Optional: define local icon fallback if iconUrl is empty
|
||||
IconData fallbackIcon = _getLocalIconForService(name);
|
||||
// final idMap = {"service_id": service['service_id'].toString()};
|
||||
// final isSelected = selectedServiceIds.contains(idMap);
|
||||
|
||||
// List<Widget> _buildOptions() {
|
||||
// return [
|
||||
// _buildOption("Flight", itineraryData["Flight"]?.isNotEmpty ?? false),
|
||||
// _buildOption("Flight"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Taxi"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Train"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Bus"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Accomodation"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Forex"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Insurance"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Visa"),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Miscellaneous"),
|
||||
// ];
|
||||
// }
|
||||
String serviceId = service['service_id'].toString();
|
||||
// bool isSelected = selectedServiceIds.contains(serviceId);
|
||||
// bool isSelected =
|
||||
// selectedServiceIds.any((item) => item["service_id"] == serviceId);
|
||||
|
||||
Widget _buildOption(String title, bool hasData) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedListOption = title;
|
||||
selectedListOption = name;
|
||||
isSelected = false;
|
||||
});
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
if(hasData)
|
||||
Icon(Icons.circle_notifications,color: Colors.green, size: 10,),
|
||||
|
||||
SizedBox(width: 5),
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: selectedListOption == title ? Colors.blueAccent : Color(0xFF575A74),
|
||||
fontWeight:
|
||||
selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
|
||||
SizedBox(width: 5),
|
||||
|
||||
SizedBox(width: 5),
|
||||
if (selectedListOption == title && widget.isViewMode == false)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedOption = title;
|
||||
isSelected = true;
|
||||
selectedItem = null;
|
||||
});
|
||||
},
|
||||
child: Icon(
|
||||
Icons.add_circle,
|
||||
color: Colors.blueAccent,
|
||||
child: Row(children: [
|
||||
iconUrl.isNotEmpty
|
||||
? Image.network(
|
||||
iconUrl,
|
||||
width: 18,
|
||||
height: 18,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Icon(
|
||||
fallbackIcon,
|
||||
size: 18,
|
||||
color: selectedListOption == name
|
||||
? Color(0xFF114D8B)
|
||||
: Color(0xFF475569),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Icon(
|
||||
fallbackIcon,
|
||||
size: 18,
|
||||
color: selectedListOption == name
|
||||
? Color(0xFF114D8B)
|
||||
: Color(0xFF475569),
|
||||
),
|
||||
),
|
||||
|
||||
]
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
|
||||
SizedBox(width: 5),
|
||||
Text(
|
||||
name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
// color: selectedListOption == title ? Colors.blueAccent : Color(0xFF575A74),
|
||||
color: selectedListOption == name
|
||||
? Color(0xFF114D8B)
|
||||
: Color(0xFF475569),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: selectedListOption == name
|
||||
? FontWeight.bold
|
||||
: FontWeight.w500),
|
||||
// fontWeight: selectedListOption == title ? FontWeight.bold : FontWeight.normal,)),
|
||||
),
|
||||
|
||||
SizedBox(width: 5),
|
||||
// if (selectedListOption == title && widget.isViewMode == false)
|
||||
if (hasData)
|
||||
Container(
|
||||
height: 13,
|
||||
width: 13,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Colors.green, width: 1.5),
|
||||
),
|
||||
child:
|
||||
Icon(Icons.notifications_rounded, size: 8, color: Colors.green
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// List<Widget> _buildOptions1() {
|
||||
// return [
|
||||
// _buildOption("Flight", itineraryData["Flight"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Taxi", itineraryData["Taxi"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Train", itineraryData["Train"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Bus", itineraryData["Bus"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption(
|
||||
// "Accomodation", itineraryData["Accomodation"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Forex", itineraryData["Forex"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption(
|
||||
// "Insurance", itineraryData["Insurance"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption("Visa", itineraryData["Visa"]?.isNotEmpty ?? false),
|
||||
// SizedBox(width: 20),
|
||||
// _buildOption(
|
||||
// "Miscellaneous", itineraryData["Miscellaneous"]?.isNotEmpty ?? false),
|
||||
// ];
|
||||
// }
|
||||
//
|
||||
//
|
||||
|
||||
// Widget _buildOption1(String title, bool hasData) {
|
||||
// return GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// selectedListOption = title;
|
||||
// isSelected = false;
|
||||
// });
|
||||
// },
|
||||
// child: Row(children: [
|
||||
// if (hasData)
|
||||
// Icon(
|
||||
// Icons.circle_notifications,
|
||||
// color: Colors.green,
|
||||
// size: 10,
|
||||
// ),
|
||||
// SizedBox(width: 5),
|
||||
// Text(title,
|
||||
// style: TextStyle(
|
||||
// fontSize: 13,
|
||||
// color: selectedListOption == title
|
||||
// ? Colors.blueAccent
|
||||
// : Color(0xFF575A74),
|
||||
// fontWeight: selectedListOption == title
|
||||
// ? FontWeight.bold
|
||||
// : FontWeight.normal,
|
||||
// )),
|
||||
// SizedBox(width: 5),
|
||||
// SizedBox(width: 5),
|
||||
// if (selectedListOption == title && widget.isViewMode == false)
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// selectedOption = title;
|
||||
// isSelected = true;
|
||||
// selectedItem = null;
|
||||
// });
|
||||
// },
|
||||
// child: Icon(
|
||||
// Icons.add_circle,
|
||||
// color: Colors.blueAccent,
|
||||
// size: 18,
|
||||
// ),
|
||||
// ),
|
||||
// ]),
|
||||
// );
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
|
||||
class ListPlans extends StatefulWidget {
|
||||
const ListPlans({super.key});
|
||||
@ -24,15 +25,37 @@ class _ListPlansState extends State<ListPlans> {
|
||||
String? orgId;
|
||||
String? token;
|
||||
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
getToken();
|
||||
initializeData();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initializeData();
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
// futurePlans = fetchPlans();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> initializeData() async {
|
||||
token = await getToken();
|
||||
userId = await getUserId();
|
||||
@ -170,336 +193,331 @@ class _ListPlansState extends State<ListPlans> {
|
||||
}
|
||||
|
||||
Widget buildTableLayout(isDesktop) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
return Container(
|
||||
color: bodyColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
color: Colors.white,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
SizedBox(height: 2),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text('Plans List',
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.keyboard_arrow_down),
|
||||
onPressed: () {},
|
||||
Row(
|
||||
children: [
|
||||
const Text('Plans List',
|
||||
style: TextStyle(
|
||||
fontFamily: "Archivo",
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF212121))),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: Colors.blueAccent),
|
||||
onPressed: () {
|
||||
context.go('/createPlan', extra: {
|
||||
// 'apiCountryData': apiCountryData,
|
||||
'orgId': orgId,
|
||||
});
|
||||
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.add_circle,
|
||||
color: Colors.white,
|
||||
),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Text('NewPlan'),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 2),
|
||||
Divider(
|
||||
thickness: 0.2, // how "thick" the line is
|
||||
color: Colors.grey, // optional
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<Plan>>(
|
||||
future: futurePlans, // Use the futurePlans variable
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.error_outline,
|
||||
color: Colors.redAccent,
|
||||
size: 60,
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width * 0.2,
|
||||
// or use Flexible
|
||||
child: TextField(
|
||||
onChanged: (query) {},
|
||||
decoration: InputDecoration(
|
||||
hintText: "Search for a plan",
|
||||
hintStyle:
|
||||
TextStyle(fontSize: 14, color: Color(0xFF9E9DBD)),
|
||||
prefixIcon:
|
||||
Icon(Icons.search, color: Color(0xFF9E9DBD)),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
Text(
|
||||
"Oops!",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.redAccent,
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Colors.grey.shade300, width: 0.5),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(
|
||||
"No Plans Available For This User",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
" Please Create Plan",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 20),
|
||||
// ElevatedButton.icon(
|
||||
// onPressed: () {
|
||||
// // Optional: retry logic or navigation
|
||||
// },
|
||||
// icon: Icon(Icons.refresh),
|
||||
// label: Text("Try Again"),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.blueAccent,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return const Center(child: Text("No plans available"));
|
||||
}
|
||||
|
||||
List<Plan> plans = snapshot.data!; // Extract the list of plans
|
||||
|
||||
// Ensure planId is sorted in descending order
|
||||
plans.sort((a, b) => int.parse(b.planId.toString())
|
||||
.compareTo(int.parse(a.planId.toString())));
|
||||
|
||||
// return ResponsiveBuilder(
|
||||
// builder: (context, sizingInfo) {
|
||||
// bool isTabletOrDesktop = sizingInfo.isTablet || sizingInfo.isDesktop;
|
||||
//
|
||||
// return SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// child: Container(
|
||||
// color: Colors.grey,
|
||||
// child: SizedBox(
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
// child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical,
|
||||
// // scrollDirection: isTabletOrDesktop ? Axis.vertical : Axis.horizontal,
|
||||
//
|
||||
// // constraints: isTabletOrDesktop
|
||||
// // ? const BoxConstraints(maxWidth: double.infinity)
|
||||
// // : BoxConstraints.tightFor(width: 600),
|
||||
//
|
||||
//
|
||||
// child: DataTable(
|
||||
// // columnSpacing: 50.0,
|
||||
// dividerThickness: 0.5, // Reduce the thickness of row dividers
|
||||
// border: TableBorder(
|
||||
// horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200), // Reduce horizontal line thickness
|
||||
// ),
|
||||
// columns: const [
|
||||
// DataColumn(label: Text('Plan ID', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Title', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Trip Type', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Cost Center', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Functional Department', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Purpose Of Travel', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// // DataColumn(label: Text('Description', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// //
|
||||
// DataColumn(label: Text('Is Billable', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Status', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// DataColumn(label: Text('Actions', style: TextStyle(color: Colors.grey, fontWeight: FontWeight.bold,))),
|
||||
// ],
|
||||
// rows: plans.map((plan) {
|
||||
// return DataRow(cells: [
|
||||
// DataCell(Text(plan.planId)),
|
||||
// // DataCell(Text(plan.tripTitle)),
|
||||
// DataCell(Row(
|
||||
// children: [
|
||||
// Flexible(
|
||||
// child: Text(
|
||||
// plan.tripTitle,
|
||||
// softWrap: true,
|
||||
// overflow: TextOverflow.ellipsis, // Adds "..." if text is too long
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// )),
|
||||
//
|
||||
//
|
||||
// DataCell(Text(plan.tripType)),
|
||||
// DataCell(Text(plan.costCenter)),
|
||||
// // DataCell(Text(plan.functionalDepartment)),
|
||||
// // DataCell(Text(plan.purposeOfTravel)),
|
||||
// // DataCell(Text(plan.description)),
|
||||
// //
|
||||
// DataCell(Text(plan.isBillable)),
|
||||
// DataCell(Text(plan.status)),
|
||||
// DataCell(
|
||||
// TextButton(
|
||||
// onPressed: () {
|
||||
// viewPlan(plan.planId);
|
||||
// print("View button clicked for ${plan.planId}");
|
||||
// },
|
||||
// child: const Text('View',
|
||||
// style: TextStyle(color: Colors.blueAccent)),
|
||||
// ),
|
||||
// ),
|
||||
// ]);
|
||||
// }).toList(),
|
||||
// ),
|
||||
//
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
//
|
||||
// },
|
||||
// );
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 1.5,
|
||||
// width: MediaQuery.of(context).size.width , // Ensure table is wider than screen
|
||||
// width: double.infinity , // Ensure table is wider than screen
|
||||
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.vertical, // Inner wrapper for vertical scrolling
|
||||
scrollDirection: Axis
|
||||
.horizontal, // Inner wrapper for vertical scrolling
|
||||
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: 1300),
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
|
||||
child: Container(
|
||||
// color: Colors.amber,
|
||||
child: DataTable(
|
||||
columnSpacing:
|
||||
50.0, // Adjust spacing between columns
|
||||
dividerThickness: 0.5,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(
|
||||
label: Text('Plan ID',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Title',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Type',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Cost Center',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Is Billable',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Status',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Actions',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold))),
|
||||
],
|
||||
|
||||
rows: plans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId)),
|
||||
DataCell(Text(plan.tripTitle,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(plan.tripType)),
|
||||
DataCell(Text(plan.costCenter)),
|
||||
DataCell(Text(plan.isBillable)),
|
||||
DataCell(
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 5,
|
||||
horizontal:
|
||||
10), // Padding for better look
|
||||
decoration: BoxDecoration(
|
||||
color: plan.status == "Active"
|
||||
? Colors.green.shade50
|
||||
: Colors
|
||||
.grey.shade50, // Background color
|
||||
borderRadius: BorderRadius.circular(
|
||||
10), // Rounded corners
|
||||
),
|
||||
child: Text(
|
||||
plan.status,
|
||||
style: TextStyle(
|
||||
color: plan.status == "Active"
|
||||
? Colors.green
|
||||
: Colors.grey, // Text color
|
||||
fontWeight: FontWeight
|
||||
.bold, // Optional: Make text bold
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(Row(children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.remove_red_eye,
|
||||
color: Colors.blue),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: true);
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.edit, color: Colors.green),
|
||||
onPressed: () {
|
||||
viewPlan(plan.planId, isViewMode: false);
|
||||
},
|
||||
),
|
||||
// IconButton(
|
||||
// icon: Icon(Icons.delete, color: Colors.red),
|
||||
// onPressed: () {
|
||||
// deletePlan(plan.planId);
|
||||
// },
|
||||
// ),
|
||||
])),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
// borderRadius: BorderRadius.circular(8),
|
||||
borderSide:
|
||||
BorderSide(color: Colors.blueAccent, width: 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
// SizedBox(width: 16),
|
||||
Spacer(),
|
||||
// ElevatedButton(
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// foregroundColor: Colors.white,
|
||||
// backgroundColor: Colors.blueAccent,
|
||||
// ),
|
||||
// onPressed: () {
|
||||
// context.go('/createPlan', extra: {
|
||||
// 'orgId': orgId,
|
||||
// });
|
||||
// if (!isDesktop) Navigator.pop(context);
|
||||
// },
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(Icons.add_circle, color: Colors.white),
|
||||
// SizedBox(width: 5),
|
||||
// Text('NewPlan'),
|
||||
//
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF114D8B),
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: Color(0xFF114D8B),
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: Color(0xFF114D8B), width: 2),
|
||||
),
|
||||
padding:
|
||||
EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
context.go('/createPlan', extra: {
|
||||
'orgId': orgId,
|
||||
});
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize:
|
||||
MainAxisSize.min, // Ensures content fits nicely
|
||||
children: [
|
||||
Text(
|
||||
"Add New Plan",
|
||||
style: TextStyle(fontSize: 13),
|
||||
),
|
||||
SizedBox(width: 8), // spacing between icon and text
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
size: 15,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
FutureBuilder<List<Plan>>(
|
||||
future: futurePlans,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError ||
|
||||
!snapshot.hasData ||
|
||||
snapshot.data!.isEmpty) {
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Icon(Icons.error_outline,
|
||||
color: Colors.redAccent, size: 60),
|
||||
SizedBox(height: 16),
|
||||
Text("Oops!",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.redAccent)),
|
||||
SizedBox(height: 8),
|
||||
Text("No Plans Available For This User",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.grey)),
|
||||
SizedBox(height: 20),
|
||||
Text("Please Create Plan",
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16, color: Colors.grey)),
|
||||
SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Plan> plans = snapshot.data!;
|
||||
plans.sort((a, b) =>
|
||||
int.parse(b.planId).compareTo(int.parse(a.planId)));
|
||||
|
||||
Widget table = LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
double minWidth = isDesktop ? constraints.maxWidth : 1300;
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: minWidth),
|
||||
child: DataTable(
|
||||
dividerThickness: 0.5,
|
||||
columnSpacing: isDesktop ? 24.0 : 16.0,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(
|
||||
width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
DataColumn(
|
||||
label: Text('Plan Id',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontSize: 14,
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('UserName',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Title',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Trip Type',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Created On',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Status',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
DataColumn(
|
||||
label: Text('Actions',
|
||||
style: TextStyle(
|
||||
color: Color(0xFF9E9DBD),
|
||||
fontFamily: "Archivo",
|
||||
fontWeight: FontWeight.bold))),
|
||||
],
|
||||
rows: plans.map((plan) {
|
||||
return DataRow(cells: [
|
||||
DataCell(Text(plan.planId,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(
|
||||
plan.userName.isNotEmpty
|
||||
? plan.userName
|
||||
: plan.travellerName,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.tripTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
),
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis)),
|
||||
DataCell(Text(plan.tripType,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Text(plan.createdOn,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontFamily: "Archivo",
|
||||
))),
|
||||
DataCell(Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 4, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: plan.status == "Active"
|
||||
? layoutColor
|
||||
: Colors.grey.shade50,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Text(
|
||||
plan.statusValue,
|
||||
style: TextStyle(
|
||||
color: plan.status == "Active"
|
||||
? Colors.white
|
||||
: Colors.grey,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
)),
|
||||
DataCell(Row(children: [
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.remove_red_eye,
|
||||
color: Color(0xFF475569),
|
||||
size: 18,
|
||||
),
|
||||
onPressed: () => viewPlan(plan.planId,
|
||||
isViewMode: true)),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
viewPlan(plan.planId, isViewMode: false),
|
||||
child: Image.asset(
|
||||
'assets/images/IconsImg/edit.png',
|
||||
width: 20,
|
||||
height: 15),
|
||||
),
|
||||
|
||||
// IconButton(
|
||||
// icon: const Icon(Icons.edit,
|
||||
// color: Colors.green),
|
||||
// onPressed: () => viewPlan(plan.planId,
|
||||
// isViewMode: false) ),
|
||||
])),
|
||||
]);
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return Expanded(
|
||||
child: isDesktop
|
||||
? table
|
||||
: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: table,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,10 +2,14 @@ import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/Screens/policy/policyCriteria.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
import '../../widgets/custom_text_field.dart';
|
||||
import '../../widgets/custom_user_form.dart';
|
||||
|
||||
@ -17,14 +21,156 @@ class Policy extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PolicyState extends State<Policy> {
|
||||
final GlobalKey<PolicyCriteriaState> policyCriteriaKey =
|
||||
GlobalKey<PolicyCriteriaState>();
|
||||
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
late String policyType = "domestic";
|
||||
int? selectedServiceIndex = 1;
|
||||
// int? selectedServiceIndex = 1;
|
||||
ValueNotifier<String> selectedServiceIndex = ValueNotifier("1");
|
||||
late String selectedService = "Train";
|
||||
// ValueNotifier<String> selectedService = ValueNotifier("Train");
|
||||
bool isViewMode = false;
|
||||
String? _selectedTripType;
|
||||
String? PolicyName;
|
||||
String? SelectedDomestic = "1";
|
||||
String? SelectedInternational = "0";
|
||||
String? orgId;
|
||||
String? userId;
|
||||
|
||||
bool showClass = true;
|
||||
bool showCost = true;
|
||||
|
||||
TextEditingController _policyController = TextEditingController();
|
||||
List<Map<String, dynamic>>? policy_details = [];
|
||||
|
||||
Map<String, dynamic> get policyData {
|
||||
List<Map<String, dynamic>> policyDetails = policy_details!.where((service) {
|
||||
// Only check these specific fields for emptiness
|
||||
final fieldsToCheck = [
|
||||
'cost',
|
||||
'class',
|
||||
'a1_action',
|
||||
'a2_action',
|
||||
'a3_action'
|
||||
];
|
||||
|
||||
// If any of the important fields has a value, keep it
|
||||
return fieldsToCheck.any((field) {
|
||||
final value = service[field];
|
||||
return value != null && value.toString().trim().isNotEmpty;
|
||||
});
|
||||
}).toList();
|
||||
|
||||
Map<String, dynamic> data = {
|
||||
"name": _policyController.text,
|
||||
"domestic": SelectedDomestic,
|
||||
"international": SelectedInternational,
|
||||
"is_active": "1",
|
||||
"org_id": orgId,
|
||||
"created_by": userId,
|
||||
"policy_details": policyDetails,
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadinitializeData();
|
||||
loadInitialData();
|
||||
});
|
||||
|
||||
// updateData();
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
void loadinitializeData() async {
|
||||
orgId = await getOrgId();
|
||||
userId = await getUserId();
|
||||
}
|
||||
|
||||
// void updateData(){}
|
||||
|
||||
void handleSubmit() async {
|
||||
print("USR Detail Submit - $policyData");
|
||||
|
||||
policyCriteriaKey.currentState?.saveCurrentPolicy();
|
||||
|
||||
// Now the full data is ready in policyDataFromChild
|
||||
print("Submitting full policyData: $policyData");
|
||||
|
||||
Map<String, dynamic> data = policyData;
|
||||
|
||||
createPolicyData(data);
|
||||
|
||||
// if (!isValidData(data)) {
|
||||
// print("USERDETAILS : $policyData");
|
||||
// print("Validation Failed: Required fields are missing.");
|
||||
// setState(() {});
|
||||
// return; // Stop execution if validation fails
|
||||
// } else {
|
||||
// // print("USERDETAILS : $policyData");
|
||||
// // orgId = await getOrgId();
|
||||
//
|
||||
// createPolicyData(policyData);
|
||||
// }
|
||||
}
|
||||
|
||||
Future<void> createPolicyData(Map<String, dynamic> policyData) async {
|
||||
final String apiUrldata = '$apiUrl/api/policy/createOrUpdate';
|
||||
final token = await getToken(); // Fetch token
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
// 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(policyData), // Convert map to JSON
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("policyData submitted successfully!");
|
||||
print("Response: ${response.body}");
|
||||
|
||||
context.go('/PolicyList');
|
||||
} else {
|
||||
print("Failed to submit policyData. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print(" Error submitting policyData: $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
@ -32,14 +178,70 @@ class _PolicyState extends State<Policy> {
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
||||
appBar: isDesktop ? null : const CustomAppBar(title: 'Policy'),
|
||||
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
body: Row(
|
||||
body: Column(
|
||||
children: [
|
||||
if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
Expanded(child: buildPolicyLayout(isDesktop))
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
Expanded(
|
||||
child: Container(
|
||||
color: bodyColor,
|
||||
child: buildPolicyLayout(isDesktop),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
color: Colors.white,
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: _buildSubmit(isDesktop),
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: _buildSubmit(isDesktop),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// Row(
|
||||
// children: [
|
||||
// if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
// Expanded(
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: Container(
|
||||
// // height: MediaQuery.of(context).size.height * 0.8,
|
||||
// color: bodyColor,
|
||||
// child: buildPolicyLayout(isDesktop)),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Container(
|
||||
// color: Colors.white,
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.all(8.0),
|
||||
// child: isDesktop
|
||||
// ? Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.end,
|
||||
// children: _buildSubmit(isDesktop),
|
||||
// )
|
||||
// : Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: _buildSubmit(isDesktop),
|
||||
// )),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -49,9 +251,11 @@ class _PolicyState extends State<Policy> {
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Container(
|
||||
margin: isDesktop
|
||||
? EdgeInsets.all(20.0)
|
||||
? EdgeInsets.all(10.0)
|
||||
: EdgeInsets.only(left: 20.0, bottom: 20.0, top: 10.0, right: 20.0),
|
||||
height: MediaQuery.of(context).size.height,
|
||||
height: isDesktop
|
||||
? MediaQuery.of(context).size.height * 0.98
|
||||
: MediaQuery.of(context).size.height,
|
||||
decoration: BoxDecoration(
|
||||
border: isDesktop
|
||||
? Border.all(
|
||||
@ -59,7 +263,10 @@ class _PolicyState extends State<Policy> {
|
||||
color: Color(0xFFF7F7FB),
|
||||
)
|
||||
: null,
|
||||
color: Color(0xFFF7F7FB),
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFF7F7FB),
|
||||
|
||||
// color: Colors.amber,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
@ -72,7 +279,8 @@ class _PolicyState extends State<Policy> {
|
||||
Container(
|
||||
padding: isDesktop ? EdgeInsets.all(6) : EdgeInsets.all(3),
|
||||
// color: Colors.white, // Background to avoid overlapping
|
||||
color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
||||
color: Colors.white,
|
||||
// color: isDesktop ? Color(0xFFF7F7FB) : Colors.white,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
@ -87,73 +295,6 @@ class _PolicyState extends State<Policy> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Container(
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "domestic";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Colors.blue.shade300,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "Domestic",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "domestic"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// setState(() {
|
||||
// policyType = "international";
|
||||
// });
|
||||
// },
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.all(10),
|
||||
// // color: Color(0xFFEBEBF7),
|
||||
// color: policyType == "international"
|
||||
// ? Colors.blueAccent.shade100
|
||||
// : Color(0xFFEBEBF7),
|
||||
// // color: Color(0xFFE3F2FD),
|
||||
//
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "International",
|
||||
// style: TextStyle(
|
||||
// color: policyType == "international"
|
||||
// ? Colors.white
|
||||
// : Colors.black87,
|
||||
// fontSize: 15,
|
||||
// fontWeight: FontWeight.bold),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -182,7 +323,7 @@ class _PolicyState extends State<Policy> {
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
// controller: controllers["Fname"],
|
||||
controller: _policyController,
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {},
|
||||
decoration: InputDecoration(
|
||||
@ -202,6 +343,9 @@ class _PolicyState extends State<Policy> {
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
@ -226,6 +370,10 @@ class _PolicyState extends State<Policy> {
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Divider(
|
||||
thickness: 0.2,
|
||||
color: Colors.grey,
|
||||
),
|
||||
isDesktop
|
||||
? Expanded(
|
||||
child: Row(
|
||||
@ -242,7 +390,7 @@ class _PolicyState extends State<Policy> {
|
||||
_buildPolicyCategory(isDesktop),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -290,9 +438,9 @@ class _PolicyState extends State<Policy> {
|
||||
child: Flex(
|
||||
direction: isDesktop ? Axis.vertical : Axis.horizontal,
|
||||
children: services.asMap().entries.map((entry) {
|
||||
int index = entry.key;
|
||||
int index = entry.key + 1;
|
||||
String service = entry.value;
|
||||
bool isSelected = selectedServiceIndex == index;
|
||||
bool isSelected = selectedServiceIndex.value == index.toString();
|
||||
|
||||
return SizedBox(
|
||||
width: isDesktop ? 180 : null,
|
||||
@ -305,7 +453,7 @@ class _PolicyState extends State<Policy> {
|
||||
onTap: () {
|
||||
print("Selected Services - $service - $index");
|
||||
setState(() {
|
||||
selectedServiceIndex = index;
|
||||
selectedServiceIndex.value = index.toString();
|
||||
selectedService = service;
|
||||
|
||||
if (selectedService == "Flight" ||
|
||||
@ -322,15 +470,14 @@ class _PolicyState extends State<Policy> {
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(8),
|
||||
margin: EdgeInsets.all(5),
|
||||
padding: isDesktop
|
||||
? EdgeInsets.all(8)
|
||||
: EdgeInsets.only(top: 3, bottom: 3, left: 8, right: 8),
|
||||
decoration: BoxDecoration(
|
||||
// color: Colors.blue,
|
||||
color: isSelected
|
||||
? Colors.blueAccent.shade100
|
||||
: Colors.grey.shade100,
|
||||
color:
|
||||
isSelected ? Color(0xFF114D8B) : Colors.grey.shade100,
|
||||
// : Color(0xFFEBEBF7),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
@ -354,17 +501,220 @@ class _PolicyState extends State<Policy> {
|
||||
Widget _buildPolicyCategory(bool isDesktop) {
|
||||
return Expanded(
|
||||
child: Container(
|
||||
margin: EdgeInsets.all(10),
|
||||
// color: Colors.brown.shade100,
|
||||
color: Colors.white60,
|
||||
// color: Colors.white60,
|
||||
child: PolicyCriteria(
|
||||
isDesktop: isDesktop,
|
||||
isClass: showClass,
|
||||
isCost: showCost,
|
||||
selectedTab: selectedService)),
|
||||
key: policyCriteriaKey,
|
||||
isDesktop: isDesktop,
|
||||
isClass: showClass,
|
||||
isCost: showCost,
|
||||
selectedTabNotifier: selectedServiceIndex,
|
||||
selectedService: selectedService,
|
||||
userId: userId,
|
||||
onPolicyDataChanged: (List<Map<String, dynamic>>? policyData) {
|
||||
print("🟢 policyData received from child: $policyData");
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
policy_details = policyData;
|
||||
});
|
||||
});
|
||||
},
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildTripType(bool isDesktop) {
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
layoutColor: layoutColor,
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
width: 130,
|
||||
isFocused: _selectedTripType == "1",
|
||||
isDesktop: isDesktop,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Domestic",
|
||||
style: TextStyle(
|
||||
color: _selectedTripType == "1" ? Colors.white : Colors.black,
|
||||
fontWeight: _selectedTripType == "1" ? FontWeight.w600 : null,
|
||||
fontSize: 13),
|
||||
),
|
||||
|
||||
// Radio<String>(
|
||||
// activeColor: Colors.blueAccent,
|
||||
// // contentPadding: EdgeInsets.zero,
|
||||
// visualDensity: VisualDensity.compact,
|
||||
// // dense: true,
|
||||
// value: "1",
|
||||
// groupValue: _selectedTripType,
|
||||
// onChanged: widget.isViewMode
|
||||
// ? null
|
||||
// : (value) {
|
||||
// setState(() {
|
||||
// _selectedTripType = value!;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedTripType = "1";
|
||||
PolicyName = "Domestic Policy";
|
||||
SelectedDomestic = "1";
|
||||
SelectedInternational = "0";
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
// color: _selectedOption == option["value"]
|
||||
// ? Colors.blueAccent
|
||||
// : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(4), // Rounded rectangle
|
||||
border: Border.all(
|
||||
color:
|
||||
_selectedTripType == "1" ? Colors.white : Colors.black,
|
||||
width: _selectedTripType == "1" ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: _selectedTripType == "1"
|
||||
? Icon(Icons.rectangle, size: 8, color: Colors.white)
|
||||
: null, // Add checkmark if selected
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
layoutColor: layoutColor,
|
||||
borderRadius: BorderRadius.circular(25),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
width: 150,
|
||||
// padding: EdgeInsets.symmetric(horizontal: 5, vertical: 2),
|
||||
isFocused: _selectedTripType == "2",
|
||||
isDesktop: isDesktop,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"International",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: _selectedTripType == "2" ? Colors.white : Colors.black,
|
||||
fontWeight: _selectedTripType == "2" ? FontWeight.w600 : null,
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedTripType = "2";
|
||||
PolicyName = "International Policy";
|
||||
SelectedDomestic = "0";
|
||||
SelectedInternational = "1";
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 15,
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.rectangle,
|
||||
// color: _selectedOption == option["value"]
|
||||
// ? Colors.blueAccent
|
||||
// : Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(4), // Rounded rectangle
|
||||
border: Border.all(
|
||||
color:
|
||||
_selectedTripType == "2" ? Colors.white : Colors.black,
|
||||
width: _selectedTripType == "2" ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: _selectedTripType == "2"
|
||||
? Icon(Icons.rectangle, size: 8, color: Colors.white)
|
||||
: null, // Add checkmark if selected
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
// RadioListTile<String>(
|
||||
// activeColor: Colors.blueAccent,
|
||||
// contentPadding: EdgeInsets.zero,
|
||||
// dense: true,
|
||||
// title: Text("International"),
|
||||
// value: "2",
|
||||
// groupValue: _selectedTripType,
|
||||
// onChanged: widget.isViewMode
|
||||
// ? null
|
||||
// : (value) {
|
||||
// setState(() {
|
||||
// _selectedTripType = value!;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildSubmit(isDesktop) {
|
||||
return [
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor ?? Colors.grey, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () {
|
||||
context.go('/PolicyList');
|
||||
},
|
||||
child: Text("Cancel")),
|
||||
SizedBox(
|
||||
width: 20,
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: isViewMode
|
||||
? SystemMouseCursors.forbidden
|
||||
: SystemMouseCursors.click,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor:
|
||||
isViewMode ? layoutColor : layoutColor, // Keep original color
|
||||
foregroundColor:
|
||||
isViewMode ? Colors.white : Colors.white, // Keep original color
|
||||
disabledBackgroundColor:
|
||||
layoutColor, // Ensure color remains when disabled
|
||||
disabledForegroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: BorderSide(color: layoutColor ?? Colors.grey, width: 2),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed:
|
||||
isViewMode ? null : handleSubmit, // Disable when in view mode
|
||||
child: Text("Submit"),
|
||||
),
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
List<Widget> _buildTripType1(bool isDesktop) {
|
||||
return [
|
||||
CustomTextFieldWrapper(
|
||||
color: Color(0xFFF4F4FB),
|
||||
@ -387,6 +737,9 @@ class _PolicyState extends State<Policy> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
PolicyName = "Domestic Policy";
|
||||
SelectedDomestic = "1";
|
||||
SelectedInternational = "0";
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -415,6 +768,9 @@ class _PolicyState extends State<Policy> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_selectedTripType = value!;
|
||||
PolicyName = "International Policy";
|
||||
SelectedDomestic = "0";
|
||||
SelectedInternational = "1";
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@ -6,37 +6,154 @@ import '../../widgets/custom_user_form.dart';
|
||||
|
||||
class PolicyCriteria extends StatefulWidget {
|
||||
bool isDesktop;
|
||||
String selectedTab;
|
||||
String selectedService;
|
||||
final ValueNotifier<String> selectedTabNotifier;
|
||||
String? userId;
|
||||
bool isClass = true;
|
||||
bool isCost = true;
|
||||
|
||||
final Function(List<Map<String, dynamic>>?) onPolicyDataChanged;
|
||||
|
||||
PolicyCriteria(
|
||||
{super.key,
|
||||
required this.isDesktop,
|
||||
required this.selectedTab,
|
||||
required this.selectedService,
|
||||
required this.selectedTabNotifier,
|
||||
required this.userId,
|
||||
required this.isClass,
|
||||
required this.isCost});
|
||||
required this.isCost,
|
||||
required this.onPolicyDataChanged});
|
||||
@override
|
||||
_PolicyCriteriaState createState() => _PolicyCriteriaState();
|
||||
PolicyCriteriaState createState() => PolicyCriteriaState();
|
||||
}
|
||||
|
||||
class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
String? selectedFirstApprovarType;
|
||||
String? selectedService;
|
||||
String? _selectedTripType;
|
||||
class PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
String? ServiceId = "1";
|
||||
|
||||
// final TextEditingController _costController = TextEditingController();
|
||||
// final TextEditingController _classController = TextEditingController();
|
||||
// String? FirstApproverAction;
|
||||
// String? SecondApproverAction;
|
||||
// String? ThirdApproverAction;
|
||||
// String? SelectedParallelProcess = "3";
|
||||
|
||||
Map<String, TextEditingController> costController = {};
|
||||
Map<String, TextEditingController> classController = {};
|
||||
Map<String, String?> FirstApproverAction = {};
|
||||
Map<String, String?> SecondApproverAction = {};
|
||||
Map<String, String?> ThirdApproverAction = {};
|
||||
Map<String, String?> SelectedParallelProcess = {};
|
||||
|
||||
Map<String, String> validationErrors = {};
|
||||
|
||||
// bool isClass = true;
|
||||
// bool isCost = true;
|
||||
|
||||
List<Map<String, dynamic>>? policyData = [];
|
||||
|
||||
// Map<String, dynamic> get policyServices {
|
||||
// Map<String, dynamic> data = {
|
||||
// "service_id": ServiceId,
|
||||
// "cost": costController[ServiceId]?.text,
|
||||
// "class": classController[ServiceId]?.text,
|
||||
// "a1_action": FirstApproverAction[ServiceId],
|
||||
// "a2_action": SecondApproverAction[ServiceId],
|
||||
// "a3_action": ThirdApproverAction[ServiceId],
|
||||
// "parallel_process_from": SelectedParallelProcess[ServiceId],
|
||||
// "created_by": widget.userId
|
||||
// };
|
||||
// return data;
|
||||
// }
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
fieldForPolicy();
|
||||
// selectedService = widget.selectedTab;
|
||||
widget.selectedTabNotifier.addListener(() {
|
||||
print("selectedTab changed: ${widget.selectedTabNotifier.value}");
|
||||
fieldForPolicy();
|
||||
});
|
||||
}
|
||||
|
||||
void saveCurrentPolicy() {
|
||||
if (ServiceId != null) {
|
||||
addOrUpdatePolicy(ServiceId!);
|
||||
}
|
||||
}
|
||||
|
||||
void addOrUpdatePolicy(String serviceId) {
|
||||
Map<String, dynamic> data = {
|
||||
"service_id": serviceId,
|
||||
"cost": costController[serviceId]?.text,
|
||||
"class": classController[serviceId]?.text,
|
||||
"a1_action": FirstApproverAction[serviceId],
|
||||
"a2_action": SecondApproverAction[serviceId],
|
||||
"a3_action": ThirdApproverAction[serviceId],
|
||||
"parallel_process_from": SelectedParallelProcess[serviceId],
|
||||
"created_by": widget.userId
|
||||
};
|
||||
|
||||
final cost = costController[serviceId]?.text ?? "";
|
||||
final travelClass = classController[serviceId]?.text ?? "";
|
||||
final a1 = FirstApproverAction[serviceId];
|
||||
final a2 = SecondApproverAction[serviceId];
|
||||
final a3 = ThirdApproverAction[serviceId];
|
||||
|
||||
bool hasValue = cost.trim().isNotEmpty || travelClass.trim().isNotEmpty;
|
||||
bool allActionsNull = a1 == null && a2 == null && a3 == null;
|
||||
bool someActionsMissing = [a1, a2, a3].where((a) => a != null).length > 0 &&
|
||||
[a1, a2, a3].where((a) => a == null).length > 0;
|
||||
|
||||
if (someActionsMissing) {
|
||||
validationErrors[serviceId] = "All 3 approver actions must be selected.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasValue && allActionsNull) {
|
||||
validationErrors[serviceId] = "Please select all actions.";
|
||||
return;
|
||||
}
|
||||
|
||||
validationErrors.remove(serviceId);
|
||||
|
||||
int index =
|
||||
policyData!.indexWhere((item) => item["service_id"] == serviceId);
|
||||
if (index != -1) {
|
||||
policyData![index] = data; // Replace existing entry
|
||||
print("🔁 Updated policy for ServiceId: $serviceId");
|
||||
} else {
|
||||
policyData!.add(data); // Add new entry
|
||||
print("➕ Added policy for ServiceId: $serviceId");
|
||||
}
|
||||
|
||||
print("📋 policyData - $policyData");
|
||||
}
|
||||
|
||||
void fieldForPolicy() {
|
||||
print("fieldForPolicy - ${widget.selectedTab}");
|
||||
print("fieldForPolicy - ${widget.selectedTabNotifier.value}");
|
||||
|
||||
// Save current input to policyData before switching
|
||||
if (ServiceId != null) {
|
||||
addOrUpdatePolicy(
|
||||
ServiceId!); // 👈 Save current values for existing service
|
||||
}
|
||||
setState(() {
|
||||
ServiceId = widget.selectedTabNotifier.value ?? "1";
|
||||
// Initialize controllers and variables if not present
|
||||
costController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||
classController.putIfAbsent(ServiceId!, () => TextEditingController());
|
||||
|
||||
FirstApproverAction.putIfAbsent(ServiceId!, () => null);
|
||||
SecondApproverAction.putIfAbsent(ServiceId!, () => null);
|
||||
ThirdApproverAction.putIfAbsent(ServiceId!, () => null);
|
||||
SelectedParallelProcess.putIfAbsent(ServiceId!, () => "3");
|
||||
});
|
||||
|
||||
widget.onPolicyDataChanged(policyData);
|
||||
}
|
||||
|
||||
void printData() {
|
||||
// print("policyServices - $policyServices");
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
@ -49,7 +166,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
" Policy Criteria For ${widget.selectedTab} ",
|
||||
" Policy Criteria For ${widget.selectedService} ",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF9E9DBD),
|
||||
@ -59,102 +176,53 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
),
|
||||
if (widget.isClass!)
|
||||
widget.isDesktop ? SizedBox(height: 15) : SizedBox(height: 5),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color: Colors.grey,
|
||||
padding:
|
||||
const EdgeInsets.only(top: 5, bottom: 5, left: 5, right: 5),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.isClass!)
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Class",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
// controller: controllers["Fname"],
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Class",
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior:
|
||||
FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
if (widget.isCost!)
|
||||
Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Cost",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
// controller: controllers["Fname"],
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Cost",
|
||||
labelStyle: TextStyle(
|
||||
fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior:
|
||||
FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding:
|
||||
EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 18.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
// color: Colors.grey,
|
||||
padding: const EdgeInsets.only(
|
||||
top: 5, bottom: 5, left: 5, right: 5),
|
||||
child: widget.isDesktop
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
if (widget.isClass!)
|
||||
buildClassWidget(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
if (widget.isCost!)
|
||||
buildCostWidget(widget.isDesktop),
|
||||
],
|
||||
)
|
||||
: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (widget.isClass!)
|
||||
buildClassWidget(widget.isDesktop),
|
||||
SizedBox(height: 10),
|
||||
if (widget.isCost!)
|
||||
buildCostWidget(widget.isDesktop),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.isCost!) SizedBox(height: 15),
|
||||
if (validationErrors[ServiceId] != null)
|
||||
Row(
|
||||
children: [
|
||||
Text(validationErrors[ServiceId]!,
|
||||
style: TextStyle(
|
||||
color: Colors.red,
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold))
|
||||
],
|
||||
),
|
||||
if (validationErrors[ServiceId] != null) SizedBox(height: 15),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -163,7 +231,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
// color: Color(0xFFEBEBF7),
|
||||
// ),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// color: Colors.brown.shade200,
|
||||
color: Colors.brown.shade200,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
@ -241,7 +309,8 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem:
|
||||
selectedFirstApprovarType,
|
||||
FirstApproverAction[
|
||||
ServiceId],
|
||||
// enabled: !isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
// showSearchBox: true,
|
||||
@ -251,7 +320,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
maxHeight: 250),
|
||||
),
|
||||
items: [
|
||||
"Approve",
|
||||
"Approval",
|
||||
"Notification",
|
||||
],
|
||||
dropdownDecoratorProps:
|
||||
@ -275,15 +344,16 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
selectedItem ?? "Select",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors
|
||||
.blueAccent),
|
||||
color: Color(
|
||||
0xFF114D8B)),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
(String? newValue) {
|
||||
setState(() {
|
||||
// Find the country_code based on selected country_name
|
||||
selectedFirstApprovarType =
|
||||
FirstApproverAction[
|
||||
ServiceId!] =
|
||||
newValue;
|
||||
|
||||
// print("selectedUserType - $selectedUserType");
|
||||
@ -296,21 +366,41 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.grey,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
// color: Colors.green,
|
||||
color: Colors.grey,
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
SelectedParallelProcess[
|
||||
ServiceId!] = "1";
|
||||
});
|
||||
|
||||
print(
|
||||
"SelectedParallelProcess - $SelectedParallelProcess");
|
||||
},
|
||||
child: Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: (SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"1")
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
color:
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"1")
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
// color: Colors.grey,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -328,7 +418,8 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem:
|
||||
selectedFirstApprovarType,
|
||||
SecondApproverAction[
|
||||
ServiceId],
|
||||
// enabled: !isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
// showSearchBox: true,
|
||||
@ -338,7 +429,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
maxHeight: 250),
|
||||
),
|
||||
items: [
|
||||
"Approve",
|
||||
"Approval",
|
||||
"Notification",
|
||||
],
|
||||
dropdownDecoratorProps:
|
||||
@ -362,15 +453,16 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
selectedItem ?? "Select",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors
|
||||
.blueAccent),
|
||||
color: Color(
|
||||
0xFF114D8B)),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
(String? newValue) {
|
||||
setState(() {
|
||||
// Find the country_code based on selected country_name
|
||||
selectedFirstApprovarType =
|
||||
SecondApproverAction[
|
||||
ServiceId!] =
|
||||
newValue;
|
||||
|
||||
// print("selectedUserType - $selectedUserType");
|
||||
@ -383,21 +475,46 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.green,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
color: Colors.green,
|
||||
// color: Colors.grey,
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
SelectedParallelProcess[
|
||||
ServiceId!] = "2";
|
||||
});
|
||||
|
||||
print(
|
||||
"SelectedParallelProcess - $SelectedParallelProcess");
|
||||
},
|
||||
child: Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ((SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"1") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"2"))
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
color: ((SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"1") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"2"))
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
// color: Colors.grey,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -415,7 +532,8 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
height: 40,
|
||||
child: DropdownSearch<String>(
|
||||
selectedItem:
|
||||
selectedFirstApprovarType,
|
||||
ThirdApproverAction[
|
||||
ServiceId],
|
||||
// enabled: !isViewMode,
|
||||
popupProps: PopupProps.menu(
|
||||
// showSearchBox: true,
|
||||
@ -425,7 +543,7 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
maxHeight: 250),
|
||||
),
|
||||
items: [
|
||||
"Approve",
|
||||
"Approval",
|
||||
"Notification",
|
||||
],
|
||||
dropdownDecoratorProps:
|
||||
@ -449,15 +567,16 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
selectedItem ?? "Select",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors
|
||||
.blueAccent),
|
||||
color: Color(
|
||||
0xFF114D8B)),
|
||||
),
|
||||
),
|
||||
onChanged:
|
||||
(String? newValue) {
|
||||
setState(() {
|
||||
// Find the country_code based on selected country_name
|
||||
selectedFirstApprovarType =
|
||||
ThirdApproverAction[
|
||||
ServiceId!] =
|
||||
newValue;
|
||||
|
||||
// print("selectedUserType - $selectedUserType");
|
||||
@ -470,21 +589,47 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Colors.green,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
color: Colors.green,
|
||||
// color: Colors.grey,
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
SelectedParallelProcess[
|
||||
ServiceId!] = "3";
|
||||
});
|
||||
print(
|
||||
"SelectedParallelProcess - $SelectedParallelProcess[ServiceId]");
|
||||
},
|
||||
child: Container(
|
||||
height: 25,
|
||||
width: 25,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ((SelectedParallelProcess[ServiceId] == "1") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"2") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"3"))
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
width: 2),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
size: 20,
|
||||
color: ((SelectedParallelProcess[ServiceId] == "1") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"2") ||
|
||||
(SelectedParallelProcess[
|
||||
ServiceId] ==
|
||||
"3"))
|
||||
? Colors.green
|
||||
: Colors.grey,
|
||||
// color: Colors.grey,
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -504,4 +649,82 @@ class _PolicyCriteriaState extends State<PolicyCriteria> {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildClassWidget(isDesktop) {
|
||||
return Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Class",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
controller: classController[ServiceId],
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Class",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildCostWidget(isDesktop) {
|
||||
return Row(
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("Cost",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w200,
|
||||
color: Colors.black)),
|
||||
SizedBox(height: 5),
|
||||
CustomTextFieldUserWrapper(
|
||||
isFocused: false,
|
||||
isDesktop: widget.isDesktop,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: TextField(
|
||||
style: TextStyle(fontSize: 12),
|
||||
controller: costController[ServiceId],
|
||||
// enabled: !isViewMode,
|
||||
onChanged: (value) {
|
||||
printData();
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
labelText: "Cost",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
border: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
235
lib/Screens/policy/policy_list.dart
Normal file
235
lib/Screens/policy/policy_list.dart
Normal file
@ -0,0 +1,235 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/Screens/group/group.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
import '../../services/apiService.dart';
|
||||
import '../../utils/auth_utils.dart';
|
||||
|
||||
class PolicyList extends StatefulWidget {
|
||||
@override
|
||||
_PolicyListState createState() => _PolicyListState();
|
||||
}
|
||||
|
||||
class _PolicyListState extends State<PolicyList> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
List<dynamic>? apiAllGroups;
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
loadAllGroups();
|
||||
loadInitialData();
|
||||
});
|
||||
}
|
||||
|
||||
void loadInitialData() async {
|
||||
String? layoutString = await getLayoutColor();
|
||||
String? bodyStringColor = await getBodyColor();
|
||||
|
||||
setState(() {
|
||||
layoutColor = layoutString != null
|
||||
? Color(int.parse(layoutString))
|
||||
: Colors.redAccent;
|
||||
|
||||
bodyColor = bodyStringColor != null
|
||||
? Color(int.parse(bodyStringColor))
|
||||
: Colors.white;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> loadAllGroups() async {
|
||||
try {
|
||||
final result = await apiService.fetchAllPolicy();
|
||||
setState(() {
|
||||
apiAllGroups = result;
|
||||
});
|
||||
print("Fetched services: $apiAllGroups");
|
||||
} catch (e) {
|
||||
print('Error fetching role list: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void deleteGroup(int groupId) {
|
||||
setState(() {
|
||||
apiAllGroups?.removeWhere((group) => group['group_id'] == groupId);
|
||||
});
|
||||
}
|
||||
|
||||
// Future<void> deleteGroupFromApi(int groupId) async {
|
||||
// try {
|
||||
// await apiService.deleteGroup(groupId); // your delete API call
|
||||
// deleteGroup(groupId); // remove from UI list
|
||||
// } catch (e) {
|
||||
// print('Error deleting group: $e');
|
||||
// }
|
||||
// }'
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
appBar: isDesktop ? null : const CustomAppBar(title: 'Home'),
|
||||
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
body: Row(
|
||||
children: [
|
||||
if (isDesktop) CustomDrawer(isDesktop: true),
|
||||
Expanded(child: buildGroupListLayout(isDesktop))
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildGroupListLayout(bool isDesktop) {
|
||||
return Container(
|
||||
color: bodyColor,
|
||||
// color: Colors.white,
|
||||
width: double.infinity,
|
||||
height: MediaQuery.of(context).size.height,
|
||||
// margin: const EdgeInsets.all(8),
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Text('Policy List',
|
||||
style:
|
||||
TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.keyboard_arrow_down),
|
||||
onPressed: () {},
|
||||
),
|
||||
],
|
||||
),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
foregroundColor: Colors.white,
|
||||
backgroundColor: layoutColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
// side: BorderSide(color: , width: 1),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
onPressed: () async {
|
||||
// List<dynamic> users = await futureUsers;
|
||||
context.go('/Policy');
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Text('New Policy'),
|
||||
SizedBox(
|
||||
width: 5,
|
||||
),
|
||||
Icon(
|
||||
Icons.add_circle_outline_rounded,
|
||||
color: Colors.white,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).size.height * 0.899,
|
||||
padding: const EdgeInsets.all(10),
|
||||
// margin: const EdgeInsets.only(bottom: 10),
|
||||
color: Colors.white,
|
||||
// color: Colors.red.shade100,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Column(
|
||||
children: [
|
||||
buildGroupListView(isDesktop),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Widget buildGroupListView(bool isDesktop) {
|
||||
// return Container(
|
||||
// child: Text("DAta"),
|
||||
// );
|
||||
// }
|
||||
|
||||
Widget buildGroupListView(bool isDesktop) {
|
||||
if (apiAllGroups == null || apiAllGroups!.isEmpty) {
|
||||
return Center(child: Text("No groups found."));
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: apiAllGroups!.length,
|
||||
itemBuilder: (context, index) {
|
||||
final group = apiAllGroups![index];
|
||||
return Card(
|
||||
// color: bodyColor,
|
||||
color: Color(0xFFF5F5F5),
|
||||
margin: EdgeInsets.symmetric(vertical: 6, horizontal: 10),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text("Group Name: ${group['name']}",
|
||||
style: TextStyle(
|
||||
fontSize: 13, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
Expanded(flex: 1, child: Text(" ${group['created_on']}")),
|
||||
Expanded(flex: 1, child: Text("${group['created_by']}")),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go("/CreateGroup", extra: group);
|
||||
|
||||
print("Edit ${group['group_id']} $group");
|
||||
},
|
||||
child: Text("Edit"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,11 @@ class Plan {
|
||||
final String description;
|
||||
final String isBillable;
|
||||
|
||||
final String userName;
|
||||
final String travellerName;
|
||||
final String statusValue;
|
||||
final String createdOn;
|
||||
|
||||
Plan({
|
||||
required this.planId,
|
||||
required this.tripTitle,
|
||||
@ -21,6 +26,10 @@ class Plan {
|
||||
required this.soNumber,
|
||||
required this.description,
|
||||
required this.isBillable,
|
||||
required this.userName,
|
||||
required this.travellerName,
|
||||
required this.statusValue,
|
||||
required this.createdOn,
|
||||
});
|
||||
|
||||
factory Plan.fromJson(Map<String, dynamic> json) {
|
||||
@ -35,6 +44,10 @@ class Plan {
|
||||
soNumber: json['so_number'] ?? '',
|
||||
description: json['description'] ?? '',
|
||||
isBillable: json['is_billable_value'] ?? '',
|
||||
userName: json["user_name"] ?? '',
|
||||
travellerName: json["traveller_name"] ?? '',
|
||||
statusValue: json["status_value"] ?? '',
|
||||
createdOn: json["created_on"] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../services/apiService.dart';
|
||||
|
||||
class CustomDrawer extends StatefulWidget {
|
||||
final bool isDesktop;
|
||||
const CustomDrawer({super.key, required this.isDesktop});
|
||||
@ -14,15 +16,25 @@ class CustomDrawer extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CustomDrawerState extends State<CustomDrawer> {
|
||||
final ApiService apiService = ApiService();
|
||||
|
||||
String? token;
|
||||
Map<String, dynamic>? userData;
|
||||
Map<String, dynamic>? fetchedUserData;
|
||||
Map<String, dynamic> userDetails = {};
|
||||
|
||||
Map<String, dynamic>? selectedOrg;
|
||||
Color? layoutColor;
|
||||
Color? bodyColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeData();
|
||||
// initializeData();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
initializeData();
|
||||
getOrganizationData();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> initializeData() async {
|
||||
@ -66,92 +78,259 @@ class _CustomDrawerState extends State<CustomDrawer> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> getOrganizationData() async {
|
||||
try {
|
||||
print("getUpdatedServices");
|
||||
|
||||
final result = await apiService.fetchOrganization();
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
print("UUPdatedServices - $result");
|
||||
|
||||
setState(() {
|
||||
selectedOrg = result;
|
||||
|
||||
layoutColor = selectedOrg?['layout_color'] != null
|
||||
? Color(int.parse(selectedOrg!['layout_color']))
|
||||
: Colors.white;
|
||||
|
||||
bodyColor = selectedOrg?['color'] != null
|
||||
? Color(int.parse(
|
||||
selectedOrg!['color'].toString().replaceFirst('0x', ''),
|
||||
radix: 16))
|
||||
: Colors.blue;
|
||||
});
|
||||
|
||||
// Save to SharedPreferences
|
||||
await prefs.setString('layout_color', selectedOrg?['layout_color']);
|
||||
await prefs.setString('body_color', selectedOrg?['color']);
|
||||
|
||||
print(
|
||||
"Layout Color- ${selectedOrg?['layout_color']} - $layoutColor ---------- bodyColor - $bodyColor");
|
||||
} catch (e) {
|
||||
print("Error : $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget drawerContent = Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print("ONTAP Custom");
|
||||
print("ONTAP Custom- $userDetails ");
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
"selectedUser": userDetails,
|
||||
"isEditProfile": true,
|
||||
"isViewMode": true
|
||||
},
|
||||
);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 80,
|
||||
child: Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
padding: EdgeInsets.all(16),
|
||||
width: double.infinity,
|
||||
child: Row(
|
||||
color: Colors.white,
|
||||
// color: Color(0xFFF3F3FA),
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(18),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.only(left: 20),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueAccent, shape: BoxShape.circle),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"]?.isNotEmpty == true
|
||||
? userData!["name"]![0].toUpperCase()
|
||||
: "N/A",
|
||||
style: TextStyle(
|
||||
color: Colors.white, fontSize: 25),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/images/login/travelSpend_Logo.png',
|
||||
width: 160,
|
||||
height: 40,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style:
|
||||
TextStyle(color: Colors.black87, fontSize: 11),
|
||||
),
|
||||
Text(
|
||||
userData?["email"] ?? "N/A",
|
||||
style:
|
||||
TextStyle(color: Colors.black45, fontSize: 10),
|
||||
"Welcome,",
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: "Archivo",
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
print("ONTAP Custom");
|
||||
print("ONTAP Custom- $userDetails ");
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
"selectedUser": userDetails,
|
||||
"isEditProfile": true,
|
||||
"isViewMode": true
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: "Archivo",
|
||||
// color: Color(0xFF12B24B),
|
||||
color: layoutColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)),
|
||||
),
|
||||
|
||||
// GestureDetector(
|
||||
// onTap: () {
|
||||
// print("ONTAP Custom");
|
||||
// print("ONTAP Custom- $userDetails ");
|
||||
// context.go(
|
||||
// "/CreateUserDetails",
|
||||
// extra: {
|
||||
// "selectedUser": userDetails,
|
||||
// "isEditProfile": true,
|
||||
// "isViewMode": true
|
||||
// },
|
||||
// );
|
||||
// },
|
||||
// child: SizedBox(
|
||||
// height: 80,
|
||||
// child: Container(
|
||||
// color: Color(0xFFF3F3FA),
|
||||
// padding: EdgeInsets.all(16),
|
||||
// // width: double.infinity,
|
||||
// child: Row(
|
||||
// // mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// Column(
|
||||
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: [
|
||||
// Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// "Welcome",
|
||||
// style: TextStyle(
|
||||
// color: Colors.black,
|
||||
// fontSize: 13,
|
||||
// fontWeight: FontWeight.w400,
|
||||
// fontFamily: "Archivo"),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// Column(
|
||||
// children: [
|
||||
// Text(
|
||||
// userData?["name"] ?? "N/A",
|
||||
// style: TextStyle(
|
||||
// color: Color(0xFF12B24B),
|
||||
// fontSize: 13,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// fontFamily: "Archivo"),
|
||||
// ),
|
||||
// ],
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ],
|
||||
// )),
|
||||
// ),
|
||||
// ),
|
||||
SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Components",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Archivo",
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildDrawerItem(context, Icons.home, 'Home', '/home'),
|
||||
_buildExpandableItem(context, Icons.assessment, 'Plans', [
|
||||
_buildSubDrawerItem(context, 'My Travel Request', '/listPlan'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildExpandableItem(
|
||||
context, Icons.account_circle_outlined, 'User ', [
|
||||
_buildSubDrawerItem(context, 'User List', '/listUser'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildExpandableItem(context, Icons.policy, 'Settings ', [
|
||||
_buildSubDrawerItem(context, 'Organization', '/OrganizationSetup'),
|
||||
_buildSubDrawerItem(context, 'Group', '/group'),
|
||||
_buildSubDrawerItem(context, 'Policy', '/Policy'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildDrawerItem(context, Icons.logout, 'Logout', '/')
|
||||
],
|
||||
_buildDrawerItem(context, Icons.home_outlined, 'Home', '/home'),
|
||||
_buildExpandableItem(
|
||||
context,
|
||||
Icons.assessment_outlined,
|
||||
'Plans',
|
||||
[
|
||||
_buildSubDrawerItem(
|
||||
context, 'My Travel Request', '/listPlan'),
|
||||
_buildSubDrawerItem(context, 'My Approvals', '/ApprovalList')
|
||||
],
|
||||
'/listPlan'),
|
||||
_buildExpandableItem(
|
||||
context,
|
||||
Icons.account_circle_outlined,
|
||||
'User ',
|
||||
[
|
||||
_buildSubDrawerItem(context, 'User List', '/listUser'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
],
|
||||
'/listUser'),
|
||||
_buildExpandableItem(
|
||||
context,
|
||||
Icons.settings_outlined,
|
||||
'Settings ',
|
||||
[
|
||||
_buildSubDrawerItem(
|
||||
context, 'Organization', '/OrganizationSetup'),
|
||||
_buildSubDrawerItem(context, 'Group', '/group'),
|
||||
_buildSubDrawerItem(context, 'Policy', '/PolicyList'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
],
|
||||
'/OrganizationSetup',
|
||||
),
|
||||
_buildDrawerItem(context, Icons.login_outlined, 'Logout', '/'),
|
||||
if (widget.isDesktop) Spacer(),
|
||||
Container(
|
||||
margin: const EdgeInsets.all(20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Powered by",
|
||||
style:
|
||||
TextStyle(fontSize: 11, color: Color(0xFF212121)),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/images/login/travelSpend_Logo.png',
|
||||
width: 90,
|
||||
height: 25,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@ -172,35 +351,193 @@ class _CustomDrawerState extends State<CustomDrawer> {
|
||||
/// **Reusable Drawer Item**
|
||||
Widget _buildDrawerItem(
|
||||
BuildContext context, IconData icon, String title, String route) {
|
||||
return ListTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
onTap: () async {
|
||||
if (route == '/') {
|
||||
// Handle logout separately
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.clear(); // Clear stored token or session data
|
||||
context.go("/"); // Redirect to login instead of home
|
||||
} else {
|
||||
context.go(route);
|
||||
}
|
||||
});
|
||||
String selectedRoute = GoRouterState.of(context).uri.toString();
|
||||
|
||||
// return Container(
|
||||
// color: selectedRoute == route ? Colors.blue.shade50 : null,
|
||||
// child: InkWell(
|
||||
// onTap: () async {
|
||||
// if (route == '/') {
|
||||
// final pref = await SharedPreferences.getInstance();
|
||||
// await pref.clear();
|
||||
// context.go("/");
|
||||
// } else {
|
||||
// context.go(route);
|
||||
// }
|
||||
// },
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12),
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Icon(
|
||||
// icon,
|
||||
// size: 20,
|
||||
// color: Color(0xFF475569),
|
||||
// ),
|
||||
// // SizedBox(width: 9), // Reduce or increase this for spacing
|
||||
// Text(
|
||||
// title,
|
||||
// style: TextStyle(
|
||||
// fontSize: 14,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Color(0xFF475569),
|
||||
// fontFamily: "Archivo",
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
|
||||
return Material(
|
||||
color: selectedRoute == route ? bodyColor : Colors.transparent,
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF475569),
|
||||
fontFamily: "Archivo"),
|
||||
),
|
||||
// tileColor: selectedRoute == route ? Colors.blue.shade50 : null,
|
||||
|
||||
onTap: () async {
|
||||
if (route == '/') {
|
||||
// Handle logout separately
|
||||
final pref = await SharedPreferences.getInstance();
|
||||
await pref.clear(); // Clear stored token or session data
|
||||
context.go("/"); // Redirect to login instead of home
|
||||
} else {
|
||||
context.go(route);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExpandableItem(BuildContext context, IconData icon, String title,
|
||||
List<Widget> children) {
|
||||
return ExpansionTile(
|
||||
leading: Icon(icon),
|
||||
title: Text(title),
|
||||
shape: const Border(), // Removes top and bottom dividers
|
||||
childrenPadding: const EdgeInsets.only(left: 40), // Indent sub-items
|
||||
children: children,
|
||||
Widget _buildExpandableItem(
|
||||
BuildContext context,
|
||||
IconData icon,
|
||||
String title,
|
||||
List<Widget> children,
|
||||
String routeToMatch,
|
||||
) {
|
||||
String selectedRoute = GoRouterState.of(context).uri.toString();
|
||||
return Theme(
|
||||
data: Theme.of(context).copyWith(
|
||||
dividerColor:
|
||||
Colors.transparent, // Removes default ExpansionTile divider
|
||||
),
|
||||
child: Material(
|
||||
color: selectedRoute == routeToMatch ? bodyColor : Colors.transparent,
|
||||
child: ExpansionTile(
|
||||
tilePadding: EdgeInsets.symmetric(horizontal: 16),
|
||||
// childrenPadding: EdgeInsets.only(left: 36),
|
||||
leading: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: Color(0xFF475569),
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
// You could manually build this instead of using `leading`, but it's simpler here
|
||||
// SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF475569),
|
||||
fontFamily: "Archivo",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
collapsedBackgroundColor: Colors.transparent,
|
||||
shape: const Border(),
|
||||
children: children,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubDrawerItem(BuildContext context, String title, String route) {
|
||||
String selectedRoute = GoRouterState.of(context).uri.toString();
|
||||
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
context.go(route);
|
||||
if (!widget.isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 44.0, vertical: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.circle_rounded,
|
||||
color: Color(0xFF475569),
|
||||
size: 6,
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: selectedRoute == route ? Colors.blue : Color(0xFF475569),
|
||||
fontFamily: "Archivo",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildExpandableItem1(BuildContext context, IconData icon,
|
||||
String title, List<Widget> children) {
|
||||
return ExpansionTile(
|
||||
leading: Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF475569),
|
||||
fontFamily: "Archivo"),
|
||||
),
|
||||
collapsedBackgroundColor: Colors.transparent,
|
||||
shape: const Border(), // Removes top and bottom dividers
|
||||
// childrenPadding: const EdgeInsets.only(left: 40), // Indent sub-items
|
||||
childrenPadding: EdgeInsets.only(left: 24),
|
||||
children: children,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSubDrawerItem1(
|
||||
BuildContext context, String title, String route) {
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
leading: Icon(
|
||||
Icons.circle_rounded,
|
||||
color: Color(0xFF475569),
|
||||
size: 8,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Color(0xFF475569),
|
||||
fontFamily: "Archivo"),
|
||||
),
|
||||
onTap: () {
|
||||
context.go(route);
|
||||
if (!widget.isDesktop) Navigator.pop(context);
|
||||
|
||||
@ -5,13 +5,16 @@ import 'package:frontend/Screens/authentication/login/login_page.dart';
|
||||
import 'package:frontend/Screens/authentication/loginPage1.dart';
|
||||
import 'package:frontend/Screens/dashboard/home_page.dart';
|
||||
import 'package:frontend/Screens/organization/orgSetup.dart';
|
||||
import 'package:frontend/Screens/organization/org_List.dart';
|
||||
import 'package:frontend/Screens/plans/create_plans.dart';
|
||||
import 'package:frontend/Screens/plans/list_plans.dart';
|
||||
import 'package:frontend/Screens/policy/policy.dart';
|
||||
import 'package:frontend/Screens/policy/policy_list.dart';
|
||||
import 'package:frontend/Screens/userManagement/create_user/create_user.dart';
|
||||
import 'package:frontend/Screens/userManagement/user_List.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../Screens/approvals/approval_list.dart';
|
||||
import '../Screens/group/group.dart';
|
||||
import '../Screens/group/groupList.dart';
|
||||
|
||||
@ -62,6 +65,10 @@ final GoRouter router = GoRouter(
|
||||
path: '/Policy',
|
||||
builder: (context, state) => Policy(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/PolicyList',
|
||||
builder: (context, state) => PolicyList(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/OrganizationSetup',
|
||||
builder: (context, state) => OrgSetUp(),
|
||||
@ -70,6 +77,10 @@ final GoRouter router = GoRouter(
|
||||
path: '/group',
|
||||
builder: (context, state) => GroupList(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/approvallist',
|
||||
builder: (context, state) => ApprovalList(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/CreateGroup',
|
||||
pageBuilder: (context, state) => MaterialPage(
|
||||
|
||||
@ -265,9 +265,10 @@ class ApiService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchUpdatedOrganization() async {
|
||||
Future<Map<String, dynamic>> fetchOrganization() async {
|
||||
String? orgId = await getOrgId();
|
||||
|
||||
// final String apiUrldata = '$apiUrl/api/organizations';
|
||||
final String apiUrldata = '$apiUrl/api/organizations/find/$orgId';
|
||||
|
||||
final token = await getToken();
|
||||
@ -288,16 +289,87 @@ class ApiService {
|
||||
try {
|
||||
final data = json.decode(response.body);
|
||||
print(data);
|
||||
if (!data.containsKey('data') || data['data'] is! List) {
|
||||
if (!data.containsKey('data') || data['data'] is! Map) {
|
||||
throw Exception(
|
||||
"Invalid response format: 'data' field is missing or not a List");
|
||||
"Invalid response format: 'data' field is missing or not a Map");
|
||||
}
|
||||
return data['data'];
|
||||
|
||||
// Make sure each item is a Map<String, dynamic>
|
||||
// final List<Map<String, dynamic>> orgList =
|
||||
// List<Map<String, dynamic>>.from(data['data']);
|
||||
// return orgList;
|
||||
|
||||
return Map<String, dynamic>.from(data['data']);
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
throw Exception('Failed to load organizations');
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getViewPlan(
|
||||
String planId, List plansJson) async {
|
||||
try {
|
||||
final plan = plansJson.firstWhere(
|
||||
(item) => item["plan_id"].toString() == planId,
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
if (plan == null) {
|
||||
throw Exception("Plan with ID $planId not found.");
|
||||
}
|
||||
|
||||
return Map<String, dynamic>.from(plan);
|
||||
} catch (e) {
|
||||
throw Exception("Error finding plan: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> fetchUserApprovalList() async {
|
||||
String? orgId = await getOrgId();
|
||||
String? userId = await getUserId();
|
||||
|
||||
// final String apiUrldata = '$apiUrl/api/organizations';
|
||||
final String apiUrldata =
|
||||
'$apiUrl/api/plans/findApprovalList?user_id=$userId&org_id=$orgId';
|
||||
|
||||
// '$apiUrl/api/findApprovalList?user_id=$userId&org_id=$orgId';
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// Make sure each item is a Map<String, dynamic>
|
||||
// final List<Map<String, dynamic>> orgList =
|
||||
// List<Map<String, dynamic>>.from(data['data']);
|
||||
// return orgList;
|
||||
|
||||
return Map<String, dynamic>.from(data['data']);
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load organizations');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,16 @@ Future<String?> getToken() async {
|
||||
return prefs.getString("auth_token");
|
||||
}
|
||||
|
||||
Future<String?> getLayoutColor() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("layout_color");
|
||||
}
|
||||
|
||||
Future<String?> getBodyColor() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("body_color");
|
||||
}
|
||||
|
||||
Future<String?> getUserId() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
13
lib/utils/colorOpcity.dart
Normal file
13
lib/utils/colorOpcity.dart
Normal file
@ -0,0 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension ColorOpacityExtension on Color {
|
||||
Color withOpacitySafe(double opacity) {
|
||||
final int alpha = (opacity * 255).round().clamp(0, 255);
|
||||
return Color.fromARGB(
|
||||
alpha, // alpha value should be an int
|
||||
this.r.toInt(), // red channel
|
||||
this.g.toInt(), // green channel
|
||||
this.b.toInt(), // blue channel
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:frontend/utils/colorOpcity.dart';
|
||||
|
||||
class CustomTextFieldWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
@ -8,6 +9,8 @@ class CustomTextFieldWrapper extends StatefulWidget {
|
||||
final Color? color;
|
||||
final VoidCallback? onFocusChange; // Callback for focus handling
|
||||
final EdgeInsetsGeometry padding;
|
||||
final BorderRadius? borderRadius;
|
||||
final Color? layoutColor;
|
||||
|
||||
const CustomTextFieldWrapper({
|
||||
super.key,
|
||||
@ -18,6 +21,8 @@ class CustomTextFieldWrapper extends StatefulWidget {
|
||||
this.color = Colors.white,
|
||||
this.onFocusChange,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 12),
|
||||
this.borderRadius,
|
||||
this.layoutColor,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -25,6 +30,16 @@ class CustomTextFieldWrapper extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
|
||||
Color getColorWithOpacity(Color color, double opacity) {
|
||||
final int alpha = (opacity * 255).round().clamp(0, 255);
|
||||
return Color.fromARGB(
|
||||
alpha,
|
||||
color.r.toInt(),
|
||||
color.g.toInt(),
|
||||
color.b.toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -34,22 +49,30 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
|
||||
: MediaQuery.of(context).size.width * 0.85),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.color,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: widget.isFocused
|
||||
? (widget.layoutColor ?? widget.color)
|
||||
: widget.color,
|
||||
borderRadius: widget.borderRadius ?? BorderRadius.circular(8),
|
||||
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 2.0 : 0.5,
|
||||
color: widget.isFocused
|
||||
? (widget.layoutColor ?? Colors.blueAccent)
|
||||
: Color(0xFFF5F5F5),
|
||||
width: widget.isFocused ? 1.5 : 1.5,
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
// boxShadow: widget.isFocused
|
||||
// ? [
|
||||
// BoxShadow(
|
||||
// // color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
// // color: widget.isFocused
|
||||
// // ? (widget.layoutColor ?? Colors.red).withAlpha(204)
|
||||
// // : Color(0xFFF5F5F5),
|
||||
// blurRadius: 10,
|
||||
// spreadRadius: 1,
|
||||
// offset: Offset(0, 4),
|
||||
// ),
|
||||
// ]
|
||||
// : [],
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
|
||||
@ -21,10 +21,12 @@ class CustomTextFieldUserWrapper extends StatefulWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
_CustomTextFieldUserWrapperState createState() => _CustomTextFieldUserWrapperState();
|
||||
_CustomTextFieldUserWrapperState createState() =>
|
||||
_CustomTextFieldUserWrapperState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldUserWrapperState extends State<CustomTextFieldUserWrapper> {
|
||||
class _CustomTextFieldUserWrapperState
|
||||
extends State<CustomTextFieldUserWrapper> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
@ -35,22 +37,22 @@ class _CustomTextFieldUserWrapperState extends State<CustomTextFieldUserWrapper>
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
// color: widget.color,
|
||||
color: Color(0xFFF7F7FB),
|
||||
// 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),
|
||||
|
||||
),
|
||||
]
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: widget.child,
|
||||
|
||||
@ -47,6 +47,7 @@ dependencies:
|
||||
image_picker: ^1.1.2
|
||||
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
@ -68,6 +69,14 @@ flutter:
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/images/login/login_img1.png
|
||||
- assets/images/login/travelSpend_Logo.png
|
||||
- assets/images/login/TravelSpendsLogo1.png
|
||||
- assets/images/login/VectorG.png
|
||||
- assets/images/IconsImg/delete.png
|
||||
- assets/images/IconsImg/edit.png
|
||||
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user