feat: add availability screen individual components

This commit is contained in:
Freek van de Ven 2024-07-10 11:06:10 +02:00 committed by Bart Ribbers
parent 095eff2c21
commit 20104160eb
5 changed files with 201 additions and 14 deletions

View file

@ -18,6 +18,11 @@ class AvailabilityTranslations {
required this.availabilityWithoutTemplateLabel, required this.availabilityWithoutTemplateLabel,
required this.overviewScreenTitle, required this.overviewScreenTitle,
required this.createTemplateButton, required this.createTemplateButton,
required this.unavailableForDay,
required this.unavailableForMultipleDays,
required this.availabilityAddTemplateTitle,
required this.availabilityTimeTitle,
required this.availabilitiesTimeTitle,
required this.templateScreenTitle, required this.templateScreenTitle,
required this.dayTemplates, required this.dayTemplates,
required this.weekTemplates, required this.weekTemplates,
@ -41,6 +46,8 @@ class AvailabilityTranslations {
required this.saveButton, required this.saveButton,
required this.addButton, required this.addButton,
required this.timeFormatter, required this.timeFormatter,
required this.dayMonthFormatter,
required this.periodFormatter,
required this.monthYearFormatter, required this.monthYearFormatter,
required this.weekDayAbbreviatedFormatter, required this.weekDayAbbreviatedFormatter,
}); });
@ -54,6 +61,11 @@ class AvailabilityTranslations {
this.templateSelectionLabel = "Selected day(s)", this.templateSelectionLabel = "Selected day(s)",
this.availabilityWithoutTemplateLabel = "Availabilty without template", this.availabilityWithoutTemplateLabel = "Availabilty without template",
this.createTemplateButton = "Create a new template", this.createTemplateButton = "Create a new template",
this.unavailableForDay = "I am not available this day",
this.unavailableForMultipleDays = "I am not available these days",
this.availabilityAddTemplateTitle = "Add template to availability",
this.availabilityTimeTitle = "Start and end time workday",
this.availabilitiesTimeTitle = "Start and end time workdays",
this.overviewScreenTitle = "Availability", this.overviewScreenTitle = "Availability",
this.templateScreenTitle = "Templates", this.templateScreenTitle = "Templates",
this.dayTemplates = "Day templates", this.dayTemplates = "Day templates",
@ -79,6 +91,8 @@ class AvailabilityTranslations {
"Select between which times you want to take a break", "Select between which times you want to take a break",
this.saveButton = "Save", this.saveButton = "Save",
this.addButton = "Add", this.addButton = "Add",
this.dayMonthFormatter = _defaultDayMonthFormatter,
this.periodFormatter = _defaultPeriodFormatter,
this.monthYearFormatter = _defaultMonthYearFormatter, this.monthYearFormatter = _defaultMonthYearFormatter,
this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter, this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter,
this.timeFormatter = _defaultTimeFormatter, this.timeFormatter = _defaultTimeFormatter,
@ -105,6 +119,21 @@ class AvailabilityTranslations {
/// The label on the button to go to the template screen /// The label on the button to go to the template screen
final String createTemplateButton; final String createTemplateButton;
/// The text shown to clear the availability for a day
final String unavailableForDay;
/// The text shown to clear the availability for multiple days
final String unavailableForMultipleDays;
/// The title on the template selection section for adding availabilities
final String availabilityAddTemplateTitle;
/// The title on the time selection section for adding a single availability
final String availabilityTimeTitle;
/// The title on the time selection section for adding multiple availabilities
final String availabilitiesTimeTitle;
/// The title on the template screen /// The title on the template screen
final String templateScreenTitle; final String templateScreenTitle;
@ -172,6 +201,16 @@ class AvailabilityTranslations {
/// The text on the add button /// The text on the add button
final String addButton; final String addButton;
/// Gets the day and month formatted as a string
///
/// The default implementation is `Dayname day monthname` in english
final String Function(BuildContext, DateTime) dayMonthFormatter;
/// Gets the period between two dates formatted as a string
///
/// The default implementation is `day monthname to day monthname` in english
final String Function(BuildContext, DateTimeRange) periodFormatter;
/// Gets the month and year formatted as a string /// Gets the month and year formatted as a string
/// ///
/// The default implementation is `MonthName Year` in english /// The default implementation is `MonthName Year` in english
@ -190,8 +229,15 @@ class AvailabilityTranslations {
} }
String _defaultTimeFormatter(BuildContext context, DateTime date) => String _defaultTimeFormatter(BuildContext context, DateTime date) =>
"${date.hour}:${date.minute}"; "${date.hour.toString().padLeft(2, '0')}:"
"${date.minute.toString().padLeft(2, '0')}";
String _defaultDayMonthFormatter(BuildContext context, DateTime date) =>
"${_getDayName(date.weekday)} ${date.day} ${_getMonthName(date.month)}";
String _defaultPeriodFormatter(BuildContext context, DateTimeRange range) =>
"${range.start.day} ${_getMonthName(range.start.month)} to "
"${range.end.day} ${_getMonthName(range.end.month)}";
String _defaultWeekDayAbbreviatedFormatter( String _defaultWeekDayAbbreviatedFormatter(
BuildContext context, BuildContext context,
DateTime date, DateTime date,
@ -201,16 +247,8 @@ String _defaultWeekDayAbbreviatedFormatter(
String _defaultMonthYearFormatter(BuildContext context, DateTime date) => String _defaultMonthYearFormatter(BuildContext context, DateTime date) =>
"${_getMonthName(date.month)} ${date.year}"; "${_getMonthName(date.month)} ${date.year}";
String _getWeekDayAbbreviation(int weekday) => switch (weekday) { String _getWeekDayAbbreviation(int weekday) =>
1 => "Mo", _getDayName(weekday).substring(0, 2);
2 => "Tu",
3 => "We",
4 => "Th",
5 => "Fr",
6 => "Sa",
7 => "Su",
_ => "",
};
String _getMonthName(int month) => switch (month) { String _getMonthName(int month) => switch (month) {
1 => "January", 1 => "January",
@ -227,3 +265,14 @@ String _getMonthName(int month) => switch (month) {
12 => "December", 12 => "December",
_ => "", _ => "",
}; };
String _getDayName(int day) => switch (day) {
1 => "Monday",
2 => "Tuesday",
3 => "Wednesday",
4 => "Thursday",
5 => "Friday",
6 => "Saturday",
7 => "Sunday",
_ => "",
};

View file

@ -0,0 +1,68 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/util/scope.dart";
///
class AvailabilityClearSection extends StatelessWidget {
///
const AvailabilityClearSection({
required this.range,
required this.clearAvailable,
required this.onChanged,
super.key,
});
/// The date range for which the availabilities can be cleared
/// If the range is a single day, the range will be formatted as a single day
/// If the range is multiple days, the range will be formatted as a period
final DateTimeRange range;
/// Whether the clear available checkbox should be checked
final bool clearAvailable;
/// Callback for when the clear available checkbox is toggled
// ignore: avoid_positional_boolean_parameters
final void Function(bool isChecked) onChanged;
@override
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 isSingleDay = range.start.isAtSameMomentAs(range.end);
var titleText = isSingleDay
? translations.dayMonthFormatter(context, range.start)
: translations.periodFormatter(context, range);
var unavailableText = isSingleDay
? translations.unavailableForDay
: translations.unavailableForMultipleDays;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
titleText,
style: textTheme.titleMedium,
),
Row(
children: [
Checkbox(
value: clearAvailable,
onChanged: (value) {
if (value == null) return;
onChanged(value);
},
),
Text(
unavailableText,
style: textTheme.bodyMedium,
),
],
),
],
);
}
}

View file

@ -0,0 +1,16 @@
import "package:flutter/material.dart";
/// Selection of the template to use for the availability
///
/// This can show multiple templates when the user selects a date range.
/// When updating the templates for a date range where there are multiple
/// different templates used, the user first needs to remove the existing
/// templates.
class AvailabilityTemplateSelection extends StatelessWidget {
/// Constructor
const AvailabilityTemplateSelection({super.key});
@override
Widget build(BuildContext context) =>
const SizedBox(height: 50, child: Placeholder());
}

View file

@ -0,0 +1,52 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/widgets/generic_time_selection.dart";
import "package:flutter_availability/src/util/scope.dart";
///
class AvailabilityTimeSelection extends StatelessWidget {
///
const AvailabilityTimeSelection({
required this.startTime,
required this.endTime,
required this.onStartChanged,
required this.onEndChanged,
required this.dateRange,
super.key,
});
///
final DateTime? startTime;
///
final DateTime? endTime;
///
final void Function(DateTime) onStartChanged;
///
final void Function(DateTime) onEndChanged;
/// The date range for which the availabilities are being managed
final DateTimeRange dateRange;
@override
Widget build(BuildContext context) {
var availabilityScope = AvailabilityScope.of(context);
var options = availabilityScope.options;
var translations = options.translations;
var isSingleDay = dateRange.start.isAtSameMomentAs(dateRange.end);
var titleText = isSingleDay
? translations.availabilityTimeTitle
: translations.availabilitiesTimeTitle;
return TimeSelection(
title: titleText,
description: null,
startTime: startTime,
endTime: endTime,
onStartChanged: onStartChanged,
onEndChanged: onEndChanged,
);
}
}

View file

@ -20,7 +20,7 @@ class TimeSelection extends StatelessWidget {
final String title; final String title;
/// The description of the time selection /// The description of the time selection
final String description; final String? description;
/// the axis aligment for the column /// the axis aligment for the column
final CrossAxisAlignment crossAxisAlignment; final CrossAxisAlignment crossAxisAlignment;
@ -49,8 +49,10 @@ class TimeSelection extends StatelessWidget {
crossAxisAlignment: crossAxisAlignment, crossAxisAlignment: crossAxisAlignment,
children: [ children: [
Text(title, style: textTheme.titleMedium), Text(title, style: textTheme.titleMedium),
if (description != null) ...[
const SizedBox(height: 4), const SizedBox(height: 4),
Text(description, style: textTheme.bodyMedium), Text(description!, style: textTheme.bodyMedium),
],
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
children: [ children: [