mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
Fix bugs related to the user table, privacy policy, and table filter.
This commit is contained in:
@ -31,7 +31,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
|
|
||||||
////////////////////////////// forget password //////////////////////////////////
|
////////////////////////////// forget password //////////////////////////////////
|
||||||
final TextEditingController forgetEmailController = TextEditingController();
|
final TextEditingController forgetEmailController = TextEditingController();
|
||||||
final TextEditingController forgetPasswordController = TextEditingController();
|
final TextEditingController forgetPasswordController =
|
||||||
|
TextEditingController();
|
||||||
final TextEditingController forgetOtp = TextEditingController();
|
final TextEditingController forgetOtp = TextEditingController();
|
||||||
final forgetFormKey = GlobalKey<FormState>();
|
final forgetFormKey = GlobalKey<FormState>();
|
||||||
final forgetEmailKey = GlobalKey<FormState>();
|
final forgetEmailKey = GlobalKey<FormState>();
|
||||||
@ -48,7 +49,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_remainingTime = 1;
|
_remainingTime = 1;
|
||||||
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
add(UpdateTimerEvent(
|
||||||
|
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
try {
|
try {
|
||||||
forgetEmailValidate = '';
|
forgetEmailValidate = '';
|
||||||
_remainingTime = (await AuthenticationAPI.sendOtp(
|
_remainingTime = (await AuthenticationAPI.sendOtp(
|
||||||
@ -85,7 +87,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
add(const UpdateTimerEvent(remainingTime: 0, isButtonEnabled: true));
|
||||||
} else {
|
} else {
|
||||||
add(UpdateTimerEvent(remainingTime: _remainingTime, isButtonEnabled: false));
|
add(UpdateTimerEvent(
|
||||||
|
remainingTime: _remainingTime, isButtonEnabled: false));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -95,7 +98,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
emit(const TimerState(isButtonEnabled: true, remainingTime: 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> changePassword(ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
Future<void> changePassword(
|
||||||
|
ChangePasswordEvent event, Emitter<AuthState> emit) async {
|
||||||
emit(LoadingForgetState());
|
emit(LoadingForgetState());
|
||||||
try {
|
try {
|
||||||
var response = await AuthenticationAPI.verifyOtp(
|
var response = await AuthenticationAPI.verifyOtp(
|
||||||
@ -111,7 +115,8 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final errorData = e.response!.data;
|
final errorData = e.response!.data;
|
||||||
String errorMessage = errorData['error']['message'] ?? 'something went wrong';
|
String errorMessage =
|
||||||
|
errorData['error']['message'] ?? 'something went wrong';
|
||||||
validate = errorMessage;
|
validate = errorMessage;
|
||||||
emit(AuthInitialState());
|
emit(AuthInitialState());
|
||||||
}
|
}
|
||||||
@ -125,7 +130,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
void _onUpdateTimer(UpdateTimerEvent event, Emitter<AuthState> emit) {
|
||||||
emit(TimerState(isButtonEnabled: event.isButtonEnabled, remainingTime: event.remainingTime));
|
emit(TimerState(
|
||||||
|
isButtonEnabled: event.isButtonEnabled,
|
||||||
|
remainingTime: event.remainingTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////// login /////////////////////////////////////
|
///////////////////////////////////// login /////////////////////////////////////
|
||||||
@ -161,15 +168,23 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
password: event.password,
|
password: event.password,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (failure) {
|
} on DioException catch (e) {
|
||||||
validate = 'Invalid Credentials!';
|
final errorData = e.response!.data;
|
||||||
|
String errorMessage = errorData['error']['message'];
|
||||||
|
if (errorMessage == "Access denied for web platform") {
|
||||||
|
validate = errorMessage;
|
||||||
|
} else {
|
||||||
|
validate = 'Invalid Credentials!';
|
||||||
|
}
|
||||||
emit(LoginInitial());
|
emit(LoginInitial());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (token.accessTokenIsNotEmpty) {
|
if (token.accessTokenIsNotEmpty) {
|
||||||
FlutterSecureStorage storage = const FlutterSecureStorage();
|
FlutterSecureStorage storage = const FlutterSecureStorage();
|
||||||
await storage.write(key: Token.loginAccessTokenKey, value: token.accessToken);
|
await storage.write(
|
||||||
|
key: Token.loginAccessTokenKey, value: token.accessToken);
|
||||||
const FlutterSecureStorage().write(
|
const FlutterSecureStorage().write(
|
||||||
key: UserModel.userUuidKey,
|
key: UserModel.userUuidKey,
|
||||||
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
value: Token.decodeToken(token.accessToken)['uuid'].toString());
|
||||||
@ -327,12 +342,14 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
static Future<String> getTokenAndValidate() async {
|
static Future<String> getTokenAndValidate() async {
|
||||||
try {
|
try {
|
||||||
const storage = FlutterSecureStorage();
|
const storage = FlutterSecureStorage();
|
||||||
final firstLaunch =
|
final firstLaunch = await SharedPreferencesHelper.readBoolFromSP(
|
||||||
await SharedPreferencesHelper.readBoolFromSP(StringsManager.firstLaunch) ?? true;
|
StringsManager.firstLaunch) ??
|
||||||
|
true;
|
||||||
if (firstLaunch) {
|
if (firstLaunch) {
|
||||||
storage.deleteAll();
|
storage.deleteAll();
|
||||||
}
|
}
|
||||||
await SharedPreferencesHelper.saveBoolToSP(StringsManager.firstLaunch, false);
|
await SharedPreferencesHelper.saveBoolToSP(
|
||||||
|
StringsManager.firstLaunch, false);
|
||||||
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
final value = await storage.read(key: Token.loginAccessTokenKey) ?? '';
|
||||||
if (value.isEmpty) {
|
if (value.isEmpty) {
|
||||||
return 'Token not found';
|
return 'Token not found';
|
||||||
@ -385,7 +402,9 @@ class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
|||||||
final String formattedTime = [
|
final String formattedTime = [
|
||||||
if (days > 0) '${days}d', // Append 'd' for days
|
if (days > 0) '${days}d', // Append 'd' for days
|
||||||
if (days > 0 || hours > 0)
|
if (days > 0 || hours > 0)
|
||||||
hours.toString().padLeft(2, '0'), // Show hours if there are days or hours
|
hours
|
||||||
|
.toString()
|
||||||
|
.padLeft(2, '0'), // Show hours if there are days or hours
|
||||||
minutes.toString().padLeft(2, '0'),
|
minutes.toString().padLeft(2, '0'),
|
||||||
seconds.toString().padLeft(2, '0'),
|
seconds.toString().padLeft(2, '0'),
|
||||||
].join(':');
|
].join(':');
|
||||||
|
@ -21,7 +21,9 @@ class LoginWithEmailModel {
|
|||||||
return {
|
return {
|
||||||
'email': email,
|
'email': email,
|
||||||
'password': password,
|
'password': password,
|
||||||
|
"platform": "web"
|
||||||
// 'regionUuid': regionUuid,
|
// 'regionUuid': regionUuid,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//tst@tst.com
|
@ -62,6 +62,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
emit(LoadingHome());
|
emit(LoadingHome());
|
||||||
terms = await HomeApi().fetchTerms();
|
terms = await HomeApi().fetchTerms();
|
||||||
add(FetchPolicyEvent());
|
add(FetchPolicyEvent());
|
||||||
|
emit(PolicyAgreement());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -71,6 +72,7 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
|
|||||||
try {
|
try {
|
||||||
emit(LoadingHome());
|
emit(LoadingHome());
|
||||||
policy = await HomeApi().fetchPolicy();
|
policy = await HomeApi().fetchPolicy();
|
||||||
|
emit(PolicyAgreement());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
|||||||
final scrollPosition = _scrollController.position;
|
final scrollPosition = _scrollController.position;
|
||||||
if (scrollPosition.maxScrollExtent <= 0) {
|
if (scrollPosition.maxScrollExtent <= 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isAtEnd = true;
|
_isAtEnd = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,9 +63,11 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String get _dialogTitle =>
|
String get _dialogTitle =>
|
||||||
_currentPage == 2 ? 'User Agreement' : 'Privacy Policy';
|
_currentPage == 1 ? 'User Agreement' : 'Privacy Policy';
|
||||||
|
|
||||||
String get _dialogContent => _currentPage == 2 ? widget.terms : widget.policy;
|
String get _dialogContent => _currentPage == 1 ? widget.terms : widget.policy;
|
||||||
|
final String staticText =
|
||||||
|
'<h5 style="color: #FF5722;">If you cancel you will be logged out.</h5>';
|
||||||
|
|
||||||
Widget _buildScrollableContent() {
|
Widget _buildScrollableContent() {
|
||||||
return Container(
|
return Container(
|
||||||
@ -85,7 +87,7 @@ class _AgreementAndPrivacyDialogState extends State<AgreementAndPrivacyDialog> {
|
|||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(25),
|
padding: const EdgeInsets.all(25),
|
||||||
child: Html(
|
child: Html(
|
||||||
data: _dialogContent,
|
data: "$_dialogContent $staticText",
|
||||||
onLinkTap: (url, attributes, element) async {
|
onLinkTap: (url, attributes, element) async {
|
||||||
if (url != null) {
|
if (url != null) {
|
||||||
final uri = Uri.parse(url);
|
final uri = Uri.parse(url);
|
||||||
|
@ -24,7 +24,7 @@ class HomeWebPage extends StatelessWidget {
|
|||||||
listener: (BuildContext context, state) {
|
listener: (BuildContext context, state) {
|
||||||
if (state is HomeInitial) {
|
if (state is HomeInitial) {
|
||||||
if (homeBloc.user!.hasAcceptedWebAgreement == false) {
|
if (homeBloc.user!.hasAcceptedWebAgreement == false) {
|
||||||
Future.delayed(const Duration(seconds: 1), () {
|
Future.delayed(const Duration(seconds: 2), () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
|
@ -42,7 +42,9 @@ class RolesUserModel {
|
|||||||
invitedBy:
|
invitedBy:
|
||||||
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
json['invitedBy'].toString().toLowerCase().replaceAll("_", " "),
|
||||||
phoneNumber: json['phoneNumber'],
|
phoneNumber: json['phoneNumber'],
|
||||||
jobTitle: json['jobTitle'] ?? "-",
|
jobTitle: json['jobTitle'] == null || json['jobTitle'] == " "
|
||||||
|
? "_"
|
||||||
|
: json['jobTitle'],
|
||||||
createdDate: json['createdDate'],
|
createdDate: json['createdDate'],
|
||||||
createdTime: json['createdTime'],
|
createdTime: json['createdTime'],
|
||||||
);
|
);
|
||||||
|
@ -79,13 +79,14 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
|
|
||||||
List<TreeNode> updatedCommunities = [];
|
List<TreeNode> updatedCommunities = [];
|
||||||
List<TreeNode> spacesNodes = [];
|
List<TreeNode> spacesNodes = [];
|
||||||
|
List<String> communityIds = [];
|
||||||
_onLoadCommunityAndSpaces(
|
_onLoadCommunityAndSpaces(
|
||||||
LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async {
|
LoadCommunityAndSpacesEvent event, Emitter<UsersState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
List<CommunityModel> communities =
|
List<CommunityModel> communities =
|
||||||
await CommunitySpaceManagementApi().fetchCommunities();
|
await CommunitySpaceManagementApi().fetchCommunities();
|
||||||
|
communityIds = communities.map((community) => community.uuid).toList();
|
||||||
updatedCommunities = await Future.wait(
|
updatedCommunities = await Future.wait(
|
||||||
communities.map((community) async {
|
communities.map((community) async {
|
||||||
List<SpaceModel> spaces =
|
List<SpaceModel> spaces =
|
||||||
@ -102,7 +103,6 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
}).toList(),
|
}).toList(),
|
||||||
);
|
);
|
||||||
emit(const SpacesLoadedState());
|
emit(const SpacesLoadedState());
|
||||||
return updatedCommunities;
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState('Error loading communities and spaces: $e'));
|
emit(ErrorState('Error loading communities and spaces: $e'));
|
||||||
}
|
}
|
||||||
@ -177,7 +177,6 @@ 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));
|
|
||||||
emit(RolePermissionInitial());
|
emit(RolePermissionInitial());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState('Error loading communities and spaces: $e'));
|
emit(ErrorState('Error loading communities and spaces: $e'));
|
||||||
@ -208,10 +207,13 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
return anyMatch;
|
return anyMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
_sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
void _sendInvitUser(SendInviteUsers event, Emitter<UsersState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
List<String> selectedIds = getSelectedIds(updatedCommunities)
|
||||||
|
.where((id) => !communityIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
bool res = await UserPermissionApi().sendInviteUser(
|
bool res = await UserPermissionApi().sendInviteUser(
|
||||||
email: emailController.text,
|
email: emailController.text,
|
||||||
firstName: firstNameController.text,
|
firstName: firstNameController.text,
|
||||||
@ -219,9 +221,10 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
lastName: lastNameController.text,
|
lastName: lastNameController.text,
|
||||||
phoneNumber: phoneController.text,
|
phoneNumber: phoneController.text,
|
||||||
roleUuid: roleSelected,
|
roleUuid: roleSelected,
|
||||||
spaceUuids: selectedIds,
|
spaceUuids: selectedIds,
|
||||||
);
|
);
|
||||||
if (res == true) {
|
|
||||||
|
if (res) {
|
||||||
showCustomDialog(
|
showCustomDialog(
|
||||||
barrierDismissible: false,
|
barrierDismissible: false,
|
||||||
context: event.context,
|
context: event.context,
|
||||||
@ -248,10 +251,14 @@ class UsersBloc extends Bloc<UsersEvent, UsersState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
|
_editInviteUser(EditInviteUsers event, Emitter<UsersState> emit) async {
|
||||||
try {
|
try {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
List<String> selectedIds = getSelectedIds(updatedCommunities);
|
List<String> selectedIds = getSelectedIds(updatedCommunities)
|
||||||
|
.where((id) => !communityIds.contains(id))
|
||||||
|
.toList();
|
||||||
|
|
||||||
bool res = await UserPermissionApi().editInviteUser(
|
bool res = await UserPermissionApi().editInviteUser(
|
||||||
userId: event.userId,
|
userId: event.userId,
|
||||||
firstName: firstNameController.text,
|
firstName: firstNameController.text,
|
||||||
|
@ -218,7 +218,7 @@ class BasicsView extends StatelessWidget {
|
|||||||
if (_blocRole.checkEmailValid != "Valid email") {
|
if (_blocRole.checkEmailValid != "Valid email") {
|
||||||
return _blocRole.checkEmailValid;
|
return _blocRole.checkEmailValid;
|
||||||
}
|
}
|
||||||
return null;
|
// return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -81,7 +81,7 @@ Future<void> showPopUpFilterMenu({
|
|||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
const Text(
|
const Text(
|
||||||
"Filter by Status",
|
"Filter by ",
|
||||||
style: TextStyle(fontWeight: FontWeight.bold),
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
@ -40,9 +40,7 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
roleTypes.clear();
|
roleTypes.clear();
|
||||||
jobTitle.clear();
|
jobTitle.clear();
|
||||||
createdBy.clear();
|
createdBy.clear();
|
||||||
// deActivate.clear();
|
|
||||||
users = await UserPermissionApi().fetchUsers();
|
users = await UserPermissionApi().fetchUsers();
|
||||||
|
|
||||||
users.sort((a, b) {
|
users.sort((a, b) {
|
||||||
final dateA = _parseDateTime(a.createdDate);
|
final dateA = _parseDateTime(a.createdDate);
|
||||||
final dateB = _parseDateTime(b.createdDate);
|
final dateB = _parseDateTime(b.createdDate);
|
||||||
@ -57,15 +55,12 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
for (var user in users) {
|
for (var user in users) {
|
||||||
createdBy.add(user.invitedBy.toString());
|
createdBy.add(user.invitedBy.toString());
|
||||||
}
|
}
|
||||||
// for (var user in users) {
|
|
||||||
// deActivate.add(user.status.toString());
|
|
||||||
// }
|
|
||||||
initialUsers = List.from(users);
|
initialUsers = List.from(users);
|
||||||
roleTypes = roleTypes.toSet().toList();
|
roleTypes = roleTypes.toSet().toList();
|
||||||
jobTitle = jobTitle.toSet().toList();
|
jobTitle = jobTitle.toSet().toList();
|
||||||
createdBy = createdBy.toSet().toList();
|
createdBy = createdBy.toSet().toList();
|
||||||
// deActivate = deActivate.toSet().toList();
|
|
||||||
_handlePageChange(ChangePage(1), emit);
|
_handlePageChange(ChangePage(1), emit);
|
||||||
|
add(ChangePage(currentPage));
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(ErrorState(e.toString()));
|
emit(ErrorState(e.toString()));
|
||||||
@ -125,6 +120,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
void _toggleSortUsersByNameAsc(
|
void _toggleSortUsersByNameAsc(
|
||||||
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
SortUsersByNameAsc event, Emitter<UserTableState> emit) {
|
||||||
|
selectedRoles.clear();
|
||||||
|
selectedJobTitles.clear();
|
||||||
|
selectedCreatedBy.clear();
|
||||||
|
selectedStatuses.clear();
|
||||||
if (currentSortOrder == "Asc") {
|
if (currentSortOrder == "Asc") {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
@ -143,13 +142,16 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
void _toggleSortUsersByNameDesc(
|
void _toggleSortUsersByNameDesc(
|
||||||
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
SortUsersByNameDesc event, Emitter<UserTableState> emit) {
|
||||||
|
selectedRoles.clear();
|
||||||
|
selectedJobTitles.clear();
|
||||||
|
selectedCreatedBy.clear();
|
||||||
|
selectedStatuses.clear();
|
||||||
if (currentSortOrder == "Desc") {
|
if (currentSortOrder == "Desc") {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
users = List.from(initialUsers); // Reset to saved initial state
|
users = List.from(initialUsers);
|
||||||
emit(UsersLoadedState(users: users));
|
emit(UsersLoadedState(users: users));
|
||||||
} else {
|
} else {
|
||||||
// Sort descending
|
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "Desc";
|
currentSortOrder = "Desc";
|
||||||
users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
users.sort((a, b) => b.firstName!.compareTo(a.firstName!));
|
||||||
@ -159,6 +161,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
void _toggleSortUsersByDateNewestToOldest(
|
void _toggleSortUsersByDateNewestToOldest(
|
||||||
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
DateNewestToOldestEvent event, Emitter<UserTableState> emit) {
|
||||||
|
selectedRoles.clear();
|
||||||
|
selectedJobTitles.clear();
|
||||||
|
selectedCreatedBy.clear();
|
||||||
|
selectedStatuses.clear();
|
||||||
if (currentSortOrderDate == "NewestToOldest") {
|
if (currentSortOrderDate == "NewestToOldest") {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
@ -179,6 +185,10 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
void _toggleSortUsersByDateOldestToNewest(
|
void _toggleSortUsersByDateOldestToNewest(
|
||||||
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
DateOldestToNewestEvent event, Emitter<UserTableState> emit) {
|
||||||
|
selectedRoles.clear();
|
||||||
|
selectedJobTitles.clear();
|
||||||
|
selectedCreatedBy.clear();
|
||||||
|
selectedStatuses.clear();
|
||||||
if (currentSortOrderDate == "OldestToNewest") {
|
if (currentSortOrderDate == "OldestToNewest") {
|
||||||
emit(UsersLoadingState());
|
emit(UsersLoadingState());
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
@ -337,7 +347,20 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
|
|
||||||
final filteredUsers = initialUsers.where((user) {
|
final filteredUsers = initialUsers.where((user) {
|
||||||
if (selectedStatuses.isEmpty) return true;
|
if (selectedStatuses.isEmpty) return true;
|
||||||
return selectedStatuses.contains(user.status);
|
|
||||||
|
return selectedStatuses.any((status) {
|
||||||
|
final userStatus = user.status?.toLowerCase() ?? '';
|
||||||
|
switch (status.toLowerCase()) {
|
||||||
|
case 'active':
|
||||||
|
return user.isEnabled == true && userStatus != 'invited';
|
||||||
|
case 'disabled':
|
||||||
|
return user.isEnabled == false;
|
||||||
|
case 'invited':
|
||||||
|
return userStatus == 'invited';
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}).toList();
|
}).toList();
|
||||||
if (event.sortOrder == "Asc") {
|
if (event.sortOrder == "Asc") {
|
||||||
currentSortOrder = "Asc";
|
currentSortOrder = "Asc";
|
||||||
@ -351,9 +374,11 @@ class UserTableBloc extends Bloc<UserTableEvent, UserTableState> {
|
|||||||
} else {
|
} else {
|
||||||
currentSortOrder = "";
|
currentSortOrder = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
emit(UsersLoadedState(users: filteredUsers));
|
emit(UsersLoadedState(users: filteredUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void _resetAllFilters(Emitter<UserTableState> emit) {
|
void _resetAllFilters(Emitter<UserTableState> emit) {
|
||||||
selectedRoles.clear();
|
selectedRoles.clear();
|
||||||
selectedJobTitles.clear();
|
selectedJobTitles.clear();
|
||||||
|
@ -12,7 +12,7 @@ Future<void> showDateFilterMenu({
|
|||||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||||
final RelativeRect position = RelativeRect.fromRect(
|
final RelativeRect position = RelativeRect.fromRect(
|
||||||
Rect.fromLTRB(
|
Rect.fromLTRB(
|
||||||
overlay.size.width / 2,
|
overlay.size.width / 3,
|
||||||
240,
|
240,
|
||||||
0,
|
0,
|
||||||
overlay.size.height,
|
overlay.size.height,
|
||||||
@ -40,7 +40,6 @@ Future<void> showDateFilterMenu({
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"Sort from newest to oldest",
|
"Sort from newest to oldest",
|
||||||
// style: context.textTheme.bodyMedium,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isSelected == "NewestToOldest"
|
color: isSelected == "NewestToOldest"
|
||||||
? Colors.black
|
? Colors.black
|
||||||
@ -65,9 +64,5 @@ Future<void> showDateFilterMenu({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).then((value) {
|
).then((value) {});
|
||||||
// setState(() {
|
|
||||||
// _isDropdownOpen = false;
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,6 @@ Future<void> showDeActivateFilterMenu({
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"Sort A to Z",
|
"Sort A to Z",
|
||||||
// style: context.textTheme.bodyMedium,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isSelected == "NewestToOldest"
|
color: isSelected == "NewestToOldest"
|
||||||
? Colors.black
|
? Colors.black
|
||||||
@ -65,9 +64,5 @@ Future<void> showDeActivateFilterMenu({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).then((value) {
|
).then((value) {});
|
||||||
// setState(() {
|
|
||||||
// _isDropdownOpen = false;
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ Future<void> showNameMenu({
|
|||||||
Overlay.of(context).context.findRenderObject() as RenderBox;
|
Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||||
final RelativeRect position = RelativeRect.fromRect(
|
final RelativeRect position = RelativeRect.fromRect(
|
||||||
Rect.fromLTRB(
|
Rect.fromLTRB(
|
||||||
overlay.size.width / 25,
|
overlay.size.width / 35,
|
||||||
240,
|
240,
|
||||||
0,
|
0,
|
||||||
overlay.size.height,
|
overlay.size.height,
|
||||||
@ -40,7 +40,6 @@ Future<void> showNameMenu({
|
|||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
"Sort A to Z",
|
"Sort A to Z",
|
||||||
// style: context.textTheme.bodyMedium,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isSelected == "Asc" ? Colors.black : Colors.blueGrey),
|
color: isSelected == "Asc" ? Colors.black : Colors.blueGrey),
|
||||||
),
|
),
|
||||||
@ -61,9 +60,5 @@ Future<void> showNameMenu({
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
).then((value) {
|
).then((value) {});
|
||||||
// setState(() {
|
|
||||||
// _isDropdownOpen = false;
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -1,256 +1,59 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_svg/svg.dart';
|
import 'package:flutter_svg/flutter_svg.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/style.dart';
|
import 'package:syncrow_web/utils/style.dart';
|
||||||
|
|
||||||
class DynamicTableScreen extends StatefulWidget {
|
class _HeaderColumn extends StatelessWidget {
|
||||||
final List<String> titles;
|
final String title;
|
||||||
final List<List<Widget>> rows;
|
final double width;
|
||||||
final void Function(int columnIndex)? onFilter;
|
final bool showFilter;
|
||||||
|
final VoidCallback? onFilter;
|
||||||
|
final Function(double) onResize;
|
||||||
|
|
||||||
DynamicTableScreen(
|
const _HeaderColumn({
|
||||||
{required this.titles, required this.rows, required this.onFilter});
|
required this.title,
|
||||||
|
required this.width,
|
||||||
@override
|
required this.showFilter,
|
||||||
_DynamicTableScreenState createState() => _DynamicTableScreenState();
|
required this.onResize,
|
||||||
}
|
this.onFilter,
|
||||||
|
Key? key,
|
||||||
class _DynamicTableScreenState extends State<DynamicTableScreen>
|
}) : super(key: key);
|
||||||
with WidgetsBindingObserver {
|
|
||||||
late List<double> columnWidths;
|
|
||||||
late double totalWidth;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
columnWidths = List<double>.filled(widget.titles.length, 150.0);
|
|
||||||
totalWidth = columnWidths.reduce((a, b) => a + b);
|
|
||||||
WidgetsBinding.instance.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didChangeMetrics() {
|
|
||||||
super.didChangeMetrics();
|
|
||||||
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.1; // 25% of screen width for the tenth column
|
|
||||||
}
|
|
||||||
return newScreenWidth *
|
|
||||||
0.09; // Default to 10% of screen width for other columns
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
return MouseRegion(
|
||||||
if (columnWidths.every((width) => width == screenWidth * 7)) {
|
cursor: SystemMouseCursors.resizeColumn,
|
||||||
columnWidths = List<double>.generate(widget.titles.length, (index) {
|
child: GestureDetector(
|
||||||
if (index == 1) {
|
onHorizontalDragUpdate: (details) => onResize(details.delta.dx),
|
||||||
return screenWidth * 0.11;
|
child: Container(
|
||||||
} else if (index == 9) {
|
width: width,
|
||||||
return screenWidth * 0.1;
|
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
}
|
decoration: const BoxDecoration(
|
||||||
return screenWidth * 0.09;
|
border: Border(right: BorderSide(color: ColorsManager.boxDivider)),
|
||||||
});
|
),
|
||||||
setState(() {});
|
child: Row(
|
||||||
}
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
return SingleChildScrollView(
|
|
||||||
clipBehavior: Clip.none,
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
child: Container(
|
|
||||||
decoration: containerDecoration.copyWith(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(20))),
|
|
||||||
child: FittedBox(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Expanded(
|
||||||
width: totalWidth,
|
child: Text(
|
||||||
decoration: containerDecoration.copyWith(
|
title,
|
||||||
color: ColorsManager.circleRolesBackground,
|
maxLines: 2,
|
||||||
borderRadius: const BorderRadius.only(
|
overflow: TextOverflow.ellipsis,
|
||||||
topLeft: Radius.circular(15),
|
style: const TextStyle(
|
||||||
topRight: Radius.circular(15))),
|
fontWeight: FontWeight.w400,
|
||||||
child: Row(
|
fontSize: 13,
|
||||||
children: List.generate(widget.titles.length, (index) {
|
color: ColorsManager.grayColor,
|
||||||
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 != 8 &&
|
|
||||||
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);
|
|
||||||
totalWidth = columnWidths.reduce((a, b) => a + b);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: MouseRegion(
|
|
||||||
cursor: SystemMouseCursors.resizeColumn,
|
|
||||||
child: Container(
|
|
||||||
color: Colors.green,
|
|
||||||
child: Container(
|
|
||||||
color: ColorsManager.boxDivider,
|
|
||||||
width: 1,
|
|
||||||
height: 50,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
widget.rows.isEmpty
|
if (showFilter)
|
||||||
? SizedBox(
|
IconButton(
|
||||||
height: MediaQuery.of(context).size.height / 2,
|
icon: SvgPicture.asset(Assets.filterTableIcon),
|
||||||
child: Column(
|
onPressed: onFilter,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
padding: EdgeInsets.zero,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
constraints: const BoxConstraints(),
|
||||||
children: [
|
),
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
SvgPicture.asset(Assets.emptyTable),
|
|
||||||
const SizedBox(
|
|
||||||
height: 15,
|
|
||||||
),
|
|
||||||
const Text(
|
|
||||||
'No Users',
|
|
||||||
style: TextStyle(
|
|
||||||
color: ColorsManager.lightGrayColor,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w700),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Center(
|
|
||||||
child: Container(
|
|
||||||
width: totalWidth,
|
|
||||||
decoration: containerDecoration.copyWith(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
bottomLeft: Radius.circular(15),
|
|
||||||
bottomRight: Radius.circular(15))),
|
|
||||||
child: ListView.builder(
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: widget.rows.length,
|
|
||||||
itemBuilder: (context, rowIndex) {
|
|
||||||
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(() {});
|
|
||||||
}
|
|
||||||
final row = widget.rows[rowIndex];
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -258,3 +61,204 @@ class _DynamicTableScreenState extends State<DynamicTableScreen>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _TableRow extends StatelessWidget {
|
||||||
|
final List<Widget> cells;
|
||||||
|
final List<double> columnWidths;
|
||||||
|
final bool isLast;
|
||||||
|
|
||||||
|
const _TableRow({
|
||||||
|
required this.cells,
|
||||||
|
required this.columnWidths,
|
||||||
|
required this.isLast,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
for (int i = 0; i < cells.length; i++)
|
||||||
|
Container(
|
||||||
|
width: columnWidths[i],
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
// decoration: BoxDecoration(
|
||||||
|
// border: Border(
|
||||||
|
// right: BorderSide(color: ColorsManager.boxDivider),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
child: cells[i],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (!isLast)
|
||||||
|
Divider(
|
||||||
|
height: 1,
|
||||||
|
thickness: 1,
|
||||||
|
color: ColorsManager.boxDivider,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
class DynamicTableScreen extends StatefulWidget {
|
||||||
|
final List<String> titles;
|
||||||
|
final List<List<Widget>> rows;
|
||||||
|
final void Function(int columnIndex)? onFilter;
|
||||||
|
|
||||||
|
const DynamicTableScreen({
|
||||||
|
required this.titles,
|
||||||
|
required this.rows,
|
||||||
|
required this.onFilter,
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DynamicTableScreenState createState() => _DynamicTableScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DynamicTableScreenState extends State<DynamicTableScreen> {
|
||||||
|
late List<double> columnWidths;
|
||||||
|
final double _minColumnWidth = 100.0;
|
||||||
|
final double _maxColumnWidth = 300.0;
|
||||||
|
final double _dividerWidth = 1.0;
|
||||||
|
double _lastAvailableWidth = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
columnWidths = List.filled(widget.titles.length, _minColumnWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _handleColumnResize(int index, double delta) {
|
||||||
|
setState(() {
|
||||||
|
double newWidth = columnWidths[index] + delta;
|
||||||
|
newWidth = newWidth.clamp(_minColumnWidth, _maxColumnWidth);
|
||||||
|
double actualDelta = newWidth - columnWidths[index];
|
||||||
|
if (actualDelta == 0) return;
|
||||||
|
|
||||||
|
int nextIndex = (index + 1) % columnWidths.length;
|
||||||
|
columnWidths[index] = newWidth;
|
||||||
|
columnWidths[nextIndex] = (columnWidths[nextIndex] - actualDelta)
|
||||||
|
.clamp(_minColumnWidth, _maxColumnWidth);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildHeader() {
|
||||||
|
return Container(
|
||||||
|
decoration: containerDecoration.copyWith(
|
||||||
|
color: ColorsManager.circleRolesBackground,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(15),
|
||||||
|
topRight: Radius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
for (int i = 0; i < widget.titles.length; i++)
|
||||||
|
_HeaderColumn(
|
||||||
|
title: widget.titles[i],
|
||||||
|
width: columnWidths[i],
|
||||||
|
showFilter: i != 1 && i != 9 && i != 8 && i != 5,
|
||||||
|
onFilter: () => widget.onFilter?.call(i),
|
||||||
|
onResize: (delta) => _handleColumnResize(i, delta),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildBody() {
|
||||||
|
if (widget.rows.isEmpty) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 300,
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(Assets.emptyTable),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
const Text(
|
||||||
|
'No Users',
|
||||||
|
style: TextStyle(
|
||||||
|
color: ColorsManager.lightGrayColor,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: containerDecoration.copyWith(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(15),
|
||||||
|
bottomRight: Radius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
for (int rowIndex = 0; rowIndex < widget.rows.length; rowIndex++)
|
||||||
|
_TableRow(
|
||||||
|
cells: widget.rows[rowIndex],
|
||||||
|
columnWidths: columnWidths,
|
||||||
|
isLast: rowIndex == widget.rows.length - 1,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return LayoutBuilder(
|
||||||
|
builder: (context, constraints) {
|
||||||
|
final availableWidth = constraints.maxWidth;
|
||||||
|
final totalDividersWidth = (widget.titles.length - 1) * _dividerWidth;
|
||||||
|
|
||||||
|
if (_lastAvailableWidth != availableWidth) {
|
||||||
|
final equalWidth =
|
||||||
|
(availableWidth - totalDividersWidth) / widget.titles.length;
|
||||||
|
final clampedWidth =
|
||||||
|
equalWidth.clamp(_minColumnWidth, _maxColumnWidth);
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
setState(() {
|
||||||
|
columnWidths = List.filled(widget.titles.length, clampedWidth);
|
||||||
|
_lastAvailableWidth = availableWidth;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
final totalTableWidth =
|
||||||
|
columnWidths.fold(0.0, (sum, w) => sum + w) + totalDividersWidth;
|
||||||
|
return SingleChildScrollView(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
child: Container(
|
||||||
|
width: totalTableWidth,
|
||||||
|
decoration: containerDecoration.copyWith(
|
||||||
|
color: ColorsManager.whiteColors,
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildHeader(),
|
||||||
|
_buildBody(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -108,7 +108,6 @@ class UsersPage extends StatelessWidget {
|
|||||||
final screenSize = MediaQuery.of(context).size;
|
final screenSize = MediaQuery.of(context).size;
|
||||||
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
final _blocRole = BlocProvider.of<UserTableBloc>(context);
|
||||||
if (state is UsersLoadingState) {
|
if (state is UsersLoadingState) {
|
||||||
_blocRole.add(ChangePage(_blocRole.currentPage));
|
|
||||||
return const Center(child: CircularProgressIndicator());
|
return const Center(child: CircularProgressIndicator());
|
||||||
} else if (state is UsersLoadedState) {
|
} else if (state is UsersLoadedState) {
|
||||||
return Padding(
|
return Padding(
|
||||||
@ -215,7 +214,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
|
|
||||||
showPopUpFilterMenu(
|
showPopUpFilterMenu(
|
||||||
position: RelativeRect.fromLTRB(
|
position: RelativeRect.fromLTRB(
|
||||||
overlay.size.width / 4,
|
overlay.size.width / 5.3,
|
||||||
240,
|
240,
|
||||||
overlay.size.width / 4,
|
overlay.size.width / 4,
|
||||||
0,
|
0,
|
||||||
@ -225,6 +224,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
isSelected: _blocRole.currentSortOrder,
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
searchController.clear();
|
||||||
_blocRole.add(FilterClearEvent());
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
@ -265,6 +265,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
isSelected: _blocRole.currentSortOrder,
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
searchController.clear();
|
||||||
_blocRole.add(FilterClearEvent());
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
@ -320,6 +321,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
isSelected: _blocRole.currentSortOrder,
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
searchController.clear();
|
||||||
_blocRole.add(FilterClearEvent());
|
_blocRole.add(FilterClearEvent());
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
@ -343,6 +345,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
for (var item in _blocRole.status)
|
for (var item in _blocRole.status)
|
||||||
item: _blocRole.selectedStatuses.contains(item),
|
item: _blocRole.selectedStatuses.contains(item),
|
||||||
};
|
};
|
||||||
|
|
||||||
final RenderBox overlay = Overlay.of(context)
|
final RenderBox overlay = Overlay.of(context)
|
||||||
.context
|
.context
|
||||||
.findRenderObject() as RenderBox;
|
.findRenderObject() as RenderBox;
|
||||||
@ -350,7 +353,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
position: RelativeRect.fromLTRB(
|
position: RelativeRect.fromLTRB(
|
||||||
overlay.size.width / 0,
|
overlay.size.width / 0,
|
||||||
240,
|
240,
|
||||||
overlay.size.width / 4,
|
overlay.size.width / 5,
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
list: _blocRole.status,
|
list: _blocRole.status,
|
||||||
@ -358,8 +361,8 @@ class UsersPage extends StatelessWidget {
|
|||||||
checkboxStates: checkboxStates,
|
checkboxStates: checkboxStates,
|
||||||
isSelected: _blocRole.currentSortOrder,
|
isSelected: _blocRole.currentSortOrder,
|
||||||
onOkPressed: () {
|
onOkPressed: () {
|
||||||
|
searchController.clear();
|
||||||
_blocRole.add(FilterClearEvent());
|
_blocRole.add(FilterClearEvent());
|
||||||
|
|
||||||
final selectedItems = checkboxStates.entries
|
final selectedItems = checkboxStates.entries
|
||||||
.where((entry) => entry.value)
|
.where((entry) => entry.value)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
@ -410,7 +413,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
return [
|
return [
|
||||||
Text('${user.firstName} ${user.lastName}'),
|
Text('${user.firstName} ${user.lastName}'),
|
||||||
Text(user.email),
|
Text(user.email),
|
||||||
Text(user.jobTitle ?? '-'),
|
Text(user.jobTitle),
|
||||||
Text(user.roleType ?? ''),
|
Text(user.roleType ?? ''),
|
||||||
Text(user.createdDate ?? ''),
|
Text(user.createdDate ?? ''),
|
||||||
Text(user.createdTime ?? ''),
|
Text(user.createdTime ?? ''),
|
||||||
@ -427,11 +430,6 @@ class UsersPage extends StatelessWidget {
|
|||||||
userId: user.uuid,
|
userId: user.uuid,
|
||||||
onTap: user.status != "invited"
|
onTap: user.status != "invited"
|
||||||
? () {
|
? () {
|
||||||
// final newStatus = user.status == 'active'
|
|
||||||
// ? 'disabled'
|
|
||||||
// : user.status == 'disabled'
|
|
||||||
// ? 'invited'
|
|
||||||
// : 'active';
|
|
||||||
context.read<UserTableBloc>().add(
|
context.read<UserTableBloc>().add(
|
||||||
ChangeUserStatus(
|
ChangeUserStatus(
|
||||||
userId: user.uuid,
|
userId: user.uuid,
|
||||||
@ -443,10 +441,6 @@ class UsersPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
// actionButton(
|
|
||||||
// title: "Activity Log",
|
|
||||||
// onTap: () {},
|
|
||||||
// ),
|
|
||||||
actionButton(
|
actionButton(
|
||||||
title: "Edit",
|
title: "Edit",
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -487,9 +481,7 @@ class UsersPage extends StatelessWidget {
|
|||||||
},
|
},
|
||||||
).then((v) {
|
).then((v) {
|
||||||
if (v != null) {
|
if (v != null) {
|
||||||
if (v != null) {
|
_blocRole.add(const GetUsers());
|
||||||
_blocRole.add(const GetUsers());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -18,8 +18,7 @@ class UserPermissionApi {
|
|||||||
showServerMessage: true,
|
showServerMessage: true,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
debugPrint('fetchUsers Response: $json');
|
debugPrint('fetchUsers Response: $json');
|
||||||
final List<dynamic> data =
|
final List<dynamic> data = json['data'] ?? [];
|
||||||
json['data'] ?? []; // Default to an empty list if no data
|
|
||||||
return data.map((item) => RolesUserModel.fromJson(item)).toList();
|
return data.map((item) => RolesUserModel.fromJson(item)).toList();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -119,7 +118,7 @@ class UserPermissionApi {
|
|||||||
);
|
);
|
||||||
return response ?? 'Unknown error occurred';
|
return response ?? 'Unknown error occurred';
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
final errorMessage = e.response?.data['error'];
|
final errorMessage = e.response?.data['error']['message'];
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return e.toString();
|
return e.toString();
|
||||||
@ -205,7 +204,6 @@ class UserPermissionApi {
|
|||||||
.replaceAll("{invitedUserUuid}", userUuid),
|
.replaceAll("{invitedUserUuid}", userUuid),
|
||||||
body: bodya,
|
body: bodya,
|
||||||
expectedResponseModel: (json) {
|
expectedResponseModel: (json) {
|
||||||
print('changeUserStatusById==${json['success']}');
|
|
||||||
return json['success'];
|
return json['success'];
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -213,7 +211,6 @@ class UserPermissionApi {
|
|||||||
return response;
|
return response;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
print(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
96
pubspec.lock
96
pubspec.lock
@ -65,6 +65,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.6"
|
version: "3.0.6"
|
||||||
|
csslib:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: csslib
|
||||||
|
sha256: "831883fb353c8bdc1d71979e5b342c7d88acfbc643113c14ae51e2442ea0f20f"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.17.3"
|
||||||
cupertino_icons:
|
cupertino_icons:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -182,6 +190,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.1.0"
|
version: "5.1.0"
|
||||||
|
flutter_html:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_html
|
||||||
|
sha256: "02ad69e813ecfc0728a455e4bf892b9379983e050722b1dce00192ee2e41d1ee"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0-beta.2"
|
||||||
flutter_lints:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -280,6 +296,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
|
html:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: html
|
||||||
|
sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.15.4"
|
||||||
http:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -352,6 +376,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.0"
|
version: "3.0.0"
|
||||||
|
list_counter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: list_counter
|
||||||
|
sha256: c447ae3dfcd1c55f0152867090e67e219d42fe6d4f2807db4bbe8b8d69912237
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.2"
|
||||||
logging:
|
logging:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -629,6 +661,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.2"
|
version: "1.3.2"
|
||||||
|
url_launcher:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: url_launcher
|
||||||
|
sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.1"
|
||||||
|
url_launcher_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_android
|
||||||
|
sha256: f0c73347dfcfa5b3db8bc06e1502668265d39c08f310c29bff4e28eea9699f79
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.9"
|
||||||
|
url_launcher_ios:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_ios
|
||||||
|
sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.3.2"
|
||||||
|
url_launcher_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_linux
|
||||||
|
sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.1"
|
||||||
|
url_launcher_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_macos
|
||||||
|
sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
|
url_launcher_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_platform_interface
|
||||||
|
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.2"
|
||||||
|
url_launcher_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_web
|
||||||
|
sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.3"
|
||||||
|
url_launcher_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: url_launcher_windows
|
||||||
|
sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.4"
|
||||||
uuid:
|
uuid:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
Reference in New Issue
Block a user