From 009b7c0316805f7c1a54362bb74f00d3281215b5 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 2 Jul 2025 15:53:54 +0300 Subject: [PATCH] Refactor SpaceDetails feature to replace LoadSpacesParam with LoadSpaceDetailsParam, enhancing clarity in parameter handling. Introduce ClearSpaceDetails event in SpaceDetailsBloc for better state management. Update SpaceDetailsDialog and SpaceDetailsForm to utilize new parameter and improve dialog functionality. --- .../remote_space_details_service.dart | 4 +- .../params/load_space_details_param.dart | 7 + .../domain/params/load_spaces_param.dart | 3 - .../services/space_details_service.dart | 4 +- .../presentation/bloc/space_details_bloc.dart | 10 +- .../bloc/space_details_event.dart | 11 +- .../bloc/space_details_state.dart | 6 +- .../widgets/space_details_dialog.dart | 134 ++++++++++-------- .../widgets/space_details_form.dart | 76 ++++++++++ 9 files changed, 183 insertions(+), 72 deletions(-) create mode 100644 lib/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart delete mode 100644 lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart create mode 100644 lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart diff --git a/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart b/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart index 2e999361..17514e85 100644 --- a/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart +++ b/lib/pages/space_management_v2/modules/space_details/data/services/remote_space_details_service.dart @@ -1,6 +1,6 @@ import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/http_service.dart'; @@ -15,7 +15,7 @@ class RemoteSpaceDetailsService implements SpaceDetailsService { static const _defaultErrorMessage = 'Failed to load space details'; @override - Future getSpaceDetails(LoadSpacesParam param) async { + Future getSpaceDetails(LoadSpaceDetailsParam param) async { try { final response = await _httpService.get( path: 'endpoint', diff --git a/lib/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart b/lib/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart new file mode 100644 index 00000000..e5efbb83 --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart @@ -0,0 +1,7 @@ +class LoadSpaceDetailsParam { + const LoadSpaceDetailsParam({ + this.spaceUuid, + }); + + final String? spaceUuid; +} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart b/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart deleted file mode 100644 index 5324ed98..00000000 --- a/lib/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart +++ /dev/null @@ -1,3 +0,0 @@ -class LoadSpacesParam { - const LoadSpacesParam(); -} diff --git a/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart b/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart index b032560b..16b09ff1 100644 --- a/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart +++ b/lib/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart @@ -1,6 +1,6 @@ import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; abstract class SpaceDetailsService { - Future getSpaceDetails(LoadSpacesParam param); + Future getSpaceDetails(LoadSpaceDetailsParam param); } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart index 6eb96632..d397daf9 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart @@ -1,7 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_spaces_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/services/space_details_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -11,6 +11,7 @@ part 'space_details_state.dart'; class SpaceDetailsBloc extends Bloc { SpaceDetailsBloc(this._spaceDetailsService) : super(SpaceDetailsInitial()) { on(_onLoadSpaceDetails); + on(_onClearSpaceDetails); } final SpaceDetailsService _spaceDetailsService; @@ -31,4 +32,11 @@ class SpaceDetailsBloc extends Bloc { emit(SpaceDetailsFailure(e.toString())); } } + + void _onClearSpaceDetails( + ClearSpaceDetails event, + Emitter emit, + ) { + emit(SpaceDetailsInitial()); + } } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart index fe559e26..9dd40fba 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_event.dart @@ -7,11 +7,18 @@ sealed class SpaceDetailsEvent extends Equatable { List get props => []; } -class LoadSpaceDetails extends SpaceDetailsEvent { +final class LoadSpaceDetails extends SpaceDetailsEvent { const LoadSpaceDetails(this.param); - final LoadSpacesParam param; + final LoadSpaceDetailsParam param; @override List get props => [param]; } + +final class ClearSpaceDetails extends SpaceDetailsEvent { + const ClearSpaceDetails(); + + @override + List get props => []; +} \ No newline at end of file diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart index c7378f89..53c4cf77 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_state.dart @@ -21,10 +21,10 @@ final class SpaceDetailsLoaded extends SpaceDetailsState { } final class SpaceDetailsFailure extends SpaceDetailsState { - final String message; + final String errorMessage; - const SpaceDetailsFailure(this.message); + const SpaceDetailsFailure(this.errorMessage); @override - List get props => [message]; + List get props => [errorMessage]; } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart index ee6c7e7c..98b5027a 100644 --- a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_dialog.dart @@ -1,17 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart' - show SpaceDetailsModel; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_picker.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_name_text_field.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/params/load_space_details_param.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/bloc/space_details_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; -class SpaceDetailsDialog extends StatelessWidget { +class SpaceDetailsDialog extends StatefulWidget { const SpaceDetailsDialog({ required this.title, required this.space, @@ -24,58 +20,78 @@ class SpaceDetailsDialog extends StatelessWidget { final void Function(SpaceDetailsModel space) onSave; @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => SpaceDetailsModelBloc(initialState: space), - child: Builder( - builder: (context) { - final space = context.watch().state; - return AlertDialog( - title: title, - backgroundColor: ColorsManager.whiteColors, - content: SizedBox( - height: context.screenHeight * 0.25, - child: Row( - spacing: 20, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(child: SpaceIconPicker(iconPath: space.icon)), - Expanded( - flex: 2, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SpaceNameTextField( - initialValue: space.spaceName, - isNameFieldExist: (value) { - final subspaces = space.subspaces; - if (subspaces.isEmpty) return false; - return subspaces.any( - (subspace) => subspace.name == value, - ); - }, - ), - const Spacer(), - SpaceSubSpacesBox( - subspaces: space.subspaces, - ), - const SizedBox(height: 16), - SpaceDetailsDevicesBox(space: space), - ], - ), - ), - ], - ), + State createState() => _SpaceDetailsDialogState(); +} + +class _SpaceDetailsDialogState extends State { + @override + void initState() { + final isCreateMode = widget.space.uuid.isEmpty; + + if (!isCreateMode) { + context.read().add( + LoadSpaceDetails( + LoadSpaceDetailsParam(spaceUuid: widget.space.uuid), ), - actions: [ - SpaceDetailsActionButtons( - onSave: () => onSave(space), - onCancel: Navigator.of(context).pop, - ), - ], ); - }, + } + super.initState(); + } + + @override + void deactivate() { + context.read().add(const ClearSpaceDetails()); + super.deactivate(); + } + + @override + Widget build(BuildContext context) { + final isCreateMode = widget.space.uuid.isEmpty; + if (isCreateMode) { + return SpaceDetailsForm( + title: widget.title, + space: widget.space, + onSave: widget.onSave, + ); + } + + return BlocBuilder( + builder: (context, state) => switch (state) { + SpaceDetailsInitial() => _buildLoadingDialog(), + SpaceDetailsLoading() => _buildLoadingDialog(), + SpaceDetailsLoaded(:final spaceDetails) => SpaceDetailsForm( + title: widget.title, + space: spaceDetails, + onSave: widget.onSave, + ), + SpaceDetailsFailure(:final errorMessage) => _buildErrorDialog( + errorMessage, + ), + }, + ); + } + + Widget _buildLoadingDialog() { + return AlertDialog( + title: widget.title, + backgroundColor: ColorsManager.whiteColors, + content: const Center(child: CircularProgressIndicator()), + ); + } + + Widget _buildErrorDialog(String errorMessage) { + return AlertDialog( + title: widget.title, + backgroundColor: ColorsManager.whiteColors, + content: Center( + child: Text( + errorMessage, + style: context.textTheme.bodyLarge?.copyWith( + color: ColorsManager.red, + fontWeight: FontWeight.w500, + fontSize: 18, + ), + ), ), ); } diff --git a/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart new file mode 100644 index 00000000..aa08d7fe --- /dev/null +++ b/lib/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_form.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/domain/models/space_details_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_action_buttons.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_details_devices_box.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_icon_picker.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_name_text_field.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/widgets/space_sub_spaces_box.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_space/presentation/bloc/space_details_model_bloc/space_details_model_bloc.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +class SpaceDetailsForm extends StatelessWidget { + const SpaceDetailsForm({ + required this.title, + required this.space, + required this.onSave, + super.key, + }); + + final Widget title; + final SpaceDetailsModel space; + final void Function(SpaceDetailsModel space) onSave; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => SpaceDetailsModelBloc(initialState: space), + child: AlertDialog( + title: title, + backgroundColor: ColorsManager.whiteColors, + content: SizedBox( + height: context.screenHeight * 0.25, + child: Row( + spacing: 20, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: SpaceIconPicker(iconPath: space.icon)), + Expanded( + flex: 2, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SpaceNameTextField( + initialValue: space.spaceName, + isNameFieldExist: (value) { + final subspaces = space.subspaces; + if (subspaces.isEmpty) return false; + return subspaces.any( + (subspace) => subspace.name == value, + ); + }, + ), + const Spacer(), + SpaceSubSpacesBox( + subspaces: space.subspaces, + ), + const SizedBox(height: 16), + SpaceDetailsDevicesBox(space: space), + ], + ), + ), + ], + ), + ), + actions: [ + SpaceDetailsActionButtons( + onSave: () => onSave(space), + onCancel: Navigator.of(context).pop, + ), + ], + ), + ); + } +}