diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart index 31e19af4..ee6018e6 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart @@ -87,6 +87,7 @@ class SpaceManagementBloc prevSpaceModels = List.from( (previousState as dynamic).spaceModels ?? [], ); + allSpaces.addAll(prevSpaceModels); } if (prevSpaceModels.isEmpty) { @@ -314,7 +315,6 @@ class SpaceManagementBloc SelectSpaceEvent event, Emitter emit, ) { - _handleCommunitySpaceStateUpdate( emit: emit, selectedCommunity: event.selectedCommunity, diff --git a/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart b/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart index e601cca4..3abab9cc 100644 --- a/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart @@ -64,11 +64,11 @@ class SpaceManagementPageState extends State { ); } else if (state is SpaceModelLoaded) { return LoadedSpaceView( - communities: state.communities, - products: state.products, - spaceModels: state.spaceModels, - shouldNavigateToSpaceModelPage: true, - ); + communities: state.communities, + products: state.products, + spaceModels: state.spaceModels, + shouldNavigateToSpaceModelPage: true, + ); } else if (state is SpaceManagementError) { return Center(child: Text('Error: ${state.errorMessage}')); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart index 795a140f..c83c9ca9 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart @@ -22,7 +22,9 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_li import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/duplicate_process_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/helper/connection_helper.dart'; import 'package:syncrow_web/pages/spaces_management/helper/space_helper.dart'; +import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -131,7 +133,7 @@ class _CommunityStructureAreaState extends State { communities: widget.communities, communityName: widget.selectedCommunity?.name, community: widget.selectedCommunity, - isSave: isSave(spaces), + isSave: SpaceHelper.isSave(spaces), isEditingName: isEditingName, nameController: _nameController, onSave: _saveSpaces, @@ -176,7 +178,8 @@ class _CommunityStructureAreaState extends State { children: [ for (var connection in connections) Opacity( - opacity: _isHighlightedConnection(connection) + opacity: ConnectionHelper.isHighlightedConnection( + connection, widget.selectedSpace) ? 1.0 : 0.3, // Adjust opacity child: CustomPaint( @@ -209,7 +212,8 @@ class _CommunityStructureAreaState extends State { }, buildSpaceContainer: (int index) { final bool isHighlighted = - _isHighlightedSpace(spaces[index]); + SpaceHelper.isHighlightedSpace( + spaces[index], widget.selectedSpace); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, @@ -295,7 +299,8 @@ class _CommunityStructureAreaState extends State { return CreateSpaceDialog( products: widget.products, spaceModels: widget.spaceModels, - allTags: _getAllTagValues(spaces), + allTags: + TagHelper.getAllTagValues(widget.communities, widget.spaceModels), parentSpace: parentIndex != null ? spaces[parentIndex] : null, onCreateSpace: (String name, String icon, @@ -306,7 +311,7 @@ class _CommunityStructureAreaState extends State { setState(() { // Set the first space in the center or use passed position Offset centerPosition = - position ?? _getCenterPosition(screenSize); + position ?? ConnectionHelper.getCenterPosition(screenSize); SpaceModel newSpace = SpaceModel( name: name, icon: icon, @@ -358,7 +363,8 @@ class _CommunityStructureAreaState extends State { tags: widget.selectedSpace?.tags, subspaces: widget.selectedSpace?.subspaces, isEdit: true, - allTags: _getAllTagValues(spaces), + allTags: TagHelper.getAllTagValues( + widget.communities, widget.spaceModels), onCreateSpace: (String name, String icon, List selectedProducts, @@ -527,17 +533,6 @@ class _CommunityStructureAreaState extends State { ); } - bool _isHighlightedSpace(SpaceModel space) { - final selectedSpace = widget.selectedSpace; - if (selectedSpace == null) return true; - - return space == selectedSpace || - selectedSpace.parent?.internalId == space.internalId || - selectedSpace.children - ?.any((child) => child.internalId == space.internalId) == - true; - } - void _deselectSpace(BuildContext context) { context.read().add( SelectSpaceEvent( @@ -545,28 +540,6 @@ class _CommunityStructureAreaState extends State { ); } - bool _isHighlightedConnection(Connection connection) { - if (widget.selectedSpace == null) return true; - - return connection.startSpace == widget.selectedSpace || - connection.endSpace == widget.selectedSpace; - } - - Offset _getCenterPosition(Size screenSize) { - return Offset( - screenSize.width / 2 - 260, - screenSize.height / 2 - 200, - ); - } - - bool isSave(List spaces) { - return spaces.isNotEmpty && - spaces.any((space) => - space.status == SpaceStatus.newSpace || - space.status == SpaceStatus.modified || - space.status == SpaceStatus.deleted); - } - void _onDuplicate(BuildContext parentContext) { final screenWidth = MediaQuery.of(context).size.width; @@ -652,11 +625,10 @@ class _CommunityStructureAreaState extends State { const double horizontalGap = 200.0; const double verticalGap = 100.0; - final Map nameCounters = {}; - SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition, SpaceModel? duplicatedParent) { - Offset newPosition = parentPosition + Offset(horizontalGap, 0); + Offset newPosition = + Offset(parentPosition.dx + horizontalGap, original.position.dy); while (spaces.any((s) => (s.position - newPosition).distance < horizontalGap && @@ -690,7 +662,7 @@ class _CommunityStructureAreaState extends State { final newConnection = Connection( startSpace: duplicatedParent, endSpace: duplicated, - direction: "down", + direction: original.incomingConnection?.direction ?? 'down', ); connections.add(newConnection); duplicated.incomingConnection = newConnection; @@ -729,10 +701,8 @@ class _CommunityStructureAreaState extends State { child.incomingConnection?.direction == "down" ?? false; if (isDownDirection && childrenWithDownDirection.length == 1) { - // Place the only "down" child vertically aligned with the parent childStartPosition = duplicated.position + Offset(0, verticalGap); } else if (!isDownDirection) { - // Position children with other directions horizontally childStartPosition = duplicated.position + Offset(horizontalGap, 0); } @@ -753,14 +723,4 @@ class _CommunityStructureAreaState extends State { duplicateRecursive(space, space.position, duplicatedParent); } } - - List _getAllTagValues(List spaces) { - final List allTags = []; - for (final space in spaces) { - if (space.tags != null) { - allTags.addAll(space.listAllTagValues()); - } - } - return allTags; - } } diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index a435a8fc..cd5ce4a5 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -83,8 +83,13 @@ class CreateSpaceDialogState extends State { widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; - tags = widget.tags ?? []; - subspaces = widget.subspaces ?? []; + if (widget.currentSpaceModel != null) { + subspaces = []; + tags = []; + } else { + tags = widget.tags ?? []; + subspaces = widget.subspaces ?? []; + } selectedSpaceModel = widget.currentSpaceModel; } @@ -461,6 +466,7 @@ class CreateSpaceDialogState extends State { builder: (context) => AssignTagDialog( products: widget.products, subspaces: subspaces, + allTags: widget.allTags, addedProducts: TagHelper .createInitialSelectedProductsForTags( tags ?? [], subspaces), diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart index 2d9222a6..3de906d3 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart @@ -57,6 +57,7 @@ class AssignTagBloc extends Bloc { if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); tags[event.index].tag = event.tag; + emit(AssignTagLoaded( tags: tags, isSaveEnabled: _validateTags(tags), @@ -78,6 +79,7 @@ class AssignTagBloc extends Bloc { emit(AssignTagLoaded( tags: tags, isSaveEnabled: _validateTags(tags), + errorMessage: _getValidationError(tags), )); } }); @@ -106,12 +108,13 @@ class AssignTagBloc extends Bloc { emit(AssignTagLoaded( tags: updatedTags, isSaveEnabled: _validateTags(updatedTags), + errorMessage: _getValidationError(updatedTags), )); } else { emit(const AssignTagLoaded( - tags: [], - isSaveEnabled: false, - )); + tags: [], + isSaveEnabled: false, + errorMessage: 'Failed to delete tag')); } }); } @@ -125,7 +128,10 @@ class AssignTagBloc extends Bloc { String? _getValidationError(List tags) { final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty); - if (hasEmptyTag) return 'Tags cannot be empty.'; + if (hasEmptyTag) { + return 'Tags cannot be empty.'; + } + final duplicateTags = tags .map((tag) => tag.tag?.trim() ?? '') .fold>({}, (map, tag) { diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart index 19cf4435..1ae0ea90 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart @@ -21,11 +21,11 @@ class AssignTagLoaded extends AssignTagState { const AssignTagLoaded({ required this.tags, required this.isSaveEnabled, - this.errorMessage, + required this.errorMessage, }); @override - List get props => [tags, isSaveEnabled]; + List get props => [tags, isSaveEnabled, errorMessage ?? '']; } class AssignTagError extends AssignTagState { diff --git a/lib/pages/spaces_management/helper/connection_helper.dart b/lib/pages/spaces_management/helper/connection_helper.dart new file mode 100644 index 00000000..7de8d80b --- /dev/null +++ b/lib/pages/spaces_management/helper/connection_helper.dart @@ -0,0 +1,21 @@ +import 'dart:ui'; + +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; + +class ConnectionHelper { + static Offset getCenterPosition(Size screenSize) { + return Offset( + screenSize.width / 2 - 260, + screenSize.height / 2 - 200, + ); + } + + static bool isHighlightedConnection( + Connection connection, SpaceModel? selectedSpace) { + if (selectedSpace == null) return true; + + return connection.startSpace == selectedSpace || + connection.endSpace == selectedSpace; + } +} diff --git a/lib/pages/spaces_management/helper/space_helper.dart b/lib/pages/spaces_management/helper/space_helper.dart index 75aebce9..c316106e 100644 --- a/lib/pages/spaces_management/helper/space_helper.dart +++ b/lib/pages/spaces_management/helper/space_helper.dart @@ -40,4 +40,22 @@ class SpaceHelper { return "$baseName(${maxNumber + 1})"; } + + static bool isSave(List spaces) { + return spaces.isNotEmpty && + spaces.any((space) => + space.status == SpaceStatus.newSpace || + space.status == SpaceStatus.modified || + space.status == SpaceStatus.deleted); + } + + static bool isHighlightedSpace(SpaceModel space, SpaceModel? selectedSpace) { + if (selectedSpace == null) return true; + + return space == selectedSpace || + selectedSpace.parent?.internalId == space.internalId || + selectedSpace.children + ?.any((child) => child.internalId == space.internalId) == + true; + } } diff --git a/lib/pages/spaces_management/helper/tag_helper.dart b/lib/pages/spaces_management/helper/tag_helper.dart index 757f46e1..9c0299ba 100644 --- a/lib/pages/spaces_management/helper/tag_helper.dart +++ b/lib/pages/spaces_management/helper/tag_helper.dart @@ -1,8 +1,11 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; @@ -337,4 +340,25 @@ class TagHelper { checkTagExistInSubspace: checkTagExistInSubspace, ); } + + static List getAllTagValues( + List communities, List? spaceModels) { + final Set allTags = {}; + + if (spaceModels != null) { + for (var model in spaceModels) { + allTags.addAll(model.listAllTagValues()); + } + } + + for (final community in communities) { + for (final space in community.spaces) { + if (space.tags != null) { + allTags.addAll(space.listAllTagValues()); + } + } + } + + return allTags.toList(); + } }