From 6dcc851d971742fcc5fd82efe95ea0aa3cf33740 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 6 Jul 2025 10:46:20 +0300 Subject: [PATCH] Made `CommunityDialog` reusable for edit and create features. --- ...ce_management_community_dialog_helper.dart | 27 +++---- .../shared/widgets/community_dialog.dart} | 78 ++++++++----------- .../widgets/community_structure_header.dart | 21 ++--- .../domain/models/community_model.dart | 20 +++++ .../presentation/create_community_dialog.dart | 44 ++++++----- .../remote_update_community_service.dart | 3 +- .../domain/params/update_community_param.dart | 10 --- .../services/update_community_service.dart | 3 +- .../bloc/update_community_bloc.dart | 3 +- .../bloc/update_community_event.dart | 6 +- .../presentation/edit_community_dialog.dart | 57 ++++++++++++++ 11 files changed, 161 insertions(+), 111 deletions(-) rename lib/pages/space_management_v2/{modules/create_community/presentation/create_community_dialog_widget.dart => main_module/shared/widgets/community_dialog.dart} (55%) delete mode 100644 lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart create mode 100644 lib/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart diff --git a/lib/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart b/lib/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart index 5322c3ea..791ed10e 100644 --- a/lib/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart +++ b/lib/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart @@ -1,24 +1,21 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart'; abstract final class SpaceManagementCommunityDialogHelper { - static void showCreateDialog(BuildContext context) { + static void showCreateDialog(BuildContext context) => showDialog( + context: context, + builder: (_) => const CreateCommunityDialog(), + ); + + static void showEditDialog( + BuildContext context, + CommunityModel community, + ) { showDialog( context: context, - builder: (_) => CreateCommunityDialog( - title: const SelectableText('Community Name'), - onCreateCommunity: (community) { - context.read().add( - InsertCommunity(community), - ); - context.read().add( - SelectCommunityEvent(community: community), - ); - }, - ), + builder: (_) => EditCommunityDialog(community: community), ); } } diff --git a/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart b/lib/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart similarity index 55% rename from lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart rename to lib/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart index ab9f7b9a..f420abbd 100644 --- a/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart +++ b/lib/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart @@ -1,28 +1,28 @@ import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_name_text_field.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class CreateCommunityDialogWidget extends StatefulWidget { +class CommunityDialog extends StatefulWidget { final String? initialName; final Widget title; + final void Function(String name) onSubmit; + final String? errorMessage; - const CreateCommunityDialogWidget({ - super.key, + const CommunityDialog({ required this.title, + required this.onSubmit, this.initialName, + this.errorMessage, + super.key, }); @override - State createState() => - _CreateCommunityDialogWidgetState(); + State createState() => _CommunityDialogState(); } -class _CreateCommunityDialogWidgetState extends State { +class _CommunityDialogState extends State { late final TextEditingController _nameController; @override @@ -63,35 +63,31 @@ class _CreateCommunityDialogWidgetState extends State( - builder: (context, state) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - DefaultTextStyle( - style: Theme.of(context).textTheme.headlineMedium!, - child: widget.title, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DefaultTextStyle( + style: Theme.of(context).textTheme.headlineMedium!, + child: widget.title, + ), + const SizedBox(height: 18), + CreateCommunityNameTextField( + nameController: _nameController, + ), + if (widget.errorMessage != null) + Padding( + padding: const EdgeInsets.only(top: 18), + child: SelectableText( + '* ${widget.errorMessage}', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.error, + ), ), - const SizedBox(height: 18), - CreateCommunityNameTextField( - nameController: _nameController, - ), - if (state case CreateCommunityFailure(:final message)) - Padding( - padding: const EdgeInsets.only(top: 18), - child: SelectableText( - '* $message', - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: Theme.of(context).colorScheme.error, - ), - ), - ), - const SizedBox(height: 24), - _buildActionButtons(context), - ], - ); - }, + ), + const SizedBox(height: 24), + _buildActionButtons(context), + ], ), ), ), @@ -132,13 +128,7 @@ class _CreateCommunityDialogWidgetState extends State().add( - CreateCommunity( - CreateCommunityParam( - name: _nameController.text.trim(), - ), - ), - ); + widget.onSubmit.call(_nameController.text.trim()); } } } diff --git a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart index b457c413..4f71075b 100644 --- a/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart +++ b/lib/pages/space_management_v2/main_module/widgets/community_structure_header.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/helpers/space_management_community_dialog_helper.dart'; import 'package:syncrow_web/pages/space_management_v2/main_module/widgets/community_structure_header_action_buttons.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/space_details/presentation/helpers/space_details_dialog_helper.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; @@ -44,18 +44,6 @@ class CommunityStructureHeader extends StatelessWidget { ); } - void _showCreateCommunityDialog(BuildContext context) { - showDialog( - context: context, - builder: (context) => CreateCommunityDialog( - title: const Text('Edit Community'), - onCreateCommunity: (community) { - // TODO(FarisArmoush): Implement - }, - ), - ); - } - Widget _buildCommunityInfo( BuildContext context, ThemeData theme, double screenWidth) { final selectedCommunity = @@ -86,7 +74,12 @@ class CommunityStructureHeader extends StatelessWidget { ), const SizedBox(width: 2), GestureDetector( - onTap: () => _showCreateCommunityDialog(context), + onTap: () { + SpaceManagementCommunityDialogHelper.showEditDialog( + context, + selectedCommunity, + ); + }, child: SvgPicture.asset( Assets.iconEdit, width: 16, diff --git a/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart b/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart index 37f131b3..c2489bf6 100644 --- a/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart +++ b/lib/pages/space_management_v2/modules/communities/domain/models/community_model.dart @@ -39,6 +39,26 @@ class CommunityModel extends Equatable { .toList(); } + CommunityModel copyWith({ + String? uuid, + String? name, + DateTime? createdAt, + DateTime? updatedAt, + String? description, + String? externalId, + List? spaces, + }) { + return CommunityModel( + uuid: uuid ?? this.uuid, + name: name ?? this.name, + createdAt: createdAt ?? this.createdAt, + updatedAt: updatedAt ?? this.updatedAt, + description: description ?? this.description, + externalId: externalId ?? this.externalId, + spaces: spaces ?? this.spaces, + ); + } + @override List get props => [uuid, name, spaces]; } diff --git a/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart b/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart index a9af44d6..2949145a 100644 --- a/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart +++ b/lib/pages/space_management_v2/modules/create_community/presentation/create_community_dialog.dart @@ -1,28 +1,23 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/bloc/communities_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/presentation/communities_tree_selection_bloc/communities_tree_selection_bloc.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/data/services/remote_create_community_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/create_community/domain/param/create_community_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/bloc/create_community_bloc.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/create_community/presentation/create_community_dialog_widget.dart'; import 'package:syncrow_web/services/api/http_service.dart'; class CreateCommunityDialog extends StatelessWidget { - final void Function(CommunityModel community) onCreateCommunity; - final String? initialName; - final Widget title; - - const CreateCommunityDialog({ - super.key, - required this.onCreateCommunity, - required this.title, - this.initialName, - }); + const CreateCommunityDialog({super.key}); @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => CreateCommunityBloc(RemoteCreateCommunityService(HTTPService())), - child: BlocListener( + create: (_) => CreateCommunityBloc( + RemoteCreateCommunityService(HTTPService()), + ), + child: BlocConsumer( listener: (context, state) { switch (state) { case CreateCommunityLoading(): @@ -39,7 +34,12 @@ class CreateCommunityDialog extends StatelessWidget { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Community created successfully')), ); - onCreateCommunity.call(community); + context.read().add( + InsertCommunity(community), + ); + context.read().add( + SelectCommunityEvent(community: community), + ); break; case CreateCommunityFailure(): Navigator.of(context).pop(); @@ -48,10 +48,16 @@ class CreateCommunityDialog extends StatelessWidget { break; } }, - child: CreateCommunityDialogWidget( - title: title, - initialName: initialName, - ), + builder: (BuildContext context, CreateCommunityState state) { + return CommunityDialog( + title: const Text('Create Community'), + initialName: null, + onSubmit: (name) => context.read().add( + CreateCommunity(CreateCommunityParam(name: name)), + ), + errorMessage: state is CreateCommunityFailure ? state.message : null, + ); + }, ), ); } diff --git a/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart b/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart index 6c550673..f8f71afd 100644 --- a/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart +++ b/lib/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart @@ -1,6 +1,5 @@ import 'package:dio/dio.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; import 'package:syncrow_web/services/api/http_service.dart'; @@ -13,7 +12,7 @@ class RemoteUpdateCommunityService implements UpdateCommunityService { static const _defaultErrorMessage = 'Failed to update community'; @override - Future updateCommunity(UpdateCommunityParam param) async { + Future updateCommunity(CommunityModel param) async { try { final response = await _httpService.put( path: 'endpoint', diff --git a/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart b/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart deleted file mode 100644 index 69dfc4e2..00000000 --- a/lib/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:equatable/equatable.dart'; - -class UpdateCommunityParam extends Equatable { - const UpdateCommunityParam({required this.name}); - - final String name; - - @override - List get props => [name]; -} diff --git a/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart b/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart index 9703fdc6..d32e79b6 100644 --- a/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart +++ b/lib/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart @@ -1,6 +1,5 @@ import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart'; abstract class UpdateCommunityService { - Future updateCommunity(UpdateCommunityParam param); + Future updateCommunity(CommunityModel community); } diff --git a/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart index 4e913c22..6a4c2051 100644 --- a/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart +++ b/lib/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart @@ -1,7 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; -import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/params/update_community_param.dart'; import 'package:syncrow_web/pages/space_management_v2/modules/update_community/domain/services/update_community_service.dart'; import 'package:syncrow_web/services/api/api_exception.dart'; @@ -24,7 +23,7 @@ class UpdateCommunityBloc extends Bloc get props => [param]; + List get props => [communityModel]; } diff --git a/lib/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart b/lib/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart new file mode 100644 index 00000000..f42cea78 --- /dev/null +++ b/lib/pages/space_management_v2/modules/update_community/presentation/edit_community_dialog.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/space_management_v2/main_module/shared/widgets/community_dialog.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/communities/domain/models/community_model.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/data/services/remote_update_community_service.dart'; +import 'package:syncrow_web/pages/space_management_v2/modules/update_community/presentation/bloc/update_community_bloc.dart'; +import 'package:syncrow_web/services/api/http_service.dart'; + +class EditCommunityDialog extends StatelessWidget { + const EditCommunityDialog({required this.community, super.key}); + + final CommunityModel community; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => + UpdateCommunityBloc(RemoteUpdateCommunityService(HTTPService())), + child: BlocConsumer( + listener: (context, state) { + switch (state) { + case UpdateCommunityLoading(): + showDialog( + context: context, + builder: (context) => const Center( + child: CircularProgressIndicator(), + ), + ); + break; + case UpdateCommunitySuccess(:final community): + Navigator.of(context).pop(); + Navigator.of(context).pop(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('${community.name} updated successfully'), + ), + ); + break; + case UpdateCommunityFailure(): + Navigator.of(context).pop(); + break; + default: + break; + } + }, + builder: (context, state) => CommunityDialog( + title: const Text('Edit Community'), + initialName: community.name, + errorMessage: state is UpdateCommunityFailure ? state.message : null, + onSubmit: (name) => context.read().add( + UpdateCommunity(community.copyWith(name: name)), + ), + ), + ), + ); + } +}