feat: integrate ClockInPageLoaded event to initialize state on ClockInBloc

This commit is contained in:
Achintha Isuru
2026-01-30 16:49:10 -05:00
parent f1ccc97fae
commit 9038d6533e
2 changed files with 405 additions and 398 deletions

View File

@@ -33,6 +33,8 @@ class ClockInBloc extends Bloc<ClockInEvent, ClockInState> {
on<CheckInRequested>(_onCheckIn); on<CheckInRequested>(_onCheckIn);
on<CheckOutRequested>(_onCheckOut); on<CheckOutRequested>(_onCheckOut);
on<CheckInModeChanged>(_onModeChanged); on<CheckInModeChanged>(_onModeChanged);
add(ClockInPageLoaded());
} }
AttendanceStatus _mapToStatus(Map<String, dynamic> map) { AttendanceStatus _mapToStatus(Map<String, dynamic> map) {

View File

@@ -1,18 +1,19 @@
import 'package:design_system/design_system.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_modular/flutter_modular.dart'; import 'package:flutter_modular/flutter_modular.dart';
import 'package:lucide_icons/lucide_icons.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:krow_domain/krow_domain.dart'; import 'package:lucide_icons/lucide_icons.dart';
import '../theme/app_colors.dart';
import '../bloc/clock_in_bloc.dart'; import '../bloc/clock_in_bloc.dart';
import '../bloc/clock_in_event.dart'; import '../bloc/clock_in_event.dart';
import '../bloc/clock_in_state.dart'; import '../bloc/clock_in_state.dart';
import '../theme/app_colors.dart';
import '../widgets/attendance_card.dart'; import '../widgets/attendance_card.dart';
import '../widgets/date_selector.dart';
import '../widgets/swipe_to_check_in.dart';
import '../widgets/lunch_break_modal.dart';
import '../widgets/commute_tracker.dart'; import '../widgets/commute_tracker.dart';
import '../widgets/date_selector.dart';
import '../widgets/lunch_break_modal.dart';
import '../widgets/swipe_to_check_in.dart';
class ClockInPage extends StatefulWidget { class ClockInPage extends StatefulWidget {
const ClockInPage({super.key}); const ClockInPage({super.key});
@@ -28,23 +29,24 @@ class _ClockInPageState extends State<ClockInPage> {
void initState() { void initState() {
super.initState(); super.initState();
_bloc = Modular.get<ClockInBloc>(); _bloc = Modular.get<ClockInBloc>();
_bloc.add(ClockInPageLoaded());
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider.value( return BlocProvider<ClockInBloc>.value(
value: _bloc, value: _bloc,
child: BlocConsumer<ClockInBloc, ClockInState>( child: BlocConsumer<ClockInBloc, ClockInState>(
listener: (context, state) { listener: (context, state) {
if (state.status == ClockInStatus.failure && state.errorMessage != null) { if (state.status == ClockInStatus.failure &&
ScaffoldMessenger.of(context).showSnackBar( state.errorMessage != null) {
SnackBar(content: Text(state.errorMessage!)), ScaffoldMessenger.of(
); context,
).showSnackBar(SnackBar(content: Text(state.errorMessage!)));
} }
}, },
builder: (context, state) { builder: (context, state) {
if (state.status == ClockInStatus.loading && state.todayShift == null) { if (state.status == ClockInStatus.loading &&
state.todayShift == null) {
return const Scaffold( return const Scaffold(
body: Center(child: CircularProgressIndicator()), body: Center(child: CircularProgressIndicator()),
); );
@@ -64,416 +66,408 @@ class _ClockInPageState extends State<ClockInPage> {
: '--:-- --'; : '--:-- --';
return Scaffold( return Scaffold(
backgroundColor: Colors.transparent, appBar: UiAppBar(
body: Container( titleWidget: Text(
decoration: const BoxDecoration( 'Clock In to your Shift',
gradient: LinearGradient( style: UiTypography.title1m.textPrimary,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0xFFF8FAFC), // slate-50
Colors.white,
],
),
), ),
child: SafeArea( showBackButton: false,
child: SingleChildScrollView( centerTitle: false,
padding: const EdgeInsets.only(bottom: 100), ),
child: Column( body: SafeArea(
crossAxisAlignment: CrossAxisAlignment.start, child: SingleChildScrollView(
children: [ padding: const EdgeInsets.only(
_buildHeader(), bottom: UiConstants.space24,
top: UiConstants.space6,
Padding( ),
padding: const EdgeInsets.symmetric(horizontal: 20), child: Column(
child: Column( crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: <Widget>[
// Commute Tracker (shows before date selector when applicable) Padding(
if (todayShift != null) padding: const EdgeInsets.symmetric(horizontal: 20),
CommuteTracker( child: Column(
shift: todayShift, crossAxisAlignment: CrossAxisAlignment.start,
hasLocationConsent: false, // Mock value children: [
isCommuteModeOn: false, // Mock value // Commute Tracker (shows before date selector when applicable)
distanceMeters: 500, // Mock value for demo if (todayShift != null)
etaMinutes: 8, // Mock value for demo CommuteTracker(
), shift: todayShift,
// Date Selector hasLocationConsent: false, // Mock value
DateSelector( isCommuteModeOn: false, // Mock value
selectedDate: state.selectedDate, distanceMeters: 500, // Mock value for demo
onSelect: (date) => _bloc.add(DateSelected(date)), etaMinutes: 8, // Mock value for demo
shiftDates: [
DateFormat('yyyy-MM-dd').format(DateTime.now()),
],
), ),
const SizedBox(height: 20), // Date Selector
DateSelector(
selectedDate: state.selectedDate,
onSelect: (date) => _bloc.add(DateSelected(date)),
shiftDates: [
DateFormat('yyyy-MM-dd').format(DateTime.now()),
],
),
const SizedBox(height: 20),
// Today Attendance Section // Today Attendance Section
const Align( const Align(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
child: Text( child: Text(
"Today Attendance", "Today Attendance",
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.krowCharcoal, color: AppColors.krowCharcoal,
),
), ),
), ),
const SizedBox(height: 12), ),
GridView.count( const SizedBox(height: 12),
shrinkWrap: true, GridView.count(
physics: const NeverScrollableScrollPhysics(), shrinkWrap: true,
crossAxisCount: 2, physics: const NeverScrollableScrollPhysics(),
mainAxisSpacing: 12, crossAxisCount: 2,
crossAxisSpacing: 12, mainAxisSpacing: 12,
childAspectRatio: 1.0, crossAxisSpacing: 12,
childAspectRatio: 1.0,
children: [
AttendanceCard(
type: AttendanceType.checkin,
title: "Check In",
value: checkInStr,
subtitle: checkInTime != null
? "On Time"
: "Pending",
scheduledTime: "09:00 AM",
),
AttendanceCard(
type: AttendanceType.checkout,
title: "Check Out",
value: checkOutStr,
subtitle: checkOutTime != null
? "Go Home"
: "Pending",
scheduledTime: "05:00 PM",
),
AttendanceCard(
type: AttendanceType.breaks,
title: "Break Time",
// TODO: Connect to Data Connect when 'breakDuration' field is added to Shift schema.
value: "00:30 min",
subtitle: "Scheduled 00:30 min",
),
const AttendanceCard(
type: AttendanceType.days,
title: "Total Days",
// TODO: Connect to Data Connect when 'staffStats' or similar aggregation API is available.
// Currently avoided to prevent fetching full shift history for a simple count.
value: "28",
subtitle: "Working Days",
),
],
),
const SizedBox(height: 24),
// Your Activity Header
const Text(
"Your Activity",
textAlign: TextAlign.start,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: AppColors.krowCharcoal,
),
),
const SizedBox(height: 12),
// Check-in Mode Toggle
const Align(
alignment: Alignment.centerLeft,
child: Text(
"Check-in Method",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF334155), // slate-700
),
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // slate-100
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [ children: [
AttendanceCard( _buildModeTab(
type: AttendanceType.checkin, "Swipe",
title: "Check In", LucideIcons.mapPin,
value: checkInStr, 'swipe',
subtitle: checkInTime != null state.checkInMode,
? "On Time"
: "Pending",
scheduledTime: "09:00 AM",
),
AttendanceCard(
type: AttendanceType.checkout,
title: "Check Out",
value: checkOutStr,
subtitle: checkOutTime != null
? "Go Home"
: "Pending",
scheduledTime: "05:00 PM",
),
AttendanceCard(
type: AttendanceType.breaks,
title: "Break Time",
// TODO: Connect to Data Connect when 'breakDuration' field is added to Shift schema.
value: "00:30 min",
subtitle: "Scheduled 00:30 min",
),
const AttendanceCard(
type: AttendanceType.days,
title: "Total Days",
// TODO: Connect to Data Connect when 'staffStats' or similar aggregation API is available.
// Currently avoided to prevent fetching full shift history for a simple count.
value: "28",
subtitle: "Working Days",
), ),
// _buildModeTab("NFC Tap", LucideIcons.wifi, 'nfc', state.checkInMode),
], ],
), ),
const SizedBox(height: 24), ),
const SizedBox(height: 16),
// Your Activity Header // Selected Shift Info Card
// Your Activity Header if (todayShift != null)
Row( Container(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: const EdgeInsets.all(12),
children: [ margin: const EdgeInsets.only(bottom: 16),
const Text( decoration: BoxDecoration(
"Your Activity", color: Colors.white,
style: TextStyle( borderRadius: BorderRadius.circular(12),
fontSize: 18, border: Border.all(
fontWeight: FontWeight.bold, color: const Color(0xFFE2E8F0),
color: AppColors.krowCharcoal, ), // slate-200
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 2,
offset: const Offset(0, 1),
), ),
), ],
GestureDetector( ),
onTap: () { child: Row(
debugPrint('Navigating to shifts...'); crossAxisAlignment: CrossAxisAlignment.start,
}, children: [
child: Row( Expanded(
children: const [ child: Column(
Text( crossAxisAlignment:
"View all", CrossAxisAlignment.start,
children: [
const Text(
"TODAY'S SHIFT",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: AppColors.krowBlue,
letterSpacing: 0.5,
),
),
const SizedBox(height: 2),
Text(
todayShift.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(
0xFF1E293B,
), // slate-800
),
),
Text(
"${todayShift.clientName}${todayShift.location}",
style: const TextStyle(
fontSize: 12,
color: Color(
0xFF64748B,
), // slate-500
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Text(
"9:00 AM - 5:00 PM",
style: TextStyle( style: TextStyle(
color: AppColors.krowBlue, fontSize: 12,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
fontSize: 14, color: Color(0xFF475569), // slate-600
), ),
), ),
SizedBox(width: 4), Text(
Icon( "\$${todayShift.hourlyRate}/hr",
LucideIcons.chevronRight, style: const TextStyle(
size: 16, fontSize: 12,
color: AppColors.krowBlue, fontWeight: FontWeight.w600,
color: AppColors.krowBlue,
),
), ),
], ],
), ),
),
],
),
const SizedBox(height: 12),
// Check-in Mode Toggle
const Align(
alignment: Alignment.centerLeft,
child: Text(
"Check-in Method",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF334155), // slate-700
),
),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: const Color(0xFFF1F5F9), // slate-100
borderRadius: BorderRadius.circular(12),
),
child: Row(
children: [
_buildModeTab("Swipe", LucideIcons.mapPin, 'swipe', state.checkInMode),
// _buildModeTab("NFC Tap", LucideIcons.wifi, 'nfc', state.checkInMode),
], ],
), ),
), ),
const SizedBox(height: 16),
// Selected Shift Info Card // Swipe To Check In / Checked Out State / No Shift State
if (todayShift != null) if (todayShift != null && checkOutTime == null) ...[
Container( SwipeToCheckIn(
padding: const EdgeInsets.all(12), isCheckedIn: isCheckedIn,
margin: const EdgeInsets.only(bottom: 16), mode: state.checkInMode,
decoration: BoxDecoration( isLoading:
color: Colors.white, state.status ==
borderRadius: BorderRadius.circular(12), ClockInStatus.actionInProgress,
border: Border.all( onCheckIn: () async {
color: const Color(0xFFE2E8F0), // Show NFC dialog if mode is 'nfc'
), // slate-200 if (state.checkInMode == 'nfc') {
boxShadow: [ await _showNFCDialog(context);
BoxShadow( } else {
color: Colors.black.withOpacity(0.05), _bloc.add(
blurRadius: 2, CheckInRequested(shiftId: todayShift.id),
offset: const Offset(0, 1),
),
],
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
"TODAY'S SHIFT",
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.w600,
color: AppColors.krowBlue,
letterSpacing: 0.5,
),
),
const SizedBox(height: 2),
Text(
todayShift.title,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF1E293B), // slate-800
),
),
Text(
"${todayShift.clientName}${todayShift.location}",
style: const TextStyle(
fontSize: 12,
color: Color(0xFF64748B), // slate-500
),
),
],
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Text(
"9:00 AM - 5:00 PM",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF475569), // slate-600
),
),
Text(
"\$${todayShift.hourlyRate}/hr",
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: AppColors.krowBlue,
),
),
],
),
],
),
),
// Swipe To Check In / Checked Out State / No Shift State
if (todayShift != null && checkOutTime == null) ...[
SwipeToCheckIn(
isCheckedIn: isCheckedIn,
mode: state.checkInMode,
isLoading: state.status == ClockInStatus.actionInProgress,
onCheckIn: () async {
// Show NFC dialog if mode is 'nfc'
if (state.checkInMode == 'nfc') {
await _showNFCDialog(context);
} else {
_bloc.add(CheckInRequested(shiftId: todayShift.id));
}
},
onCheckOut: () {
showDialog(
context: context,
builder: (context) => LunchBreakDialog(
onComplete: () {
Navigator.of(context).pop(); // Close dialog first
_bloc.add(const CheckOutRequested());
},
),
); );
}, }
},
onCheckOut: () {
showDialog(
context: context,
builder: (context) => LunchBreakDialog(
onComplete: () {
Navigator.of(
context,
).pop(); // Close dialog first
_bloc.add(const CheckOutRequested());
},
),
);
},
),
] else if (todayShift != null &&
checkOutTime != null) ...[
// Shift Completed State
Container(
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: const Color(0xFFECFDF5), // emerald-50
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: const Color(0xFFA7F3D0),
), // emerald-200
), ),
] else if (todayShift != null && checkOutTime != null) ...[ child: Column(
// Shift Completed State children: [
Container( Container(
padding: const EdgeInsets.all(24), width: 48,
decoration: BoxDecoration( height: 48,
color: const Color(0xFFECFDF5), // emerald-50 decoration: const BoxDecoration(
borderRadius: BorderRadius.circular(16), color: Color(0xFFD1FAE5), // emerald-100
border: Border.all( shape: BoxShape.circle,
color: const Color(0xFFA7F3D0),
), // emerald-200
),
child: Column(
children: [
Container(
width: 48,
height: 48,
decoration: const BoxDecoration(
color: Color(0xFFD1FAE5), // emerald-100
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.check,
color: Color(0xFF059669), // emerald-600
size: 24,
),
), ),
const SizedBox(height: 12), child: const Icon(
const Text( LucideIcons.check,
"Shift Completed!", color: Color(0xFF059669), // emerald-600
style: TextStyle( size: 24,
fontSize: 16,
fontWeight: FontWeight.w600,
color: Color(0xFF065F46), // emerald-800
),
), ),
const SizedBox(height: 4), ),
const Text( const SizedBox(height: 12),
"Great work today", const Text(
style: TextStyle( "Shift Completed!",
fontSize: 14, style: TextStyle(
color: Color(0xFF059669), // emerald-600 fontSize: 16,
), fontWeight: FontWeight.w600,
color: Color(0xFF065F46), // emerald-800
), ),
], ),
), const SizedBox(height: 4),
const Text(
"Great work today",
style: TextStyle(
fontSize: 14,
color: Color(0xFF059669), // emerald-600
),
),
],
), ),
] else ...[ ),
// No Shift State ] else ...[
Container( // No Shift State
padding: const EdgeInsets.all(24), Container(
decoration: BoxDecoration( padding: const EdgeInsets.all(24),
color: const Color(0xFFF1F5F9), // slate-100 decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16), color: const Color(0xFFF1F5F9), // slate-100
), borderRadius: BorderRadius.circular(16),
child: Column(
children: [
const Text(
"No confirmed shifts for today",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Color(0xFF475569), // slate-600
),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
const Text(
"Accept a shift to clock in",
style: TextStyle(
fontSize: 14,
color: Color(0xFF64748B), // slate-500
),
textAlign: TextAlign.center,
),
],
),
), ),
], child: Column(
children: [
const Text(
"No confirmed shifts for today",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Color(0xFF475569), // slate-600
),
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
const Text(
"Accept a shift to clock in",
style: TextStyle(
fontSize: 14,
color: Color(0xFF64748B), // slate-500
),
textAlign: TextAlign.center,
),
],
),
),
],
// Checked In Banner // Checked In Banner
if (isCheckedIn && checkInTime != null) ...[ if (isCheckedIn && checkInTime != null) ...[
const SizedBox(height: 12), const SizedBox(height: 12),
Container( Container(
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFFECFDF5), // emerald-50 color: const Color(0xFFECFDF5), // emerald-50
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all( border: Border.all(
color: const Color(0xFFA7F3D0), color: const Color(0xFFA7F3D0),
), // emerald-200 ), // emerald-200
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
children: [ MainAxisAlignment.spaceBetween,
Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Column(
children: [ crossAxisAlignment:
const Text( CrossAxisAlignment.start,
"Checked in at", children: [
style: TextStyle( const Text(
fontSize: 12, "Checked in at",
fontWeight: FontWeight.w500, style: TextStyle(
color: Color(0xFF059669), fontSize: 12,
), fontWeight: FontWeight.w500,
color: Color(0xFF059669),
), ),
Text( ),
DateFormat('h:mm a').format(checkInTime), Text(
style: const TextStyle( DateFormat(
fontSize: 16, 'h:mm a',
fontWeight: FontWeight.bold, ).format(checkInTime),
color: Color(0xFF065F46), style: const TextStyle(
), fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF065F46),
), ),
],
),
Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Color(0xFFD1FAE5),
shape: BoxShape.circle,
),
child: const Icon(
LucideIcons.check,
color: Color(0xFF059669),
), ),
],
),
Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
color: Color(0xFFD1FAE5),
shape: BoxShape.circle,
), ),
], child: const Icon(
), LucideIcons.check,
color: Color(0xFF059669),
),
),
],
), ),
], ),
],
const SizedBox(height: 16), const SizedBox(height: 16),
// Recent Activity List // Recent Activity List
if (state.activityLog.isNotEmpty) ...state.activityLog.map( if (state.activityLog.isNotEmpty)
...state.activityLog.map(
(activity) => Container( (activity) => Container(
margin: const EdgeInsets.only(bottom: 12), margin: const EdgeInsets.only(bottom: 12),
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
@@ -490,7 +484,9 @@ class _ClockInPageState extends State<ClockInPage> {
width: 40, width: 40,
height: 40, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.krowBlue.withOpacity(0.1), color: AppColors.krowBlue.withOpacity(
0.1,
),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: const Icon( child: const Icon(
@@ -502,23 +498,28 @@ class _ClockInPageState extends State<ClockInPage> {
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
Text( Text(
DateFormat( DateFormat('MMM d').format(
'MMM d', activity['date'] as DateTime,
).format(activity['date'] as DateTime), ),
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Color(0xFF0F172A), // slate-900 color: Color(
0xFF0F172A,
), // slate-900
), ),
), ),
Text( Text(
"${activity['start']} - ${activity['end']}", "${activity['start']} - ${activity['end']}",
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: Color(0xFF64748B), // slate-500 color: Color(
0xFF64748B,
), // slate-500
), ),
), ),
], ],
@@ -542,7 +543,6 @@ class _ClockInPageState extends State<ClockInPage> {
), ),
], ],
), ),
),
), ),
), ),
); );
@@ -551,7 +551,12 @@ class _ClockInPageState extends State<ClockInPage> {
); );
} }
Widget _buildModeTab(String label, IconData icon, String value, String currentMode) { Widget _buildModeTab(
String label,
IconData icon,
String value,
String currentMode,
) {
final isSelected = currentMode == value; final isSelected = currentMode == value;
return Expanded( return Expanded(
child: GestureDetector( child: GestureDetector(
@@ -678,7 +683,7 @@ class _ClockInPageState extends State<ClockInPage> {
Future<void> _showNFCDialog(BuildContext context) async { Future<void> _showNFCDialog(BuildContext context) async {
bool scanned = false; bool scanned = false;
// Using a local navigator context since we are in a dialog // Using a local navigator context since we are in a dialog
await showDialog( await showDialog(
context: context, context: context,
@@ -771,11 +776,11 @@ class _ClockInPageState extends State<ClockInPage> {
); );
}, },
); );
// After dialog closes, trigger the event if scan was successful (simulated) // After dialog closes, trigger the event if scan was successful (simulated)
// In real app, we would check the dialog result // In real app, we would check the dialog result
if (scanned && _bloc.state.todayShift != null) { if (scanned && _bloc.state.todayShift != null) {
_bloc.add(CheckInRequested(shiftId: _bloc.state.todayShift!.id)); _bloc.add(CheckInRequested(shiftId: _bloc.state.todayShift!.id));
} }
} }
} }