From f2e279a592b7a8b59ff8a603d41744f462d68236 Mon Sep 17 00:00:00 2001 From: Freek van de Ven Date: Sat, 6 Jul 2024 20:37:33 +0200 Subject: [PATCH] feat: add template overview screen The mocked templates will be replaced later with a template stream for fetching templates from the flutter_availability_data_interface --- .../src/config/availability_translations.dart | 27 ++- .../flutter_availability/lib/src/routes.dart | 22 +- .../lib/src/ui/screens/template_overview.dart | 199 ++++++++++++++++++ 3 files changed, 240 insertions(+), 8 deletions(-) create mode 100644 packages/flutter_availability/lib/src/ui/screens/template_overview.dart diff --git a/packages/flutter_availability/lib/src/config/availability_translations.dart b/packages/flutter_availability/lib/src/config/availability_translations.dart index 5069e45..69df198 100644 --- a/packages/flutter_availability/lib/src/config/availability_translations.dart +++ b/packages/flutter_availability/lib/src/config/availability_translations.dart @@ -17,6 +17,11 @@ class AvailabilityTranslations { required this.templateSelectionLabel, required this.overviewScreenTitle, required this.createTemplateButton, + required this.templateScreenTitle, + required this.dayTemplates, + required this.weekTemplates, + required this.createDayTemplate, + required this.createWeekTemplate, required this.monthYearFormatter, required this.weekDayAbbreviatedFormatter, }); @@ -30,6 +35,11 @@ class AvailabilityTranslations { this.templateSelectionLabel = "Selected day(s)", this.createTemplateButton = "Create a new template", this.overviewScreenTitle = "Availability", + this.templateScreenTitle = "Templates", + this.dayTemplates = "Day templates", + this.weekTemplates = "Week templates", + this.createDayTemplate = "Create day template", + this.createWeekTemplate = "Create week template", this.monthYearFormatter = _defaultMonthYearFormatter, this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter, }); @@ -49,9 +59,24 @@ class AvailabilityTranslations { /// The title on the overview screen final String overviewScreenTitle; - /// The label on the button to go to the tempalte creation page + /// The label on the button to go to the template screen final String createTemplateButton; + /// The title on the template screen + final String templateScreenTitle; + + /// The title for the day templates section on the template screen + final String dayTemplates; + + /// The title for the week templates section on the template screen + final String weekTemplates; + + /// The label for the button to create a new day template + final String createDayTemplate; + + /// The label for the button to create a new week template + final String createWeekTemplate; + /// Gets the month and year formatted as a string /// /// The default implementation is `MonthName Year` in english diff --git a/packages/flutter_availability/lib/src/routes.dart b/packages/flutter_availability/lib/src/routes.dart index 9fef226..fe54ab4 100644 --- a/packages/flutter_availability/lib/src/routes.dart +++ b/packages/flutter_availability/lib/src/routes.dart @@ -1,17 +1,25 @@ import "package:flutter/material.dart"; import "package:flutter_availability/flutter_availability.dart"; import "package:flutter_availability/src/ui/screens/template_availability_day_overview.dart"; +import "package:flutter_availability/src/ui/screens/template_overview.dart"; /// MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute( builder: (context) => AvailabilityOverview( - onEditDateRange: (range) async { - await Navigator.of(context).push(availabilityViewRoute(range.start)); - }, - onViewTemplates: () {}, - onExit: () { - onExit(); - }, + onEditDateRange: (range) async => + Navigator.of(context).push(availabilityViewRoute(range.start)), + onViewTemplates: () async => + Navigator.of(context).push(templateOverviewRoute()), + onExit: () => onExit(), + ), + ); + +/// +MaterialPageRoute templateOverviewRoute() => MaterialPageRoute( + builder: (context) => AvailabilityTemplateOverview( + onExit: () => Navigator.of(context).pop(), + onEditTemplate: (template) {}, + onAddTemplate: (type) {}, ), ); diff --git a/packages/flutter_availability/lib/src/ui/screens/template_overview.dart b/packages/flutter_availability/lib/src/ui/screens/template_overview.dart new file mode 100644 index 0000000..d2f8d58 --- /dev/null +++ b/packages/flutter_availability/lib/src/ui/screens/template_overview.dart @@ -0,0 +1,199 @@ +import "package:flutter/material.dart"; +import "package:flutter_availability/src/util/scope.dart"; +import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart"; + +/// Overview screen for all the availability templates +class AvailabilityTemplateOverview extends StatelessWidget { + /// Constructor + const AvailabilityTemplateOverview({ + required this.onExit, + required this.onEditTemplate, + required this.onAddTemplate, + super.key, + }); + + /// Callback for when the user wants to navigate back + final VoidCallback onExit; + + /// Callback for when the user goes to edit an existing template + final void Function(AvailabilityTemplateModel template) onEditTemplate; + + /// Callback for when the user goes to create a new template + final void Function(AvailabilityTemplateType type) onAddTemplate; + + @override + Widget build(BuildContext context) { + var theme = Theme.of(context); + var availabilityScope = AvailabilityScope.of(context); + var options = availabilityScope.options; + var translations = options.translations; + var spacing = options.spacing; + + var title = Center( + child: Text( + translations.templateScreenTitle, + style: theme.textTheme.displaySmall, + ), + ); + + var dayTemplateSection = _TemplateListSection( + sectionTitle: translations.dayTemplates, + createButtonText: translations.createDayTemplate, + templates: [ + for (var template in <(Color, String)>[ + (Colors.red, "Template 1"), + (Colors.blue, "Template 2"), + (Colors.green, "Template 3"), + (Colors.yellow, "Template 4"), + ]) ...[ + AvailabilityTemplateModel( + userId: "1", + id: "1", + name: template.$2, + templateType: AvailabilityTemplateType.day, + templateData: DayTemplateData( + startTime: DateTime.now(), + endTime: DateTime.now(), + breaks: [], + ), + color: template.$1.value, + ), + ], + ], + onEditTemplate: onEditTemplate, + onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.day), + ); + + var weekTemplateSection = _TemplateListSection( + sectionTitle: translations.weekTemplates, + createButtonText: translations.createWeekTemplate, + templates: [ + for (var template in <(Color, String)>[ + (Colors.purple, "Template 5"), + (Colors.orange, "Template 6"), + (Colors.teal, "Template 7"), + (Colors.pink, "Template 8"), + (Colors.indigo, "Template 9"), + ]) ...[ + AvailabilityTemplateModel( + userId: "1", + id: "1", + name: template.$2, + templateType: AvailabilityTemplateType.week, + templateData: DayTemplateData( + startTime: DateTime.now(), + endTime: DateTime.now(), + breaks: [], + ), + color: template.$1.value, + ), + ], + ], + onEditTemplate: onEditTemplate, + onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.week), + ); + + var body = Padding( + padding: EdgeInsets.symmetric(horizontal: spacing.sidePadding), + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 40), + title, + const SizedBox(height: 24), + dayTemplateSection, + const SizedBox(height: 40), + weekTemplateSection, + SizedBox(height: spacing.bottomButtonPadding), + ], + ), + ), + ); + + return options.baseScreenBuilder(context, onExit, body); + } +} + +/// Displays a list of templates and a button to create a new template +class _TemplateListSection extends StatelessWidget { + const _TemplateListSection({ + required this.sectionTitle, + required this.createButtonText, + required this.templates, + required this.onEditTemplate, + required this.onAddTemplate, + }); + + final String sectionTitle; + final String createButtonText; + final List templates; + final void Function(AvailabilityTemplateModel template) onEditTemplate; + final VoidCallback onAddTemplate; + + @override + Widget build(BuildContext context) { + var theme = Theme.of(context); + var textTheme = theme.textTheme; + + var templateCreationButton = GestureDetector( + onTap: onAddTemplate, + child: Container( + color: Colors.transparent, + height: 44, + margin: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.add), + const SizedBox(width: 8), + Text( + createButtonText, + style: textTheme.bodyLarge?.copyWith( + decoration: TextDecoration.underline, + ), + ), + ], + ), + ), + ); + + return Column( + children: [ + Text(sectionTitle, style: textTheme.titleMedium), + const SizedBox(height: 8), + const Divider(height: 1), + for (var template in templates) ...[ + GestureDetector( + onTap: () => onEditTemplate(template), + child: Container( + padding: const EdgeInsets.all(12), + margin: const EdgeInsets.only(top: 8), + decoration: BoxDecoration( + border: Border.all(color: theme.dividerColor, width: 1), + borderRadius: BorderRadius.circular(5), + ), + child: Row( + children: [ + Container( + decoration: BoxDecoration( + color: Color(template.color), + borderRadius: BorderRadius.circular(5), + ), + height: 20, + width: 20, + ), + const SizedBox(width: 8), + Text(template.name, style: textTheme.bodyLarge), + const Spacer(), + const Icon(Icons.edit), + ], + ), + ), + ), + ], + const SizedBox(height: 8), + templateCreationButton, + ], + ); + } +}