Merge pull request #67 from SyncrowIOT/roles_permissions_issues

fixes filter and table view and add user dialog
This commit is contained in:
mohammadnemer1
2025-01-13 15:41:33 +03:00
committed by GitHub
12 changed files with 532 additions and 520 deletions

View File

@ -42,7 +42,7 @@ class RolesUserModel {
invitedBy:
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
phoneNumber: json['phoneNumber'],
jobTitle: json['jobTitle'].toString(),
jobTitle: json['jobTitle'] ?? "-",
createdDate: json['createdDate'],
createdTime: json['createdTime'],
);

View File

@ -114,7 +114,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
currentStep++;
if (currentStep == 2) {
_blocRole.add(
CheckStepStatus(isEditUser: false));
const CheckStepStatus(isEditUser: false));
} else if (currentStep == 3) {
_blocRole
.add(const CheckSpacesStepStatus());
@ -151,11 +151,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
Widget _getFormContent() {
switch (currentStep) {
case 1:
return BasicsView(
return const BasicsView(
userId: '',
);
case 2:
return SpacesAccessView();
return const SpacesAccessView();
case 3:
return const RolesAndPermission();
default:
@ -172,7 +172,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
bloc.add(const CheckSpacesStepStatus());
currentStep = step;
Future.delayed(const Duration(milliseconds: 500), () {
bloc.add(ValidateBasicsStep());
bloc.add(const ValidateBasicsStep());
});
});
@ -237,10 +237,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
onTap: () {
setState(() {
currentStep = step;
bloc.add(CheckStepStatus(isEditUser: false));
bloc.add(const CheckStepStatus(isEditUser: false));
if (step3 == 3) {
bloc.add(const CheckRoleStepStatus());
}
});
},
child: Column(

View File

@ -4,7 +4,6 @@ import 'package:intl_phone_field/countries.dart';
import 'package:intl_phone_field/country_picker_dialog.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
@ -47,7 +46,9 @@ class BasicsView extends StatelessWidget {
),
Row(
children: [
Expanded(
SizedBox(
width: MediaQuery.of(context).size.width * 0.18,
height: MediaQuery.of(context).size.width * 0.08,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -76,12 +77,12 @@ class BasicsView extends StatelessWidget {
child: TextFormField(
style:
const TextStyle(color: ColorsManager.blackColor),
onChanged: (value) {
Future.delayed(const Duration(milliseconds: 200),
() {
_blocRole.add(ValidateBasicsStep());
});
},
// onChanged: (value) {
// Future.delayed(const Duration(milliseconds: 200),
// () {
// _blocRole.add(const ValidateBasicsStep());
// });
// },
controller: _blocRole.firstNameController,
decoration: inputTextFormDeco(
hintText: "Enter first name",
@ -103,7 +104,9 @@ class BasicsView extends StatelessWidget {
),
),
const SizedBox(width: 10),
Expanded(
SizedBox(
width: MediaQuery.of(context).size.width * 0.18,
height: MediaQuery.of(context).size.width * 0.08,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -128,12 +131,12 @@ class BasicsView extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(8.0),
child: TextFormField(
onChanged: (value) {
Future.delayed(const Duration(milliseconds: 200),
() {
_blocRole.add(ValidateBasicsStep());
});
},
// onChanged: (value) {
// Future.delayed(const Duration(milliseconds: 200),
// () {
// _blocRole.add(ValidateBasicsStep());
// });
// },
controller: _blocRole.lastNameController,
style: const TextStyle(color: Colors.black),
decoration:
@ -186,13 +189,13 @@ class BasicsView extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: TextFormField(
enabled: userId != '' ? false : true,
onChanged: (value) {
Future.delayed(const Duration(milliseconds: 200), () {
_blocRole.add(CheckStepStatus(
isEditUser: userId != '' ? false : true));
_blocRole.add(ValidateBasicsStep());
});
},
// onChanged: (value) {
// Future.delayed(const Duration(milliseconds: 200), () {
// _blocRole.add(CheckStepStatus(
// isEditUser: userId != '' ? false : true));
// _blocRole.add(ValidateBasicsStep());
// });
// },
controller: _blocRole.emailController,
style: const TextStyle(color: ColorsManager.blackColor),
decoration: inputTextFormDeco(hintText: "name@example.com")

View File

@ -11,7 +11,14 @@ class DeleteUserDialog extends StatefulWidget {
}
class _DeleteUserDialogState extends State<DeleteUserDialog> {
int currentStep = 1;
bool isLoading = false;
bool _isDisposed = false;
@override
void dispose() {
_isDisposed = true;
super.dispose();
}
@override
Widget build(BuildContext context) {
@ -56,7 +63,7 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
Expanded(
child: InkWell(
onTap: () {
Navigator.of(context).pop(true);
Navigator.of(context).pop(false); // Return false if canceled
},
child: Container(
padding: const EdgeInsets.all(10),
@ -76,7 +83,26 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
)),
Expanded(
child: InkWell(
onTap: widget.onTapDelete,
onTap: isLoading
? null
: () async {
setState(() {
isLoading = true;
});
try {
if (widget.onTapDelete != null) {
await widget.onTapDelete!();
}
} finally {
if (!_isDisposed) {
setState(() {
isLoading = false;
});
}
}
Navigator.of(context).pop(true);
},
child: Container(
padding: const EdgeInsets.all(10),
decoration: const BoxDecoration(
@ -91,13 +117,22 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
),
),
),
child: const Center(
child: Text(
'Delete',
style: TextStyle(
color: ColorsManager.red,
),
))),
child: Center(
child: isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
color: ColorsManager.red,
strokeWidth: 2.0,
),
)
: const Text(
'Delete',
style: TextStyle(
color: ColorsManager.red,
),
))),
)),
],
)

View File

@ -128,7 +128,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
),
const SizedBox(width: 8),
Text(
option.title,
' ${option.title.isNotEmpty ? option.title[0].toUpperCase() : ''}${option.title.substring(1)}',
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w700,
fontSize: 12,
@ -184,7 +184,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
),
const SizedBox(width: 8),
Text(
subOption.title,
' ${subOption.title.isNotEmpty ? subOption.title[0].toUpperCase() : ''}${subOption.title.substring(1)}',
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w700,
fontSize: 12,
@ -246,7 +246,7 @@ class _PermissionManagementState extends State<PermissionManagement> {
),
const SizedBox(width: 8),
Text(
child.title,
' ${child.title.isNotEmpty ? child.title[0].toUpperCase() : ''}${child.title.substring(1)}',
style: context.textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.w400,
fontSize: 12,

View File

@ -5,141 +5,156 @@ import 'package:syncrow_web/utils/style.dart';
Future<void> showPopUpFilterMenu({
required BuildContext context,
Function()? onSortAtoZ,
Function()? onSortZtoA,
required Function(String value) onSortAtoZ,
required Function(String value) onSortZtoA,
Function()? cancelButton,
required Map<String, bool> checkboxStates,
required RelativeRect position,
Function()? onOkPressed,
List<String>? list,
String? isSelected,
}) async {
await showMenu(
context: context,
position:position,
position: position,
color: ColorsManager.whiteColors,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
items: <PopupMenuEntry>[
PopupMenuItem(
onTap: onSortAtoZ,
child: ListTile(
leading: Image.asset(
Assets.AtoZIcon,
width: 25,
),
title: const Text(
"Sort A to Z",
style: TextStyle(color: Colors.blueGrey),
),
),
),
PopupMenuItem(
onTap: onSortZtoA,
child: ListTile(
leading: Image.asset(
Assets.ZtoAIcon,
width: 25,
),
title: const Text(
"Sort Z to A",
style: TextStyle(color: Colors.blueGrey),
),
),
),
const PopupMenuDivider(),
const PopupMenuItem(
child: Text(
"Filter by Status",
style: TextStyle(fontWeight: FontWeight.bold),
)
// Container(
// decoration: containerDecoration.copyWith(
// boxShadow: [],
// borderRadius: const BorderRadius.only(
// topLeft: Radius.circular(10), topRight: Radius.circular(10))),
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: TextFormField(
// onChanged: onTextFieldChanged,
// style: const TextStyle(color: Colors.black),
// decoration: textBoxDecoration(radios: 15)!.copyWith(
// fillColor: ColorsManager.whiteColors,
// errorStyle: const TextStyle(height: 0),
// hintStyle: context.textTheme.titleSmall?.copyWith(
// color: Colors.grey,
// fontSize: 12,
// ),
// hintText: 'Search',
// suffixIcon: SizedBox(
// child: SvgPicture.asset(
// Assets.searchIconUser,
// fit: BoxFit.none,
// ),
// ),
// ),
// // "Filter by Status",
// // style: TextStyle(fontWeight: FontWeight.bold),
// ),
// ),
// ),
),
PopupMenuItem(
child: Container(
decoration: containerDecoration.copyWith(
boxShadow: [],
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10))),
padding: const EdgeInsets.all(10),
height: 200,
width: 400,
child: Container(
padding: const EdgeInsets.all(10),
color: Colors.white,
child: ListView.builder(
itemCount: list?.length ?? 0,
itemBuilder: (context, index) {
final item = list![index];
return CheckboxListTile(
dense: true,
title: Text(item),
value: checkboxStates[item],
onChanged: (bool? newValue) {
checkboxStates[item] = newValue ?? false;
(context as Element).markNeedsBuild();
},
);
},
),
),
),
),
PopupMenuItem(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pop(); // Close the menu
},
child: const Text("Cancel"),
),
GestureDetector(
onTap: onOkPressed,
child: const Text(
"OK",
style: TextStyle(
color: ColorsManager.spaceColor,
enabled: false,
child: StatefulBuilder(
builder: (context, setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
child: ListTile(
onTap: () {
setState(() {
if (isSelected == 'Asc') {
isSelected = null;
onSortAtoZ.call('');
} else {
onSortAtoZ.call('Asc');
isSelected = 'Asc';
}
});
},
leading: Image.asset(
Assets.AtoZIcon,
width: 25,
),
title: Text(
"Sort A to Z",
style: TextStyle(
color: isSelected == "Asc"
? ColorsManager.blackColor
: ColorsManager.grayColor),
),
),
),
),
),
],
ListTile(
onTap: () {
setState(() {
if (isSelected == 'Desc') {
isSelected = null;
onSortZtoA.call('');
} else {
onSortZtoA.call('Desc');
isSelected = 'Desc';
}
});
},
leading: Image.asset(
Assets.ZtoAIcon,
width: 25,
),
title: Text(
"Sort Z to A",
style: TextStyle(
color: isSelected == "Desc"
? ColorsManager.blackColor
: ColorsManager.grayColor),
),
),
const Divider(),
const Text(
"Filter by Status",
style: TextStyle(fontWeight: FontWeight.bold),
),
Container(
decoration: containerDecoration.copyWith(
boxShadow: [],
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(10),
topRight: Radius.circular(10),
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10))),
padding: const EdgeInsets.all(10),
height: 200,
width: 400,
child: Container(
padding: const EdgeInsets.all(10),
color: Colors.white,
child: ListView.builder(
itemCount: list?.length ?? 0,
itemBuilder: (context, index) {
final item = list![index];
return Row(
children: [
Checkbox(
value: checkboxStates[item],
onChanged: (bool? newValue) {
checkboxStates[item] = newValue ?? false;
(context as Element).markNeedsBuild();
},
),
Text(
item,
style: TextStyle(color: ColorsManager.grayColor),
),
],
);
},
),
),
),
const SizedBox(
height: 10,
),
const Divider(),
const SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
Navigator.of(context).pop(); // Close the menu
},
child: const Text("Cancel"),
),
GestureDetector(
onTap: onOkPressed,
child: const Text(
"OK",
style: TextStyle(
color: ColorsManager.spaceColor,
),
),
),
],
),
const SizedBox(
height: 10,
),
],
);
},
),
),
],

View File

@ -52,14 +52,16 @@ class _RoleDropdownState extends State<RoleDropdown> {
SizedBox(
child: DropdownButtonFormField<String>(
dropdownColor: ColorsManager.whiteColors,
alignment: Alignment.center,
// alignment: Alignment.,
focusColor: Colors.white,
autofocus: true,
value: selectedRole.isNotEmpty ? selectedRole : null,
items: widget.bloc!.roles.map((role) {
return DropdownMenuItem<String>(
value: role.uuid,
child: Text(role.type),
child: Text(
' ${role.type.isNotEmpty ? role.type[0].toUpperCase() : ''}${role.type.substring(1)}',
),
);
}).toList(),
onChanged: (value) {

View File

@ -93,7 +93,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
try {
emit(UsersLoadingState());
bool res = await UserPermissionApi().changeUserStatusById(
event.userId, event.newStatus == "disabled" ? true : false);
event.userId, event.newStatus == "disabled" ? false : true);
if (res == true) {
add(const GetUsers());
// users = users.map((user) {
@ -133,7 +133,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
} else {
emit(UsersLoadingState());
currentSortOrder = "Asc";
users.sort((a, b) => a.firstName!.compareTo(b.firstName!));
users.sort((a, b) => a.firstName
.toString()
.toLowerCase()
.compareTo(b.firstName.toString().toLowerCase()));
emit(UsersLoadedState(users: users));
}
}
@ -164,6 +167,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
emit(UsersLoadedState(users: users));
} else {
emit(UsersLoadingState());
currentSortOrder = "NewestToOldest";
users.sort((a, b) {
final dateA = _parseDateTime(a.createdDate);
final dateB = _parseDateTime(b.createdDate);
@ -188,6 +192,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
final dateB = _parseDateTime(b.createdDate);
return dateA.compareTo(dateB);
});
currentSortOrder = "OldestToNewest";
emit(UsersLoadedState(users: users));
}
}
@ -210,7 +215,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
final query = event.query.toLowerCase();
final filteredUsers = initialUsers.where((user) {
final fullName = "${user.firstName} ${user.lastName}".toLowerCase();
final email = user.email.toLowerCase() ;
final email = user.email.toLowerCase();
return fullName.contains(query) || email.contains(query);
}).toList();
emit(UsersLoadedState(users: filteredUsers));
@ -256,49 +261,96 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
void _filterUsersByRole(
FilterUsersByRoleEvent event, Emitter<UserTableState> emit) {
selectedRoles = event.selectedRoles.toSet();
selectedRoles = event.selectedRoles!.toSet();
final filteredUsers = initialUsers.where((user) {
if (selectedRoles.isEmpty) return true;
return selectedRoles.contains(user.roleType);
}).toList();
if (event.sortOrder == "Asc") {
currentSortOrder = "Asc";
filteredUsers.sort((a, b) => a.firstName
.toString()
.toLowerCase()
.compareTo(b.firstName.toString().toLowerCase()));
} else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else {
currentSortOrder = "";
}
emit(UsersLoadedState(users: filteredUsers));
}
void _filterUsersByJobTitle(
FilterUsersByJobEvent event, Emitter<UserTableState> emit) {
selectedJobTitles = event.selectedJob.toSet();
selectedJobTitles = event.selectedJob!.toSet();
emit(UsersLoadingState());
final filteredUsers = initialUsers.where((user) {
if (selectedJobTitles.isEmpty) return true;
return selectedJobTitles.contains(user.jobTitle);
}).toList();
if (event.sortOrder == "Asc") {
currentSortOrder = "Asc";
filteredUsers.sort((a, b) => a.firstName
.toString()
.toLowerCase()
.compareTo(b.firstName.toString().toLowerCase()));
} else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else {
currentSortOrder = "";
}
emit(UsersLoadedState(users: filteredUsers));
}
void _filterUsersByCreated(
FilterUsersByCreatedEvent event, Emitter<UserTableState> emit) {
selectedCreatedBy = event.selectedCreatedBy.toSet();
selectedCreatedBy = event.selectedCreatedBy!.toSet();
final filteredUsers = initialUsers.where((user) {
if (selectedCreatedBy.isEmpty) return true;
return selectedCreatedBy.contains(user.invitedBy);
}).toList();
if (event.sortOrder == "Asc") {
currentSortOrder = "Asc";
filteredUsers.sort((a, b) => a.firstName
.toString()
.toLowerCase()
.compareTo(b.firstName.toString().toLowerCase()));
} else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else {
currentSortOrder = "";
}
emit(UsersLoadedState(users: filteredUsers));
}
void _filterUserStatus(
FilterUsersByDeActevateEvent event, Emitter<UserTableState> emit) {
selectedStatuses = event.selectedActivate.toSet();
selectedStatuses = event.selectedActivate!.toSet();
final filteredUsers = initialUsers.where((user) {
if (selectedStatuses.isEmpty) return true;
return selectedStatuses.contains(user.status);
}).toList();
if (event.sortOrder == "Asc") {
currentSortOrder = "Asc";
filteredUsers.sort((a, b) => a.firstName
.toString()
.toLowerCase()
.compareTo(b.firstName.toString().toLowerCase()));
} else if (event.sortOrder == "Desc") {
currentSortOrder = "Desc";
filteredUsers.sort((a, b) => b.firstName!.compareTo(a.firstName!));
} else {
currentSortOrder = "";
}
emit(UsersLoadedState(users: filteredUsers));
}

View File

@ -89,35 +89,36 @@ class DeleteUserEvent extends UserTableEvent {
}
class FilterUsersByRoleEvent extends UserTableEvent {
final List<String> selectedRoles;
final List<String>? selectedRoles;
final String? sortOrder;
FilterUsersByRoleEvent(this.selectedRoles);
@override
List<Object?> get props => [selectedRoles];
const FilterUsersByRoleEvent({this.selectedRoles, this.sortOrder});
List<Object?> get props => [selectedRoles, sortOrder];
}
class FilterUsersByJobEvent extends UserTableEvent {
final List<String> selectedJob;
final List<String>? selectedJob;
final String? sortOrder;
FilterUsersByJobEvent(this.selectedJob);
@override
List<Object?> get props => [selectedJob];
const FilterUsersByJobEvent({this.selectedJob, this.sortOrder});
List<Object?> get props => [selectedJob, sortOrder];
}
class FilterUsersByCreatedEvent extends UserTableEvent {
final List<String> selectedCreatedBy;
final List<String>? selectedCreatedBy;
FilterUsersByCreatedEvent(this.selectedCreatedBy);
@override
List<Object?> get props => [selectedCreatedBy];
final String? sortOrder;
const FilterUsersByCreatedEvent({this.selectedCreatedBy, this.sortOrder});
List<Object?> get props => [selectedCreatedBy, sortOrder];
}
class FilterUsersByDeActevateEvent extends UserTableEvent {
final List<String> selectedActivate;
final List<String>? selectedActivate;
final String? sortOrder;
FilterUsersByDeActevateEvent(this.selectedActivate);
@override
List<Object?> get props => [selectedActivate];
const FilterUsersByDeActevateEvent({this.selectedActivate, this.sortOrder});
List<Object?> get props => [selectedActivate, sortOrder];
}
class FilterOptionsEvent extends UserTableEvent {

View File

@ -19,19 +19,13 @@ class DynamicTableScreen extends StatefulWidget {
class _DynamicTableScreenState extends State<DynamicTableScreen>
with WidgetsBindingObserver {
late List<double> columnWidths;
late double totalWidth;
// @override
// void initState() {
// super.initState();
// // Initialize column widths with default sizes proportional to the screen width
// // Assigning placeholder values here. The actual sizes will be updated in `build`.
// }
@override
void initState() {
super.initState();
setState(() {
columnWidths = List<double>.filled(widget.titles.length, 150.0);
});
columnWidths = List<double>.filled(widget.titles.length, 150.0);
totalWidth = columnWidths.reduce((a, b) => a + b);
WidgetsBinding.instance.addObserver(this);
}
@ -44,7 +38,6 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
@override
void didChangeMetrics() {
super.didChangeMetrics();
// Screen size might have changed
final newScreenWidth = MediaQuery.of(context).size.width;
setState(() {
columnWidths = List<double>.generate(widget.titles.length, (index) {
@ -53,7 +46,7 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
0.12; // 20% of screen width for the second column
} else if (index == 9) {
return newScreenWidth *
0.2; // 25% of screen width for the tenth column
0.1; // 25% of screen width for the tenth column
}
return newScreenWidth *
0.09; // Default to 10% of screen width for other columns
@ -64,293 +57,204 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
@override
Widget build(BuildContext context) {
final screenWidth = MediaQuery.of(context).size.width;
// Initialize column widths if they are still set to placeholder values
if (columnWidths.every((width) => width == 120.0)) {
if (columnWidths.every((width) => width == screenWidth * 7)) {
columnWidths = List<double>.generate(widget.titles.length, (index) {
if (index == 1) {
return screenWidth * 0.11;
} else if (index == 9) {
return screenWidth * 0.2;
return screenWidth * 0.1;
}
return screenWidth * 0.11;
return screenWidth * 0.09;
});
setState(() {});
}
return Container(
child: SingleChildScrollView(
clipBehavior: Clip.none,
scrollDirection: Axis.horizontal,
child: Container(
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.all(Radius.circular(20))),
child: FittedBox(
child: Column(
children: [
// Header Row with Resizable Columns
Container(
width: MediaQuery.of(context).size.width,
decoration: containerDecoration.copyWith(
color: ColorsManager.circleRolesBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15))),
child: Row(
children: List.generate(widget.titles.length, (index) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
FittedBox(
child: Container(
padding: const EdgeInsets.only(left: 5, right: 5),
width: columnWidths[index],
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
child: Text(
widget.titles[index],
maxLines: 2,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.w400,
fontSize: 13,
color: ColorsManager.grayColor,
),
return SingleChildScrollView(
clipBehavior: Clip.none,
scrollDirection: Axis.horizontal,
child: Container(
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.all(Radius.circular(20))),
child: FittedBox(
child: Column(
children: [
Container(
width: totalWidth,
decoration: containerDecoration.copyWith(
color: ColorsManager.circleRolesBackground,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15))),
child: Row(
children: List.generate(widget.titles.length, (index) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
FittedBox(
child: Container(
padding: const EdgeInsets.only(left: 5, right: 5),
width: columnWidths[index],
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
SizedBox(
child: Text(
widget.titles[index],
maxLines: 2,
style: const TextStyle(
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.w400,
fontSize: 13,
color: ColorsManager.grayColor,
),
),
if (index != 1 &&
index != 9 &&
index != 8 &&
index != 5)
FittedBox(
child: IconButton(
icon: SvgPicture.asset(
Assets.filterTableIcon,
fit: BoxFit.none,
),
onPressed: () {
if (widget.onFilter != null) {
widget.onFilter!(index);
}
},
),
if (index != 1 &&
index != 9 &&
index != 8 &&
index != 5)
FittedBox(
child: IconButton(
icon: SvgPicture.asset(
Assets.filterTableIcon,
fit: BoxFit.none,
),
)
],
),
),
),
GestureDetector(
onHorizontalDragUpdate: (details) {
setState(() {
columnWidths[index] = (columnWidths[index] +
details.delta.dx)
.clamp(
150.0, 300.0); // Minimum & Maximum size
});
},
child: MouseRegion(
cursor: SystemMouseCursors
.resizeColumn, // Set the cursor to resize
child: Container(
color: Colors.green,
child: Container(
color: ColorsManager.boxDivider,
width: 1,
height: 50, // Height of the header cell
),
),
),
),
],
);
}),
),
),
// Data Rows with Dividers
widget.rows.isEmpty
? Container(
child: SizedBox(
height: MediaQuery.of(context).size.height / 2,
child: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SvgPicture.asset(Assets.emptyTable),
const SizedBox(
height: 15,
onPressed: () {
if (widget.onFilter != null) {
widget.onFilter!(index);
}
},
),
const Text(
'No Users',
style: TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 16,
fontWeight: FontWeight.w700),
)
],
),
)
],
),
),
),
)
: Center(
child: Container(
// height: MediaQuery.of(context).size.height * 0.59,
width: MediaQuery.of(context).size.width,
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15))),
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.rows.length,
itemBuilder: (context, rowIndex) {
if (columnWidths
.every((width) => width == 120.0)) {
columnWidths = List<double>.generate(
widget.titles.length, (index) {
if (index == 1) {
return screenWidth * 0.11;
} else if (index == 9) {
return screenWidth * 0.2;
}
GestureDetector(
onHorizontalDragUpdate: (details) {
setState(() {
columnWidths[index] =
(columnWidths[index] + details.delta.dx)
.clamp(150.0, 300.0);
totalWidth = columnWidths.reduce((a, b) => a + b);
});
},
child: MouseRegion(
cursor: SystemMouseCursors.resizeColumn,
child: Container(
color: Colors.green,
child: Container(
color: ColorsManager.boxDivider,
width: 1,
height: 50,
),
),
),
),
],
);
}),
),
),
widget.rows.isEmpty
? SizedBox(
height: MediaQuery.of(context).size.height / 2,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
SvgPicture.asset(Assets.emptyTable),
const SizedBox(
height: 15,
),
const Text(
'No Users',
style: TextStyle(
color: ColorsManager.lightGrayColor,
fontSize: 16,
fontWeight: FontWeight.w700),
)
],
),
],
),
)
: Center(
child: Container(
width: totalWidth,
decoration: containerDecoration.copyWith(
color: ColorsManager.whiteColors,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(15),
bottomRight: Radius.circular(15))),
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: widget.rows.length,
itemBuilder: (context, rowIndex) {
if (columnWidths.every((width) => width == 120.0)) {
columnWidths = List<double>.generate(
widget.titles.length, (index) {
if (index == 1) {
return screenWidth * 0.11;
});
setState(() {});
}
final row = widget.rows[rowIndex];
return Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(
left: 5,
top: 10,
right: 5,
bottom: 10),
child: Row(
children:
List.generate(row.length, (index) {
return SizedBox(
width: columnWidths[index],
child: SizedBox(
child: Padding(
padding: const EdgeInsets.only(
left: 15, right: 10),
child: row[index],
),
),
);
}),
),
),
),
if (rowIndex < widget.rows.length - 1)
Row(
children: List.generate(
widget.titles.length, (index) {
} else if (index == 9) {
return screenWidth * 0.2;
}
return screenWidth * 0.11;
});
setState(() {});
}
final row = widget.rows[rowIndex];
return Column(
children: [
Container(
child: Padding(
padding: const EdgeInsets.only(
left: 5, top: 10, right: 5, bottom: 10),
child: Row(
children:
List.generate(row.length, (index) {
return SizedBox(
width: columnWidths[index],
child: const Divider(
color: ColorsManager.boxDivider,
thickness: 1,
height: 1,
child: SizedBox(
child: Padding(
padding: const EdgeInsets.only(
left: 15, right: 10),
child: row[index],
),
),
);
}),
),
],
);
},
),
),
),
if (rowIndex < widget.rows.length - 1)
Row(
children: List.generate(
widget.titles.length, (index) {
return SizedBox(
width: columnWidths[index],
child: const Divider(
color: ColorsManager.boxDivider,
thickness: 1,
height: 1,
),
);
}),
),
],
);
},
),
),
],
),
),
],
),
),
),
);
}
}
// Widget build(BuildContext context) {
// return Scaffold(
// body: SingleChildScrollView(
// scrollDirection: Axis.horizontal,
// child: SingleChildScrollView(
// scrollDirection: Axis.vertical,
// child: Column(
// children: [
// // Header Row with Resizable Columns
// Container(
// color: Colors.green,
// child: Row(
// children: List.generate(widget.titles.length, (index) {
// return Row(
// children: [
// Container(
// width: columnWidths[index],
// decoration: const BoxDecoration(
// color: Colors.green,
// ),
// child: Text(
// widget.titles[index],
// style: TextStyle(fontWeight: FontWeight.bold),
// textAlign: TextAlign.center,
// ),
// ),
// GestureDetector(
// onHorizontalDragUpdate: (details) {
// setState(() {
// columnWidths[index] = (columnWidths[index] +
// details.delta.dx)
// .clamp(50.0, 300.0); // Minimum & Maximum size
// });
// },
// child: MouseRegion(
// cursor: SystemMouseCursors
// .resizeColumn, // Set the cursor to resize
// child: Container(
// color: Colors.green,
// child: Container(
// color: Colors.black,
// width: 1,
// height: 50, // Height of the header cell
// ),
// ),
// ),
// ),
// ],
// );
// }),
// ),
// ),
// // Data Rows
// ...widget.rows.map((row) {
// return Row(
// children: List.generate(row.length, (index) {
// return Container(
// width: columnWidths[index],
// child: row[index],
// );
// }),
// );
// }).toList(),
// ],
// ),
// ),
// ),
// );
// }

View File

@ -107,7 +107,6 @@ class UsersPage extends StatelessWidget {
builder: (context, state) {
final screenSize = MediaQuery.of(context).size;
final _blocRole = BlocProvider.of<UserTableBloc>(context);
if (state is UsersLoadingState) {
_blocRole.add(ChangePage(_blocRole.currentPage));
return const Center(child: CircularProgressIndicator());
@ -189,8 +188,6 @@ class UsersPage extends StatelessWidget {
const SizedBox(height: 25),
DynamicTableScreen(
onFilter: (columnIndex) {
_blocRole.add(FilterClearEvent());
if (columnIndex == 0) {
showNameMenu(
context: context,
@ -210,11 +207,12 @@ class UsersPage extends StatelessWidget {
if (columnIndex == 2) {
final Map<String, bool> checkboxStates = {
for (var item in _blocRole.jobTitle)
item: false, // Initialize with false
item: _blocRole.selectedJobTitles.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
.findRenderObject() as RenderBox;
showPopUpFilterMenu(
position: RelativeRect.fromLTRB(
overlay.size.width / 4,
@ -225,26 +223,28 @@ class UsersPage extends StatelessWidget {
list: _blocRole.jobTitle,
context: context,
checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder,
onOkPressed: () {
_blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
Navigator.of(context).pop();
_blocRole.add(FilterUsersByJobEvent(selectedItems));
_blocRole.add(FilterUsersByJobEvent(
selectedJob: selectedItems,
sortOrder: _blocRole.currentSortOrder,
));
},
onSortAtoZ: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
onSortAtoZ: (v) {
_blocRole.currentSortOrder = v;
},
onSortZtoA: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
onSortZtoA: (v) {
_blocRole.currentSortOrder = v;
},
);
}
if (columnIndex == 3) {
final Map<String, bool> checkboxStates = {
for (var item in _blocRole.roleTypes)
@ -263,32 +263,31 @@ class UsersPage extends StatelessWidget {
list: _blocRole.roleTypes,
context: context,
checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder,
onOkPressed: () {
_blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
Navigator.of(context).pop();
context
.read<UserTableBloc>()
.add(FilterUsersByRoleEvent(selectedItems));
context.read<UserTableBloc>().add(
FilterUsersByRoleEvent(
selectedRoles: selectedItems,
sortOrder: _blocRole.currentSortOrder));
},
onSortAtoZ: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
onSortAtoZ: (v) {
_blocRole.currentSortOrder = v;
},
onSortZtoA: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
onSortZtoA: (v) {
_blocRole.currentSortOrder = v;
},
);
}
if (columnIndex == 4) {
showDateFilterMenu(
context: context,
isSelected: _blocRole.currentSortOrderDate,
isSelected: _blocRole.currentSortOrder,
aToZTap: () {
context
.read<UserTableBloc>()
@ -319,32 +318,30 @@ class UsersPage extends StatelessWidget {
list: _blocRole.createdBy,
context: context,
checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder,
onOkPressed: () {
_blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
Navigator.of(context).pop();
_blocRole
.add(FilterUsersByCreatedEvent(selectedItems));
_blocRole.add(FilterUsersByCreatedEvent(
selectedCreatedBy: selectedItems,
sortOrder: _blocRole.currentSortOrder));
},
onSortAtoZ: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
onSortAtoZ: (v) {
_blocRole.currentSortOrder = v;
},
onSortZtoA: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
onSortZtoA: (v) {
_blocRole.currentSortOrder = v;
},
);
}
if (columnIndex == 7) {
final Map<String, bool> checkboxStates = {
for (var item in _blocRole.status)
item: _blocRole.selectedCreatedBy.contains(item),
item: _blocRole.selectedStatuses.contains(item),
};
final RenderBox overlay = Overlay.of(context)
.context
@ -359,24 +356,24 @@ class UsersPage extends StatelessWidget {
list: _blocRole.status,
context: context,
checkboxStates: checkboxStates,
isSelected: _blocRole.currentSortOrder,
onOkPressed: () {
_blocRole.add(FilterClearEvent());
final selectedItems = checkboxStates.entries
.where((entry) => entry.value)
.map((entry) => entry.key)
.toList();
Navigator.of(context).pop();
_blocRole
.add(FilterUsersByCreatedEvent(selectedItems));
_blocRole.add(FilterUsersByDeActevateEvent(
selectedActivate: selectedItems,
sortOrder: _blocRole.currentSortOrder));
},
onSortAtoZ: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameAsc());
onSortAtoZ: (v) {
_blocRole.currentSortOrder = v;
},
onSortZtoA: () {
context
.read<UserTableBloc>()
.add(const SortUsersByNameDesc());
onSortZtoA: (v) {
_blocRole.currentSortOrder = v;
},
);
}
@ -412,8 +409,8 @@ class UsersPage extends StatelessWidget {
rows: state.users.map((user) {
return [
Text('${user.firstName} ${user.lastName}'),
Text(user.email ),
Text(user.jobTitle ?? ''),
Text(user.email),
Text(user.jobTitle ?? '-'),
Text(user.roleType ?? ''),
Text(user.createdDate ?? ''),
Text(user.createdTime ?? ''),
@ -476,11 +473,17 @@ class UsersPage extends StatelessWidget {
barrierDismissible: false,
builder: (BuildContext context) {
return DeleteUserDialog(
onTapDelete: () {
onTapDelete: () async {
try {
_blocRole.add(DeleteUserEvent(
user.uuid, context));
},
);
await Future.delayed(
const Duration(seconds: 2));
return true;
} catch (e) {
return false;
}
});
},
).then((v) {
if (v != null) {
@ -504,6 +507,7 @@ class UsersPage extends StatelessWidget {
SizedBox(
width: 500,
child: NumberPagination(
visiblePagesCount: 4,
buttonRadius: 10,
selectedButtonColor: ColorsManager.secondaryColor,
buttonUnSelectedBorderColor:

View File

@ -71,8 +71,8 @@ class UserPermissionApi {
"firstName": firstName,
"lastName": lastName,
"email": email,
"jobTitle": jobTitle != '' ? jobTitle : " ",
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
"jobTitle": jobTitle != '' ? jobTitle : null,
"phoneNumber": phoneNumber != '' ? phoneNumber : null,
"roleUuid": roleUuid,
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c",
"spaceUuids": spaceUuids,
@ -119,13 +119,8 @@ class UserPermissionApi {
);
return response ?? 'Unknown error occurred';
} on DioException catch (e) {
if (e.response != null) {
final errorMessage = e.response?.data['error'];
return errorMessage is String
? errorMessage
: 'Error occurred while checking email';
}
return 'Error occurred while checking email';
final errorMessage = e.response?.data['error'];
return errorMessage;
} catch (e) {
return e.toString();
}