mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 14:47:23 +00:00
edit_user and pagination and search and filter
This commit is contained in:
267
lib/pages/roles_and_permission/model/edit_user_model.dart
Normal file
267
lib/pages/roles_and_permission/model/edit_user_model.dart
Normal file
@ -0,0 +1,267 @@
|
||||
// import 'dart:convert';
|
||||
|
||||
// // Model for Space
|
||||
// class UserSpaceModel {
|
||||
// final String uuid;
|
||||
// final DateTime createdAt;
|
||||
// final DateTime updatedAt;
|
||||
|
||||
// UserSpaceModel({
|
||||
// required this.uuid,
|
||||
// required this.createdAt,
|
||||
// required this.updatedAt,
|
||||
// });
|
||||
|
||||
// factory UserSpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
// return UserSpaceModel(
|
||||
// uuid: json['uuid'],
|
||||
// createdAt: DateTime.parse(json['createdAt']),
|
||||
// updatedAt: DateTime.parse(json['updatedAt']),
|
||||
// );
|
||||
// }
|
||||
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'uuid': uuid,
|
||||
// 'createdAt': createdAt.toIso8601String(),
|
||||
// 'updatedAt': updatedAt.toIso8601String(),
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Model for User
|
||||
// class EditUserModel {
|
||||
// final String uuid;
|
||||
// final DateTime createdAt;
|
||||
// final dynamic email;
|
||||
// final dynamic? jobTitle;
|
||||
// final dynamic status;
|
||||
// final String firstName;
|
||||
// final String lastName;
|
||||
// final String? phoneNumber;
|
||||
// final bool isEnabled;
|
||||
// final dynamic invitedBy;
|
||||
// final dynamic roleType;
|
||||
// final List<UserSpaceModel> spaces;
|
||||
// final String createdDate;
|
||||
// final String createdTime;
|
||||
|
||||
// EditUserModel({
|
||||
// required this.uuid,
|
||||
// required this.createdAt,
|
||||
// required this.email,
|
||||
// this.jobTitle,
|
||||
// required this.status,
|
||||
// required this.firstName,
|
||||
// required this.lastName,
|
||||
// this.phoneNumber,
|
||||
// required this.isEnabled,
|
||||
// required this.invitedBy,
|
||||
// required this.roleType,
|
||||
// required this.spaces,
|
||||
// required this.createdDate,
|
||||
// required this.createdTime,
|
||||
// });
|
||||
|
||||
// factory EditUserModel.fromJson(Map<String, dynamic> json) {
|
||||
// var spacesList = (json['spaces'] as List)
|
||||
// .map((spaceJson) => UserSpaceModel.fromJson(spaceJson))
|
||||
// .toList();
|
||||
|
||||
// return EditUserModel(
|
||||
// uuid: json['uuid'],
|
||||
// createdAt: DateTime.parse(json['createdAt']),
|
||||
// email: json['email'],
|
||||
// jobTitle: json['jobTitle'],
|
||||
// status: json['status'],
|
||||
// firstName: json['firstName'],
|
||||
// lastName: json['lastName'],
|
||||
// phoneNumber: json['phoneNumber'],
|
||||
// isEnabled: json['isEnabled'],
|
||||
// invitedBy: json['invitedBy'],
|
||||
// roleType: json['roleType'],
|
||||
// spaces: spacesList,
|
||||
// createdDate: json['createdDate'],
|
||||
// createdTime: json['createdTime'],
|
||||
// );
|
||||
// }
|
||||
|
||||
// Map<String, dynamic> toJson() {
|
||||
// return {
|
||||
// 'uuid': uuid,
|
||||
// 'createdAt': createdAt.toIso8601String(),
|
||||
// 'email': email,
|
||||
// 'jobTitle': jobTitle,
|
||||
// 'status': status,
|
||||
// 'firstName': firstName,
|
||||
// 'lastName': lastName,
|
||||
// 'phoneNumber': phoneNumber,
|
||||
// 'isEnabled': isEnabled,
|
||||
// 'invitedBy': invitedBy,
|
||||
// 'roleType': roleType,
|
||||
// 'spaces': spaces.map((space) => space.toJson()).toList(),
|
||||
// 'createdDate': createdDate,
|
||||
// 'createdTime': createdTime,
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
|
||||
class UserProjectResponse {
|
||||
final int statusCode;
|
||||
final String message;
|
||||
final EditUserModel data;
|
||||
final bool success;
|
||||
|
||||
UserProjectResponse({
|
||||
required this.statusCode,
|
||||
required this.message,
|
||||
required this.data,
|
||||
required this.success,
|
||||
});
|
||||
|
||||
/// Create a [UserProjectResponse] from JSON data
|
||||
factory UserProjectResponse.fromJson(Map<String, dynamic> json) {
|
||||
return UserProjectResponse(
|
||||
statusCode: json['statusCode'] as int,
|
||||
message: json['message'] as String,
|
||||
data: EditUserModel.fromJson(json['data'] as Map<String, dynamic>),
|
||||
success: json['success'] as bool,
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert the [UserProjectResponse] to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'statusCode': statusCode,
|
||||
'message': message,
|
||||
'data': data.toJson(),
|
||||
'success': success,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class EditUserModel {
|
||||
final String uuid;
|
||||
final String firstName;
|
||||
final String lastName;
|
||||
final String email;
|
||||
final String createdDate; // e.g. "1/3/2025"
|
||||
final String createdTime; // e.g. "8:41:43 AM"
|
||||
final String status; // e.g. "invited"
|
||||
final String invitedBy; // e.g. "SUPER_ADMIN"
|
||||
final String phoneNumber; // can be empty
|
||||
final String jobTitle; // can be empty
|
||||
final String roleType; // e.g. "ADMIN"
|
||||
final List<UserSpaceModel> spaces;
|
||||
|
||||
EditUserModel({
|
||||
required this.uuid,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.email,
|
||||
required this.createdDate,
|
||||
required this.createdTime,
|
||||
required this.status,
|
||||
required this.invitedBy,
|
||||
required this.phoneNumber,
|
||||
required this.jobTitle,
|
||||
required this.roleType,
|
||||
required this.spaces,
|
||||
});
|
||||
|
||||
/// Create a [UserData] from JSON data
|
||||
factory EditUserModel.fromJson(Map<String, dynamic> json) {
|
||||
return EditUserModel(
|
||||
uuid: json['uuid'] as String,
|
||||
firstName: json['firstName'] as String,
|
||||
lastName: json['lastName'] as String,
|
||||
email: json['email'] as String,
|
||||
createdDate: json['createdDate'] as String,
|
||||
createdTime: json['createdTime'] as String,
|
||||
status: json['status'] as String,
|
||||
invitedBy: json['invitedBy'] as String,
|
||||
phoneNumber: json['phoneNumber'] ?? '',
|
||||
jobTitle: json['jobTitle'] ?? '',
|
||||
roleType: json['roleType'] as String,
|
||||
spaces: (json['spaces'] as List<dynamic>)
|
||||
.map((e) => UserSpaceModel.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert the [UserData] to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'firstName': firstName,
|
||||
'lastName': lastName,
|
||||
'email': email,
|
||||
'createdDate': createdDate,
|
||||
'createdTime': createdTime,
|
||||
'status': status,
|
||||
'invitedBy': invitedBy,
|
||||
'phoneNumber': phoneNumber,
|
||||
'jobTitle': jobTitle,
|
||||
'roleType': roleType,
|
||||
'spaces': spaces.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class UserSpaceModel {
|
||||
final String uuid;
|
||||
final String createdAt; // e.g. "2024-11-04T07:20:35.940Z"
|
||||
final String updatedAt; // e.g. "2024-11-28T18:47:29.736Z"
|
||||
final dynamic spaceTuyaUuid;
|
||||
final dynamic spaceName;
|
||||
final dynamic invitationCode;
|
||||
final bool disabled;
|
||||
final double x;
|
||||
final double y;
|
||||
final String icon;
|
||||
|
||||
UserSpaceModel({
|
||||
required this.uuid,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
required this.spaceTuyaUuid,
|
||||
required this.spaceName,
|
||||
required this.invitationCode,
|
||||
required this.disabled,
|
||||
required this.x,
|
||||
required this.y,
|
||||
required this.icon,
|
||||
});
|
||||
|
||||
/// Create a [UserSpaceModel] from JSON data
|
||||
factory UserSpaceModel.fromJson(Map<String, dynamic> json) {
|
||||
return UserSpaceModel(
|
||||
uuid: json['uuid'] as String,
|
||||
createdAt: json['createdAt'] as String,
|
||||
updatedAt: json['updatedAt'] as String,
|
||||
spaceTuyaUuid: json['spaceTuyaUuid'] as String?,
|
||||
spaceName: json['spaceName'] as String,
|
||||
invitationCode: json['invitationCode'] as String?,
|
||||
disabled: json['disabled'] as bool,
|
||||
x: (json['x'] as num).toDouble(),
|
||||
y: (json['y'] as num).toDouble(),
|
||||
icon: json['icon'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert the [UserSpaceModel] to JSON
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'createdAt': createdAt,
|
||||
'updatedAt': updatedAt,
|
||||
'spaceTuyaUuid': spaceTuyaUuid,
|
||||
'spaceName': spaceName,
|
||||
'invitationCode': invitationCode,
|
||||
'disabled': disabled,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'icon': icon,
|
||||
};
|
||||
}
|
||||
}
|
@ -1,22 +1,50 @@
|
||||
class RolesUserModel {
|
||||
String? id;
|
||||
String? userName;
|
||||
String? userEmail;
|
||||
String? userRole;
|
||||
String? creationDate;
|
||||
String? creationTime;
|
||||
String? createdBy;
|
||||
String? status;
|
||||
String? action;
|
||||
RolesUserModel(
|
||||
{this.id,
|
||||
this.userName,
|
||||
this.userEmail,
|
||||
this.userRole,
|
||||
this.creationDate,
|
||||
this.creationTime,
|
||||
this.status,
|
||||
this.action,
|
||||
this.createdBy,
|
||||
});
|
||||
final String uuid;
|
||||
final DateTime createdAt;
|
||||
final String email;
|
||||
final dynamic firstName;
|
||||
final dynamic lastName;
|
||||
final dynamic roleType;
|
||||
final dynamic status;
|
||||
final bool isEnabled;
|
||||
final String invitedBy;
|
||||
final dynamic phoneNumber;
|
||||
final dynamic jobTitle;
|
||||
final dynamic createdDate;
|
||||
final dynamic createdTime;
|
||||
|
||||
RolesUserModel({
|
||||
required this.uuid,
|
||||
required this.createdAt,
|
||||
required this.email,
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
required this.roleType,
|
||||
required this.status,
|
||||
required this.isEnabled,
|
||||
required this.invitedBy,
|
||||
this.phoneNumber,
|
||||
this.jobTitle,
|
||||
required this.createdDate,
|
||||
required this.createdTime,
|
||||
});
|
||||
|
||||
factory RolesUserModel.fromJson(Map<String, dynamic> json) {
|
||||
return RolesUserModel(
|
||||
uuid: json['uuid'],
|
||||
createdAt: DateTime.parse(json['createdAt']),
|
||||
email: json['email'],
|
||||
firstName: json['firstName'],
|
||||
lastName: json['lastName'],
|
||||
roleType: json['roleType'].toString().toLowerCase().replaceAll("_", " "),
|
||||
status: json['status'],
|
||||
isEnabled: json['isEnabled'],
|
||||
invitedBy:
|
||||
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
||||
phoneNumber: json['phoneNumber'],
|
||||
jobTitle: json['jobTitle'].toString(),
|
||||
createdDate: json['createdDate'],
|
||||
createdTime: json['createdTime'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/common/custom_dialog.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/edit_user_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.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';
|
||||
@ -25,6 +26,11 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
on<ValidateBasicsStep>(_validateBasicsStep);
|
||||
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
||||
on<CheckEmailEvent>(checkEmail);
|
||||
on<GetUserByIdEvent>(getUserById);
|
||||
on<ToggleNodeExpansion>(_onToggleNodeExpansion);
|
||||
on<ToggleNodeCheck>(_onToggleNodeCheck);
|
||||
on<EditInviteUsers>(_editInvitUser);
|
||||
|
||||
}
|
||||
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
||||
if (formKey.currentState?.validate() ?? false) {
|
||||
@ -42,9 +48,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
final TextEditingController emailController = TextEditingController();
|
||||
final TextEditingController phoneController = TextEditingController();
|
||||
final TextEditingController jobTitleController = TextEditingController();
|
||||
|
||||
final TextEditingController roleSearchController = TextEditingController();
|
||||
// final TextEditingController jobTitleController = TextEditingController();
|
||||
|
||||
bool? isCompleteBasics;
|
||||
bool? isCompleteRolePermissions;
|
||||
@ -84,6 +88,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
await CommunitySpaceManagementApi().fetchCommunities();
|
||||
updatedCommunities = await Future.wait(
|
||||
communities.map((community) async {
|
||||
print(community.uuid);
|
||||
List<SpaceModel> spaces =
|
||||
await _fetchSpacesForCommunity(community.uuid);
|
||||
spacesNodes = _buildTreeNodes(spaces);
|
||||
@ -228,8 +233,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(event.context).pop();
|
||||
Navigator.of(event.context).pop();
|
||||
Navigator.of(event.context).pop(true);
|
||||
Navigator.of(event.context).pop(true);
|
||||
},
|
||||
child: const Text('OK'),
|
||||
),
|
||||
@ -244,6 +249,48 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
}
|
||||
}
|
||||
|
||||
_editInvitUser(EditInviteUsers event, Emitter<UsersState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities) ?? [];
|
||||
bool res = await UserPermissionApi().editInviteUser(
|
||||
userId: event.userId,
|
||||
firstName: firstNameController.text,
|
||||
jobTitle: jobTitleController.text,
|
||||
lastName: lastNameController.text,
|
||||
phoneNumber: phoneController.text,
|
||||
roleUuid: roleSelected,
|
||||
spaceUuids: selectedIds,
|
||||
);
|
||||
if (res == true) {
|
||||
showCustomDialog(
|
||||
barrierDismissible: false,
|
||||
context: event.context,
|
||||
message: "The invite was sent successfully.",
|
||||
iconPath: Assets.deviceNoteIcon,
|
||||
title: "Invite Success",
|
||||
dialogHeight: MediaQuery.of(event.context).size.height * 0.3,
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(event.context).pop(true);
|
||||
Navigator.of(event.context).pop(true);
|
||||
},
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
).then(
|
||||
(value) {},
|
||||
);
|
||||
} else {
|
||||
emit(const ErrorState('Failed to send invite.'));
|
||||
}
|
||||
emit(SaveState());
|
||||
} catch (e) {
|
||||
emit(ErrorState('Failed to send invite: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
void searchRolePermission(SearchPermission event, Emitter<UsersState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
if (event.searchTerm!.isEmpty) {
|
||||
@ -268,18 +315,22 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
|
||||
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
add(const CheckEmailEvent());
|
||||
final emailRegex = RegExp(
|
||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||
);
|
||||
bool isEmailValid = emailRegex.hasMatch(emailController.text);
|
||||
bool isEmailServerValid = checkEmailValid == 'Valid email';
|
||||
isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||
lastNameController.text.isNotEmpty &&
|
||||
emailController.text.isNotEmpty &&
|
||||
isEmailValid &&
|
||||
isEmailServerValid;
|
||||
|
||||
if (event.isEditUser == false) {
|
||||
add(const CheckEmailEvent());
|
||||
final emailRegex = RegExp(
|
||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||
);
|
||||
bool isEmailValid = emailRegex.hasMatch(emailController.text);
|
||||
bool isEmailServerValid = checkEmailValid == 'Valid email';
|
||||
isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||
lastNameController.text.isNotEmpty &&
|
||||
emailController.text.isNotEmpty &&
|
||||
isEmailValid &&
|
||||
isEmailServerValid;
|
||||
} else {
|
||||
isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||
lastNameController.text.isNotEmpty;
|
||||
}
|
||||
emit(ChangeStatusSteps());
|
||||
emit(ValidateBasics());
|
||||
return isCompleteBasics!;
|
||||
@ -293,4 +344,153 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditUserModel? res = EditUserModel(
|
||||
spaces: [],
|
||||
jobTitle: '',
|
||||
phoneNumber: '',
|
||||
uuid: '',
|
||||
email: '',
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
roleType: '',
|
||||
status: '',
|
||||
invitedBy: '',
|
||||
createdDate: '',
|
||||
createdTime: '');
|
||||
|
||||
Future<void> getUserById(
|
||||
GetUserByIdEvent event,
|
||||
Emitter<UsersState> emit,
|
||||
) async {
|
||||
emit(UsersLoadingState());
|
||||
|
||||
try {
|
||||
if (event.uuid?.isNotEmpty ?? false) {
|
||||
final res = await UserPermissionApi().fetchUserById(event.uuid);
|
||||
|
||||
if (res != null) {
|
||||
// Populate the text controllers
|
||||
firstNameController.text = res.firstName;
|
||||
lastNameController.text = res.lastName;
|
||||
emailController.text = res.email;
|
||||
phoneController.text = res.phoneNumber ?? '';
|
||||
jobTitleController.text = res.jobTitle ?? '';
|
||||
res.roleType;
|
||||
if (updatedCommunities.isNotEmpty) {
|
||||
// Create a list of UUIDs to mark
|
||||
final uuidsToMark = res.spaces.map((space) => space.uuid).toList();
|
||||
// Print all IDs and mark nodes in updatedCommunities
|
||||
print('Printing and marking nodes in updatedCommunities:');
|
||||
_printAndMarkNodes(updatedCommunities, uuidsToMark);
|
||||
} else {
|
||||
print('updatedCommunities is empty!');
|
||||
}
|
||||
final roleId = roles
|
||||
.firstWhere((element) =>
|
||||
element.type ==
|
||||
res.roleType.toString().toLowerCase().replaceAll("_", " "))
|
||||
.uuid;
|
||||
print('Role ID: $roleId');
|
||||
roleSelected = roleId;
|
||||
add(PermissionEvent(roleUuid: roleSelected));
|
||||
emit(ChangeStatusSteps());
|
||||
} else {
|
||||
// emit(UsersErrorState("User not found"));
|
||||
}
|
||||
} else {
|
||||
// emit(UsersErrorState("Invalid user ID"));
|
||||
}
|
||||
} catch (e) {
|
||||
print("Failed to fetch user data: $e");
|
||||
// emit(UsersErrorState("Failed to fetch user data: $e"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively print all the node IDs, including nested children.
|
||||
/// Recursively print all node IDs and mark nodes as `isChecked` if their UUID exists in the list.
|
||||
void _printAndMarkNodes(List<TreeNode> nodes, List<String> uuidsToMark,
|
||||
[int level = 0]) {
|
||||
for (final node in nodes) {
|
||||
// Check if the current node's UUID exists in the list of UUIDs to mark.
|
||||
if (uuidsToMark.contains(node.uuid)) {
|
||||
node.isChecked = true; // Mark the node as checked.
|
||||
print(
|
||||
'${' ' * level}MATCH FOUND: Node ID: ${node.uuid}, Title: ${node.title} is marked as checked.');
|
||||
} else {
|
||||
print('${' ' * level}Node ID: ${node.uuid}, Title: ${node.title}');
|
||||
}
|
||||
if (node.children.isNotEmpty) {
|
||||
_printAndMarkNodes(node.children, uuidsToMark, level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onToggleNodeExpansion(
|
||||
ToggleNodeExpansion event,
|
||||
Emitter<UsersState> emit,
|
||||
) {
|
||||
emit(UsersLoadingState());
|
||||
event.node.isExpanded = !event.node.isExpanded;
|
||||
emit(ChangeStatusSteps());
|
||||
}
|
||||
|
||||
void _onToggleNodeCheck(
|
||||
ToggleNodeCheck event,
|
||||
Emitter<UsersState> emit,
|
||||
) {
|
||||
emit(UsersLoadingState());
|
||||
//Toggle node's checked state
|
||||
event.node.isChecked = !event.node.isChecked;
|
||||
debugPrint(
|
||||
'Node toggled. ID: ${event.node.uuid}, isChecked: ${event.node.isChecked}',
|
||||
);
|
||||
// Update children and parent
|
||||
_updateChildrenCheckStatus(event.node, event.node.isChecked);
|
||||
_updateParentCheckStatus(event.node);
|
||||
|
||||
// Finally, emit a new state
|
||||
emit(ChangeStatusSteps());
|
||||
}
|
||||
|
||||
// Existing methods that remain in the BLoC:
|
||||
|
||||
void _updateChildrenCheckStatus(TreeNode node, bool isChecked) {
|
||||
for (var child in node.children) {
|
||||
child.isChecked = isChecked;
|
||||
_updateChildrenCheckStatus(child, isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateParentCheckStatus(TreeNode node) {
|
||||
TreeNode? parent = _findParent(updatedCommunities, node);
|
||||
if (parent != null) {
|
||||
parent.isChecked = _areAllChildrenChecked(parent);
|
||||
_updateParentCheckStatus(parent);
|
||||
}
|
||||
}
|
||||
|
||||
bool _areAllChildrenChecked(TreeNode node) {
|
||||
return node.children.isNotEmpty &&
|
||||
node.children.every((child) =>
|
||||
child.isChecked &&
|
||||
(child.children.isEmpty || _areAllChildrenChecked(child)));
|
||||
}
|
||||
|
||||
// Private helper method to find the parent of a given node.
|
||||
TreeNode? _findParent(List<TreeNode> nodes, TreeNode target) {
|
||||
for (var node in nodes) {
|
||||
if (node.children.contains(target)) {
|
||||
return node;
|
||||
}
|
||||
final parent = _findParent(node.children, target);
|
||||
if (parent != null) {
|
||||
return parent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,14 @@ class SendInviteUsers extends UsersEvent {
|
||||
List<Object?> get props => [context];
|
||||
}
|
||||
|
||||
class EditInviteUsers extends UsersEvent {
|
||||
final BuildContext context;
|
||||
final String userId;
|
||||
const EditInviteUsers({required this.context, required this.userId});
|
||||
@override
|
||||
List<Object?> get props => [context, userId];
|
||||
}
|
||||
|
||||
class CheckSpacesStepStatus extends UsersEvent {
|
||||
const CheckSpacesStepStatus();
|
||||
@override
|
||||
@ -52,9 +60,11 @@ class GetBatchStatus extends UsersEvent {
|
||||
List<Object?> get props => [uuids];
|
||||
}
|
||||
|
||||
//isEditUser:widget.userId!=''? false:true
|
||||
class CheckStepStatus extends UsersEvent {
|
||||
final int? steps;
|
||||
const CheckStepStatus({this.steps});
|
||||
bool? isEditUser = false;
|
||||
CheckStepStatus({this.steps, required this.isEditUser});
|
||||
@override
|
||||
List<Object?> get props => [steps];
|
||||
}
|
||||
@ -85,7 +95,7 @@ class SelecteId extends UsersEvent {
|
||||
}
|
||||
|
||||
class ValidateBasicsStep extends UsersEvent {
|
||||
const ValidateBasicsStep();
|
||||
ValidateBasicsStep();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
@ -95,3 +105,79 @@ class CheckEmailEvent extends UsersEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GetUserByIdEvent extends UsersEvent {
|
||||
final String? uuid;
|
||||
const GetUserByIdEvent({this.uuid});
|
||||
@override
|
||||
List<Object?> get props => [uuid];
|
||||
}
|
||||
|
||||
class ToggleNodeExpansion extends UsersEvent {
|
||||
final TreeNode node;
|
||||
|
||||
ToggleNodeExpansion({required this.node});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [node];
|
||||
}
|
||||
|
||||
class UpdateNodeCheckStatus extends UsersEvent {
|
||||
final TreeNode node;
|
||||
|
||||
UpdateNodeCheckStatus({required this.node});
|
||||
@override
|
||||
List<Object?> get props => [node];
|
||||
}
|
||||
|
||||
// Define new events
|
||||
class ToggleNodeHighlightEvent extends UsersEvent {
|
||||
final TreeNode node;
|
||||
|
||||
ToggleNodeHighlightEvent(this.node);
|
||||
@override
|
||||
List<Object?> get props => [node];
|
||||
}
|
||||
|
||||
class ExpandAllNodesEvent extends UsersEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class CollapseAllNodesEvent extends UsersEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ClearSelectionsEvent extends UsersEvent {
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ToggleNodeCheckEvent extends UsersEvent {
|
||||
final TreeNode node;
|
||||
|
||||
ToggleNodeCheckEvent(this.node);
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
// users_event.dart
|
||||
|
||||
// 1. Extend UsersEvent
|
||||
class ToggleNodeCheck extends UsersEvent {
|
||||
final TreeNode node;
|
||||
|
||||
// 2. Add a constructor that takes the node to toggle
|
||||
ToggleNodeCheck(this.node);
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class EditUserEvent extends UsersEvent {
|
||||
const EditUserEvent();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
|
||||
|
||||
sealed class UsersState extends Equatable {
|
||||
const UsersState();
|
||||
@ -80,3 +81,10 @@ final class ValidateBasics extends UsersState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
class UsersLoadedState extends UsersState {
|
||||
final List<TreeNode> updatedCommunities;
|
||||
|
||||
UsersLoadedState({required this.updatedCommunities});
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
@ -113,7 +113,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
if (currentStep < 3) {
|
||||
currentStep++;
|
||||
if (currentStep == 2) {
|
||||
_blocRole.add(const CheckStepStatus());
|
||||
_blocRole.add(
|
||||
CheckStepStatus(isEditUser: false));
|
||||
} else if (currentStep == 3) {
|
||||
_blocRole
|
||||
.add(const CheckSpacesStepStatus());
|
||||
@ -150,9 +151,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
Widget _getFormContent() {
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return const BasicsView();
|
||||
return BasicsView(
|
||||
userId: '',
|
||||
);
|
||||
case 2:
|
||||
return const SpacesAccessView();
|
||||
return SpacesAccessView();
|
||||
case 3:
|
||||
return const RolesAndPermission();
|
||||
default:
|
||||
@ -169,7 +172,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
bloc.add(const CheckSpacesStepStatus());
|
||||
currentStep = step;
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
bloc.add(const ValidateBasicsStep());
|
||||
bloc.add(ValidateBasicsStep());
|
||||
});
|
||||
});
|
||||
|
||||
@ -234,7 +237,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
onTap: () {
|
||||
setState(() {
|
||||
currentStep = step;
|
||||
bloc.add(const CheckStepStatus());
|
||||
bloc.add(CheckStepStatus(isEditUser: false));
|
||||
if (step3 == 3) {
|
||||
bloc.add(const CheckRoleStepStatus());
|
||||
}
|
||||
@ -299,7 +302,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
currentStep = step;
|
||||
step3 = step;
|
||||
bloc.add(const CheckSpacesStepStatus());
|
||||
bloc.add(const CheckStepStatus());
|
||||
bloc.add(CheckStepStatus(isEditUser: false));
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
|
@ -11,7 +11,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class BasicsView extends StatelessWidget {
|
||||
const BasicsView({super.key});
|
||||
String? userId = '';
|
||||
BasicsView({super.key, this.userId});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<UsersBloc, UsersState>(builder: (context, state) {
|
||||
@ -184,10 +185,11 @@ class BasicsView extends StatelessWidget {
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: TextFormField(
|
||||
enabled: userId!=''? false:true,
|
||||
onChanged: (value) {
|
||||
Future.delayed(const Duration(milliseconds: 200), () {
|
||||
_blocRole.add(const CheckStepStatus());
|
||||
_blocRole.add(ValidateBasicsStep());
|
||||
_blocRole.add(CheckStepStatus(isEditUser:userId!=''? false:true));
|
||||
_blocRole.add( ValidateBasicsStep());
|
||||
});
|
||||
},
|
||||
controller: _blocRole.emailController,
|
||||
|
@ -0,0 +1,146 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/svg.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/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class TreeView extends StatelessWidget {
|
||||
final String? userId;
|
||||
|
||||
const TreeView({
|
||||
super.key,
|
||||
this.userId,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(context);
|
||||
debugPrint('TreeView constructed with userId = $userId');
|
||||
return BlocProvider(
|
||||
create: (_) => UsersBloc(),
|
||||
// ..add(const LoadCommunityAndSpacesEvent()),
|
||||
child: BlocConsumer<UsersBloc, UsersState>(
|
||||
listener: (context, state) {
|
||||
// if (state is SpacesLoadedState) {
|
||||
// _blocRole.add(GetUserByIdEvent(uuid: userId));
|
||||
// }
|
||||
},
|
||||
builder: (context, state) {
|
||||
if (state is UsersLoadingState) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return SingleChildScrollView(
|
||||
child: _buildTree(_blocRole.updatedCommunities, _blocRole),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTree(
|
||||
List<TreeNode> nodes,
|
||||
UsersBloc bloc, {
|
||||
int level = 0,
|
||||
}) {
|
||||
return Column(
|
||||
children: nodes.map((node) {
|
||||
return Container(
|
||||
color: node.isHighlighted ? Colors.blue.shade50 : Colors.transparent,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Row(
|
||||
children: [
|
||||
/// Checkbox (GestureDetector)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
bloc.add(ToggleNodeCheck(node));
|
||||
},
|
||||
child: Image.asset(
|
||||
_getCheckBoxImage(node),
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: level * 10.0),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
bloc.add(ToggleNodeExpansion(node: node));
|
||||
},
|
||||
child: node.children.isNotEmpty
|
||||
? SvgPicture.asset(
|
||||
node.isExpanded
|
||||
? Assets.arrowDown
|
||||
: Assets.arrowForward,
|
||||
fit: BoxFit.none,
|
||||
)
|
||||
: const SizedBox(width: 16),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Text(
|
||||
node.title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: node.isHighlighted
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (node.isExpanded)
|
||||
_buildTree(
|
||||
node.children,
|
||||
bloc,
|
||||
level: level + 1,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
String _getCheckBoxImage(TreeNode node) {
|
||||
if (node.children.isEmpty) {
|
||||
return node.isChecked ? Assets.CheckBoxChecked : Assets.emptyBox;
|
||||
}
|
||||
if (_areAllChildrenChecked(node)) {
|
||||
return Assets.CheckBoxChecked;
|
||||
} else if (_areSomeChildrenChecked(node)) {
|
||||
return Assets.rectangleCheckBox;
|
||||
} else {
|
||||
return Assets.emptyBox;
|
||||
}
|
||||
}
|
||||
|
||||
bool _areAllChildrenChecked(TreeNode node) {
|
||||
return node.children.isNotEmpty &&
|
||||
node.children.every((child) =>
|
||||
child.isChecked &&
|
||||
(child.children.isEmpty || _areAllChildrenChecked(child)));
|
||||
}
|
||||
|
||||
bool _areSomeChildrenChecked(TreeNode node) {
|
||||
return node.children.isNotEmpty &&
|
||||
node.children.any((child) =>
|
||||
child.isChecked ||
|
||||
(child.children.isNotEmpty && _areSomeChildrenChecked(child)));
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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_status.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class DeleteUserDialog extends StatefulWidget {
|
||||
const DeleteUserDialog({super.key});
|
||||
final Function()? onTapDelete;
|
||||
DeleteUserDialog({super.key, this.onTapDelete});
|
||||
|
||||
@override
|
||||
_DeleteUserDialogState createState() => _DeleteUserDialogState();
|
||||
@ -17,47 +15,94 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (BuildContext context) => UsersBloc(),
|
||||
child: BlocConsumer<UsersBloc, UsersState>(
|
||||
listener: (context, state) {},
|
||||
builder: (context, state) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(context);
|
||||
|
||||
return Dialog(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: const Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
child: Text(
|
||||
"Delete User",
|
||||
style: TextStyle(
|
||||
color: ColorsManager.red,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
return Dialog(
|
||||
child: Container(
|
||||
height: 160,
|
||||
width: 200,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
child: Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
child: Text(
|
||||
"Delete User",
|
||||
style: TextStyle(
|
||||
color: ColorsManager.red,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 25,
|
||||
right: 25,
|
||||
),
|
||||
child: Divider(),
|
||||
),
|
||||
const Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 25, right: 25, top: 10, bottom: 10),
|
||||
child: Text(
|
||||
"Are you sure you want to delete this user?",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
)),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: ColorsManager.grayBorder,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayBorder,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
Expanded(
|
||||
child: const Center(child: Text('Cancel'))),
|
||||
)),
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: widget.onTapDelete,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: ColorsManager.grayBorder,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayBorder,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: const Center(
|
||||
child: Text(
|
||||
"Are you sure you want to delete this user?",
|
||||
textAlign: TextAlign.center,
|
||||
)),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: Text('Cancel')),
|
||||
Expanded(child: Text('Delete')),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}));
|
||||
'Delete',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.red,
|
||||
),
|
||||
))),
|
||||
)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,364 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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/pages/roles_and_permission/users_page/add_user_dialog/view/basics_view.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/roles_and_permission.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/spaces_access_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class EditUserDialog extends StatefulWidget {
|
||||
final String? userId;
|
||||
const EditUserDialog({super.key, this.userId});
|
||||
|
||||
@override
|
||||
_EditUserDialogState createState() => _EditUserDialogState();
|
||||
}
|
||||
|
||||
class _EditUserDialogState extends State<EditUserDialog> {
|
||||
int currentStep = 1;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (BuildContext context) => UsersBloc()
|
||||
..add(const LoadCommunityAndSpacesEvent())
|
||||
..add(const RoleEvent())
|
||||
..add(GetUserByIdEvent(uuid: widget.userId)),
|
||||
child: BlocConsumer<UsersBloc, UsersState>(listener: (context, state) {
|
||||
if (state is SpacesLoadedState) {
|
||||
BlocProvider.of<UsersBloc>(context)
|
||||
.add(GetUserByIdEvent(uuid: widget.userId));
|
||||
}
|
||||
}, builder: (context, state) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(context);
|
||||
|
||||
return Dialog(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||
width: 900,
|
||||
child: Column(
|
||||
children: [
|
||||
// Title
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
child: Text(
|
||||
"Edit User",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorsManager.secondaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildStep1Indicator(1, "Basics", _blocRole),
|
||||
_buildStep2Indicator(2, "Spaces", _blocRole),
|
||||
_buildStep3Indicator(
|
||||
3, "Role & Permissions", _blocRole),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 1,
|
||||
color: ColorsManager.grayBorder,
|
||||
),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Expanded(
|
||||
child: _getFormContent(widget.userId),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Cancel"),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
// _blocRole.add(const CheckEmailEvent());
|
||||
|
||||
setState(() {
|
||||
if (currentStep < 3) {
|
||||
currentStep++;
|
||||
if (currentStep == 2) {
|
||||
_blocRole
|
||||
.add(CheckStepStatus(isEditUser: true));
|
||||
} else if (currentStep == 3) {
|
||||
_blocRole.add(const CheckSpacesStepStatus());
|
||||
}
|
||||
} else {
|
||||
_blocRole.add(EditInviteUsers(
|
||||
context: context,
|
||||
userId: widget.userId!));
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
currentStep < 3 ? "Next" : "Save",
|
||||
style: TextStyle(
|
||||
color: (_blocRole.isCompleteSpaces == false ||
|
||||
_blocRole.isCompleteBasics == false ||
|
||||
_blocRole.isCompleteRolePermissions ==
|
||||
false) &&
|
||||
currentStep == 3
|
||||
? ColorsManager.grayColor
|
||||
: ColorsManager.secondaryColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}));
|
||||
}
|
||||
|
||||
Widget _getFormContent(userid) {
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return BasicsView(
|
||||
userId: userid,
|
||||
);
|
||||
case 2:
|
||||
return SpacesAccessView(
|
||||
userId: userid,
|
||||
);
|
||||
case 3:
|
||||
return const RolesAndPermission();
|
||||
default:
|
||||
return Container();
|
||||
}
|
||||
}
|
||||
|
||||
int step3 = 0;
|
||||
|
||||
Widget _buildStep1Indicator(int step, String label, UsersBloc bloc) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
bloc.add(const CheckSpacesStepStatus());
|
||||
currentStep = step;
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
bloc.add(ValidateBasicsStep());
|
||||
});
|
||||
});
|
||||
|
||||
if (step3 == 3) {
|
||||
bloc.add(const CheckRoleStepStatus());
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
currentStep == step
|
||||
? Assets.currentProcessIcon
|
||||
: bloc.isCompleteBasics == false
|
||||
? Assets.wrongProcessIcon
|
||||
: bloc.isCompleteBasics == true
|
||||
? Assets.completeProcessIcon
|
||||
: Assets.uncomplete_ProcessIcon,
|
||||
width: 25,
|
||||
height: 25,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (step != 3)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep2Indicator(int step, String label, UsersBloc bloc) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
currentStep = step;
|
||||
bloc.add(CheckStepStatus(isEditUser: true));
|
||||
if (step3 == 3) {
|
||||
bloc.add(const CheckRoleStepStatus());
|
||||
}
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
currentStep == step
|
||||
? Assets.currentProcessIcon
|
||||
: bloc.isCompleteSpaces == false
|
||||
? Assets.wrongProcessIcon
|
||||
: bloc.isCompleteSpaces == true
|
||||
? Assets.completeProcessIcon
|
||||
: Assets.uncomplete_ProcessIcon,
|
||||
width: 25,
|
||||
height: 25,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (step != 3)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStep3Indicator(int step, String label, UsersBloc bloc) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
currentStep = step;
|
||||
step3 = step;
|
||||
bloc.add(const CheckSpacesStepStatus());
|
||||
bloc.add(CheckStepStatus(isEditUser: true));
|
||||
});
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
currentStep == step
|
||||
? Assets.currentProcessIcon
|
||||
: bloc.isCompleteRolePermissions == false
|
||||
? Assets.wrongProcessIcon
|
||||
: bloc.isCompleteRolePermissions == true
|
||||
? Assets.completeProcessIcon
|
||||
: Assets.uncomplete_ProcessIcon,
|
||||
width: 25,
|
||||
height: 25,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: currentStep == step
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.greyColor,
|
||||
fontWeight: currentStep == step
|
||||
? FontWeight.bold
|
||||
: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (step != 3)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 12),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 1,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
Future<void> showPopUpFilterMenu({
|
||||
required BuildContext context,
|
||||
Function()? onSortAtoZ,
|
||||
Function()? onSortZtoA,
|
||||
Function()? cancelButton,
|
||||
required Map<String, bool> checkboxStates,
|
||||
Function()? onOkPressed,
|
||||
List<String>? list,
|
||||
}) async {
|
||||
final RenderBox overlay =
|
||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
|
||||
await showMenu(
|
||||
context: context,
|
||||
position: RelativeRect.fromLTRB(
|
||||
overlay.size.width / 4,
|
||||
240,
|
||||
overlay.size.width / 4,
|
||||
0,
|
||||
),
|
||||
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),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
width: 400,
|
||||
child: ListView.builder(
|
||||
itemCount: list?.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final item = list![index];
|
||||
return CheckboxListTile(
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
@ -4,14 +4,15 @@ import 'package:flutter_svg/svg.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/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class SpacesAccessView extends StatelessWidget {
|
||||
const SpacesAccessView({super.key});
|
||||
String? userId = '';
|
||||
SpacesAccessView({super.key, this.userId});
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = MediaQuery.of(context).size;
|
||||
@ -109,9 +110,7 @@ class SpacesAccessView extends StatelessWidget {
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
color: ColorsManager.whiteColors,
|
||||
child: TreeView(
|
||||
bloc: _blocRole,
|
||||
))))
|
||||
child: TreeView(userId: userId))))
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -123,155 +122,3 @@ class SpacesAccessView extends StatelessWidget {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
|
||||
class TreeView extends StatefulWidget {
|
||||
UsersBloc? bloc;
|
||||
TreeView({super.key, this.bloc});
|
||||
@override
|
||||
_TreeViewState createState() => _TreeViewState();
|
||||
}
|
||||
|
||||
class _TreeViewState extends State<TreeView> {
|
||||
Widget _buildTree(List<TreeNode> nodes, {int level = 0}) {
|
||||
return Column(
|
||||
children: nodes.map((node) => _buildNode(node, level: level)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildNode(TreeNode node, {int level = 0}) {
|
||||
return Container(
|
||||
color: node.isHighlighted ? Colors.blue.shade50 : Colors.transparent,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5.0),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
node.isChecked = !node.isChecked;
|
||||
_updateChildrenCheckStatus(node, node.isChecked);
|
||||
_updateParentCheckStatus(node);
|
||||
// widget.bloc!.add(
|
||||
// SelecteId(nodes: widget.bloc!.updatedCommunities));
|
||||
});
|
||||
},
|
||||
child: Image.asset(
|
||||
_getCheckBoxImage(node),
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 15),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: level * 10.0),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
node.isExpanded = !node.isExpanded;
|
||||
});
|
||||
},
|
||||
child: SizedBox(
|
||||
child: SvgPicture.asset(
|
||||
node.children.isNotEmpty
|
||||
? (node.isExpanded
|
||||
? Assets.arrowDown
|
||||
: Assets.arrowForward)
|
||||
: Assets.arrowForward,
|
||||
fit: BoxFit.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Text(
|
||||
node.title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: node.isHighlighted
|
||||
? ColorsManager.blackColor
|
||||
: ColorsManager.textGray,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (node.isExpanded && node.children.isNotEmpty)
|
||||
_buildTree(node.children, level: level + 1),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _getCheckBoxImage(TreeNode node) {
|
||||
if (node.children.isEmpty) {
|
||||
return node.isChecked ? Assets.CheckBoxChecked : Assets.emptyBox;
|
||||
}
|
||||
if (_areAllChildrenChecked(node)) {
|
||||
return Assets.CheckBoxChecked;
|
||||
} else if (_areSomeChildrenChecked(node)) {
|
||||
return Assets.rectangleCheckBox;
|
||||
} else {
|
||||
return Assets.emptyBox;
|
||||
}
|
||||
}
|
||||
|
||||
bool _areAllChildrenChecked(TreeNode node) {
|
||||
return node.children.isNotEmpty &&
|
||||
node.children.every((child) =>
|
||||
child.isChecked &&
|
||||
(child.children.isEmpty || _areAllChildrenChecked(child)));
|
||||
}
|
||||
|
||||
bool _areSomeChildrenChecked(TreeNode node) {
|
||||
return node.children.isNotEmpty &&
|
||||
node.children.any((child) =>
|
||||
child.isChecked ||
|
||||
(child.children.isNotEmpty && _areSomeChildrenChecked(child)));
|
||||
}
|
||||
|
||||
void _updateChildrenCheckStatus(TreeNode node, bool isChecked) {
|
||||
for (var child in node.children) {
|
||||
child.isChecked = isChecked;
|
||||
_updateChildrenCheckStatus(child, isChecked);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateParentCheckStatus(TreeNode node) {
|
||||
TreeNode? parent = _findParent(widget.bloc!.updatedCommunities, node);
|
||||
if (parent != null) {
|
||||
setState(() {
|
||||
parent.isChecked = _areAllChildrenChecked(parent);
|
||||
_updateParentCheckStatus(parent);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TreeNode? _findParent(List<TreeNode> nodes, TreeNode target) {
|
||||
for (var node in nodes) {
|
||||
if (node.children.contains(target)) {
|
||||
return node;
|
||||
}
|
||||
var parent = _findParent(node.children, target);
|
||||
if (parent != null) return parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SingleChildScrollView(
|
||||
child: _buildTree(widget.bloc!.updatedCommunities),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart';
|
||||
import 'package:syncrow_web/services/user_permission.dart';
|
||||
|
||||
class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
UserTableBloc() : super(TableInitial()) {
|
||||
@ -12,79 +14,108 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
on<SortUsersByNameDesc>(_toggleSortUsersByNameDesc);
|
||||
on<DateOldestToNewestEvent>(_toggleSortUsersByDateOldestToNewest);
|
||||
on<DateNewestToOldestEvent>(_toggleSortUsersByDateNewestToOldest);
|
||||
on<SearchUsers>(_searchUsers);
|
||||
on<ChangePage>(_handlePageChange);
|
||||
on<FilterUsersByRoleEvent>(_filterUsersByRole);
|
||||
on<FilterUsersByJobEvent>(_filterUsersByJobTitle);
|
||||
on<FilterUsersByCreatedEvent>(_filterUsersByCreated);
|
||||
on<FilterUsersByDeActevateEvent>(_filterUserActevate);
|
||||
on<DeleteUserEvent>(_deleteUser);
|
||||
}
|
||||
|
||||
int itemsPerPage = 10;
|
||||
int currentPage = 1;
|
||||
List<RolesUserModel> users = [];
|
||||
List<RolesUserModel> initialUsers = []; // Save the initial state
|
||||
String currentSortOrder = ''; // Keeps track of the current sorting order
|
||||
String currentSortOrderDate = ''; // Keeps track of the current sorting order
|
||||
List<RolesUserModel> initialUsers = [];
|
||||
String currentSortOrder = '';
|
||||
String currentSortOrderDate = '';
|
||||
List<String> roleTypes = [];
|
||||
List<String> jobTitle = [];
|
||||
List<String> createdBy = [];
|
||||
List<String> deActivate = [];
|
||||
|
||||
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async {
|
||||
emit(UsersLoadingState());
|
||||
try {
|
||||
users = [
|
||||
RolesUserModel(
|
||||
id: '1',
|
||||
userName: 'b 1',
|
||||
userEmail: 'test1@test.com',
|
||||
action: '',
|
||||
createdBy: 'Admin',
|
||||
creationDate: '25/10/2024',
|
||||
creationTime: '10:30 AM',
|
||||
status: 'Invited',
|
||||
),
|
||||
RolesUserModel(
|
||||
id: '2',
|
||||
userName: 'a 2',
|
||||
userEmail: 'test2@test.com',
|
||||
action: '',
|
||||
createdBy: 'Admin',
|
||||
creationDate: '24/10/2024',
|
||||
creationTime: '2:30 PM',
|
||||
status: 'Active',
|
||||
),
|
||||
RolesUserModel(
|
||||
id: '3',
|
||||
userName: 'c 3',
|
||||
userEmail: 'test3@test.com',
|
||||
action: '',
|
||||
createdBy: 'Admin',
|
||||
creationDate: '23/10/2024',
|
||||
creationTime: '9:00 AM',
|
||||
status: 'Disabled',
|
||||
),
|
||||
];
|
||||
// Sort users by newest to oldest as default
|
||||
roleTypes.clear();
|
||||
jobTitle.clear();
|
||||
createdBy.clear();
|
||||
deActivate.clear();
|
||||
users = await UserPermissionApi().fetchUsers();
|
||||
for (var user in users) {
|
||||
roleTypes.add(user.roleType.toString());
|
||||
}
|
||||
for (var user in users) {
|
||||
jobTitle.add(user.jobTitle.toString());
|
||||
}
|
||||
for (var user in users) {
|
||||
createdBy.add(user.invitedBy.toString());
|
||||
}
|
||||
for (var user in users) {
|
||||
deActivate.add(user.status.toString());
|
||||
}
|
||||
roleTypes = roleTypes.toSet().toList();
|
||||
jobTitle = jobTitle.toSet().toList();
|
||||
createdBy = createdBy.toSet().toList();
|
||||
deActivate = deActivate.toSet().toList();
|
||||
|
||||
users.sort((a, b) {
|
||||
final dateA = _parseDateTime(a.creationDate!);
|
||||
final dateB = _parseDateTime(b.creationDate!);
|
||||
return dateB.compareTo(dateA); // Newest to oldest
|
||||
final dateA = _parseDateTime(a.createdDate);
|
||||
final dateB = _parseDateTime(b.createdDate);
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
initialUsers = List.from(users); // Save the initial state
|
||||
initialUsers = List.from(users);
|
||||
_handlePageChange(ChangePage(1), emit);
|
||||
emit(UsersLoadedState(users: users));
|
||||
} catch (e) {
|
||||
emit(ErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _changeUserStatus(ChangeUserStatus event, Emitter<UserTableState> emit) {
|
||||
Future<void> _deleteUser(
|
||||
DeleteUserEvent event, Emitter<UserTableState> emit) async {
|
||||
emit(UsersLoadingState());
|
||||
try {
|
||||
users = users.map((user) {
|
||||
if (user.id == event.userId) {
|
||||
return RolesUserModel(
|
||||
id: user.id,
|
||||
userName: user.userName,
|
||||
userEmail: user.userEmail,
|
||||
createdBy: user.createdBy,
|
||||
creationDate: user.creationDate,
|
||||
creationTime: user.creationTime,
|
||||
status: event.newStatus,
|
||||
action: user.action,
|
||||
);
|
||||
}
|
||||
return user;
|
||||
}).toList();
|
||||
bool res = await UserPermissionApi().deleteUserById(event.userId);
|
||||
if (res == true) {
|
||||
Navigator.of(event.context).pop(true);
|
||||
} else {
|
||||
emit(const ErrorState('Something error'));
|
||||
}
|
||||
emit(UsersLoadedState(users: users));
|
||||
} catch (e) {
|
||||
emit(ErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _changeUserStatus(
|
||||
ChangeUserStatus event, Emitter<UserTableState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
bool res = await UserPermissionApi().changeUserStatusById(
|
||||
event.userId, event.newStatus == "disabled" ? true : false);
|
||||
if (res == true) {
|
||||
add(const GetUsers());
|
||||
// users = users.map((user) {
|
||||
// if (user.uuid == event.userId) {
|
||||
// return RolesUserModel(
|
||||
// uuid: user.uuid,
|
||||
// createdAt: user.createdAt,
|
||||
// email: user.email,
|
||||
// firstName: user.firstName,
|
||||
// lastName: user.lastName,
|
||||
// roleType: user.roleType,
|
||||
// status: event.newStatus,
|
||||
// isEnabled: event.newStatus == "disabled" ? false : true,
|
||||
// invitedBy: user.invitedBy,
|
||||
// phoneNumber: user.phoneNumber,
|
||||
// jobTitle: user.jobTitle,
|
||||
// createdDate: user.createdDate,
|
||||
// createdTime: user.createdTime,
|
||||
// );
|
||||
// }
|
||||
// return user;
|
||||
// }).toList();
|
||||
}
|
||||
emit(UsersLoadedState(users: users));
|
||||
} catch (e) {
|
||||
emit(ErrorState(e.toString()));
|
||||
@ -94,16 +125,14 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
void _toggleSortUsersByNameAsc(
|
||||
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
||||
if (currentSortOrder == "Asc") {
|
||||
// If already sorted ascending, reset to the initial state
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
users = List.from(initialUsers); // Reset to saved initial state
|
||||
users = List.from(initialUsers);
|
||||
emit(UsersLoadedState(users: users));
|
||||
} else {
|
||||
// Sort ascending
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "Asc";
|
||||
users.sort((a, b) => a.userName!.compareTo(b.userName!));
|
||||
users.sort((a, b) => a.firstName!.compareTo(b.firstName!));
|
||||
emit(UsersLoadedState(users: users));
|
||||
}
|
||||
}
|
||||
@ -111,7 +140,6 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
void _toggleSortUsersByNameDesc(
|
||||
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
||||
if (currentSortOrder == "Desc") {
|
||||
// If already sorted descending, reset to the initial state
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
users = List.from(initialUsers); // Reset to saved initial state
|
||||
@ -120,7 +148,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
// Sort descending
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "Desc";
|
||||
users.sort((a, b) => b.userName!.compareTo(a.userName!));
|
||||
users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||
emit(UsersLoadedState(users: users));
|
||||
}
|
||||
}
|
||||
@ -128,19 +156,17 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
void _toggleSortUsersByDateNewestToOldest(
|
||||
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
||||
if (currentSortOrderDate == "NewestToOldest") {
|
||||
// If already sorted ascending, reset to the initial state
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
currentSortOrderDate = "";
|
||||
users = List.from(initialUsers); // Reset to saved initial state
|
||||
users = List.from(initialUsers);
|
||||
emit(UsersLoadedState(users: users));
|
||||
} else {
|
||||
// Sort ascending
|
||||
emit(UsersLoadingState());
|
||||
users.sort((a, b) {
|
||||
final dateA = _parseDateTime(a.creationDate!);
|
||||
final dateB = _parseDateTime(b.creationDate!);
|
||||
return dateB.compareTo(dateA); // Newest to oldest
|
||||
final dateA = _parseDateTime(a.createdDate);
|
||||
final dateB = _parseDateTime(b.createdDate);
|
||||
return dateB.compareTo(dateA);
|
||||
});
|
||||
emit(UsersLoadedState(users: users));
|
||||
}
|
||||
@ -149,19 +175,17 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
void _toggleSortUsersByDateOldestToNewest(
|
||||
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
||||
if (currentSortOrderDate == "OldestToNewest") {
|
||||
// If already sorted ascending, reset to the initial state
|
||||
emit(UsersLoadingState());
|
||||
currentSortOrder = "";
|
||||
currentSortOrderDate = "";
|
||||
users = List.from(initialUsers); // Reset to saved initial state
|
||||
users = List.from(initialUsers);
|
||||
emit(UsersLoadedState(users: users));
|
||||
} else {
|
||||
// Sort ascending
|
||||
emit(UsersLoadingState());
|
||||
users.sort((a, b) {
|
||||
final dateA = _parseDateTime(a.creationDate!);
|
||||
final dateB = _parseDateTime(b.creationDate!);
|
||||
return dateA.compareTo(dateB); // Newest to oldest
|
||||
final dateA = _parseDateTime(a.createdDate);
|
||||
final dateB = _parseDateTime(b.createdDate);
|
||||
return dateA.compareTo(dateB);
|
||||
});
|
||||
emit(UsersLoadedState(users: users));
|
||||
}
|
||||
@ -169,17 +193,94 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||
|
||||
DateTime _parseDateTime(String date) {
|
||||
try {
|
||||
// Split the date into day, month, and year
|
||||
final dateParts = date.split('/');
|
||||
final day = int.parse(dateParts[0]);
|
||||
final month = int.parse(dateParts[1]);
|
||||
final year = int.parse(dateParts[2]);
|
||||
|
||||
// Split the time into hours and minutes
|
||||
|
||||
return DateTime(year, month, day);
|
||||
} catch (e) {
|
||||
throw FormatException('Invalid date or time format: $date ');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _searchUsers(
|
||||
SearchUsers event, Emitter<UserTableState> emit) async {
|
||||
try {
|
||||
final query = event.query.toLowerCase();
|
||||
final filteredUsers = initialUsers.where((user) {
|
||||
final fullName = "${user.firstName} ${user.lastName}".toLowerCase();
|
||||
final email = user.email?.toLowerCase() ?? "";
|
||||
return fullName.contains(query) || email.contains(query);
|
||||
}).toList();
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
} catch (e) {
|
||||
emit(ErrorState(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
void _paginateUsers(
|
||||
int pageNumber, int itemsPerPage, Emitter<UserTableState> emit) {
|
||||
final startIndex = (pageNumber - 1) * itemsPerPage;
|
||||
final endIndex = startIndex + itemsPerPage;
|
||||
if (startIndex >= users.length) {
|
||||
emit(UsersLoadedState(users: const []));
|
||||
return;
|
||||
}
|
||||
final paginatedUsers = users.sublist(
|
||||
startIndex,
|
||||
endIndex > users.length ? users.length : endIndex,
|
||||
);
|
||||
emit(UsersLoadedState(users: paginatedUsers));
|
||||
}
|
||||
|
||||
void _handlePageChange(ChangePage event, Emitter<UserTableState> emit) {
|
||||
final itemsPerPage = 10;
|
||||
final startIndex = (event.pageNumber - 1) * itemsPerPage;
|
||||
final endIndex = startIndex + itemsPerPage;
|
||||
if (startIndex >= users.length) {
|
||||
emit(UsersLoadedState(users: []));
|
||||
return;
|
||||
}
|
||||
final paginatedUsers = users.sublist(
|
||||
startIndex,
|
||||
endIndex > users.length ? users.length : endIndex,
|
||||
);
|
||||
emit(UsersLoadedState(users: paginatedUsers));
|
||||
}
|
||||
|
||||
void _filterUsersByRole(
|
||||
FilterUsersByRoleEvent event, Emitter<UserTableState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
final filteredUsers = initialUsers.where((user) {
|
||||
return event.selectedRoles.contains(user.roleType);
|
||||
}).toList();
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
}
|
||||
|
||||
void _filterUsersByJobTitle(
|
||||
FilterUsersByJobEvent event, Emitter<UserTableState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
final filteredUsers = users.where((user) {
|
||||
return event.selectedJob.contains(user.jobTitle);
|
||||
}).toList();
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
}
|
||||
|
||||
void _filterUsersByCreated(
|
||||
FilterUsersByCreatedEvent event, Emitter<UserTableState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
final filteredUsers = initialUsers.where((user) {
|
||||
return event.selectedCreatedBy.contains(user.invitedBy);
|
||||
}).toList();
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
}
|
||||
|
||||
void _filterUserActevate(
|
||||
FilterUsersByDeActevateEvent event, Emitter<UserTableState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
final filteredUsers = initialUsers.where((user) {
|
||||
return event.selectedActivate.contains(user.status);
|
||||
}).toList();
|
||||
emit(UsersLoadedState(users: filteredUsers));
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
sealed class UserTableEvent extends Equatable {
|
||||
const UserTableEvent();
|
||||
@ -47,7 +48,6 @@ class StoreUsersEvent extends UserTableEvent {
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
|
||||
class DateNewestToOldestEvent extends UserTableEvent {
|
||||
const DateNewestToOldestEvent();
|
||||
|
||||
@ -60,4 +60,62 @@ class DateOldestToNewestEvent extends UserTableEvent {
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
}
|
||||
|
||||
class SearchUsers extends UserTableEvent {
|
||||
final String query;
|
||||
SearchUsers(this.query);
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ChangePage extends UserTableEvent {
|
||||
final int pageNumber;
|
||||
|
||||
ChangePage(this.pageNumber);
|
||||
|
||||
@override
|
||||
List<Object> get props => [pageNumber];
|
||||
}
|
||||
|
||||
class DeleteUserEvent extends UserTableEvent {
|
||||
final String userId;
|
||||
final BuildContext context;
|
||||
|
||||
const DeleteUserEvent(this.userId, this.context);
|
||||
|
||||
@override
|
||||
List<Object> get props => [userId, context];
|
||||
}
|
||||
|
||||
class FilterUsersByRoleEvent extends UserTableEvent {
|
||||
final List<String> selectedRoles;
|
||||
|
||||
FilterUsersByRoleEvent(this.selectedRoles);
|
||||
@override
|
||||
List<Object?> get props => [selectedRoles];
|
||||
}
|
||||
|
||||
class FilterUsersByJobEvent extends UserTableEvent {
|
||||
final List<String> selectedJob;
|
||||
|
||||
FilterUsersByJobEvent(this.selectedJob);
|
||||
@override
|
||||
List<Object?> get props => [selectedJob];
|
||||
}
|
||||
|
||||
class FilterUsersByCreatedEvent extends UserTableEvent {
|
||||
final List<String> selectedCreatedBy;
|
||||
|
||||
FilterUsersByCreatedEvent(this.selectedCreatedBy);
|
||||
@override
|
||||
List<Object?> get props => [selectedCreatedBy];
|
||||
}
|
||||
|
||||
class FilterUsersByDeActevateEvent extends UserTableEvent {
|
||||
final List<String> selectedActivate;
|
||||
|
||||
FilterUsersByDeActevateEvent(this.selectedActivate);
|
||||
@override
|
||||
List<Object?> get props => [selectedActivate];
|
||||
}
|
||||
|
@ -81,15 +81,17 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
||||
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: BorderRadius.only(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(15),
|
||||
topRight: Radius.circular(15))),
|
||||
child: Row(
|
||||
@ -167,56 +169,96 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
||||
),
|
||||
),
|
||||
// Data Rows with Dividers
|
||||
Container(
|
||||
decoration: containerDecoration.copyWith(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: const BorderRadius.only(
|
||||
bottomLeft: Radius.circular(15),
|
||||
bottomRight: Radius.circular(15))),
|
||||
child: Column(
|
||||
children: widget.rows.map((row) {
|
||||
int rowIndex = widget.rows.indexOf(row);
|
||||
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],
|
||||
),
|
||||
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,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const Text(
|
||||
'No Users',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
: 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) {
|
||||
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) {
|
||||
return SizedBox(
|
||||
width: columnWidths[index],
|
||||
child: const Divider(
|
||||
color: ColorsManager.boxDivider,
|
||||
thickness: 1,
|
||||
height: 1,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}))
|
||||
// Add a Divider below each row except the last one
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -1,7 +1,11 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:number_pagination/number_pagination.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/add_user_dialog.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/delete_user_dialog.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/edit_user_dialog.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/popup_menu_filter.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_bloc.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart';
|
||||
@ -15,7 +19,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
|
||||
class UsersPage extends StatelessWidget {
|
||||
const UsersPage({super.key});
|
||||
UsersPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final TextEditingController searchController = TextEditingController();
|
||||
@ -44,9 +49,9 @@ class UsersPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
color: status == "Invited"
|
||||
color: status == "invited"
|
||||
? ColorsManager.invitedOrange.withOpacity(0.5)
|
||||
: status == "Active"
|
||||
: status == "active"
|
||||
? ColorsManager.activeGreen.withOpacity(0.5)
|
||||
: ColorsManager.disabledPink.withOpacity(0.5),
|
||||
),
|
||||
@ -60,9 +65,9 @@ class UsersPage extends StatelessWidget {
|
||||
Text(
|
||||
status,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: status == "Invited"
|
||||
color: status == "invited"
|
||||
? ColorsManager.invitedOrangeText
|
||||
: status == "Active"
|
||||
: status == "active"
|
||||
? ColorsManager.activeGreenText
|
||||
: ColorsManager.disabledRedText,
|
||||
fontWeight: FontWeight.w400,
|
||||
@ -81,23 +86,14 @@ class UsersPage extends StatelessWidget {
|
||||
required Function()? onTap}) {
|
||||
return Center(
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
final newStatus = status == 'Active'
|
||||
? 'Disabled'
|
||||
: status == 'Disabled'
|
||||
? 'Invited'
|
||||
: 'Active';
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(ChangeUserStatus(userId: userId, newStatus: newStatus));
|
||||
},
|
||||
onTap: onTap,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
||||
child: SvgPicture.asset(
|
||||
status == "Invited"
|
||||
status == "invited"
|
||||
? Assets.invitedIcon
|
||||
: status == "Active"
|
||||
: status == "active"
|
||||
? Assets.activeUser
|
||||
: Assets.deActiveUser,
|
||||
height: 35,
|
||||
@ -113,12 +109,14 @@ class UsersPage extends StatelessWidget {
|
||||
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||
|
||||
if (state is UsersLoadingState) {
|
||||
_blocRole.add(ChangePage(_blocRole.currentPage));
|
||||
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
} else if (state is UsersLoadedState) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
child: ListView(
|
||||
shrinkWrap: true,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
@ -131,6 +129,9 @@ class UsersPage extends StatelessWidget {
|
||||
width: screenSize.width * 0.4,
|
||||
child: TextFormField(
|
||||
controller: searchController,
|
||||
onChanged: (value) {
|
||||
context.read<UserTableBloc>().add(SearchUsers(value));
|
||||
},
|
||||
style: const TextStyle(color: Colors.black),
|
||||
decoration: textBoxDecoration(radios: 15)!.copyWith(
|
||||
fillColor: ColorsManager.whiteColors,
|
||||
@ -158,9 +159,9 @@ class UsersPage extends StatelessWidget {
|
||||
builder: (BuildContext context) {
|
||||
return const AddNewUserDialog();
|
||||
},
|
||||
).then((listDevice) {
|
||||
if (listDevice != null) {
|
||||
|
||||
).then((v) {
|
||||
if (v != null) {
|
||||
_blocRole.add(const GetUsers());
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -201,6 +202,66 @@ class UsersPage extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 2) {
|
||||
final Map<String, bool> checkboxStates = {
|
||||
for (var item in _blocRole.jobTitle)
|
||||
item: false, // Initialize with false
|
||||
};
|
||||
|
||||
showPopUpFilterMenu(
|
||||
list: _blocRole.jobTitle,
|
||||
context: context,
|
||||
checkboxStates: checkboxStates,
|
||||
onOkPressed: () {
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
Navigator.of(context).pop();
|
||||
_blocRole.add(FilterUsersByJobEvent(selectedItems));
|
||||
},
|
||||
onSortAtoZ: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameAsc());
|
||||
},
|
||||
onSortZtoA: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameDesc());
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 3) {
|
||||
final Map<String, bool> checkboxStates = {
|
||||
for (var item in _blocRole.roleTypes)
|
||||
item: false, // Initialize with false
|
||||
};
|
||||
|
||||
showPopUpFilterMenu(
|
||||
list: _blocRole.roleTypes,
|
||||
context: context,
|
||||
checkboxStates: checkboxStates,
|
||||
onOkPressed: () {
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
Navigator.of(context).pop();
|
||||
_blocRole.add(FilterUsersByRoleEvent(selectedItems));
|
||||
},
|
||||
onSortAtoZ: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameAsc());
|
||||
},
|
||||
onSortZtoA: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameDesc());
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 4) {
|
||||
showDateFilterMenu(
|
||||
context: context,
|
||||
@ -217,6 +278,37 @@ class UsersPage extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 6) {
|
||||
final Map<String, bool> checkboxStates = {
|
||||
for (var item in _blocRole.createdBy)
|
||||
item: false, // Initialize with false
|
||||
};
|
||||
|
||||
showPopUpFilterMenu(
|
||||
list: _blocRole.createdBy,
|
||||
context: context,
|
||||
checkboxStates: checkboxStates,
|
||||
onOkPressed: () {
|
||||
final selectedItems = checkboxStates.entries
|
||||
.where((entry) => entry.value)
|
||||
.map((entry) => entry.key)
|
||||
.toList();
|
||||
Navigator.of(context).pop();
|
||||
_blocRole
|
||||
.add(FilterUsersByCreatedEvent(selectedItems));
|
||||
},
|
||||
onSortAtoZ: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameAsc());
|
||||
},
|
||||
onSortZtoA: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameDesc());
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 8) {
|
||||
showDeActivateFilterMenu(
|
||||
context: context,
|
||||
@ -248,19 +340,37 @@ class UsersPage extends StatelessWidget {
|
||||
],
|
||||
rows: state.users.map((user) {
|
||||
return [
|
||||
Text(user.userName!),
|
||||
Text(user.userEmail!),
|
||||
const Text("Test"),
|
||||
const Text("Member"),
|
||||
Text(user.creationDate!),
|
||||
Text(user.creationTime!),
|
||||
Text(user.createdBy!),
|
||||
Text('${user.firstName} ${user.lastName}'),
|
||||
Text(user.email ?? ''),
|
||||
Text(user.jobTitle ?? ''),
|
||||
Text(user.roleType ?? ''),
|
||||
Text(user.createdDate ?? ''),
|
||||
Text(user.createdTime ?? ''),
|
||||
Text(user.invitedBy),
|
||||
changeIconStatus(
|
||||
status: user.status!,
|
||||
userId: user.id!,
|
||||
onTap: () {},
|
||||
status:
|
||||
user.isEnabled == false ? 'disabled' : user.status,
|
||||
userId: user.uuid,
|
||||
onTap: user.status != "invited"
|
||||
? () {
|
||||
final newStatus = user.status == 'active'
|
||||
? 'disabled'
|
||||
: user.status == 'disabled'
|
||||
? 'invited'
|
||||
: 'active';
|
||||
context.read<UserTableBloc>().add(
|
||||
ChangeUserStatus(
|
||||
userId: user.uuid,
|
||||
newStatus: user.isEnabled == false
|
||||
? 'disabled'
|
||||
: user.status));
|
||||
}
|
||||
: null,
|
||||
),
|
||||
status(
|
||||
status:
|
||||
user.isEnabled == false ? 'disabled' : user.status,
|
||||
),
|
||||
status(status: user.status!),
|
||||
Row(
|
||||
children: [
|
||||
// actionButton(
|
||||
@ -269,17 +379,80 @@ class UsersPage extends StatelessWidget {
|
||||
// ),
|
||||
actionButton(
|
||||
title: "Edit",
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return EditUserDialog(userId: user.uuid);
|
||||
},
|
||||
).then((v) {
|
||||
if (v != null) {
|
||||
if (v != null) {
|
||||
_blocRole.add(const GetUsers());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
actionButton(
|
||||
title: "Delete",
|
||||
onTap: () {},
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return DeleteUserDialog(
|
||||
onTapDelete: () {
|
||||
_blocRole.add(
|
||||
DeleteUserEvent(user.uuid, context));
|
||||
},
|
||||
);
|
||||
},
|
||||
).then((v) {
|
||||
if (v != null) {
|
||||
if (v != null) {
|
||||
_blocRole.add(const GetUsers());
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}).toList(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
width: 500,
|
||||
child: NumberPagination(
|
||||
buttonRadius: 10,
|
||||
selectedButtonColor: ColorsManager.secondaryColor,
|
||||
buttonUnSelectedBorderColor: ColorsManager.grayBorder,
|
||||
lastPageIcon:
|
||||
const Icon(Icons.keyboard_double_arrow_right),
|
||||
firstPageIcon:
|
||||
const Icon(Icons.keyboard_double_arrow_left),
|
||||
totalPages:
|
||||
(_blocRole.users.length / _blocRole.itemsPerPage)
|
||||
.ceil(),
|
||||
currentPage: _blocRole.currentPage,
|
||||
onPageChanged: (int pageNumber) {
|
||||
_blocRole.currentPage = pageNumber;
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(ChangePage(pageNumber));
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -77,7 +77,7 @@ class RolesAndPermissionPage extends StatelessWidget {
|
||||
),
|
||||
scaffoldBody: BlocProvider<UserTableBloc>(
|
||||
create: (context) => UserTableBloc()..add(const GetUsers()),
|
||||
child: const UsersPage(),
|
||||
child: UsersPage(),
|
||||
)
|
||||
// _blocRole.tapSelect == false
|
||||
// ? UsersPage(
|
||||
|
@ -252,6 +252,7 @@ class CommunitySpaceManagementApi {
|
||||
path: ApiEndpoints.getSpaceHierarchy
|
||||
.replaceAll('{communityId}', communityId),
|
||||
expectedResponseModel: (json) {
|
||||
print(json);
|
||||
final spaceModels = (json['data'] as List)
|
||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||
.toList();
|
||||
|
@ -1,6 +1,9 @@
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/edit_user_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart';
|
||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart';
|
||||
import 'package:syncrow_web/services/api/http_service.dart';
|
||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
@ -8,6 +11,25 @@ import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||
class UserPermissionApi {
|
||||
static final HTTPService _httpService = HTTPService();
|
||||
|
||||
Future<List<RolesUserModel>> fetchUsers() async {
|
||||
try {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getUsers,
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
debugPrint('fetchUsers Response: $json');
|
||||
final List<dynamic> data =
|
||||
json['data'] ?? []; // Default to an empty list if no data
|
||||
return data.map((item) => RolesUserModel.fromJson(item)).toList();
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e, stackTrace) {
|
||||
debugPrint('Error in fetchUsers: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
fetchRoles() async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.roleTypes,
|
||||
@ -67,6 +89,7 @@ class UserPermissionApi {
|
||||
}
|
||||
},
|
||||
);
|
||||
print('sendInviteUser=$body');
|
||||
|
||||
return response ?? [];
|
||||
} on DioException catch (e) {
|
||||
@ -107,4 +130,95 @@ class UserPermissionApi {
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Future<EditUserModel> fetchUserById(userUuid) async {
|
||||
final response = await _httpService.get(
|
||||
path: ApiEndpoints.getUserById.replaceAll("{userUuid}", userUuid),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
EditUserModel res = EditUserModel.fromJson(json['data']);
|
||||
return res;
|
||||
},
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
Future<bool> editInviteUser({
|
||||
String? firstName,
|
||||
String? userId,
|
||||
String? lastName,
|
||||
String? jobTitle,
|
||||
String? phoneNumber,
|
||||
String? roleUuid,
|
||||
List<String>? spaceUuids,
|
||||
}) async {
|
||||
try {
|
||||
final body = <String, dynamic>{
|
||||
"firstName": firstName,
|
||||
"lastName": lastName,
|
||||
"jobTitle": jobTitle != '' ? jobTitle : " ",
|
||||
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
|
||||
"roleUuid": roleUuid,
|
||||
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c",
|
||||
"spaceUuids": spaceUuids,
|
||||
};
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.editUser.replaceAll('{inviteUserUuid}', userId!),
|
||||
body: jsonEncode(body),
|
||||
expectedResponseModel: (json) {
|
||||
if (json['statusCode'] != 400) {
|
||||
return json["success"];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} on DioException catch (e) {
|
||||
return false;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> deleteUserById(userUuid) async {
|
||||
try {
|
||||
final response = await _httpService.delete(
|
||||
path: ApiEndpoints.deleteUser.replaceAll("{inviteUserUuid}", userUuid),
|
||||
showServerMessage: true,
|
||||
expectedResponseModel: (json) {
|
||||
return json['success'];
|
||||
},
|
||||
);
|
||||
return response;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> changeUserStatusById(userUuid, status) async {
|
||||
try {
|
||||
Map<String, dynamic> bodya = {
|
||||
"disable": status,
|
||||
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c"
|
||||
};
|
||||
print('changeUserStatusById==$bodya');
|
||||
print('changeUserStatusById==$userUuid');
|
||||
|
||||
final response = await _httpService.put(
|
||||
path: ApiEndpoints.changeUserStatus
|
||||
.replaceAll("{invitedUserUuid}", userUuid),
|
||||
body: bodya,
|
||||
expectedResponseModel: (json) {
|
||||
print('changeUserStatusById==${json['success']}');
|
||||
return json['success'];
|
||||
},
|
||||
);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
return false;
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,13 @@ abstract class ApiEndpoints {
|
||||
static const String roleTypes = '/role/types';
|
||||
static const String permission = '/permission/{roleUuid}';
|
||||
static const String inviteUser = '/invite-user';
|
||||
|
||||
static const String checkEmail = '/invite-user/check-email';
|
||||
static const String getUsers = '/projects/${projectUuid}/user';
|
||||
static const String getUserById = '/projects/${projectUuid}/user/{userUuid}';
|
||||
static const String editUser = '/invite-user/{inviteUserUuid}';
|
||||
static const String deleteUser = '/invite-user/{inviteUserUuid}';
|
||||
static const String changeUserStatus = '/invite-user/{invitedUserUuid}/disable';
|
||||
|
||||
// static const String updateAutomation = '/automation/{automationId}';
|
||||
// https://syncrow-dev.azurewebsites.net/invite-user/check-email
|
||||
}
|
||||
|
36
macos/Podfile.lock
Normal file
36
macos/Podfile.lock
Normal file
@ -0,0 +1,36 @@
|
||||
PODS:
|
||||
- flutter_secure_storage_macos (6.1.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
flutter_secure_storage_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
flutter_secure_storage_macos: 59459653abe1adb92abbc8ea747d79f8d19866c9
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
|
||||
PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367
|
||||
|
||||
COCOAPODS: 1.16.2
|
@ -21,6 +21,8 @@
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
108157F896CD9F637B06D7C0 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAF1C60594A51D692304366 /* Pods_Runner.framework */; };
|
||||
2D0F1F294F673EF0DB5E4CA1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */; };
|
||||
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||
@ -60,11 +62,12 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
24D7BEF98D33245EFB9F6A1B /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; };
|
||||
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; };
|
||||
33CC10ED2044A3C60003C045 /* syncrow_web.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "syncrow_web.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10ED2044A3C60003C045 /* syncrow_web.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = syncrow_web.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = "<group>"; };
|
||||
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
@ -76,8 +79,15 @@
|
||||
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
|
||||
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
|
||||
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
|
||||
5DAF1C60594A51D692304366 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
|
||||
81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
|
||||
A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AB949539E0D0A8E2BDAB9ADF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||
E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
F244F079A053D959E1C5C362 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -85,6 +95,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2D0F1F294F673EF0DB5E4CA1 /* Pods_RunnerTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -92,6 +103,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
108157F896CD9F637B06D7C0 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -125,6 +137,7 @@
|
||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||
33CC10EE2044A3C60003C045 /* Products */,
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||
75DCDFECC7757C5159E8F0C5 /* Pods */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
@ -172,9 +185,25 @@
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
75DCDFECC7757C5159E8F0C5 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
24D7BEF98D33245EFB9F6A1B /* Pods-Runner.debug.xcconfig */,
|
||||
F244F079A053D959E1C5C362 /* Pods-Runner.release.xcconfig */,
|
||||
AB949539E0D0A8E2BDAB9ADF /* Pods-Runner.profile.xcconfig */,
|
||||
96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */,
|
||||
A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */,
|
||||
81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5DAF1C60594A51D692304366 /* Pods_Runner.framework */,
|
||||
E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
@ -186,6 +215,7 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
A1935203066F42991FF0ED43 /* [CP] Check Pods Manifest.lock */,
|
||||
331C80D1294CF70F00263BE5 /* Sources */,
|
||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||
331C80D3294CF70F00263BE5 /* Resources */,
|
||||
@ -204,11 +234,13 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||
buildPhases = (
|
||||
8ECFD939A4D371A145DBA191 /* [CP] Check Pods Manifest.lock */,
|
||||
33CC10E92044A3C60003C045 /* Sources */,
|
||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||
33CC10EB2044A3C60003C045 /* Resources */,
|
||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||
92D754792F50A5D35F6D5AEE /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@ -329,6 +361,67 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
||||
};
|
||||
8ECFD939A4D371A145DBA191 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
92D754792F50A5D35F6D5AEE /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputFileListPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
A1935203066F42991FF0ED43 /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@ -380,6 +473,7 @@
|
||||
/* Begin XCBuildConfiguration section */
|
||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@ -394,6 +488,7 @@
|
||||
};
|
||||
331C80DC294CF71000263BE5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
@ -408,6 +503,7 @@
|
||||
};
|
||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
|
@ -4,4 +4,7 @@
|
||||
<FileRef
|
||||
location = "group:Runner.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:Pods/Pods.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
@ -392,6 +392,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
number_pagination:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: number_pagination
|
||||
sha256: "75d3a28616196e7c8df431d0fb7c48e811e462155f4cf3b5b4167b3408421327"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.6"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -53,6 +53,7 @@ dependencies:
|
||||
uuid: ^4.4.2
|
||||
time_picker_spinner: ^1.0.0
|
||||
intl_phone_field: ^3.2.0
|
||||
number_pagination: ^1.1.6
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user