mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +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,
|
double? iconWidth,
|
||||||
VoidCallback? onOkPressed,
|
VoidCallback? onOkPressed,
|
||||||
bool barrierDismissible = false,
|
bool barrierDismissible = false,
|
||||||
required List<Widget> actions,
|
List<Widget>? actions,
|
||||||
}) {
|
}) {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -16,7 +16,7 @@ class RoleTypeModel {
|
|||||||
uuid: json['uuid'],
|
uuid: json['uuid'],
|
||||||
createdAt: json['createdAt'],
|
createdAt: json['createdAt'],
|
||||||
updatedAt: json['updatedAt'],
|
updatedAt: json['updatedAt'],
|
||||||
type: json['type'],
|
type: json['type'].toString().toLowerCase().replaceAll("_", " "),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:syncrow_web/pages/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/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/add_user_dialog/bloc/users_event.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_event.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/bloc/users_status.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_status.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/permission_option_model.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/model/tree_node_model.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/view/roles_and_permission.dart';
|
|
||||||
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
|
||||||
import 'package:syncrow_web/services/space_mana_api.dart';
|
import 'package:syncrow_web/services/space_mana_api.dart';
|
||||||
import 'package:syncrow_web/services/user_permission.dart';
|
import 'package:syncrow_web/services/user_permission.dart';
|
||||||
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
||||||
UsersBloc() : super(UsersInitial()) {
|
UsersBloc() : super(UsersInitial()) {
|
||||||
on<GetUsers>(_getUsers);
|
|
||||||
on<ChangeUserStatus>(_changeUserStatus);
|
|
||||||
on<CheckStepStatus>(isCompleteBasicsFun);
|
on<CheckStepStatus>(isCompleteBasicsFun);
|
||||||
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
on<LoadCommunityAndSpacesEvent>(_onLoadCommunityAndSpaces);
|
||||||
on<SearchAnode>(searchTreeNode);
|
on<SearchAnode>(searchTreeNode);
|
||||||
@ -25,6 +24,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
on<SendInviteUsers>(_sendInvitUser);
|
on<SendInviteUsers>(_sendInvitUser);
|
||||||
on<ValidateBasicsStep>(_validateBasicsStep);
|
on<ValidateBasicsStep>(_validateBasicsStep);
|
||||||
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
on<CheckRoleStepStatus>(isCompleteRoleFun);
|
||||||
|
on<CheckEmailEvent>(checkEmail);
|
||||||
}
|
}
|
||||||
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
void _validateBasicsStep(ValidateBasicsStep event, Emitter<UsersState> emit) {
|
||||||
if (formKey.currentState?.validate() ?? false) {
|
if (formKey.currentState?.validate() ?? false) {
|
||||||
@ -34,74 +34,8 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<RolesUserModel> users = [];
|
|
||||||
String roleSelected = '';
|
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 formKey = GlobalKey<FormState>();
|
||||||
final TextEditingController firstNameController = TextEditingController();
|
final TextEditingController firstNameController = TextEditingController();
|
||||||
final TextEditingController lastNameController = TextEditingController();
|
final TextEditingController lastNameController = TextEditingController();
|
||||||
@ -109,6 +43,9 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
final TextEditingController phoneController = TextEditingController();
|
final TextEditingController phoneController = TextEditingController();
|
||||||
final TextEditingController jobTitleController = TextEditingController();
|
final TextEditingController jobTitleController = TextEditingController();
|
||||||
|
|
||||||
|
final TextEditingController roleSearchController = TextEditingController();
|
||||||
|
// final TextEditingController jobTitleController = TextEditingController();
|
||||||
|
|
||||||
bool? isCompleteBasics;
|
bool? isCompleteBasics;
|
||||||
bool? isCompleteRolePermissions;
|
bool? isCompleteRolePermissions;
|
||||||
bool? isCompleteSpaces;
|
bool? isCompleteSpaces;
|
||||||
@ -117,30 +54,6 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
int numberSpaces = 0;
|
int numberSpaces = 0;
|
||||||
int numberRole = 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(
|
void isCompleteSpacesFun(
|
||||||
CheckSpacesStepStatus event, Emitter<UsersState> emit) {
|
CheckSpacesStepStatus event, Emitter<UsersState> emit) {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
@ -184,7 +97,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
emit(ChangeStatusSteps());
|
emit(SpacesLoadedState());
|
||||||
return updatedCommunities;
|
return updatedCommunities;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState('Error loading communities and spaces: $e'));
|
emit(ErrorState('Error loading communities and spaces: $e'));
|
||||||
@ -239,6 +152,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> selectedIds = [];
|
List<String> selectedIds = [];
|
||||||
|
|
||||||
List<String> getSelectedIds(List<TreeNode> nodes) {
|
List<String> getSelectedIds(List<TreeNode> nodes) {
|
||||||
List<String> selectedIds = [];
|
List<String> selectedIds = [];
|
||||||
for (var node in nodes) {
|
for (var node in nodes) {
|
||||||
@ -259,7 +173,7 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
roles = await UserPermissionApi().fetchRoles();
|
roles = await UserPermissionApi().fetchRoles();
|
||||||
add(PermissionEvent(roleUuid: roles.first.uuid));
|
// add(PermissionEvent(roleUuid: roles.first.uuid));
|
||||||
emit(RolePermissionInitial());
|
emit(RolePermissionInitial());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState('Error loading communities and spaces: $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 {
|
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
List<String> selectedIds = getSelectedIds(updatedCommunities) ?? [];
|
||||||
await UserPermissionApi().sendInviteUser(
|
bool res = await UserPermissionApi().sendInviteUser(
|
||||||
email: emailController.text,
|
email: emailController.text,
|
||||||
firstName: firstNameController.text,
|
firstName: firstNameController.text,
|
||||||
jobTitle: jobTitleController.text,
|
jobTitle: jobTitleController.text,
|
||||||
lastName: lastNameController.text,
|
lastName: lastNameController.text,
|
||||||
phoneNumber: phoneController.text,
|
phoneNumber: phoneController.text,
|
||||||
roleUuid: roleSelected,
|
roleUuid: roleSelected,
|
||||||
spaceUuids: selectedIds);
|
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());
|
emit(SaveState());
|
||||||
} catch (e) {
|
} 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());
|
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) {
|
void _clearHighlightsRolePermission(List<PermissionOption> nodes) {
|
||||||
for (var node in nodes) {
|
for (var node in nodes) {
|
||||||
node.isHighlighted = false;
|
node.isHighlighted = false;
|
@ -1,21 +1,17 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/model/tree_node_model.dart';
|
import 'package:flutter/material.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/pages/roles_and_permission/users_page/add_user_dialog/model/tree_node_model.dart';
|
||||||
|
|
||||||
sealed class UsersEvent extends Equatable {
|
sealed class UsersEvent extends Equatable {
|
||||||
const UsersEvent();
|
const UsersEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
class GetUsers extends UsersEvent {
|
|
||||||
const GetUsers();
|
|
||||||
@override
|
|
||||||
List<Object?> get props => [];
|
|
||||||
}
|
|
||||||
|
|
||||||
class SendInviteUsers extends UsersEvent {
|
class SendInviteUsers extends UsersEvent {
|
||||||
const SendInviteUsers();
|
final BuildContext context;
|
||||||
|
const SendInviteUsers({required this.context});
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
List<Object?> get props => [context];
|
||||||
}
|
}
|
||||||
|
|
||||||
class CheckSpacesStepStatus extends UsersEvent {
|
class CheckSpacesStepStatus extends UsersEvent {
|
||||||
@ -30,7 +26,6 @@ class CheckRoleStepStatus extends UsersEvent {
|
|||||||
List<Object?> get props => [];
|
List<Object?> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class LoadCommunityAndSpacesEvent extends UsersEvent {
|
class LoadCommunityAndSpacesEvent extends UsersEvent {
|
||||||
const LoadCommunityAndSpacesEvent();
|
const LoadCommunityAndSpacesEvent();
|
||||||
@override
|
@override
|
||||||
@ -57,16 +52,6 @@ class GetBatchStatus extends UsersEvent {
|
|||||||
List<Object?> get props => [uuids];
|
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 {
|
class CheckStepStatus extends UsersEvent {
|
||||||
final int? steps;
|
final int? steps;
|
||||||
const CheckStepStatus({this.steps});
|
const CheckStepStatus({this.steps});
|
||||||
@ -104,3 +89,9 @@ class ValidateBasicsStep extends UsersEvent {
|
|||||||
@override
|
@override
|
||||||
List<Object?> get props => [];
|
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 => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
final class UsersLoadedState extends UsersState {
|
final class SpacesLoadedState extends UsersState {
|
||||||
List<RolesUserModel> users = [];
|
SpacesLoadedState();
|
||||||
UsersLoadedState({required this.users});
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [users];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
final class ErrorState extends UsersState {
|
final class ErrorState extends UsersState {
|
||||||
@ -77,3 +76,7 @@ class BasicsStepInvalidState extends UsersState {
|
|||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/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/add_user_dialog/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/add_user_dialog/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/add_user_dialog/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/add_user_dialog/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/view/spaces_access_view.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
@ -56,7 +56,6 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// Sidebar for Steps
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: const EdgeInsets.all(20),
|
padding: const EdgeInsets.all(20),
|
||||||
@ -75,7 +74,6 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
width: 1,
|
width: 1,
|
||||||
color: ColorsManager.grayBorder,
|
color: ColorsManager.grayBorder,
|
||||||
),
|
),
|
||||||
// Main content (Form)
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -109,6 +107,8 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
_blocRole.add(const CheckEmailEvent());
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
if (currentStep < 3) {
|
if (currentStep < 3) {
|
||||||
currentStep++;
|
currentStep++;
|
||||||
@ -117,11 +117,10 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
} else if (currentStep == 3) {
|
} else if (currentStep == 3) {
|
||||||
_blocRole
|
_blocRole
|
||||||
.add(const CheckSpacesStepStatus());
|
.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) {
|
Widget _buildStep2Indicator(int step, String label, UsersBloc bloc) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
bloc.add(const CheckStepStatus());
|
bloc.add(const CheckStepStatus());
|
||||||
|
if (step3 == 3) {
|
||||||
|
bloc.add(const CheckRoleStepStatus());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
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(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
bloc.add(const CheckSpacesStepStatus());
|
|
||||||
currentStep = step;
|
currentStep = step;
|
||||||
});
|
step3 = step;
|
||||||
Future.delayed(const Duration(milliseconds: 500), () {
|
bloc.add(const CheckSpacesStepStatus());
|
||||||
bloc.add(const ValidateBasicsStep());
|
bloc.add(const CheckStepStatus());
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -243,9 +313,9 @@ class _AddNewUserDialogState extends State<AddNewUserDialog> {
|
|||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
currentStep == step
|
currentStep == step
|
||||||
? Assets.currentProcessIcon
|
? Assets.currentProcessIcon
|
||||||
: bloc.isCompleteBasics == false
|
: bloc.isCompleteRolePermissions == false
|
||||||
? Assets.wrongProcessIcon
|
? Assets.wrongProcessIcon
|
||||||
: bloc.isCompleteBasics == true
|
: bloc.isCompleteRolePermissions == true
|
||||||
? Assets.completeProcessIcon
|
? Assets.completeProcessIcon
|
||||||
: Assets.uncomplete_ProcessIcon,
|
: Assets.uncomplete_ProcessIcon,
|
||||||
width: 25,
|
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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/country_picker_dialog.dart';
|
||||||
import 'package:intl_phone_field/intl_phone_field.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/add_user_dialog/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_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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/utils/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
@ -30,7 +32,7 @@ class BasicsView extends StatelessWidget {
|
|||||||
color: Colors.black),
|
color: Colors.black),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 80,
|
height: 50,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'To get started, fill out some basic information about who you’re adding as a user.',
|
'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(
|
const SizedBox(
|
||||||
height: 25,
|
height: 35,
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// First Name
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -52,9 +53,6 @@ class BasicsView extends StatelessWidget {
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
// SizedBox(
|
|
||||||
// width: 15,
|
|
||||||
// ),
|
|
||||||
const Text(
|
const Text(
|
||||||
" * ",
|
" * ",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@ -75,7 +73,14 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
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,
|
controller: _blocRole.firstNameController,
|
||||||
decoration: inputTextFormDeco(
|
decoration: inputTextFormDeco(
|
||||||
hintText: "Enter first name",
|
hintText: "Enter first name",
|
||||||
@ -96,10 +101,7 @@ class BasicsView extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
SizedBox(width: 10),
|
|
||||||
|
|
||||||
// Last Name
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -125,8 +127,14 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
onChanged: (value) {
|
||||||
|
Future.delayed(const Duration(milliseconds: 200),
|
||||||
|
() {
|
||||||
|
_blocRole.add(ValidateBasicsStep());
|
||||||
|
});
|
||||||
|
},
|
||||||
controller: _blocRole.lastNameController,
|
controller: _blocRole.lastNameController,
|
||||||
style: TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
decoration:
|
decoration:
|
||||||
inputTextFormDeco(hintText: "Enter last name")
|
inputTextFormDeco(hintText: "Enter last name")
|
||||||
.copyWith(
|
.copyWith(
|
||||||
@ -149,7 +157,7 @@ class BasicsView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Column(
|
Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -176,8 +184,14 @@ class BasicsView extends StatelessWidget {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
onChanged: (value) {
|
||||||
|
Future.delayed(const Duration(milliseconds: 200), () {
|
||||||
|
_blocRole.add(const CheckStepStatus());
|
||||||
|
_blocRole.add(ValidateBasicsStep());
|
||||||
|
});
|
||||||
|
},
|
||||||
controller: _blocRole.emailController,
|
controller: _blocRole.emailController,
|
||||||
style: TextStyle(color: Colors.black),
|
style: const TextStyle(color: ColorsManager.blackColor),
|
||||||
decoration: inputTextFormDeco(hintText: "name@example.com")
|
decoration: inputTextFormDeco(hintText: "name@example.com")
|
||||||
.copyWith(
|
.copyWith(
|
||||||
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
hintStyle: context.textTheme.bodyMedium?.copyWith(
|
||||||
@ -189,24 +203,24 @@ class BasicsView extends StatelessWidget {
|
|||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Enter Email Address';
|
return 'Enter Email Address';
|
||||||
}
|
}
|
||||||
// Regular expression for email validation
|
|
||||||
final emailRegex = RegExp(
|
final emailRegex = RegExp(
|
||||||
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$',
|
||||||
);
|
);
|
||||||
if (!emailRegex.hasMatch(value)) {
|
if (!emailRegex.hasMatch(value)) {
|
||||||
return 'Enter a valid Email Address';
|
return 'Enter a valid Email Address';
|
||||||
}
|
}
|
||||||
|
if (_blocRole.checkEmailValid != "Valid email") {
|
||||||
|
return _blocRole.checkEmailValid;
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// Phone Number
|
|
||||||
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -227,7 +241,8 @@ class BasicsView extends StatelessWidget {
|
|||||||
pickerDialogStyle: PickerDialogStyle(),
|
pickerDialogStyle: PickerDialogStyle(),
|
||||||
dropdownIconPosition: IconPosition.leading,
|
dropdownIconPosition: IconPosition.leading,
|
||||||
disableLengthCheck: true,
|
disableLengthCheck: true,
|
||||||
dropdownTextStyle: TextStyle(color: Colors.black),
|
dropdownTextStyle:
|
||||||
|
const TextStyle(color: ColorsManager.blackColor),
|
||||||
textInputAction: TextInputAction.done,
|
textInputAction: TextInputAction.done,
|
||||||
decoration: inputTextFormDeco(
|
decoration: inputTextFormDeco(
|
||||||
hintText: "05x xxx xxxx",
|
hintText: "05x xxx xxxx",
|
||||||
@ -238,18 +253,39 @@ class BasicsView extends StatelessWidget {
|
|||||||
color: ColorsManager.textGray),
|
color: ColorsManager.textGray),
|
||||||
),
|
),
|
||||||
initialCountryCode: 'AE',
|
initialCountryCode: 'AE',
|
||||||
style: TextStyle(color: Colors.black),
|
countries: const [
|
||||||
onChanged: (phone) {
|
Country(
|
||||||
print(phone.completeNumber);
|
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,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
SizedBox(width: 10),
|
|
||||||
|
|
||||||
// Job Title
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -270,7 +306,8 @@ class BasicsView extends StatelessWidget {
|
|||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: _blocRole.jobTitleController,
|
controller: _blocRole.jobTitleController,
|
||||||
style: TextStyle(color: Colors.black),
|
style:
|
||||||
|
const TextStyle(color: ColorsManager.blackColor),
|
||||||
decoration: inputTextFormDeco(
|
decoration: inputTextFormDeco(
|
||||||
hintText: "Job Title (Optional)")
|
hintText: "Job Title (Optional)")
|
||||||
.copyWith(
|
.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/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/add_user_dialog/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_status.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
class DeleteUserDialog extends StatefulWidget {
|
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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_svg/svg.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/add_user_dialog/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/add_user_dialog/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/add_user_dialog/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/model/tree_node_model.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package
|
import 'package:flutter_bloc/flutter_bloc.dart'; // Import Bloc package
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:syncrow_web/pages/roles_and_permission/users_page/bloc/users_bloc.dart';
|
import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/add_user_dialog.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/users_table/bloc/user_table_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/users_table/bloc/user_table_event.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/users_table/bloc/user_table_state.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/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/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
@ -85,7 +88,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
? 'Invited'
|
? 'Invited'
|
||||||
: 'Active';
|
: 'Active';
|
||||||
context
|
context
|
||||||
.read<UsersBloc>()
|
.read<UserTableBloc>()
|
||||||
.add(ChangeUserStatus(userId: userId, newStatus: newStatus));
|
.add(ChangeUserStatus(userId: userId, newStatus: newStatus));
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -104,12 +107,10 @@ class UsersPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return RolesAndPermission();
|
return BlocBuilder<UserTableBloc, UserTableState>(
|
||||||
// }
|
|
||||||
// }
|
|
||||||
return BlocBuilder<UsersBloc, UsersState>(
|
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final screenSize = MediaQuery.of(context).size;
|
final screenSize = MediaQuery.of(context).size;
|
||||||
|
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||||
|
|
||||||
if (state is UsersLoadingState) {
|
if (state is UsersLoadingState) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
@ -158,7 +159,9 @@ class UsersPage extends StatelessWidget {
|
|||||||
return const AddNewUserDialog();
|
return const AddNewUserDialog();
|
||||||
},
|
},
|
||||||
).then((listDevice) {
|
).then((listDevice) {
|
||||||
if (listDevice != null) {}
|
if (listDevice != null) {
|
||||||
|
|
||||||
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -181,6 +184,56 @@ class UsersPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 25),
|
const SizedBox(height: 25),
|
||||||
DynamicTableScreen(
|
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 [
|
titles: const [
|
||||||
"Full Name",
|
"Full Name",
|
||||||
"Email Address",
|
"Email Address",
|
||||||
@ -210,10 +263,10 @@ class UsersPage extends StatelessWidget {
|
|||||||
status(status: user.status!),
|
status(status: user.status!),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
actionButton(
|
// actionButton(
|
||||||
title: "Activity Log",
|
// title: "Activity Log",
|
||||||
onTap: () {},
|
// onTap: () {},
|
||||||
),
|
// ),
|
||||||
actionButton(
|
actionButton(
|
||||||
title: "Edit",
|
title: "Edit",
|
||||||
onTap: () {},
|
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:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.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_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/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/users_table/bloc/user_table_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/users_table/bloc/user_table_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/view/users_page.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
import 'package:syncrow_web/utils/extension/build_context_x.dart';
|
||||||
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
import 'package:syncrow_web/web_layout/web_scaffold.dart';
|
||||||
@ -17,8 +16,7 @@ class RolesAndPermissionPage extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (BuildContext context) =>
|
create: (BuildContext context) => RolesPermissionBloc(),
|
||||||
RolesPermissionBloc()..add(const GetRoles()),
|
|
||||||
child: BlocConsumer<RolesPermissionBloc, RolesPermissionState>(
|
child: BlocConsumer<RolesPermissionBloc, RolesPermissionState>(
|
||||||
listener: (context, state) {},
|
listener: (context, state) {},
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
@ -77,8 +75,8 @@ class RolesAndPermissionPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
scaffoldBody: BlocProvider<UsersBloc>(
|
scaffoldBody: BlocProvider<UserTableBloc>(
|
||||||
create: (context) => UsersBloc()..add(const GetUsers()),
|
create: (context) => UserTableBloc()..add(const GetUsers()),
|
||||||
child: const UsersPage(),
|
child: const UsersPage(),
|
||||||
)
|
)
|
||||||
// _blocRole.tapSelect == false
|
// _blocRole.tapSelect == false
|
||||||
|
@ -252,7 +252,6 @@ class CommunitySpaceManagementApi {
|
|||||||
path: ApiEndpoints.getSpaceHierarchy
|
path: ApiEndpoints.getSpaceHierarchy
|
||||||
.replaceAll('{communityId}', communityId),
|
.replaceAll('{communityId}', communityId),
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
print('=-=-=-=$json');
|
|
||||||
final spaceModels = (json['data'] as List)
|
final spaceModels = (json['data'] as List)
|
||||||
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
.map((spaceJson) => SpaceModel.fromJson(spaceJson))
|
||||||
.toList();
|
.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/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/services/api/http_service.dart';
|
||||||
import 'package:syncrow_web/utils/constants/api_const.dart';
|
import 'package:syncrow_web/utils/constants/api_const.dart';
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ class UserPermissionApi {
|
|||||||
return response ?? [];
|
return response ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Future sendInviteUser({
|
Future<bool> sendInviteUser({
|
||||||
String? firstName,
|
String? firstName,
|
||||||
String? lastName,
|
String? lastName,
|
||||||
String? email,
|
String? email,
|
||||||
@ -42,22 +44,58 @@ class UserPermissionApi {
|
|||||||
String? roleUuid,
|
String? roleUuid,
|
||||||
List<String>? spaceUuids,
|
List<String>? spaceUuids,
|
||||||
}) async {
|
}) async {
|
||||||
final response = await _httpService.post(
|
try {
|
||||||
path: ApiEndpoints.permission,
|
final body = <String, dynamic>{
|
||||||
showServerMessage: true,
|
|
||||||
body: {
|
|
||||||
"firstName": firstName,
|
"firstName": firstName,
|
||||||
"lastName": lastName,
|
"lastName": lastName,
|
||||||
"email": email,
|
"email": email,
|
||||||
"jobTitle": jobTitle,
|
"jobTitle": jobTitle != '' ? jobTitle : " ",
|
||||||
"phoneNumber": phoneNumber,
|
"phoneNumber": phoneNumber != '' ? phoneNumber : " ",
|
||||||
"roleUuid": roleUuid,
|
"roleUuid": roleUuid,
|
||||||
"spaceUuids": spaceUuids
|
"projectUuid": "0e62577c-06fa-41b9-8a92-99a21fbaf51c",
|
||||||
},
|
"spaceUuids": spaceUuids,
|
||||||
expectedResponseModel: (json) {
|
};
|
||||||
print(json);
|
final response = await _httpService.post(
|
||||||
},
|
path: ApiEndpoints.inviteUser,
|
||||||
);
|
showServerMessage: true,
|
||||||
return response ?? [];
|
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';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
|
||||||
abstract class ApiEndpoints {
|
abstract class ApiEndpoints {
|
||||||
|
static const String projectUuid = "0e62577c-06fa-41b9-8a92-99a21fbaf51c";
|
||||||
static String baseUrl = dotenv.env['BASE_URL'] ?? '';
|
static String baseUrl = dotenv.env['BASE_URL'] ?? '';
|
||||||
static const String signUp = '/authentication/user/signup';
|
static const String signUp = '/authentication/user/signup';
|
||||||
static const String login = '/authentication/user/login';
|
static const String login = '/authentication/user/login';
|
||||||
@ -38,23 +39,32 @@ abstract class ApiEndpoints {
|
|||||||
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}';
|
||||||
|
|
||||||
// Space Module
|
// Space Module
|
||||||
static const String createSpace = '/communities/{communityId}/spaces';
|
static const String createSpace =
|
||||||
static const String listSpaces = '/communities/{communityId}/spaces';
|
'/projects/${projectUuid}/communities/{communityId}/spaces';
|
||||||
|
static const String listSpaces =
|
||||||
|
'/projects/${projectUuid}/communities/{communityId}/spaces';
|
||||||
static const String deleteSpace =
|
static const String deleteSpace =
|
||||||
'/communities/{communityId}/spaces/{spaceId}';
|
'/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}';
|
||||||
static const String updateSpace =
|
static const String updateSpace =
|
||||||
'/communities/{communityId}/spaces/{spaceId}';
|
'/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}';
|
||||||
static const String getSpace = '/communities/{communityId}/spaces/{spaceId}';
|
static const String getSpace =
|
||||||
static const String getSpaceHierarchy = '/communities/{communityId}/spaces';
|
'/projects/${projectUuid}/communities/{communityId}/spaces/{spaceId}';
|
||||||
|
static const String getSpaceHierarchy =
|
||||||
|
'/projects/${projectUuid}/communities/{communityId}/spaces';
|
||||||
|
|
||||||
// Community Module
|
// Community Module
|
||||||
static const String createCommunity = '/communities';
|
static const String createCommunity = '/projects/${projectUuid}/communities';
|
||||||
static const String getCommunityList = '/communities';
|
static const String getCommunityList = '/projects/${projectUuid}/communities';
|
||||||
static const String getCommunityById = '/communities/{communityId}';
|
static const String getCommunityById =
|
||||||
static const String updateCommunity = '/communities/{communityId}';
|
'/projects/${projectUuid}/communities/{communityId}';
|
||||||
static const String deleteCommunity = '/communities/{communityId}';
|
static const String updateCommunity =
|
||||||
static const String getUserCommunities = '/communities/user/{userUuid}';
|
'/projects/${projectUuid}/communities/{communityId}';
|
||||||
static const String createUserCommunity = '/communities/user';
|
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 =
|
static const String getDeviceLogsByDate =
|
||||||
'/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}';
|
'/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 roleTypes = '/role/types';
|
||||||
static const String permission = '/permission/{roleUuid}';
|
static const String permission = '/permission/{roleUuid}';
|
||||||
static const String inviteUser = '/invite-user';
|
static const String inviteUser = '/invite-user';
|
||||||
|
static const String checkEmail = '/invite-user/check-email';
|
||||||
// static const String updateAutomation = '/automation/{automationId}';
|
// 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 arrowDown = 'assets/icons/arrow_down.svg';
|
||||||
|
|
||||||
static const String userManagement = 'assets/icons/user_management.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
|
//user_management.svg
|
||||||
|
Reference in New Issue
Block a user