diff --git a/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart b/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart index 1930963b..ede6afb9 100644 --- a/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart +++ b/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart @@ -24,6 +24,8 @@ class AddDeviceTypeWidget extends StatelessWidget { final String spaceName; final bool isCreate; final Function(List, List?)? onSave; + final List projectTags; + const AddDeviceTypeWidget( {super.key, @@ -35,7 +37,8 @@ class AddDeviceTypeWidget extends StatelessWidget { this.allTags, this.spaceTags, this.onSave, - required this.spaceName}); + required this.spaceName, + required this.projectTags}); @override Widget build(BuildContext context) { @@ -134,7 +137,8 @@ class AddDeviceTypeWidget extends StatelessWidget { spaceName: spaceName, initialTags: initialTags, title: dialogTitle, - onSave: onSave), + onSave: onSave, + projectTags: projectTags), ); } }, 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 5dcd3e89..337e9950 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 @@ -296,11 +296,10 @@ class SpaceManagementBloc extends Bloc> _waitForCommunityList(SpaceTreeBloc spaceBloc) async { @@ -365,13 +364,13 @@ class SpaceManagementBloc extends Bloc communities; final List spaces; final List? spaceModels; + final List projectTags; - CommunityStructureArea({ - this.selectedCommunity, - this.selectedSpace, - required this.communities, - this.products, - required this.spaces, - this.onSpaceSelected, - this.spaceModels, - }); + CommunityStructureArea( + {this.selectedCommunity, + this.selectedSpace, + required this.communities, + this.products, + required this.spaces, + this.onSpaceSelected, + this.spaceModels, + required this.projectTags}); @override _CommunityStructureAreaState createState() => _CommunityStructureAreaState(); @@ -64,8 +65,7 @@ class _CommunityStructureAreaState extends State { void initState() { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = - widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); _nameController = TextEditingController( text: widget.selectedCommunity?.name ?? '', @@ -92,14 +92,12 @@ class _CommunityStructureAreaState extends State { if (oldWidget.spaces != widget.spaces) { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = - widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); }); } - if (widget.selectedSpace != oldWidget.selectedSpace && - widget.selectedSpace != null) { + if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) { WidgetsBinding.instance.addPostFrameCallback((_) { _moveToSpace(widget.selectedSpace!); }); @@ -182,8 +180,7 @@ class _CommunityStructureAreaState extends State { connection, widget.selectedSpace) ? 1.0 : 0.3, // Adjust opacity - child: CustomPaint( - painter: CurvedLinePainter([connection])), + child: CustomPaint(painter: CurvedLinePainter([connection])), ), for (var entry in spaces.asMap().entries) if (entry.value.status != SpaceStatus.deleted && @@ -193,15 +190,12 @@ class _CommunityStructureAreaState extends State { top: entry.value.position.dy, child: SpaceCardWidget( index: entry.key, - onButtonTap: (int index, Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: - spaces[index].position + newPosition, - parentIndex: index, - direction: direction, - ); + onButtonTap: (int index, Offset newPosition, String direction) { + _showCreateSpaceDialog(screenSize, + position: spaces[index].position + newPosition, + parentIndex: index, + direction: direction, + projectTags: widget.projectTags); }, position: entry.value.position, isHovered: entry.value.isHovered, @@ -211,9 +205,8 @@ class _CommunityStructureAreaState extends State { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { - final bool isHighlighted = - SpaceHelper.isHighlightedSpace( - spaces[index], widget.selectedSpace); + final bool isHighlighted = SpaceHelper.isHighlightedSpace( + spaces[index], widget.selectedSpace); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, @@ -238,7 +231,8 @@ class _CommunityStructureAreaState extends State { onTap: () { _showCreateSpaceDialog(screenSize, canvasHeight: canvasHeight, - canvasWidth: canvasWidth); + canvasWidth: canvasWidth, + projectTags: widget.projectTags); }, ), ), @@ -292,26 +286,22 @@ class _CommunityStructureAreaState extends State { int? parentIndex, String? direction, double? canvasWidth, - double? canvasHeight}) { + double? canvasHeight, + required List projectTags}) { showDialog( context: context, builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, spaceModels: widget.spaceModels, - allTags: - TagHelper.getAllTagValues(widget.communities, widget.spaceModels), + allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), parentSpace: parentIndex != null ? spaces[parentIndex] : null, - onCreateSpace: (String name, - String icon, - List selectedProducts, - SpaceTemplateModel? spaceModel, - List? subspaces, - List? tags) { + projectTags: projectTags, + onCreateSpace: (String name, String icon, List selectedProducts, + SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { setState(() { // Set the first space in the center or use passed position - Offset centerPosition = - position ?? ConnectionHelper.getCenterPosition(screenSize); + Offset centerPosition = position ?? ConnectionHelper.getCenterPosition(screenSize); SpaceModel newSpace = SpaceModel( name: name, icon: icon, @@ -356,21 +346,17 @@ class _CommunityStructureAreaState extends State { spaceModels: widget.spaceModels, name: widget.selectedSpace!.name, icon: widget.selectedSpace!.icon, - parentSpace: SpaceHelper.findSpaceByInternalId( - widget.selectedSpace?.parent?.internalId, spaces), + projectTags: widget.projectTags, + parentSpace: + SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces), editSpace: widget.selectedSpace, currentSpaceModel: widget.selectedSpace?.spaceModel, tags: widget.selectedSpace?.tags, subspaces: widget.selectedSpace?.subspaces, isEdit: true, - allTags: TagHelper.getAllTagValues( - widget.communities, widget.spaceModels), - onCreateSpace: (String name, - String icon, - List selectedProducts, - SpaceTemplateModel? spaceModel, - List? subspaces, - List? tags) { + allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), + onCreateSpace: (String name, String icon, List selectedProducts, + SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { setState(() { // Update the space's properties widget.selectedSpace!.name = name; @@ -380,8 +366,7 @@ class _CommunityStructureAreaState extends State { widget.selectedSpace!.tags = tags; if (widget.selectedSpace!.status != SpaceStatus.newSpace) { - widget.selectedSpace!.status = - SpaceStatus.modified; // Mark as modified + widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified } for (var space in spaces) { @@ -411,8 +396,7 @@ class _CommunityStructureAreaState extends State { List result = []; void flatten(SpaceModel space) { - if (space.status == SpaceStatus.deleted || - space.status == SpaceStatus.parentDeleted) { + if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) { return; } result.add(space); @@ -527,16 +511,13 @@ class _CommunityStructureAreaState extends State { void _selectSpace(BuildContext context, SpaceModel space) { context.read().add( - SelectSpaceEvent( - selectedCommunity: widget.selectedCommunity, - selectedSpace: space), + SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space), ); } void _deselectSpace(BuildContext context) { context.read().add( - SelectSpaceEvent( - selectedCommunity: widget.selectedCommunity, selectedSpace: null), + SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null), ); } @@ -625,19 +606,16 @@ class _CommunityStructureAreaState extends State { const double horizontalGap = 200.0; const double verticalGap = 100.0; - SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition, - SpaceModel? duplicatedParent) { - Offset newPosition = - Offset(parentPosition.dx + horizontalGap, original.position.dy); + SpaceModel duplicateRecursive( + SpaceModel original, Offset parentPosition, SpaceModel? duplicatedParent) { + Offset newPosition = Offset(parentPosition.dx + horizontalGap, original.position.dy); while (spaces.any((s) => - (s.position - newPosition).distance < horizontalGap && - s.status != SpaceStatus.deleted)) { + (s.position - newPosition).distance < horizontalGap && s.status != SpaceStatus.deleted)) { newPosition += Offset(horizontalGap, 0); } - final duplicatedName = - SpaceHelper.generateUniqueSpaceName(original.name, spaces); + final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces); final List? duplicatedSubspaces; final List? duplicatedTags; @@ -681,8 +659,7 @@ class _CommunityStructureAreaState extends State { if (original.parent != null && duplicatedParent == null) { final originalParent = original.parent!; - final duplicatedParent = - originalToDuplicate[originalParent] ?? originalParent; + final duplicatedParent = originalToDuplicate[originalParent] ?? originalParent; final parentConnection = Connection( startSpace: duplicatedParent, @@ -698,8 +675,7 @@ class _CommunityStructureAreaState extends State { final childrenWithDownDirection = original.children .where((child) => - child.incomingConnection?.direction == "down" && - child.status != SpaceStatus.deleted) + child.incomingConnection?.direction == "down" && child.status != SpaceStatus.deleted) .toList(); Offset childStartPosition = childrenWithDownDirection.length == 1 @@ -707,8 +683,7 @@ class _CommunityStructureAreaState extends State { : newPosition + Offset(0, verticalGap); for (final child in original.children) { - final isDownDirection = - child.incomingConnection?.direction == "down" ?? false; + final isDownDirection = child.incomingConnection?.direction == "down" ?? false; if (isDownDirection && childrenWithDownDirection.length == 1) { childStartPosition = duplicated.position + Offset(0, verticalGap); @@ -716,8 +691,7 @@ class _CommunityStructureAreaState extends State { childStartPosition = duplicated.position + Offset(horizontalGap, 0); } - final duplicatedChild = - duplicateRecursive(child, childStartPosition, duplicated); + final duplicatedChild = duplicateRecursive(child, childStartPosition, duplicated); duplicated.children.add(duplicatedChild); childStartPosition += Offset(0, verticalGap); } @@ -728,8 +702,7 @@ class _CommunityStructureAreaState extends State { if (space.parent == null) { duplicateRecursive(space, space.position, null); } else { - final duplicatedParent = - originalToDuplicate[space.parent!] ?? space.parent!; + final duplicatedParent = originalToDuplicate[space.parent!] ?? space.parent!; duplicateRecursive(space, space.position, duplicatedParent); } } 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 f3df1637..1c14a1f1 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 @@ -42,6 +42,7 @@ class CreateSpaceDialog extends StatefulWidget { final List? tags; final List? allTags; final SpaceTemplateModel? currentSpaceModel; + final List projectTags; const CreateSpaceDialog( {super.key, @@ -57,7 +58,8 @@ class CreateSpaceDialog extends StatefulWidget { this.spaceModels, this.subspaces, this.tags, - this.currentSpaceModel}); + this.currentSpaceModel, + required this.projectTags}); @override CreateSpaceDialogState createState() => CreateSpaceDialogState(); @@ -80,10 +82,8 @@ class CreateSpaceDialogState extends State { super.initState(); selectedIcon = widget.icon ?? Assets.location; nameController = TextEditingController(text: widget.name ?? ''); - selectedProducts = - widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; - isOkButtonEnabled = - enteredName.isNotEmpty || nameController.text.isNotEmpty; + selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; + isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; if (widget.currentSpaceModel != null) { subspaces = []; tags = []; @@ -96,15 +96,13 @@ class CreateSpaceDialogState extends State { @override Widget build(BuildContext context) { - bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty || - subspaces != null && subspaces!.isNotEmpty); + bool isSpaceModelDisabled = + (tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty); bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null); final screenWidth = MediaQuery.of(context).size.width; return AlertDialog( - title: widget.isEdit - ? const Text('Edit Space') - : const Text('Create New Space'), + title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( width: screenWidth * 0.5, @@ -178,7 +176,8 @@ class CreateSpaceDialogState extends State { isNameFieldInvalid = value.isEmpty; if (!isNameFieldInvalid) { - if (SpaceHelper.isNameConflict(value, widget.parentSpace, widget.editSpace)) { + if (SpaceHelper.isNameConflict( + value, widget.parentSpace, widget.editSpace)) { isNameFieldExist = true; isOkButtonEnabled = false; } else { @@ -245,9 +244,7 @@ class CreateSpaceDialogState extends State { padding: EdgeInsets.zero, ), onPressed: () { - isSpaceModelDisabled - ? null - : _showLinkSpaceModelDialog(context); + isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context); }, child: ButtonContentWidget( svgAssets: Assets.link, @@ -257,8 +254,7 @@ class CreateSpaceDialogState extends State { ) : Container( width: screenWidth * 0.25, - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 16.0), + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0), decoration: BoxDecoration( color: ColorsManager.boxColor, borderRadius: BorderRadius.circular(10), @@ -273,8 +269,7 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodyMedium! - .copyWith( - color: ColorsManager.spaceColor), + .copyWith(color: ColorsManager.spaceColor), ), backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -343,8 +338,8 @@ class CreateSpaceDialogState extends State { onPressed: () async { isTagsAndSubspaceModelDisabled ? null - : _showSubSpaceDialog(context, enteredName, - [], false, widget.products, subspaces); + : _showSubSpaceDialog( + context, enteredName, [], false, widget.products, subspaces); }, child: ButtonContentWidget( icon: Icons.add, @@ -371,22 +366,16 @@ class CreateSpaceDialogState extends State { if (subspaces != null) ...subspaces!.map((subspace) { return Column( - crossAxisAlignment: - CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ SubspaceNameDisplayWidget( text: subspace.subspaceName, validateName: (updatedName) { - bool nameExists = - subspaces!.any((s) { - bool isSameId = s.internalId == - subspace.internalId; - bool isSameName = s.subspaceName - .trim() - .toLowerCase() == - updatedName - .trim() - .toLowerCase(); + bool nameExists = subspaces!.any((s) { + bool isSameId = s.internalId == subspace.internalId; + bool isSameName = + s.subspaceName.trim().toLowerCase() == + updatedName.trim().toLowerCase(); return !isSameId && isSameName; }); @@ -395,8 +384,7 @@ class CreateSpaceDialogState extends State { }, onNameChanged: (updatedName) { setState(() { - subspace.subspaceName = - updatedName; + subspace.subspaceName = updatedName; }); }, ), @@ -405,8 +393,8 @@ class CreateSpaceDialogState extends State { }), EditChip( onTap: () async { - _showSubSpaceDialog(context, enteredName, - [], true, widget.products, subspaces); + _showSubSpaceDialog(context, enteredName, [], true, + widget.products, subspaces); }, ) ], @@ -415,9 +403,7 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 10), (tags?.isNotEmpty == true || - subspaces?.any((subspace) => - subspace.tags?.isNotEmpty == true) == - true) + subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true) ? SizedBox( width: screenWidth * 0.25, child: Container( @@ -437,16 +423,14 @@ class CreateSpaceDialogState extends State { // Combine tags from spaceModel and subspaces ...TagHelper.groupTags([ ...?tags, - ...?subspaces?.expand( - (subspace) => subspace.tags ?? []) + ...?subspaces?.expand((subspace) => subspace.tags ?? []) ]).entries.map( (entry) => Chip( avatar: SizedBox( width: 24, height: 24, child: SvgPicture.asset( - entry.key.icon ?? - 'assets/icons/gateway.svg', + entry.key.icon ?? 'assets/icons/gateway.svg', fit: BoxFit.contain, ), ), @@ -455,15 +439,11 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodySmall - ?.copyWith( - color: ColorsManager - .spaceColor), + ?.copyWith(color: ColorsManager.spaceColor), ), - backgroundColor: - ColorsManager.whiteColors, + backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(16), + borderRadius: BorderRadius.circular(16), side: const BorderSide( color: ColorsManager.spaceColor, ), @@ -472,23 +452,21 @@ class CreateSpaceDialogState extends State { ), EditChip(onTap: () async { - final result = await showDialog( + await showDialog( context: context, builder: (context) => AssignTagDialog( products: widget.products, subspaces: subspaces, allTags: widget.allTags, - addedProducts: TagHelper - .createInitialSelectedProductsForTags( + addedProducts: + TagHelper.createInitialSelectedProductsForTags( tags ?? [], subspaces), title: 'Edit Device', - initialTags: - TagHelper.generateInitialForTags( - spaceTags: tags, - subspaces: subspaces), + initialTags: TagHelper.generateInitialForTags( + spaceTags: tags, subspaces: subspaces), spaceName: widget.name ?? '', - onSave: - (updatedTags, updatedSubspaces) { + projectTags: widget.projectTags, + onSave: (updatedTags, updatedSubspaces) { setState(() { tags = updatedTags; subspaces = updatedSubspaces; @@ -547,25 +525,17 @@ class CreateSpaceDialogState extends State { }); return; } else { - String newName = enteredName.isNotEmpty - ? enteredName - : (widget.name ?? ''); + String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); if (newName.isNotEmpty) { - widget.onCreateSpace( - newName, - selectedIcon, - selectedProducts, - selectedSpaceModel, - subspaces, - tags); + widget.onCreateSpace(newName, selectedIcon, selectedProducts, + selectedSpaceModel, subspaces, tags); Navigator.of(context).pop(); } } }, borderRadius: 10, - backgroundColor: isOkButtonEnabled - ? ColorsManager.secondaryColor - : ColorsManager.grayColor, + backgroundColor: + isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor, foregroundColor: ColorsManager.whiteColors, child: const Text('OK'), ), @@ -592,7 +562,6 @@ class CreateSpaceDialogState extends State { ); } - void _showLinkSpaceModelDialog(BuildContext context) { showDialog( context: context, @@ -613,13 +582,8 @@ class CreateSpaceDialogState extends State { ); } - void _showSubSpaceDialog( - BuildContext context, - String name, - final List? spaceTags, - bool isEdit, - List? products, - final List? existingSubSpaces) { + void _showSubSpaceDialog(BuildContext context, String name, final List? spaceTags, + bool isEdit, List? products, final List? existingSubSpaces) { showDialog( context: context, builder: (BuildContext context) { @@ -634,12 +598,10 @@ class CreateSpaceDialogState extends State { final List tagsToAppendToSpace = []; if (slectedSubspaces != null) { - final updatedIds = - slectedSubspaces.map((s) => s.internalId).toSet(); + final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet(); if (existingSubSpaces != null) { - final deletedSubspaces = existingSubSpaces - .where((s) => !updatedIds.contains(s.internalId)) - .toList(); + final deletedSubspaces = + existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList(); for (var s in deletedSubspaces) { if (s.tags != null) { tagsToAppendToSpace.addAll(s.tags!); @@ -659,20 +621,20 @@ class CreateSpaceDialogState extends State { ); } - void _showTagCreateDialog(BuildContext context, String name, bool isEdit, - List? products) { + void _showTagCreateDialog( + BuildContext context, String name, bool isEdit, List? products) { isEdit ? showDialog( context: context, builder: (BuildContext context) { return AssignTagDialog( title: 'Edit Device', - addedProducts: TagHelper.createInitialSelectedProductsForTags( - tags, subspaces), + addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces), spaceName: name, products: products, subspaces: subspaces, allTags: widget.allTags, + projectTags: widget.projectTags, onSave: (selectedSpaceTags, selectedSubspaces) { setState(() { tags = selectedSpaceTags; @@ -682,8 +644,7 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == - selectedSubspace.subspaceName) { + if (subspace.subspaceName == selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } @@ -705,9 +666,9 @@ class CreateSpaceDialogState extends State { spaceTags: tags, isCreate: true, allTags: widget.allTags, + projectTags: widget.projectTags, initialSelectedProducts: - TagHelper.createInitialSelectedProductsForTags( - tags, subspaces), + TagHelper.createInitialSelectedProductsForTags(tags, subspaces), onSave: (selectedSpaceTags, selectedSubspaces) { setState(() { tags = selectedSpaceTags; @@ -717,8 +678,7 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == - selectedSubspace.subspaceName) { + if (subspace.subspaceName == selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } 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 04bcf8d2..fef2a4c9 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 @@ -114,6 +114,7 @@ class _LoadedSpaceViewState extends State { products: widget.products, communities: widget.communities, spaceModels: _spaceModels, + projectTags: widget.projectTags, ), ], ), 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 a06e6977..74161b6f 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 @@ -4,17 +4,16 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_e import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart'; class AssignTagBloc extends Bloc { - final List allTags; + final List projectTags; - AssignTagBloc(this.allTags) : super(AssignTagInitial()) { + AssignTagBloc(this.projectTags) : super(AssignTagInitial()) { on((event, emit) { final initialTags = event.initialTags ?? []; final existingTagCounts = {}; for (var tag in initialTags) { if (tag.product != null) { - existingTagCounts[tag.product!.uuid] = - (existingTagCounts[tag.product!.uuid] ?? 0) + 1; + existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1; } } @@ -23,17 +22,14 @@ class AssignTagBloc extends Bloc { for (var selectedProduct in event.addedProducts) { final existingCount = existingTagCounts[selectedProduct.productId] ?? 0; - if (selectedProduct.count == 0 || - selectedProduct.count <= existingCount) { - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) { + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); continue; } final missingCount = selectedProduct.count - existingCount; - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); if (missingCount > 0) { tags.addAll(List.generate( @@ -47,11 +43,11 @@ class AssignTagBloc extends Bloc { } } - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, - updatedTags: updatedTags, + updatedTags: updatedTags, isSaveEnabled: _validateTags(tags), errorMessage: '', )); @@ -62,9 +58,16 @@ class AssignTagBloc extends Bloc { if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - tags[event.index] = tags[event.index].copyWith(tag: event.tag); + if (event.index < 0 || event.index >= tags.length) return; - final updatedTags = _calculateAvailableTags(allTags, tags); + tags[event.index] = tags[event.index].copyWith( + tag: event.tag.tag, + uuid: event.tag.uuid, + product: event.tag.product, + internalId: event.tag.internalId, + location: event.tag.location, + ); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -82,10 +85,9 @@ class AssignTagBloc extends Bloc { final tags = List.from(currentState.tags); // Update the location - tags[event.index] = - tags[event.index].copyWith(location: event.location); + tags[event.index] = tags[event.index].copyWith(location: event.location); - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -104,7 +106,7 @@ class AssignTagBloc extends Bloc { emit(AssignTagLoaded( tags: tags, - updatedTags: _calculateAvailableTags(allTags, tags), + updatedTags: _calculateAvailableTags(projectTags, tags), isSaveEnabled: _validateTags(tags), errorMessage: _getValidationError(tags), )); @@ -115,11 +117,10 @@ class AssignTagBloc extends Bloc { final currentState = state; if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags) - ..remove(event.tagToDelete); + final tags = List.from(currentState.tags)..remove(event.tagToDelete); // Recalculate available tags - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -140,10 +141,8 @@ class AssignTagBloc extends Bloc { // Get validation error for duplicate tags String? _getValidationError(List tags) { - final nonEmptyTags = tags - .map((tag) => tag.tag?.trim() ?? '') - .where((tag) => tag.isNotEmpty) - .toList(); + final nonEmptyTags = + tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList(); final duplicateTags = nonEmptyTags .fold>({}, (map, tag) { @@ -162,14 +161,16 @@ class AssignTagBloc extends Bloc { return null; } - List _calculateAvailableTags(List allTags, List tags) { - final selectedTags = tags + List _calculateAvailableTags(List allTags, List selectedTags) { + final selectedTagSet = selectedTags .where((tag) => (tag.tag?.trim().isNotEmpty ?? false)) .map((tag) => tag.tag!.trim()) .toSet(); - final availableTags = - allTags.where((tag) => !selectedTags.contains(tag.trim())).toList(); + final availableTags = allTags + .where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim())) + .toList(); + return availableTags; } } diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart index 9116b094..7d81ffdb 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart @@ -24,7 +24,7 @@ class InitializeTags extends AssignTagEvent { class UpdateTagEvent extends AssignTagEvent { final int index; - final String tag; + final Tag tag; const UpdateTagEvent({required this.index, required this.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 6a2dae4b..53700a33 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 @@ -5,7 +5,7 @@ abstract class AssignTagState extends Equatable { const AssignTagState(); @override - List get props => []; + List get props => []; } class AssignTagInitial extends AssignTagState {} @@ -14,7 +14,7 @@ class AssignTagLoading extends AssignTagState {} class AssignTagLoaded extends AssignTagState { final List tags; - final List updatedTags; + final List updatedTags; final bool isSaveEnabled; final String? errorMessage; @@ -27,8 +27,7 @@ class AssignTagLoaded extends AssignTagState { }); @override - List get props => - [tags, updatedTags, isSaveEnabled, errorMessage ?? '']; + List get props => [tags, updatedTags, isSaveEnabled, errorMessage ?? '']; } class AssignTagError extends AssignTagState { @@ -37,5 +36,5 @@ class AssignTagError extends AssignTagState { const AssignTagError(this.errorMessage); @override - List get props => [errorMessage]; + List get props => [errorMessage]; } diff --git a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart index 3b794b61..fd1454e5 100644 --- a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart +++ b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/common/dialog_textfield_dropdown.dart'; +import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.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/add_device_type/views/add_device_type_widget.dart'; @@ -26,6 +26,7 @@ class AssignTagDialog extends StatelessWidget { final String spaceName; final String title; final Function(List, List?)? onSave; + final List projectTags; const AssignTagDialog( {Key? key, @@ -37,18 +38,17 @@ class AssignTagDialog extends StatelessWidget { this.allTags, required this.spaceName, required this.title, - this.onSave}) + this.onSave, + required this.projectTags}) : super(key: key); @override Widget build(BuildContext context) { - final List locations = (subspaces ?? []) - .map((subspace) => subspace.subspaceName) - .toList() - ..add('Main Space'); + final List locations = + (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); return BlocProvider( - create: (_) => AssignTagBloc(allTags ?? []) + create: (_) => AssignTagBloc(projectTags) ..add(InitializeTags( initialTags: initialTags, addedProducts: addedProducts, @@ -70,8 +70,7 @@ class AssignTagDialog extends StatelessWidget { ClipRRect( borderRadius: BorderRadius.circular(20), child: DataTable( - headingRowColor: WidgetStateProperty.all( - ColorsManager.dataHeaderGrey), + headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), key: ValueKey(state.tags.length), border: TableBorder.all( color: ColorsManager.dataHeaderGrey, @@ -80,22 +79,15 @@ class AssignTagDialog extends StatelessWidget { ), columns: [ DataColumn( - label: Text('#', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( - label: Text('Device', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('Device', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( numeric: false, - label: Text('Tag', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( - label: Text('Location', - style: - Theme.of(context).textTheme.bodyMedium)), + label: + Text('Location', style: Theme.of(context).textTheme.bodyMedium)), ], rows: state.tags.isEmpty ? [ @@ -103,12 +95,8 @@ class AssignTagDialog extends StatelessWidget { DataCell( Center( child: Text('No Data Available', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager - .lightGrayColor, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.lightGrayColor, )), ), ), @@ -126,8 +114,7 @@ class AssignTagDialog extends StatelessWidget { DataCell(Text((index + 1).toString())), DataCell( Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( @@ -141,31 +128,25 @@ class AssignTagDialog extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: ColorsManager - .lightGrayColor, + color: ColorsManager.lightGrayColor, width: 1.0, ), ), child: IconButton( icon: const Icon( Icons.close, - color: ColorsManager - .lightGreyColor, + color: ColorsManager.lightGreyColor, size: 16, ), onPressed: () { - context - .read() - .add(DeleteTag( - tagToDelete: tag, - tags: state.tags)); + context.read().add( + DeleteTag(tagToDelete: tag, tags: state.tags)); controllers.removeAt(index); }, tooltip: 'Delete Tag', padding: EdgeInsets.zero, - constraints: - const BoxConstraints(), + constraints: const BoxConstraints(), ), ), ], @@ -173,23 +154,20 @@ class AssignTagDialog extends StatelessWidget { ), DataCell( Container( - alignment: Alignment - .centerLeft, // Align cell content to the left + alignment: + Alignment.centerLeft, // Align cell content to the left child: SizedBox( - width: double - .infinity, // Ensure full width for dropdown - child: DialogTextfieldDropdown( - key: ValueKey( - 'dropdown_${Uuid().v4()}_${index}'), + width: double.infinity, + child: TagDialogTextfieldDropdown( + key: ValueKey('dropdown_${const Uuid().v4()}_$index'), items: state.updatedTags, - initialValue: tag.tag, + product: tag.product?.uuid ?? 'Unknown', + initialValue: tag, onSelected: (value) { - controller.text = value; - context - .read() - .add(UpdateTagEvent( + controller.text = value.tag ?? ''; + context.read().add(UpdateTagEvent( index: index, - tag: value.trim(), + tag: value, )); }, ), @@ -201,12 +179,9 @@ class AssignTagDialog extends StatelessWidget { width: double.infinity, child: DialogDropdown( items: locations, - selectedValue: - tag.location ?? 'Main Space', + selectedValue: tag.location ?? 'Main Space', onSelected: (value) { - context - .read() - .add(UpdateLocation( + context.read().add(UpdateLocation( index: index, location: value, )); @@ -238,13 +213,11 @@ class AssignTagDialog extends StatelessWidget { label: 'Add New Device', onPressed: () async { final updatedTags = List.from(state.tags); - final result = - TagHelper.processTags(updatedTags, subspaces); + final result = TagHelper.processTags(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = List.from( - result['subspaces'] as List); + final processedTags = result['updatedTags'] as List; + final processedSubspaces = + List.from(result['subspaces'] as List); Navigator.of(context).pop(); @@ -253,8 +226,9 @@ class AssignTagDialog extends StatelessWidget { builder: (context) => AddDeviceTypeWidget( products: products, subspaces: processedSubspaces, - initialSelectedProducts: TagHelper - .createInitialSelectedProductsForTags( + projectTags: projectTags, + initialSelectedProducts: + TagHelper.createInitialSelectedProductsForTags( processedTags, processedSubspaces), spaceName: spaceName, spaceTags: processedTags, @@ -278,14 +252,11 @@ class AssignTagDialog extends StatelessWidget { onPressed: state.isSaveEnabled ? () async { final updatedTags = List.from(state.tags); - final result = TagHelper.processTags( - updatedTags, subspaces); + final result = TagHelper.processTags(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; + final processedTags = result['updatedTags'] as List; final processedSubspaces = - List.from( - result['subspaces'] as List); + List.from(result['subspaces'] as List); onSave?.call(processedTags, processedSubspaces); Navigator.of(context).pop(); } diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart index 51b89859..7df82b5e 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart @@ -8,7 +8,7 @@ class AssignTagModelBloc extends Bloc AssignTagModelBloc(this.projectTags) : super(AssignTagModelInitial()) { on((event, emit) { - final initialTags = event.initialTags ?? []; + final initialTags = event.initialTags; final existingTagCounts = {}; for (var tag in initialTags) { @@ -67,7 +67,7 @@ class AssignTagModelBloc extends Bloc location: event.tag.location, ); - final updatedTags = _calculateAvailableTags(projectTags ?? [], tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( tags: tags, @@ -83,12 +83,9 @@ class AssignTagModelBloc extends Bloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - for (var t in tags) { - } // Use copyWith for immutability tags[event.index] = tags[event.index].copyWith(location: event.location); - final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index c9d0b57a..85be3bf3 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -171,8 +171,7 @@ class AssignTagModelsDialog extends StatelessWidget { alignment: Alignment .centerLeft, // Align cell content to the left child: SizedBox( - width: - double.infinity, // Ensure full width for dropdown + width: double.infinity, child: TagDialogTextfieldDropdown( key: ValueKey( 'dropdown_${const Uuid().v4()}_$index'),