diff --git a/assets/icons/atoz_icon.png b/assets/icons/atoz_icon.png new file mode 100644 index 00000000..33a9c351 Binary files /dev/null and b/assets/icons/atoz_icon.png differ diff --git a/assets/icons/filter_table_icon.svg b/assets/icons/filter_table_icon.svg new file mode 100644 index 00000000..d90e983e --- /dev/null +++ b/assets/icons/filter_table_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/icons/ztoa_icon.png b/assets/icons/ztoa_icon.png new file mode 100644 index 00000000..003e5725 Binary files /dev/null and b/assets/icons/ztoa_icon.png differ diff --git a/lib/pages/common/custom_dialog.dart b/lib/pages/common/custom_dialog.dart index a40ef10f..9899bda4 100644 --- a/lib/pages/common/custom_dialog.dart +++ b/lib/pages/common/custom_dialog.dart @@ -12,7 +12,7 @@ Future showCustomDialog({ double? iconWidth, VoidCallback? onOkPressed, bool barrierDismissible = false, - required List actions, + List? actions, }) { return showDialog( context: context, diff --git a/lib/pages/roles_and_permission/model/role_type_model.dart b/lib/pages/roles_and_permission/model/role_type_model.dart index 1705e25c..16b24ec5 100644 --- a/lib/pages/roles_and_permission/model/role_type_model.dart +++ b/lib/pages/roles_and_permission/model/role_type_model.dart @@ -16,7 +16,7 @@ class RoleTypeModel { uuid: json['uuid'], createdAt: json['createdAt'], updatedAt: json['updatedAt'], - type: json['type'], + type: json['type'].toString().toLowerCase().replaceAll("_", " "), ); } } diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart similarity index 73% rename from lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart index f0e3c9ad..f0ddf01e 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart @@ -1,20 +1,19 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/common/custom_dialog.dart'; import 'package:syncrow_web/pages/roles_and_permission/model/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/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/model/tree_node_model.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/roles_and_permission.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/permission_option_model.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/user_permission.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; class UsersBloc extends Bloc { UsersBloc() : super(UsersInitial()) { - on(_getUsers); - on(_changeUserStatus); on(isCompleteBasicsFun); on(_onLoadCommunityAndSpaces); on(searchTreeNode); @@ -25,6 +24,7 @@ class UsersBloc extends Bloc { on(_sendInvitUser); on(_validateBasicsStep); on(isCompleteRoleFun); + on(checkEmail); } void _validateBasicsStep(ValidateBasicsStep event, Emitter emit) { if (formKey.currentState?.validate() ?? false) { @@ -34,74 +34,8 @@ class UsersBloc extends Bloc { } } - List users = []; String roleSelected = ''; - Future _getUsers(GetUsers event, Emitter emit) async { - emit(UsersLoadingState()); - try { - users = [ - RolesUserModel( - id: '1', - userName: 'user 1', - userEmail: 'test1@test.com', - action: '', - createdBy: 'Admin', - creationDate: '25/10/2024', - creationTime: '10:30 AM', - status: 'Invited', - ), - RolesUserModel( - id: '2', - userName: 'user 2', - userEmail: 'test2@test.com', - action: '', - createdBy: 'Admin', - creationDate: '25/10/2024', - creationTime: '10:30 AM', - status: 'Active', - ), - RolesUserModel( - id: '3', - userName: 'user 3', - userEmail: 'test3@test.com', - action: '', - createdBy: 'Admin', - creationDate: '25/10/2024', - creationTime: '10:30 AM', - status: 'Disabled', - ), - ]; - emit(UsersLoadedState(users: users)); - } catch (e) { - emit(ErrorState(e.toString())); - } - } - - void _changeUserStatus(ChangeUserStatus event, Emitter emit) { - try { - users = users.map((user) { - if (user.id == event.userId) { - return RolesUserModel( - id: user.id, - userName: user.userName, - userEmail: user.userEmail, - createdBy: user.createdBy, - creationDate: user.creationDate, - creationTime: user.creationTime, - status: event.newStatus, - action: user.action, - ); - } - return user; - }).toList(); - - emit(UsersLoadedState(users: users)); - } catch (e) { - emit(ErrorState(e.toString())); - } - } - final formKey = GlobalKey(); final TextEditingController firstNameController = TextEditingController(); final TextEditingController lastNameController = TextEditingController(); @@ -109,6 +43,9 @@ class UsersBloc extends Bloc { final TextEditingController phoneController = TextEditingController(); final TextEditingController jobTitleController = TextEditingController(); + final TextEditingController roleSearchController = TextEditingController(); + // final TextEditingController jobTitleController = TextEditingController(); + bool? isCompleteBasics; bool? isCompleteRolePermissions; bool? isCompleteSpaces; @@ -117,30 +54,6 @@ class UsersBloc extends Bloc { int numberSpaces = 0; int numberRole = 0; - // isCompleteBasicsFun(CheckStepStatus event, Emitter emit) { - // emit(UsersLoadingState()); - // isCompleteBasics = firstNameController.text.isNotEmpty && - // lastNameController.text.isNotEmpty && - // emailController.text.isNotEmpty; - // emit(ChangeStatusSteps()); - // return isCompleteBasics; - // } - - bool isCompleteBasicsFun(CheckStepStatus event, Emitter 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 && - emailRegex.hasMatch(emailController.text); - - emit(ChangeStatusSteps()); - return isCompleteBasics!; - } - void isCompleteSpacesFun( CheckSpacesStepStatus event, Emitter emit) { emit(UsersLoadingState()); @@ -184,7 +97,7 @@ class UsersBloc extends Bloc { ); }).toList(), ); - emit(ChangeStatusSteps()); + emit(SpacesLoadedState()); return updatedCommunities; } catch (e) { emit(ErrorState('Error loading communities and spaces: $e')); @@ -239,6 +152,7 @@ class UsersBloc extends Bloc { } List selectedIds = []; + List getSelectedIds(List nodes) { List selectedIds = []; for (var node in nodes) { @@ -259,7 +173,7 @@ class UsersBloc extends Bloc { try { emit(UsersLoadingState()); roles = await UserPermissionApi().fetchRoles(); - add(PermissionEvent(roleUuid: roles.first.uuid)); + // add(PermissionEvent(roleUuid: roles.first.uuid)); emit(RolePermissionInitial()); } catch (e) { emit(ErrorState('Error loading communities and spaces: $e')); @@ -294,18 +208,40 @@ class UsersBloc extends Bloc { _sendInvitUser(SendInviteUsers event, Emitter emit) async { try { emit(UsersLoadingState()); - List 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); + List selectedIds = getSelectedIds(updatedCommunities) ?? []; + bool res = await UserPermissionApi().sendInviteUser( + email: emailController.text, + 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: [ + TextButton( + onPressed: () { + Navigator.of(event.context).pop(); + Navigator.of(event.context).pop(); + }, + child: const Text('OK'), + ), + ], + ); + } else { + emit(const ErrorState('Failed to send invite.')); + } emit(SaveState()); } catch (e) { - emit(ErrorState('Error: $e')); + emit(ErrorState('Failed to send invite: ${e.toString()}')); } } @@ -319,6 +255,37 @@ class UsersBloc extends Bloc { emit(ChangeStatusSteps()); } + String checkEmailValid = ''; + + Future checkEmail( + CheckEmailEvent event, Emitter emit) async { + emit(UsersLoadingState()); + String? res = await UserPermissionApi().checkEmail( + emailController.text, + ); + checkEmailValid = res!; + emit(ChangeStatusSteps()); + } + + bool isCompleteBasicsFun(CheckStepStatus event, Emitter emit) { + emit(UsersLoadingState()); + add(const CheckEmailEvent()); + final emailRegex = RegExp( + r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', + ); + bool isEmailValid = emailRegex.hasMatch(emailController.text); + bool isEmailServerValid = checkEmailValid == 'Valid email'; + isCompleteBasics = firstNameController.text.isNotEmpty && + lastNameController.text.isNotEmpty && + emailController.text.isNotEmpty && + isEmailValid && + isEmailServerValid; + + emit(ChangeStatusSteps()); + emit(ValidateBasics()); + return isCompleteBasics!; + } + void _clearHighlightsRolePermission(List nodes) { for (var node in nodes) { node.isHighlighted = false; diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_event.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart similarity index 82% rename from lib/pages/roles_and_permission/users_page/bloc/users_event.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart index 633c1ea5..950726d4 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_event.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart @@ -1,21 +1,17 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/model/tree_node_model.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/roles_and_permission.dart'; +import 'package:flutter/material.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/tree_node_model.dart'; sealed class UsersEvent extends Equatable { const UsersEvent(); } -class GetUsers extends UsersEvent { - const GetUsers(); - @override - List get props => []; -} - class SendInviteUsers extends UsersEvent { - const SendInviteUsers(); + final BuildContext context; + const SendInviteUsers({required this.context}); @override - List get props => []; + List get props => [context]; } class CheckSpacesStepStatus extends UsersEvent { @@ -30,7 +26,6 @@ class CheckRoleStepStatus extends UsersEvent { List get props => []; } - class LoadCommunityAndSpacesEvent extends UsersEvent { const LoadCommunityAndSpacesEvent(); @override @@ -57,16 +52,6 @@ class GetBatchStatus extends UsersEvent { List get props => [uuids]; } -class ChangeUserStatus extends UsersEvent { - final String userId; - final String newStatus; - - const ChangeUserStatus({required this.userId, required this.newStatus}); - - @override - List get props => [userId, newStatus]; -} - class CheckStepStatus extends UsersEvent { final int? steps; const CheckStepStatus({this.steps}); @@ -104,3 +89,9 @@ class ValidateBasicsStep extends UsersEvent { @override List get props => []; } + +class CheckEmailEvent extends UsersEvent { + const CheckEmailEvent(); + @override + List get props => []; +} diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_status.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart similarity index 88% rename from lib/pages/roles_and_permission/users_page/bloc/users_status.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart index b5fc01d7..c1bf3512 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_status.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart @@ -30,11 +30,10 @@ final class SaveState extends UsersState { List get props => []; } -final class UsersLoadedState extends UsersState { - List users = []; - UsersLoadedState({required this.users}); +final class SpacesLoadedState extends UsersState { + SpacesLoadedState(); @override - List get props => [users]; + List get props => []; } final class ErrorState extends UsersState { @@ -77,3 +76,7 @@ class BasicsStepInvalidState extends UsersState { @override List get props => []; } +final class ValidateBasics extends UsersState { + @override + List get props => []; +} diff --git a/lib/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart new file mode 100644 index 00000000..c476ebb4 --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart @@ -0,0 +1,40 @@ +class PermissionOption { + String id; + String title; + bool isChecked; + bool isHighlighted; + List subOptions; + + PermissionOption({ + required this.id, + required this.title, + this.isChecked = false, + this.isHighlighted = false, + this.subOptions = const [], + }); + + factory PermissionOption.fromJson(Map json) { + return PermissionOption( + id: json['id'] ?? '', + title: json['title'].toString().toLowerCase().replaceAll("_", " ") ?? '', + isChecked: json['isChecked'] ?? false, + isHighlighted: json['isHighlighted'] ?? false, + subOptions: (json['subOptions'] as List?) + ?.map((sub) => PermissionOption.fromJson(sub)) + .toList() ?? + [], + ); + } + + Map toJson() { + return { + 'id': id, + 'title': title, + 'isChecked': isChecked, + 'isHighlighted': isHighlighted, + 'subOptions': subOptions.map((sub) => sub.toJson()).toList(), + }; + } +} + +enum CheckState { none, some, all } diff --git a/lib/pages/roles_and_permission/users_page/model/tree_node_model.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart similarity index 100% rename from lib/pages/roles_and_permission/users_page/model/tree_node_model.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart diff --git a/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/add_user_dialog.dart similarity index 92% rename from lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/view/add_user_dialog.dart index cae89058..14f7a0fb 100644 --- a/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/add_user_dialog.dart @@ -1,12 +1,12 @@ 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/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/basics_view.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/roles_and_permission.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/spaces_access_view.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'; @@ -56,7 +56,6 @@ class _AddNewUserDialogState extends State { Expanded( child: Row( children: [ - // Sidebar for Steps Expanded( child: Container( padding: const EdgeInsets.all(20), @@ -75,7 +74,6 @@ class _AddNewUserDialogState extends State { width: 1, color: ColorsManager.grayBorder, ), - // Main content (Form) Expanded( flex: 2, child: Padding( @@ -109,6 +107,8 @@ class _AddNewUserDialogState extends State { ), InkWell( onTap: () { + _blocRole.add(const CheckEmailEvent()); + setState(() { if (currentStep < 3) { currentStep++; @@ -117,11 +117,10 @@ class _AddNewUserDialogState extends State { } else if (currentStep == 3) { _blocRole .add(const CheckSpacesStepStatus()); - _blocRole - .add(const CheckSpacesStepStatus()); - } else { - _blocRole.add(const SendInviteUsers()); } + } else { + _blocRole + .add(SendInviteUsers(context: context)); } }); }, @@ -161,12 +160,84 @@ class _AddNewUserDialogState extends State { } } + 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(const 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(const CheckStepStatus()); + if (step3 == 3) { + bloc.add(const CheckRoleStepStatus()); + } }); }, child: Column( @@ -221,15 +292,14 @@ class _AddNewUserDialogState extends State { ); } - Widget _buildStep1Indicator(int step, String label, UsersBloc bloc) { + Widget _buildStep3Indicator(int step, String label, UsersBloc bloc) { return GestureDetector( onTap: () { setState(() { - bloc.add(const CheckSpacesStepStatus()); currentStep = step; - }); - Future.delayed(const Duration(milliseconds: 500), () { - bloc.add(const ValidateBasicsStep()); + step3 = step; + bloc.add(const CheckSpacesStepStatus()); + bloc.add(const CheckStepStatus()); }); }, child: Column( @@ -243,9 +313,9 @@ class _AddNewUserDialogState extends State { SvgPicture.asset( currentStep == step ? Assets.currentProcessIcon - : bloc.isCompleteBasics == false + : bloc.isCompleteRolePermissions == false ? Assets.wrongProcessIcon - : bloc.isCompleteBasics == true + : bloc.isCompleteRolePermissions == true ? Assets.completeProcessIcon : Assets.uncomplete_ProcessIcon, width: 25, @@ -283,63 +353,4 @@ class _AddNewUserDialogState extends State { ), ); } - - Widget _buildStep3Indicator(int step, String label, UsersBloc bloc) { - return GestureDetector( - onTap: () { - setState(() { - currentStep = step; - bloc.add(const CheckSpacesStepStatus()); - bloc.add(const CheckStepStatus()); - }); - }, - 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 - : 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, - ), - ), - ) - ], - ), - ); - } } diff --git a/lib/pages/roles_and_permission/users_page/view/basics_view.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/basics_view.dart similarity index 76% rename from lib/pages/roles_and_permission/users_page/view/basics_view.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/view/basics_view.dart index 22398ed3..a6ec686b 100644 --- a/lib/pages/roles_and_permission/users_page/view/basics_view.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/basics_view.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl_phone_field/countries.dart'; import 'package:intl_phone_field/country_picker_dialog.dart'; import 'package:intl_phone_field/intl_phone_field.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_event.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; @@ -30,7 +32,7 @@ class BasicsView extends StatelessWidget { color: Colors.black), ), const SizedBox( - height: 80, + height: 50, ), Text( 'To get started, fill out some basic information about who you’re adding as a user.', @@ -40,11 +42,10 @@ class BasicsView extends StatelessWidget { ), ), const SizedBox( - height: 25, + height: 35, ), Row( children: [ - // First Name Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -52,9 +53,6 @@ class BasicsView extends StatelessWidget { SizedBox( child: Row( children: [ - // SizedBox( - // width: 15, - // ), const Text( " * ", style: TextStyle( @@ -75,7 +73,14 @@ class BasicsView extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: TextFormField( - style: TextStyle(color: Colors.black), + style: + const TextStyle(color: ColorsManager.blackColor), + onChanged: (value) { + Future.delayed(const Duration(milliseconds: 200), + () { + _blocRole.add(ValidateBasicsStep()); + }); + }, controller: _blocRole.firstNameController, decoration: inputTextFormDeco( hintText: "Enter first name", @@ -96,10 +101,7 @@ class BasicsView extends StatelessWidget { ], ), ), - - SizedBox(width: 10), - - // Last Name + const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -125,8 +127,14 @@ class BasicsView extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: TextFormField( + onChanged: (value) { + Future.delayed(const Duration(milliseconds: 200), + () { + _blocRole.add(ValidateBasicsStep()); + }); + }, controller: _blocRole.lastNameController, - style: TextStyle(color: Colors.black), + style: const TextStyle(color: Colors.black), decoration: inputTextFormDeco(hintText: "Enter last name") .copyWith( @@ -149,7 +157,7 @@ class BasicsView extends StatelessWidget { ), ], ), - SizedBox(height: 10), + const SizedBox(height: 10), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -176,8 +184,14 @@ class BasicsView extends StatelessWidget { Padding( padding: const EdgeInsets.all(8.0), child: TextFormField( + onChanged: (value) { + Future.delayed(const Duration(milliseconds: 200), () { + _blocRole.add(const CheckStepStatus()); + _blocRole.add(ValidateBasicsStep()); + }); + }, controller: _blocRole.emailController, - style: TextStyle(color: Colors.black), + style: const TextStyle(color: ColorsManager.blackColor), decoration: inputTextFormDeco(hintText: "name@example.com") .copyWith( hintStyle: context.textTheme.bodyMedium?.copyWith( @@ -189,24 +203,24 @@ class BasicsView extends StatelessWidget { if (value == null || value.isEmpty) { 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'; } + if (_blocRole.checkEmailValid != "Valid email") { + return _blocRole.checkEmailValid; + } return null; }, ), ), ], ), - SizedBox(height: 10), + const SizedBox(height: 10), Row( children: [ - // Phone Number - Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -227,7 +241,8 @@ class BasicsView extends StatelessWidget { pickerDialogStyle: PickerDialogStyle(), dropdownIconPosition: IconPosition.leading, disableLengthCheck: true, - dropdownTextStyle: TextStyle(color: Colors.black), + dropdownTextStyle: + const TextStyle(color: ColorsManager.blackColor), textInputAction: TextInputAction.done, decoration: inputTextFormDeco( hintText: "05x xxx xxxx", @@ -238,18 +253,39 @@ class BasicsView extends StatelessWidget { color: ColorsManager.textGray), ), initialCountryCode: 'AE', - style: TextStyle(color: Colors.black), - onChanged: (phone) { - print(phone.completeNumber); - }, + countries: const [ + Country( + name: "United Arab Emirates", + nameTranslations: { + "en": "United Arab Emirates", + "ar": "الإمارات العربية المتحدة", + }, + flag: "🇦🇪", + code: "AE", + dialCode: "971", + minLength: 9, + maxLength: 9, + ), + Country( + name: "Saudi Arabia", + nameTranslations: { + "en": "Saudi Arabia", + "ar": "السعودية", + }, + flag: "🇸🇦", + code: "SA", + dialCode: "966", + minLength: 9, + maxLength: 9, + ), + ], + style: const TextStyle(color: Colors.black), + controller: _blocRole.phoneController, ) ], ), ), - - SizedBox(width: 10), - - // Job Title + const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -270,7 +306,8 @@ class BasicsView extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: TextFormField( controller: _blocRole.jobTitleController, - style: TextStyle(color: Colors.black), + style: + const TextStyle(color: ColorsManager.blackColor), decoration: inputTextFormDeco( hintText: "Job Title (Optional)") .copyWith( @@ -287,7 +324,7 @@ class BasicsView extends StatelessWidget { ), ], ), - SizedBox(height: 20), + const SizedBox(height: 20), ], ), ); diff --git a/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/delete_user_dialog.dart similarity index 96% rename from lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/view/delete_user_dialog.dart index f10fd4ed..002b0171 100644 --- a/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/delete_user_dialog.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_bloc.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class DeleteUserDialog extends StatefulWidget { diff --git a/lib/pages/roles_and_permission/users_page/add_user_dialog/view/permission_management.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/permission_management.dart new file mode 100644 index 00000000..b16cd8d2 --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/permission_management.dart @@ -0,0 +1,239 @@ +import 'package:flutter/material.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/model/permission_option_model.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class PermissionManagement extends StatefulWidget { + final UsersBloc? bloc; + const PermissionManagement({Key? key, this.bloc}) : super(key: key); + + @override + _PermissionManagementState createState() => _PermissionManagementState(); +} + +class _PermissionManagementState extends State { + void toggleOptionById(String id) { + setState(() { + for (var mainOption in widget.bloc!.permissions) { + if (mainOption.id == id) { + final isChecked = + checkifOneOfthemChecked(mainOption) == CheckState.all; + mainOption.isChecked = !isChecked; + + for (var subOption in mainOption.subOptions) { + subOption.isChecked = !isChecked; + for (var child in subOption.subOptions) { + child.isChecked = !isChecked; + } + } + return; + } + + for (var subOption in mainOption.subOptions) { + if (subOption.id == id) { + subOption.isChecked = !subOption.isChecked; + for (var child in subOption.subOptions) { + child.isChecked = subOption.isChecked; + } + mainOption.isChecked = + mainOption.subOptions.every((sub) => sub.isChecked); + return; + } + + for (var child in subOption.subOptions) { + if (child.id == id) { + child.isChecked = !child.isChecked; + subOption.isChecked = + subOption.subOptions.every((child) => child.isChecked); + mainOption.isChecked = + mainOption.subOptions.every((sub) => sub.isChecked); + return; + } + } + } + } + }); + } + + CheckState checkifOneOfthemChecked(PermissionOption mainOption) { + bool allSelected = true; + bool someSelected = false; + + for (var subOption in mainOption.subOptions) { + if (subOption.isChecked) { + someSelected = true; + } else { + allSelected = false; + } + + for (var child in subOption.subOptions) { + if (child.isChecked) { + someSelected = true; + } else { + allSelected = false; + } + } + } + + if (allSelected) { + return CheckState.all; + } else if (someSelected) { + return CheckState.some; + } else { + return CheckState.none; + } + } + + @override + Widget build(BuildContext context) { + return ListView.builder( + padding: const EdgeInsets.all(8), + itemCount: widget.bloc!.permissions.length, + itemBuilder: (context, index) { + final option = widget.bloc!.permissions[index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + InkWell( + // onTap: () => toggleOptionById(option.id), + child: Builder( + builder: (context) { + final checkState = checkifOneOfthemChecked(option); + + if (checkState == CheckState.all) { + return Image.asset( + Assets.CheckBoxChecked, + width: 20, + height: 20, + ); + } else if (checkState == CheckState.some) { + return Image.asset( + Assets.rectangleCheckBox, + width: 20, + height: 20, + ); + } else { + return Image.asset( + Assets.emptyBox, + width: 20, + height: 20, + ); + } + }, + ), + ), + const SizedBox(width: 8), + Text( + option.title, + style: context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w700, + fontSize: 12, + color: ColorsManager.blackColor), + ), + ], + ), + const SizedBox( + height: 10, + ), + ...option.subOptions.map((subOption) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + color: option.isHighlighted + ? Colors.blue.shade50 + : Colors.white, + child: Row( + children: [ + InkWell( + // onTap: () => toggleOptionById(subOption.id), + child: Builder( + builder: (context) { + final checkState = + checkifOneOfthemChecked(PermissionOption( + id: subOption.id, + title: subOption.title, + subOptions: [subOption], + )); + + if (checkState == CheckState.all) { + return Image.asset( + Assets.CheckBoxChecked, + width: 20, + height: 20, + ); + } else if (checkState == CheckState.some) { + return Image.asset( + Assets.rectangleCheckBox, + width: 20, + height: 20, + ); + } else { + return Image.asset( + Assets.emptyBox, + width: 20, + height: 20, + ); + } + }, + ), + ), + const SizedBox(width: 8), + Text( + subOption.title, + style: context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w700, + fontSize: 12, + color: ColorsManager.lightGreyColor), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(left: 50.0), + child: GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 2.0, + crossAxisSpacing: 0.2, + childAspectRatio: 5, + ), + itemCount: subOption.subOptions.length, + itemBuilder: (context, index) { + final child = subOption.subOptions[index]; + return CheckboxListTile( + selectedTileColor: child.isHighlighted + ? Colors.blue.shade50 + : Colors.white, + dense: true, + controlAffinity: ListTileControlAffinity.leading, + title: Text( + child.title, + style: context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.lightGreyColor), + ), + value: child.isChecked, + onChanged: (value) => toggleOptionById(child.id), + enabled: false, + ); + }, + ), + ) + ], + ); + }).toList(), + ], + ); + }, + ); + } +} diff --git a/lib/pages/roles_and_permission/users_page/add_user_dialog/view/role_dropdown.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/role_dropdown.dart new file mode 100644 index 00000000..1bc9331e --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/role_dropdown.dart @@ -0,0 +1,115 @@ +import 'package:flutter/material.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/utils/color_manager.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class RoleDropdown extends StatefulWidget { + final UsersBloc? bloc; + const RoleDropdown({super.key, this.bloc}); + + @override + _RoleDropdownState createState() => _RoleDropdownState(); +} + +class _RoleDropdownState extends State { + late String selectedRole; + + @override + void initState() { + super.initState(); + selectedRole = widget.bloc!.roleSelected; + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(10.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Row( + children: [ + Text( + " * ", + style: TextStyle( + color: ColorsManager.red, + fontWeight: FontWeight.w900, + fontSize: 15, + ), + ), + Text( + "Role", + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 16, + color: Colors.black, + ), + ), + ], + ), + const SizedBox(height: 8), + SizedBox( + child: DropdownButtonFormField( + dropdownColor: ColorsManager.whiteColors, + alignment: Alignment.center, + focusColor: Colors.white, + autofocus: true, + value: selectedRole.isNotEmpty ? selectedRole : null, + items: widget.bloc!.roles.map((role) { + return DropdownMenuItem( + value: role.uuid, + child: Text(role.type), + ); + }).toList(), + onChanged: (value) { + setState(() { + selectedRole = value!; + }); + widget.bloc!.roleSelected = selectedRole; + widget.bloc! + .add(PermissionEvent(roleUuid: widget.bloc!.roleSelected)); + }, + icon: const SizedBox.shrink(), + borderRadius: const BorderRadius.all(Radius.circular(10)), + hint: const Padding( + padding: EdgeInsets.only(left: 10), + child: Text( + "Please Select", + style: TextStyle( + color: ColorsManager.textGray, + ), + ), + ), + decoration: inputTextFormDeco().copyWith( + contentPadding: EdgeInsets.zero, + suffixIcon: Container( + padding: EdgeInsets.zero, + width: 70, + height: 45, + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: const BorderRadius.only( + bottomRight: Radius.circular(10), + topRight: Radius.circular(10), + ), + border: Border.all( + color: ColorsManager.textGray, + width: 1.0, + ), + ), + child: const Center( + child: Icon( + Icons.keyboard_arrow_down, + color: ColorsManager.textGray, + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/roles_and_permission/users_page/add_user_dialog/view/roles_and_permission.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/roles_and_permission.dart new file mode 100644 index 00000000..b054a88c --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/roles_and_permission.dart @@ -0,0 +1,127 @@ +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/permission_management.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/role_dropdown.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class RolesAndPermission extends StatelessWidget { + const RolesAndPermission({super.key}); + @override + Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + + return BlocBuilder(builder: (context, state) { + final _blocRole = BlocProvider.of(context); + return Container( + color: Colors.white, + child: Form( + key: _blocRole.formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text( + 'Role & Permissions', + style: context.textTheme.bodyLarge?.copyWith( + fontWeight: FontWeight.w700, + fontSize: 20, + color: Colors.black), + ), + const SizedBox( + height: 15, + ), + SizedBox( + width: 350, + height: 100, + child: RoleDropdown( + bloc: _blocRole, + )), + const SizedBox(height: 10), + Expanded( + child: SizedBox( + child: Column( + children: [ + Expanded( + flex: 2, + child: Container( + decoration: const BoxDecoration( + color: ColorsManager.CircleRolesBackground, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + topLeft: Radius.circular(20)), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(20)), + border: Border.all( + color: ColorsManager.grayBorder)), + child: TextFormField( + style: + const TextStyle(color: Colors.black), + controller: + _blocRole.roleSearchController, + onChanged: (value) { + _blocRole.add(SearchPermission( + nodes: _blocRole.permissions, + searchTerm: value)); + }, + decoration: textBoxDecoration(radios: 20)! + .copyWith( + fillColor: Colors.white, + suffixIcon: Padding( + padding: + const EdgeInsets.only(right: 16), + child: SvgPicture.asset( + Assets.textFieldSearch, + width: 24, + height: 24, + ), + ), + hintStyle: context.textTheme.bodyMedium + ?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.textGray), + ), + ), + ), + ), + ], + ), + ), + ), + ), + Expanded( + flex: 7, + child: Container( + color: ColorsManager.CircleRolesBackground, + padding: const EdgeInsets.all(8.0), + child: Container( + color: ColorsManager.whiteColors, + child: PermissionManagement( + bloc: _blocRole, + )))) + ], + ), + ), + ), + ], + ), + ), + ); + }); + } +} diff --git a/lib/pages/roles_and_permission/users_page/view/spaces_access_view.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/spaces_access_view.dart similarity index 98% rename from lib/pages/roles_and_permission/users_page/view/spaces_access_view.dart rename to lib/pages/roles_and_permission/users_page/add_user_dialog/view/spaces_access_view.dart index 4bc330b2..f942d1d5 100644 --- a/lib/pages/roles_and_permission/users_page/view/spaces_access_view.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/spaces_access_view.dart @@ -1,10 +1,10 @@ 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/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/model/tree_node_model.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'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; diff --git a/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_bloc.dart b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_bloc.dart new file mode 100644 index 00000000..2a31a91f --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_bloc.dart @@ -0,0 +1,185 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart'; + +class UserTableBloc extends Bloc { + UserTableBloc() : super(TableInitial()) { + on(_getUsers); + on(_changeUserStatus); + on(_toggleSortUsersByNameAsc); + on(_toggleSortUsersByNameDesc); + on(_toggleSortUsersByDateOldestToNewest); + on(_toggleSortUsersByDateNewestToOldest); + } + + List users = []; + List initialUsers = []; // Save the initial state + String currentSortOrder = ''; // Keeps track of the current sorting order + String currentSortOrderDate = ''; // Keeps track of the current sorting order + + Future _getUsers(GetUsers event, Emitter emit) async { + emit(UsersLoadingState()); + try { + users = [ + RolesUserModel( + id: '1', + userName: 'b 1', + userEmail: 'test1@test.com', + action: '', + createdBy: 'Admin', + creationDate: '25/10/2024', + creationTime: '10:30 AM', + status: 'Invited', + ), + RolesUserModel( + id: '2', + userName: 'a 2', + userEmail: 'test2@test.com', + action: '', + createdBy: 'Admin', + creationDate: '24/10/2024', + creationTime: '2:30 PM', + status: 'Active', + ), + RolesUserModel( + id: '3', + userName: 'c 3', + userEmail: 'test3@test.com', + action: '', + createdBy: 'Admin', + creationDate: '23/10/2024', + creationTime: '9:00 AM', + status: 'Disabled', + ), + ]; + // Sort users by newest to oldest as default + users.sort((a, b) { + final dateA = _parseDateTime(a.creationDate!); + final dateB = _parseDateTime(b.creationDate!); + return dateB.compareTo(dateA); // Newest to oldest + }); + initialUsers = List.from(users); // Save the initial state + emit(UsersLoadedState(users: users)); + } catch (e) { + emit(ErrorState(e.toString())); + } + } + + void _changeUserStatus(ChangeUserStatus event, Emitter emit) { + try { + users = users.map((user) { + if (user.id == event.userId) { + return RolesUserModel( + id: user.id, + userName: user.userName, + userEmail: user.userEmail, + createdBy: user.createdBy, + creationDate: user.creationDate, + creationTime: user.creationTime, + status: event.newStatus, + action: user.action, + ); + } + return user; + }).toList(); + + emit(UsersLoadedState(users: users)); + } catch (e) { + emit(ErrorState(e.toString())); + } + } + + void _toggleSortUsersByNameAsc( + SortUsersByNameAsc event, Emitter emit) { + if (currentSortOrder == "Asc") { + // If already sorted ascending, reset to the initial state + emit(UsersLoadingState()); + currentSortOrder = ""; + users = List.from(initialUsers); // Reset to saved initial state + emit(UsersLoadedState(users: users)); + } else { + // Sort ascending + emit(UsersLoadingState()); + currentSortOrder = "Asc"; + users.sort((a, b) => a.userName!.compareTo(b.userName!)); + emit(UsersLoadedState(users: users)); + } + } + + void _toggleSortUsersByNameDesc( + SortUsersByNameDesc event, Emitter emit) { + if (currentSortOrder == "Desc") { + // If already sorted descending, reset to the initial state + emit(UsersLoadingState()); + currentSortOrder = ""; + users = List.from(initialUsers); // Reset to saved initial state + emit(UsersLoadedState(users: users)); + } else { + // Sort descending + emit(UsersLoadingState()); + currentSortOrder = "Desc"; + users.sort((a, b) => b.userName!.compareTo(a.userName!)); + emit(UsersLoadedState(users: users)); + } + } + + void _toggleSortUsersByDateNewestToOldest( + DateNewestToOldestEvent event, Emitter emit) { + if (currentSortOrderDate == "NewestToOldest") { + // If already sorted ascending, reset to the initial state + emit(UsersLoadingState()); + currentSortOrder = ""; + currentSortOrderDate = ""; + users = List.from(initialUsers); // Reset to saved initial state + emit(UsersLoadedState(users: users)); + } else { + // Sort ascending + emit(UsersLoadingState()); + users.sort((a, b) { + final dateA = _parseDateTime(a.creationDate!); + final dateB = _parseDateTime(b.creationDate!); + return dateB.compareTo(dateA); // Newest to oldest + }); + emit(UsersLoadedState(users: users)); + } + } + + void _toggleSortUsersByDateOldestToNewest( + DateOldestToNewestEvent event, Emitter emit) { + if (currentSortOrderDate == "OldestToNewest") { + // If already sorted ascending, reset to the initial state + emit(UsersLoadingState()); + currentSortOrder = ""; + currentSortOrderDate = ""; + users = List.from(initialUsers); // Reset to saved initial state + emit(UsersLoadedState(users: users)); + } else { + // Sort ascending + emit(UsersLoadingState()); + users.sort((a, b) { + final dateA = _parseDateTime(a.creationDate!); + final dateB = _parseDateTime(b.creationDate!); + return dateA.compareTo(dateB); // Newest to oldest + }); + emit(UsersLoadedState(users: users)); + } + } + + DateTime _parseDateTime(String date) { + try { + // Split the date into day, month, and year + final dateParts = date.split('/'); + final day = int.parse(dateParts[0]); + final month = int.parse(dateParts[1]); + final year = int.parse(dateParts[2]); + + // Split the time into hours and minutes + + return DateTime(year, month, day); + } catch (e) { + throw FormatException('Invalid date or time format: $date '); + } + } +} diff --git a/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart new file mode 100644 index 00000000..a6c77bd3 --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart @@ -0,0 +1,63 @@ +import 'package:equatable/equatable.dart'; + +sealed class UserTableEvent extends Equatable { + const UserTableEvent(); +} + +class GetRoles extends UserTableEvent { + const GetRoles(); + @override + List get props => []; +} + +class GetUsers extends UserTableEvent { + const GetUsers(); + @override + List get props => []; +} + +class ChangeUserStatus extends UserTableEvent { + final String userId; + final String newStatus; + + const ChangeUserStatus({required this.userId, required this.newStatus}); + + @override + List get props => [userId, newStatus]; +} + +class SortUsersByNameAsc extends UserTableEvent { + const SortUsersByNameAsc(); + + @override + List get props => []; +} + +class SortUsersByNameDesc extends UserTableEvent { + const SortUsersByNameDesc(); + + @override + List get props => []; +} + +class StoreUsersEvent extends UserTableEvent { + const StoreUsersEvent(); + + @override + List get props => []; +} + + +class DateNewestToOldestEvent extends UserTableEvent { + const DateNewestToOldestEvent(); + + @override + List get props => []; +} + +class DateOldestToNewestEvent extends UserTableEvent { + const DateOldestToNewestEvent(); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart new file mode 100644 index 00000000..2a132947 --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart @@ -0,0 +1,82 @@ +import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/roles_and_permission/model/roles_user_model.dart'; + +sealed class UserTableState extends Equatable { + const UserTableState(); +} + +final class TableInitial extends UserTableState { + @override + List get props => []; +} + +final class RolesLoadingState extends UserTableState { + @override + List get props => []; +} + +final class UsersLoadingState extends UserTableState { + @override + List get props => []; +} + +final class RolesLoadedState extends UserTableState { + @override + List get props => []; +} + +final class UsersLoadedState extends UserTableState { + List users = []; + UsersLoadedState({required this.users}); + @override + List get props => [users]; +} + +final class ErrorState extends UserTableState { + final String message; + + const ErrorState(this.message); + + @override + List get props => [message]; +} + +/// report state +final class SosReportLoadingState extends UserTableState { + @override + List get props => []; +} + +final class RolesErrorState extends UserTableState { + final String message; + + const RolesErrorState(this.message); + + @override + List get props => [message]; +} + +/// automation reports + +final class SosAutomationReportLoadingState extends UserTableState { + @override + List get props => []; +} + +final class SosAutomationReportErrorState extends UserTableState { + final String message; + + const SosAutomationReportErrorState(this.message); + + @override + List get props => [message]; +} + +final class ChangeTapStatus extends UserTableState { + bool select = true; + + ChangeTapStatus({required this.select}); + + @override + List get props => [select]; +} diff --git a/lib/pages/roles_and_permission/users_page/users_table/view/creation_date_filter.dart b/lib/pages/roles_and_permission/users_page/users_table/view/creation_date_filter.dart new file mode 100644 index 00000000..45ebc3ae --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/view/creation_date_filter.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +Future showDateFilterMenu({ + required BuildContext context, + Function()? aToZTap, + Function()? zToaTap, + String? isSelected, +}) async { + final RenderBox overlay = + Overlay.of(context).context.findRenderObject() as RenderBox; + final RelativeRect position = RelativeRect.fromRect( + Rect.fromLTRB( + overlay.size.width / 2, + 240, + 0, + overlay.size.height, + ), + Offset.zero & overlay.size, + ); + + await showMenu( + context: context, + position: position, + color: ColorsManager.whiteColors, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + bottomLeft: Radius.circular(10), + ), + ), + items: [ + PopupMenuItem( + onTap: aToZTap, + child: ListTile( + leading: Image.asset( + Assets.AtoZIcon, + width: 25, + ), + title: Text( + "Sort from newest to oldest", + // style: context.textTheme.bodyMedium, + style: TextStyle( + color: isSelected == "NewestToOldest" + ? Colors.black + : Colors.blueGrey), + ), + ), + ), + PopupMenuItem( + onTap: zToaTap, + child: ListTile( + leading: Image.asset( + Assets.ZtoAIcon, + width: 25, + ), + title: Text( + "Sort from oldest to newest", + style: TextStyle( + color: isSelected == "OldestToNewest" + ? Colors.black + : Colors.blueGrey), + ), + ), + ), + ], + ).then((value) { + // setState(() { + // _isDropdownOpen = false; + // }); + }); +} diff --git a/lib/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart b/lib/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart new file mode 100644 index 00000000..e78eae6b --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +Future showDeActivateFilterMenu({ + required BuildContext context, + Function()? aToZTap, + Function()? zToaTap, + String? isSelected, +}) async { + final RenderBox overlay = + Overlay.of(context).context.findRenderObject() as RenderBox; + final RelativeRect position = RelativeRect.fromRect( + Rect.fromLTRB( + overlay.size.width / 2, + 240, + 0, + overlay.size.height, + ), + Offset.zero & overlay.size, + ); + + await showMenu( + context: context, + position: position, + color: ColorsManager.whiteColors, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + bottomLeft: Radius.circular(10), + ), + ), + items: [ + PopupMenuItem( + onTap: aToZTap, + child: ListTile( + leading: Image.asset( + Assets.AtoZIcon, + width: 25, + ), + title: Text( + "Sort A to Z", + // style: context.textTheme.bodyMedium, + style: TextStyle( + color: isSelected == "NewestToOldest" + ? Colors.black + : Colors.blueGrey), + ), + ), + ), + PopupMenuItem( + onTap: zToaTap, + child: ListTile( + leading: Image.asset( + Assets.ZtoAIcon, + width: 25, + ), + title: Text( + "Sort Z to A", + style: TextStyle( + color: isSelected == "OldestToNewest" + ? Colors.black + : Colors.blueGrey), + ), + ), + ), + ], + ).then((value) { + // setState(() { + // _isDropdownOpen = false; + // }); + }); +} diff --git a/lib/pages/roles_and_permission/users_page/users_table/view/name_filter.dart b/lib/pages/roles_and_permission/users_page/users_table/view/name_filter.dart new file mode 100644 index 00000000..e869e10b --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/view/name_filter.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +Future showNameMenu({ + required BuildContext context, + Function()? aToZTap, + Function()? zToaTap, + String? isSelected, +}) async { + final RenderBox overlay = + Overlay.of(context).context.findRenderObject() as RenderBox; + final RelativeRect position = RelativeRect.fromRect( + Rect.fromLTRB( + overlay.size.width / 25, + 240, + 0, + overlay.size.height, + ), + Offset.zero & overlay.size, + ); + + await showMenu( + context: context, + position: position, + color: ColorsManager.whiteColors, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(10), + bottomLeft: Radius.circular(10), + ), + ), + items: [ + PopupMenuItem( + onTap: aToZTap, + child: ListTile( + leading: Image.asset( + Assets.AtoZIcon, + width: 25, + ), + title: Text( + "Sort A to Z", + // style: context.textTheme.bodyMedium, + style: TextStyle( + color: isSelected == "Asc" ? Colors.black : Colors.blueGrey), + ), + ), + ), + PopupMenuItem( + onTap: zToaTap, + child: ListTile( + leading: Image.asset( + Assets.ZtoAIcon, + width: 25, + ), + title: Text( + "Sort Z to A", + style: TextStyle( + color: isSelected == "Desc" ? Colors.black : Colors.blueGrey), + ), + ), + ), + ], + ).then((value) { + // setState(() { + // _isDropdownOpen = false; + // }); + }); +} diff --git a/lib/pages/roles_and_permission/users_page/users_table/view/user_table.dart b/lib/pages/roles_and_permission/users_page/users_table/view/user_table.dart new file mode 100644 index 00000000..926ac92a --- /dev/null +++ b/lib/pages/roles_and_permission/users_page/users_table/view/user_table.dart @@ -0,0 +1,299 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class DynamicTableScreen extends StatefulWidget { + final List titles; + final List> rows; + final void Function(int columnIndex)? onFilter; + + DynamicTableScreen( + {required this.titles, required this.rows, required this.onFilter}); + + @override + _DynamicTableScreenState createState() => _DynamicTableScreenState(); +} + +class _DynamicTableScreenState extends State + with WidgetsBindingObserver { + late List columnWidths; + + // @override + // void initState() { + // super.initState(); + // // Initialize column widths with default sizes proportional to the screen width + // // Assigning placeholder values here. The actual sizes will be updated in `build`. + // } + @override + void initState() { + super.initState(); + columnWidths = List.filled(widget.titles.length, 150.0); + WidgetsBinding.instance.addObserver(this); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeMetrics() { + super.didChangeMetrics(); + // Screen size might have changed + final newScreenWidth = MediaQuery.of(context).size.width; + setState(() { + columnWidths = List.generate(widget.titles.length, (index) { + if (index == 1) { + return newScreenWidth * + 0.12; // 20% of screen width for the second column + } else if (index == 9) { + return newScreenWidth * + 0.2; // 25% of screen width for the tenth column + } + return newScreenWidth * + 0.09; // Default to 10% of screen width for other columns + }); + }); + } + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + + // Initialize column widths if they are still set to placeholder values + if (columnWidths.every((width) => width == 120.0)) { + columnWidths = List.generate(widget.titles.length, (index) { + if (index == 1) { + return screenWidth * 0.11; + } else if (index == 9) { + return screenWidth * 0.2; + } + return screenWidth * 0.11; + }); + setState(() {}); + } + return Container( + child: SingleChildScrollView( + clipBehavior: Clip.none, + scrollDirection: Axis.horizontal, + child: Container( + decoration: containerDecoration.copyWith( + borderRadius: const BorderRadius.all(Radius.circular(20))), + child: FittedBox( + child: Column( + children: [ + // Header Row with Resizable Columns + Container( + decoration: containerDecoration.copyWith( + color: ColorsManager.CircleRolesBackground, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15), + topRight: Radius.circular(15))), + child: Row( + children: List.generate(widget.titles.length, (index) { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + FittedBox( + child: Container( + padding: const EdgeInsets.only(left: 5, right: 5), + width: columnWidths[index], + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + child: Text( + widget.titles[index], + maxLines: 2, + style: const TextStyle( + overflow: TextOverflow.ellipsis, + fontWeight: FontWeight.w400, + fontSize: 13, + color: ColorsManager.grayColor, + ), + ), + ), + if (index != 1 && + index != 9 && + index != 7 && + index != 5) + FittedBox( + child: IconButton( + icon: SvgPicture.asset( + Assets.filterTableIcon, + fit: BoxFit.none, + ), + onPressed: () { + if (widget.onFilter != null) { + widget.onFilter!(index); + } + }, + ), + ) + ], + ), + ), + ), + GestureDetector( + onHorizontalDragUpdate: (details) { + setState(() { + columnWidths[index] = (columnWidths[index] + + details.delta.dx) + .clamp( + 150.0, 300.0); // Minimum & Maximum size + }); + }, + child: MouseRegion( + cursor: SystemMouseCursors + .resizeColumn, // Set the cursor to resize + child: Container( + color: Colors.green, + child: Container( + color: ColorsManager.boxDivider, + width: 1, + height: 50, // Height of the header cell + ), + ), + ), + ), + ], + ); + }), + ), + ), + // Data Rows with Dividers + Container( + decoration: containerDecoration.copyWith( + color: ColorsManager.whiteColors, + borderRadius: const BorderRadius.only( + bottomLeft: Radius.circular(15), + bottomRight: Radius.circular(15))), + child: Column( + children: widget.rows.map((row) { + int rowIndex = widget.rows.indexOf(row); + return Column( + children: [ + Container( + child: Padding( + padding: const EdgeInsets.only( + left: 5, top: 10, right: 5, bottom: 10), + child: Row( + children: List.generate(row.length, (index) { + return SizedBox( + width: columnWidths[index], + child: SizedBox( + child: Padding( + padding: const EdgeInsets.only( + left: 15, right: 10), + child: row[index], + ), + ), + ); + }), + ), + ), + ), + if (rowIndex < widget.rows.length - 1) + Row( + children: List.generate(widget.titles.length, + (index) { + return SizedBox( + width: columnWidths[index], + child: const Divider( + color: ColorsManager.boxDivider, + thickness: 1, + height: 1, + ), + ); + })) + // Add a Divider below each row except the last one + ], + ); + }).toList(), + ), + ), + ], + ), + ), + ), + ), + ); + } +} + + + + // Widget build(BuildContext context) { + // return Scaffold( + // body: SingleChildScrollView( + // scrollDirection: Axis.horizontal, + // child: SingleChildScrollView( + // scrollDirection: Axis.vertical, + // child: Column( + // children: [ + // // Header Row with Resizable Columns + // Container( + // color: Colors.green, + // child: Row( + // children: List.generate(widget.titles.length, (index) { + // return Row( + // children: [ + // Container( + // width: columnWidths[index], + // decoration: const BoxDecoration( + // color: Colors.green, + // ), + // child: Text( + // widget.titles[index], + // style: TextStyle(fontWeight: FontWeight.bold), + // textAlign: TextAlign.center, + // ), + // ), + // GestureDetector( + // onHorizontalDragUpdate: (details) { + // setState(() { + // columnWidths[index] = (columnWidths[index] + + // details.delta.dx) + // .clamp(50.0, 300.0); // Minimum & Maximum size + // }); + // }, + // child: MouseRegion( + // cursor: SystemMouseCursors + // .resizeColumn, // Set the cursor to resize + // child: Container( + // color: Colors.green, + // child: Container( + // color: Colors.black, + // width: 1, + // height: 50, // Height of the header cell + // ), + // ), + // ), + // ), + // ], + // ); + // }), + // ), + // ), + // // Data Rows + // ...widget.rows.map((row) { + // return Row( + // children: List.generate(row.length, (index) { + // return Container( + // width: columnWidths[index], + // child: row[index], + // ); + // }), + // ); + // }).toList(), + // ], + // ), + // ), + // ), + // ); + // } \ No newline at end of file diff --git a/lib/pages/roles_and_permission/users_page/view/users_page.dart b/lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart similarity index 73% rename from lib/pages/roles_and_permission/users_page/view/users_page.dart rename to lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart index 273b8a34..758f062b 100644 --- a/lib/pages/roles_and_permission/users_page/view/users_page.dart +++ b/lib/pages/roles_and_permission/users_page/users_table/view/users_page.dart @@ -1,11 +1,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package import 'package:flutter_svg/flutter_svg.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/add_user_dialog.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/user_table.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/users_table/bloc/user_table_bloc.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_event.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/bloc/user_table_state.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/creation_date_filter.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/de_activate_filter.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/name_filter.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/users_table/view/user_table.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -85,7 +88,7 @@ class UsersPage extends StatelessWidget { ? 'Invited' : 'Active'; context - .read() + .read() .add(ChangeUserStatus(userId: userId, newStatus: newStatus)); }, child: Padding( @@ -104,12 +107,10 @@ class UsersPage extends StatelessWidget { ); } -// return RolesAndPermission(); -// } -// } - return BlocBuilder( + return BlocBuilder( builder: (context, state) { final screenSize = MediaQuery.of(context).size; + final _blocRole = BlocProvider.of(context); if (state is UsersLoadingState) { return const Center(child: CircularProgressIndicator()); @@ -158,7 +159,9 @@ class UsersPage extends StatelessWidget { return const AddNewUserDialog(); }, ).then((listDevice) { - if (listDevice != null) {} + if (listDevice != null) { + + } }); }, child: Container( @@ -181,6 +184,56 @@ class UsersPage extends StatelessWidget { ), const SizedBox(height: 25), DynamicTableScreen( + onFilter: (columnIndex) { + if (columnIndex == 0) { + showNameMenu( + context: context, + isSelected: _blocRole.currentSortOrder, + aToZTap: () { + context + .read() + .add(const SortUsersByNameAsc()); + }, + zToaTap: () { + context + .read() + .add(const SortUsersByNameDesc()); + }, + ); + } + if (columnIndex == 4) { + showDateFilterMenu( + context: context, + isSelected: _blocRole.currentSortOrderDate, + aToZTap: () { + context + .read() + .add(const DateNewestToOldestEvent()); + }, + zToaTap: () { + context + .read() + .add(const DateOldestToNewestEvent()); + }, + ); + } + if (columnIndex == 8) { + showDeActivateFilterMenu( + context: context, + isSelected: _blocRole.currentSortOrderDate, + aToZTap: () { + context + .read() + .add(const DateNewestToOldestEvent()); + }, + zToaTap: () { + context + .read() + .add(const DateOldestToNewestEvent()); + }, + ); + } + }, titles: const [ "Full Name", "Email Address", @@ -210,10 +263,10 @@ class UsersPage extends StatelessWidget { status(status: user.status!), Row( children: [ - actionButton( - title: "Activity Log", - onTap: () {}, - ), + // actionButton( + // title: "Activity Log", + // onTap: () {}, + // ), actionButton( title: "Edit", onTap: () {}, diff --git a/lib/pages/roles_and_permission/users_page/view/roles_and_permission.dart b/lib/pages/roles_and_permission/users_page/view/roles_and_permission.dart deleted file mode 100644 index 9b7ab542..00000000 --- a/lib/pages/roles_and_permission/users_page/view/roles_and_permission.dart +++ /dev/null @@ -1,489 +0,0 @@ -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/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; -import 'package:syncrow_web/utils/style.dart'; - -class RolesAndPermission extends StatelessWidget { - const RolesAndPermission({super.key}); - @override - Widget build(BuildContext context) { - final size = MediaQuery.of(context).size; - - return BlocBuilder(builder: (context, state) { - final _blocRole = BlocProvider.of(context); - return Container( - color: Colors.white, - child: Form( - key: _blocRole.formKey, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Text( - 'Role & Permissions', - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w700, - fontSize: 20, - color: Colors.black), - ), - const SizedBox( - height: 15, - ), - SizedBox( - width: 300, - height: 110, - child: DropdownExample( - bloc: _blocRole, - )), - const SizedBox(height: 10), - Expanded( - child: SizedBox( - child: Column( - children: [ - Expanded( - flex: 2, - child: Container( - decoration: const BoxDecoration( - color: ColorsManager.CircleRolesBackground, - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20)), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20)), - border: Border.all( - color: ColorsManager.grayBorder)), - child: TextFormField( - style: - const TextStyle(color: Colors.black), - controller: _blocRole.firstNameController, - onChanged: (value) { - - _blocRole.add(SearchPermission( - nodes: _blocRole.permissions, - searchTerm: value)); - }, - decoration: textBoxDecoration(radios: 20)! - .copyWith( - fillColor: Colors.white, - suffixIcon: Padding( - padding: - const EdgeInsets.only(right: 16), - child: SvgPicture.asset( - Assets.textFieldSearch, - width: 24, - height: 24, - ), - ), - hintStyle: context.textTheme.bodyMedium - ?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.textGray), - ), - ), - ), - ), - ], - ), - ), - ), - ), - Expanded( - flex: 7, - child: Container( - color: ColorsManager.CircleRolesBackground, - padding: const EdgeInsets.all(8.0), - child: Container( - color: ColorsManager.whiteColors, - child: DeviceManagement( - bloc: _blocRole, - )))) - ], - ), - ), - ), - ], - ), - ), - ); - }); - } -} - -class DropdownExample extends StatefulWidget { - final UsersBloc? bloc; - const DropdownExample({super.key, this.bloc}); - - @override - _DropdownExampleState createState() => _DropdownExampleState(); -} - -class _DropdownExampleState extends State { - String? selectedRole; - @override - void initState() { - super.initState(); - if (widget.bloc != null && widget.bloc!.roles.isNotEmpty) { - selectedRole = widget.bloc!.roles.first.uuid; - } - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(10.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - "Role", - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - color: Colors.black, - ), - ), - const SizedBox(height: 8), - SizedBox( - child: DropdownButtonFormField( - alignment: Alignment.center, - focusColor: Colors.white, - autofocus: true, - value: selectedRole, - items: widget.bloc!.roles.map((role) { - return DropdownMenuItem( - value: role.uuid, - child: Text(role.type), - ); - }).toList(), - onChanged: (value) { - setState(() { - selectedRole = value; - }); - widget.bloc!.add(PermissionEvent(roleUuid: selectedRole)); - }, - padding: EdgeInsets.zero, - icon: const SizedBox.shrink(), - borderRadius: const BorderRadius.all(Radius.circular(10)), - hint: const Padding( - padding: EdgeInsets.only(left: 20), - child: Text("Please Select"), - ), - decoration: inputTextFormDeco().copyWith( - contentPadding: EdgeInsets.zero, - suffixIcon: Container( - padding: EdgeInsets.zero, - width: 70, - height: 50, - decoration: BoxDecoration( - color: Colors.grey[200], - borderRadius: const BorderRadius.only( - bottomRight: Radius.circular(10), - topRight: Radius.circular(10), - ), - border: Border.all( - color: Colors.grey, - width: 1.0, - ), - ), - child: const Center( - child: Icon(Icons.keyboard_arrow_down), - ), - ), - ), - ), - ), - ], - ), - ); - } -} - -class DeviceManagement extends StatefulWidget { - final UsersBloc? bloc; - const DeviceManagement({Key? key, this.bloc}) : super(key: key); - - @override - _DeviceManagementState createState() => _DeviceManagementState(); -} - -class _DeviceManagementState extends State { - void toggleOptionById(String id) { - setState(() { - for (var mainOption in widget.bloc!.permissions) { - if (mainOption.id == id) { - final isChecked = - checkifOneOfthemChecked(mainOption) == CheckState.all; - mainOption.isChecked = !isChecked; - - for (var subOption in mainOption.subOptions) { - subOption.isChecked = !isChecked; - for (var child in subOption.subOptions) { - child.isChecked = !isChecked; - } - } - return; - } - - for (var subOption in mainOption.subOptions) { - if (subOption.id == id) { - subOption.isChecked = !subOption.isChecked; - for (var child in subOption.subOptions) { - child.isChecked = subOption.isChecked; - } - mainOption.isChecked = - mainOption.subOptions.every((sub) => sub.isChecked); - return; - } - - for (var child in subOption.subOptions) { - if (child.id == id) { - child.isChecked = !child.isChecked; - subOption.isChecked = - subOption.subOptions.every((child) => child.isChecked); - mainOption.isChecked = - mainOption.subOptions.every((sub) => sub.isChecked); - return; - } - } - } - } - }); - } - - CheckState checkifOneOfthemChecked(PermissionOption mainOption) { - bool allSelected = true; - bool someSelected = false; - - for (var subOption in mainOption.subOptions) { - if (subOption.isChecked) { - someSelected = true; - } else { - allSelected = false; - } - - for (var child in subOption.subOptions) { - if (child.isChecked) { - someSelected = true; - } else { - allSelected = false; - } - } - } - - if (allSelected) { - return CheckState.all; - } else if (someSelected) { - return CheckState.some; - } else { - return CheckState.none; - } - } - - @override - Widget build(BuildContext context) { - return ListView.builder( - padding: const EdgeInsets.all(8), - itemCount: widget.bloc!.permissions.length, - itemBuilder: (context, index) { - final option = widget.bloc!.permissions[index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - InkWell( - // onTap: () => toggleOptionById(option.id), - child: Builder( - builder: (context) { - final checkState = checkifOneOfthemChecked(option); - - if (checkState == CheckState.all) { - return Image.asset( - Assets.CheckBoxChecked, - width: 20, - height: 20, - ); - } else if (checkState == CheckState.some) { - return Image.asset( - Assets.rectangleCheckBox, - width: 20, - height: 20, - ); - } else { - return Image.asset( - Assets.emptyBox, - width: 20, - height: 20, - ); - } - }, - ), - ), - const SizedBox(width: 8), - Text( - option.title, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w700, - fontSize: 12, - color: ColorsManager.blackColor), - ), - ], - ), - const SizedBox( - height: 10, - ), - ...option.subOptions.map((subOption) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - color: option.isHighlighted - ? Colors.blue.shade50 - : Colors.white, - child: Row( - children: [ - InkWell( - // onTap: () => toggleOptionById(subOption.id), - child: Builder( - builder: (context) { - final checkState = - checkifOneOfthemChecked(PermissionOption( - id: subOption.id, - title: subOption.title, - subOptions: [subOption], - )); - - if (checkState == CheckState.all) { - return Image.asset( - Assets.CheckBoxChecked, - width: 20, - height: 20, - ); - } else if (checkState == CheckState.some) { - return Image.asset( - Assets.rectangleCheckBox, - width: 20, - height: 20, - ); - } else { - return Image.asset( - Assets.emptyBox, - width: 20, - height: 20, - ); - } - }, - ), - ), - const SizedBox(width: 8), - Text( - subOption.title, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w700, - fontSize: 12, - color: ColorsManager.lightGreyColor), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(left: 50.0), - child: GridView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 2.0, - crossAxisSpacing: 0.2, - childAspectRatio: 5, - ), - itemCount: subOption.subOptions.length, - itemBuilder: (context, index) { - final child = subOption.subOptions[index]; - return CheckboxListTile( - selectedTileColor: child.isHighlighted - ? Colors.blue.shade50 - : Colors.white, - dense: true, - controlAffinity: ListTileControlAffinity.leading, - title: Text( - child.title, - style: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.lightGreyColor), - ), - value: child.isChecked, - onChanged: (value) => toggleOptionById(child.id), - enabled: false, - ); - }, - ), - ) - ], - ); - }).toList(), - ], - ); - }, - ); - } -} - - -enum CheckState { none, some, all } - -class PermissionOption { - String id; - String title; - bool isChecked; - bool isHighlighted; - List subOptions; - - PermissionOption({ - required this.id, - required this.title, - this.isChecked = false, - this.isHighlighted = false, - this.subOptions = const [], - }); - - factory PermissionOption.fromJson(Map json) { - return PermissionOption( - id: json['id'] ?? '', - title: json['title'] ?? '', - isChecked: json['isChecked'] ?? false, - isHighlighted: json['isHighlighted'] ?? false, - subOptions: (json['subOptions'] as List?) - ?.map((sub) => PermissionOption.fromJson(sub)) - .toList() ?? - [], - ); - } - - Map toJson() { - return { - 'id': id, - 'title': title, - 'isChecked': isChecked, - 'isHighlighted': isHighlighted, - 'subOptions': subOptions.map((sub) => sub.toJson()).toList(), - }; - } -} diff --git a/lib/pages/roles_and_permission/users_page/view/user_table.dart b/lib/pages/roles_and_permission/users_page/view/user_table.dart deleted file mode 100644 index f951f06e..00000000 --- a/lib/pages/roles_and_permission/users_page/view/user_table.dart +++ /dev/null @@ -1,256 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; - -class DynamicTableScreen extends StatefulWidget { - final List titles; - final List> rows; - - DynamicTableScreen({required this.titles, required this.rows}); - - @override - _DynamicTableScreenState createState() => _DynamicTableScreenState(); -} - -class _DynamicTableScreenState extends State - with WidgetsBindingObserver { - late List columnWidths; - - // @override - // void initState() { - // super.initState(); - // // Initialize column widths with default sizes proportional to the screen width - // // Assigning placeholder values here. The actual sizes will be updated in `build`. - // } - @override - void initState() { - super.initState(); - columnWidths = List.filled(widget.titles.length, 150.0); - - WidgetsBinding.instance.addObserver(this); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - @override - void didChangeMetrics() { - super.didChangeMetrics(); - // Screen size might have changed - final newScreenWidth = MediaQuery.of(context).size.width; - setState(() { - columnWidths = List.generate(widget.titles.length, (index) { - if (index == 1) { - return newScreenWidth * - 0.12; // 20% of screen width for the second column - } else if (index == 9) { - return newScreenWidth * - 0.2; // 25% of screen width for the tenth column - } - return newScreenWidth * - 0.09; // Default to 10% of screen width for other columns - }); - }); - } - - @override - Widget build(BuildContext context) { - final screenWidth = MediaQuery.of(context).size.width; - - // Initialize column widths if they are still set to placeholder values - if (columnWidths.every((width) => width == 150.0)) { - columnWidths = List.generate(widget.titles.length, (index) { - if (index == 1) { - return screenWidth * - 0.12; // 20% of screen width for the second column - } else if (index == 9) { - return screenWidth * 0.2; // 25% of screen width for the tenth column - } - return screenWidth * - 0.09; // Default to 10% of screen width for other columns - }); - setState(() {}); - } - return SizedBox( - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: FittedBox( - child: Column( - children: [ - // Header Row with Resizable Columns - Container( - color: ColorsManager.CircleRolesBackground, - child: Row( - children: List.generate(widget.titles.length, (index) { - return Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - FittedBox( - child: Container( - padding: const EdgeInsets.only(left: 10, right: 10), - width: columnWidths[index], - child: Text( - widget.titles[index], - style: const TextStyle( - fontWeight: FontWeight.w400, - fontSize: 13, - color: ColorsManager.grayColor, - ), - ), - ), - ), - GestureDetector( - onHorizontalDragUpdate: (details) { - setState(() { - columnWidths[index] = (columnWidths[index] + - details.delta.dx) - .clamp( - 150.0, 300.0); // Minimum & Maximum size - }); - }, - child: MouseRegion( - cursor: SystemMouseCursors - .resizeColumn, // Set the cursor to resize - child: Container( - color: Colors.green, - child: Container( - color: ColorsManager.boxDivider, - width: 1, - height: 50, // Height of the header cell - ), - ), - ), - ), - ], - ); - }), - ), - ), - // Data Rows with Dividers - Container( - color: ColorsManager.whiteColors, - child: Column( - children: widget.rows.map((row) { - int rowIndex = widget.rows.indexOf(row); - return Column( - children: [ - Container( - child: Padding( - padding: const EdgeInsets.only( - left: 5, top: 10, right: 5, bottom: 10), - child: Row( - children: List.generate(row.length, (index) { - return SizedBox( - width: columnWidths[index], - child: SizedBox( - child: Padding( - padding: const EdgeInsets.only( - left: 15, right: 10), - child: row[index], - ), - ), - ); - }), - ), - ), - ), - if (rowIndex < widget.rows.length - 1) - Row( - children: - List.generate(widget.titles.length, (index) { - return SizedBox( - width: columnWidths[index], - child: const Divider( - color: ColorsManager.boxDivider, - thickness: 1, - height: 1, - ), - ); - })) - // Add a Divider below each row except the last one - ], - ); - }).toList(), - ), - ), - ], - ), - ), - ), - ); - } -} - - - - // Widget build(BuildContext context) { - // return Scaffold( - // body: SingleChildScrollView( - // scrollDirection: Axis.horizontal, - // child: SingleChildScrollView( - // scrollDirection: Axis.vertical, - // child: Column( - // children: [ - // // Header Row with Resizable Columns - // Container( - // color: Colors.green, - // child: Row( - // children: List.generate(widget.titles.length, (index) { - // return Row( - // children: [ - // Container( - // width: columnWidths[index], - // decoration: const BoxDecoration( - // color: Colors.green, - // ), - // child: Text( - // widget.titles[index], - // style: TextStyle(fontWeight: FontWeight.bold), - // textAlign: TextAlign.center, - // ), - // ), - // GestureDetector( - // onHorizontalDragUpdate: (details) { - // setState(() { - // columnWidths[index] = (columnWidths[index] + - // details.delta.dx) - // .clamp(50.0, 300.0); // Minimum & Maximum size - // }); - // }, - // child: MouseRegion( - // cursor: SystemMouseCursors - // .resizeColumn, // Set the cursor to resize - // child: Container( - // color: Colors.green, - // child: Container( - // color: Colors.black, - // width: 1, - // height: 50, // Height of the header cell - // ), - // ), - // ), - // ), - // ], - // ); - // }), - // ), - // ), - // // Data Rows - // ...widget.rows.map((row) { - // return Row( - // children: List.generate(row.length, (index) { - // return Container( - // width: columnWidths[index], - // child: row[index], - // ); - // }), - // ); - // }).toList(), - // ], - // ), - // ), - // ), - // ); - // } \ No newline at end of file diff --git a/lib/pages/roles_and_permission/view/roles_and_permission_page.dart b/lib/pages/roles_and_permission/view/roles_and_permission_page.dart index 05f924ca..da003536 100644 --- a/lib/pages/roles_and_permission/view/roles_and_permission_page.dart +++ b/lib/pages/roles_and_permission/view/roles_and_permission_page.dart @@ -2,11 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_event.dart'; import 'package:syncrow_web/pages/roles_and_permission/bloc/roles_permission_state.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/users_page.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/view/users_page.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/web_layout/web_scaffold.dart'; @@ -17,8 +16,7 @@ class RolesAndPermissionPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => - RolesPermissionBloc()..add(const GetRoles()), + create: (BuildContext context) => RolesPermissionBloc(), child: BlocConsumer( listener: (context, state) {}, builder: (context, state) { @@ -77,8 +75,8 @@ class RolesAndPermissionPage extends StatelessWidget { ), ], ), - scaffoldBody: BlocProvider( - create: (context) => UsersBloc()..add(const GetUsers()), + scaffoldBody: BlocProvider( + create: (context) => UserTableBloc()..add(const GetUsers()), child: const UsersPage(), ) // _blocRole.tapSelect == false diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 58dc0d9d..5d2464e6 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -252,7 +252,6 @@ class CommunitySpaceManagementApi { path: ApiEndpoints.getSpaceHierarchy .replaceAll('{communityId}', communityId), expectedResponseModel: (json) { - print('=-=-=-=$json'); final spaceModels = (json['data'] as List) .map((spaceJson) => SpaceModel.fromJson(spaceJson)) .toList(); diff --git a/lib/services/user_permission.dart b/lib/services/user_permission.dart index 22083191..d5cb8243 100644 --- a/lib/services/user_permission.dart +++ b/lib/services/user_permission.dart @@ -1,5 +1,7 @@ +import 'dart:convert'; +import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/roles_and_permission/model/role_type_model.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/view/roles_and_permission.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; @@ -33,7 +35,7 @@ class UserPermissionApi { return response ?? []; } - Future sendInviteUser({ + Future sendInviteUser({ String? firstName, String? lastName, String? email, @@ -42,22 +44,58 @@ class UserPermissionApi { String? roleUuid, List? spaceUuids, }) async { - final response = await _httpService.post( - path: ApiEndpoints.permission, - showServerMessage: true, - body: { + try { + final body = { "firstName": firstName, "lastName": lastName, "email": email, - "jobTitle": jobTitle, - "phoneNumber": phoneNumber, + "jobTitle": jobTitle != '' ? jobTitle : " ", + "phoneNumber": phoneNumber != '' ? phoneNumber : " ", "roleUuid": roleUuid, - "spaceUuids": spaceUuids - }, - expectedResponseModel: (json) { - print(json); - }, - ); - return response ?? []; + "projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c", + "spaceUuids": spaceUuids, + }; + final response = await _httpService.post( + path: ApiEndpoints.inviteUser, + showServerMessage: true, + 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 checkEmail(email) async { + try { + final response = await _httpService.post( + path: ApiEndpoints.checkEmail, + showServerMessage: true, + body: {"email": email}, + expectedResponseModel: (json) { + if (json['statusCode'] != 400) { + return json["message"]; + } + }, + ); + return response ?? []; + } on DioException catch (e) { + if (e.response != null) { + final errorMessage = e.response?.data['error']; + return errorMessage; + } + } catch (e) { + return e.toString(); + } } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 28923538..da746fbd 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -1,6 +1,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; abstract class ApiEndpoints { + static const String projectUuid = "0e62577c-06fa-41b9-8a92-99a21fbaf51c"; static String baseUrl = dotenv.env['BASE_URL'] ?? ''; static const String signUp = '/authentication/user/signup'; static const String login = '/authentication/user/login'; @@ -38,23 +39,32 @@ abstract class ApiEndpoints { static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}'; // Space Module - static const String createSpace = '/communities/{communityId}/spaces'; - static const String listSpaces = '/communities/{communityId}/spaces'; + static const String createSpace = + '/projects/${projectUuid}/communities/{communityId}/spaces'; + static const String listSpaces = + '/projects/${projectUuid}/communities/{communityId}/spaces'; static const String deleteSpace = - '/communities/{communityId}/spaces/{spaceId}'; + '/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}'; static const String updateSpace = - '/communities/{communityId}/spaces/{spaceId}'; - static const String getSpace = '/communities/{communityId}/spaces/{spaceId}'; - static const String getSpaceHierarchy = '/communities/{communityId}/spaces'; + '/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}'; + static const String getSpace = + '/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}'; + static const String getSpaceHierarchy = + '/projects/${projectUuid}/communities/{communityId}/spaces'; // Community Module - static const String createCommunity = '/communities'; - static const String getCommunityList = '/communities'; - static const String getCommunityById = '/communities/{communityId}'; - static const String updateCommunity = '/communities/{communityId}'; - static const String deleteCommunity = '/communities/{communityId}'; - static const String getUserCommunities = '/communities/user/{userUuid}'; - static const String createUserCommunity = '/communities/user'; + static const String createCommunity = '/projects/${projectUuid}/communities'; + static const String getCommunityList = '/projects/${projectUuid}/communities'; + static const String getCommunityById = + '/projects/${projectUuid}/communities/{communityId}'; + static const String updateCommunity = + '/projects/${projectUuid}/communities/{communityId}'; + static const String deleteCommunity = + '/projects/${projectUuid}/communities/{communityId}'; + static const String getUserCommunities = + '/projects/${projectUuid}/communities/user/{userUuid}'; + static const String createUserCommunity = + '/projects/${projectUuid}/communities/user'; static const String getDeviceLogsByDate = '/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}'; @@ -90,5 +100,7 @@ abstract class ApiEndpoints { static const String roleTypes = '/role/types'; static const String permission = '/permission/{roleUuid}'; static const String inviteUser = '/invite-user'; + static const String checkEmail = '/invite-user/check-email'; // static const String updateAutomation = '/automation/{automationId}'; + // https://syncrow-dev.azurewebsites.net/invite-user/check-email } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index 8b271d5e..1f4074c4 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -394,5 +394,8 @@ class Assets { static const String arrowDown = 'assets/icons/arrow_down.svg'; static const String userManagement = 'assets/icons/user_management.svg'; + static const String filterTableIcon = 'assets/icons/filter_table_icon.svg'; + static const String ZtoAIcon = 'assets/icons/ztoa_icon.png'; + static const String AtoZIcon = 'assets/icons/atoz_icon.png'; } //user_management.svg