add_user_dialog

This commit is contained in:
mohammad
2024-12-17 16:51:32 +03:00
parent c31f1262a2
commit bd1204c03a
12 changed files with 481 additions and 212 deletions

View File

@ -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<String, dynamic> json) {
return RoleTypeModel(
uuid: json['uuid'],
createdAt: json['createdAt'],
updatedAt: json['updatedAt'],
type: json['type'],
);
}
}

View File

@ -1,12 +1,15 @@
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/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/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_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/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/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/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_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/space_mana_api.dart';
import 'package:syncrow_web/services/user_permission.dart';
class UsersBloc extends Bloc<UsersEvent, UsersState> { class UsersBloc extends Bloc<UsersEvent, UsersState> {
UsersBloc() : super(UsersInitial()) { UsersBloc() : super(UsersInitial()) {
@ -16,6 +19,9 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces); on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
on<SearchAnode>(searchTreeNode); on<SearchAnode>(searchTreeNode);
on<CheckSpacesStepStatus>(isCompleteSpacesFun); on<CheckSpacesStepStatus>(isCompleteSpacesFun);
on<RoleEvent>(_getRolePermission);
on<PermissionEvent>(_getPermissions);
on<SearchPermission>(searchRolePermission);
} }
List<RolesUserModel> users = []; List<RolesUserModel> users = [];
@ -112,7 +118,6 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
void isCompleteSpacesFun( void isCompleteSpacesFun(
CheckSpacesStepStatus event, Emitter<UsersState> emit) { CheckSpacesStepStatus event, Emitter<UsersState> emit) {
emit(UsersLoadingState()); emit(UsersLoadingState());
try { try {
List<String> selectedIds = getSelectedIds(updatedCommunities); List<String> selectedIds = getSelectedIds(updatedCommunities);
isCompleteSpaces = selectedIds.isNotEmpty; isCompleteSpaces = selectedIds.isNotEmpty;
@ -224,4 +229,61 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
} }
return selectedIds; return selectedIds;
} }
List<RoleTypeModel> roles = [];
List<PermissionOption> permissions = [];
_getRolePermission(RoleEvent event, Emitter<UsersState> 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<UsersState> 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<PermissionOption> 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<UsersState> emit) {
emit(UsersLoadingState());
if (event.searchTerm!.isEmpty) {
_clearHighlightsRolePermission(permissions);
} else {
_searchRolePermission(permissions, event.searchTerm!);
}
emit(ChangeStatusSteps());
}
void _clearHighlightsRolePermission(List<PermissionOption> nodes) {
for (var node in nodes) {
node.isHighlighted = false;
if (node.subOptions.isNotEmpty) {
_clearHighlightsRolePermission(node.subOptions);
}
}
}
} }

View File

@ -1,5 +1,6 @@
import 'package:equatable/equatable.dart'; 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/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 { sealed class UsersEvent extends Equatable {
const UsersEvent(); const UsersEvent();
@ -10,6 +11,7 @@ class GetUsers extends UsersEvent {
@override @override
List<Object?> get props => []; List<Object?> get props => [];
} }
class CheckSpacesStepStatus extends UsersEvent { class CheckSpacesStepStatus extends UsersEvent {
const CheckSpacesStepStatus(); const CheckSpacesStepStatus();
@override @override
@ -22,6 +24,19 @@ class LoadCommunityAndSpacesEvent extends UsersEvent {
List<Object?> get props => []; List<Object?> get props => [];
} }
class RoleEvent extends UsersEvent {
const RoleEvent();
@override
List<Object?> get props => [];
}
class PermissionEvent extends UsersEvent {
final String? roleUuid;
const PermissionEvent({this.roleUuid = ""});
@override
List<Object?> get props => [roleUuid];
}
class GetBatchStatus extends UsersEvent { class GetBatchStatus extends UsersEvent {
final List<String> uuids; final List<String> uuids;
const GetBatchStatus(this.uuids); const GetBatchStatus(this.uuids);
@ -29,7 +44,6 @@ class GetBatchStatus extends UsersEvent {
List<Object?> get props => [uuids]; List<Object?> get props => [uuids];
} }
//LoadCommunityAndSpacesEvent
class ChangeUserStatus extends UsersEvent { class ChangeUserStatus extends UsersEvent {
final String userId; final String userId;
final String newStatus; final String newStatus;
@ -54,9 +68,20 @@ class SearchAnode extends UsersEvent {
@override @override
List<Object?> get props => [nodes, searchTerm]; List<Object?> get props => [nodes, searchTerm];
} }
class SearchPermission extends UsersEvent {
List<PermissionOption>? nodes;
String? searchTerm;
SearchPermission({this.nodes, this.searchTerm});
@override
List<Object?> get props => [nodes, searchTerm];
}
class SelecteId extends UsersEvent { class SelecteId extends UsersEvent {
List<TreeNode>? nodes; List<TreeNode>? nodes;
SelecteId({this.nodes,}); SelecteId({
this.nodes,
});
@override @override
List<Object?> get props => [nodes]; List<Object?> get props => [nodes];
} }

View File

@ -9,6 +9,10 @@ final class UsersInitial extends UsersState {
@override @override
List<Object> get props => []; List<Object> get props => [];
} }
final class RolePermissionInitial extends UsersState {
@override
List<Object> get props => [];
}
final class ChangeStatusSteps extends UsersState { final class ChangeStatusSteps extends UsersState {
@override @override

View File

@ -23,8 +23,9 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) => create: (BuildContext context) => UsersBloc()
UsersBloc()..add(LoadCommunityAndSpacesEvent()), ..add(const LoadCommunityAndSpacesEvent())
..add(const RoleEvent()),
child: BlocConsumer<UsersBloc, UsersState>( child: BlocConsumer<UsersBloc, UsersState>(
listener: (context, state) {}, listener: (context, state) {},
builder: (context, state) { builder: (context, state) {
@ -45,7 +46,9 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
child: Text( child: Text(
"Add New User", "Add New User",
style: TextStyle( style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold), fontSize: 20,
fontWeight: FontWeight.w700,
color: ColorsManager.secondaryColor),
), ),
), ),
), ),

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package import 'package:flutter_bloc/flutter_bloc.dart';
import 'package: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_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/bloc/users_status.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
@ -47,9 +49,16 @@ class BasicsView extends StatelessWidget {
SizedBox( SizedBox(
child: Row( child: Row(
children: [ children: [
Text( // SizedBox(
// width: 15,
// ),
const Text(
" * ", " * ",
style: TextStyle(color: ColorsManager.red), style: TextStyle(
color: ColorsManager.red,
fontWeight: FontWeight.w900,
fontSize: 15,
),
), ),
Text( Text(
'First Name', 'First Name',
@ -97,7 +106,11 @@ class BasicsView extends StatelessWidget {
children: [ children: [
const Text( const Text(
" * ", " * ",
style: TextStyle(color: ColorsManager.red), style: TextStyle(
color: ColorsManager.red,
fontWeight: FontWeight.w900,
fontSize: 15,
),
), ),
Text('Last Name', Text('Last Name',
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
@ -140,9 +153,13 @@ class BasicsView extends StatelessWidget {
SizedBox( SizedBox(
child: Row( child: Row(
children: [ children: [
Text( const Text(
" * ", " * ",
style: TextStyle(color: ColorsManager.red), style: TextStyle(
color: ColorsManager.red,
fontWeight: FontWeight.w900,
fontSize: 15,
),
), ),
Text( Text(
'Email Address', 'Email Address',
@ -187,10 +204,6 @@ class BasicsView extends StatelessWidget {
SizedBox( SizedBox(
child: Row( child: Row(
children: [ children: [
Text(
"*",
style: TextStyle(color: ColorsManager.red),
),
Text( Text(
'Mobile Number', 'Mobile Number',
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(
@ -200,11 +213,55 @@ class BasicsView extends StatelessWidget {
), ),
], ],
)), )),
Padding( // InternationalPhoneNumberInput(
padding: const EdgeInsets.all(8.0), // spaceBetweenSelectorAndTextField: 50,
child: TextFormField( // initialValue: PhoneNumber(isoCode: 'AE'),
style: const TextStyle(color: Colors.black), // inputDecoration: inputTextFormDeco(
controller: _blocRole.phoneController, // 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( decoration: inputTextFormDeco(
hintText: "05x xxx xxxx", hintText: "05x xxx xxxx",
).copyWith( ).copyWith(
@ -213,15 +270,36 @@ class BasicsView extends StatelessWidget {
fontSize: 12, fontSize: 12,
color: ColorsManager.textGray), color: ColorsManager.textGray),
), ),
validator: (value) {
if (value == null || value.isEmpty) { initialCountryCode: 'AE',
return 'Please enter a phone number'; style: TextStyle(color: Colors.black),
} onChanged: (phone) {
return null; print(phone.completeNumber);
}, },
keyboardType: TextInputType.phone, )
),
), // 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( SizedBox(
child: Row( child: Row(
children: [ children: [
Text(
"*",
style: TextStyle(color: ColorsManager.red),
),
Text( Text(
'Job Title', 'Job Title',
style: context.textTheme.bodyMedium?.copyWith( style: context.textTheme.bodyMedium?.copyWith(

View File

@ -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/pages/roles_and_permission/users_page/bloc/users_status.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class AddNewUserDialog extends StatefulWidget { class DeleteUserDialog extends StatefulWidget {
const AddNewUserDialog({super.key}); const DeleteUserDialog({super.key});
@override @override
_AddNewUserDialogState createState() => _AddNewUserDialogState(); _DeleteUserDialogState createState() => _DeleteUserDialogState();
} }
class _AddNewUserDialogState extends State<AddNewUserDialog> { class _DeleteUserDialogState extends State<DeleteUserDialog> {
int currentStep = 1; int currentStep = 1;
@override @override

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_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_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/bloc/users_status.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -34,7 +35,12 @@ class RolesAndPermission extends StatelessWidget {
const SizedBox( const SizedBox(
height: 15, height: 15,
), ),
const SizedBox(width: 300, height: 110, child: DropdownExample()), SizedBox(
width: 300,
height: 110,
child: DropdownExample(
bloc: _blocRole,
)),
const SizedBox(height: 10), const SizedBox(height: 10),
Expanded( Expanded(
child: SizedBox( child: SizedBox(
@ -64,6 +70,11 @@ class RolesAndPermission extends StatelessWidget {
style: style:
const TextStyle(color: Colors.black), const TextStyle(color: Colors.black),
controller: _blocRole.firstNameController, controller: _blocRole.firstNameController,
onChanged: (value) {
_blocRole.add(SearchPermission(
nodes: _blocRole.permissions,
searchTerm: value));
},
decoration: textBoxDecoration(radios: 20)! decoration: textBoxDecoration(radios: 20)!
.copyWith( .copyWith(
fillColor: Colors.white, fillColor: Colors.white,
@ -97,7 +108,9 @@ class RolesAndPermission extends StatelessWidget {
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container( child: Container(
color: ColorsManager.whiteColors, color: ColorsManager.whiteColors,
child: const DeviceManagement()))) child: DeviceManagement(
bloc: _blocRole,
))))
], ],
), ),
), ),
@ -111,7 +124,8 @@ class RolesAndPermission extends StatelessWidget {
} }
class DropdownExample extends StatefulWidget { class DropdownExample extends StatefulWidget {
const DropdownExample({super.key}); final UsersBloc? bloc;
const DropdownExample({super.key, this.bloc});
@override @override
_DropdownExampleState createState() => _DropdownExampleState(); _DropdownExampleState createState() => _DropdownExampleState();
@ -119,7 +133,13 @@ class DropdownExample extends StatefulWidget {
class _DropdownExampleState extends State<DropdownExample> { class _DropdownExampleState extends State<DropdownExample> {
String? selectedRole; String? selectedRole;
List<String> 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -140,19 +160,20 @@ class _DropdownExampleState extends State<DropdownExample> {
SizedBox( SizedBox(
child: DropdownButtonFormField<String>( child: DropdownButtonFormField<String>(
alignment: Alignment.center, alignment: Alignment.center,
focusColor: ColorsManager.whiteColors, focusColor: Colors.white,
autofocus: true, autofocus: true,
value: selectedRole, value: selectedRole,
items: roles.map((role) { items: widget.bloc!.roles.map((role) {
return DropdownMenuItem( return DropdownMenuItem<String>(
value: role, value: role.uuid,
child: Text(role), child: Text(role.type),
); );
}).toList(), }).toList(),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
selectedRole = value; selectedRole = value;
}); });
widget.bloc!.add(PermissionEvent(roleUuid: selectedRole));
}, },
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
icon: const SizedBox.shrink(), icon: const SizedBox.shrink(),
@ -168,13 +189,13 @@ class _DropdownExampleState extends State<DropdownExample> {
width: 70, width: 70,
height: 50, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: ColorsManager.graysColor, color: Colors.grey[200],
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(10), bottomRight: Radius.circular(10),
topRight: Radius.circular(10), topRight: Radius.circular(10),
), ),
border: Border.all( border: Border.all(
color: ColorsManager.grayBorder, color: Colors.grey,
width: 1.0, width: 1.0,
), ),
), ),
@ -192,60 +213,17 @@ class _DropdownExampleState extends State<DropdownExample> {
} }
class DeviceManagement extends StatefulWidget { class DeviceManagement extends StatefulWidget {
const DeviceManagement({Key? key}) : super(key: key); final UsersBloc? bloc;
const DeviceManagement({Key? key, this.bloc}) : super(key: key);
@override @override
_DeviceManagementState createState() => _DeviceManagementState(); _DeviceManagementState createState() => _DeviceManagementState();
} }
class _DeviceManagementState extends State<DeviceManagement> { class _DeviceManagementState extends State<DeviceManagement> {
final List<MainRoleOption> 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) { void toggleOptionById(String id) {
setState(() { setState(() {
for (var mainOption in options) { for (var mainOption in widget.bloc!.permissions) {
if (mainOption.id == id) { if (mainOption.id == id) {
final isChecked = final isChecked =
checkifOneOfthemChecked(mainOption) == CheckState.all; checkifOneOfthemChecked(mainOption) == CheckState.all;
@ -253,7 +231,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
for (var subOption in mainOption.subOptions) { for (var subOption in mainOption.subOptions) {
subOption.isChecked = !isChecked; subOption.isChecked = !isChecked;
for (var child in subOption.children) { for (var child in subOption.subOptions) {
child.isChecked = !isChecked; child.isChecked = !isChecked;
} }
} }
@ -263,7 +241,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
for (var subOption in mainOption.subOptions) { for (var subOption in mainOption.subOptions) {
if (subOption.id == id) { if (subOption.id == id) {
subOption.isChecked = !subOption.isChecked; subOption.isChecked = !subOption.isChecked;
for (var child in subOption.children) { for (var child in subOption.subOptions) {
child.isChecked = subOption.isChecked; child.isChecked = subOption.isChecked;
} }
mainOption.isChecked = mainOption.isChecked =
@ -271,11 +249,11 @@ class _DeviceManagementState extends State<DeviceManagement> {
return; return;
} }
for (var child in subOption.children) { for (var child in subOption.subOptions) {
if (child.id == id) { if (child.id == id) {
child.isChecked = !child.isChecked; child.isChecked = !child.isChecked;
subOption.isChecked = subOption.isChecked =
subOption.children.every((child) => child.isChecked); subOption.subOptions.every((child) => child.isChecked);
mainOption.isChecked = mainOption.isChecked =
mainOption.subOptions.every((sub) => sub.isChecked); mainOption.subOptions.every((sub) => sub.isChecked);
return; return;
@ -286,7 +264,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
}); });
} }
CheckState checkifOneOfthemChecked(MainRoleOption mainOption) { CheckState checkifOneOfthemChecked(PermissionOption mainOption) {
bool allSelected = true; bool allSelected = true;
bool someSelected = false; bool someSelected = false;
@ -297,7 +275,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
allSelected = false; allSelected = false;
} }
for (var child in subOption.children) { for (var child in subOption.subOptions) {
if (child.isChecked) { if (child.isChecked) {
someSelected = true; someSelected = true;
} else { } else {
@ -319,16 +297,16 @@ class _DeviceManagementState extends State<DeviceManagement> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView.builder( return ListView.builder(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
itemCount: options.length, itemCount: widget.bloc!.permissions.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final option = options[index]; final option = widget.bloc!.permissions[index];
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
InkWell( InkWell(
onTap: () => toggleOptionById(option.id), // onTap: () => toggleOptionById(option.id),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
final checkState = checkifOneOfthemChecked(option); final checkState = checkifOneOfthemChecked(option);
@ -372,14 +350,18 @@ class _DeviceManagementState extends State<DeviceManagement> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Container(
color: option.isHighlighted
? Colors.blue.shade50
: Colors.white,
child: Row(
children: [ children: [
InkWell( InkWell(
onTap: () => toggleOptionById(subOption.id), // onTap: () => toggleOptionById(subOption.id),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
final checkState = final checkState =
checkifOneOfthemChecked(MainRoleOption( checkifOneOfthemChecked(PermissionOption(
id: subOption.id, id: subOption.id,
title: subOption.title, title: subOption.title,
subOptions: [subOption], subOptions: [subOption],
@ -417,6 +399,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
), ),
], ],
), ),
),
Padding( Padding(
padding: const EdgeInsets.only(left: 50.0), padding: const EdgeInsets.only(left: 50.0),
child: GridView.builder( child: GridView.builder(
@ -424,15 +407,18 @@ class _DeviceManagementState extends State<DeviceManagement> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount( const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // 2 items per row crossAxisCount: 2,
mainAxisSpacing: 2.0, // Space between rows mainAxisSpacing: 2.0,
crossAxisSpacing: 0.2, // Space between columns crossAxisSpacing: 0.2,
childAspectRatio: 5, // Adjust aspect ratio as needed childAspectRatio: 5,
), ),
itemCount: subOption.children.length, itemCount: subOption.subOptions.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final child = subOption.children[index]; final child = subOption.subOptions[index];
return CheckboxListTile( return CheckboxListTile(
selectedTileColor: child.isHighlighted
? Colors.blue.shade50
: Colors.white,
dense: true, dense: true,
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
title: Text( title: Text(
@ -444,6 +430,7 @@ class _DeviceManagementState extends State<DeviceManagement> {
), ),
value: child.isChecked, value: child.isChecked,
onChanged: (value) => toggleOptionById(child.id), onChanged: (value) => toggleOptionById(child.id),
enabled: false,
); );
}, },
), ),
@ -458,43 +445,44 @@ class _DeviceManagementState extends State<DeviceManagement> {
} }
} }
class MainRoleOption {
String id;
String title;
bool isChecked;
List<SubRoleOption> subOptions;
MainRoleOption({
required this.id,
required this.title,
this.isChecked = false,
this.subOptions = const [],
});
}
class SubRoleOption {
String id;
String title;
bool isChecked;
List<ChildRoleOption> 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 } enum CheckState { none, some, all }
class PermissionOption {
String id;
String title;
bool isChecked;
bool isHighlighted;
List<PermissionOption> subOptions;
PermissionOption({
required this.id,
required this.title,
this.isChecked = false,
this.isHighlighted = false,
this.subOptions = const [],
});
factory PermissionOption.fromJson(Map<String, dynamic> 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<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'isChecked': isChecked,
'isHighlighted': isHighlighted,
'subOptions': subOptions.map((sub) => sub.toJson()).toList(),
};
}
}

View File

@ -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<RoleTypeModel> fetchedRoles = (json['data'] as List)
.map((item) => RoleTypeModel.fromJson(item))
.toList();
return fetchedRoles;
},
);
return response;
}
Future<List<PermissionOption>> 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 ?? [];
}
}

View File

@ -11,12 +11,14 @@ abstract class ApiEndpoints {
static const String visitorPassword = '/visitor-password'; static const String visitorPassword = '/visitor-password';
static const String getDevices = '/visitor-password/devices'; 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 = static const String sendOnlineMultipleTime =
'/visitor-password/temporary-password/online/multiple-time'; '/visitor-password/temporary-password/online/multiple-time';
//offline Password //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 = static const String sendOffLineMultipleTime =
'/visitor-password/temporary-password/offline/multiple-time'; '/visitor-password/temporary-password/offline/multiple-time';
@ -38,8 +40,10 @@ abstract class ApiEndpoints {
// Space Module // Space Module
static const String createSpace = '/communities/{communityId}/spaces'; static const String createSpace = '/communities/{communityId}/spaces';
static const String listSpaces = '/communities/{communityId}/spaces'; static const String listSpaces = '/communities/{communityId}/spaces';
static const String deleteSpace = '/communities/{communityId}/spaces/{spaceId}'; static const String deleteSpace =
static const String updateSpace = '/communities/{communityId}/spaces/{spaceId}'; '/communities/{communityId}/spaces/{spaceId}';
static const String updateSpace =
'/communities/{communityId}/spaces/{spaceId}';
static const String getSpace = '/communities/{communityId}/spaces/{spaceId}'; static const String getSpace = '/communities/{communityId}/spaces/{spaceId}';
static const String getSpaceHierarchy = '/communities/{communityId}/spaces'; static const String getSpaceHierarchy = '/communities/{communityId}/spaces';
@ -55,11 +59,15 @@ abstract class ApiEndpoints {
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}'; '/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
static const String scheduleByDeviceId = '/schedule/{deviceUuid}'; static const String scheduleByDeviceId = '/schedule/{deviceUuid}';
static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}'; static const String getScheduleByDeviceId =
static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}'; '/schedule/{deviceUuid}?category={category}';
static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}'; 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 factoryReset = '/device/factory/reset/{deviceUuid}';
static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; static const String powerClamp =
'/device/{powerClampUuid}/power-clamp/status';
//product //product
static const String listProducts = '/products'; static const String listProducts = '/products';
@ -68,8 +76,10 @@ abstract class ApiEndpoints {
static const String getIconScene = '/scene/icon'; static const String getIconScene = '/scene/icon';
static const String createScene = '/scene/tap-to-run'; static const String createScene = '/scene/tap-to-run';
static const String createAutomation = '/automation'; static const String createAutomation = '/automation';
static const String getUnitScenes = '/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; static const String getUnitScenes =
static const String getAutomationDetails = '/automation/details/{automationId}'; '/communities/{communityUuid}/spaces/{spaceUuid}/scenes';
static const String getAutomationDetails =
'/automation/details/{automationId}';
static const String getScene = '/scene/tap-to-run/{sceneId}'; static const String getScene = '/scene/tap-to-run/{sceneId}';
static const String deleteScene = '/scene/tap-to-run/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{sceneId}';
@ -77,4 +87,7 @@ abstract class ApiEndpoints {
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 updateAutomation = '/automation/{automationId}';
static const String roleTypes = '/role/types';
static const String permission = '/permission/{roleUuid}';
// static const String updateAutomation = '/automation/{automationId}';
} }

View File

@ -304,6 +304,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.19.0" 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: js:
dependency: transitive dependency: transitive
description: description:
@ -336,6 +352,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" 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: lints:
dependency: transitive dependency: transitive
description: description:

View File

@ -52,6 +52,8 @@ dependencies:
fl_chart: ^0.69.0 fl_chart: ^0.69.0
uuid: ^4.4.2 uuid: ^4.4.2
time_picker_spinner: ^1.0.0 time_picker_spinner: ^1.0.0
intl_phone_field: ^3.2.0
intl_phone_number_input: ^0.7.4
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: