From 2f5c736c209d7af23c93f066a91608c7384d2baa Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Tue, 27 Jan 2026 13:07:31 -0500 Subject: [PATCH] feat: implement emergency contact management with BLoC, including event and state handling --- .../blocs/emergency_contact_bloc.dart | 78 ++---------------- .../blocs/emergency_contact_event.dart | 34 ++++++++ .../blocs/emergency_contact_state.dart | 38 +++++++++ .../pages/emergency_contact_screen.dart | 2 +- .../emergency_contact_save_button.dart | 80 ++++++++++++------- 5 files changed, 128 insertions(+), 104 deletions(-) create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_event.dart create mode 100644 apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_state.dart diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_bloc.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_bloc.dart index 269563a4..1d4e2169 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_bloc.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_bloc.dart @@ -1,80 +1,14 @@ -import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:krow_domain/krow_domain.dart'; import '../../domain/arguments/get_emergency_contacts_arguments.dart'; import '../../domain/arguments/save_emergency_contacts_arguments.dart'; import '../../domain/usecases/get_emergency_contacts_usecase.dart'; import '../../domain/usecases/save_emergency_contacts_usecase.dart'; +import 'emergency_contact_event.dart'; +import 'emergency_contact_state.dart'; -// Events -abstract class EmergencyContactEvent extends Equatable { - const EmergencyContactEvent(); - - @override - List get props => []; -} - -class EmergencyContactsLoaded extends EmergencyContactEvent {} - -class EmergencyContactAdded extends EmergencyContactEvent {} - -class EmergencyContactRemoved extends EmergencyContactEvent { - final int index; - - const EmergencyContactRemoved(this.index); - - @override - List get props => [index]; -} - -class EmergencyContactUpdated extends EmergencyContactEvent { - final int index; - final EmergencyContact contact; - - const EmergencyContactUpdated(this.index, this.contact); - - @override - List get props => [index, contact]; -} - -class EmergencyContactsSaved extends EmergencyContactEvent {} - -// State -enum EmergencyContactStatus { initial, loading, success, saving, failure } - -class EmergencyContactState extends Equatable { - final EmergencyContactStatus status; - final List contacts; - final String? errorMessage; - - const EmergencyContactState({ - this.status = EmergencyContactStatus.initial, - this.contacts = const [], - this.errorMessage, - }); - - EmergencyContactState copyWith({ - EmergencyContactStatus? status, - List? contacts, - String? errorMessage, - }) { - return EmergencyContactState( - status: status ?? this.status, - contacts: contacts ?? this.contacts, - errorMessage: errorMessage ?? this.errorMessage, - ); - } - - bool get isValid { - if (contacts.isEmpty) return false; - // Check if at least one contact is valid (or all?) - // Usually all added contacts should be valid. - return contacts.every((c) => c.name.isNotEmpty && c.phone.isNotEmpty); - } - - @override - List get props => [status, contacts, errorMessage]; -} +export 'emergency_contact_event.dart'; +export 'emergency_contact_state.dart'; // BLoC class EmergencyContactBloc @@ -106,7 +40,7 @@ class EmergencyContactBloc const GetEmergencyContactsArguments(), ); emit(state.copyWith( - status: EmergencyContactStatus.success, + status: EmergencyContactStatus.loaded, contacts: contacts.isNotEmpty ? contacts : [EmergencyContact.empty()], @@ -157,7 +91,7 @@ class EmergencyContactBloc contacts: state.contacts, ), ); - emit(state.copyWith(status: EmergencyContactStatus.success)); + emit(state.copyWith(status: EmergencyContactStatus.saved)); } catch (e) { emit(state.copyWith( status: EmergencyContactStatus.failure, diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_event.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_event.dart new file mode 100644 index 00000000..75c3dcdf --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_event.dart @@ -0,0 +1,34 @@ +import 'package:equatable/equatable.dart'; +import 'package:krow_domain/krow_domain.dart'; + +abstract class EmergencyContactEvent extends Equatable { + const EmergencyContactEvent(); + + @override + List get props => []; +} + +class EmergencyContactsLoaded extends EmergencyContactEvent {} + +class EmergencyContactAdded extends EmergencyContactEvent {} + +class EmergencyContactRemoved extends EmergencyContactEvent { + final int index; + + const EmergencyContactRemoved(this.index); + + @override + List get props => [index]; +} + +class EmergencyContactUpdated extends EmergencyContactEvent { + final int index; + final EmergencyContact contact; + + const EmergencyContactUpdated(this.index, this.contact); + + @override + List get props => [index, contact]; +} + +class EmergencyContactsSaved extends EmergencyContactEvent {} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_state.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_state.dart new file mode 100644 index 00000000..8c2386bf --- /dev/null +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/blocs/emergency_contact_state.dart @@ -0,0 +1,38 @@ +import 'package:equatable/equatable.dart'; +import 'package:krow_domain/krow_domain.dart'; + +enum EmergencyContactStatus { initial, loading, loaded, saving, saved, failure } + +class EmergencyContactState extends Equatable { + final EmergencyContactStatus status; + final List contacts; + final String? errorMessage; + + const EmergencyContactState({ + this.status = EmergencyContactStatus.initial, + this.contacts = const [], + this.errorMessage, + }); + + EmergencyContactState copyWith({ + EmergencyContactStatus? status, + List? contacts, + String? errorMessage, + }) { + return EmergencyContactState( + status: status ?? this.status, + contacts: contacts ?? this.contacts, + errorMessage: errorMessage ?? this.errorMessage, + ); + } + + bool get isValid { + if (contacts.isEmpty) return false; + // Check if at least one contact is valid (or all?) + // Usually all added contacts should be valid. + return contacts.every((c) => c.name.isNotEmpty && c.phone.isNotEmpty); + } + + @override + List get props => [status, contacts, errorMessage]; +} diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/pages/emergency_contact_screen.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/pages/emergency_contact_screen.dart index def60251..3e3637cd 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/pages/emergency_contact_screen.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/pages/emergency_contact_screen.dart @@ -71,7 +71,7 @@ class EmergencyContactScreen extends StatelessWidget { ), ), ), - EmergencyContactSaveButton(state: state), + const EmergencyContactSaveButton(), ], ); }, diff --git a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/widgets/emergency_contact_save_button.dart b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/widgets/emergency_contact_save_button.dart index 0b5ea1d7..e21e94f2 100644 --- a/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/widgets/emergency_contact_save_button.dart +++ b/apps/mobile/packages/features/staff/profile_sections/onboarding/emergency_contact/lib/src/presentation/widgets/emergency_contact_save_button.dart @@ -4,41 +4,59 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../blocs/emergency_contact_bloc.dart'; class EmergencyContactSaveButton extends StatelessWidget { - final EmergencyContactState state; + const EmergencyContactSaveButton({super.key}); - const EmergencyContactSaveButton({super.key, required this.state}); + void _onSave(BuildContext context) { + context.read().add(EmergencyContactsSaved()); + } @override Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(UiConstants.space4), - decoration: BoxDecoration( - color: UiColors.bgPopup, - border: Border(top: BorderSide(color: UiColors.border)), - ), - child: SafeArea( - child: UiButton.primary( - fullWidth: true, - onPressed: state.isValid - ? () => context - .read() - .add(EmergencyContactsSaved()) - : null, - child: state.status == EmergencyContactStatus.saving - ? SizedBox( - height: 20.0, - width: 20.0, - child: CircularProgressIndicator( - strokeWidth: 2, - valueColor: - AlwaysStoppedAnimation(UiColors.primaryForeground), - ), - ) - : Text( - 'Save & Continue', - ), - ), - ), + return BlocConsumer( + listenWhen: (previous, current) => previous.status != current.status, + listener: (context, state) { + if (state.status == EmergencyContactStatus.saved) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Emergency contacts saved successfully', + style: UiTypography.body2r.textPrimary, + ), + backgroundColor: UiColors.iconSuccess, + ), + ); + } + }, + builder: (context, state) { + final isLoading = state.status == EmergencyContactStatus.saving; + return Container( + padding: EdgeInsets.all(UiConstants.space4), + decoration: BoxDecoration( + color: UiColors.bgPopup, + border: Border(top: BorderSide(color: UiColors.border)), + ), + child: SafeArea( + child: UiButton.primary( + fullWidth: true, + onPressed: state.isValid && !isLoading + ? () => _onSave(context) + : null, + child: isLoading + ? SizedBox( + height: 20.0, + width: 20.0, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + UiColors.primaryForeground, + ), + ), + ) + : const Text('Save & Continue'), + ), + ), + ); + }, ); } }