Skip to content

Commit c5656f7

Browse files
authored
Merge pull request #140 from flutter-news-app-full-source-code/refactor/ui-enhacement
Refactor/UI enhacement
2 parents 7d8fb37 + 6efae19 commit c5656f7

22 files changed

+652
-212
lines changed

lib/l10n/app_localizations.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3997,6 +3997,48 @@ abstract class AppLocalizations {
39973997
/// In en, this message translates to:
39983998
/// **'Community'**
39993999
String get navCommunity;
4000+
4001+
/// Title for the dialog confirming user promotion.
4002+
///
4003+
/// In en, this message translates to:
4004+
/// **'Confirm Promotion'**
4005+
String get confirmPromotionTitle;
4006+
4007+
/// Message for the dialog confirming user promotion.
4008+
///
4009+
/// In en, this message translates to:
4010+
/// **'Are you sure you want to promote {email} to a Publisher?'**
4011+
String confirmPromotionMessage(String email);
4012+
4013+
/// Title for the dialog confirming user demotion.
4014+
///
4015+
/// In en, this message translates to:
4016+
/// **'Confirm Demotion'**
4017+
String get confirmDemotionTitle;
4018+
4019+
/// Message for the dialog confirming user demotion.
4020+
///
4021+
/// In en, this message translates to:
4022+
/// **'Are you sure you want to demote {email} to a standard user?'**
4023+
String confirmDemotionMessage(String email);
4024+
4025+
/// Tooltip for the icon indicating a premium user.
4026+
///
4027+
/// In en, this message translates to:
4028+
/// **'Premium'**
4029+
String get premiumUserTooltip;
4030+
4031+
/// Tooltip for the icon indicating an admin user.
4032+
///
4033+
/// In en, this message translates to:
4034+
/// **'Admin'**
4035+
String get adminUserTooltip;
4036+
4037+
/// Tooltip for the icon indicating a publisher user.
4038+
///
4039+
/// In en, this message translates to:
4040+
/// **'Publisher'**
4041+
String get publisherUserTooltip;
40004042
}
40014043

40024044
class _AppLocalizationsDelegate

lib/l10n/app_localizations_ar.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,4 +2142,29 @@ class AppLocalizationsAr extends AppLocalizations {
21422142

21432143
@override
21442144
String get navCommunity => 'المجتمع';
2145+
2146+
@override
2147+
String get confirmPromotionTitle => 'تأكيد الترقية';
2148+
2149+
@override
2150+
String confirmPromotionMessage(String email) {
2151+
return 'هل أنت متأكد أنك تريد ترقية $email إلى ناشر؟';
2152+
}
2153+
2154+
@override
2155+
String get confirmDemotionTitle => 'تأكيد التخفيض';
2156+
2157+
@override
2158+
String confirmDemotionMessage(String email) {
2159+
return 'هل أنت متأكد أنك تريد تخفيض رتبة $email إلى مستخدم عادي؟';
2160+
}
2161+
2162+
@override
2163+
String get premiumUserTooltip => 'مستخدم مميز';
2164+
2165+
@override
2166+
String get adminUserTooltip => 'مسؤول';
2167+
2168+
@override
2169+
String get publisherUserTooltip => 'ناشر';
21452170
}

lib/l10n/app_localizations_en.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2148,4 +2148,29 @@ class AppLocalizationsEn extends AppLocalizations {
21482148

21492149
@override
21502150
String get navCommunity => 'Community';
2151+
2152+
@override
2153+
String get confirmPromotionTitle => 'Confirm Promotion';
2154+
2155+
@override
2156+
String confirmPromotionMessage(String email) {
2157+
return 'Are you sure you want to promote $email to a Publisher?';
2158+
}
2159+
2160+
@override
2161+
String get confirmDemotionTitle => 'Confirm Demotion';
2162+
2163+
@override
2164+
String confirmDemotionMessage(String email) {
2165+
return 'Are you sure you want to demote $email to a standard user?';
2166+
}
2167+
2168+
@override
2169+
String get premiumUserTooltip => 'Premium';
2170+
2171+
@override
2172+
String get adminUserTooltip => 'Admin';
2173+
2174+
@override
2175+
String get publisherUserTooltip => 'Publisher';
21512176
}

lib/l10n/arb/app_ar.arb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2698,5 +2698,43 @@
26982698
"navCommunity": "المجتمع",
26992699
"@navCommunity": {
27002700
"description": "تسمية تنقل قصيرة لإدارة المجتمع."
2701+
},
2702+
"confirmPromotionTitle": "تأكيد الترقية",
2703+
"@confirmPromotionTitle": {
2704+
"description": "عنوان مربع حوار تأكيد ترقية المستخدم."
2705+
},
2706+
"confirmPromotionMessage": "هل أنت متأكد أنك تريد ترقية {email} إلى ناشر؟",
2707+
"@confirmPromotionMessage": {
2708+
"description": "رسالة مربع حوار تأكيد ترقية المستخدم.",
2709+
"placeholders": {
2710+
"email": {
2711+
"type": "String"
2712+
}
2713+
}
2714+
},
2715+
"confirmDemotionTitle": "تأكيد التخفيض",
2716+
"@confirmDemotionTitle": {
2717+
"description": "عنوان مربع حوار تأكيد تخفيض رتبة المستخدم."
2718+
},
2719+
"confirmDemotionMessage": "هل أنت متأكد أنك تريد تخفيض رتبة {email} إلى مستخدم عادي؟",
2720+
"@confirmDemotionMessage": {
2721+
"description": "رسالة مربع حوار تأكيد تخفيض رتبة المستخدم.",
2722+
"placeholders": {
2723+
"email": {
2724+
"type": "String"
2725+
}
2726+
}
2727+
},
2728+
"premiumUserTooltip": "مستخدم مميز",
2729+
"@premiumUserTooltip": {
2730+
"description": "تلميح للأيقونة التي تشير إلى مستخدم مميز."
2731+
},
2732+
"adminUserTooltip": "مسؤول",
2733+
"@adminUserTooltip": {
2734+
"description": "تلميح للأيقونة التي تشير إلى مستخدم مسؤول."
2735+
},
2736+
"publisherUserTooltip": "ناشر",
2737+
"@publisherUserTooltip": {
2738+
"description": "تلميح للأيقونة التي تشير إلى مستخدم ناشر."
27012739
}
27022740
}

lib/l10n/arb/app_en.arb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2694,5 +2694,43 @@
26942694
"navCommunity": "Community",
26952695
"@navCommunity": {
26962696
"description": "Short navigation label for Community Management."
2697+
},
2698+
"confirmPromotionTitle": "Confirm Promotion",
2699+
"@confirmPromotionTitle": {
2700+
"description": "Title for the dialog confirming user promotion."
2701+
},
2702+
"confirmPromotionMessage": "Are you sure you want to promote {email} to a Publisher?",
2703+
"@confirmPromotionMessage": {
2704+
"description": "Message for the dialog confirming user promotion.",
2705+
"placeholders": {
2706+
"email": {
2707+
"type": "String"
2708+
}
2709+
}
2710+
},
2711+
"confirmDemotionTitle": "Confirm Demotion",
2712+
"@confirmDemotionTitle": {
2713+
"description": "Title for the dialog confirming user demotion."
2714+
},
2715+
"confirmDemotionMessage": "Are you sure you want to demote {email} to a standard user?",
2716+
"@confirmDemotionMessage": {
2717+
"description": "Message for the dialog confirming user demotion.",
2718+
"placeholders": {
2719+
"email": {
2720+
"type": "String"
2721+
}
2722+
}
2723+
},
2724+
"premiumUserTooltip": "Premium",
2725+
"@premiumUserTooltip": {
2726+
"description": "Tooltip for the icon indicating a premium user."
2727+
},
2728+
"adminUserTooltip": "Admin",
2729+
"@adminUserTooltip": {
2730+
"description": "Tooltip for the icon indicating an admin user."
2731+
},
2732+
"publisherUserTooltip": "Publisher",
2733+
"@publisherUserTooltip": {
2734+
"description": "Tooltip for the icon indicating a publisher user."
26972735
}
26982736
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/app_localizations.dart';
4+
5+
/// An extension on [AppUserRole] to provide UI-related helpers.
6+
extension AppUserRoleUI on AppUserRole {
7+
/// A convenience getter to check if the user role is premium.
8+
bool get isPremium => this == AppUserRole.premiumUser;
9+
10+
/// Returns a premium indicator icon wrapped in a tooltip if the user is a
11+
/// premium user.
12+
///
13+
/// Returns a gold star icon for premium users, otherwise returns null.
14+
Widget? getPremiumIcon(AppLocalizations l10n) {
15+
if (isPremium) {
16+
return Tooltip(
17+
message: l10n.premiumUserTooltip,
18+
child: const Icon(Icons.star, color: Colors.amber, size: 16),
19+
);
20+
}
21+
return null;
22+
}
23+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_news_app_web_dashboard_full_source_code/l10n/l10n.dart';
3+
4+
/// {@template confirmation_dialog}
5+
/// A reusable dialog to confirm a user action.
6+
///
7+
/// This dialog displays a title, content, and two buttons: a cancel button
8+
/// and a confirm button. The text for these buttons and the action to be
9+
/// performed on confirmation are customizable.
10+
/// {@endtemplate}
11+
class ConfirmationDialog extends StatelessWidget {
12+
/// {@macro confirmation_dialog}
13+
const ConfirmationDialog({
14+
required this.title,
15+
required this.content,
16+
required this.onConfirm,
17+
this.confirmText,
18+
super.key,
19+
});
20+
21+
/// The title of the dialog.
22+
final String title;
23+
24+
/// The main content or question of the dialog.
25+
final String content;
26+
27+
/// The callback to be executed when the user confirms the action.
28+
final VoidCallback onConfirm;
29+
30+
/// The text for the confirmation button. Defaults to 'Confirm'.
31+
final String? confirmText;
32+
33+
@override
34+
Widget build(BuildContext context) {
35+
final l10n = AppLocalizationsX(context).l10n;
36+
37+
return AlertDialog(
38+
title: Text(title),
39+
content: Text(content),
40+
actions: <Widget>[
41+
TextButton(
42+
onPressed: () => Navigator.of(context).pop(),
43+
child: Text(l10n.cancelButton),
44+
),
45+
ElevatedButton(
46+
onPressed: () {
47+
Navigator.of(context).pop();
48+
onConfirm();
49+
},
50+
child: Text(confirmText ?? l10n.confirmSaveButton),
51+
),
52+
],
53+
);
54+
}
55+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export 'searchable_selection_page.dart';
2+
export 'selection_page_arguments.dart';

lib/shared/widgets/widgets.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
export 'package:flutter_news_app_web_dashboard_full_source_code/shared/widgets/about_icon.dart';
2-
export 'package:flutter_news_app_web_dashboard_full_source_code/shared/widgets/searchable_selection_input.dart';
1+
export 'about_icon.dart';
2+
export 'confirmation_dialog.dart';
3+
export 'searchable_selection_input.dart';
4+
export 'selection_page/selection_page.dart';

lib/user_management/bloc/user_filter/user_filter_bloc.dart

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import 'package:core/core.dart';
33
import 'package:equatable/equatable.dart';
44
import 'package:flutter_news_app_web_dashboard_full_source_code/user_management/bloc/user_management_bloc.dart'
55
show UserManagementBloc;
6+
import 'package:flutter_news_app_web_dashboard_full_source_code/user_management/enums/authentication_filter.dart';
7+
import 'package:flutter_news_app_web_dashboard_full_source_code/user_management/enums/subscription_filter.dart';
68

79
part 'user_filter_event.dart';
810
part 'user_filter_state.dart';
@@ -18,8 +20,6 @@ class UserFilterBloc extends Bloc<UserFilterEvent, UserFilterState> {
1820
/// {@macro user_filter_bloc}
1921
UserFilterBloc() : super(const UserFilterState()) {
2022
on<UserFilterSearchQueryChanged>(_onSearchQueryChanged);
21-
on<UserFilterAppRolesChanged>(_onAppRolesChanged);
22-
on<UserFilterDashboardRolesChanged>(_onDashboardRolesChanged);
2323
on<UserFilterReset>(_onFilterReset);
2424
on<UserFilterApplied>(_onFilterApplied);
2525
}
@@ -32,22 +32,6 @@ class UserFilterBloc extends Bloc<UserFilterEvent, UserFilterState> {
3232
emit(state.copyWith(searchQuery: event.query));
3333
}
3434

35-
/// Handles changes to the selected app roles filter.
36-
void _onAppRolesChanged(
37-
UserFilterAppRolesChanged event,
38-
Emitter<UserFilterState> emit,
39-
) {
40-
emit(state.copyWith(selectedAppRoles: event.appRoles));
41-
}
42-
43-
/// Handles changes to the selected dashboard roles filter.
44-
void _onDashboardRolesChanged(
45-
UserFilterDashboardRolesChanged event,
46-
Emitter<UserFilterState> emit,
47-
) {
48-
emit(state.copyWith(selectedDashboardRoles: event.dashboardRoles));
49-
}
50-
5135
/// Resets all filters to their default values.
5236
void _onFilterReset(
5337
UserFilterReset event,
@@ -64,35 +48,10 @@ class UserFilterBloc extends Bloc<UserFilterEvent, UserFilterState> {
6448
emit(
6549
state.copyWith(
6650
searchQuery: event.searchQuery,
67-
selectedAppRoles: event.selectedAppRoles,
68-
selectedDashboardRoles: event.selectedDashboardRoles,
51+
authenticationFilter: event.authenticationFilter,
52+
subscriptionFilter: event.subscriptionFilter,
53+
dashboardRole: event.dashboardRole,
6954
),
7055
);
7156
}
72-
73-
/// Builds the filter map for the data repository query.
74-
Map<String, dynamic> buildFilterMap() {
75-
final filter = <String, dynamic>{};
76-
77-
if (state.searchQuery.isNotEmpty) {
78-
filter[r'$or'] = [
79-
{
80-
'email': {r'$regex': state.searchQuery, r'$options': 'i'},
81-
},
82-
{'_id': state.searchQuery},
83-
];
84-
}
85-
86-
if (state.selectedAppRoles.isNotEmpty) {
87-
filter['appRole'] = {
88-
r'$in': state.selectedAppRoles.map((r) => r.name).toList(),
89-
};
90-
}
91-
if (state.selectedDashboardRoles.isNotEmpty) {
92-
filter['dashboardRole'] = {
93-
r'$in': state.selectedDashboardRoles.map((r) => r.name).toList(),
94-
};
95-
}
96-
return filter;
97-
}
9857
}

0 commit comments

Comments
 (0)