feat: add template selection for creating availabilities

This commit is contained in:
Freek van de Ven 2024-07-11 16:06:11 +02:00 committed by Bart Ribbers
parent fe910ef041
commit 04b843d2fd
5 changed files with 161 additions and 9 deletions

View file

@ -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;

View file

@ -18,7 +18,8 @@ MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute(
);
///
MaterialPageRoute templateOverviewRoute() => MaterialPageRoute(
MaterialPageRoute<AvailabilityTemplateModel?> 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(),
),
);

View file

@ -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<AvailabilityTemplateModel?> Function() onTemplateSelection;
@override
State<AvailabilitiesModificationScreen> createState() =>
_AvailabilitiesModificationScreenState();
@ -41,6 +46,7 @@ class AvailabilitiesModificationScreen extends StatefulWidget {
class _AvailabilitiesModificationScreenState
extends State<AvailabilitiesModificationScreen> {
late AvailabilityModel _availability;
late List<AvailabilityTemplateModel> _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,

View file

@ -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<List<AvailabilityTemplateModel>> 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 ?? <AvailabilityTemplateModel>[]) ...[
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),
),
],
),
),

View file

@ -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<AvailabilityTemplateModel> 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),
),
],
),
),
],
],
);
}
}