From e78d5938dd3260f339cf452314bb39e41db3d6de Mon Sep 17 00:00:00 2001 From: Achintha Isuru Date: Tue, 24 Feb 2026 13:53:36 -0500 Subject: [PATCH] client hub bloc updated --- .../lib/src/widgets/ui_app_bar.dart | 4 - .../presentation/blocs/client_hubs_bloc.dart | 94 +--------- .../presentation/blocs/client_hubs_event.dart | 32 ---- .../presentation/blocs/client_hubs_state.dart | 21 +-- .../blocs/edit_hub/edit_hub_bloc.dart | 4 +- .../blocs/hub_details/hub_details_bloc.dart | 4 +- .../presentation/pages/client_hubs_page.dart | 168 +++++------------- .../src/presentation/widgets/hub_card.dart | 43 +---- .../presentation/widgets/hub_info_card.dart | 5 +- 9 files changed, 66 insertions(+), 309 deletions(-) diff --git a/apps/mobile/packages/design_system/lib/src/widgets/ui_app_bar.dart b/apps/mobile/packages/design_system/lib/src/widgets/ui_app_bar.dart index 4394bb7e..f3f4040e 100644 --- a/apps/mobile/packages/design_system/lib/src/widgets/ui_app_bar.dart +++ b/apps/mobile/packages/design_system/lib/src/widgets/ui_app_bar.dart @@ -1,10 +1,6 @@ import 'package:design_system/design_system.dart'; -import 'package:design_system/src/ui_typography.dart'; import 'package:flutter/material.dart'; -import '../ui_icons.dart'; -import 'ui_icon_button.dart'; - /// A custom AppBar for the Krow UI design system. /// /// This widget provides a consistent look and feel for top app bars across the application. diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_bloc.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_bloc.dart index dd6a1801..4bd08959 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_bloc.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_bloc.dart @@ -2,10 +2,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_modular/flutter_modular.dart'; import 'package:krow_core/core.dart'; import 'package:krow_domain/krow_domain.dart'; -import '../../domain/arguments/assign_nfc_tag_arguments.dart'; -import '../../domain/arguments/delete_hub_arguments.dart'; -import '../../domain/usecases/assign_nfc_tag_usecase.dart'; -import '../../domain/usecases/delete_hub_usecase.dart'; import '../../domain/usecases/get_hubs_usecase.dart'; import 'client_hubs_event.dart'; import 'client_hubs_state.dart'; @@ -13,39 +9,18 @@ import 'client_hubs_state.dart'; /// BLoC responsible for managing the state of the Client Hubs feature. /// /// It orchestrates the flow between the UI and the domain layer by invoking -/// specific use cases for fetching, deleting, and assigning tags to hubs. +/// specific use cases for fetching hubs. class ClientHubsBloc extends Bloc with BlocErrorHandler implements Disposable { - ClientHubsBloc({ - required GetHubsUseCase getHubsUseCase, - required DeleteHubUseCase deleteHubUseCase, - required AssignNfcTagUseCase assignNfcTagUseCase, - }) : _getHubsUseCase = getHubsUseCase, - _deleteHubUseCase = deleteHubUseCase, - _assignNfcTagUseCase = assignNfcTagUseCase, - super(const ClientHubsState()) { + ClientHubsBloc({required GetHubsUseCase getHubsUseCase}) + : _getHubsUseCase = getHubsUseCase, + super(const ClientHubsState()) { on(_onFetched); - on(_onDeleteRequested); - on(_onNfcTagAssignRequested); on(_onMessageCleared); - - on(_onIdentifyDialogToggled); } + final GetHubsUseCase _getHubsUseCase; - final DeleteHubUseCase _deleteHubUseCase; - final AssignNfcTagUseCase _assignNfcTagUseCase; - - void _onIdentifyDialogToggled( - ClientHubsIdentifyDialogToggled event, - Emitter emit, - ) { - if (event.hub == null) { - emit(state.copyWith(clearHubToIdentify: true)); - } else { - emit(state.copyWith(hubToIdentify: event.hub)); - } - } Future _onFetched( ClientHubsFetched event, @@ -66,61 +41,6 @@ class ClientHubsBloc extends Bloc ); } - Future _onDeleteRequested( - ClientHubsDeleteRequested event, - Emitter emit, - ) async { - emit(state.copyWith(status: ClientHubsStatus.actionInProgress)); - - await handleError( - emit: emit.call, - action: () async { - await _deleteHubUseCase.call(DeleteHubArguments(hubId: event.hubId)); - final List hubs = await _getHubsUseCase.call(); - emit( - state.copyWith( - status: ClientHubsStatus.actionSuccess, - hubs: hubs, - successMessage: 'Hub deleted successfully', - ), - ); - }, - onError: (String errorKey) => state.copyWith( - status: ClientHubsStatus.actionFailure, - errorMessage: errorKey, - ), - ); - } - - Future _onNfcTagAssignRequested( - ClientHubsNfcTagAssignRequested event, - Emitter emit, - ) async { - emit(state.copyWith(status: ClientHubsStatus.actionInProgress)); - - await handleError( - emit: emit.call, - action: () async { - await _assignNfcTagUseCase.call( - AssignNfcTagArguments(hubId: event.hubId, nfcTagId: event.nfcTagId), - ); - final List hubs = await _getHubsUseCase.call(); - emit( - state.copyWith( - status: ClientHubsStatus.actionSuccess, - hubs: hubs, - successMessage: 'NFC tag assigned successfully', - clearHubToIdentify: true, - ), - ); - }, - onError: (String errorKey) => state.copyWith( - status: ClientHubsStatus.actionFailure, - errorMessage: errorKey, - ), - ); - } - void _onMessageCleared( ClientHubsMessageCleared event, Emitter emit, @@ -130,8 +50,8 @@ class ClientHubsBloc extends Bloc clearErrorMessage: true, clearSuccessMessage: true, status: - state.status == ClientHubsStatus.actionSuccess || - state.status == ClientHubsStatus.actionFailure + state.status == ClientHubsStatus.success || + state.status == ClientHubsStatus.failure ? ClientHubsStatus.success : state.status, ), diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_event.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_event.dart index c84737f4..f329807b 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_event.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_event.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; -import 'package:krow_domain/krow_domain.dart'; /// Base class for all client hubs events. abstract class ClientHubsEvent extends Equatable { @@ -14,38 +13,7 @@ class ClientHubsFetched extends ClientHubsEvent { const ClientHubsFetched(); } -/// Event triggered to delete a hub. -class ClientHubsDeleteRequested extends ClientHubsEvent { - const ClientHubsDeleteRequested(this.hubId); - final String hubId; - - @override - List get props => [hubId]; -} - -/// Event triggered to assign an NFC tag to a hub. -class ClientHubsNfcTagAssignRequested extends ClientHubsEvent { - const ClientHubsNfcTagAssignRequested({ - required this.hubId, - required this.nfcTagId, - }); - final String hubId; - final String nfcTagId; - - @override - List get props => [hubId, nfcTagId]; -} - /// Event triggered to clear any error or success messages. class ClientHubsMessageCleared extends ClientHubsEvent { const ClientHubsMessageCleared(); } - -/// Event triggered to toggle the visibility of the "Identify NFC" dialog. -class ClientHubsIdentifyDialogToggled extends ClientHubsEvent { - const ClientHubsIdentifyDialogToggled({this.hub}); - final Hub? hub; - - @override - List get props => [hub]; -} diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_state.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_state.dart index 0dcbb7bd..8d9c0daa 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_state.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/client_hubs_state.dart @@ -2,15 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:krow_domain/krow_domain.dart'; /// Enum representing the status of the client hubs state. -enum ClientHubsStatus { - initial, - loading, - success, - failure, - actionInProgress, - actionSuccess, - actionFailure, -} +enum ClientHubsStatus { initial, loading, success, failure } /// State class for the ClientHubs BLoC. class ClientHubsState extends Equatable { @@ -19,7 +11,6 @@ class ClientHubsState extends Equatable { this.hubs = const [], this.errorMessage, this.successMessage, - this.hubToIdentify, }); final ClientHubsStatus status; @@ -27,17 +18,11 @@ class ClientHubsState extends Equatable { final String? errorMessage; final String? successMessage; - /// The hub currently being identified/assigned an NFC tag. - /// If null, the identification dialog is closed. - final Hub? hubToIdentify; - ClientHubsState copyWith({ ClientHubsStatus? status, List? hubs, String? errorMessage, String? successMessage, - Hub? hubToIdentify, - bool clearHubToIdentify = false, bool clearErrorMessage = false, bool clearSuccessMessage = false, }) { @@ -50,9 +35,6 @@ class ClientHubsState extends Equatable { successMessage: clearSuccessMessage ? null : (successMessage ?? this.successMessage), - hubToIdentify: clearHubToIdentify - ? null - : (hubToIdentify ?? this.hubToIdentify), ); } @@ -62,6 +44,5 @@ class ClientHubsState extends Equatable { hubs, errorMessage, successMessage, - hubToIdentify, ]; } diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/edit_hub/edit_hub_bloc.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/edit_hub/edit_hub_bloc.dart index 42a3734e..6923899a 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/edit_hub/edit_hub_bloc.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/edit_hub/edit_hub_bloc.dart @@ -29,7 +29,7 @@ class EditHubBloc extends Bloc emit(state.copyWith(status: EditHubStatus.loading)); await handleError( - emit: emit, + emit: emit.call, action: () async { await _createHubUseCase.call( CreateHubArguments( @@ -64,7 +64,7 @@ class EditHubBloc extends Bloc emit(state.copyWith(status: EditHubStatus.loading)); await handleError( - emit: emit, + emit: emit.call, action: () async { await _updateHubUseCase.call( UpdateHubArguments( diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/hub_details/hub_details_bloc.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/hub_details/hub_details_bloc.dart index 9a82b60f..bda30551 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/hub_details/hub_details_bloc.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/blocs/hub_details/hub_details_bloc.dart @@ -30,7 +30,7 @@ class HubDetailsBloc extends Bloc emit(state.copyWith(status: HubDetailsStatus.loading)); await handleError( - emit: emit, + emit: emit.call, action: () async { await _deleteHubUseCase.call(DeleteHubArguments(hubId: event.id)); emit( @@ -54,7 +54,7 @@ class HubDetailsBloc extends Bloc emit(state.copyWith(status: HubDetailsStatus.loading)); await handleError( - emit: emit, + emit: emit.call, action: () async { await _assignNfcTagUseCase.call( AssignNfcTagArguments(hubId: event.hubId, nfcTagId: event.nfcTagId), diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart index cb6d329d..1bcdb4ed 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/pages/client_hubs_page.dart @@ -12,7 +12,6 @@ import '../blocs/client_hubs_state.dart'; import '../widgets/hub_card.dart'; import '../widgets/hub_empty_state.dart'; import '../widgets/hub_info_card.dart'; -import '../widgets/identify_nfc_dialog.dart'; /// The main page for the client hubs feature. /// @@ -72,84 +71,54 @@ class ClientHubsPage extends StatelessWidget { ), child: const Icon(UiIcons.add), ), - body: Stack( - children: [ - CustomScrollView( - slivers: [ - _buildAppBar(context), - SliverPadding( - padding: const EdgeInsets.symmetric( - horizontal: UiConstants.space5, - vertical: UiConstants.space5, - ).copyWith(bottom: 100), - sliver: SliverList( - delegate: SliverChildListDelegate([ - if (state.status == ClientHubsStatus.loading) - const Center(child: CircularProgressIndicator()) - else if (state.hubs.isEmpty) - HubEmptyState( - onAddPressed: () async { - final bool? success = await Modular.to - .toEditHub(); - if (success == true && context.mounted) { - BlocProvider.of( - context, - ).add(const ClientHubsFetched()); - } - }, - ) - else ...[ - ...state.hubs.map( - (Hub hub) => HubCard( - hub: hub, - onTap: () async { - final bool? success = await Modular.to - .toHubDetails(hub); - if (success == true && context.mounted) { - BlocProvider.of( - context, - ).add(const ClientHubsFetched()); - } - }, - onNfcPressed: () => - BlocProvider.of( - context, - ).add( - ClientHubsIdentifyDialogToggled(hub: hub), - ), - onDeletePressed: () => - _confirmDeleteHub(context, hub), - ), - ), - ], - const SizedBox(height: UiConstants.space5), - const HubInfoCard(), - ]), + body: CustomScrollView( + slivers: [ + _buildAppBar(context), + SliverPadding( + padding: const EdgeInsets.symmetric( + horizontal: UiConstants.space5, + vertical: UiConstants.space5, + ).copyWith(bottom: 100), + sliver: SliverList( + delegate: SliverChildListDelegate([ + const Padding( + padding: EdgeInsets.only(bottom: UiConstants.space5), + child: HubInfoCard(), ), - ), - ], - ), - if (state.hubToIdentify != null) - IdentifyNfcDialog( - hub: state.hubToIdentify!, - onAssign: (String tagId) { - BlocProvider.of(context).add( - ClientHubsNfcTagAssignRequested( - hubId: state.hubToIdentify!.id, - nfcTagId: tagId, + if (state.status == ClientHubsStatus.loading) + const Center(child: CircularProgressIndicator()) + else if (state.hubs.isEmpty) + HubEmptyState( + onAddPressed: () async { + final bool? success = await Modular.to.toEditHub(); + if (success == true && context.mounted) { + BlocProvider.of( + context, + ).add(const ClientHubsFetched()); + } + }, + ) + else ...[ + ...state.hubs.map( + (Hub hub) => HubCard( + hub: hub, + onTap: () async { + final bool? success = await Modular.to + .toHubDetails(hub); + if (success == true && context.mounted) { + BlocProvider.of( + context, + ).add(const ClientHubsFetched()); + } + }, + ), ), - ); - }, - onCancel: () => BlocProvider.of( - context, - ).add(const ClientHubsIdentifyDialogToggled()), - ), - if (state.status == ClientHubsStatus.actionInProgress) - Container( - color: UiColors.black.withValues(alpha: 0.1), - child: const Center(child: CircularProgressIndicator()), + ], + const SizedBox(height: UiConstants.space5), + ]), ), + ), ], ), ); @@ -160,7 +129,7 @@ class ClientHubsPage extends StatelessWidget { Widget _buildAppBar(BuildContext context) { return SliverAppBar( - backgroundColor: UiColors.foreground, // Dark Slate equivalent + backgroundColor: UiColors.foreground, automaticallyImplyLeading: false, expandedHeight: 140, pinned: true, @@ -219,51 +188,4 @@ class ClientHubsPage extends StatelessWidget { ), ); } - - Future _confirmDeleteHub(BuildContext context, Hub hub) async { - final String hubName = hub.name.isEmpty ? t.client_hubs.title : hub.name; - return showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return AlertDialog( - title: Text(t.client_hubs.delete_dialog.title), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(t.client_hubs.delete_dialog.message(hubName: hubName)), - const SizedBox(height: UiConstants.space2), - Text(t.client_hubs.delete_dialog.undo_warning), - const SizedBox(height: UiConstants.space2), - Text( - t.client_hubs.delete_dialog.dependency_warning, - style: UiTypography.footnote1r.copyWith( - color: UiColors.textSecondary, - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () => Modular.to.pop(), - child: Text(t.client_hubs.delete_dialog.cancel), - ), - TextButton( - onPressed: () { - BlocProvider.of( - context, - ).add(ClientHubsDeleteRequested(hub.id)); - Modular.to.pop(); - }, - style: TextButton.styleFrom( - foregroundColor: UiColors.destructive, - ), - child: Text(t.client_hubs.delete_dialog.delete), - ), - ], - ); - }, - ); - } } diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_card.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_card.dart index d8504194..eb6b1aba 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_card.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_card.dart @@ -6,23 +6,11 @@ import 'package:core_localization/core_localization.dart'; /// A card displaying information about a single hub. class HubCard extends StatelessWidget { /// Creates a [HubCard]. - const HubCard({ - required this.hub, - required this.onNfcPressed, - required this.onDeletePressed, - required this.onTap, - super.key, - }); + const HubCard({required this.hub, required this.onTap, super.key}); /// The hub to display. final Hub hub; - /// Callback when the NFC button is pressed. - final VoidCallback onNfcPressed; - - /// Callback when the delete button is pressed. - final VoidCallback onDeletePressed; - /// Callback when the card is tapped. final VoidCallback onTap; @@ -37,13 +25,7 @@ class HubCard extends StatelessWidget { decoration: BoxDecoration( color: UiColors.white, borderRadius: BorderRadius.circular(UiConstants.radiusBase), - boxShadow: const [ - BoxShadow( - color: UiColors.popupShadow, - blurRadius: 10, - offset: Offset(0, 4), - ), - ], + border: Border.all(color: UiColors.border), ), child: Padding( padding: const EdgeInsets.all(UiConstants.space4), @@ -72,6 +54,7 @@ class HubCard extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: UiConstants.space1), child: Row( + mainAxisSize: MainAxisSize.min, children: [ const Icon( UiIcons.mapPin, @@ -79,7 +62,7 @@ class HubCard extends StatelessWidget { color: UiColors.iconThird, ), const SizedBox(width: UiConstants.space1), - Expanded( + Flexible( child: Text( hub.address, style: UiTypography.footnote1r.textSecondary, @@ -104,20 +87,10 @@ class HubCard extends StatelessWidget { ], ), ), - Row( - children: [ - IconButton( - onPressed: onDeletePressed, - icon: const Icon( - UiIcons.delete, - color: UiColors.destructive, - size: 20, - ), - padding: EdgeInsets.zero, - constraints: const BoxConstraints(), - splashRadius: 20, - ), - ], + const Icon( + UiIcons.chevronRight, + size: 16, + color: UiColors.iconSecondary, ), ], ), diff --git a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_info_card.dart b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_info_card.dart index 013e533c..634d9029 100644 --- a/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_info_card.dart +++ b/apps/mobile/packages/features/client/hubs/lib/src/presentation/widgets/hub_info_card.dart @@ -31,10 +31,7 @@ class HubInfoCard extends StatelessWidget { const SizedBox(height: UiConstants.space1), Text( t.client_hubs.about_hubs.description, - style: UiTypography.footnote1r.copyWith( - color: UiColors.textSecondary, - height: 1.4, - ), + style: UiTypography.footnote1r.textSecondary, ), ], ),