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,29 +66,27 @@ 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,
],
), ),
showBackButton: false,
centerTitle: false,
), ),
child: SafeArea( body: SafeArea(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: const EdgeInsets.only(bottom: 100), padding: const EdgeInsets.only(
bottom: UiConstants.space24,
top: UiConstants.space6,
),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: <Widget>[
_buildHeader(),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
// Commute Tracker (shows before date selector when applicable) // Commute Tracker (shows before date selector when applicable)
if (todayShift != null) if (todayShift != null)
@@ -166,43 +166,15 @@ class _ClockInPageState extends State<ClockInPage> {
const SizedBox(height: 24), const SizedBox(height: 24),
// Your Activity Header // Your Activity Header
// Your Activity Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text( const Text(
"Your Activity", "Your Activity",
textAlign: TextAlign.start,
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: AppColors.krowCharcoal, color: AppColors.krowCharcoal,
), ),
), ),
GestureDetector(
onTap: () {
debugPrint('Navigating to shifts...');
},
child: Row(
children: const [
Text(
"View all",
style: TextStyle(
color: AppColors.krowBlue,
fontWeight: FontWeight.w500,
fontSize: 14,
),
),
SizedBox(width: 4),
Icon(
LucideIcons.chevronRight,
size: 16,
color: AppColors.krowBlue,
),
],
),
),
],
),
const SizedBox(height: 12), const SizedBox(height: 12),
// Check-in Mode Toggle // Check-in Mode Toggle
@@ -226,7 +198,12 @@ class _ClockInPageState extends State<ClockInPage> {
), ),
child: Row( child: Row(
children: [ children: [
_buildModeTab("Swipe", LucideIcons.mapPin, 'swipe', state.checkInMode), _buildModeTab(
"Swipe",
LucideIcons.mapPin,
'swipe',
state.checkInMode,
),
// _buildModeTab("NFC Tap", LucideIcons.wifi, 'nfc', state.checkInMode), // _buildModeTab("NFC Tap", LucideIcons.wifi, 'nfc', state.checkInMode),
], ],
), ),
@@ -257,7 +234,8 @@ class _ClockInPageState extends State<ClockInPage> {
children: [ children: [
Expanded( Expanded(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
const Text( const Text(
"TODAY'S SHIFT", "TODAY'S SHIFT",
@@ -274,14 +252,18 @@ class _ClockInPageState extends State<ClockInPage> {
style: const TextStyle( style: const TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF1E293B), // slate-800 color: Color(
0xFF1E293B,
), // slate-800
), ),
), ),
Text( Text(
"${todayShift.clientName}${todayShift.location}", "${todayShift.clientName}${todayShift.location}",
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: Color(0xFF64748B), // slate-500 color: Color(
0xFF64748B,
), // slate-500
), ),
), ),
], ],
@@ -317,13 +299,17 @@ class _ClockInPageState extends State<ClockInPage> {
SwipeToCheckIn( SwipeToCheckIn(
isCheckedIn: isCheckedIn, isCheckedIn: isCheckedIn,
mode: state.checkInMode, mode: state.checkInMode,
isLoading: state.status == ClockInStatus.actionInProgress, isLoading:
state.status ==
ClockInStatus.actionInProgress,
onCheckIn: () async { onCheckIn: () async {
// Show NFC dialog if mode is 'nfc' // Show NFC dialog if mode is 'nfc'
if (state.checkInMode == 'nfc') { if (state.checkInMode == 'nfc') {
await _showNFCDialog(context); await _showNFCDialog(context);
} else { } else {
_bloc.add(CheckInRequested(shiftId: todayShift.id)); _bloc.add(
CheckInRequested(shiftId: todayShift.id),
);
} }
}, },
onCheckOut: () { onCheckOut: () {
@@ -331,14 +317,17 @@ class _ClockInPageState extends State<ClockInPage> {
context: context, context: context,
builder: (context) => LunchBreakDialog( builder: (context) => LunchBreakDialog(
onComplete: () { onComplete: () {
Navigator.of(context).pop(); // Close dialog first Navigator.of(
context,
).pop(); // Close dialog first
_bloc.add(const CheckOutRequested()); _bloc.add(const CheckOutRequested());
}, },
), ),
); );
}, },
), ),
] else if (todayShift != null && checkOutTime != null) ...[ ] else if (todayShift != null &&
checkOutTime != null) ...[
// Shift Completed State // Shift Completed State
Container( Container(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
@@ -430,10 +419,12 @@ class _ClockInPageState extends State<ClockInPage> {
), // emerald-200 ), // emerald-200
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [ children: [
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment:
CrossAxisAlignment.start,
children: [ children: [
const Text( const Text(
"Checked in at", "Checked in at",
@@ -444,7 +435,9 @@ class _ClockInPageState extends State<ClockInPage> {
), ),
), ),
Text( Text(
DateFormat('h:mm a').format(checkInTime), DateFormat(
'h:mm a',
).format(checkInTime),
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@@ -473,7 +466,8 @@ class _ClockInPageState extends State<ClockInPage> {
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
), ),
), ),
], ],
@@ -544,14 +545,18 @@ 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(