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.overviewScreenTitle,
required this.createTemplateButton,
required this.unavailableForDay,
required this.unavailableForMultipleDays,
required this.availabilityAddTemplateTitle,
required this.availabilityTimeTitle,
required this.availabilitiesTimeTitle,
required this.templateScreenTitle,
required this.dayTemplates,
required this.weekTemplates,
@ -41,6 +46,8 @@ class AvailabilityTranslations {
required this.saveButton,
required this.addButton,
required this.timeFormatter,
required this.dayMonthFormatter,
required this.periodFormatter,
required this.monthYearFormatter,
required this.weekDayAbbreviatedFormatter,
});
@ -54,6 +61,11 @@ class AvailabilityTranslations {
this.templateSelectionLabel = "Selected day(s)",
this.availabilityWithoutTemplateLabel = "Availabilty without 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.templateScreenTitle = "Templates",
this.dayTemplates = "Day templates",
@ -79,6 +91,8 @@ class AvailabilityTranslations {
"Select between which times you want to take a break",
this.saveButton = "Save",
this.addButton = "Add",
this.dayMonthFormatter = _defaultDayMonthFormatter,
this.periodFormatter = _defaultPeriodFormatter,
this.monthYearFormatter = _defaultMonthYearFormatter,
this.weekDayAbbreviatedFormatter = _defaultWeekDayAbbreviatedFormatter,
this.timeFormatter = _defaultTimeFormatter,
@ -105,6 +119,21 @@ class AvailabilityTranslations {
/// The label on the button to go to the template screen
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
final String templateScreenTitle;
@ -172,6 +201,16 @@ class AvailabilityTranslations {
/// The text on the add button
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
///
/// The default implementation is `MonthName Year` in english
@ -190,8 +229,15 @@ class AvailabilityTranslations {
}
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(
BuildContext context,
DateTime date,
@ -201,16 +247,8 @@ String _defaultWeekDayAbbreviatedFormatter(
String _defaultMonthYearFormatter(BuildContext context, DateTime date) =>
"${_getMonthName(date.month)} ${date.year}";
String _getWeekDayAbbreviation(int weekday) => switch (weekday) {
1 => "Mo",
2 => "Tu",
3 => "We",
4 => "Th",
5 => "Fr",
6 => "Sa",
7 => "Su",
_ => "",
};
String _getWeekDayAbbreviation(int weekday) =>
_getDayName(weekday).substring(0, 2);
String _getMonthName(int month) => switch (month) {
1 => "January",
@ -227,3 +265,14 @@ String _getMonthName(int month) => switch (month) {
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;
/// The description of the time selection
final String description;
final String? description;
/// the axis aligment for the column
final CrossAxisAlignment crossAxisAlignment;
@ -49,8 +49,10 @@ class TimeSelection extends StatelessWidget {
crossAxisAlignment: crossAxisAlignment,
children: [
Text(title, style: textTheme.titleMedium),
const SizedBox(height: 4),
Text(description, style: textTheme.bodyMedium),
if (description != null) ...[
const SizedBox(height: 4),
Text(description!, style: textTheme.bodyMedium),
],
const SizedBox(height: 8),
Row(
children: [