mirror of
https://github.com/Iconica-Development/flutter_availability.git
synced 2025-05-19 05:03: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,
|
||||
);
|
||||
|
||||
availability.validate();
|
||||
|
||||
await dataInterface.setAvailabilitiesForUser(
|
||||
userId: userId,
|
||||
availability: updatedAvailability,
|
||||
|
@ -163,6 +165,9 @@ class AvailabilityService {
|
|||
var updatedTemplate = template.copyWith(
|
||||
userId: userId,
|
||||
);
|
||||
|
||||
template.validate();
|
||||
|
||||
await dataInterface.createTemplateForUser(
|
||||
userId,
|
||||
updatedTemplate,
|
||||
|
@ -171,6 +176,8 @@ class AvailabilityService {
|
|||
|
||||
/// Updates a template
|
||||
Future<void> updateTemplate(AvailabilityTemplateModel template) async {
|
||||
template.validate();
|
||||
|
||||
await dataInterface.updateTemplateForUser(
|
||||
userId,
|
||||
template.id!,
|
||||
|
|
|
@ -1,8 +1,31 @@
|
|||
/// A set of errors
|
||||
enum AvailabilityError {
|
||||
/// Error identifier when checking break mismatch
|
||||
breakMismatchWithAvailability(
|
||||
"The break needs to be within the timeframe of the availability",
|
||||
/// Error identifier for when a submitted break ends before the availability
|
||||
/// starts
|
||||
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);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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/ui/view_models/availability_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/pause_selection.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";
|
||||
|
||||
/// Screen for modifying the availabilities for a specific daterange
|
||||
|
@ -78,18 +78,35 @@ class _AvailabilitiesModificationScreenState
|
|||
widget.onExit();
|
||||
return;
|
||||
}
|
||||
if (_availabilityViewModel.templateSelected) {
|
||||
await service.applyTemplate(
|
||||
template: _availabilityViewModel.templates.first,
|
||||
range: widget.dateRange,
|
||||
);
|
||||
} else {
|
||||
await service.createAvailability(
|
||||
availability: _availabilityViewModel.toModel(),
|
||||
range: widget.dateRange,
|
||||
|
||||
AvailabilityError? error;
|
||||
try {
|
||||
if (_availabilityViewModel.templateSelected) {
|
||||
await service.applyTemplate(
|
||||
template: _availabilityViewModel.templates.first,
|
||||
range: widget.dateRange,
|
||||
);
|
||||
} else {
|
||||
await service.createAvailability(
|
||||
availability: _availabilityViewModel.toModel(),
|
||||
range: widget.dateRange,
|
||||
);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
widget.onExit();
|
||||
}
|
||||
|
||||
Future<void> onClickSave() async {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
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/template_daydata_view_model.dart";
|
||||
import "package:flutter_availability/src/ui/widgets/base_page.dart";
|
||||
|
@ -70,12 +71,32 @@ class _DayTemplateModificationScreenState
|
|||
|
||||
Future<void> onSavePressed() async {
|
||||
var template = _viewModel.toTemplate();
|
||||
if (widget.template == null) {
|
||||
await service.createTemplate(template);
|
||||
} else {
|
||||
await service.updateTemplate(template);
|
||||
AvailabilityError? error;
|
||||
try {
|
||||
if (widget.template == null) {
|
||||
await service.createTemplate(template);
|
||||
} else {
|
||||
await service.updateTemplate(template);
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
widget.onExit();
|
||||
}
|
||||
|
||||
var canSave = _viewModel.canSave;
|
||||
|
|
|
@ -23,11 +23,13 @@ class DefaultErrorDisplayDialog extends StatelessWidget {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
children: [
|
||||
Text(_error.name),
|
||||
const SizedBox(height: 20),
|
||||
Text(_error.description),
|
||||
],
|
||||
Widget build(BuildContext context) => Scaffold(
|
||||
body: Column(
|
||||
children: [
|
||||
Text(_error.name),
|
||||
const SizedBox(height: 20),
|
||||
Text(_error.description),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,16 @@
|
|||
// ignore_for_file: Generated using data class generator
|
||||
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
|
||||
class AvailabilityModel {
|
||||
/// Creates a new availability
|
||||
|
@ -81,6 +91,17 @@ class AvailabilityModel {
|
|||
}
|
||||
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]
|
||||
|
@ -177,4 +198,19 @@ class AvailabilityBreakModel {
|
|||
endTime.hour == other.endTime.hour &&
|
||||
endTime.minute == other.endTime.minute &&
|
||||
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/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
|
||||
enum AvailabilityTemplateType {
|
||||
/// 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
|
||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay) =>
|
||||
templateData.getEndTimeForDayOfWeek(weekDay);
|
||||
|
||||
/// Verify the validity of this template
|
||||
void validate() {
|
||||
templateData.validate();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
DateTime? getEndTimeForDayOfWeek(WeekDay weekDay);
|
||||
|
||||
/// Verify the validity of the data in this template
|
||||
void validate();
|
||||
}
|
||||
|
||||
/// 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
|
||||
@override
|
||||
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
|
||||
|
@ -371,6 +397,25 @@ class DayTemplateData implements TemplateData {
|
|||
/// Get the end time for the specified day in the week for this template
|
||||
@override
|
||||
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) {
|
||||
|
|
Loading…
Reference in a new issue