user_invite

This commit is contained in:
mohammad
2024-12-18 17:11:37 +03:00
parent 298aab5116
commit 573852b4b4
10 changed files with 198 additions and 144 deletions
+1 -3
View File
@@ -95,9 +95,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
title: 'Move in',
icon: Assets.moveinIcon,
active: true,
onPress: (context) {
context.go(RoutesConst.rolesAndPermissions);
},
onPress: (context) {},
color: ColorsManager.primaryColor,
),
HomeItemModel(
@@ -22,9 +22,20 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
on<RoleEvent>(_getRolePermission);
on<PermissionEvent>(_getPermissions);
on<SearchPermission>(searchRolePermission);
on<SendInviteUsers>(_sendInvitUser);
on<ValidateBasicsStep>(_validateBasicsStep);
on<CheckRoleStepStatus>(isCompleteRoleFun);
}
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
if (formKey.currentState?.validate() ?? false) {
emit(const BasicsStepValidState());
} else {
emit(const BasicsStepInvalidState());
}
}
List<RolesUserModel> users = [];
String roleSelected = '';
Future<void> _getUsers(GetUsers event, Emitter<UsersState> emit) async {
emit(UsersLoadingState());
@@ -106,31 +117,42 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
int numberSpaces = 0;
int numberRole = 0;
isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
// isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
// emit(UsersLoadingState());
// isCompleteBasics = firstNameController.text.isNotEmpty &&
// lastNameController.text.isNotEmpty &&
// emailController.text.isNotEmpty;
// emit(ChangeStatusSteps());
// return isCompleteBasics;
// }
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState());
final emailRegex = RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
);
isCompleteBasics = firstNameController.text.isNotEmpty &&
lastNameController.text.isNotEmpty &&
emailController.text.isNotEmpty;
emailController.text.isNotEmpty &&
emailRegex.hasMatch(emailController.text);
emit(ChangeStatusSteps());
return isCompleteBasics;
return isCompleteBasics!;
}
void isCompleteSpacesFun(
CheckSpacesStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState());
try {
List<String> selectedIds = getSelectedIds(updatedCommunities);
isCompleteSpaces = selectedIds.isNotEmpty;
} catch (e) {
emit(ErrorState('Error while retrieving selected IDs: $e'));
return;
}
List<String> selectedIds = getSelectedIds(updatedCommunities);
isCompleteSpaces = selectedIds.isNotEmpty;
emit(ChangeStatusSteps());
}
bool checkRolePermissions() {
return true;
void isCompleteRoleFun(CheckRoleStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState());
isCompleteRolePermissions = roleSelected != '';
emit(ChangeStatusSteps());
}
Future<List<SpaceModel>> _fetchSpacesForCommunity(
@@ -249,6 +271,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
emit(UsersLoadingState());
permissions = await UserPermissionApi().fetchPermission(
event.roleUuid == "" ? roles.first.uuid : event.roleUuid);
roleSelected = event.roleUuid!;
emit(RolePermissionInitial());
} catch (e) {
emit(ErrorState('Error loading communities and spaces: $e'));
@@ -268,17 +291,22 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
return anyMatch;
}
_sendInvitUser(List<PermissionOption> nodes, String searchTerm) async {
emit(UsersLoadingState());
await UserPermissionApi().sendInviteUser(
email: emailController.text,
firstName: firstNameController.text,
jobTitle: jobTitleController.text,
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: '',
spaceUuids: selectedIds);
emit(RolePermissionInitial());
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
try {
emit(UsersLoadingState());
List<String> selectedIds = getSelectedIds(updatedCommunities);
await UserPermissionApi().sendInviteUser(
email: emailController.text,
firstName: firstNameController.text,
jobTitle: jobTitleController.text,
lastName: lastNameController.text,
phoneNumber: phoneController.text,
roleUuid: roleSelected,
spaceUuids: selectedIds);
emit(SaveState());
} catch (e) {
emit(ErrorState('Error: $e'));
}
}
void searchRolePermission(SearchPermission event, Emitter<UsersState> emit) {
@@ -12,12 +12,25 @@ class GetUsers extends UsersEvent {
List<Object?> get props => [];
}
class SendInviteUsers extends UsersEvent {
const SendInviteUsers();
@override
List<Object?> get props => [];
}
class CheckSpacesStepStatus extends UsersEvent {
const CheckSpacesStepStatus();
@override
List<Object?> get props => [];
}
class CheckRoleStepStatus extends UsersEvent {
const CheckRoleStepStatus();
@override
List<Object?> get props => [];
}
class LoadCommunityAndSpacesEvent extends UsersEvent {
const LoadCommunityAndSpacesEvent();
@override
@@ -85,3 +98,9 @@ class SelecteId extends UsersEvent {
@override
List<Object?> get props => [nodes];
}
class ValidateBasicsStep extends UsersEvent {
const ValidateBasicsStep();
@override
List<Object?> get props => [];
}
@@ -9,6 +9,7 @@ final class UsersInitial extends UsersState {
@override
List<Object> get props => [];
}
final class RolePermissionInitial extends UsersState {
@override
List<Object> get props => [];
@@ -24,6 +25,11 @@ final class UsersLoadingState extends UsersState {
List<Object> get props => [];
}
final class SaveState extends UsersState {
@override
List<Object> get props => [];
}
final class UsersLoadedState extends UsersState {
List<RolesUserModel> users = [];
UsersLoadedState({required this.users});
@@ -59,3 +65,15 @@ final class ChangeTapStatus extends UsersState {
@override
List<Object> get props => [select];
}
final class BasicsStepValidState extends UsersState {
const BasicsStepValidState();
@override
List<Object> get props => [];
}
class BasicsStepInvalidState extends UsersState {
const BasicsStepInvalidState();
@override
List<Object> get props => [];
}
@@ -88,37 +88,6 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
child: _getFormContent(),
),
const SizedBox(height: 20),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("Cancel"),
),
ElevatedButton(
onPressed: () {
if (_blocRole.formKey.currentState
?.validate() ??
false) {
// Proceed to next step or finish
setState(() {
if (currentStep < 3) {
currentStep++;
} else {
Navigator.of(context).pop();
}
});
}
},
child: Text(currentStep < 3
? "Next"
: "Finish"),
),
],
),
],
),
),
@@ -126,6 +95,53 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
],
),
),
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: () {
setState(() {
if (currentStep < 3) {
currentStep++;
if (currentStep == 2) {
_blocRole.add(const CheckStepStatus());
} else if (currentStep == 3) {
_blocRole
.add(const CheckSpacesStepStatus());
_blocRole
.add(const CheckSpacesStepStatus());
} else {
_blocRole.add(const SendInviteUsers());
}
}
});
},
child: Text(
currentStep < 3 ? "Next" : "Save",
style: TextStyle(
color: (_blocRole.isCompleteSpaces == false ||
_blocRole.isCompleteBasics ==
false ||
_blocRole
.isCompleteRolePermissions ==
false) &&
currentStep == 3
? ColorsManager.grayColor
: ColorsManager.secondaryColor),
),
),
],
),
),
],
),
));
@@ -209,8 +225,12 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
return GestureDetector(
onTap: () {
setState(() {
bloc.add(const CheckSpacesStepStatus());
currentStep = step;
});
Future.delayed(const Duration(milliseconds: 500), () {
bloc.add(const ValidateBasicsStep());
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -270,6 +290,7 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
setState(() {
currentStep = step;
bloc.add(const CheckSpacesStepStatus());
bloc.add(const CheckStepStatus());
});
},
child: Column(
@@ -14,6 +14,9 @@ class BasicsView extends StatelessWidget {
Widget build(BuildContext context) {
return BlocBuilder<UsersBloc, UsersState>(builder: (context, state) {
final _blocRole = BlocProvider.of<UsersBloc>(context);
if (state is BasicsStepInvalidState) {
_blocRole.formKey.currentState?.validate();
}
return Form(
key: _blocRole.formKey,
child: ListView(
@@ -184,7 +187,14 @@ class BasicsView extends StatelessWidget {
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Enter last name';
return 'Enter Email Address';
}
// Regular expression for email validation
final emailRegex = RegExp(
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
);
if (!emailRegex.hasMatch(value)) {
return 'Enter a valid Email Address';
}
return null;
},
@@ -213,49 +223,6 @@ class BasicsView extends StatelessWidget {
),
],
)),
// InternationalPhoneNumberInput(
// spaceBetweenSelectorAndTextField: 50,
// initialValue: PhoneNumber(isoCode: 'AE'),
// inputDecoration: inputTextFormDeco(
// hintText: "x xxx xxxx",
// ).copyWith(
// hintStyle: context.textTheme.bodyMedium?.copyWith(
// fontWeight: FontWeight.w400,
// fontSize: 12,
// color: ColorsManager.textGray),
// ),
// onInputChanged: (PhoneNumber number) {
// print(number.phoneNumber);
// },
// onInputValidated: (bool value) {
// print(value);
// },
// selectorConfig: const SelectorConfig(
// selectorType: PhoneInputSelectorType.BOTTOM_SHEET,
// useBottomSheetSafeArea: true,
// leadingPadding: 15,
// trailingSpace: false,
// setSelectorButtonAsPrefixIcon: true),
// ignoreBlank: true,
// autoValidateMode: AutovalidateMode.disabled,
// selectorTextStyle:
// TextStyle(color: ColorsManager.blackColor),
// // initialValue: number,
// // textFieldController: controller,
// formatInput: true,
// keyboardType: const TextInputType.numberWithOptions(
// signed: true, decimal: true),
// // inputBorder: OutlineInputBorder(
// // borderSide:
// // BorderSide(color: Colors.black,)),
// onSaved: (PhoneNumber number) {
// print('On Saved: $number');
// },
// textStyle:
// const TextStyle(color: ColorsManager.blackColor),
// ),
IntlPhoneField(
pickerDialogStyle: PickerDialogStyle(),
dropdownIconPosition: IconPosition.leading,
@@ -270,36 +237,12 @@ class BasicsView extends StatelessWidget {
fontSize: 12,
color: ColorsManager.textGray),
),
initialCountryCode: 'AE',
style: TextStyle(color: Colors.black),
onChanged: (phone) {
print(phone.completeNumber);
},
)
// Padding(
// padding: const EdgeInsets.all(8.0),
// child: TextFormField(
// style: const TextStyle(color: Colors.black),
// controller: _blocRole.phoneController,
// decoration: inputTextFormDeco(
// hintText: "05x xxx xxxx",
// ).copyWith(
// hintStyle: context.textTheme.bodyMedium?.copyWith(
// fontWeight: FontWeight.w400,
// fontSize: 12,
// color: ColorsManager.textGray),
// ),
// validator: (value) {
// if (value == null || value.isEmpty) {
// return 'Please enter a phone number';
// }
// return null;
// },
// keyboardType: TextInputType.phone,
// ),
// ),
],
),
),
@@ -71,6 +71,7 @@ class RolesAndPermission extends StatelessWidget {
const TextStyle(color: Colors.black),
controller: _blocRole.firstNameController,
onChanged: (value) {
_blocRole.add(SearchPermission(
nodes: _blocRole.permissions,
searchTerm: value));
+6 -6
View File
@@ -389,10 +389,10 @@ class Assets {
'assets/icons/current_process_icon.svg';
static const String uncomplete_ProcessIcon =
'assets/icons/uncompleate_process_icon.svg';
static const String wrongProcessIcon =
'assets/icons/wrong_process_icon.svg';
static const String arrowForward =
'assets/icons/arrow_forward.svg';
static const String arrowDown =
'assets/icons/arrow_down.svg';
static const String wrongProcessIcon = 'assets/icons/wrong_process_icon.svg';
static const String arrowForward = 'assets/icons/arrow_forward.svg';
static const String arrowDown = 'assets/icons/arrow_down.svg';
static const String userManagement = 'assets/icons/user_management.svg';
}
//user_management.svg
+31 -12
View File
@@ -53,7 +53,8 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
}
Future<void> _showPopupMenu(BuildContext context) async {
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromLTRB(
overlay.size.width,
@@ -86,11 +87,13 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
),
),
PopupMenuItem(
onTap: () {},
onTap: () {
context.go(RoutesConst.rolesAndPermissions);
},
child: ListTile(
leading: SvgPicture.asset(Assets.settings),
leading: SvgPicture.asset(Assets.userManagement),
title: Text(
"Settings",
"User Management",
style: context.textTheme.bodyMedium,
),
),
@@ -107,7 +110,8 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
height: 200,
width: 400,
child: Padding(
padding: const EdgeInsets.only(top: 24, left: 24, right: 24),
padding:
const EdgeInsets.only(top: 24, left: 24, right: 24),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
@@ -120,7 +124,10 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
padding: const EdgeInsets.only(top: 16),
child: Text(
'Log out of your Syncrow account',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black,
@@ -151,11 +158,15 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'${widget.user?.firstName ?? ''} ${widget.user?.lastName}',
style: Theme.of(context).textTheme.titleMedium!.copyWith(
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,
@@ -163,7 +174,10 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
),
Text(
' ${widget.user?.email}',
style: Theme.of(context).textTheme.bodySmall!.copyWith(
style: Theme.of(context)
.textTheme
.bodySmall!
.copyWith(
color: Colors.black,
),
),
@@ -189,7 +203,10 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
elevation: 1,
child: Text(
'Cancel',
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(
fontSize: 12,
color: Colors.black,
),
@@ -211,8 +228,10 @@ class _UserDropdownMenuState extends State<UserDropdownMenu> {
elevation: 1,
child: Text(
'Logout',
style:
Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 12, color: Colors.white),
style: Theme.of(context)
.textTheme
.bodyMedium!
.copyWith(fontSize: 12, color: Colors.white),
),
),
),