User Profile Management
This commit is contained in:
parent
23e6c1608a
commit
7d684e86b3
@ -386,7 +386,6 @@ class _ForexScreenState extends State<ForexScreen> {
|
||||
print("CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount");
|
||||
|
||||
|
||||
|
||||
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
||||
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
||||
} else {
|
||||
|
||||
@ -154,8 +154,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void _clearError(String field) {
|
||||
if (mounted && errorMessages.containsKey(field)) {
|
||||
setState(() {
|
||||
@ -165,11 +163,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool isValidData(Map<String, dynamic> data) {
|
||||
errorMessages.clear(); // Reset errors
|
||||
|
||||
@ -327,8 +320,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
||||
Column(
|
||||
children: _buildTripType(isDesktop)
|
||||
)
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
if (isDesktop)
|
||||
|
||||
@ -410,10 +410,6 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
final String apiUrldata = '$apiUrl/api/getDropdownMaster';
|
||||
|
||||
final token = await getToken();
|
||||
final userId = "1";
|
||||
// final userId = await getUserId();
|
||||
|
||||
print("SUSRTRT- $userId");
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
@ -456,7 +452,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
// final userId = await getUserId();
|
||||
final userId = await getUserId();
|
||||
|
||||
// print("SUSRTRT- $userId");
|
||||
//
|
||||
@ -508,7 +504,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
// final userId = await getUserId();
|
||||
final userId = await getUserId();
|
||||
|
||||
// print("SUSRTRT- $userId");
|
||||
//
|
||||
@ -1270,7 +1266,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
||||
keyboardType: TextInputType.multiline,
|
||||
style: TextStyle(fontSize: 12),
|
||||
enabled: !widget.isViewMode,
|
||||
decoration: InputDecoration(
|
||||
decoration: InputDecoration(
|
||||
labelText: "Description",
|
||||
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
floatingLabelBehavior: FloatingLabelBehavior.never,
|
||||
|
||||
@ -465,27 +465,51 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
|
||||
List<Widget> _buildOptions() {
|
||||
return [
|
||||
_buildOption("Flight"),
|
||||
_buildOption("Flight", itineraryData["Flight"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Taxi"),
|
||||
_buildOption("Taxi", itineraryData["Taxi"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Train"),
|
||||
_buildOption("Train", itineraryData["Train"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Bus"),
|
||||
_buildOption("Bus", itineraryData["Bus"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Accomodation"),
|
||||
_buildOption("Accomodation", itineraryData["Accomodation"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Forex"),
|
||||
_buildOption("Forex", itineraryData["Forex"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Insurance"),
|
||||
_buildOption("Insurance", itineraryData["Insurance"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Visa"),
|
||||
_buildOption("Visa", itineraryData["Visa"]?.isNotEmpty ?? false),
|
||||
SizedBox(width: 20),
|
||||
_buildOption("Miscellaneous"),
|
||||
_buildOption("Miscellaneous", itineraryData["Miscellaneous"]?.isNotEmpty ?? false),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildOption(String title) {
|
||||
|
||||
// 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"),
|
||||
// ];
|
||||
// }
|
||||
|
||||
Widget _buildOption(String title, bool hasData) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
@ -495,6 +519,11 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
if(hasData)
|
||||
Icon(Icons.circle_notifications,color: Colors.green, size: 10,),
|
||||
|
||||
SizedBox(width: 5),
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
|
||||
@ -23,25 +23,67 @@ class ListPlans extends StatefulWidget{
|
||||
class _ListPlansState extends State<ListPlans>{
|
||||
|
||||
late Future<List<Plan>> futurePlans;
|
||||
String? userId;
|
||||
String? token;
|
||||
|
||||
@override
|
||||
void initState(){
|
||||
super.initState();
|
||||
futurePlans = fetchPlans();
|
||||
getToken();
|
||||
initializeData();
|
||||
|
||||
|
||||
// futurePlans = fetchPlans();
|
||||
|
||||
}
|
||||
|
||||
Future<void> initializeData ()async{
|
||||
token = await getToken();
|
||||
userId = await getUserId();
|
||||
|
||||
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?> 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';
|
||||
final String apiUrldata = '$apiUrl/api/plans?user_id=$userId';
|
||||
|
||||
// final token = await getToken();
|
||||
|
||||
final token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
@ -68,7 +110,7 @@ class _ListPlansState extends State<ListPlans>{
|
||||
Future <Map<String,dynamic>> getViewPlan(String planId) async{
|
||||
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
||||
print("API URL: $apiUrldata");
|
||||
final token = await getToken();
|
||||
// final token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
@ -269,9 +311,6 @@ class _ListPlansState extends State<ListPlans>{
|
||||
// },
|
||||
// );
|
||||
|
||||
|
||||
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
||||
@ -317,7 +356,22 @@ class _ListPlansState extends State<ListPlans>{
|
||||
DataCell(Text(plan.tripType)),
|
||||
DataCell(Text(plan.costCenter)),
|
||||
DataCell(Text(plan.isBillable)),
|
||||
DataCell(Text(plan.status)),
|
||||
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:[
|
||||
|
||||
1
lib/Screens/services/apiService.dart
Normal file
1
lib/Screens/services/apiService.dart
Normal file
@ -0,0 +1 @@
|
||||
// TODO Implement this library.
|
||||
2472
lib/Screens/userManagement/create_user/create_user.dart
Normal file
2472
lib/Screens/userManagement/create_user/create_user.dart
Normal file
File diff suppressed because it is too large
Load Diff
490
lib/Screens/userManagement/user_List.dart
Normal file
490
lib/Screens/userManagement/user_List.dart
Normal file
@ -0,0 +1,490 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../config/apiUrl.dart';
|
||||
import '../../routes/custom_appBar.dart';
|
||||
import '../../routes/custom_drawer.dart';
|
||||
|
||||
class UserListScreen extends StatefulWidget {
|
||||
@override
|
||||
_UserListScreenState createState() => _UserListScreenState();
|
||||
}
|
||||
|
||||
class _UserListScreenState extends State<UserListScreen> {
|
||||
late Future<List<dynamic>> futureUsers;
|
||||
List<dynamic>? apiCountryData;
|
||||
String? selectedUserId;
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString('auth_token');
|
||||
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
final String apiUrlData = '$apiUrl/api/users';
|
||||
final String? token = await getToken();
|
||||
|
||||
print("Fetch Users");
|
||||
print("TOEKRWE: $token");
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrlData),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
return data['data']; // Returning raw JSON list
|
||||
} else {
|
||||
throw Exception('Failed to load users');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> fetchCountryList() async {
|
||||
final String apiUrldata = '$apiUrl/api/getcountryMaster';
|
||||
|
||||
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("Country - $data");
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is!List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
List <dynamic> plansJson = data['data']; // 'data' is a Map, not a List
|
||||
|
||||
if (data['data'] is List) {
|
||||
List<dynamic> plansJson = data['data'];
|
||||
print("plansJson.length - ${plansJson.length}");
|
||||
} else {
|
||||
print("The 'data' key does not contain a list.");
|
||||
}
|
||||
|
||||
setState(() {
|
||||
apiCountryData = plansJson; // Store API response in state
|
||||
});
|
||||
print('plansJSONContry - $plansJson');
|
||||
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load plans');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
futureUsers = fetchUsers();
|
||||
fetchCountryList();
|
||||
}
|
||||
|
||||
void handleDelete(userId){
|
||||
print("handDel - $userId");
|
||||
}
|
||||
|
||||
|
||||
void handleToggleUserStatus(String userId, String currentStatus) async {
|
||||
print("Toggling user status - $userId (Current: $currentStatus)");
|
||||
|
||||
final String apiUrlData = '$apiUrl/api/users/update/$userId'; // API for updating user
|
||||
final String? token = await getToken();
|
||||
|
||||
if (token == null) {
|
||||
print("Error: Token not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Toggle status: If active ("1"), set to inactive ("0"); otherwise, activate ("1")
|
||||
String newStatus = (currentStatus == "1") ? "0" : "1";
|
||||
|
||||
try {
|
||||
final response = await http.put(
|
||||
Uri.parse(apiUrlData),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: jsonEncode({
|
||||
"is_active": newStatus // Set new status dynamically
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
print("User status updated successfully to $newStatus!");
|
||||
refreshUserList(); // Refresh users list after update
|
||||
} else {
|
||||
print("Failed to update user status. Status: ${response.statusCode}");
|
||||
print("Error: ${response.body}");
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error updating user status: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh user list after update
|
||||
void refreshUserList() {
|
||||
setState(() {
|
||||
futureUsers = fetchUsers(); // Re-fetch users after status update
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ResponsiveBuilder(builder: (context, sizingInfo) {
|
||||
bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop;
|
||||
|
||||
|
||||
return Scaffold(
|
||||
appBar: isDesktop ? null : const CustomAppBar(title:'User Management'),
|
||||
drawer: isDesktop ? null : CustomDrawer(isDesktop: false),
|
||||
body:
|
||||
Container(
|
||||
color: Colors.white,
|
||||
child: Row(
|
||||
children: [
|
||||
if(isDesktop)
|
||||
CustomDrawer(isDesktop: true),
|
||||
// const Expanded(child: Center(child: Text("User Page Content"))),
|
||||
Expanded(child: buildUserTable(isDesktop)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Widget buildUserTable(bool isDesktop) {
|
||||
|
||||
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Text('User 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;
|
||||
|
||||
// Print the resolved value
|
||||
print("CREATELIAS - $users");
|
||||
|
||||
context.go("/CreateUserDetails",
|
||||
extra: {
|
||||
// 'apiCountryData': apiCountryData,
|
||||
'apiUserData' :users,
|
||||
}
|
||||
);
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.add_circle,color: Colors.white,),
|
||||
SizedBox(width: 5,),
|
||||
Text('New User'),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
|
||||
FutureBuilder<List<dynamic>>(
|
||||
future: futureUsers,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return Center(child: CircularProgressIndicator());
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text("Error: ${snapshot.error}"));
|
||||
} else if (!snapshot.hasData || snapshot.data!.isEmpty) {
|
||||
return Center(child: Text("No users found"));
|
||||
}
|
||||
|
||||
List<dynamic> users = snapshot.data!;
|
||||
Color borderColor = Color(0xFF9E9DBD);
|
||||
|
||||
return Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 1.5,
|
||||
child: SingleChildScrollView(
|
||||
|
||||
scrollDirection: Axis.horizontal, // Inner wrapper for vertical scrolling
|
||||
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(minWidth: MediaQuery.of(context).size.width),
|
||||
// constraints: BoxConstraints(minWidth: 1300),
|
||||
// width: MediaQuery.of(context).size.width ,
|
||||
|
||||
child: Container(
|
||||
// color: Colors.amber,
|
||||
child: DataTable(
|
||||
columnSpacing: 20.0, // Adjust spacing between columns
|
||||
dividerThickness: 0.5,
|
||||
dataRowMinHeight: 60.0, // Minimum row height
|
||||
dataRowMaxHeight: 100.0,
|
||||
border: TableBorder(
|
||||
horizontalInside: BorderSide(width: 0.5, color: Colors.grey.shade200),
|
||||
),
|
||||
columns: const [
|
||||
|
||||
DataColumn(label: Text('User Details',style: TextStyle(color: Color(0xFF9E9DBD),fontSize: 15, fontWeight: FontWeight.bold),)),
|
||||
DataColumn(label: Text('Role',style: TextStyle(color: Color(0xFF9E9DBD),fontSize: 15, fontWeight: FontWeight.bold),)),
|
||||
DataColumn(label: Text('Level',style: TextStyle(color: Color(0xFF9E9DBD),fontSize: 15, fontWeight: FontWeight.bold),)),
|
||||
DataColumn(label: Text('Status',style: TextStyle(color: Color(0xFF9E9DBD),fontSize: 15, fontWeight: FontWeight.bold),)),
|
||||
DataColumn(label: Text('Actions',style: TextStyle(color: Color(0xFF9E9DBD),fontSize: 15, fontWeight: FontWeight.bold),)),
|
||||
|
||||
],
|
||||
|
||||
rows: users.map((user) {
|
||||
String userId = user['user_id'].toString(); // Get user ID
|
||||
bool isSelected = selectedUserId == userId;
|
||||
|
||||
return DataRow(cells: [
|
||||
// DataCell(Text(user['user_id'].toString())),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
Align(alignment: Alignment.center,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedUserId = userId; // Store clicked user ID
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: 15, // Adjust size
|
||||
height: 15,
|
||||
decoration: BoxDecoration(
|
||||
// Background color
|
||||
shape: BoxShape.rectangle,
|
||||
border: Border.all(
|
||||
color: isSelected ? Colors.blueAccent : Color(0xFF9E9DBD),
|
||||
// color: Color(0xFF9E9DBD),
|
||||
// color: Color.fromRGBO(128, 128, 128, 0.6),
|
||||
width: isSelected ?2:1), // Grey outline
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
SizedBox(width: 50,),
|
||||
Align(
|
||||
alignment: Alignment.center,
|
||||
child: Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Color(0xFF9E9DBD), width: 1), // Grey outline
|
||||
),
|
||||
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
|
||||
width: 40, // Adjust size
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.amber, // Inner circle background
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
(user['first_name'] != null && user['first_name']!.isNotEmpty)
|
||||
? user['first_name']![0].toUpperCase()
|
||||
: "?",
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white, ),
|
||||
),],
|
||||
|
||||
|
||||
),
|
||||
),
|
||||
)),
|
||||
),
|
||||
SizedBox(width: 20,),
|
||||
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Text("${user['first_name'] ?? ''} ${user['last_name'] ?? ''}",
|
||||
style: TextStyle( color: Colors.blueAccent,fontSize: 16),),
|
||||
],),
|
||||
|
||||
SizedBox(height: 5),
|
||||
|
||||
Row(children: [
|
||||
Icon(Icons.mail_outline, size: 15,color: Color(0xFF9E9EBE)),
|
||||
SizedBox(width: 10,),
|
||||
Text(user['email'] ?? '')
|
||||
],),
|
||||
|
||||
SizedBox(height: 5),
|
||||
Row(children: [
|
||||
Icon(Icons.account_tree_outlined, size: 15,color:Color(0xFF9E9EBE)),
|
||||
SizedBox(width: 10,),
|
||||
Text(user['user_type'] ?? '') ],),
|
||||
],),
|
||||
|
||||
|
||||
],
|
||||
)
|
||||
),
|
||||
|
||||
DataCell(Text(user['role_id'] ?? 'N/A',style: TextStyle(color: user['is_active'] == "1" ? Colors.black :Colors.grey, fontWeight: FontWeight.bold),)),
|
||||
DataCell(Text(user['level_id'] ?? 'N/A',style: TextStyle(color: user['is_active'] == "1" ? Colors.black :Colors.grey, fontWeight: FontWeight.bold),)),
|
||||
DataCell(
|
||||
GestureDetector( onTap :(){
|
||||
handleToggleUserStatus(user['user_id'], user['is_active']);
|
||||
},
|
||||
child: Text(
|
||||
user['is_active'] == "1" ? "Active" : "Inactive",
|
||||
style: TextStyle(color: user['is_active'] == "1" ? Colors.lightGreen :Colors.grey ,
|
||||
|
||||
fontWeight: FontWeight.bold),)
|
||||
|
||||
)
|
||||
|
||||
),
|
||||
|
||||
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
MouseRegion(
|
||||
cursor: user['is_active'] == "0" ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.remove_red_eye, color: user['is_active'] == "0" ? Colors.grey : Colors.blueAccent),
|
||||
onPressed: user['is_active'] == "0"
|
||||
? null
|
||||
: () {
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
"selectedUser": user,
|
||||
"isViewMode": true
|
||||
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: user['is_active'] == "0" ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.edit, color: user['is_active'] == "0" ? Colors.grey : Colors.green),
|
||||
onPressed: user['is_active'] == "0"
|
||||
? null
|
||||
: () {
|
||||
print("USER: $user");
|
||||
context.go(
|
||||
"/CreateUserDetails",
|
||||
extra: {
|
||||
"selectedUser": user,
|
||||
"isViewMode": false
|
||||
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
MouseRegion(
|
||||
cursor: user['is_active'] == "0" ? SystemMouseCursors.forbidden : SystemMouseCursors.click,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.delete, color: user['is_active'] == "0" ? Colors.grey : Colors.redAccent),
|
||||
onPressed: user['is_active'] == "0"
|
||||
? null
|
||||
: () {
|
||||
print("USER ID: ${user['user_id']}");
|
||||
var userId = user['user_id'];
|
||||
handleDelete(userId);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
]);
|
||||
}).toList(),
|
||||
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
]));
|
||||
}
|
||||
}
|
||||
@ -1,37 +1,161 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:responsive_builder/responsive_builder.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
class CustomDrawer extends StatelessWidget{
|
||||
final bool isDesktop;
|
||||
class CustomDrawer extends StatefulWidget{
|
||||
|
||||
final bool isDesktop;
|
||||
const CustomDrawer({super.key, required this.isDesktop});
|
||||
|
||||
@override
|
||||
_CustomDrawerState createState() => _CustomDrawerState();
|
||||
|
||||
}
|
||||
|
||||
class _CustomDrawerState extends State<CustomDrawer>{
|
||||
|
||||
String? token;
|
||||
Map<String,dynamic>? userData;
|
||||
Map<String, dynamic>? fetchedUserData;
|
||||
Map<String, dynamic> userDetails = {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
initializeData();
|
||||
}
|
||||
|
||||
Future<void> initializeData() async{
|
||||
|
||||
print("initializeDatainitializeData");
|
||||
token = await getToken();
|
||||
fetchedUserData = await getUserData();
|
||||
|
||||
if(token == null || fetchedUserData == null)
|
||||
{
|
||||
print("Token or USerId missing");
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
userData = fetchedUserData;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<String?> getToken() async{
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("auth_token");
|
||||
}
|
||||
|
||||
Future <Map<String,dynamic>?> getUserData() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final String? userDataString = prefs.getString('user_data');
|
||||
|
||||
if(userDataString != null){
|
||||
try{
|
||||
userDetails = jsonDecode(userDataString);
|
||||
|
||||
return{
|
||||
"user_id" : userDetails["user_id"].toString(),
|
||||
"name" : "${userDetails["first_name"]} ${userDetails["last_name"]}",
|
||||
"email" : userDetails["email"] ?? "",
|
||||
};
|
||||
}catch (e) {
|
||||
print("Error decoding user data: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const CustomDrawer({super.key, required this.isDesktop});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context){
|
||||
Widget drawerContent = Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 80,
|
||||
child: const DrawerHeader(
|
||||
decoration: BoxDecoration(color: Colors.blue),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Text('Menu', style: TextStyle(color: Colors.white, fontSize: 20)))
|
||||
Widget drawerContent = Container(
|
||||
color: Color(0xFFF3F3FA),
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap:(){
|
||||
print("ONTAP Custom");
|
||||
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(
|
||||
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2.0),
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration:BoxDecoration(
|
||||
color: Colors.blueAccent,
|
||||
shape: BoxShape.circle
|
||||
) ,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [ Text(
|
||||
userData?["name"]?.isNotEmpty == true
|
||||
? userData!["name"]![0].toUpperCase()
|
||||
: "N/A",
|
||||
style: TextStyle(color: Colors.white, fontSize: 25),
|
||||
),],),
|
||||
),
|
||||
),
|
||||
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
userData?["name"] ?? "N/A",
|
||||
style: TextStyle(color: Colors.black87, fontSize: 11),
|
||||
),
|
||||
Text(
|
||||
userData?["email"] ?? "N/A",
|
||||
style: TextStyle(color: Colors.black45, fontSize: 10),
|
||||
),
|
||||
|
||||
],)
|
||||
],)
|
||||
),
|
||||
),
|
||||
),
|
||||
_buildDrawerItem(context, Icons.home,'Home', '/home'),
|
||||
_buildExpandableItem(context,Icons.assessment,'Plans',[
|
||||
_buildSubDrawerItem(context,'My Plans','/listPlan'),
|
||||
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||
]),
|
||||
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
||||
],
|
||||
),
|
||||
_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')
|
||||
]),
|
||||
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (isDesktop) {
|
||||
if (widget.isDesktop) {
|
||||
// Sidebar for Desktop (always visible)**
|
||||
return Container(
|
||||
width: 250, // Fixed width for sidebar
|
||||
@ -74,14 +198,13 @@ class CustomDrawer extends StatelessWidget{
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildSubDrawerItem(BuildContext context, String title, String route)
|
||||
{
|
||||
return ListTile(
|
||||
title: Text(title),
|
||||
onTap: (){
|
||||
context.go(route);
|
||||
if (!isDesktop) Navigator.pop(context);
|
||||
if (!widget.isDesktop) Navigator.pop(context);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import 'package:frontend/Screens/authentication/loginPage1.dart';
|
||||
import 'package:frontend/Screens/dashboard/home_page.dart';
|
||||
import 'package:frontend/Screens/plans/create_plans.dart';
|
||||
import 'package:frontend/Screens/plans/list_plans.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';
|
||||
|
||||
final GoRouter router = GoRouter(
|
||||
@ -24,7 +26,13 @@ final GoRouter router = GoRouter(
|
||||
path: '/createPlan',
|
||||
builder: (context, state) => CreatePlan(),
|
||||
),
|
||||
|
||||
|
||||
GoRoute(
|
||||
path: '/listUser',
|
||||
builder: (context, state) => UserListScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/CreateUserDetails',
|
||||
builder: (context, state) => CreateUserForm(),
|
||||
),
|
||||
],
|
||||
);
|
||||
72
lib/services/apiService.dart
Normal file
72
lib/services/apiService.dart
Normal file
@ -0,0 +1,72 @@
|
||||
import 'dart:convert';
|
||||
import 'package:frontend/utils/auth_utils.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import '../../config/apiUrl.dart';
|
||||
|
||||
|
||||
class ApiService {
|
||||
|
||||
Future<List<dynamic>> fetchCountryList() async {
|
||||
|
||||
final String apiUrldata = '$apiUrl/api/getcountryMaster';
|
||||
final token = await getToken();
|
||||
|
||||
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("Country - $data");
|
||||
|
||||
if (!data.containsKey('data') || data['data'] is! List) {
|
||||
throw Exception("Invalid response format: 'data' field is missing or not a List");
|
||||
}
|
||||
|
||||
return data['data'];
|
||||
} catch (e) {
|
||||
throw Exception('Error parsing response: $e');
|
||||
}
|
||||
} else {
|
||||
throw Exception('Failed to load country list');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<dynamic>> fetchUsers() async {
|
||||
final String apiUrlData = '$apiUrl/api/users';
|
||||
final String? token = await getToken();
|
||||
|
||||
print("Fetch Users");
|
||||
print("TOEKRWE: $token");
|
||||
|
||||
if (token == null) {
|
||||
throw Exception('Token not found. Please log in.');
|
||||
}
|
||||
|
||||
final response = await http.get(
|
||||
Uri.parse(apiUrlData),
|
||||
headers: {
|
||||
'Authorization': 'Bearer $token',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = json.decode(response.body);
|
||||
return data['data']; // Returning raw JSON list
|
||||
} else {
|
||||
throw Exception('Failed to load users');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
6
lib/utils/auth_utils.dart
Normal file
6
lib/utils/auth_utils.dart
Normal file
@ -0,0 +1,6 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString("auth_token");
|
||||
}
|
||||
@ -35,6 +35,7 @@ class _CustomTextFieldForexWrapperState extends State<CustomTextFieldForexWrappe
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
color: widget.color,
|
||||
// color: Color(0xFFF7F7FB),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
|
||||
59
lib/widgets/custom_user_form.dart
Normal file
59
lib/widgets/custom_user_form.dart
Normal file
@ -0,0 +1,59 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomTextFieldUserWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
final bool isFocused;
|
||||
final bool isDesktop;
|
||||
final double? width;
|
||||
final Color? color;
|
||||
final VoidCallback? onFocusChange; // Callback for focus handling
|
||||
final EdgeInsetsGeometry padding;
|
||||
|
||||
const CustomTextFieldUserWrapper({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.isFocused,
|
||||
required this.isDesktop,
|
||||
this.width,
|
||||
this.color = Colors.white,
|
||||
this.onFocusChange,
|
||||
this.padding = const EdgeInsets.symmetric(horizontal: 12),
|
||||
});
|
||||
|
||||
@override
|
||||
_CustomTextFieldUserWrapperState createState() => _CustomTextFieldUserWrapperState();
|
||||
}
|
||||
|
||||
class _CustomTextFieldUserWrapperState extends State<CustomTextFieldUserWrapper> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: widget.width ?? // Use custom width if provided, else default
|
||||
(widget.isDesktop
|
||||
? MediaQuery.of(context).size.width * 0.25
|
||||
: MediaQuery.of(context).size.width * 0.8),
|
||||
padding: widget.padding,
|
||||
decoration: BoxDecoration(
|
||||
// color: widget.color,
|
||||
color: Color(0xFFF7F7FB),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
||||
width: widget.isFocused ? 2.0 : 0.5,
|
||||
),
|
||||
boxShadow: widget.isFocused
|
||||
? [
|
||||
BoxShadow(
|
||||
color: Color.fromRGBO(120, 180, 252, 0.3),
|
||||
blurRadius: 10,
|
||||
spreadRadius: 2,
|
||||
offset: Offset(0, 4),
|
||||
|
||||
),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -5,8 +5,10 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import file_picker
|
||||
import shared_preferences_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
}
|
||||
|
||||
40
pubspec.lock
40
pubspec.lock
@ -17,6 +17,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
bcrypt:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bcrypt
|
||||
sha256: "9dc3f234d5935a76917a6056613e1a6d9b53f7fa56f98e24cd49b8969307764b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -49,6 +57,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.0"
|
||||
cross_file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cross_file
|
||||
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.4+2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -105,6 +121,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
file_picker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: "36a1652d99cb6bf8ccc8b9f43aded1fd60b234d23ce78af422c07f950a436ef7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -118,6 +142,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.27"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -453,6 +485,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.10.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@ -41,6 +41,8 @@ dependencies:
|
||||
easy_stepper: ^0.8.5+1
|
||||
intl: ^0.20.2
|
||||
dropdown_search: ^5.0.6
|
||||
file_picker: ^10.0.0
|
||||
bcrypt: ^1.1.3
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user