feat: add week template overview

This commit is contained in:
Freek van de Ven 2024-07-16 10:17:45 +02:00 committed by FlutterJoey
parent 5b54111850
commit c7ea82677c
3 changed files with 214 additions and 0 deletions

View file

@ -125,6 +125,7 @@ class AvailabilityColors {
this.outsideMonthTextColor, this.outsideMonthTextColor,
this.textDarkColor, this.textDarkColor,
this.textLightColor, this.textLightColor,
this.templateWeekOverviewBackgroundColor,
this.templateColors = const [ this.templateColors = const [
Color(0xFF9bb8f2), Color(0xFF9bb8f2),
Color(0xFF4b77d0), Color(0xFF4b77d0),
@ -160,6 +161,11 @@ class AvailabilityColors {
/// If not provided the text color will be the theme's text color /// If not provided the text color will be the theme's text color
final Color? textDarkColor; final Color? textDarkColor;
/// The color of the background in the template week overview that creates a
/// layered effect by interchanging a color and a transparent color
/// If not provided the color will be the theme's [ColorScheme.surface]
final Color? templateWeekOverviewBackgroundColor;
/// The colors that are used for the templates /// The colors that are used for the templates
final List<Color> templateColors; final List<Color> templateColors;
} }

View file

@ -43,9 +43,13 @@ class AvailabilityTranslations {
required this.templateTitleHintText, required this.templateTitleHintText,
required this.templateTitleLabel, required this.templateTitleLabel,
required this.templateColorLabel, required this.templateColorLabel,
required this.weekTemplateOverviewTitle,
required this.pause,
required this.unavailable,
required this.time, required this.time,
required this.timeSeparator, required this.timeSeparator,
required this.timeMinutes, required this.timeMinutes,
required this.timeMinutesShort,
required this.templateTimeLabel, required this.templateTimeLabel,
required this.pauseSectionTitle, required this.pauseSectionTitle,
required this.pauseSectionOptional, required this.pauseSectionOptional,
@ -105,9 +109,13 @@ class AvailabilityTranslations {
this.templateTitleHintText = "What do you want to call this template?", this.templateTitleHintText = "What do you want to call this template?",
this.templateTitleLabel = "Template Title", this.templateTitleLabel = "Template Title",
this.templateColorLabel = "Colorlabel", this.templateColorLabel = "Colorlabel",
this.weekTemplateOverviewTitle = "Overview availability",
this.pause = "Pause",
this.unavailable = "Unavailable",
this.time = "Time", this.time = "Time",
this.timeSeparator = "to", this.timeSeparator = "to",
this.timeMinutes = "minutes", this.timeMinutes = "minutes",
this.timeMinutesShort = "min",
this.templateTimeLabel = "When are you available?", this.templateTimeLabel = "When are you available?",
this.pauseSectionTitle = "Add a pause", this.pauseSectionTitle = "Add a pause",
this.pauseSectionOptional = "(Optional)", this.pauseSectionOptional = "(Optional)",
@ -229,6 +237,15 @@ class AvailabilityTranslations {
/// The title above the color selection for templates /// The title above the color selection for templates
final String templateColorLabel; final String templateColorLabel;
/// The title for the week overview section
final String weekTemplateOverviewTitle;
/// The label used for pause
final String pause;
/// The label used for unavailable
final String unavailable;
/// The title for time sections /// The title for time sections
final String time; final String time;
@ -238,6 +255,9 @@ class AvailabilityTranslations {
/// The text used for minutes /// The text used for minutes
final String timeMinutes; final String timeMinutes;
/// The text used for minutes in a short form
final String timeMinutesShort;
/// The label for the template time input /// The label for the template time input
final String templateTimeLabel; final String templateTimeLabel;

View file

@ -0,0 +1,188 @@
import "package:flutter/material.dart";
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
import "package:flutter_availability/src/ui/widgets/calendar_grid.dart";
import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
/// Shows an overview of the template of a week before saving it
class TemplateWeekOverview extends StatelessWidget {
///
const TemplateWeekOverview({
required this.template,
super.key,
});
/// The template to show
final AvailabilityTemplateModel template;
@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 colors = options.colors;
var dayNames = getDaysOfTheWeekAsStrings(translations, context);
var templateData = template.templateData as WeekTemplateData;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
translations.weekTemplateOverviewTitle,
style: textTheme.titleMedium,
),
const SizedBox(height: 8),
DecoratedBox(
decoration: BoxDecoration(
color: colors.templateWeekOverviewBackgroundColor ??
theme.colorScheme.primaryContainer,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: theme.dividerColor,
),
),
child: Column(
children: [
for (var day in WeekDay.values) ...[
_TemplateDayDetailRow(
dayName: dayNames[day.index],
dayData: templateData.data.containsKey(day)
? ViewDayTemplateData.fromDayTemplateData(
templateData.data[day]!,
)
: null,
isOdd: day.index.isOdd,
),
],
],
),
),
],
);
}
}
class _TemplateDayDetailRow extends StatelessWidget {
const _TemplateDayDetailRow({
required this.dayName,
required this.dayData,
required this.isOdd,
});
/// The name of the day
final String dayName;
/// There odd rows do not have a background color
/// This causes a layered effect
final bool isOdd;
/// The data of the day
final ViewDayTemplateData? dayData;
@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 startTime = dayData?.startTime;
var endTime = dayData?.endTime;
var dayHasAvailability = startTime != null && endTime != null;
String? dayPeriod;
if (dayHasAvailability) {
dayPeriod = "${translations.timeFormatter(context, startTime)} - "
"${translations.timeFormatter(context, endTime)}";
} else {
dayPeriod = translations.unavailable;
}
var breaks = dayData?.breaks ?? <AvailabilityBreakModel>[];
BoxDecoration? boxDecoration;
if (isOdd) {
boxDecoration = BoxDecoration(
color: Colors.white,
border: Border(
left: BorderSide(
color: theme.dividerColor,
),
right: BorderSide(
color: theme.dividerColor,
),
),
);
}
return Container(
decoration: boxDecoration,
padding: const EdgeInsets.all(16),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(dayName, style: textTheme.bodyLarge),
Text(dayPeriod, style: textTheme.bodyLarge),
],
),
// for each break add a line
for (var dayBreak in breaks) ...[
const SizedBox(height: 4),
_TemplateDayDetailPauseRow(dayBreak: dayBreak),
],
],
),
);
}
}
class _TemplateDayDetailPauseRow extends StatelessWidget {
const _TemplateDayDetailPauseRow({
required this.dayBreak,
});
final AvailabilityBreakModel dayBreak;
@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 startTime = TimeOfDay.fromDateTime(dayBreak.startTime);
var endTime = TimeOfDay.fromDateTime(dayBreak.endTime);
var startTimeString = translations.timeFormatter(context, startTime);
var endTimeString = translations.timeFormatter(context, endTime);
var pausePeriod =
"$startTimeString - $endTimeString (${dayBreak.duration.inMinutes} "
"${translations.timeMinutesShort})";
var pauseTextStyle = textTheme.bodyMedium?.copyWith(
fontStyle: FontStyle.italic,
);
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.only(left: 16),
child: Text(
translations.pause,
style: pauseTextStyle,
),
),
Text(
pausePeriod,
style: pauseTextStyle,
),
],
);
}
}