diff --git a/packages/flutter_availability/lib/src/config/availability_translations.dart b/packages/flutter_availability/lib/src/config/availability_translations.dart index 1f3a801..4a20560 100644 --- a/packages/flutter_availability/lib/src/config/availability_translations.dart +++ b/packages/flutter_availability/lib/src/config/availability_translations.dart @@ -24,6 +24,8 @@ class AvailabilityTranslations { required this.unavailableForDay, required this.unavailableForMultipleDays, required this.availabilityAddTemplateTitle, + required this.availabilityUsedTemplate, + required this.availabilityUsedTemplates, required this.availabilityTimeTitle, required this.availabilitiesTimeTitle, required this.availabilityDialogConfirmTitle, @@ -75,6 +77,8 @@ class AvailabilityTranslations { this.unavailableForDay = "I am not available this day", this.unavailableForMultipleDays = "I am not available these days", this.availabilityAddTemplateTitle = "Add template to availability", + this.availabilityUsedTemplate = "Used template", + this.availabilityUsedTemplates = "Used templates", this.availabilityTimeTitle = "Start and end time workday", this.availabilitiesTimeTitle = "Start and end time workdays", this.availabilityDialogConfirmTitle = @@ -156,6 +160,12 @@ class AvailabilityTranslations { /// The title on the template selection section for adding availabilities final String availabilityAddTemplateTitle; + /// The title on the template selection section when a single template is used + final String availabilityUsedTemplate; + + /// The title on the template selection section when more templates are used + final String availabilityUsedTemplates; + /// The title on the time selection section for adding a single availability final String availabilityTimeTitle; diff --git a/packages/flutter_availability/lib/src/routes.dart b/packages/flutter_availability/lib/src/routes.dart index da4ce8b..7c3980c 100644 --- a/packages/flutter_availability/lib/src/routes.dart +++ b/packages/flutter_availability/lib/src/routes.dart @@ -18,7 +18,8 @@ MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute( ); /// -MaterialPageRoute templateOverviewRoute() => MaterialPageRoute( +MaterialPageRoute templateOverviewRoute() => + MaterialPageRoute( builder: (context) => AvailabilityTemplateOverview( onExit: () => Navigator.of(context).pop(), onEditTemplate: (template) async { @@ -31,6 +32,9 @@ MaterialPageRoute templateOverviewRoute() => MaterialPageRoute( await Navigator.of(context).push(templateEditDayRoute(null)); } }, + onSelectTemplate: (template) async { + Navigator.of(context).pop(template); + }, ), ); @@ -52,8 +56,11 @@ MaterialPageRoute availabilityViewRoute( builder: (context) => AvailabilitiesModificationScreen( dateRange: dateRange, initialAvailabilities: initialAvailabilities, - onExit: () { - Navigator.of(context).pop(); + onTemplateSelection: () async { + var selectedTemplate = + Navigator.of(context).push(templateOverviewRoute()); + return selectedTemplate; }, + onExit: () => Navigator.of(context).pop(), ), ); diff --git a/packages/flutter_availability/lib/src/ui/screens/availability_modification.dart b/packages/flutter_availability/lib/src/ui/screens/availability_modification.dart index c1a9cbf..e028fed 100644 --- a/packages/flutter_availability/lib/src/ui/screens/availability_modification.dart +++ b/packages/flutter_availability/lib/src/ui/screens/availability_modification.dart @@ -16,6 +16,7 @@ class AvailabilitiesModificationScreen extends StatefulWidget { required this.dateRange, required this.onExit, required this.initialAvailabilities, + required this.onTemplateSelection, super.key, }); @@ -33,6 +34,10 @@ class AvailabilitiesModificationScreen extends StatefulWidget { /// availabilities have been saved final VoidCallback onExit; + /// Callback for when the user wants to go to the template overview screen to + /// select a template + final Future Function() onTemplateSelection; + @override State createState() => _AvailabilitiesModificationScreenState(); @@ -41,6 +46,7 @@ class AvailabilitiesModificationScreen extends StatefulWidget { class _AvailabilitiesModificationScreenState extends State { late AvailabilityModel _availability; + late List _selectedTemplates; bool _clearAvailability = false; TimeOfDay? _startTime; TimeOfDay? _endTime; @@ -56,6 +62,7 @@ class _AvailabilitiesModificationScreenState endDate: widget.dateRange.end, breaks: [], ); + _selectedTemplates = widget.initialAvailabilities.getUniqueTemplates(); } @override @@ -121,7 +128,22 @@ class _AvailabilitiesModificationScreenState }, ); - var templateSelection = const AvailabilityTemplateSelection(); + var templateSelection = AvailabilityTemplateSelection( + selectedTemplates: _selectedTemplates, + onTemplateAdd: () async { + var template = await widget.onTemplateSelection(); + if (template != null) { + setState(() { + _selectedTemplates = [template]; + }); + } + }, + onTemplatesRemoved: () { + setState(() { + _selectedTemplates = []; + }); + }, + ); var timeSelection = AvailabilityTimeSelection( dateRange: widget.dateRange, diff --git a/packages/flutter_availability/lib/src/ui/screens/template_overview.dart b/packages/flutter_availability/lib/src/ui/screens/template_overview.dart index 7244635..c477208 100644 --- a/packages/flutter_availability/lib/src/ui/screens/template_overview.dart +++ b/packages/flutter_availability/lib/src/ui/screens/template_overview.dart @@ -10,6 +10,7 @@ class AvailabilityTemplateOverview extends HookWidget { required this.onExit, required this.onEditTemplate, required this.onAddTemplate, + this.onSelectTemplate, super.key, }); @@ -22,6 +23,9 @@ class AvailabilityTemplateOverview extends HookWidget { /// Callback for when the user goes to create a new template final void Function(AvailabilityTemplateType type) onAddTemplate; + /// Callback for when the user selects a template + final void Function(AvailabilityTemplateModel template)? onSelectTemplate; + @override Widget build(BuildContext context) { var theme = Theme.of(context); @@ -48,6 +52,7 @@ class AvailabilityTemplateOverview extends HookWidget { sectionTitle: translations.dayTemplates, createButtonText: translations.createDayTemplate, onEditTemplate: onEditTemplate, + onSelectTemplate: onSelectTemplate, onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.day), templatesSnapshot: dayTemplatesSnapshot, ); @@ -57,6 +62,7 @@ class AvailabilityTemplateOverview extends HookWidget { createButtonText: translations.createWeekTemplate, templatesSnapshot: weekTemplatesSnapshot, onEditTemplate: onEditTemplate, + onSelectTemplate: onSelectTemplate, onAddTemplate: () => onAddTemplate(AvailabilityTemplateType.week), ); @@ -89,6 +95,7 @@ class _TemplateListSection extends StatelessWidget { required this.templatesSnapshot, required this.onEditTemplate, required this.onAddTemplate, + required this.onSelectTemplate, }); final String sectionTitle; @@ -96,6 +103,7 @@ class _TemplateListSection extends StatelessWidget { final AsyncSnapshot> templatesSnapshot; final void Function(AvailabilityTemplateModel template) onEditTemplate; final VoidCallback onAddTemplate; + final void Function(AvailabilityTemplateModel template)? onSelectTemplate; @override Widget build(BuildContext context) { @@ -104,6 +112,12 @@ class _TemplateListSection extends StatelessWidget { var availabilityScope = AvailabilityScope.of(context); var options = availabilityScope.options; + void onClickTemplate(AvailabilityTemplateModel template) { + // if the onSelectTemplate is set the user can select a template + // The user will need to click on the edit button to edit + (onSelectTemplate ?? onEditTemplate).call(template); + } + var templateCreationButton = InkWell( hoverColor: Colors.transparent, onTap: onAddTemplate, @@ -136,7 +150,7 @@ class _TemplateListSection extends StatelessWidget { for (var template in templatesSnapshot.data ?? []) ...[ GestureDetector( - onTap: () => onEditTemplate(template), + onTap: () => onClickTemplate(template), child: Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.only(top: 8), @@ -157,7 +171,10 @@ class _TemplateListSection extends StatelessWidget { const SizedBox(width: 8), Text(template.name, style: textTheme.bodyLarge), const Spacer(), - const Icon(Icons.edit), + GestureDetector( + onTap: () => onEditTemplate(template), + child: const Icon(Icons.edit), + ), ], ), ), diff --git a/packages/flutter_availability/lib/src/ui/widgets/availability_template_selection.dart b/packages/flutter_availability/lib/src/ui/widgets/availability_template_selection.dart index 18de772..cd3b5b7 100644 --- a/packages/flutter_availability/lib/src/ui/widgets/availability_template_selection.dart +++ b/packages/flutter_availability/lib/src/ui/widgets/availability_template_selection.dart @@ -1,4 +1,6 @@ import "package:flutter/material.dart"; +import "package:flutter_availability/src/util/scope.dart"; +import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart"; /// Selection of the template to use for the availability /// @@ -8,9 +10,103 @@ import "package:flutter/material.dart"; /// templates. class AvailabilityTemplateSelection extends StatelessWidget { /// Constructor - const AvailabilityTemplateSelection({super.key}); + const AvailabilityTemplateSelection({ + required this.selectedTemplates, + required this.onTemplateAdd, + required this.onTemplatesRemoved, + super.key, + }); + + /// The currently selected templates + final List selectedTemplates; + + /// Callback for when the user selects a template + final VoidCallback onTemplateAdd; + + /// Callback for when the user wants to remove the templates + /// There might be multiple templates and they can only be removed all at once + final VoidCallback onTemplatesRemoved; @override - Widget build(BuildContext context) => - const SizedBox(height: 50, child: Placeholder()); + Widget build(BuildContext context) { + var theme = Theme.of(context); + var textTheme = theme.textTheme; + var availabilityScope = AvailabilityScope.of(context); + var options = availabilityScope.options; + var translations = options.translations; + + var titleText = selectedTemplates.isEmpty + ? translations.availabilityAddTemplateTitle + : selectedTemplates.length > 1 + ? translations.availabilityUsedTemplates + : translations.availabilityUsedTemplate; + + var addButton = options.bigTextButtonWrapperBuilder( + context, + onTemplateAdd, + options.bigTextButtonBuilder( + context, + onTemplateAdd, + Text(translations.addButton), + ), + ); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + titleText, + style: textTheme.titleMedium, + ), + const SizedBox(height: 8), + if (selectedTemplates.isEmpty) ...[ + addButton, + ] else ...[ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + border: Border.all( + color: theme.colorScheme.primary, + width: 1, + ), + borderRadius: BorderRadius.circular(5), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + for (var template in selectedTemplates) ...[ + Row( + children: [ + Container( + decoration: BoxDecoration( + color: Color(template.color), + borderRadius: BorderRadius.circular(5), + ), + width: 20, + height: 20, + ), + const SizedBox(width: 12), + Text(template.name, style: textTheme.bodyLarge), + ], + ), + if (template != selectedTemplates.last) + const SizedBox(height: 12), + ], + ], + ), + GestureDetector( + onTap: onTemplatesRemoved, + child: const Icon(Icons.remove), + ), + ], + ), + ), + ], + ], + ); + } }