ambito/lib/src/pages/actions/actions_page.dart
2024-11-18 09:15:00 +01:00

383 lines
12 KiB
Dart

import 'package:ambito/src/entity/_general/filter/item_filter_repository.dart';
import 'package:ambito/src/entity/measure/measure_repository.dart';
import 'package:ambito/src/packages/ambito_notifier/notifier/filter_notifier.dart';
import 'package:ambito/src/pages/ambito_page.dart';
import 'package:ambito/src/widgets/appbar/ambito_appbar.dart';
import 'package:ambito/src/widgets/form/fields/field_title.dart';
import 'package:ambito/src/widgets/form/form_widget.dart';
import 'package:ambito/src/widgets/form/form_widget_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:get/get.dart';
import 'package:highlight_text/highlight_text.dart';
import 'package:screen_breakpoints/screen_breakpoints.dart';
import '../../../main.dart';
import '../../entity/entities.dart';
import '../../packages/ambito_theme/ambito_theme.dart';
class ActionsPage extends AmbitoPage {
const ActionsPage({super.key});
@override
final String path = 'massnahmendatenbank';
@override
final String title = 'Maßnamendatenbank';
@override
State<StatefulWidget> createState() => ActionsPageState();
}
class ActionsPageState extends State<ActionsPage> {
Map<int, bool> visible = {};
List<String> effort = [], effect = [], region = [];
Set<String> type = {}, areaType = {}, support = {}, months = {};
String? filterType,
filterAreaType,
filterSupport,
filterMonths,
searchText = '';
List<String>? selectedMonths;
List massnahmen = [];
Map<String, HighlightedWord> words = {};
String? preselectAreaType;
String? preselectMeasureType;
final TextEditingController _searchController = TextEditingController();
final FocusNode _searchFocusNode = FocusNode();
@override
void initState() {
ambitoFilterNotifier.addListener(_changeListener);
setState(() {
preselectAreaType = prefs.getString('selected_areaType');
if (preselectAreaType != null && preselectAreaType!.isNotEmpty) {
filterAreaType = preselectAreaType;
ambitoFilterNotifier.setFilter('areaType', preselectAreaType!);
}
});
_initializeData();
super.initState();
}
void _changeListener() {
setState(() {
filterAreaType = ambitoFilterNotifier.getFilter('areaType');
filterType = ambitoFilterNotifier.getFilter('group');
filterSupport = ambitoFilterNotifier.getFilter('fundingProgram');
filterMonths = ambitoFilterNotifier.getFilter('month');
});
}
@override
void dispose() {
//ambitoFilterNotifier.dispose();
super.dispose();
}
void _initializeData() {
massnahmen = MeasureRepository().getAllOrdered();
_updateFilterSets();
}
void _updateFilterSets() {
final updatedTypes = <String>{}, updatedAreaTypes = <String>{};
final updatedSupports = <String>{}, updatedMonths = <String>{};
for (final massnahme in massnahmen) {
if (massnahme.actionGroup?.value != null) {
updatedTypes.add(massnahme.actionGroup!.value!);
}
massnahme.factsheetAreaType?.forEach((aType) {
if (aType.value != null) updatedAreaTypes.add(aType.value!);
});
massnahme.fundingPrograms?.forEach((aType) {
if (aType.value != null) updatedSupports.add(aType.value!);
});
massnahme.timeFrame?.forEach((tfType) {
if (tfType.value != null) updatedMonths.add(tfType.value!);
});
}
setState(() {
type = updatedTypes;
areaType = updatedAreaTypes;
support = updatedSupports;
months = updatedMonths;
});
}
void _onSearchTextChanged(String value) {
setState(() {
searchText = value;
words = {
for (var word in value.split(' '))
word: HighlightedWord(
textStyle: Theme.of(context).textTheme.headlineSmall?.copyWith(
color: Colors.white, backgroundColor: Colors.green.shade800))
};
});
}
@override
Widget build(BuildContext context) {
final updatedTypes = <String>{};
final updatedAreaTypes = <String>{};
final updatedSupports = <String>{};
final updatedMonths = <String>{};
for (final massnahme in massnahmen) {
if (massnahme.actionGroup?.value != null) {
updatedTypes.add(massnahme.actionGroup!.value!);
}
massnahme.factsheetAreaType?.forEach((aType) {
updatedAreaTypes.add(aType.value!);
});
massnahme.fundingPrograms?.forEach((aType) {
updatedSupports.add(aType.value!);
});
massnahme.timeFrame?.forEach((tfType) {
updatedMonths.add(tfType.value!);
});
}
setState(() {
type.addAll(updatedTypes);
areaType.addAll(updatedAreaTypes);
support.addAll(updatedSupports);
months.addAll(updatedMonths);
});
final actionCards = massnahmen.map((massnahme) {
List<String> possibleMonths = List.generate(
12,
(i) => FlutterI18n.translate(
context,
'general.lists.months.${i + 1}',
),
);
if (massnahme.timeFrame != null && massnahme.timeFrame.isNotEmpty) {
possibleMonths = [];
for (IdValueColor month in massnahme.timeFrame) {
possibleMonths.add(month.value!);
}
}
final typeMatches =
filterType == null || massnahme.actionGroup?.value == filterType;
final areaTypeMatches = filterAreaType == null ||
(massnahme.factsheetAreaType
?.any((aType) => aType.value == filterAreaType) ??
false);
final supportMatches = filterSupport == null ||
(massnahme.fundingPrograms
?.any((aType) => aType.value == filterSupport) ??
false);
final searchMatches = searchText == '' ||
((massnahme.name.toLowerCase().contains(searchText?.toLowerCase())) ??
false) ||
((massnahme.factsheetDefinition
.toLowerCase()
.contains(searchText?.toLowerCase())) ??
false);
final monthMatches = (selectedMonths == [] || selectedMonths == null) ||
(_hasCommonItems(selectedMonths!, possibleMonths));
setState(() {
visible[massnahme.id] = typeMatches &&
areaTypeMatches &&
supportMatches &&
searchMatches &&
monthMatches;
});
return getCard(context, massnahme);
}).toList();
return Scaffold(
appBar: AmbitoAppbar(
links: const ['dashboard', 'massnahmen'],
breakpoint: Breakpoint.fromContext(context),
),
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Align(
alignment: Alignment.centerLeft,
child: BreadCrumb(
items: <BreadCrumbItem>[
BreadCrumbItem(
content: TextButton(
onPressed: () {
Get.offAndToNamed('/');
},
child: const Text('Start'),
),
),
BreadCrumbItem(
content: const Text(
'Maßnahmen',
),
),
],
divider: const Icon(Icons.chevron_right),
),
),
getSpacer(),
_buildSearchBar(),
getSpacer(),
getSpacer(),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(width: 300, child: _buildFilter(context)),
const SizedBox(width: 16),
Expanded(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: actionCards,
),
),
),
],
),
),
getSpacer(),
],
),
),
);
}
Widget getSpacer() {
return const SizedBox(
height: 8,
);
}
Widget _buildSearchBar() {
return Center(
child: SearchBar(
controller: _searchController,
focusNode: _searchFocusNode,
elevation: WidgetStateProperty.all(0.0),
backgroundColor: WidgetStateProperty.all(Colors.white),
side: WidgetStateProperty.all(const BorderSide(color: Colors.black)),
leading: const Icon(Icons.search),
trailing: [
if (_searchFocusNode.hasFocus && _searchController.text.isNotEmpty)
IconButton(
onPressed: () {
_searchController.clear();
_searchFocusNode.unfocus();
_onSearchTextChanged('');
},
icon: const Icon(Icons.clear, color: Colors.grey, size: 20),
),
],
onChanged: _onSearchTextChanged,
),
);
}
Widget _buildFilter(BuildContext context) {
return FormWidget(
type: FormWidgetType.vertical,
fields: [
FieldTitle(text: 'Filter'),
ItemFilterRepository().getDropDown('areaType', 'Ort der Maßnahme'),
ItemFilterRepository().getDropDown('group', 'Art der Maßnahme'),
ItemFilterRepository().getDropDown('fundingProgram', 'Förderprogramm'),
ItemFilterRepository().getDropDown('month', 'Beginn der Maßnahme'),
],
);
}
Widget getCard(BuildContext context, Measure massnahme) {
final image = massnahme.getThumbnail();
final AmbitoTheme theme = getTheme(context);
return Visibility(
visible: visible[massnahme.id] ?? false,
child: InkWell(
onHover: (hovered) {},
onTap: () async {
Get.toNamed('/massnahme/${massnahme.id}');
},
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
focusColor: Colors.transparent,
hoverColor: Colors.transparent,
child: Padding(
padding: const EdgeInsets.only(bottom: 40),
child: SizedBox(
width: 800,
child: Card(
elevation: 0,
color: theme.currentColorScheme.outline.withOpacity(.1),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: (image != null)
? image
: Container(
width: 300,
height: 150,
decoration: BoxDecoration(
color: theme.currentColorScheme.primary
.withOpacity(.1),
image: const DecorationImage(
image:
AssetImage('assets/images/logo_trans.png'),
fit: BoxFit.fitHeight,
),
),
),
),
Expanded(
child: Padding(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextHighlight(
text: massnahme.name!,
words: words,
textStyle: theme.textTheme.headlineSmall,
),
const SizedBox(height: 8),
TextHighlight(
text: massnahme.factsheetDefinition ?? '',
words: words,
textStyle: theme.textTheme.bodyLarge,
),
],
),
),
),
],
),
),
),
),
),
);
}
bool _hasCommonItems(List listOne, List listTwo) {
return listOne.toSet().intersection(listTwo.toSet()).isNotEmpty;
}
}