feat(clock_in): add error handling support to check-in interactions
This commit is contained in:
@@ -16,6 +16,7 @@ abstract class CheckInInteraction {
|
||||
required bool isCheckedIn,
|
||||
required bool isDisabled,
|
||||
required bool isLoading,
|
||||
required bool hasError,
|
||||
required VoidCallback onCheckIn,
|
||||
required VoidCallback onCheckOut,
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ class NfcCheckInInteraction implements CheckInInteraction {
|
||||
required bool isCheckedIn,
|
||||
required bool isDisabled,
|
||||
required bool isLoading,
|
||||
required bool hasError,
|
||||
required VoidCallback onCheckIn,
|
||||
required VoidCallback onCheckOut,
|
||||
}) {
|
||||
|
||||
@@ -16,6 +16,7 @@ class SwipeCheckInInteraction implements CheckInInteraction {
|
||||
required bool isCheckedIn,
|
||||
required bool isDisabled,
|
||||
required bool isLoading,
|
||||
required bool hasError,
|
||||
required VoidCallback onCheckIn,
|
||||
required VoidCallback onCheckOut,
|
||||
}) {
|
||||
@@ -23,6 +24,7 @@ class SwipeCheckInInteraction implements CheckInInteraction {
|
||||
isCheckedIn: isCheckedIn,
|
||||
isDisabled: isDisabled,
|
||||
isLoading: isLoading,
|
||||
hasError: hasError,
|
||||
onCheckIn: onCheckIn,
|
||||
onCheckOut: onCheckOut,
|
||||
);
|
||||
|
||||
@@ -6,16 +6,15 @@ import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../../domain/validators/clock_in_validation_context.dart';
|
||||
import '../../domain/validators/validators/time_window_validator.dart';
|
||||
import '../bloc/clock_in/clock_in_bloc.dart';
|
||||
import '../bloc/clock_in/clock_in_event.dart';
|
||||
import '../bloc/geofence/geofence_bloc.dart';
|
||||
import '../bloc/geofence/geofence_state.dart';
|
||||
import '../../domain/validators/clock_in_validation_context.dart';
|
||||
import '../../domain/validators/validators/time_window_validator.dart';
|
||||
import '../strategies/check_in_interaction.dart';
|
||||
import '../strategies/nfc_check_in_interaction.dart';
|
||||
import '../strategies/swipe_check_in_interaction.dart';
|
||||
import 'early_check_in_banner.dart';
|
||||
import 'geofence_status_banner/geofence_status_banner.dart';
|
||||
import 'lunch_break_modal.dart';
|
||||
import 'no_shifts_banner.dart';
|
||||
@@ -35,6 +34,7 @@ class ClockInActionSection extends StatelessWidget {
|
||||
required this.checkOutTime,
|
||||
required this.checkInMode,
|
||||
required this.isActionInProgress,
|
||||
this.hasError = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -60,6 +60,9 @@ class ClockInActionSection extends StatelessWidget {
|
||||
/// Whether a check-in or check-out action is currently in progress.
|
||||
final bool isActionInProgress;
|
||||
|
||||
/// Whether the last action attempt resulted in an error.
|
||||
final bool hasError;
|
||||
|
||||
/// Resolves the [CheckInInteraction] for the current mode.
|
||||
///
|
||||
/// Falls back to [SwipeCheckInInteraction] if the mode is unrecognized.
|
||||
@@ -81,21 +84,21 @@ class ClockInActionSection extends StatelessWidget {
|
||||
|
||||
/// Builds the action widget for an active (not completed) shift.
|
||||
Widget _buildActiveShiftAction(BuildContext context) {
|
||||
if (!isCheckedIn && !_isCheckInAllowed(selectedShift!)) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const GeofenceStatusBanner(),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
EarlyCheckInBanner(
|
||||
availabilityTime: _getAvailabilityTimeText(
|
||||
selectedShift!,
|
||||
context,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
// if (!isCheckedIn && !_isCheckInAllowed(selectedShift!)) {
|
||||
// return Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: <Widget>[
|
||||
// const GeofenceStatusBanner(),
|
||||
// const SizedBox(height: UiConstants.space3),
|
||||
// EarlyCheckInBanner(
|
||||
// availabilityTime: _getAvailabilityTimeText(
|
||||
// selectedShift!,
|
||||
// context,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
return BlocBuilder<GeofenceBloc, GeofenceState>(
|
||||
builder: (BuildContext context, GeofenceState geofenceState) {
|
||||
@@ -119,6 +122,7 @@ class ClockInActionSection extends StatelessWidget {
|
||||
isCheckedIn: isCheckedIn,
|
||||
isDisabled: isGeofenceBlocking,
|
||||
isLoading: isActionInProgress,
|
||||
hasError: hasError,
|
||||
onCheckIn: () => _handleCheckIn(context),
|
||||
onCheckOut: () => _handleCheckOut(context),
|
||||
),
|
||||
|
||||
@@ -111,6 +111,7 @@ class _ClockInBodyState extends State<ClockInBody> {
|
||||
checkInMode: state.checkInMode,
|
||||
isActionInProgress:
|
||||
state.status == ClockInStatus.actionInProgress,
|
||||
hasError: state.status == ClockInStatus.failure,
|
||||
),
|
||||
|
||||
// checked-in banner (only when checked in to the selected shift)
|
||||
|
||||
@@ -17,6 +17,7 @@ class SwipeToCheckIn extends StatefulWidget {
|
||||
this.isLoading = false,
|
||||
this.isCheckedIn = false,
|
||||
this.isDisabled = false,
|
||||
this.hasError = false,
|
||||
});
|
||||
|
||||
/// Called when the user completes the swipe to check in.
|
||||
@@ -34,6 +35,9 @@ class SwipeToCheckIn extends StatefulWidget {
|
||||
/// Whether the slider is disabled (e.g. geofence blocking).
|
||||
final bool isDisabled;
|
||||
|
||||
/// Whether an error occurred during the last action attempt.
|
||||
final bool hasError;
|
||||
|
||||
@override
|
||||
State<SwipeToCheckIn> createState() => _SwipeToCheckInState();
|
||||
}
|
||||
@@ -54,9 +58,11 @@ class _SwipeToCheckInState extends State<SwipeToCheckIn>
|
||||
_dragValue = 0.0;
|
||||
});
|
||||
}
|
||||
// Reset on error: loading finished but check-in state didn't change.
|
||||
if (oldWidget.isLoading && !widget.isLoading &&
|
||||
widget.isCheckedIn == oldWidget.isCheckedIn && _isComplete) {
|
||||
// Reset on error: loading finished without state change, or validation error.
|
||||
if (_isComplete &&
|
||||
widget.isCheckedIn == oldWidget.isCheckedIn &&
|
||||
((oldWidget.isLoading && !widget.isLoading) ||
|
||||
(!oldWidget.hasError && widget.hasError))) {
|
||||
setState(() {
|
||||
_isComplete = false;
|
||||
_dragValue = 0.0;
|
||||
|
||||
Reference in New Issue
Block a user