diff --git a/app/lib/common/models/drug/warning_level.dart b/app/lib/common/models/drug/warning_level.dart index c8e0cda4..9709bdaa 100644 --- a/app/lib/common/models/drug/warning_level.dart +++ b/app/lib/common/models/drug/warning_level.dart @@ -16,6 +16,18 @@ enum WarningLevel { none, } +extension WarningLevelLabel on WarningLevel { + String getLabel(BuildContext context) { + final labelMap = { + WarningLevel.red.name: context.l10n.warning_level_red, + WarningLevel.yellow.name: context.l10n.warning_level_yellow, + WarningLevel.green.name: context.l10n.warning_level_green, + WarningLevel.none.name: context.l10n.warning_level_missing, + }; + return labelMap[name]!; + } +} + extension WarningLevelIcon on WarningLevel { static final _iconMap = { WarningLevel.red.name: Icons.dangerous_rounded, diff --git a/app/lib/common/widgets/drug_search/filter_menu.dart b/app/lib/common/widgets/drug_search/filter_menu.dart index ac98a115..ac0ca760 100644 --- a/app/lib/common/widgets/drug_search/filter_menu.dart +++ b/app/lib/common/widgets/drug_search/filter_menu.dart @@ -10,7 +10,8 @@ class FilterMenuItem { bool _value; // ignore: avoid_positional_boolean_parameters final void Function(bool newValue) updateSearch; - final Widget Function(BuildContext context, { + final Widget Function( + BuildContext context, { required bool value, required Function statefulOnChange, }) build; @@ -20,9 +21,8 @@ class FilterMenuItem { } class FilterMenu extends HookWidget { - const FilterMenu(this.cubit, this.state, this.activeDrugs, { - required this.useDrugClass - }); + const FilterMenu(this.cubit, this.state, this.activeDrugs, + {required this.useDrugClass}); final DrugListCubit cubit; final DrugListState state; @@ -35,87 +35,182 @@ class FilterMenu extends HookWidget { } Widget? _buildFilters(BuildContext context) { - return state.whenOrNull(loaded: (allDrugs, filter) => - SafeArea( - child: Column( - children: _geMenuItems(context, allDrugs, filter, activeDrugs), + return state.whenOrNull( + loaded: (allDrugs, filter) => SafeArea( + child: Container( + decoration: BoxDecoration( + border: Border.all( + color: PharMeTheme.surfaceColor, + width: PharMeTheme.smallSpace, + ), + borderRadius: BorderRadius.only( + topRight: Radius.circular(PharMeTheme.smallSpace), + )), + child: Container( + color: PharMeTheme.surfaceColor, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only( + top: PharMeTheme.mediumSpace, + right: PharMeTheme.mediumSpace, + bottom: PharMeTheme.mediumSpace, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + IconButton( + onPressed: Scaffold.of(context).closeDrawer, + icon: Icon(Icons.arrow_back_ios_rounded), + color: PharMeTheme.iconColor, + visualDensity: VisualDensity.compact, + ), + SizedBox(width: PharMeTheme.smallSpace * 0.5), + Text( + context.l10n.search_page_filter_heading, + style: PharMeTheme.textTheme.headlineMedium, + ), + ], + ), + ), + Padding( + padding: EdgeInsets.only( + left: PharMeTheme.mediumSpace, + right: PharMeTheme.mediumSpace, + bottom: PharMeTheme.mediumSpace, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: PharMeTheme.mediumSpace), + SubheaderDivider( + text: context.l10n.search_page_filter_subheading_usage, + useLine: false, + padding: 0, + ), + _getDrugStatusFilter(context, allDrugs, filter), + SizedBox(height: PharMeTheme.mediumSpace), + SubheaderDivider( + text: context.l10n.search_page_filter_subheading_level, + useLine: false, + padding: 0, + ), + SizedBox(height: PharMeTheme.smallSpace), + ..._getWarningLevelFilters(context, allDrugs, filter), + ], + ), + ), + ], + ), + ), + ), + )); + } + + int _getFilteredNumber({ + required FilterState itemFilter, + required List drugs, + }) { + return itemFilter + .filter(drugs, activeDrugs, useDrugClass: useDrugClass) + .length; + } + + Widget _getFilterText( + String text, { + required FilterState itemFilter, + required List drugs, + }) { + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(text), + Text( + ' (${_getFilteredNumber(itemFilter: itemFilter, drugs: drugs)})', + style: PharMeTheme.textTheme.labelMedium!.copyWith( + color: darkenColor(PharMeTheme.onSurfaceText, -0.2), + ), ), - ) + ], ); } - List _geMenuItems( + Widget _getDrugStatusFilter( BuildContext context, List drugs, FilterState filter, - ActiveDrugs activeDrugs, ) { - String formatItemFilterNumber(FilterState itemFilter) => - '(${ - itemFilter.filter(drugs, activeDrugs, useDrugClass: useDrugClass).length - })'; - String drugStatusNumber({ required bool showInactive}) => - formatItemFilterNumber(FilterState.from( + final value = filter.showInactive; + FilterState drugStatusFilterState({required bool showInactive}) { + return FilterState.from( FilterState.initial(), showInactive: showInactive, - )); - String warningLevelNumber(WarningLevel warningLevel) { - final currentWarningLevelFilter = FilterState.from(filter); - currentWarningLevelFilter.showWarningLevel.forEach( - (currentWarningLevel, currentValue) => - currentWarningLevelFilter.showWarningLevel[currentWarningLevel] = - currentWarningLevel == warningLevel ); - return formatItemFilterNumber(currentWarningLevelFilter); } - Widget buildDrugStatusItem() { - final value = filter.showInactive; - return DropdownButton( - value: value, - items: [ - DropdownMenuItem( - value: true, - child: Text( - '${context.l10n.search_page_filter_all_drugs} ' - '${drugStatusNumber(showInactive: true)}' - ), + return DropdownButton( + value: value, + items: [ + DropdownMenuItem( + value: true, + child: _getFilterText( + context.l10n.search_page_filter_all_drugs, + itemFilter: drugStatusFilterState(showInactive: true), + drugs: drugs, ), - DropdownMenuItem( - value: false, - child: Text( - '${context.l10n.search_page_filter_only_active_drugs} ' - '${drugStatusNumber(showInactive: false)}' - ), + ), + DropdownMenuItem( + value: false, + child: _getFilterText( + context.l10n.search_page_filter_only_active_drugs, + itemFilter: drugStatusFilterState(showInactive: false), + drugs: drugs, ), - ], - onChanged: (newValue) => newValue != value - ? cubit.search(showInactive: newValue) - : null, - ); + ), + ], + onChanged: (newValue) => + newValue != value ? cubit.search(showInactive: newValue) : null, + ); + } + + List _getWarningLevelFilters( + BuildContext context, + List drugs, + FilterState filter, + ) { + FilterState warningLevelFilter(WarningLevel warningLevel) { + final currentFilter = FilterState.from(filter); + currentFilter.showWarningLevel.forEach( + (currentWarningLevel, currentValue) => + currentFilter.showWarningLevel[currentWarningLevel] = + currentWarningLevel == warningLevel); + return currentFilter; } Widget buildWarningLevelItem(WarningLevel warningLevel) { final value = filter.showWarningLevel[warningLevel]!; return ActionChip( onPressed: () => cubit.search( - showWarningLevel: { warningLevel: !value }, + showWarningLevel: {warningLevel: !value}, ), avatar: Icon( value ? warningLevel.icon : warningLevel.outlinedIcon, - color: value ? PharMeTheme.onSurfaceText : warningLevel.textColor, + color: PharMeTheme.onSurfaceText, + ), + label: _getFilterText( + warningLevel.getLabel(context), + itemFilter: warningLevelFilter(warningLevel), + drugs: drugs, ), - label: Text(warningLevelNumber(warningLevel), - style: TextStyle(color: PharMeTheme.onSurfaceText)), visualDensity: VisualDensity.compact, - color: MaterialStatePropertyAll(value - ? warningLevel.color - : Colors.transparent + color: MaterialStatePropertyAll( + value ? warningLevel.color : Colors.transparent), + side: BorderSide( + color: value ? warningLevel.color : PharMeTheme.onSurfaceColor, ), ); } - return [ - buildDrugStatusItem(), - ...WarningLevel.values - .map(buildWarningLevelItem), - ]; + return WarningLevel.values.map(buildWarningLevelItem).toList(); } } diff --git a/app/lib/common/widgets/subheader_divider.dart b/app/lib/common/widgets/subheader_divider.dart index c908bc80..43b15522 100644 --- a/app/lib/common/widgets/subheader_divider.dart +++ b/app/lib/common/widgets/subheader_divider.dart @@ -3,14 +3,14 @@ import '../module.dart'; class SubheaderDivider extends StatelessWidget { const SubheaderDivider({ this.text = '', - this.indent = 20.0, + this.padding, this.color, this.useLine = true, super.key, }); final String text; - final double indent; + final double? padding; final Color? color; final bool useLine; @@ -18,7 +18,7 @@ class SubheaderDivider extends StatelessWidget { Widget build(BuildContext context) { final widgetColor = color ?? Colors.grey[600]; return Padding( - padding: EdgeInsets.all(PharMeTheme.smallSpace), + padding: EdgeInsets.all(padding ?? PharMeTheme.smallSpace), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 60116189..7fc4be3b 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -53,15 +53,19 @@ "general_and": "and", "general_poor_metabolizer": "Poor Metabolizer", "general_not_tested": "Not tested", + + "warning_level_green": "Standard precaution", + "warning_level_missing": "Standard precaution (missing data)", + "warning_level_yellow": "Use with caution", + "warning_level_red": "Consider avoiding", "search_page_tooltip_search": "Search for drugs by their name, brand name or class.", "search_page_tooltip_search_no_class": "Search for drugs by their name or brand name.", + "search_page_filter_heading": "Filter drugs", + "search_page_filter_subheading_usage": "Filter by usage status", + "search_page_filter_subheading_level": "Filter by guideline result", "search_page_filter_all_drugs": "All drugs", "search_page_filter_only_active_drugs": "Currently taken drugs", - "search_page_filter_green": "Green warning level", - "search_page_filter_yellow": "Yellow warning level", - "search_page_filter_red": "Red warning level", - "search_page_filter_only_with_guidelines": "Only drugs with guidelines", "search_page_indicator_explanation": "Taking drugs with an {indicatorName} ({indicator}) can influence your results for other drugs", "@search_page_indicator_explanation": { "placeholders": {