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");
|
print("CAsh - $cashAmount- CaRd- $enteredAmount - $calculateAmnt - selectedQuotedAmount - $qouteAmount");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
if (enteredAmount == null || calculateAmnt > qouteAmount!) {
|
||||||
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
errorMessages["deposit_on_card"] = "Amount cannot exceed $qouteAmount";
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -154,8 +154,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void _clearError(String field) {
|
void _clearError(String field) {
|
||||||
if (mounted && errorMessages.containsKey(field)) {
|
if (mounted && errorMessages.containsKey(field)) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -165,11 +163,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool isValidData(Map<String, dynamic> data) {
|
bool isValidData(Map<String, dynamic> data) {
|
||||||
errorMessages.clear(); // Reset errors
|
errorMessages.clear(); // Reset errors
|
||||||
|
|
||||||
@ -327,8 +320,6 @@ class _TaxiScreenState extends State<TaxiScreen> {
|
|||||||
Column(
|
Column(
|
||||||
children: _buildTripType(isDesktop)
|
children: _buildTripType(isDesktop)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (isDesktop)
|
if (isDesktop)
|
||||||
|
|||||||
@ -410,10 +410,6 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
final String apiUrldata = '$apiUrl/api/getDropdownMaster';
|
final String apiUrldata = '$apiUrl/api/getDropdownMaster';
|
||||||
|
|
||||||
final token = await getToken();
|
final token = await getToken();
|
||||||
final userId = "1";
|
|
||||||
// final userId = await getUserId();
|
|
||||||
|
|
||||||
print("SUSRTRT- $userId");
|
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
throw Exception('Token not found. Please log in.');
|
throw Exception('Token not found. Please log in.');
|
||||||
@ -456,7 +452,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
|
|
||||||
final token = await getToken();
|
final token = await getToken();
|
||||||
|
|
||||||
// final userId = await getUserId();
|
final userId = await getUserId();
|
||||||
|
|
||||||
// print("SUSRTRT- $userId");
|
// print("SUSRTRT- $userId");
|
||||||
//
|
//
|
||||||
@ -508,7 +504,7 @@ class _CreateNewPlansState extends State<CreateNewPlan> {
|
|||||||
|
|
||||||
final token = await getToken();
|
final token = await getToken();
|
||||||
|
|
||||||
// final userId = await getUserId();
|
final userId = await getUserId();
|
||||||
|
|
||||||
// print("SUSRTRT- $userId");
|
// print("SUSRTRT- $userId");
|
||||||
//
|
//
|
||||||
|
|||||||
@ -465,27 +465,51 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
|
|
||||||
List<Widget> _buildOptions() {
|
List<Widget> _buildOptions() {
|
||||||
return [
|
return [
|
||||||
_buildOption("Flight"),
|
_buildOption("Flight", itineraryData["Flight"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Taxi"),
|
_buildOption("Taxi", itineraryData["Taxi"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Train"),
|
_buildOption("Train", itineraryData["Train"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Bus"),
|
_buildOption("Bus", itineraryData["Bus"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Accomodation"),
|
_buildOption("Accomodation", itineraryData["Accomodation"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Forex"),
|
_buildOption("Forex", itineraryData["Forex"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Insurance"),
|
_buildOption("Insurance", itineraryData["Insurance"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
SizedBox(width: 20),
|
||||||
_buildOption("Visa"),
|
_buildOption("Visa", itineraryData["Visa"]?.isNotEmpty ?? false),
|
||||||
SizedBox(width: 20),
|
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(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -495,6 +519,11 @@ class _DynamicItineraryState extends State<DynamicItinerary> {
|
|||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
|
|
||||||
|
if(hasData)
|
||||||
|
Icon(Icons.circle_notifications,color: Colors.green, size: 10,),
|
||||||
|
|
||||||
|
SizedBox(width: 5),
|
||||||
Text(title,
|
Text(title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
|
|||||||
@ -23,25 +23,67 @@ class ListPlans extends StatefulWidget{
|
|||||||
class _ListPlansState extends State<ListPlans>{
|
class _ListPlansState extends State<ListPlans>{
|
||||||
|
|
||||||
late Future<List<Plan>> futurePlans;
|
late Future<List<Plan>> futurePlans;
|
||||||
|
String? userId;
|
||||||
|
String? token;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState(){
|
void initState(){
|
||||||
super.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 {
|
Future<String?> getToken() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
return prefs.getString('auth_token');
|
return prefs.getString('auth_token');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Fetch API Data
|
// Fetch API Data
|
||||||
Future<List<Plan>> fetchPlans() async {
|
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) {
|
if (token == null) {
|
||||||
throw Exception('Token not found. Please log in.');
|
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{
|
Future <Map<String,dynamic>> getViewPlan(String planId) async{
|
||||||
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
final String apiUrldata = '$apiUrl/api/plans/find/$planId';
|
||||||
print("API URL: $apiUrldata");
|
print("API URL: $apiUrldata");
|
||||||
final token = await getToken();
|
// final token = await getToken();
|
||||||
|
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
throw Exception('Token not found. Please log in.');
|
throw Exception('Token not found. Please log in.');
|
||||||
@ -269,9 +311,6 @@ class _ListPlansState extends State<ListPlans>{
|
|||||||
// },
|
// },
|
||||||
// );
|
// );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
// scrollDirection: Axis.horizontal, // Outer wrapper for horizontal scrolling
|
||||||
@ -317,7 +356,22 @@ class _ListPlansState extends State<ListPlans>{
|
|||||||
DataCell(Text(plan.tripType)),
|
DataCell(Text(plan.tripType)),
|
||||||
DataCell(Text(plan.costCenter)),
|
DataCell(Text(plan.costCenter)),
|
||||||
DataCell(Text(plan.isBillable)),
|
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(
|
DataCell(
|
||||||
Row(
|
Row(
|
||||||
children:[
|
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:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:responsive_builder/responsive_builder.dart';
|
import 'package:responsive_builder/responsive_builder.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
|
||||||
class CustomDrawer extends StatelessWidget{
|
class CustomDrawer extends StatefulWidget{
|
||||||
final bool isDesktop;
|
|
||||||
|
|
||||||
|
final bool isDesktop;
|
||||||
const CustomDrawer({super.key, required this.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context){
|
Widget build(BuildContext context){
|
||||||
Widget drawerContent = Column(
|
Widget drawerContent = Container(
|
||||||
|
color: Color(0xFFF3F3FA),
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
SizedBox(
|
GestureDetector(
|
||||||
height: 80,
|
onTap:(){
|
||||||
child: const DrawerHeader(
|
print("ONTAP Custom");
|
||||||
decoration: BoxDecoration(color: Colors.blue),
|
context.go(
|
||||||
|
"/CreateUserDetails",
|
||||||
|
extra: {
|
||||||
|
"selectedUser": userDetails,
|
||||||
|
"isEditProfile": true,
|
||||||
|
"isViewMode": true
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
child :SizedBox(
|
child :SizedBox(
|
||||||
|
height: 80,
|
||||||
|
child: Container(
|
||||||
|
color: Color(0xFFF3F3FA),
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Text('Menu', style: TextStyle(color: Colors.white, fontSize: 20)))
|
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'),
|
_buildDrawerItem(context, Icons.home,'Home', '/home'),
|
||||||
_buildExpandableItem(context,Icons.assessment,'Plans',[
|
_buildExpandableItem(context,Icons.assessment,'Plans',[
|
||||||
_buildSubDrawerItem(context,'My Plans','/listPlan'),
|
_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')
|
// _buildSubDrawerItem(context,'PlanB','/PlanB')
|
||||||
]),
|
]),
|
||||||
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
_buildDrawerItem(context,Icons.logout,'Logout','/')
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isDesktop) {
|
if (widget.isDesktop) {
|
||||||
// Sidebar for Desktop (always visible)**
|
// Sidebar for Desktop (always visible)**
|
||||||
return Container(
|
return Container(
|
||||||
width: 250, // Fixed width for sidebar
|
width: 250, // Fixed width for sidebar
|
||||||
@ -74,14 +198,13 @@ class CustomDrawer extends StatelessWidget{
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget _buildSubDrawerItem(BuildContext context, String title, String route)
|
Widget _buildSubDrawerItem(BuildContext context, String title, String route)
|
||||||
{
|
{
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
onTap: (){
|
onTap: (){
|
||||||
context.go(route);
|
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/dashboard/home_page.dart';
|
||||||
import 'package:frontend/Screens/plans/create_plans.dart';
|
import 'package:frontend/Screens/plans/create_plans.dart';
|
||||||
import 'package:frontend/Screens/plans/list_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';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
final GoRouter router = GoRouter(
|
final GoRouter router = GoRouter(
|
||||||
@ -24,7 +26,13 @@ final GoRouter router = GoRouter(
|
|||||||
path: '/createPlan',
|
path: '/createPlan',
|
||||||
builder: (context, state) => 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,
|
padding: widget.padding,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: widget.color,
|
color: widget.color,
|
||||||
|
// color: Color(0xFFF7F7FB),
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
|
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 FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import file_picker
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
40
pubspec.lock
40
pubspec.lock
@ -17,6 +17,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.11.0"
|
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:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -49,6 +57,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.19.0"
|
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:
|
crypto:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -105,6 +121,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
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:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -118,6 +142,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
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:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -453,6 +485,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.10.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@ -41,6 +41,8 @@ dependencies:
|
|||||||
easy_stepper: ^0.8.5+1
|
easy_stepper: ^0.8.5+1
|
||||||
intl: ^0.20.2
|
intl: ^0.20.2
|
||||||
dropdown_search: ^5.0.6
|
dropdown_search: ^5.0.6
|
||||||
|
file_picker: ^10.0.0
|
||||||
|
bcrypt: ^1.1.3
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user