diff --git a/lib/pages/roles_and_permission/model/role_type_model.dart b/lib/pages/roles_and_permission/model/role_type_model.dart new file mode 100644 index 00000000..1705e25c --- /dev/null +++ b/lib/pages/roles_and_permission/model/role_type_model.dart @@ -0,0 +1,22 @@ +class RoleTypeModel { + final String uuid; + final String createdAt; + final String updatedAt; + final String type; + + RoleTypeModel({ + required this.uuid, + required this.createdAt, + required this.updatedAt, + required this.type, + }); + + factory RoleTypeModel.fromJson(Map json) { + return RoleTypeModel( + uuid: json['uuid'], + createdAt: json['createdAt'], + updatedAt: json['updatedAt'], + type: json['type'], + ); + } +} diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart b/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart index ad5e49ce..5b846ecf 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart +++ b/lib/pages/roles_and_permission/users_page/bloc/users_bloc.dart @@ -1,12 +1,15 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/material.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/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'; class UsersBloc extends Bloc { UsersBloc() : super(UsersInitial()) { @@ -16,6 +19,9 @@ class UsersBloc extends Bloc { on(_onLoadCommunityAndSpaces); on(searchTreeNode); on(isCompleteSpacesFun); + on(_getRolePermission); + on(_getPermissions); + on(searchRolePermission); } List users = []; @@ -112,7 +118,6 @@ class UsersBloc extends Bloc { void isCompleteSpacesFun( CheckSpacesStepStatus event, Emitter emit) { emit(UsersLoadingState()); - try { List selectedIds = getSelectedIds(updatedCommunities); isCompleteSpaces = selectedIds.isNotEmpty; @@ -224,4 +229,61 @@ class UsersBloc extends Bloc { } return selectedIds; } + + List roles = []; + List permissions = []; + + _getRolePermission(RoleEvent event, Emitter emit) async { + try { + emit(UsersLoadingState()); + roles = await UserPermissionApi().fetchRoles(); + add(PermissionEvent(roleUuid: roles.first.uuid)); + emit(RolePermissionInitial()); + } catch (e) { + emit(ErrorState('Error loading communities and spaces: $e')); + } + } + + _getPermissions(PermissionEvent event, Emitter emit) async { + try { + emit(UsersLoadingState()); + permissions = await UserPermissionApi().fetchPermission( + event.roleUuid == "" ? roles.first.uuid : event.roleUuid); + emit(RolePermissionInitial()); + } catch (e) { + emit(ErrorState('Error loading communities and spaces: $e')); + } + } + + bool _searchRolePermission(List nodes, String searchTerm) { + bool anyMatch = false; + for (var node in nodes) { + bool isMatch = + node.title.toLowerCase().contains(searchTerm.toLowerCase()); + bool childMatch = _searchRolePermission(node.subOptions, searchTerm); + node.isHighlighted = isMatch || childMatch; + + anyMatch = anyMatch || node.isHighlighted; + } + return anyMatch; + } + + void searchRolePermission(SearchPermission event, Emitter emit) { + emit(UsersLoadingState()); + if (event.searchTerm!.isEmpty) { + _clearHighlightsRolePermission(permissions); + } else { + _searchRolePermission(permissions, event.searchTerm!); + } + emit(ChangeStatusSteps()); + } + + void _clearHighlightsRolePermission(List nodes) { + for (var node in nodes) { + node.isHighlighted = false; + if (node.subOptions.isNotEmpty) { + _clearHighlightsRolePermission(node.subOptions); + } + } + } } diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_event.dart b/lib/pages/roles_and_permission/users_page/bloc/users_event.dart index f1675b08..6f59b495 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_event.dart +++ b/lib/pages/roles_and_permission/users_page/bloc/users_event.dart @@ -1,5 +1,6 @@ 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'; sealed class UsersEvent extends Equatable { const UsersEvent(); @@ -10,6 +11,7 @@ class GetUsers extends UsersEvent { @override List get props => []; } + class CheckSpacesStepStatus extends UsersEvent { const CheckSpacesStepStatus(); @override @@ -22,6 +24,19 @@ class LoadCommunityAndSpacesEvent extends UsersEvent { List get props => []; } +class RoleEvent extends UsersEvent { + const RoleEvent(); + @override + List get props => []; +} + +class PermissionEvent extends UsersEvent { + final String? roleUuid; + const PermissionEvent({this.roleUuid = ""}); + @override + List get props => [roleUuid]; +} + class GetBatchStatus extends UsersEvent { final List uuids; const GetBatchStatus(this.uuids); @@ -29,7 +44,6 @@ class GetBatchStatus extends UsersEvent { List get props => [uuids]; } -//LoadCommunityAndSpacesEvent class ChangeUserStatus extends UsersEvent { final String userId; final String newStatus; @@ -54,9 +68,20 @@ class SearchAnode extends UsersEvent { @override List get props => [nodes, searchTerm]; } + +class SearchPermission extends UsersEvent { + List? nodes; + String? searchTerm; + SearchPermission({this.nodes, this.searchTerm}); + @override + List get props => [nodes, searchTerm]; +} + class SelecteId extends UsersEvent { List? nodes; - SelecteId({this.nodes,}); + SelecteId({ + this.nodes, + }); @override List get props => [nodes]; } diff --git a/lib/pages/roles_and_permission/users_page/bloc/users_status.dart b/lib/pages/roles_and_permission/users_page/bloc/users_status.dart index b9937f77..5d1a68f6 100644 --- a/lib/pages/roles_and_permission/users_page/bloc/users_status.dart +++ b/lib/pages/roles_and_permission/users_page/bloc/users_status.dart @@ -9,6 +9,10 @@ final class UsersInitial extends UsersState { @override List get props => []; } +final class RolePermissionInitial extends UsersState { + @override + List get props => []; +} final class ChangeStatusSteps extends UsersState { @override diff --git a/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart b/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart index 895f4825..dc45cfdc 100644 --- a/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart +++ b/lib/pages/roles_and_permission/users_page/view/add_user_dialog.dart @@ -23,8 +23,9 @@ class _AddNewUserDialogState extends State { @override Widget build(BuildContext context) { return BlocProvider( - create: (BuildContext context) => - UsersBloc()..add(LoadCommunityAndSpacesEvent()), + create: (BuildContext context) => UsersBloc() + ..add(const LoadCommunityAndSpacesEvent()) + ..add(const RoleEvent()), child: BlocConsumer( listener: (context, state) {}, builder: (context, state) { @@ -45,7 +46,9 @@ class _AddNewUserDialogState extends State { child: Text( "Add New User", style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), + fontSize: 20, + fontWeight: FontWeight.w700, + color: ColorsManager.secondaryColor), ), ), ), diff --git a/lib/pages/roles_and_permission/users_page/view/basics_view.dart b/lib/pages/roles_and_permission/users_page/view/basics_view.dart index d64d837d..abf6642c 100644 --- a/lib/pages/roles_and_permission/users_page/view/basics_view.dart +++ b/lib/pages/roles_and_permission/users_page/view/basics_view.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package +import 'package:flutter_bloc/flutter_bloc.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/utils/color_manager.dart'; @@ -47,9 +49,16 @@ class BasicsView extends StatelessWidget { SizedBox( child: Row( children: [ - Text( - "*", - style: TextStyle(color: ColorsManager.red), + // SizedBox( + // width: 15, + // ), + const Text( + " * ", + style: TextStyle( + color: ColorsManager.red, + fontWeight: FontWeight.w900, + fontSize: 15, + ), ), Text( 'First Name', @@ -96,8 +105,12 @@ class BasicsView extends StatelessWidget { child: Row( children: [ const Text( - "*", - style: TextStyle(color: ColorsManager.red), + " * ", + style: TextStyle( + color: ColorsManager.red, + fontWeight: FontWeight.w900, + fontSize: 15, + ), ), Text('Last Name', style: context.textTheme.bodyMedium?.copyWith( @@ -140,9 +153,13 @@ class BasicsView extends StatelessWidget { SizedBox( child: Row( children: [ - Text( - "*", - style: TextStyle(color: ColorsManager.red), + const Text( + " * ", + style: TextStyle( + color: ColorsManager.red, + fontWeight: FontWeight.w900, + fontSize: 15, + ), ), Text( 'Email Address', @@ -187,10 +204,6 @@ class BasicsView extends StatelessWidget { SizedBox( child: Row( children: [ - Text( - "*", - style: TextStyle(color: ColorsManager.red), - ), Text( 'Mobile Number', style: context.textTheme.bodyMedium?.copyWith( @@ -200,28 +213,93 @@ class BasicsView extends StatelessWidget { ), ], )), - Padding( - padding: const EdgeInsets.all(8.0), - child: TextFormField( - style: const TextStyle(color: Colors.black), - controller: _blocRole.phoneController, - decoration: inputTextFormDeco( - hintText: "05x xxx xxxx", - ).copyWith( - hintStyle: context.textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.textGray), - ), - validator: (value) { - if (value == null || value.isEmpty) { - return 'Please enter a phone number'; - } - return null; - }, - keyboardType: TextInputType.phone, + // InternationalPhoneNumberInput( + // spaceBetweenSelectorAndTextField: 50, + // initialValue: PhoneNumber(isoCode: 'AE'), + // inputDecoration: inputTextFormDeco( + // hintText: "x xxx xxxx", + // ).copyWith( + // hintStyle: context.textTheme.bodyMedium?.copyWith( + // fontWeight: FontWeight.w400, + // fontSize: 12, + // color: ColorsManager.textGray), + // ), + // onInputChanged: (PhoneNumber number) { + // print(number.phoneNumber); + // }, + // onInputValidated: (bool value) { + // print(value); + // }, + // selectorConfig: const SelectorConfig( + // selectorType: PhoneInputSelectorType.BOTTOM_SHEET, + // useBottomSheetSafeArea: true, + // leadingPadding: 15, + // trailingSpace: false, + // setSelectorButtonAsPrefixIcon: true), + // ignoreBlank: true, + // autoValidateMode: AutovalidateMode.disabled, + + // selectorTextStyle: + // TextStyle(color: ColorsManager.blackColor), + // // initialValue: number, + // // textFieldController: controller, + // formatInput: true, + // keyboardType: const TextInputType.numberWithOptions( + // signed: true, decimal: true), + // // inputBorder: OutlineInputBorder( + // // borderSide: + // // BorderSide(color: Colors.black,)), + // onSaved: (PhoneNumber number) { + // print('On Saved: $number'); + // }, + // textStyle: + // const TextStyle(color: ColorsManager.blackColor), + // ), + + IntlPhoneField( + pickerDialogStyle: PickerDialogStyle(), + dropdownIconPosition: IconPosition.leading, + disableLengthCheck: true, + dropdownTextStyle: TextStyle(color: Colors.black), + textInputAction: TextInputAction.done, + decoration: inputTextFormDeco( + hintText: "05x xxx xxxx", + ).copyWith( + hintStyle: context.textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.textGray), ), - ), + + initialCountryCode: 'AE', + style: TextStyle(color: Colors.black), + onChanged: (phone) { + print(phone.completeNumber); + }, + ) + + // Padding( + // padding: const EdgeInsets.all(8.0), + // child: TextFormField( + // style: const TextStyle(color: Colors.black), + // controller: _blocRole.phoneController, + // decoration: inputTextFormDeco( + // hintText: "05x xxx xxxx", + // ).copyWith( + // hintStyle: context.textTheme.bodyMedium?.copyWith( + // fontWeight: FontWeight.w400, + // fontSize: 12, + // color: ColorsManager.textGray), + // ), + // validator: (value) { + // if (value == null || value.isEmpty) { + // return 'Please enter a phone number'; + // } + // return null; + // }, + // keyboardType: TextInputType.phone, + // ), + // ), ], ), ), @@ -236,10 +314,6 @@ class BasicsView extends StatelessWidget { SizedBox( child: Row( children: [ - Text( - "*", - style: TextStyle(color: ColorsManager.red), - ), Text( 'Job Title', style: context.textTheme.bodyMedium?.copyWith( diff --git a/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart b/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart index 090d4fe3..f10fd4ed 100644 --- a/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart +++ b/lib/pages/roles_and_permission/users_page/view/delete_user_dialog.dart @@ -5,14 +5,14 @@ import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_blo import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class AddNewUserDialog extends StatefulWidget { - const AddNewUserDialog({super.key}); +class DeleteUserDialog extends StatefulWidget { + const DeleteUserDialog({super.key}); @override - _AddNewUserDialogState createState() => _AddNewUserDialogState(); + _DeleteUserDialogState createState() => _DeleteUserDialogState(); } -class _AddNewUserDialogState extends State { +class _DeleteUserDialogState extends State { int currentStep = 1; @override 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 index 812a3170..a8c186e8 100644 --- 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 @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package: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'; @@ -34,7 +35,12 @@ class RolesAndPermission extends StatelessWidget { const SizedBox( height: 15, ), - const SizedBox(width: 300, height: 110, child: DropdownExample()), + SizedBox( + width: 300, + height: 110, + child: DropdownExample( + bloc: _blocRole, + )), const SizedBox(height: 10), Expanded( child: SizedBox( @@ -64,6 +70,11 @@ class RolesAndPermission extends StatelessWidget { 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, @@ -97,7 +108,9 @@ class RolesAndPermission extends StatelessWidget { padding: const EdgeInsets.all(8.0), child: Container( color: ColorsManager.whiteColors, - child: const DeviceManagement()))) + child: DeviceManagement( + bloc: _blocRole, + )))) ], ), ), @@ -111,7 +124,8 @@ class RolesAndPermission extends StatelessWidget { } class DropdownExample extends StatefulWidget { - const DropdownExample({super.key}); + final UsersBloc? bloc; + const DropdownExample({super.key, this.bloc}); @override _DropdownExampleState createState() => _DropdownExampleState(); @@ -119,7 +133,13 @@ class DropdownExample extends StatefulWidget { class _DropdownExampleState extends State { String? selectedRole; - List roles = ['Admin', 'User', 'Guest', 'Moderator']; + @override + void initState() { + super.initState(); + if (widget.bloc != null && widget.bloc!.roles.isNotEmpty) { + selectedRole = widget.bloc!.roles.first.uuid; + } + } @override Widget build(BuildContext context) { @@ -140,19 +160,20 @@ class _DropdownExampleState extends State { SizedBox( child: DropdownButtonFormField( alignment: Alignment.center, - focusColor: ColorsManager.whiteColors, + focusColor: Colors.white, autofocus: true, value: selectedRole, - items: roles.map((role) { - return DropdownMenuItem( - value: role, - child: Text(role), + 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(), @@ -168,13 +189,13 @@ class _DropdownExampleState extends State { width: 70, height: 50, decoration: BoxDecoration( - color: ColorsManager.graysColor, + color: Colors.grey[200], borderRadius: const BorderRadius.only( bottomRight: Radius.circular(10), topRight: Radius.circular(10), ), border: Border.all( - color: ColorsManager.grayBorder, + color: Colors.grey, width: 1.0, ), ), @@ -192,60 +213,17 @@ class _DropdownExampleState extends State { } class DeviceManagement extends StatefulWidget { - const DeviceManagement({Key? key}) : super(key: key); + final UsersBloc? bloc; + const DeviceManagement({Key? key, this.bloc}) : super(key: key); @override _DeviceManagementState createState() => _DeviceManagementState(); } class _DeviceManagementState extends State { - - - final List options = [ - MainRoleOption( - id: '1', - title: "Device Management", - subOptions: [ - SubRoleOption( - id: '11', - title: "Manage devices in private spaces", - children: [ - ChildRoleOption(id: '111', title: "Control"), - ChildRoleOption(id: '112', title: "Assign device"), - ChildRoleOption(id: '113', title: "View"), - ], - ), - SubRoleOption( - id: '12', - title: "Manage", - children: [ - ChildRoleOption(id: '121', title: "cc"), - ChildRoleOption(id: '122', title: "Assign"), - ChildRoleOption(id: '123', title: "s"), - ], - ), - ], - ), - MainRoleOption( - id: '2', - title: "Device Management", - subOptions: [ - SubRoleOption( - id: '22', - title: "Manage devices in private spaces", - children: [ - ChildRoleOption(id: '211', title: "Control"), - ChildRoleOption(id: '212', title: "Assign device"), - ChildRoleOption(id: '213', title: "View"), - ], - ), - ], - ), - ]; - void toggleOptionById(String id) { setState(() { - for (var mainOption in options) { + for (var mainOption in widget.bloc!.permissions) { if (mainOption.id == id) { final isChecked = checkifOneOfthemChecked(mainOption) == CheckState.all; @@ -253,7 +231,7 @@ class _DeviceManagementState extends State { for (var subOption in mainOption.subOptions) { subOption.isChecked = !isChecked; - for (var child in subOption.children) { + for (var child in subOption.subOptions) { child.isChecked = !isChecked; } } @@ -263,7 +241,7 @@ class _DeviceManagementState extends State { for (var subOption in mainOption.subOptions) { if (subOption.id == id) { subOption.isChecked = !subOption.isChecked; - for (var child in subOption.children) { + for (var child in subOption.subOptions) { child.isChecked = subOption.isChecked; } mainOption.isChecked = @@ -271,11 +249,11 @@ class _DeviceManagementState extends State { return; } - for (var child in subOption.children) { + for (var child in subOption.subOptions) { if (child.id == id) { child.isChecked = !child.isChecked; subOption.isChecked = - subOption.children.every((child) => child.isChecked); + subOption.subOptions.every((child) => child.isChecked); mainOption.isChecked = mainOption.subOptions.every((sub) => sub.isChecked); return; @@ -286,7 +264,7 @@ class _DeviceManagementState extends State { }); } - CheckState checkifOneOfthemChecked(MainRoleOption mainOption) { + CheckState checkifOneOfthemChecked(PermissionOption mainOption) { bool allSelected = true; bool someSelected = false; @@ -297,7 +275,7 @@ class _DeviceManagementState extends State { allSelected = false; } - for (var child in subOption.children) { + for (var child in subOption.subOptions) { if (child.isChecked) { someSelected = true; } else { @@ -319,16 +297,16 @@ class _DeviceManagementState extends State { Widget build(BuildContext context) { return ListView.builder( padding: const EdgeInsets.all(8), - itemCount: options.length, + itemCount: widget.bloc!.permissions.length, itemBuilder: (context, index) { - final option = options[index]; + final option = widget.bloc!.permissions[index]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ InkWell( - onTap: () => toggleOptionById(option.id), + // onTap: () => toggleOptionById(option.id), child: Builder( builder: (context) { final checkState = checkifOneOfthemChecked(option); @@ -372,50 +350,55 @@ class _DeviceManagementState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - InkWell( - onTap: () => toggleOptionById(subOption.id), - child: Builder( - builder: (context) { - final checkState = - checkifOneOfthemChecked(MainRoleOption( - id: subOption.id, - title: subOption.title, - subOptions: [subOption], - )); + 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, - ); - } - }, + 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), - ), - ], + 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), @@ -424,15 +407,18 @@ class _DeviceManagementState extends State { physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // 2 items per row - mainAxisSpacing: 2.0, // Space between rows - crossAxisSpacing: 0.2, // Space between columns - childAspectRatio: 5, // Adjust aspect ratio as needed + crossAxisCount: 2, + mainAxisSpacing: 2.0, + crossAxisSpacing: 0.2, + childAspectRatio: 5, ), - itemCount: subOption.children.length, + itemCount: subOption.subOptions.length, itemBuilder: (context, index) { - final child = subOption.children[index]; + final child = subOption.subOptions[index]; return CheckboxListTile( + selectedTileColor: child.isHighlighted + ? Colors.blue.shade50 + : Colors.white, dense: true, controlAffinity: ListTileControlAffinity.leading, title: Text( @@ -444,6 +430,7 @@ class _DeviceManagementState extends State { ), value: child.isChecked, onChanged: (value) => toggleOptionById(child.id), + enabled: false, ); }, ), @@ -458,43 +445,44 @@ class _DeviceManagementState extends State { } } -class MainRoleOption { - String id; - String title; - bool isChecked; - List subOptions; - MainRoleOption({ - required this.id, - required this.title, - this.isChecked = false, - this.subOptions = const [], - }); -} - -class SubRoleOption { - String id; - String title; - bool isChecked; - List children; - - SubRoleOption({ - required this.id, - required this.title, - this.isChecked = false, - this.children = const [], - }); -} - -class ChildRoleOption { - String id; - String title; - bool isChecked; - - ChildRoleOption({ - required this.id, - required this.title, - this.isChecked = false, - }); -} 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/services/user_permission.dart b/lib/services/user_permission.dart new file mode 100644 index 00000000..91091dbe --- /dev/null +++ b/lib/services/user_permission.dart @@ -0,0 +1,36 @@ + +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/services/api/http_service.dart'; +import 'package:syncrow_web/utils/constants/api_const.dart'; + +class UserPermissionApi { + static final HTTPService _httpService = HTTPService(); + + fetchRoles() async { + final response = await _httpService.get( + path: ApiEndpoints.roleTypes, + showServerMessage: true, + expectedResponseModel: (json) { + final List fetchedRoles = (json['data'] as List) + .map((item) => RoleTypeModel.fromJson(item)) + .toList(); + return fetchedRoles; + }, + ); + return response; + } + + Future> fetchPermission(roleUuid) async { + final response = await _httpService.get( + path: ApiEndpoints.permission.replaceAll("roleUuid", roleUuid), + showServerMessage: true, + expectedResponseModel: (json) { + return (json as List) + .map((data) => PermissionOption.fromJson(data)) + .toList(); + }, + ); + return response ?? []; + } +} diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 007b488d..752c5bea 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -11,12 +11,14 @@ abstract class ApiEndpoints { static const String visitorPassword = '/visitor-password'; static const String getDevices = '/visitor-password/devices'; - static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time'; + static const String sendOnlineOneTime = + '/visitor-password/temporary-password/online/one-time'; static const String sendOnlineMultipleTime = '/visitor-password/temporary-password/online/multiple-time'; //offline Password - static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time'; + static const String sendOffLineOneTime = + '/visitor-password/temporary-password/offline/one-time'; static const String sendOffLineMultipleTime = '/visitor-password/temporary-password/offline/multiple-time'; @@ -38,8 +40,10 @@ abstract class ApiEndpoints { // Space Module static const String createSpace = '/communities/{communityId}/spaces'; static const String listSpaces = '/communities/{communityId}/spaces'; - static const String deleteSpace = '/communities/{communityId}/spaces/{spaceId}'; - static const String updateSpace = '/communities/{communityId}/spaces/{spaceId}'; + static const String deleteSpace = + '/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'; @@ -55,11 +59,15 @@ abstract class ApiEndpoints { '/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}'; static const String scheduleByDeviceId = '/schedule/{deviceUuid}'; - static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}'; - static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}'; - static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}'; + static const String getScheduleByDeviceId = + '/schedule/{deviceUuid}?category={category}'; + static const String deleteScheduleByDeviceId = + '/schedule/{deviceUuid}/{scheduleUuid}'; + static const String updateScheduleByDeviceId = + '/schedule/enable/{deviceUuid}'; static const String factoryReset = '/device/factory/reset/{deviceUuid}'; - static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; + static const String powerClamp = + '/device/{powerClampUuid}/power-clamp/status'; //product static const String listProducts = '/products'; @@ -68,13 +76,18 @@ abstract class ApiEndpoints { static const String getIconScene = '/scene/icon'; static const String createScene = '/scene/tap-to-run'; static const String createAutomation = '/automation'; - static const String getUnitScenes = '/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; - static const String getAutomationDetails = '/automation/details/{automationId}'; + static const String getUnitScenes = + '/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; + static const String getAutomationDetails = + '/automation/details/{automationId}'; static const String getScene = '/scene/tap-to-run/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{sceneId}'; static const String deleteAutomation = '/automation/{automationId}'; - static const String updateScene = '/scene/tap-to-run/{sceneId}'; + static const String updateScene = '/scene/tap-to-run/{sceneId}'; static const String updateAutomation = '/automation/{automationId}'; + static const String roleTypes = '/role/types'; + static const String permission = '/permission/{roleUuid}'; + // static const String updateAutomation = '/automation/{automationId}'; } diff --git a/pubspec.lock b/pubspec.lock index ea2315d7..98c62a94 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -304,6 +304,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" + intl_phone_field: + dependency: "direct main" + description: + name: intl_phone_field + sha256: "73819d3dfcb68d2c85663606f6842597c3ddf6688ac777f051b17814fe767bbf" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + intl_phone_number_input: + dependency: "direct main" + description: + name: intl_phone_number_input + sha256: "1c4328713a9503ab26a1fdbb6b00b4cada68c18aac922b35bedbc72eff1297c3" + url: "https://pub.dev" + source: hosted + version: "0.7.4" js: dependency: transitive description: @@ -336,6 +352,30 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.1" + libphonenumber_platform_interface: + dependency: transitive + description: + name: libphonenumber_platform_interface + sha256: f801f6c65523f56504b83f0890e6dad584ab3a7507dca65fec0eed640afea40f + url: "https://pub.dev" + source: hosted + version: "0.4.2" + libphonenumber_plugin: + dependency: transitive + description: + name: libphonenumber_plugin + sha256: c615021d9816fbda2b2587881019ed595ecdf54d999652d7e4cce0e1f026368c + url: "https://pub.dev" + source: hosted + version: "0.3.3" + libphonenumber_web: + dependency: transitive + description: + name: libphonenumber_web + sha256: "8186f420dbe97c3132283e52819daff1e55d60d6db46f7ea5ac42f42a28cc2ef" + url: "https://pub.dev" + source: hosted + version: "0.3.2" lints: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ffff62ac..c99481d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,8 @@ dependencies: fl_chart: ^0.69.0 uuid: ^4.4.2 time_picker_spinner: ^1.0.0 + intl_phone_field: ^3.2.0 + intl_phone_number_input: ^0.7.4 dev_dependencies: flutter_test: