diff --git a/lib/common/widgets/custom_expansion_tile.dart b/lib/common/widgets/custom_expansion_tile.dart index 8df9b663..3823b18f 100644 --- a/lib/common/widgets/custom_expansion_tile.dart +++ b/lib/common/widgets/custom_expansion_tile.dart @@ -56,24 +56,6 @@ class CustomExpansionTileState extends State { children: [ Row( children: [ - // Checkbox with independent state management - Checkbox( - value: false, - onChanged: (bool? value) { - setState(() {}); - }, - side: WidgetStateBorderSide.resolveWith((states) { - return const BorderSide(color: ColorsManager.grayBorder); - }), - fillColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.selected)) { - return ColorsManager.grayBorder; - } else { - return ColorsManager.checkBoxFillColor; - } - }), - checkColor: ColorsManager.whiteColors, - ), // Expand/collapse icon, now wrapped in a GestureDetector for specific onTap if (widget.children != null && widget.children!.isNotEmpty) GestureDetector( @@ -84,7 +66,9 @@ class CustomExpansionTileState extends State { }); }, child: Icon( - _isExpanded ? Icons.keyboard_arrow_down : Icons.keyboard_arrow_right, + _isExpanded + ? Icons.keyboard_arrow_down + : Icons.keyboard_arrow_right, color: ColorsManager.lightGrayColor, size: 16.0, // Adjusted size for better alignment ), @@ -101,8 +85,10 @@ class CustomExpansionTileState extends State { _capitalizeFirstLetter(widget.title), style: TextStyle( color: widget.isSelected - ? ColorsManager.blackColor // Change color to black when selected - : ColorsManager.lightGrayColor, // Gray when not selected + ? ColorsManager + .blackColor // Change color to black when selected + : ColorsManager + .lightGrayColor, // Gray when not selected fontWeight: FontWeight.w400, ), ), @@ -111,7 +97,9 @@ class CustomExpansionTileState extends State { ], ), // The expanded section (children) that shows when the tile is expanded - if (_isExpanded && widget.children != null && widget.children!.isNotEmpty) + if (_isExpanded && + widget.children != null && + widget.children!.isNotEmpty) Padding( padding: const EdgeInsets.only(left: 48.0), // Indented children child: Column( diff --git a/lib/pages/access_management/bloc/access_bloc.dart b/lib/pages/access_management/bloc/access_bloc.dart index 4b1c37d6..562bd5b5 100644 --- a/lib/pages/access_management/bloc/access_bloc.dart +++ b/lib/pages/access_management/bloc/access_bloc.dart @@ -8,9 +8,6 @@ import 'package:syncrow_web/pages/common/hour_picker_dialog.dart'; import 'package:syncrow_web/services/access_mang_api.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/app_enum.dart'; -import 'package:syncrow_web/utils/constants/strings_manager.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; -import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; class AccessBloc extends Bloc { diff --git a/lib/pages/space_tree/bloc/space_tree_bloc.dart b/lib/pages/space_tree/bloc/space_tree_bloc.dart index e7fb4dc1..31adaeb1 100644 --- a/lib/pages/space_tree/bloc/space_tree_bloc.dart +++ b/lib/pages/space_tree/bloc/space_tree_bloc.dart @@ -16,6 +16,33 @@ class SpaceTreeBloc extends Bloc { on(_onSearch); on(_clearAllData); on(_clearCachedData); + on(_onCommunityAdded); + on(_onCommunityUpdate); + } + + void _onCommunityUpdate( + OnCommunityUpdated event, + Emitter emit, + ) async { + emit(SpaceTreeLoadingState()); + + try { + final updatedCommunity = event.updatedCommunity; + final updatedCommunities = + List.from(state.communityList); + + final index = updatedCommunities + .indexWhere((community) => community.uuid == updatedCommunity.uuid); + + if (index != -1) { + updatedCommunities[index] = updatedCommunity; + emit(state.copyWith(communitiesList: updatedCommunities)); + } else { + emit(SpaceTreeErrorState('Community not found in the list.')); + } + } catch (e) { + emit(SpaceTreeErrorState('Error updating community: $e')); + } } _fetchSpaces(InitialEvent event, Emitter emit) async { @@ -28,8 +55,8 @@ class SpaceTreeBloc extends Bloc { List updatedCommunities = await Future.wait( communities.map((community) async { - List spaces = - await CommunitySpaceManagementApi().getSpaceHierarchy(community.uuid, projectUuid); + List spaces = await CommunitySpaceManagementApi() + .getSpaceHierarchy(community.uuid, projectUuid); return CommunityModel( uuid: community.uuid, @@ -44,15 +71,27 @@ class SpaceTreeBloc extends Bloc { ); emit(state.copyWith( - communitiesList: updatedCommunities, expandedCommunity: [], expandedSpaces: [])); + communitiesList: updatedCommunities, + expandedCommunity: [], + expandedSpaces: [])); } catch (e) { emit(SpaceTreeErrorState('Error loading communities and spaces: $e')); } } - _onCommunityExpanded(OnCommunityExpanded event, Emitter emit) async { + void _onCommunityAdded( + OnCommunityAdded event, Emitter emit) async { + final updatedCommunities = List.from(state.communityList); + updatedCommunities.add(event.newCommunity); + + emit(state.copyWith(communitiesList: updatedCommunities)); + } + + _onCommunityExpanded( + OnCommunityExpanded event, Emitter emit) async { try { - List updatedExpandedCommunityList = List.from(state.expandedCommunities); + List updatedExpandedCommunityList = + List.from(state.expandedCommunities); if (updatedExpandedCommunityList.contains(event.communityId)) { updatedExpandedCommunityList.remove(event.communityId); @@ -84,14 +123,19 @@ class SpaceTreeBloc extends Bloc { } } - _onCommunitySelected(OnCommunitySelected event, Emitter emit) async { + _onCommunitySelected( + OnCommunitySelected event, Emitter emit) async { try { List updatedSelectedCommunities = List.from(state.selectedCommunities.toSet().toList()); - List updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList()); - List updatedSoldChecks = List.from(state.soldCheck.toSet().toList()); - Map> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces); - List selectedSpacesInCommunity = communityAndSpaces[event.communityId] ?? []; + List updatedSelectedSpaces = + List.from(state.selectedSpaces.toSet().toList()); + List updatedSoldChecks = + List.from(state.soldCheck.toSet().toList()); + Map> communityAndSpaces = + Map.from(state.selectedCommunityAndSpaces); + List selectedSpacesInCommunity = + communityAndSpaces[event.communityId] ?? []; List childrenIds = _getAllChildIds(event.children); @@ -124,11 +168,15 @@ class SpaceTreeBloc extends Bloc { try { List updatedSelectedCommunities = List.from(state.selectedCommunities.toSet().toList()); - List updatedSelectedSpaces = List.from(state.selectedSpaces.toSet().toList()); - List updatedSoldChecks = List.from(state.soldCheck.toSet().toList()); - Map> communityAndSpaces = Map.from(state.selectedCommunityAndSpaces); + List updatedSelectedSpaces = + List.from(state.selectedSpaces.toSet().toList()); + List updatedSoldChecks = + List.from(state.soldCheck.toSet().toList()); + Map> communityAndSpaces = + Map.from(state.selectedCommunityAndSpaces); - List selectedSpacesInCommunity = communityAndSpaces[event.communityModel.uuid] ?? []; + List selectedSpacesInCommunity = + communityAndSpaces[event.communityModel.uuid] ?? []; List childrenIds = _getAllChildIds(event.children); bool isChildSelected = false; @@ -151,9 +199,11 @@ class SpaceTreeBloc extends Bloc { selectedSpacesInCommunity.addAll(childrenIds); } - List spaces = _getThePathToChild(event.communityModel.uuid, event.spaceId); + List spaces = + _getThePathToChild(event.communityModel.uuid, event.spaceId); for (String space in spaces) { - if (!updatedSelectedSpaces.contains(space) && !updatedSoldChecks.contains(space)) { + if (!updatedSelectedSpaces.contains(space) && + !updatedSoldChecks.contains(space)) { updatedSoldChecks.add(space); } } @@ -176,7 +226,9 @@ class SpaceTreeBloc extends Bloc { updatedSoldChecks.remove(event.spaceId); List parents = - _getThePathToChild(event.communityModel.uuid, event.spaceId).toSet().toList(); + _getThePathToChild(event.communityModel.uuid, event.spaceId) + .toSet() + .toList(); if (updatedSelectedSpaces.isEmpty) { updatedSoldChecks.removeWhere(parents.contains); @@ -184,7 +236,8 @@ class SpaceTreeBloc extends Bloc { } else { // Check if any parent has selected children for (String space in parents) { - if (!_noChildrenSelected(event.communityModel, space, updatedSelectedSpaces, parents)) { + if (!_noChildrenSelected( + event.communityModel, space, updatedSelectedSpaces, parents)) { updatedSoldChecks.remove(space); } } @@ -209,8 +262,8 @@ class SpaceTreeBloc extends Bloc { } } - _noChildrenSelected( - CommunityModel community, String spaceId, List selectedSpaces, List parents) { + _noChildrenSelected(CommunityModel community, String spaceId, + List selectedSpaces, List parents) { if (selectedSpaces.contains(spaceId)) { return true; } @@ -237,10 +290,11 @@ class SpaceTreeBloc extends Bloc { // Filter communities and expand only those that match the query filteredCommunity = communities.where((community) { - final containsQueryInCommunity = - community.name.toLowerCase().contains(event.searchQuery.toLowerCase()); - final containsQueryInSpaces = - community.spaces.any((space) => _containsQuery(space, event.searchQuery.toLowerCase())); + final containsQueryInCommunity = community.name + .toLowerCase() + .contains(event.searchQuery.toLowerCase()); + final containsQueryInSpaces = community.spaces.any( + (space) => _containsQuery(space, event.searchQuery.toLowerCase())); return containsQueryInCommunity || containsQueryInSpaces; }).toList(); @@ -293,8 +347,8 @@ class SpaceTreeBloc extends Bloc { // Helper function to determine if any space or its children match the search query bool _containsQuery(SpaceModel space, String query) { final matchesSpace = space.name.toLowerCase().contains(query); - final matchesChildren = - space.children.any((child) => _containsQuery(child, query)); // Recursive check for children + final matchesChildren = space.children.any((child) => + _containsQuery(child, query)); // Recursive check for children return matchesSpace || matchesChildren; } @@ -317,8 +371,8 @@ class SpaceTreeBloc extends Bloc { return children; } - bool _anySpacesSelectedInCommunity( - CommunityModel community, List selectedSpaces, List partialCheckedList) { + bool _anySpacesSelectedInCommunity(CommunityModel community, + List selectedSpaces, List partialCheckedList) { bool result = false; List ids = _getAllChildIds(community.spaces); for (var id in ids) { @@ -347,7 +401,8 @@ class SpaceTreeBloc extends Bloc { return ids; } - List _getAllParentsIds(SpaceModel child, String spaceId, List listIds) { + List _getAllParentsIds( + SpaceModel child, String spaceId, List listIds) { List ids = listIds; ids.add(child.uuid ?? ''); diff --git a/lib/pages/space_tree/bloc/space_tree_event.dart b/lib/pages/space_tree/bloc/space_tree_event.dart index e8fa996f..fdf1240b 100644 --- a/lib/pages/space_tree/bloc/space_tree_event.dart +++ b/lib/pages/space_tree/bloc/space_tree_event.dart @@ -69,6 +69,23 @@ class SearchQueryEvent extends SpaceTreeEvent { List get props => [searchQuery]; } +class OnCommunityAdded extends SpaceTreeEvent { + final CommunityModel newCommunity; + const OnCommunityAdded(this.newCommunity); + + @override + List get props => [newCommunity]; +} + +class OnCommunityUpdated extends SpaceTreeEvent { + final CommunityModel updatedCommunity; + const OnCommunityUpdated(this.updatedCommunity); + + @override + List get props => [updatedCommunity]; +} + + class ClearAllData extends SpaceTreeEvent {} class ClearCachedData extends SpaceTreeEvent {} 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 358bdd48..49fa5d72 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 @@ -1,8 +1,8 @@ -import 'dart:developer'; - +import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; +import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; +import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/create_subspace_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; @@ -17,10 +17,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_updat import 'package:syncrow_web/services/product_api.dart'; import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; -import 'package:syncrow_web/utils/constants/action_enum.dart'; -import 'package:syncrow_web/utils/constants/strings_manager.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; -import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; +import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action; class SpaceManagementBloc extends Bloc { @@ -29,11 +26,16 @@ class SpaceManagementBloc final SpaceModelManagementApi _spaceModelApi; List? _cachedProducts; + List? _cachedSpaceModels; + final SpaceTreeBloc _spaceTreeBloc; - SpaceManagementBloc(this._api, this._productApi, this._spaceModelApi) - : super(SpaceManagementInitial()) { + SpaceManagementBloc( + this._api, + this._productApi, + this._spaceModelApi, + this._spaceTreeBloc, + ) : super(SpaceManagementInitial()) { on(_onLoadCommunityAndSpaces); - on(_onUpdateSpacePosition); on(_onCreateCommunity); on(_onSelectCommunity); on(_onCommunityDelete); @@ -44,18 +46,94 @@ class SpaceManagementBloc on(_onNewCommunity); on(_onBlankState); on(_onLoadSpaceModel); + on(_updateSpaceModelCache); + on(_deleteSpaceModelFromCache); } - void _logEvent(String eventName) { - log('Event Triggered: $eventName'); + Future _updateSpaceModelCache( + UpdateSpaceModelCache event, Emitter emit) async { + if (_cachedSpaceModels != null) { + _cachedSpaceModels = _cachedSpaceModels!.map((model) { + return model.uuid == event.updatedModel.uuid + ? event.updatedModel + : model; + }).toList(); + } else { + _cachedSpaceModels = await fetchSpaceModels(); + } + + emit(SpaceModelLoaded( + communities: state is SpaceManagementLoaded + ? (state as SpaceManagementLoaded).communities + : [], + products: _cachedProducts ?? [], + spaceModels: List.from(_cachedSpaceModels ?? []), + )); + } + + void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event, + Emitter emit) async { + if (_cachedSpaceModels != null) { + _cachedSpaceModels = _cachedSpaceModels! + .where((model) => model.uuid != event.deletedUuid) + .toList(); + } else { + _cachedSpaceModels = await fetchSpaceModels(); + } + + emit(SpaceModelLoaded( + communities: state is SpaceManagementLoaded + ? (state as SpaceManagementLoaded).communities + : [], + products: _cachedProducts ?? [], + spaceModels: List.from(_cachedSpaceModels ?? []), + )); + } + + void updateCachedSpaceModels(List updatedModels) { + _cachedSpaceModels = List.from(updatedModels); + } + + void addToCachedSpaceModels(SpaceTemplateModel newModel) { + _cachedSpaceModels?.add(newModel); + } + + Future> fetchSpaceModels() async { + try { + if (_cachedSpaceModels != null) { + return _cachedSpaceModels!; + } + + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + + List allSpaceModels = []; + + bool hasNext = true; + int page = 1; + + while (hasNext) { + final spaceModels = await _spaceModelApi.listSpaceModels( + page: page, projectId: projectUuid); + if (spaceModels.isNotEmpty) { + allSpaceModels.addAll(spaceModels); + page++; + } else { + hasNext = false; + } + } + + _cachedSpaceModels = allSpaceModels; + + return allSpaceModels; + } catch (e) { + return []; + } } void _onUpdateCommunity( UpdateCommunityEvent event, Emitter emit, ) async { - _logEvent('UpdateCommunityEvent'); - final previousState = state; try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; @@ -70,11 +148,13 @@ class SpaceManagementBloc for (var community in updatedCommunities) { if (community.uuid == event.communityUuid) { community.name = event.name; + _spaceTreeBloc.add(OnCommunityAdded(community)); + break; } } - var prevSpaceModels = await fetchSpaceModels(previousState); + var prevSpaceModels = await fetchSpaceModels(); emit(SpaceManagementLoaded( communities: updatedCommunities, @@ -91,46 +171,6 @@ class SpaceManagementBloc } } - Future> fetchSpaceModels( - SpaceManagementState previousState) async { - try { - final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - - List allSpaces = []; - List prevSpaceModels = []; - - if (previousState is SpaceManagementLoaded || - previousState is BlankState) { - prevSpaceModels = List.from( - (previousState as dynamic).spaceModels ?? [], - ); - allSpaces.addAll(prevSpaceModels); - } - - if (prevSpaceModels.isEmpty) { - bool hasNext = true; - int page = 1; - - while (hasNext) { - final spaces = await _spaceModelApi.listSpaceModels( - page: page, projectId: projectUuid); - if (spaces.isNotEmpty) { - allSpaces.addAll(spaces); - page++; - } else { - hasNext = false; - } - } - prevSpaceModels = await _spaceModelApi.listSpaceModels( - page: 1, projectId: projectUuid); - } - - return allSpaces; - } catch (e) { - return []; - } - } - void _onloadProducts() async { if (_cachedProducts == null) { final products = await _productApi.fetchProducts(); @@ -168,7 +208,7 @@ class SpaceManagementBloc return; } - var prevSpaceModels = await fetchSpaceModels(previousState); + var prevSpaceModels = await fetchSpaceModels(); emit(BlankState( communities: event.communities, @@ -185,8 +225,10 @@ class SpaceManagementBloc try { final previousState = state; final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + var spaceBloc = event.context.read(); + List communities = await _waitForCommunityList(spaceBloc); - var prevSpaceModels = await fetchSpaceModels(previousState); + var prevSpaceModels = await fetchSpaceModels(); if (previousState is SpaceManagementLoaded || previousState is BlankState) { @@ -199,25 +241,31 @@ class SpaceManagementBloc return; } - final communities = await _api.fetchCommunities(projectUuid); - final updatedCommunities = - await Future.wait(communities.map((community) async { - final spaces = await _fetchSpacesForCommunity(community.uuid); + if (communities.isEmpty) { + communities = await _api.fetchCommunities(projectUuid); - return CommunityModel( - uuid: community.uuid, - createdAt: community.createdAt, - updatedAt: community.updatedAt, - name: community.name, - description: community.description, - spaces: spaces, - region: community.region, + List updatedCommunities = await Future.wait( + communities.map((community) async { + List spaces = + await _fetchSpacesForCommunity(community.uuid); + return CommunityModel( + uuid: community.uuid, + createdAt: community.createdAt, + updatedAt: community.updatedAt, + name: community.name, + description: community.description, + spaces: spaces, + region: community.region, + ); + }).toList(), ); - })); + + communities = updatedCommunities; + } emit(BlankState( spaceModels: prevSpaceModels, - communities: updatedCommunities, + communities: communities, products: _cachedProducts ?? [], )); } catch (error) { @@ -229,41 +277,39 @@ class SpaceManagementBloc LoadCommunityAndSpacesEvent event, Emitter emit, ) async { - _logEvent('LoadCommunityAndSpacesEvent'); + var spaceBloc = event.context.read(); + _onloadProducts(); - var prevState = state; - emit(SpaceManagementLoading()); - try { - final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + // Wait until `communityList` is loaded + List communities = await _waitForCommunityList(spaceBloc); - _onloadProducts(); - List communities = - await _api.fetchCommunities(projectUuid); + // Fetch space models after communities are available + final prevSpaceModels = await fetchSpaceModels(); + emit(SpaceManagementLoaded( + communities: communities, + products: _cachedProducts ?? [], + spaceModels: prevSpaceModels, + )); + } - List updatedCommunities = await Future.wait( - communities.map((community) async { - List spaces = - await _fetchSpacesForCommunity(community.uuid); - return CommunityModel( - uuid: community.uuid, - createdAt: community.createdAt, - updatedAt: community.updatedAt, - name: community.name, - description: community.description, - spaces: spaces, // New spaces list - region: community.region, - ); - }).toList(), - ); - - final prevSpaceModels = await fetchSpaceModels(prevState); - emit(SpaceManagementLoaded( - communities: updatedCommunities, - products: _cachedProducts ?? [], - spaceModels: prevSpaceModels)); - } catch (e) { - emit(SpaceManagementError('Error loading communities and spaces: $e')); + Future> _waitForCommunityList( + SpaceTreeBloc spaceBloc) async { + // Check if communityList is already populated + if (spaceBloc.state.communityList.isNotEmpty) { + return spaceBloc.state.communityList; } + + final completer = Completer>(); + final subscription = spaceBloc.stream.listen((state) { + if (state.communityList.isNotEmpty) { + completer.complete(state.communityList); + } + }); + + // Return the list once available, then cancel the listener + final communities = await completer.future; + await subscription.cancel(); + return communities; } void _onCommunityDelete( @@ -277,7 +323,7 @@ class SpaceManagementBloc final success = await _api.deleteCommunity(event.communityUuid, projectUuid); if (success) { - add(LoadCommunityAndSpacesEvent()); + // add(LoadCommunityAndSpacesEvent()); } else { emit(const SpaceManagementError('Failed to delete the community.')); } @@ -287,11 +333,6 @@ class SpaceManagementBloc } } - void _onUpdateSpacePosition( - UpdateSpacePositionEvent event, - Emitter emit, - ) {} - void _onCreateCommunity( CreateCommunityEvent event, Emitter emit, @@ -304,7 +345,7 @@ class SpaceManagementBloc CommunityModel? newCommunity = await _api.createCommunity( event.name, event.description, projectUuid); - var prevSpaceModels = await fetchSpaceModels(previousState); + var prevSpaceModels = await fetchSpaceModels(); if (newCommunity != null) { if (previousState is SpaceManagementLoaded || @@ -313,6 +354,8 @@ class SpaceManagementBloc (previousState as dynamic).communities, ); final updatedCommunities = prevCommunities..add(newCommunity); + _spaceTreeBloc.add(OnCommunityAdded(newCommunity)); + emit(SpaceManagementLoaded( spaceModels: prevSpaceModels, communities: updatedCommunities, @@ -407,8 +450,6 @@ class SpaceManagementBloc event.communityUuid, emit, ); - } else { - add(LoadCommunityAndSpacesEvent()); } } catch (e) { emit(SpaceManagementError('Error saving spaces: $e')); @@ -425,13 +466,15 @@ class SpaceManagementBloc String communityUuid, Emitter emit, ) async { - var prevSpaceModels = await fetchSpaceModels(previousState); + var prevSpaceModels = await fetchSpaceModels(); final communities = List.from(previousState.communities); for (var community in communities) { if (community.uuid == communityUuid) { community.spaces = allSpaces; + _spaceTreeBloc.add(OnCommunityUpdated(community)); + emit(SpaceManagementLoaded( communities: communities, products: _cachedProducts ?? [], @@ -483,13 +526,15 @@ class SpaceManagementBloc .any((subspace) => subspace.uuid == prevSubspace.uuid); if (!existsInNew) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.delete, uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, + uuid: prevSubspace.uuid)); } } } else if (prevSubspaces != null && newSubspaces == null) { for (var prevSubspace in prevSubspaces) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.delete, uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, + uuid: prevSubspace.uuid)); } } @@ -502,14 +547,14 @@ class SpaceManagementBloc if (newSubspace.tags != null) { for (var tag in newSubspace.tags!) { tagUpdates.add(TagModelUpdate( - action: Action.add, + action: custom_action.Action.add, uuid: tag.uuid == '' ? null : tag.uuid, tag: tag.tag, productUuid: tag.product?.uuid)); } } subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.add, + action: custom_action.Action.add, subspaceName: newSubspace.subspaceName, tags: tagUpdates)); } @@ -528,7 +573,7 @@ class SpaceManagementBloc final List tagSubspaceUpdates = processTagUpdates(prevSubspace.tags, newSubspace.tags); subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.update, + action: custom_action.Action.update, uuid: newSubspace.uuid, subspaceName: newSubspace.subspaceName, tags: tagSubspaceUpdates)); @@ -615,35 +660,42 @@ class SpaceManagementBloc void _onLoadSpaceModel( SpaceModelLoadEvent event, Emitter emit) async { emit(SpaceManagementLoading()); + try { var prevState = state; final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + var spaceBloc = event.context.read(); + List communities = spaceBloc.state.communityList; - List communities = - await _api.fetchCommunities(projectUuid); + var prevSpaceModels = await fetchSpaceModels(); - List updatedCommunities = await Future.wait( - communities.map((community) async { - List spaces = - await _fetchSpacesForCommunity(community.uuid); - return CommunityModel( - uuid: community.uuid, - createdAt: community.createdAt, - updatedAt: community.updatedAt, - name: community.name, - description: community.description, - spaces: spaces, // New spaces list - region: community.region, - ); - }).toList(), - ); + if (communities.isEmpty) { + communities = await _api.fetchCommunities(projectUuid); - var prevSpaceModels = await fetchSpaceModels(prevState); + List updatedCommunities = await Future.wait( + communities.map((community) async { + List spaces = + await _fetchSpacesForCommunity(community.uuid); + return CommunityModel( + uuid: community.uuid, + createdAt: community.createdAt, + updatedAt: community.updatedAt, + name: community.name, + description: community.description, + spaces: spaces, + region: community.region, + ); + }).toList(), + ); + + communities = updatedCommunities; + } emit(SpaceModelLoaded( - communities: updatedCommunities, - products: _cachedProducts ?? [], - spaceModels: prevSpaceModels)); + communities: communities, + products: _cachedProducts ?? [], + spaceModels: prevSpaceModels, + )); } catch (e) { emit(SpaceManagementError('Error loading communities and spaces: $e')); } @@ -659,7 +711,7 @@ class SpaceManagementBloc if (prevTags == null && newTags != null) { for (var newTag in newTags) { tagUpdates.add(TagModelUpdate( - action: Action.add, + action: custom_action.Action.add, tag: newTag.tag, uuid: newTag.uuid, productUuid: newTag.product?.uuid, @@ -675,14 +727,14 @@ class SpaceManagementBloc final existsInNew = newTags.any((newTag) => newTag.uuid == prevTag.uuid); if (!existsInNew) { - tagUpdates - .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate( + action: custom_action.Action.delete, uuid: prevTag.uuid)); } } } else if (prevTags != null && newTags == null) { for (var prevTag in prevTags) { - tagUpdates - .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate( + action: custom_action.Action.delete, uuid: prevTag.uuid)); } } @@ -695,7 +747,7 @@ class SpaceManagementBloc if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) && !processedTags.contains(newTag.tag)) { tagUpdates.add(TagModelUpdate( - action: Action.add, + action: custom_action.Action.add, tag: newTag.tag, uuid: newTag.uuid == '' ? null : newTag.uuid, productUuid: newTag.product?.uuid)); @@ -712,7 +764,7 @@ class SpaceManagementBloc final newTag = newTagMap[prevTag.uuid]; if (newTag != null) { tagUpdates.add(TagModelUpdate( - action: Action.update, + action: custom_action.Action.update, uuid: newTag.uuid, tag: newTag.tag, )); diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_event.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_event.dart index d25534b4..6bd685ba 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_event.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_event.dart @@ -1,16 +1,23 @@ import 'package:equatable/equatable.dart'; import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; // Import for Offset +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; // Import for Offset abstract class SpaceManagementEvent extends Equatable { const SpaceManagementEvent(); @override - List get props => []; + List get props => []; } -class LoadCommunityAndSpacesEvent extends SpaceManagementEvent {} +class LoadCommunityAndSpacesEvent extends SpaceManagementEvent { + final BuildContext context; + + const LoadCommunityAndSpacesEvent(this.context); + @override + List get props => [context]; +} class DeleteCommunityEvent extends SpaceManagementEvent { final String communityUuid; @@ -74,14 +81,12 @@ class UpdateSpacePositionEvent extends SpaceManagementEvent { class CreateCommunityEvent extends SpaceManagementEvent { final String name; final String description; + final BuildContext context; - const CreateCommunityEvent({ - required this.name, - required this.description, - }); + const CreateCommunityEvent(this.name, this.description, this.context); @override - List get props => [name, description]; + List get props => [name, description, context]; } class UpdateCommunityEvent extends SpaceManagementEvent { @@ -141,7 +146,28 @@ class LoadSpaceHierarchyEvent extends SpaceManagementEvent { List get props => [communityId]; } +class BlankStateEvent extends SpaceManagementEvent { + final BuildContext context; -class BlankStateEvent extends SpaceManagementEvent {} + const BlankStateEvent(this.context); + @override + List get props => [context]; +} -class SpaceModelLoadEvent extends SpaceManagementEvent {} +class SpaceModelLoadEvent extends SpaceManagementEvent { + final BuildContext context; + + const SpaceModelLoadEvent(this.context); + @override + List get props => [context]; +} + +class UpdateSpaceModelCache extends SpaceManagementEvent { + final SpaceTemplateModel updatedModel; + UpdateSpaceModelCache(this.updatedModel); +} + +class DeleteSpaceModelFromCache extends SpaceManagementEvent { + final String deletedUuid; + DeleteSpaceModelFromCache(this.deletedUuid); +} \ No newline at end of file diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart index 571651e5..674fce4c 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart @@ -30,10 +30,6 @@ class SpaceManagementLoaded extends SpaceManagementState { this.spaceModels}); } -class SpaceModelManagenetLoaded extends SpaceManagementState { - SpaceModelManagenetLoaded(); -} - class BlankState extends SpaceManagementState { final List communities; final List products; @@ -75,3 +71,4 @@ class SpaceModelLoaded extends SpaceManagementState { @override List get props => [communities, products, spaceModels]; } + 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 42eb3ab7..0a847837 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 @@ -1,6 +1,9 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; +import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; @@ -29,14 +32,15 @@ class SpaceManagementPageState extends State { return MultiBlocProvider( providers: [ BlocProvider( - create: (_) => SpaceManagementBloc( + create: (context) => SpaceManagementBloc( _api, _productApi, _spaceModelApi, - )..add(LoadCommunityAndSpacesEvent()), + BlocProvider.of(context), + )..add(LoadCommunityAndSpacesEvent(this.context)), ), BlocProvider( - create: (_) => CenterBodyBloc(), + create: (context) => CenterBodyBloc(), ), ], child: WebScaffold( @@ -60,6 +64,7 @@ class SpaceManagementPageState extends State { shouldNavigateToSpaceModelPage: false, ); } else if (state is SpaceManagementLoaded) { + return LoadedSpaceView( communities: state.communities, selectedCommunity: state.selectedCommunity, diff --git a/lib/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart index 999c27bd..66f1a026 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart @@ -73,17 +73,14 @@ class _BlankCommunityWidgetState extends State { context: parentContext, builder: (context) => CreateCommunityDialog( isEditMode: false, - existingCommunityNames: widget.communities.map((community) => community.name).toList(), + existingCommunityNames: + widget.communities.map((community) => community.name).toList(), onCreateCommunity: (String communityName, String description) { parentContext.read().add( - CreateCommunityEvent( - name: communityName, - description: description, - ), + CreateCommunityEvent(communityName, description, context), ); }, ), ); } - } diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_tile.dart b/lib/pages/spaces_management/all_spaces/widgets/community_tile.dart index 69a723c0..9bd45671 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_tile.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_tile.dart @@ -21,15 +21,17 @@ class CommunityTile extends StatelessWidget { @override Widget build(BuildContext context) { - return CustomExpansionTile( - title: title, - initiallyExpanded: isExpanded, - isSelected: isSelected, - onExpansionChanged: (bool expanded) { - onExpansionChanged(title, expanded); - }, - onItemSelected: onItemSelected, - children: children ?? [], - ); + return Padding( + padding: const EdgeInsets.only(left: 16.0), + child: CustomExpansionTile( + title: title, + initiallyExpanded: isExpanded, + isSelected: isSelected, + onExpansionChanged: (bool expanded) { + onExpansionChanged(title, expanded); + }, + onItemSelected: onItemSelected, + children: children ?? [], + )); } } diff --git a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart index dccd7fd9..4d6fc3fb 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart @@ -40,6 +40,7 @@ class _LoadedSpaceViewState extends State { @override void initState() { super.initState(); + _spaceModels = List.from(widget.spaceModels ?? []); } @@ -47,6 +48,7 @@ class _LoadedSpaceViewState extends State { @override void didUpdateWidget(covariant LoadedSpaceView oldWidget) { super.didUpdateWidget(oldWidget); + if (widget.spaceModels != oldWidget.spaceModels) { WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { @@ -76,29 +78,33 @@ class _LoadedSpaceViewState extends State { clipBehavior: Clip.none, children: [ widget.shouldNavigateToSpaceModelPage - ? Row( - children: [ - SizedBox(width: 300, child: SpaceTreeView(onSelect: () {})), - Expanded( - child: BlocProvider( - create: (context) => SpaceModelBloc( - api: SpaceModelManagementApi(), - initialSpaceModels: _spaceModels, + ? _spaceModels.isNotEmpty + ? Row( + children: [ + SizedBox( + width: 300, child: SpaceTreeView(onSelect: () {})), + Expanded( + child: BlocProvider( + create: (context) => SpaceModelBloc( + api: SpaceModelManagementApi(), + initialSpaceModels: _spaceModels, + ), + child: SpaceModelPage( + products: widget.products, + onSpaceModelsUpdated: _onSpaceModelsUpdated, + ), + ), ), - child: SpaceModelPage( - products: widget.products, - onSpaceModelsUpdated: _onSpaceModelsUpdated, - ), - ), - ), - ], - ) + ], + ) + : const Center(child: CircularProgressIndicator()) : Row( children: [ SidebarWidget( communities: widget.communities, - selectedSpaceUuid: - widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '', + selectedSpaceUuid: widget.selectedSpace?.uuid ?? + widget.selectedCommunity?.uuid ?? + '', ), CommunityStructureArea( selectedCommunity: widget.selectedCommunity, diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart index 7c68e15c..8237c172 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart @@ -9,9 +9,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart'; -import 'package:syncrow_web/utils/constants/strings_manager.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; -import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; class CreateSpaceModelBloc extends Bloc { diff --git a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart index 16ba41c0..ed6ffa71 100644 --- a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart'; @@ -12,6 +14,8 @@ class SpaceModelBloc extends Bloc { required this.api, required List initialSpaceModels, }) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) { + log('Initial Space Models in: ${initialSpaceModels.map((e) => e.toJson()).toList()}'); + on(_onCreateSpaceModel); on(_onUpdateSpaceModel); on(_onDeleteSpaceModel); diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart index 9a89c452..69e619b7 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart @@ -2,6 +2,8 @@ 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/spaces_management/all_spaces/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; @@ -196,6 +198,12 @@ class CreateSpaceModelDialog extends StatelessWidget { .add(CreateSpaceModel( newSpaceModel: newModel)); + pageContext! + .read< + SpaceManagementBloc>() + .add( + UpdateSpaceModelCache( + newModel)); } Navigator.of(context) .pop(); // Close the dialog @@ -241,6 +249,11 @@ class CreateSpaceModelDialog extends StatelessWidget { spaceModelUuid: newModel.uuid ?? '')); + pageContext! + .read< + SpaceManagementBloc>() + .add(UpdateSpaceModelCache( + newModel)); } Navigator.of(context) .pop(); diff --git a/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart b/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart index ab65af44..f446695e 100644 --- a/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.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/spaces_management/all_spaces/bloc/space_management_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart'; @@ -182,6 +182,10 @@ class SpaceModelCardWidget extends StatelessWidget { pageContext!.read().add( DeleteSpaceModel(spaceModelUuid: model.uuid ?? ''), ); + + pageContext!.read().add( + DeleteSpaceModelFromCache(model.uuid ?? ''), + ); } }, ); diff --git a/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart b/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart index 45a6aaf7..0f40ddbb 100644 --- a/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart +++ b/lib/pages/spaces_management/structure_selector/view/center_body_widget.dart @@ -15,11 +15,11 @@ class CenterBodyWidget extends StatelessWidget { context.read().add(CommunityStructureSelectedEvent()); } if (state is CommunityStructureState) { - context.read().add(BlankStateEvent()); + context.read().add(BlankStateEvent(context)); } if (state is SpaceModelState) { - context.read().add(SpaceModelLoadEvent()); + context.read().add(SpaceModelLoadEvent(context)); } return Container(