mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 05:03:44 +02:00
feat: add week template modification screen
This commit is contained in:
parent
c7ea82677c
commit
fd81964b75
5 changed files with 341 additions and 14 deletions
|
@ -37,6 +37,7 @@ class AvailabilityTranslations {
|
||||||
required this.createDayTemplate,
|
required this.createDayTemplate,
|
||||||
required this.createWeekTemplate,
|
required this.createWeekTemplate,
|
||||||
required this.deleteTemplateButton,
|
required this.deleteTemplateButton,
|
||||||
|
required this.editTemplateButton,
|
||||||
required this.dayTemplateTitle,
|
required this.dayTemplateTitle,
|
||||||
required this.weekTemplateTitle,
|
required this.weekTemplateTitle,
|
||||||
required this.weekTemplateDayTitle,
|
required this.weekTemplateDayTitle,
|
||||||
|
@ -60,6 +61,7 @@ class AvailabilityTranslations {
|
||||||
required this.pauseDialogPeriodDescription,
|
required this.pauseDialogPeriodDescription,
|
||||||
required this.saveButton,
|
required this.saveButton,
|
||||||
required this.addButton,
|
required this.addButton,
|
||||||
|
required this.nextButton,
|
||||||
required this.confirmText,
|
required this.confirmText,
|
||||||
required this.cancelText,
|
required this.cancelText,
|
||||||
required this.timeFormatter,
|
required this.timeFormatter,
|
||||||
|
@ -103,6 +105,7 @@ class AvailabilityTranslations {
|
||||||
this.createDayTemplate = "Create day template",
|
this.createDayTemplate = "Create day template",
|
||||||
this.createWeekTemplate = "Create week template",
|
this.createWeekTemplate = "Create week template",
|
||||||
this.deleteTemplateButton = "Delete template",
|
this.deleteTemplateButton = "Delete template",
|
||||||
|
this.editTemplateButton = "Edit template",
|
||||||
this.dayTemplateTitle = "Day template",
|
this.dayTemplateTitle = "Day template",
|
||||||
this.weekTemplateTitle = "Week template",
|
this.weekTemplateTitle = "Week template",
|
||||||
this.weekTemplateDayTitle = "When",
|
this.weekTemplateDayTitle = "When",
|
||||||
|
@ -130,6 +133,7 @@ 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.nextButton = "Next",
|
||||||
this.confirmText = "Yes",
|
this.confirmText = "Yes",
|
||||||
this.cancelText = "No",
|
this.cancelText = "No",
|
||||||
this.dayMonthFormatter = _defaultDayMonthFormatter,
|
this.dayMonthFormatter = _defaultDayMonthFormatter,
|
||||||
|
@ -219,6 +223,9 @@ class AvailabilityTranslations {
|
||||||
/// The label on the button to delete a template
|
/// The label on the button to delete a template
|
||||||
final String deleteTemplateButton;
|
final String deleteTemplateButton;
|
||||||
|
|
||||||
|
/// The label on the button to edit a template
|
||||||
|
final String editTemplateButton;
|
||||||
|
|
||||||
/// The title for the day template edit screen
|
/// The title for the day template edit screen
|
||||||
final String dayTemplateTitle;
|
final String dayTemplateTitle;
|
||||||
|
|
||||||
|
@ -291,6 +298,9 @@ class AvailabilityTranslations {
|
||||||
/// The text on the add button
|
/// The text on the add button
|
||||||
final String addButton;
|
final String addButton;
|
||||||
|
|
||||||
|
/// The text on the next button
|
||||||
|
final String nextButton;
|
||||||
|
|
||||||
/// The text on the confirm button in the confirmation dialog
|
/// The text on the confirm button in the confirmation dialog
|
||||||
final String confirmText;
|
final String confirmText;
|
||||||
|
|
||||||
|
@ -319,7 +329,7 @@ class AvailabilityTranslations {
|
||||||
final String Function(BuildContext, DateTime) weekDayAbbreviatedFormatter;
|
final String Function(BuildContext, DateTime) weekDayAbbreviatedFormatter;
|
||||||
|
|
||||||
/// Gets the weekday formatted as a string
|
/// Gets the weekday formatted as a string
|
||||||
///
|
///
|
||||||
/// The default implementation is the full name of the weekday in english
|
/// The default implementation is the full name of the weekday in english
|
||||||
final String Function(BuildContext, DateTime) weekDayFormatter;
|
final String Function(BuildContext, DateTime) weekDayFormatter;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import "package:flutter_availability/src/service/availability_service.dart";
|
||||||
import "package:flutter_availability/src/ui/screens/availability_modification.dart";
|
import "package:flutter_availability/src/ui/screens/availability_modification.dart";
|
||||||
import "package:flutter_availability/src/ui/screens/template_day_modification.dart";
|
import "package:flutter_availability/src/ui/screens/template_day_modification.dart";
|
||||||
import "package:flutter_availability/src/ui/screens/template_overview.dart";
|
import "package:flutter_availability/src/ui/screens/template_overview.dart";
|
||||||
|
import "package:flutter_availability/src/ui/screens/template_week_modification.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -17,6 +18,17 @@ MaterialPageRoute homePageRoute(VoidCallback onExit) => MaterialPageRoute(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future _routeToTemplateEditScreen(
|
||||||
|
BuildContext context,
|
||||||
|
AvailabilityTemplateModel template,
|
||||||
|
) async {
|
||||||
|
if (template.templateType == AvailabilityTemplateType.day) {
|
||||||
|
await Navigator.of(context).push(templateEditDayRoute(template));
|
||||||
|
} else if (template.templateType == AvailabilityTemplateType.week) {
|
||||||
|
await Navigator.of(context).push(templateEditWeekRoute(template));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
MaterialPageRoute<AvailabilityTemplateModel?> templateOverviewRoute({
|
MaterialPageRoute<AvailabilityTemplateModel?> templateOverviewRoute({
|
||||||
bool allowSelection = false,
|
bool allowSelection = false,
|
||||||
|
@ -24,21 +36,22 @@ MaterialPageRoute<AvailabilityTemplateModel?> templateOverviewRoute({
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => AvailabilityTemplateOverview(
|
builder: (context) => AvailabilityTemplateOverview(
|
||||||
onExit: () => Navigator.of(context).pop(),
|
onExit: () => Navigator.of(context).pop(),
|
||||||
onEditTemplate: (template) async {
|
onEditTemplate: (template) async =>
|
||||||
if (template.templateType == AvailabilityTemplateType.day) {
|
_routeToTemplateEditScreen(context, template),
|
||||||
await Navigator.of(context).push(templateEditDayRoute(template));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onAddTemplate: (type) async {
|
onAddTemplate: (type) async {
|
||||||
if (type == AvailabilityTemplateType.day) {
|
if (type == AvailabilityTemplateType.day) {
|
||||||
await Navigator.of(context).push(templateEditDayRoute(null));
|
await Navigator.of(context).push(templateEditDayRoute(null));
|
||||||
|
} else if (type == AvailabilityTemplateType.week) {
|
||||||
|
await Navigator.of(context).push(templateEditWeekRoute(null));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSelectTemplate: (template) async {
|
||||||
|
if (allowSelection) {
|
||||||
|
Navigator.of(context).pop(template);
|
||||||
|
} else {
|
||||||
|
await _routeToTemplateEditScreen(context, template);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSelectTemplate: allowSelection
|
|
||||||
? (template) async {
|
|
||||||
Navigator.of(context).pop(template);
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -51,6 +64,15 @@ MaterialPageRoute templateEditDayRoute(AvailabilityTemplateModel? template) =>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
///
|
||||||
|
MaterialPageRoute templateEditWeekRoute(AvailabilityTemplateModel? template) =>
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => WeekTemplateModificationScreen(
|
||||||
|
template: template,
|
||||||
|
onExit: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
///
|
///
|
||||||
MaterialPageRoute availabilityViewRoute(
|
MaterialPageRoute availabilityViewRoute(
|
||||||
DateTimeRange dateRange,
|
DateTimeRange dateRange,
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/color_selection.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/template_name_input.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/template_time_break.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/template_week_day_selection.dart";
|
||||||
|
import "package:flutter_availability/src/ui/widgets/template_week_overview.dart";
|
||||||
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
|
|
||||||
|
/// Page for creating or editing a day template
|
||||||
|
class WeekTemplateModificationScreen extends StatefulWidget {
|
||||||
|
/// Constructor
|
||||||
|
const WeekTemplateModificationScreen({
|
||||||
|
required this.template,
|
||||||
|
required this.onExit,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The week template to edit or null if creating a new one
|
||||||
|
final AvailabilityTemplateModel? template;
|
||||||
|
|
||||||
|
/// Callback for when the user wants to navigate back
|
||||||
|
final VoidCallback onExit;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WeekTemplateModificationScreen> createState() =>
|
||||||
|
_WeekTemplateModificationScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WeekTemplateModificationScreenState
|
||||||
|
extends State<WeekTemplateModificationScreen> {
|
||||||
|
late int? _selectedColor;
|
||||||
|
late AvailabilityTemplateModel _template;
|
||||||
|
bool _editing = true;
|
||||||
|
WeekDay _selectedDay = WeekDay.monday;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_selectedColor = widget.template?.color;
|
||||||
|
_template = widget.template ??
|
||||||
|
AvailabilityTemplateModel(
|
||||||
|
userId: "1",
|
||||||
|
name: "",
|
||||||
|
color: 0,
|
||||||
|
templateType: AvailabilityTemplateType.week,
|
||||||
|
templateData: WeekTemplateData.forDays(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var theme = Theme.of(context);
|
||||||
|
var textTheme = theme.textTheme;
|
||||||
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
|
var service = availabilityScope.service;
|
||||||
|
var options = availabilityScope.options;
|
||||||
|
var translations = options.translations;
|
||||||
|
var spacing = options.spacing;
|
||||||
|
|
||||||
|
var weekTemplateDate = _template.templateData as WeekTemplateData;
|
||||||
|
var selectedDayData = weekTemplateDate.data[_selectedDay];
|
||||||
|
|
||||||
|
Future<void> onDeletePressed() async {
|
||||||
|
await service.deleteTemplate(_template);
|
||||||
|
widget.onExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onNextPressed() {
|
||||||
|
setState(() {
|
||||||
|
_editing = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onBackPressed() {
|
||||||
|
if (_editing) {
|
||||||
|
widget.onExit();
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_editing = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onSavePressed() async {
|
||||||
|
if (widget.template == null) {
|
||||||
|
await service.createTemplate(_template);
|
||||||
|
} else {
|
||||||
|
await service.updateTemplate(_template);
|
||||||
|
}
|
||||||
|
widget.onExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
var canSave = _template.name.isNotEmpty && _selectedColor != null;
|
||||||
|
var nextButton = options.primaryButtonBuilder(
|
||||||
|
context,
|
||||||
|
canSave ? onNextPressed : null,
|
||||||
|
Text(translations.nextButton),
|
||||||
|
);
|
||||||
|
|
||||||
|
var saveButton = options.primaryButtonBuilder(
|
||||||
|
context,
|
||||||
|
canSave ? onSavePressed : null,
|
||||||
|
Text(translations.saveButton),
|
||||||
|
);
|
||||||
|
|
||||||
|
var deleteButton = options.bigTextButtonBuilder(
|
||||||
|
context,
|
||||||
|
onDeletePressed,
|
||||||
|
Text(translations.deleteTemplateButton),
|
||||||
|
);
|
||||||
|
|
||||||
|
var previousButton = options.bigTextButtonBuilder(
|
||||||
|
context,
|
||||||
|
onBackPressed,
|
||||||
|
Text(translations.editTemplateButton),
|
||||||
|
);
|
||||||
|
|
||||||
|
var title = Center(
|
||||||
|
child: Text(
|
||||||
|
translations.weekTemplateTitle,
|
||||||
|
style: theme.textTheme.displaySmall,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var templateTitleSection = TemplateNameInput(
|
||||||
|
initialValue: _template.name,
|
||||||
|
onNameChanged: (name) {
|
||||||
|
setState(() {
|
||||||
|
_template = _template.copyWith(name: name);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
var colorSection = TemplateColorSelection(
|
||||||
|
selectedColor: _selectedColor,
|
||||||
|
onColorSelected: (color) {
|
||||||
|
setState(() {
|
||||||
|
_selectedColor = color;
|
||||||
|
_template = _template.copyWith(color: color);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
var editPage = [
|
||||||
|
_WeekTemplateSidePadding(child: templateTitleSection),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: spacing.sidePadding),
|
||||||
|
child: TemplateWeekDaySelection(
|
||||||
|
onDaySelected: (day) {
|
||||||
|
setState(() {
|
||||||
|
_selectedDay = WeekDay.values[day];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_WeekTemplateSidePadding(
|
||||||
|
child: TemplateTimeAndBreakSection(
|
||||||
|
dayData: selectedDayData != null
|
||||||
|
? ViewDayTemplateData.fromDayTemplateData(
|
||||||
|
selectedDayData,
|
||||||
|
)
|
||||||
|
: const ViewDayTemplateData(),
|
||||||
|
onDayDataChanged: (data) {
|
||||||
|
setState(() {
|
||||||
|
_template = _template.copyWith(
|
||||||
|
templateData:
|
||||||
|
// create a copy of the week template data
|
||||||
|
WeekTemplateData(
|
||||||
|
data: {
|
||||||
|
for (var entry in weekTemplateDate.data.entries)
|
||||||
|
entry.key: entry.value,
|
||||||
|
_selectedDay: data.toDayTemplateData(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_WeekTemplateSidePadding(child: colorSection),
|
||||||
|
];
|
||||||
|
|
||||||
|
var overviewPage = _WeekTemplateSidePadding(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Text(translations.templateTitleLabel, style: textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
child: 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),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
TemplateWeekOverview(
|
||||||
|
template: _template,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
var body = CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
SliverList.list(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 40),
|
||||||
|
_WeekTemplateSidePadding(child: title),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
if (_editing) ...[
|
||||||
|
...editPage,
|
||||||
|
] else ...[
|
||||||
|
overviewPage,
|
||||||
|
],
|
||||||
|
const SizedBox(height: 32),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SliverFillRemaining(
|
||||||
|
fillOverscroll: false,
|
||||||
|
hasScrollBody: false,
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: spacing.sidePadding,
|
||||||
|
).copyWith(
|
||||||
|
bottom: spacing.bottomButtonPadding,
|
||||||
|
),
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (_editing) ...[
|
||||||
|
nextButton,
|
||||||
|
] else ...[
|
||||||
|
saveButton,
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
previousButton,
|
||||||
|
],
|
||||||
|
if (widget.template != null) ...[
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
deleteButton,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return options.baseScreenBuilder(context, onBackPressed, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// One of the elements in the week template modification screen doesn't have a
|
||||||
|
/// padding around it that is why all the other elements are wrapped in
|
||||||
|
/// [_WeekTemplateSidePadding]
|
||||||
|
class _WeekTemplateSidePadding extends StatelessWidget {
|
||||||
|
const _WeekTemplateSidePadding({
|
||||||
|
required this.child,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var availabilityScope = AvailabilityScope.of(context);
|
||||||
|
var sidePadding = availabilityScope.options.spacing.sidePadding;
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: sidePadding),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,8 +37,7 @@ class CalendarGrid extends StatelessWidget {
|
||||||
var calendarDays =
|
var calendarDays =
|
||||||
_generateCalendarDays(month, days, selectedRange, colors, colorScheme);
|
_generateCalendarDays(month, days, selectedRange, colors, colorScheme);
|
||||||
|
|
||||||
var dayNames =
|
var dayNames = getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
|
||||||
getDaysOfTheWeekAsAbbreviatedStrings(translations, context);
|
|
||||||
|
|
||||||
var calendarDaysRow = Row(
|
var calendarDaysRow = Row(
|
||||||
children: List.generate(13, (index) {
|
children: List.generate(13, (index) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ import "package:flutter_availability/src/ui/models/view_template_daydata.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/template_time_selection.dart";
|
import "package:flutter_availability/src/ui/widgets/template_time_selection.dart";
|
||||||
|
|
||||||
/// Section for selecting the time and breaks for a single day
|
/// Section for selecting the time and breaks for a single day
|
||||||
class TemplateTimeAndBreakSection extends StatelessWidget {
|
class TemplateTimeAndBreakSection extends StatelessWidget {
|
||||||
///
|
///
|
||||||
const TemplateTimeAndBreakSection({
|
const TemplateTimeAndBreakSection({
|
||||||
|
|
Loading…
Reference in a new issue