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, + ), + ], + ), + ); + } +}