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 {
|
class RolesUserModel {
|
||||||
String? id;
|
final String uuid;
|
||||||
String? userName;
|
final DateTime createdAt;
|
||||||
String? userEmail;
|
final String email;
|
||||||
String? userRole;
|
final dynamic firstName;
|
||||||
String? creationDate;
|
final dynamic lastName;
|
||||||
String? creationTime;
|
final dynamic roleType;
|
||||||
String? createdBy;
|
final dynamic status;
|
||||||
String? status;
|
final bool isEnabled;
|
||||||
String? action;
|
final String invitedBy;
|
||||||
RolesUserModel(
|
final dynamic phoneNumber;
|
||||||
{this.id,
|
final dynamic jobTitle;
|
||||||
this.userName,
|
final dynamic createdDate;
|
||||||
this.userEmail,
|
final dynamic createdTime;
|
||||||
this.userRole,
|
|
||||||
this.creationDate,
|
RolesUserModel({
|
||||||
this.creationTime,
|
required this.uuid,
|
||||||
this.status,
|
required this.createdAt,
|
||||||
this.action,
|
required this.email,
|
||||||
this.createdBy,
|
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:bloc/bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:syncrow_web/pages/common/custom_dialog.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/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_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/bloc/users_status.dart';
|
||||||
@ -25,6 +26,11 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
on<ValidateBasicsStep>(_validateBasicsStep);
|
on<ValidateBasicsStep>(_validateBasicsStep);
|
||||||
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
||||||
on<CheckEmailEvent>(checkEmail);
|
on<CheckEmailEvent>(checkEmail);
|
||||||
|
on<GetUserByIdEvent>(getUserById);
|
||||||
|
on<ToggleNodeExpansion>(_onToggleNodeExpansion);
|
||||||
|
on<ToggleNodeCheck>(_onToggleNodeCheck);
|
||||||
|
on<EditInviteUsers>(_editInvitUser);
|
||||||
|
|
||||||
}
|
}
|
||||||
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
||||||
if (formKey.currentState?.validate() ?? false) {
|
if (formKey.currentState?.validate() ?? false) {
|
||||||
@ -42,9 +48,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
final TextEditingController emailController = TextEditingController();
|
final TextEditingController emailController = TextEditingController();
|
||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController phoneController = TextEditingController();
|
||||||
final TextEditingController jobTitleController = TextEditingController();
|
final TextEditingController jobTitleController = TextEditingController();
|
||||||
|
|
||||||
final TextEditingController roleSearchController = TextEditingController();
|
final TextEditingController roleSearchController = TextEditingController();
|
||||||
// final TextEditingController jobTitleController = TextEditingController();
|
|
||||||
|
|
||||||
bool? isCompleteBasics;
|
bool? isCompleteBasics;
|
||||||
bool? isCompleteRolePermissions;
|
bool? isCompleteRolePermissions;
|
||||||
@ -84,6 +88,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
await CommunitySpaceManagementApi().fetchCommunities();
|
await CommunitySpaceManagementApi().fetchCommunities();
|
||||||
updatedCommunities = await Future.wait(
|
updatedCommunities = await Future.wait(
|
||||||
communities.map((community) async {
|
communities.map((community) async {
|
||||||
|
print(community.uuid);
|
||||||
List<SpaceModel> spaces =
|
List<SpaceModel> spaces =
|
||||||
await _fetchSpacesForCommunity(community.uuid);
|
await _fetchSpacesForCommunity(community.uuid);
|
||||||
spacesNodes = _buildTreeNodes(spaces);
|
spacesNodes = _buildTreeNodes(spaces);
|
||||||
@ -228,8 +233,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(event.context).pop();
|
Navigator.of(event.context).pop(true);
|
||||||
Navigator.of(event.context).pop();
|
Navigator.of(event.context).pop(true);
|
||||||
},
|
},
|
||||||
child: const Text('OK'),
|
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) {
|
void searchRolePermission(SearchPermission event, Emitter<UsersState> emit) {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
if (event.searchTerm!.isEmpty) {
|
if (event.searchTerm!.isEmpty) {
|
||||||
@ -268,6 +315,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
|
|
||||||
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
|
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
|
if (event.isEditUser == false) {
|
||||||
add(const CheckEmailEvent());
|
add(const CheckEmailEvent());
|
||||||
final emailRegex = RegExp(
|
final emailRegex = RegExp(
|
||||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||||
@ -279,7 +327,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
emailController.text.isNotEmpty &&
|
emailController.text.isNotEmpty &&
|
||||||
isEmailValid &&
|
isEmailValid &&
|
||||||
isEmailServerValid;
|
isEmailServerValid;
|
||||||
|
} else {
|
||||||
|
isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||||
|
lastNameController.text.isNotEmpty;
|
||||||
|
}
|
||||||
emit(ChangeStatusSteps());
|
emit(ChangeStatusSteps());
|
||||||
emit(ValidateBasics());
|
emit(ValidateBasics());
|
||||||
return isCompleteBasics!;
|
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];
|
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 {
|
class CheckSpacesStepStatus extends UsersEvent {
|
||||||
const CheckSpacesStepStatus();
|
const CheckSpacesStepStatus();
|
||||||
@override
|
@override
|
||||||
@ -52,9 +60,11 @@ class GetBatchStatus extends UsersEvent {
|
|||||||
List<Object?> get props => [uuids];
|
List<Object?> get props => [uuids];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//isEditUser:widget.userId!=''? false:true
|
||||||
class CheckStepStatus extends UsersEvent {
|
class CheckStepStatus extends UsersEvent {
|
||||||
final int? steps;
|
final int? steps;
|
||||||
const CheckStepStatus({this.steps});
|
bool? isEditUser = false;
|
||||||
|
CheckStepStatus({this.steps, required this.isEditUser});
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [steps];
|
List<Object?> get props => [steps];
|
||||||
}
|
}
|
||||||
@ -85,7 +95,7 @@ class SelecteId extends UsersEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ValidateBasicsStep extends UsersEvent {
|
class ValidateBasicsStep extends UsersEvent {
|
||||||
const ValidateBasicsStep();
|
ValidateBasicsStep();
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => [];
|
||||||
}
|
}
|
||||||
@ -95,3 +105,79 @@ class CheckEmailEvent extends UsersEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
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:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_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/tree_node_model.dart';
|
||||||
|
|
||||||
sealed class UsersState extends Equatable {
|
sealed class UsersState extends Equatable {
|
||||||
const UsersState();
|
const UsersState();
|
||||||
@ -80,3 +81,10 @@ final class ValidateBasics extends UsersState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
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) {
|
if (currentStep < 3) {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
if (currentStep == 2) {
|
if (currentStep == 2) {
|
||||||
_blocRole.add(const CheckStepStatus());
|
_blocRole.add(
|
||||||
|
CheckStepStatus(isEditUser: false));
|
||||||
} else if (currentStep == 3) {
|
} else if (currentStep == 3) {
|
||||||
_blocRole
|
_blocRole
|
||||||
.add(const CheckSpacesStepStatus());
|
.add(const CheckSpacesStepStatus());
|
||||||
@ -150,9 +151,11 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
Widget _getFormContent() {
|
Widget _getFormContent() {
|
||||||
switch (currentStep) {
|
switch (currentStep) {
|
||||||
case 1:
|
case 1:
|
||||||
return const BasicsView();
|
return BasicsView(
|
||||||
|
userId: '',
|
||||||
|
);
|
||||||
case 2:
|
case 2:
|
||||||
return const SpacesAccessView();
|
return SpacesAccessView();
|
||||||
case 3:
|
case 3:
|
||||||
return const RolesAndPermission();
|
return const RolesAndPermission();
|
||||||
default:
|
default:
|
||||||
@ -169,7 +172,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
bloc.add(const CheckSpacesStepStatus());
|
bloc.add(const CheckSpacesStepStatus());
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
bloc.add(const ValidateBasicsStep());
|
bloc.add(ValidateBasicsStep());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -234,7 +237,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
bloc.add(const CheckStepStatus());
|
bloc.add(CheckStepStatus(isEditUser: false));
|
||||||
if (step3 == 3) {
|
if (step3 == 3) {
|
||||||
bloc.add(const CheckRoleStepStatus());
|
bloc.add(const CheckRoleStepStatus());
|
||||||
}
|
}
|
||||||
@ -299,7 +302,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
currentStep = step;
|
currentStep = step;
|
||||||
step3 = step;
|
step3 = step;
|
||||||
bloc.add(const CheckSpacesStepStatus());
|
bloc.add(const CheckSpacesStepStatus());
|
||||||
bloc.add(const CheckStepStatus());
|
bloc.add(CheckStepStatus(isEditUser: false));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
|
@ -11,7 +11,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
|||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class BasicsView extends StatelessWidget {
|
class BasicsView extends StatelessWidget {
|
||||||
const BasicsView({super.key});
|
String? userId = '';
|
||||||
|
BasicsView({super.key, this.userId});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<UsersBloc, UsersState>(builder: (context, state) {
|
return BlocBuilder<UsersBloc, UsersState>(builder: (context, state) {
|
||||||
@ -184,10 +185,11 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
enabled: userId!=''? false:true,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
Future.delayed(const Duration(milliseconds: 200), () {
|
Future.delayed(const Duration(milliseconds: 200), () {
|
||||||
_blocRole.add(const CheckStepStatus());
|
_blocRole.add(CheckStepStatus(isEditUser:userId!=''? false:true));
|
||||||
_blocRole.add(ValidateBasicsStep());
|
_blocRole.add( ValidateBasicsStep());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
controller: _blocRole.emailController,
|
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/material.dart';
|
||||||
import 'package:flutter/widgets.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';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class DeleteUserDialog extends StatefulWidget {
|
class DeleteUserDialog extends StatefulWidget {
|
||||||
const DeleteUserDialog({super.key});
|
final Function()? onTapDelete;
|
||||||
|
DeleteUserDialog({super.key, this.onTapDelete});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DeleteUserDialogState createState() => _DeleteUserDialogState();
|
_DeleteUserDialogState createState() => _DeleteUserDialogState();
|
||||||
@ -17,21 +15,16 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
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(
|
return Dialog(
|
||||||
child: Container(
|
child: Container(
|
||||||
|
height: 160,
|
||||||
|
width: 200,
|
||||||
decoration: const BoxDecoration(
|
decoration: const BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius: BorderRadius.all(Radius.circular(20))),
|
borderRadius: BorderRadius.all(Radius.circular(20))),
|
||||||
child: const Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -43,21 +36,73 @@ class _DeleteUserDialogState extends State<DeleteUserDialog> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Divider(),
|
const Padding(
|
||||||
Expanded(
|
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(
|
child: Text(
|
||||||
"Are you sure you want to delete this user?",
|
"Are you sure you want to delete this user?",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: Text('Cancel')),
|
Expanded(
|
||||||
Expanded(child: Text('Delete')),
|
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,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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(
|
||||||
|
'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_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_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/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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class SpacesAccessView extends StatelessWidget {
|
class SpacesAccessView extends StatelessWidget {
|
||||||
const SpacesAccessView({super.key});
|
String? userId = '';
|
||||||
|
SpacesAccessView({super.key, this.userId});
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final size = MediaQuery.of(context).size;
|
final size = MediaQuery.of(context).size;
|
||||||
@ -109,9 +110,7 @@ class SpacesAccessView extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
child: TreeView(
|
child: TreeView(userId: userId))))
|
||||||
bloc: _blocRole,
|
|
||||||
))))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -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 'dart:async';
|
||||||
import 'package:bloc/bloc.dart';
|
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/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_event.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.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> {
|
class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
||||||
UserTableBloc() : super(TableInitial()) {
|
UserTableBloc() : super(TableInitial()) {
|
||||||
@ -12,79 +14,108 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
on<SortUsersByNameDesc>(_toggleSortUsersByNameDesc);
|
on<SortUsersByNameDesc>(_toggleSortUsersByNameDesc);
|
||||||
on<DateOldestToNewestEvent>(_toggleSortUsersByDateOldestToNewest);
|
on<DateOldestToNewestEvent>(_toggleSortUsersByDateOldestToNewest);
|
||||||
on<DateNewestToOldestEvent>(_toggleSortUsersByDateNewestToOldest);
|
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> users = [];
|
||||||
List<RolesUserModel> initialUsers = []; // Save the initial state
|
List<RolesUserModel> initialUsers = [];
|
||||||
String currentSortOrder = ''; // Keeps track of the current sorting order
|
String currentSortOrder = '';
|
||||||
String currentSortOrderDate = ''; // Keeps track of the current sorting order
|
String currentSortOrderDate = '';
|
||||||
|
List<String> roleTypes = [];
|
||||||
|
List<String> jobTitle = [];
|
||||||
|
List<String> createdBy = [];
|
||||||
|
List<String> deActivate = [];
|
||||||
|
|
||||||
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async {
|
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> emit) async {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
try {
|
try {
|
||||||
users = [
|
roleTypes.clear();
|
||||||
RolesUserModel(
|
jobTitle.clear();
|
||||||
id: '1',
|
createdBy.clear();
|
||||||
userName: 'b 1',
|
deActivate.clear();
|
||||||
userEmail: 'test1@test.com',
|
users = await UserPermissionApi().fetchUsers();
|
||||||
action: '',
|
for (var user in users) {
|
||||||
createdBy: 'Admin',
|
roleTypes.add(user.roleType.toString());
|
||||||
creationDate: '25/10/2024',
|
}
|
||||||
creationTime: '10:30 AM',
|
for (var user in users) {
|
||||||
status: 'Invited',
|
jobTitle.add(user.jobTitle.toString());
|
||||||
),
|
}
|
||||||
RolesUserModel(
|
for (var user in users) {
|
||||||
id: '2',
|
createdBy.add(user.invitedBy.toString());
|
||||||
userName: 'a 2',
|
}
|
||||||
userEmail: 'test2@test.com',
|
for (var user in users) {
|
||||||
action: '',
|
deActivate.add(user.status.toString());
|
||||||
createdBy: 'Admin',
|
}
|
||||||
creationDate: '24/10/2024',
|
roleTypes = roleTypes.toSet().toList();
|
||||||
creationTime: '2:30 PM',
|
jobTitle = jobTitle.toSet().toList();
|
||||||
status: 'Active',
|
createdBy = createdBy.toSet().toList();
|
||||||
),
|
deActivate = deActivate.toSet().toList();
|
||||||
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
|
|
||||||
users.sort((a, b) {
|
users.sort((a, b) {
|
||||||
final dateA = _parseDateTime(a.creationDate!);
|
final dateA = _parseDateTime(a.createdDate);
|
||||||
final dateB = _parseDateTime(b.creationDate!);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
return dateB.compareTo(dateA); // Newest to oldest
|
return dateB.compareTo(dateA);
|
||||||
});
|
});
|
||||||
initialUsers = List.from(users); // Save the initial state
|
initialUsers = List.from(users);
|
||||||
|
_handlePageChange(ChangePage(1), emit);
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState(e.toString()));
|
emit(ErrorState(e.toString()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _changeUserStatus(ChangeUserStatus event, Emitter<UserTableState> emit) {
|
Future<void> _deleteUser(
|
||||||
|
DeleteUserEvent event, Emitter<UserTableState> emit) async {
|
||||||
|
emit(UsersLoadingState());
|
||||||
try {
|
try {
|
||||||
users = users.map((user) {
|
bool res = await UserPermissionApi().deleteUserById(event.userId);
|
||||||
if (user.id == event.userId) {
|
if (res == true) {
|
||||||
return RolesUserModel(
|
Navigator.of(event.context).pop(true);
|
||||||
id: user.id,
|
} else {
|
||||||
userName: user.userName,
|
emit(const ErrorState('Something error'));
|
||||||
userEmail: user.userEmail,
|
}
|
||||||
createdBy: user.createdBy,
|
emit(UsersLoadedState(users: users));
|
||||||
creationDate: user.creationDate,
|
} catch (e) {
|
||||||
creationTime: user.creationTime,
|
emit(ErrorState(e.toString()));
|
||||||
status: event.newStatus,
|
}
|
||||||
action: user.action,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return user;
|
|
||||||
}).toList();
|
|
||||||
|
|
||||||
|
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));
|
emit(UsersLoadedState(users: users));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState(e.toString()));
|
emit(ErrorState(e.toString()));
|
||||||
@ -94,16 +125,14 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
void _toggleSortUsersByNameAsc(
|
void _toggleSortUsersByNameAsc(
|
||||||
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
||||||
if (currentSortOrder == "Asc") {
|
if (currentSortOrder == "Asc") {
|
||||||
// If already sorted ascending, reset to the initial state
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
users = List.from(initialUsers); // Reset to saved initial state
|
users = List.from(initialUsers);
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} else {
|
} else {
|
||||||
// Sort ascending
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "Asc";
|
currentSortOrder = "Asc";
|
||||||
users.sort((a, b) => a.userName!.compareTo(b.userName!));
|
users.sort((a, b) => a.firstName!.compareTo(b.firstName!));
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +140,6 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
void _toggleSortUsersByNameDesc(
|
void _toggleSortUsersByNameDesc(
|
||||||
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
||||||
if (currentSortOrder == "Desc") {
|
if (currentSortOrder == "Desc") {
|
||||||
// If already sorted descending, reset to the initial state
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
users = List.from(initialUsers); // Reset to saved initial state
|
users = List.from(initialUsers); // Reset to saved initial state
|
||||||
@ -120,7 +148,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
// Sort descending
|
// Sort descending
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "Desc";
|
currentSortOrder = "Desc";
|
||||||
users.sort((a, b) => b.userName!.compareTo(a.userName!));
|
users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,19 +156,17 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
void _toggleSortUsersByDateNewestToOldest(
|
void _toggleSortUsersByDateNewestToOldest(
|
||||||
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
||||||
if (currentSortOrderDate == "NewestToOldest") {
|
if (currentSortOrderDate == "NewestToOldest") {
|
||||||
// If already sorted ascending, reset to the initial state
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
currentSortOrderDate = "";
|
currentSortOrderDate = "";
|
||||||
users = List.from(initialUsers); // Reset to saved initial state
|
users = List.from(initialUsers);
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} else {
|
} else {
|
||||||
// Sort ascending
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
users.sort((a, b) {
|
users.sort((a, b) {
|
||||||
final dateA = _parseDateTime(a.creationDate!);
|
final dateA = _parseDateTime(a.createdDate);
|
||||||
final dateB = _parseDateTime(b.creationDate!);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
return dateB.compareTo(dateA); // Newest to oldest
|
return dateB.compareTo(dateA);
|
||||||
});
|
});
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
@ -149,19 +175,17 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
void _toggleSortUsersByDateOldestToNewest(
|
void _toggleSortUsersByDateOldestToNewest(
|
||||||
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
||||||
if (currentSortOrderDate == "OldestToNewest") {
|
if (currentSortOrderDate == "OldestToNewest") {
|
||||||
// If already sorted ascending, reset to the initial state
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
currentSortOrderDate = "";
|
currentSortOrderDate = "";
|
||||||
users = List.from(initialUsers); // Reset to saved initial state
|
users = List.from(initialUsers);
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} else {
|
} else {
|
||||||
// Sort ascending
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
users.sort((a, b) {
|
users.sort((a, b) {
|
||||||
final dateA = _parseDateTime(a.creationDate!);
|
final dateA = _parseDateTime(a.createdDate);
|
||||||
final dateB = _parseDateTime(b.creationDate!);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
return dateA.compareTo(dateB); // Newest to oldest
|
return dateA.compareTo(dateB);
|
||||||
});
|
});
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
}
|
}
|
||||||
@ -169,17 +193,94 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
DateTime _parseDateTime(String date) {
|
DateTime _parseDateTime(String date) {
|
||||||
try {
|
try {
|
||||||
// Split the date into day, month, and year
|
|
||||||
final dateParts = date.split('/');
|
final dateParts = date.split('/');
|
||||||
final day = int.parse(dateParts[0]);
|
final day = int.parse(dateParts[0]);
|
||||||
final month = int.parse(dateParts[1]);
|
final month = int.parse(dateParts[1]);
|
||||||
final year = int.parse(dateParts[2]);
|
final year = int.parse(dateParts[2]);
|
||||||
|
|
||||||
// Split the time into hours and minutes
|
|
||||||
|
|
||||||
return DateTime(year, month, day);
|
return DateTime(year, month, day);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw FormatException('Invalid date or time format: $date ');
|
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:equatable/equatable.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
sealed class UserTableEvent extends Equatable {
|
sealed class UserTableEvent extends Equatable {
|
||||||
const UserTableEvent();
|
const UserTableEvent();
|
||||||
@ -47,7 +48,6 @@ class StoreUsersEvent extends UserTableEvent {
|
|||||||
List<Object?> get props => [];
|
List<Object?> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DateNewestToOldestEvent extends UserTableEvent {
|
class DateNewestToOldestEvent extends UserTableEvent {
|
||||||
const DateNewestToOldestEvent();
|
const DateNewestToOldestEvent();
|
||||||
|
|
||||||
@ -61,3 +61,61 @@ class DateOldestToNewestEvent extends UserTableEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
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,
|
scrollDirection: Axis.horizontal,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: containerDecoration.copyWith(
|
decoration: containerDecoration.copyWith(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
||||||
child: FittedBox(
|
child: FittedBox(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
// Header Row with Resizable Columns
|
// Header Row with Resizable Columns
|
||||||
Container(
|
Container(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
decoration: containerDecoration.copyWith(
|
decoration: containerDecoration.copyWith(
|
||||||
color: ColorsManager.circleRolesBackground,
|
color: ColorsManager.circleRolesBackground,
|
||||||
borderRadius: BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
topLeft: Radius.circular(15),
|
topLeft: Radius.circular(15),
|
||||||
topRight: Radius.circular(15))),
|
topRight: Radius.circular(15))),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -167,23 +169,62 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
// Data Rows with Dividers
|
// Data Rows with Dividers
|
||||||
Container(
|
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),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Center(
|
||||||
|
child: Container(
|
||||||
|
// height: MediaQuery.of(context).size.height * 0.59,
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
decoration: containerDecoration.copyWith(
|
decoration: containerDecoration.copyWith(
|
||||||
color: ColorsManager.whiteColors,
|
color: ColorsManager.whiteColors,
|
||||||
borderRadius: const BorderRadius.only(
|
borderRadius: const BorderRadius.only(
|
||||||
bottomLeft: Radius.circular(15),
|
bottomLeft: Radius.circular(15),
|
||||||
bottomRight: Radius.circular(15))),
|
bottomRight: Radius.circular(15))),
|
||||||
child: Column(
|
child: ListView.builder(
|
||||||
children: widget.rows.map((row) {
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
int rowIndex = widget.rows.indexOf(row);
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.rows.length,
|
||||||
|
itemBuilder: (context, rowIndex) {
|
||||||
|
final row = widget.rows[rowIndex];
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 5, top: 10, right: 5, bottom: 10),
|
left: 5,
|
||||||
|
top: 10,
|
||||||
|
right: 5,
|
||||||
|
bottom: 10),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: List.generate(row.length, (index) {
|
children:
|
||||||
|
List.generate(row.length, (index) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: columnWidths[index],
|
width: columnWidths[index],
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
@ -200,8 +241,8 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
),
|
),
|
||||||
if (rowIndex < widget.rows.length - 1)
|
if (rowIndex < widget.rows.length - 1)
|
||||||
Row(
|
Row(
|
||||||
children: List.generate(widget.titles.length,
|
children: List.generate(
|
||||||
(index) {
|
widget.titles.length, (index) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: columnWidths[index],
|
width: columnWidths[index],
|
||||||
child: const Divider(
|
child: const Divider(
|
||||||
@ -210,11 +251,12 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
height: 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/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: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/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_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_event.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.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';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class UsersPage extends StatelessWidget {
|
class UsersPage extends StatelessWidget {
|
||||||
const UsersPage({super.key});
|
UsersPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final TextEditingController searchController = TextEditingController();
|
final TextEditingController searchController = TextEditingController();
|
||||||
@ -44,9 +49,9 @@ class UsersPage extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
padding: const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
color: status == "Invited"
|
color: status == "invited"
|
||||||
? ColorsManager.invitedOrange.withOpacity(0.5)
|
? ColorsManager.invitedOrange.withOpacity(0.5)
|
||||||
: status == "Active"
|
: status == "active"
|
||||||
? ColorsManager.activeGreen.withOpacity(0.5)
|
? ColorsManager.activeGreen.withOpacity(0.5)
|
||||||
: ColorsManager.disabledPink.withOpacity(0.5),
|
: ColorsManager.disabledPink.withOpacity(0.5),
|
||||||
),
|
),
|
||||||
@ -60,9 +65,9 @@ class UsersPage extends StatelessWidget {
|
|||||||
Text(
|
Text(
|
||||||
status,
|
status,
|
||||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
color: status == "Invited"
|
color: status == "invited"
|
||||||
? ColorsManager.invitedOrangeText
|
? ColorsManager.invitedOrangeText
|
||||||
: status == "Active"
|
: status == "active"
|
||||||
? ColorsManager.activeGreenText
|
? ColorsManager.activeGreenText
|
||||||
: ColorsManager.disabledRedText,
|
: ColorsManager.disabledRedText,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
@ -81,23 +86,14 @@ class UsersPage extends StatelessWidget {
|
|||||||
required Function()? onTap}) {
|
required Function()? onTap}) {
|
||||||
return Center(
|
return Center(
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: onTap,
|
||||||
final newStatus = status == 'Active'
|
|
||||||
? 'Disabled'
|
|
||||||
: status == 'Disabled'
|
|
||||||
? 'Invited'
|
|
||||||
: 'Active';
|
|
||||||
context
|
|
||||||
.read<UserTableBloc>()
|
|
||||||
.add(ChangeUserStatus(userId: userId, newStatus: newStatus));
|
|
||||||
},
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
const EdgeInsets.only(left: 5, right: 5, bottom: 5, top: 5),
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
status == "Invited"
|
status == "invited"
|
||||||
? Assets.invitedIcon
|
? Assets.invitedIcon
|
||||||
: status == "Active"
|
: status == "active"
|
||||||
? Assets.activeUser
|
? Assets.activeUser
|
||||||
: Assets.deActiveUser,
|
: Assets.deActiveUser,
|
||||||
height: 35,
|
height: 35,
|
||||||
@ -113,12 +109,14 @@ class UsersPage extends StatelessWidget {
|
|||||||
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||||
|
|
||||||
if (state is UsersLoadingState) {
|
if (state is UsersLoadingState) {
|
||||||
|
_blocRole.add(ChangePage(_blocRole.currentPage));
|
||||||
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is UsersLoadedState) {
|
} else if (state is UsersLoadedState) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
child: Column(
|
child: ListView(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
shrinkWrap: true,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
@ -131,6 +129,9 @@ class UsersPage extends StatelessWidget {
|
|||||||
width: screenSize.width * 0.4,
|
width: screenSize.width * 0.4,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: searchController,
|
controller: searchController,
|
||||||
|
onChanged: (value) {
|
||||||
|
context.read<UserTableBloc>().add(SearchUsers(value));
|
||||||
|
},
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
decoration: textBoxDecoration(radios: 15)!.copyWith(
|
decoration: textBoxDecoration(radios: 15)!.copyWith(
|
||||||
fillColor: ColorsManager.whiteColors,
|
fillColor: ColorsManager.whiteColors,
|
||||||
@ -158,9 +159,9 @@ class UsersPage extends StatelessWidget {
|
|||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return const AddNewUserDialog();
|
return const AddNewUserDialog();
|
||||||
},
|
},
|
||||||
).then((listDevice) {
|
).then((v) {
|
||||||
if (listDevice != null) {
|
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) {
|
if (columnIndex == 4) {
|
||||||
showDateFilterMenu(
|
showDateFilterMenu(
|
||||||
context: context,
|
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) {
|
if (columnIndex == 8) {
|
||||||
showDeActivateFilterMenu(
|
showDeActivateFilterMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@ -248,19 +340,37 @@ class UsersPage extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
rows: state.users.map((user) {
|
rows: state.users.map((user) {
|
||||||
return [
|
return [
|
||||||
Text(user.userName!),
|
Text('${user.firstName} ${user.lastName}'),
|
||||||
Text(user.userEmail!),
|
Text(user.email ?? ''),
|
||||||
const Text("Test"),
|
Text(user.jobTitle ?? ''),
|
||||||
const Text("Member"),
|
Text(user.roleType ?? ''),
|
||||||
Text(user.creationDate!),
|
Text(user.createdDate ?? ''),
|
||||||
Text(user.creationTime!),
|
Text(user.createdTime ?? ''),
|
||||||
Text(user.createdBy!),
|
Text(user.invitedBy),
|
||||||
changeIconStatus(
|
changeIconStatus(
|
||||||
status: user.status!,
|
status:
|
||||||
userId: user.id!,
|
user.isEnabled == false ? 'disabled' : user.status,
|
||||||
onTap: () {},
|
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(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// actionButton(
|
// actionButton(
|
||||||
@ -269,17 +379,80 @@ class UsersPage extends StatelessWidget {
|
|||||||
// ),
|
// ),
|
||||||
actionButton(
|
actionButton(
|
||||||
title: "Edit",
|
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(
|
actionButton(
|
||||||
title: "Delete",
|
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(),
|
}).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>(
|
scaffoldBody: BlocProvider<UserTableBloc>(
|
||||||
create: (context) => UserTableBloc()..add(const GetUsers()),
|
create: (context) => UserTableBloc()..add(const GetUsers()),
|
||||||
child: const UsersPage(),
|
child: UsersPage(),
|
||||||
)
|
)
|
||||||
// _blocRole.tapSelect == false
|
// _blocRole.tapSelect == false
|
||||||
// ? UsersPage(
|
// ? UsersPage(
|
||||||
|
@ -252,6 +252,7 @@ class CommunitySpaceManagementApi {
|
|||||||
path: ApiEndpoints.getSpaceHierarchy
|
path: ApiEndpoints.getSpaceHierarchy
|
||||||
.replaceAll('{communityId}', communityId),
|
.replaceAll('{communityId}', communityId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
|
print(json);
|
||||||
final spaceModels = (json['data'] as List)
|
final spaceModels = (json['data'] as List)
|
||||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
.toList();
|
.toList();
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:dio/dio.dart';
|
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/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/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/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.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 {
|
class UserPermissionApi {
|
||||||
static final HTTPService _httpService = HTTPService();
|
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 {
|
fetchRoles() async {
|
||||||
final response = await _httpService.get(
|
final response = await _httpService.get(
|
||||||
path: ApiEndpoints.roleTypes,
|
path: ApiEndpoints.roleTypes,
|
||||||
@ -67,6 +89,7 @@ class UserPermissionApi {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
print('sendInviteUser=$body');
|
||||||
|
|
||||||
return response ?? [];
|
return response ?? [];
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
@ -107,4 +130,95 @@ class UserPermissionApi {
|
|||||||
return e.toString();
|
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 roleTypes = '/role/types';
|
||||||
static const String permission = '/permission/{roleUuid}';
|
static const String permission = '/permission/{roleUuid}';
|
||||||
static const String inviteUser = '/invite-user';
|
static const String inviteUser = '/invite-user';
|
||||||
|
|
||||||
static const String checkEmail = '/invite-user/check-email';
|
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}';
|
// 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 */
|
/* End PBXAggregateTarget section */
|
||||||
|
|
||||||
/* Begin PBXBuildFile 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 */; };
|
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
|
||||||
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
|
||||||
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
|
||||||
@ -60,11 +62,12 @@
|
|||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference 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; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -85,6 +95,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
2D0F1F294F673EF0DB5E4CA1 /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -92,6 +103,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
108157F896CD9F637B06D7C0 /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -125,6 +137,7 @@
|
|||||||
331C80D6294CF71000263BE5 /* RunnerTests */,
|
331C80D6294CF71000263BE5 /* RunnerTests */,
|
||||||
33CC10EE2044A3C60003C045 /* Products */,
|
33CC10EE2044A3C60003C045 /* Products */,
|
||||||
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
D73912EC22F37F3D000D13A0 /* Frameworks */,
|
||||||
|
75DCDFECC7757C5159E8F0C5 /* Pods */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@ -172,9 +185,25 @@
|
|||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
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 */ = {
|
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
5DAF1C60594A51D692304366 /* Pods_Runner.framework */,
|
||||||
|
E148CBDFFE42BF88E8C34DE0 /* Pods_RunnerTests.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -186,6 +215,7 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
A1935203066F42991FF0ED43 /* [CP] Check Pods Manifest.lock */,
|
||||||
331C80D1294CF70F00263BE5 /* Sources */,
|
331C80D1294CF70F00263BE5 /* Sources */,
|
||||||
331C80D2294CF70F00263BE5 /* Frameworks */,
|
331C80D2294CF70F00263BE5 /* Frameworks */,
|
||||||
331C80D3294CF70F00263BE5 /* Resources */,
|
331C80D3294CF70F00263BE5 /* Resources */,
|
||||||
@ -204,11 +234,13 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
8ECFD939A4D371A145DBA191 /* [CP] Check Pods Manifest.lock */,
|
||||||
33CC10E92044A3C60003C045 /* Sources */,
|
33CC10E92044A3C60003C045 /* Sources */,
|
||||||
33CC10EA2044A3C60003C045 /* Frameworks */,
|
33CC10EA2044A3C60003C045 /* Frameworks */,
|
||||||
33CC10EB2044A3C60003C045 /* Resources */,
|
33CC10EB2044A3C60003C045 /* Resources */,
|
||||||
33CC110E2044A8840003C045 /* Bundle Framework */,
|
33CC110E2044A8840003C045 /* Bundle Framework */,
|
||||||
3399D490228B24CF009A79C7 /* ShellScript */,
|
3399D490228B24CF009A79C7 /* ShellScript */,
|
||||||
|
92D754792F50A5D35F6D5AEE /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -329,6 +361,67 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
|
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 */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
@ -380,6 +473,7 @@
|
|||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
331C80DB294CF71000263BE5 /* Debug */ = {
|
331C80DB294CF71000263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 96C46007EE0A4E9E1D6D74CE /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@ -394,6 +488,7 @@
|
|||||||
};
|
};
|
||||||
331C80DC294CF71000263BE5 /* Release */ = {
|
331C80DC294CF71000263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = A604E311B663FBF4B7C54DC5 /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
@ -408,6 +503,7 @@
|
|||||||
};
|
};
|
||||||
331C80DD294CF71000263BE5 /* Profile */ = {
|
331C80DD294CF71000263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 81F2F315AC5109F6F5D27BE6 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
@ -392,6 +392,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -53,6 +53,7 @@ dependencies:
|
|||||||
uuid: ^4.4.2
|
uuid: ^4.4.2
|
||||||
time_picker_spinner: ^1.0.0
|
time_picker_spinner: ^1.0.0
|
||||||
intl_phone_field: ^3.2.0
|
intl_phone_field: ^3.2.0
|
||||||
|
number_pagination: ^1.1.6
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user