383 lines
12 KiB
Dart
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;
|
|
}
|
|
}
|