mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 13:13:44 +02:00
feat: validate availabilities and templates locally before submitting
This commit is contained in:
parent
c20a1a603c
commit
ec47ed4696
7 changed files with 176 additions and 25 deletions
|
@ -42,6 +42,8 @@ class AvailabilityService {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
availability.validate();
|
||||||
|
|
||||||
await dataInterface.setAvailabilitiesForUser(
|
await dataInterface.setAvailabilitiesForUser(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
availability: updatedAvailability,
|
availability: updatedAvailability,
|
||||||
|
@ -163,6 +165,9 @@ class AvailabilityService {
|
||||||
var updatedTemplate = template.copyWith(
|
var updatedTemplate = template.copyWith(
|
||||||
userId: userId,
|
userId: userId,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
template.validate();
|
||||||
|
|
||||||
await dataInterface.createTemplateForUser(
|
await dataInterface.createTemplateForUser(
|
||||||
userId,
|
userId,
|
||||||
updatedTemplate,
|
updatedTemplate,
|
||||||
|
@ -171,6 +176,8 @@ class AvailabilityService {
|
||||||
|
|
||||||
/// Updates a template
|
/// Updates a template
|
||||||
Future<void> updateTemplate(AvailabilityTemplateModel template) async {
|
Future<void> updateTemplate(AvailabilityTemplateModel template) async {
|
||||||
|
template.validate();
|
||||||
|
|
||||||
await dataInterface.updateTemplateForUser(
|
await dataInterface.updateTemplateForUser(
|
||||||
userId,
|
userId,
|
||||||
template.id!,
|
template.id!,
|
||||||
|
|
|
@ -1,8 +1,31 @@
|
||||||
/// A set of errors
|
/// A set of errors
|
||||||
enum AvailabilityError {
|
enum AvailabilityError {
|
||||||
/// Error identifier when checking break mismatch
|
/// Error identifier for when a submitted break ends before the availability
|
||||||
breakMismatchWithAvailability(
|
/// starts
|
||||||
"The break needs to be within the timeframe of the availability",
|
breakEndBeforeStart(
|
||||||
|
"The break ends before the start of the break",
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Error identifier for when the submitted duration of a break is longer than
|
||||||
|
/// the difference between the start and end time
|
||||||
|
breakSubmittedDurationTooLong(
|
||||||
|
"The submitted duration is longer than the difference between the start "
|
||||||
|
"and end time",
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Error identifier for when the end of the day is before the start
|
||||||
|
endBeforeStart("The end of the day is before the start"),
|
||||||
|
|
||||||
|
/// Error identifier for when one of the submitted breaks starts before the
|
||||||
|
/// availability
|
||||||
|
templateBreakBeforeStart(
|
||||||
|
"One of the submitted breaks starts before the start of the availability",
|
||||||
|
),
|
||||||
|
|
||||||
|
/// Error identifier for when one of the submitted breaks ends after the
|
||||||
|
/// availability
|
||||||
|
templateBreakAfterEnd(
|
||||||
|
"One of the submitted breaks ends after the end of the availability",
|
||||||
);
|
);
|
||||||
|
|
||||||
const AvailabilityError(this.description);
|
const AvailabilityError(this.description);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/flutter_availability.dart";
|
||||||
import "package:flutter_availability/src/service/availability_service.dart";
|
import "package:flutter_availability/src/service/availability_service.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/availability_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/availability_view_model.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/break_view_model.dart";
|
||||||
|
@ -8,7 +9,6 @@ import "package:flutter_availability/src/ui/widgets/availabillity_time_selection
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.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/util/scope.dart";
|
import "package:flutter_availability/src/util/scope.dart";
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
|
||||||
import "package:flutter_hooks/flutter_hooks.dart";
|
import "package:flutter_hooks/flutter_hooks.dart";
|
||||||
|
|
||||||
/// Screen for modifying the availabilities for a specific daterange
|
/// Screen for modifying the availabilities for a specific daterange
|
||||||
|
@ -78,6 +78,9 @@ class _AvailabilitiesModificationScreenState
|
||||||
widget.onExit();
|
widget.onExit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AvailabilityError? error;
|
||||||
|
try {
|
||||||
if (_availabilityViewModel.templateSelected) {
|
if (_availabilityViewModel.templateSelected) {
|
||||||
await service.applyTemplate(
|
await service.applyTemplate(
|
||||||
template: _availabilityViewModel.templates.first,
|
template: _availabilityViewModel.templates.first,
|
||||||
|
@ -90,6 +93,20 @@ class _AvailabilitiesModificationScreenState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
widget.onExit();
|
widget.onExit();
|
||||||
|
} on AvailabilityEndBeforeStartException {
|
||||||
|
error = AvailabilityError.endBeforeStart;
|
||||||
|
} on BreakEndBeforeStartException {
|
||||||
|
error = AvailabilityError.breakEndBeforeStart;
|
||||||
|
} on BreakSubmittedDurationTooLongException {
|
||||||
|
error = AvailabilityError.breakSubmittedDurationTooLong;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != null && context.mounted) {
|
||||||
|
await options.errorDisplayBuilder(
|
||||||
|
context,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> onClickSave() async {
|
Future<void> onClickSave() async {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import "package:flutter/material.dart";
|
import "package:flutter/material.dart";
|
||||||
|
import "package:flutter_availability/src/service/errors.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/day_template_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/day_template_view_model.dart";
|
||||||
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
|
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
|
||||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||||
|
@ -70,12 +71,32 @@ class _DayTemplateModificationScreenState
|
||||||
|
|
||||||
Future<void> onSavePressed() async {
|
Future<void> onSavePressed() async {
|
||||||
var template = _viewModel.toTemplate();
|
var template = _viewModel.toTemplate();
|
||||||
|
AvailabilityError? error;
|
||||||
|
try {
|
||||||
if (widget.template == null) {
|
if (widget.template == null) {
|
||||||
await service.createTemplate(template);
|
await service.createTemplate(template);
|
||||||
} else {
|
} else {
|
||||||
await service.updateTemplate(template);
|
await service.updateTemplate(template);
|
||||||
}
|
}
|
||||||
widget.onExit();
|
widget.onExit();
|
||||||
|
} on BreakEndBeforeStartException {
|
||||||
|
error = AvailabilityError.breakEndBeforeStart;
|
||||||
|
} on BreakSubmittedDurationTooLongException {
|
||||||
|
error = AvailabilityError.breakSubmittedDurationTooLong;
|
||||||
|
} on TemplateEndBeforeStartException {
|
||||||
|
error = AvailabilityError.endBeforeStart;
|
||||||
|
} on TemplateBreakBeforeStartException {
|
||||||
|
error = AvailabilityError.templateBreakBeforeStart;
|
||||||
|
} on TemplateBreakAfterEndException {
|
||||||
|
error = AvailabilityError.templateBreakAfterEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != null && context.mounted) {
|
||||||
|
await options.errorDisplayBuilder(
|
||||||
|
context,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var canSave = _viewModel.canSave;
|
var canSave = _viewModel.canSave;
|
||||||
|
|
|
@ -23,11 +23,13 @@ class DefaultErrorDisplayDialog extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => Column(
|
Widget build(BuildContext context) => Scaffold(
|
||||||
|
body: Column(
|
||||||
children: [
|
children: [
|
||||||
Text(_error.name),
|
Text(_error.name),
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
Text(_error.description),
|
Text(_error.description),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
// ignore_for_file: Generated using data class generator
|
// ignore_for_file: Generated using data class generator
|
||||||
import "package:flutter_availability_data_interface/src/utils.dart";
|
import "package:flutter_availability_data_interface/src/utils.dart";
|
||||||
|
|
||||||
|
/// Exception thrown when the end is before the start
|
||||||
|
class BreakEndBeforeStartException implements Exception {}
|
||||||
|
|
||||||
|
/// Exception thrown when the submitted duration is longer than the difference
|
||||||
|
/// between the start and end time
|
||||||
|
class BreakSubmittedDurationTooLongException implements Exception {}
|
||||||
|
|
||||||
|
/// Exception thrown when the end is before the start
|
||||||
|
class AvailabilityEndBeforeStartException implements Exception {}
|
||||||
|
|
||||||
/// A model defining the data structure for an availability
|
/// A model defining the data structure for an availability
|
||||||
class AvailabilityModel {
|
class AvailabilityModel {
|
||||||
/// Creates a new availability
|
/// Creates a new availability
|
||||||
|
@ -81,6 +91,17 @@ class AvailabilityModel {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verify the validity of this availability
|
||||||
|
void validate() {
|
||||||
|
if (startDate.compareTo(endDate) < 0) {
|
||||||
|
throw AvailabilityEndBeforeStartException();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var breakData in breaks) {
|
||||||
|
breakData.validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A model defining the structure of a break within an [AvailabilityModel]
|
/// A model defining the structure of a break within an [AvailabilityModel]
|
||||||
|
@ -177,4 +198,19 @@ class AvailabilityBreakModel {
|
||||||
endTime.hour == other.endTime.hour &&
|
endTime.hour == other.endTime.hour &&
|
||||||
endTime.minute == other.endTime.minute &&
|
endTime.minute == other.endTime.minute &&
|
||||||
submittedDuration == other.submittedDuration;
|
submittedDuration == other.submittedDuration;
|
||||||
|
|
||||||
|
/// Verify the validity of this break
|
||||||
|
void validate() {
|
||||||
|
if (endTime.compareTo(startTime) < 0) {
|
||||||
|
throw BreakEndBeforeStartException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submittedDuration != null) {
|
||||||
|
var calculatedDuration = endTime.difference(startTime);
|
||||||
|
|
||||||
|
if (calculatedDuration < submittedDuration!) {
|
||||||
|
throw BreakSubmittedDurationTooLongException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,17 @@
|
||||||
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
|
||||||
import "package:flutter_availability_data_interface/src/models/availability.dart";
|
import "package:flutter_availability_data_interface/src/models/availability.dart";
|
||||||
|
|
||||||
|
/// Exception thrown when the end is before the start
|
||||||
|
class TemplateEndBeforeStartException implements Exception {}
|
||||||
|
|
||||||
|
/// Exception thrown when the start of a break is before the start of the
|
||||||
|
/// availability
|
||||||
|
class TemplateBreakBeforeStartException implements Exception {}
|
||||||
|
|
||||||
|
/// Exception thrown when the end of a break is after the end of the
|
||||||
|
/// availability
|
||||||
|
class TemplateBreakAfterEndException implements Exception {}
|
||||||
|
|
||||||
/// A limited set of different availability template types
|
/// A limited set of different availability template types
|
||||||
enum AvailabilityTemplateType {
|
enum AvailabilityTemplateType {
|
||||||
/// A template that applies to any day, regardless of when it is.
|
/// A template that applies to any day, regardless of when it is.
|
||||||
|
@ -103,6 +114,11 @@ class AvailabilityTemplateModel {
|
||||||
/// Get the end time for the specified day in the week for this template
|
/// Get the end time for the specified day in the week for this template
|
||||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) =>
|
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) =>
|
||||||
templateData.getEndTimeForDayOfWeek(weekDay);
|
templateData.getEndTimeForDayOfWeek(weekDay);
|
||||||
|
|
||||||
|
/// Verify the validity of this template
|
||||||
|
void validate() {
|
||||||
|
templateData.validate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used as the key for defining week-based templates
|
/// Used as the key for defining week-based templates
|
||||||
|
@ -167,6 +183,9 @@ abstract interface class TemplateData {
|
||||||
|
|
||||||
/// Get the end time for the specified day in the week for this template
|
/// Get the end time for the specified day in the week for this template
|
||||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay);
|
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay);
|
||||||
|
|
||||||
|
/// Verify the validity of the data in this template
|
||||||
|
void validate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A week based template data structure
|
/// A week based template data structure
|
||||||
|
@ -261,6 +280,13 @@ class WeekTemplateData implements TemplateData {
|
||||||
/// Get the end time for the specified day in the week for this template
|
/// Get the end time for the specified day in the week for this template
|
||||||
@override
|
@override
|
||||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) => _data[weekDay]?.endTime;
|
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) => _data[weekDay]?.endTime;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void validate() {
|
||||||
|
for (var dayData in _data.entries) {
|
||||||
|
dayData.value.validate();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A day based template data structure
|
/// A day based template data structure
|
||||||
|
@ -371,6 +397,25 @@ class DayTemplateData implements TemplateData {
|
||||||
/// Get the end time for the specified day in the week for this template
|
/// Get the end time for the specified day in the week for this template
|
||||||
@override
|
@override
|
||||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) => endTime;
|
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) => endTime;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void validate() {
|
||||||
|
if (endTime.compareTo(startTime) < 0) {
|
||||||
|
throw TemplateEndBeforeStartException();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var breakData in breaks) {
|
||||||
|
breakData.validate();
|
||||||
|
|
||||||
|
if (breakData.startTime.compareTo(startTime) < 0) {
|
||||||
|
throw TemplateBreakBeforeStartException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (breakData.endTime.compareTo(endTime) > 0) {
|
||||||
|
throw TemplateBreakAfterEndException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DateTime> _getDatesBetween(DateTime startDate, DateTime endDate) {
|
List<DateTime> _getDatesBetween(DateTime startDate, DateTime endDate) {
|
||||||
|
|
Loading…
Reference in a new issue