feat: Update geofence handling to allow checkout when checked in and show verified banner for overridden geofence

This commit is contained in:
Achintha Isuru
2026-03-13 20:43:51 -04:00
parent ea078eaa02
commit 1bbd306ca0
3 changed files with 19 additions and 7 deletions

View File

@@ -119,9 +119,10 @@ class ClockInActionSection extends StatelessWidget {
final bool hasCoordinates = selectedShift?.latitude != null && final bool hasCoordinates = selectedShift?.latitude != null &&
selectedShift?.longitude != null; selectedShift?.longitude != null;
// Disable swipe when the shift has coordinates and the user is // Geofence only gates clock-in, never clock-out. When already
// not verified, not timed out, and has not overridden the geofence. // checked in the swipe must always be enabled for checkout.
final bool isGeofenceBlocking = hasCoordinates && final bool isGeofenceBlocking = !isCheckedIn &&
hasCoordinates &&
!geofenceState.isLocationVerified && !geofenceState.isLocationVerified &&
!geofenceState.isLocationTimedOut && !geofenceState.isLocationTimedOut &&
!geofenceState.isGeofenceOverridden; !geofenceState.isGeofenceOverridden;
@@ -135,7 +136,7 @@ class ClockInActionSection extends StatelessWidget {
SwipeToCheckIn( SwipeToCheckIn(
isCheckedIn: isCheckedIn, isCheckedIn: isCheckedIn,
mode: checkInMode, mode: checkInMode,
isDisabled: isCheckedIn || isGeofenceBlocking, isDisabled: isGeofenceBlocking,
isLoading: isActionInProgress, isLoading: isActionInProgress,
onCheckIn: () => _handleCheckIn(context), onCheckIn: () => _handleCheckIn(context),
onCheckOut: () => _handleCheckOut(context), onCheckOut: () => _handleCheckOut(context),

View File

@@ -20,8 +20,12 @@ class GeofenceOverrideModal extends StatefulWidget {
/// Shows the override modal as a bottom sheet. /// Shows the override modal as a bottom sheet.
/// ///
/// Requires [ClockInBloc] to be available in [context]. /// Requires [GeofenceBloc] to be available in [context].
static void show(BuildContext context) { static void show(BuildContext context) {
// Capture the bloc before opening the sheet so we don't access a
// deactivated widget's ancestor inside the builder.
final GeofenceBloc bloc = ReadContext(context).read<GeofenceBloc>();
showModalBottomSheet<void>( showModalBottomSheet<void>(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@@ -31,7 +35,7 @@ class GeofenceOverrideModal extends StatefulWidget {
), ),
), ),
builder: (_) => BlocProvider<GeofenceBloc>.value( builder: (_) => BlocProvider<GeofenceBloc>.value(
value: ReadContext(context).read<GeofenceBloc>(), value: bloc,
child: const GeofenceOverrideModal(), child: const GeofenceOverrideModal(),
), ),
); );
@@ -111,6 +115,7 @@ class _GeofenceOverrideModalState extends State<GeofenceOverrideModal> {
GeofenceOverrideApproved(notes: justification), GeofenceOverrideApproved(notes: justification),
); );
Modular.to.popSafe(); Navigator.of(context).pop();
//Modular.to.popSafe();
} }
} }

View File

@@ -35,6 +35,12 @@ class GeofenceStatusBanner extends StatelessWidget {
/// Determines which banner variant to display based on the current state. /// Determines which banner variant to display based on the current state.
Widget _buildBannerForState(GeofenceState state) { Widget _buildBannerForState(GeofenceState state) {
// If the worker overrode the geofence check, show the verified banner
// instead of any failure state — the justification has been recorded.
if (state.isGeofenceOverridden) {
return const VerifiedBanner();
}
// 1. Location services disabled. // 1. Location services disabled.
if (state.permissionStatus == LocationPermissionStatus.serviceDisabled || if (state.permissionStatus == LocationPermissionStatus.serviceDisabled ||
(state.isLocationTimedOut && !state.isLocationServiceEnabled)) { (state.isLocationTimedOut && !state.isLocationServiceEnabled)) {