mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
add_user_dialog
This commit is contained in:
BIN
assets/icons/atoz_icon.png
Normal file
BIN
assets/icons/atoz_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
3
assets/icons/filter_table_icon.svg
Normal file
3
assets/icons/filter_table_icon.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M10.0001 0.416701C10.0001 0.647137 9.81341 0.833403 9.58339 0.833403H4.16627C3.93625 0.833403 3.74957 0.647137 3.74957 0.416701C3.74957 0.186266 3.93625 0 4.16627 0H9.58339C9.81341 0 10.0001 0.186266 10.0001 0.416701ZM8.33328 2.08351H4.16627C3.93625 2.08351 3.74957 2.26977 3.74957 2.50021C3.74957 2.73064 3.93625 2.91691 4.16627 2.91691H8.33328C8.5633 2.91691 8.74998 2.73064 8.74998 2.50021C8.74998 2.26977 8.5633 2.08351 8.33328 2.08351ZM7.08318 4.16701H4.16627C3.93625 4.16701 3.74957 4.35328 3.74957 4.58372C3.74957 4.81415 3.93625 5.00042 4.16627 5.00042H7.08318C7.3132 5.00042 7.49988 4.81415 7.49988 4.58372C7.49988 4.35328 7.3132 4.16701 7.08318 4.16701ZM5.83307 6.25052H4.16627C3.93625 6.25052 3.74957 6.43679 3.74957 6.66722C3.74957 6.89766 3.93625 7.08392 4.16627 7.08392H5.83307C6.06309 7.08392 6.24977 6.89766 6.24977 6.66722C6.24977 6.43679 6.06309 6.25052 5.83307 6.25052ZM3.21077 8.03942L2.49946 8.75073V0.416701C2.49946 0.186266 2.31278 0 2.08276 0C1.85274 0 1.66606 0.186266 1.66606 0.416701V8.75073L0.954333 8.039C0.791403 7.87607 0.528048 7.87607 0.365118 8.039C0.202187 8.20193 0.202187 8.46529 0.365118 8.62822L1.49313 9.75623C1.65564 9.91874 1.86899 10 2.08276 10C2.29653 10 2.50946 9.91874 2.67198 9.75623L3.79999 8.62822C3.96292 8.46529 3.96292 8.20193 3.79999 8.039C3.63706 7.87607 3.3737 7.87649 3.21077 8.03942Z" fill="#999999"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/icons/ztoa_icon.png
Normal file
BIN
assets/icons/ztoa_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -12,7 +12,7 @@ Future<void> showCustomDialog({
|
||||
double? iconWidth,
|
||||
VoidCallback? onOkPressed,
|
||||
bool barrierDismissible = false,
|
||||
required List<Widget> actions,
|
||||
List<Widget>? actions,
|
||||
}) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
|
@ -16,7 +16,7 @@ class RoleTypeModel {
|
||||
uuid: json['uuid'],
|
||||
createdAt: json['createdAt'],
|
||||
updatedAt: json['updatedAt'],
|
||||
type: json['type'],
|
||||
type: json['type'].toString().toLowerCase().replaceAll("_", " "),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<UsersEvent, UsersState> {
|
||||
UsersBloc() : super(UsersInitial()) {
|
||||
on<GetUsers>(_getUsers);
|
||||
on<ChangeUserStatus>(_changeUserStatus);
|
||||
on<CheckStepStatus>(isCompleteBasicsFun);
|
||||
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
||||
on<SearchAnode>(searchTreeNode);
|
||||
@ -25,6 +24,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
on<SendInviteUsers>(_sendInvitUser);
|
||||
on<ValidateBasicsStep>(_validateBasicsStep);
|
||||
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
||||
on<CheckEmailEvent>(checkEmail);
|
||||
}
|
||||
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
||||
if (formKey.currentState?.validate() ?? false) {
|
||||
@ -34,74 +34,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
}
|
||||
}
|
||||
|
||||
List<RolesUserModel> users = [];
|
||||
String roleSelected = '';
|
||||
|
||||
Future<void> _getUsers(GetUsers event, Emitter<UsersState> 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<UsersState> 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<FormState>();
|
||||
final TextEditingController firstNameController = TextEditingController();
|
||||
final TextEditingController lastNameController = TextEditingController();
|
||||
@ -109,6 +43,9 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
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<UsersEvent, UsersState> {
|
||||
int numberSpaces = 0;
|
||||
int numberRole = 0;
|
||||
|
||||
// isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
|
||||
// emit(UsersLoadingState());
|
||||
// isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||
// lastNameController.text.isNotEmpty &&
|
||||
// emailController.text.isNotEmpty;
|
||||
// emit(ChangeStatusSteps());
|
||||
// return isCompleteBasics;
|
||||
// }
|
||||
|
||||
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
|
||||
final emailRegex = RegExp(
|
||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||
);
|
||||
isCompleteBasics = firstNameController.text.isNotEmpty &&
|
||||
lastNameController.text.isNotEmpty &&
|
||||
emailController.text.isNotEmpty &&
|
||||
emailRegex.hasMatch(emailController.text);
|
||||
|
||||
emit(ChangeStatusSteps());
|
||||
return isCompleteBasics!;
|
||||
}
|
||||
|
||||
void isCompleteSpacesFun(
|
||||
CheckSpacesStepStatus event, Emitter<UsersState> emit) {
|
||||
emit(UsersLoadingState());
|
||||
@ -184,7 +97,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
);
|
||||
}).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<UsersEvent, UsersState> {
|
||||
}
|
||||
|
||||
List<String> selectedIds = [];
|
||||
|
||||
List<String> getSelectedIds(List<TreeNode> nodes) {
|
||||
List<String> selectedIds = [];
|
||||
for (var node in nodes) {
|
||||
@ -259,7 +173,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||
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<UsersEvent, UsersState> {
|
||||
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
||||
try {
|
||||
emit(UsersLoadingState());
|
||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
||||
await UserPermissionApi().sendInviteUser(
|
||||
email: emailController.text,
|
||||
firstName: firstNameController.text,
|
||||
jobTitle: jobTitleController.text,
|
||||
lastName: lastNameController.text,
|
||||
phoneNumber: phoneController.text,
|
||||
roleUuid: roleSelected,
|
||||
spaceUuids: selectedIds);
|
||||
List<String> 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: <Widget>[
|
||||
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<UsersEvent, UsersState> {
|
||||
emit(ChangeStatusSteps());
|
||||
}
|
||||
|
||||
String checkEmailValid = '';
|
||||
|
||||
Future<void> checkEmail(
|
||||
CheckEmailEvent event, Emitter<UsersState> emit) async {
|
||||
emit(UsersLoadingState());
|
||||
String? res = await UserPermissionApi().checkEmail(
|
||||
emailController.text,
|
||||
);
|
||||
checkEmailValid = res!;
|
||||
emit(ChangeStatusSteps());
|
||||
}
|
||||
|
||||
bool isCompleteBasicsFun(CheckStepStatus event, Emitter<UsersState> 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<PermissionOption> nodes) {
|
||||
for (var node in nodes) {
|
||||
node.isHighlighted = false;
|
@ -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<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SendInviteUsers extends UsersEvent {
|
||||
const SendInviteUsers();
|
||||
final BuildContext context;
|
||||
const SendInviteUsers({required this.context});
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
List<Object?> get props => [context];
|
||||
}
|
||||
|
||||
class CheckSpacesStepStatus extends UsersEvent {
|
||||
@ -30,7 +26,6 @@ class CheckRoleStepStatus extends UsersEvent {
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
|
||||
class LoadCommunityAndSpacesEvent extends UsersEvent {
|
||||
const LoadCommunityAndSpacesEvent();
|
||||
@override
|
||||
@ -57,16 +52,6 @@ class GetBatchStatus extends UsersEvent {
|
||||
List<Object?> get props => [uuids];
|
||||
}
|
||||
|
||||
class ChangeUserStatus extends UsersEvent {
|
||||
final String userId;
|
||||
final String newStatus;
|
||||
|
||||
const ChangeUserStatus({required this.userId, required this.newStatus});
|
||||
|
||||
@override
|
||||
List<Object?> 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<Object?> get props => [];
|
||||
}
|
||||
|
||||
class CheckEmailEvent extends UsersEvent {
|
||||
const CheckEmailEvent();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
@ -30,11 +30,10 @@ final class SaveState extends UsersState {
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UsersLoadedState extends UsersState {
|
||||
List<RolesUserModel> users = [];
|
||||
UsersLoadedState({required this.users});
|
||||
final class SpacesLoadedState extends UsersState {
|
||||
SpacesLoadedState();
|
||||
@override
|
||||
List<Object> get props => [users];
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class ErrorState extends UsersState {
|
||||
@ -77,3 +76,7 @@ class BasicsStepInvalidState extends UsersState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
final class ValidateBasics extends UsersState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
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'].toString().toLowerCase().replaceAll("_", " ") ?? '',
|
||||
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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum CheckState { none, some, all }
|
@ -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<AddNewUserDialog> {
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
// Sidebar for Steps
|
||||
Expanded(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
@ -75,7 +74,6 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
width: 1,
|
||||
color: ColorsManager.grayBorder,
|
||||
),
|
||||
// Main content (Form)
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Padding(
|
||||
@ -109,6 +107,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_blocRole.add(const CheckEmailEvent());
|
||||
|
||||
setState(() {
|
||||
if (currentStep < 3) {
|
||||
currentStep++;
|
||||
@ -117,11 +117,10 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
||||
} 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<AddNewUserDialog> {
|
||||
}
|
||||
}
|
||||
|
||||
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<AddNewUserDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
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<AddNewUserDialog> {
|
||||
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<AddNewUserDialog> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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),
|
||||
],
|
||||
),
|
||||
);
|
@ -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 {
|
@ -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<PermissionManagement> {
|
||||
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(),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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<RoleDropdown> {
|
||||
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<String>(
|
||||
dropdownColor: ColorsManager.whiteColors,
|
||||
alignment: Alignment.center,
|
||||
focusColor: Colors.white,
|
||||
autofocus: true,
|
||||
value: selectedRole.isNotEmpty ? selectedRole : null,
|
||||
items: widget.bloc!.roles.map((role) {
|
||||
return DropdownMenuItem<String>(
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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<UsersBloc, UsersState>(builder: (context, state) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(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,
|
||||
))))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
@ -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';
|
@ -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<UserTableEvent, UserTableState> {
|
||||
UserTableBloc() : super(TableInitial()) {
|
||||
on<GetUsers>(_getUsers);
|
||||
on<ChangeUserStatus>(_changeUserStatus);
|
||||
on<SortUsersByNameAsc>(_toggleSortUsersByNameAsc);
|
||||
on<SortUsersByNameDesc>(_toggleSortUsersByNameDesc);
|
||||
on<DateOldestToNewestEvent>(_toggleSortUsersByDateOldestToNewest);
|
||||
on<DateNewestToOldestEvent>(_toggleSortUsersByDateNewestToOldest);
|
||||
}
|
||||
|
||||
List<RolesUserModel> users = [];
|
||||
List<RolesUserModel> initialUsers = []; // Save the initial state
|
||||
String currentSortOrder = ''; // Keeps track of the current sorting order
|
||||
String currentSortOrderDate = ''; // Keeps track of the current sorting order
|
||||
|
||||
Future<void> _getUsers(GetUsers event, Emitter<UserTableState> 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<UserTableState> 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<UserTableState> 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<UserTableState> 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<UserTableState> 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<UserTableState> 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 ');
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
sealed class UserTableEvent extends Equatable {
|
||||
const UserTableEvent();
|
||||
}
|
||||
|
||||
class GetRoles extends UserTableEvent {
|
||||
const GetRoles();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class GetUsers extends UserTableEvent {
|
||||
const GetUsers();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class ChangeUserStatus extends UserTableEvent {
|
||||
final String userId;
|
||||
final String newStatus;
|
||||
|
||||
const ChangeUserStatus({required this.userId, required this.newStatus});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [userId, newStatus];
|
||||
}
|
||||
|
||||
class SortUsersByNameAsc extends UserTableEvent {
|
||||
const SortUsersByNameAsc();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class SortUsersByNameDesc extends UserTableEvent {
|
||||
const SortUsersByNameDesc();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class StoreUsersEvent extends UserTableEvent {
|
||||
const StoreUsersEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
|
||||
class DateNewestToOldestEvent extends UserTableEvent {
|
||||
const DateNewestToOldestEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class DateOldestToNewestEvent extends UserTableEvent {
|
||||
const DateOldestToNewestEvent();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
@ -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<Object> get props => [];
|
||||
}
|
||||
|
||||
final class RolesLoadingState extends UserTableState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UsersLoadingState extends UserTableState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class RolesLoadedState extends UserTableState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class UsersLoadedState extends UserTableState {
|
||||
List<RolesUserModel> users = [];
|
||||
UsersLoadedState({required this.users});
|
||||
@override
|
||||
List<Object> get props => [users];
|
||||
}
|
||||
|
||||
final class ErrorState extends UserTableState {
|
||||
final String message;
|
||||
|
||||
const ErrorState(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
/// report state
|
||||
final class SosReportLoadingState extends UserTableState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class RolesErrorState extends UserTableState {
|
||||
final String message;
|
||||
|
||||
const RolesErrorState(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
/// automation reports
|
||||
|
||||
final class SosAutomationReportLoadingState extends UserTableState {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
final class SosAutomationReportErrorState extends UserTableState {
|
||||
final String message;
|
||||
|
||||
const SosAutomationReportErrorState(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
final class ChangeTapStatus extends UserTableState {
|
||||
bool select = true;
|
||||
|
||||
ChangeTapStatus({required this.select});
|
||||
|
||||
@override
|
||||
List<Object> get props => [select];
|
||||
}
|
@ -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<void> 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: <PopupMenuEntry>[
|
||||
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;
|
||||
// });
|
||||
});
|
||||
}
|
@ -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<void> 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: <PopupMenuEntry>[
|
||||
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;
|
||||
// });
|
||||
});
|
||||
}
|
@ -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<void> 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: <PopupMenuEntry>[
|
||||
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;
|
||||
// });
|
||||
});
|
||||
}
|
@ -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<String> titles;
|
||||
final List<List<Widget>> 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<DynamicTableScreen>
|
||||
with WidgetsBindingObserver {
|
||||
late List<double> 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<double>.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<double>.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<double>.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(),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
@ -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<UsersBloc>()
|
||||
.read<UserTableBloc>()
|
||||
.add(ChangeUserStatus(userId: userId, newStatus: newStatus));
|
||||
},
|
||||
child: Padding(
|
||||
@ -104,12 +107,10 @@ class UsersPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// return RolesAndPermission();
|
||||
// }
|
||||
// }
|
||||
return BlocBuilder<UsersBloc, UsersState>(
|
||||
return BlocBuilder<UserTableBloc, UserTableState>(
|
||||
builder: (context, state) {
|
||||
final screenSize = MediaQuery.of(context).size;
|
||||
final _blocRole = BlocProvider.of<UserTableBloc>(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<UserTableBloc>()
|
||||
.add(const SortUsersByNameAsc());
|
||||
},
|
||||
zToaTap: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const SortUsersByNameDesc());
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 4) {
|
||||
showDateFilterMenu(
|
||||
context: context,
|
||||
isSelected: _blocRole.currentSortOrderDate,
|
||||
aToZTap: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const DateNewestToOldestEvent());
|
||||
},
|
||||
zToaTap: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const DateOldestToNewestEvent());
|
||||
},
|
||||
);
|
||||
}
|
||||
if (columnIndex == 8) {
|
||||
showDeActivateFilterMenu(
|
||||
context: context,
|
||||
isSelected: _blocRole.currentSortOrderDate,
|
||||
aToZTap: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.add(const DateNewestToOldestEvent());
|
||||
},
|
||||
zToaTap: () {
|
||||
context
|
||||
.read<UserTableBloc>()
|
||||
.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: () {},
|
@ -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<UsersBloc, UsersState>(builder: (context, state) {
|
||||
final _blocRole = BlocProvider.of<UsersBloc>(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<DropdownExample> {
|
||||
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<String>(
|
||||
alignment: Alignment.center,
|
||||
focusColor: Colors.white,
|
||||
autofocus: true,
|
||||
value: selectedRole,
|
||||
items: widget.bloc!.roles.map((role) {
|
||||
return DropdownMenuItem<String>(
|
||||
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<DeviceManagement> {
|
||||
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<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(),
|
||||
};
|
||||
}
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class DynamicTableScreen extends StatefulWidget {
|
||||
final List<String> titles;
|
||||
final List<List<Widget>> rows;
|
||||
|
||||
DynamicTableScreen({required this.titles, required this.rows});
|
||||
|
||||
@override
|
||||
_DynamicTableScreenState createState() => _DynamicTableScreenState();
|
||||
}
|
||||
|
||||
class _DynamicTableScreenState extends State<DynamicTableScreen>
|
||||
with WidgetsBindingObserver {
|
||||
late List<double> 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<double>.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<double>.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<double>.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(),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
@ -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<RolesPermissionBloc, RolesPermissionState>(
|
||||
listener: (context, state) {},
|
||||
builder: (context, state) {
|
||||
@ -77,8 +75,8 @@ class RolesAndPermissionPage extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
scaffoldBody: BlocProvider<UsersBloc>(
|
||||
create: (context) => UsersBloc()..add(const GetUsers()),
|
||||
scaffoldBody: BlocProvider<UserTableBloc>(
|
||||
create: (context) => UserTableBloc()..add(const GetUsers()),
|
||||
child: const UsersPage(),
|
||||
)
|
||||
// _blocRole.tapSelect == false
|
||||
|
@ -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();
|
||||
|
@ -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<bool> sendInviteUser({
|
||||
String? firstName,
|
||||
String? lastName,
|
||||
String? email,
|
||||
@ -42,22 +44,58 @@ class UserPermissionApi {
|
||||
String? roleUuid,
|
||||
List<String>? spaceUuids,
|
||||
}) async {
|
||||
final response = await _httpService.post(
|
||||
path: ApiEndpoints.permission,
|
||||
showServerMessage: true,
|
||||
body: {
|
||||
try {
|
||||
final body = <String, dynamic>{
|
||||
"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<String?> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user