diff --git a/lib/common/edit_chip.dart b/lib/common/edit_chip.dart new file mode 100644 index 00000000..7607834d --- /dev/null +++ b/lib/common/edit_chip.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class EditChip extends StatelessWidget { + final String label; + final VoidCallback onTap; + final Color labelColor; + final Color backgroundColor; + final Color borderColor; + final double borderRadius; + + const EditChip({ + Key? key, + this.label = 'Edit', + required this.onTap, + this.labelColor = ColorsManager.spaceColor, + this.backgroundColor = ColorsManager.whiteColors, + this.borderColor = ColorsManager.spaceColor, + this.borderRadius = 16.0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: onTap, + child: Chip( + label: Text( + label, + style: TextStyle(color: labelColor), + ), + backgroundColor: backgroundColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius), + side: BorderSide(color: borderColor), + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/all_spaces/model/base_tag.dart b/lib/pages/spaces_management/all_spaces/model/base_tag.dart new file mode 100644 index 00000000..57f223f4 --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/model/base_tag.dart @@ -0,0 +1,26 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:uuid/uuid.dart'; + +abstract class BaseTag { + String? uuid; + String? tag; + final ProductModel? product; + String internalId; + String? location; + + BaseTag({ + this.uuid, + required this.tag, + this.product, + String? internalId, + this.location, + }) : internalId = internalId ?? const Uuid().v4(); + + Map toJson(); + BaseTag copyWith({ + String? tag, + ProductModel? product, + String? location, + String? internalId, + }); +} diff --git a/lib/pages/spaces_management/all_spaces/model/tag.dart b/lib/pages/spaces_management/all_spaces/model/tag.dart index 98494f7f..34bd08bb 100644 --- a/lib/pages/spaces_management/all_spaces/model/tag.dart +++ b/lib/pages/spaces_management/all_spaces/model/tag.dart @@ -1,22 +1,23 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_body_model.dart'; import 'package:uuid/uuid.dart'; -class Tag { - String? uuid; - String? tag; - final ProductModel? product; - String internalId; - String? location; - - Tag( - {this.uuid, - required this.tag, - this.product, - String? internalId, - this.location}) - : internalId = internalId ?? const Uuid().v4(); +class Tag extends BaseTag { + Tag({ + String? uuid, + required String? tag, + ProductModel? product, + String? internalId, + String? location, + }) : super( + uuid: uuid, + tag: tag, + product: product, + internalId: internalId, + location: location, + ); factory Tag.fromJson(Map json) { final String internalId = json['internalId'] ?? const Uuid().v4(); @@ -31,15 +32,19 @@ class Tag { ); } + @override Tag copyWith({ String? tag, ProductModel? product, String? location, + String? internalId, }) { return Tag( + uuid: uuid, tag: tag ?? this.tag, product: product ?? this.product, location: location ?? this.location, + internalId: internalId ?? this.internalId, ); } @@ -60,7 +65,7 @@ extension TagModelExtensions on Tag { ..productUuid = product?.uuid; } - CreateTagBodyModel toCreateTagBodyModel() { + CreateTagBodyModel toCreateTagBodyModel() { return CreateTagBodyModel() ..tag = tag ?? '' ..productUuid = product?.uuid; 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 f569d252..8c6e36f5 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 @@ -344,6 +344,7 @@ class _CommunityStructureAreaState extends State { builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, + spaceModels: widget.spaceModels, name: space.name, icon: space.icon, editSpace: space, @@ -463,7 +464,7 @@ class _CommunityStructureAreaState extends State { _markChildrenAsDeleted(space); } } - + _removeConnectionsForDeletedSpaces(); }); } 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 6f3e9fcb..7844381d 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 @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/common/edit_chip.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'; @@ -10,15 +11,23 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_mo import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/space_icon_const.dart'; class CreateSpaceDialog extends StatefulWidget { - final Function(String, String, List selectedProducts, - SpaceTemplateModel? spaceModel, List? subspaces, List? tags) onCreateSpace; + final Function( + String, + String, + List selectedProducts, + SpaceTemplateModel? spaceModel, + List? subspaces, + List? tags) onCreateSpace; final List? products; final String? name; final String? icon; @@ -211,42 +220,13 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 10), selectedSpaceModel == null - ? DefaultButton( + ? TextButton( onPressed: () { _showLinkSpaceModelDialog(context); }, - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, - borderColor: ColorsManager.neutralGray, - borderRadius: 16.0, - padding: 10.0, // Reduced padding for smaller size - child: Align( - alignment: Alignment.centerLeft, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(left: 6.0), - child: SvgPicture.asset( - Assets.link, - width: screenWidth * - 0.015, // Adjust icon size - height: screenWidth * 0.015, - ), - ), - const SizedBox(width: 3), - Flexible( - child: Text( - 'Link a space model', - overflow: TextOverflow - .ellipsis, // Prevent overflow - style: Theme.of(context) - .textTheme - .bodyMedium, - ), - ), - ], - ), + child: const ButtonContentWidget( + svgAssets: Assets.link, + label: 'Link a space model', ), ) : Container( @@ -307,7 +287,7 @@ class CreateSpaceDialogState extends State { ), ), Padding( - padding: EdgeInsets.symmetric(horizontal: 8.0), + padding: EdgeInsets.symmetric(horizontal: 6.0), child: Text( 'OR', style: TextStyle( @@ -326,47 +306,21 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 25), subspaces == null - ? DefaultButton( - onPressed: () { + ? TextButton( + style: TextButton.styleFrom( + overlayColor: ColorsManager.transparentColor, + ), + onPressed: () async { _showSubSpaceDialog(context, enteredName, [], false, widget.products, subspaces); }, - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, - borderColor: ColorsManager.neutralGray, - borderRadius: 16.0, - padding: 10.0, // Reduced padding for smaller size - child: Align( - alignment: Alignment.centerLeft, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(left: 6.0), - child: SvgPicture.asset( - Assets.addIcon, - width: screenWidth * - 0.015, // Adjust icon size - height: screenWidth * 0.015, - ), - ), - const SizedBox(width: 3), - Flexible( - child: Text( - 'Create sub space', - overflow: TextOverflow - .ellipsis, // Prevent overflow - style: Theme.of(context) - .textTheme - .bodyMedium, - ), - ), - ], - ), + child: const ButtonContentWidget( + icon: Icons.add, + label: 'Create Sub Space', ), ) : SizedBox( - width: screenWidth * 0.35, + width: screenWidth * 0.25, child: Container( padding: const EdgeInsets.all(8.0), decoration: BoxDecoration( @@ -383,49 +337,15 @@ class CreateSpaceDialogState extends State { children: [ if (subspaces != null) ...subspaces!.map( - (subspace) => Chip( - label: Text( - subspace.subspaceName, - style: const TextStyle( - color: ColorsManager - .spaceColor), // Text color - ), - backgroundColor: ColorsManager - .whiteColors, // Chip background color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - 16), // Rounded chip - side: const BorderSide( - color: ColorsManager - .spaceColor), // Border color - ), - ), - ), - GestureDetector( + (subspace) => SubspaceNameDisplayWidget( + text: subspace.subspaceName, + )), + EditChip( onTap: () async { - _showSubSpaceDialog( - context, - enteredName, - [], - false, - widget.products, - subspaces); + _showSubSpaceDialog(context, enteredName, + [], true, widget.products, subspaces); }, - child: Chip( - label: const Text( - 'Edit', - style: TextStyle( - color: ColorsManager.spaceColor), - ), - backgroundColor: - ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: const BorderSide( - color: ColorsManager.spaceColor), - ), - ), - ), + ) ], ), ), @@ -452,7 +372,7 @@ class CreateSpaceDialogState extends State { runSpacing: 8.0, children: [ // Combine tags from spaceModel and subspaces - ..._groupTags([ + ...TagHelper.groupTags([ ...?tags, ...?subspaces?.expand( (subspace) => subspace.tags ?? []) @@ -484,70 +404,31 @@ class CreateSpaceDialogState extends State { ), ), ), - GestureDetector( - onTap: () async { - _showTagCreateDialog(context, enteredName, - widget.products); - // Edit action - }, - child: Chip( - label: const Text( - 'Edit', - style: TextStyle( - color: ColorsManager.spaceColor), - ), - backgroundColor: - ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: const BorderSide( - color: ColorsManager.spaceColor), - ), - ), - ), + + EditChip(onTap: () async { + _showTagCreateDialog( + context, + enteredName, + widget.products, + ); + // Edit action + }) ], ), ), ) - : DefaultButton( + : TextButton( onPressed: () { _showTagCreateDialog( context, enteredName, widget.products); }, - backgroundColor: ColorsManager.textFieldGreyColor, - foregroundColor: Colors.black, - borderColor: ColorsManager.neutralGray, - borderRadius: 16.0, - padding: 10.0, // Reduced padding for smaller size - child: Align( - alignment: Alignment.centerLeft, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(left: 6.0), - child: SvgPicture.asset( - Assets.addIcon, - width: screenWidth * - 0.015, // Adjust icon size - height: screenWidth * 0.015, - ), - ), - const SizedBox(width: 3), - Flexible( - child: Text( - 'Add devices', - overflow: TextOverflow - .ellipsis, // Prevent overflow - style: Theme.of(context) - .textTheme - .bodyMedium, - ), - ), - ], - ), + style: TextButton.styleFrom( + padding: EdgeInsets.zero, ), - ) + child: const ButtonContentWidget( + icon: Icons.add, + label: 'Add Devices', + )) ], ), ), @@ -579,8 +460,13 @@ class CreateSpaceDialogState extends State { ? 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(); } } @@ -655,7 +541,7 @@ class CreateSpaceDialogState extends State { builder: (BuildContext context) { return CreateSubSpaceDialog( spaceName: name, - dialogTitle: 'Create Sub-space', + dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space', spaceTags: spaceTags, isEdit: isEdit, products: products, 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 fc778436..5eef92f8 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 @@ -29,6 +29,7 @@ class AssignTagModelsDialog extends StatelessWidget { final String title; final BuildContext? pageContext; final List? otherSpaceModels; + final List? allSpaceModels; const AssignTagModelsDialog( {Key? key, @@ -42,7 +43,8 @@ class AssignTagModelsDialog extends StatelessWidget { required this.title, this.pageContext, this.otherSpaceModels, - this.spaceModel}) + this.spaceModel, + this.allSpaceModels}) : super(key: key); @override @@ -212,8 +214,8 @@ class AssignTagModelsDialog extends StatelessWidget { width: double.infinity, child: DialogDropdown( items: locations, - selectedValue: - tag.location ?? 'Main Space', + selectedValue: tag.location ?? + 'Main Space', onSelected: (value) { context .read< @@ -250,8 +252,7 @@ class AssignTagModelsDialog extends StatelessWidget { label: 'Add New Device', onPressed: () async { for (var tag in state.tags) { - if (tag.location == null || - subspaces == null) { + if (tag.location == null) { continue; } @@ -352,6 +353,7 @@ class AssignTagModelsDialog extends StatelessWidget { builder: (BuildContext dialogContext) { return CreateSpaceModelDialog( products: products, + allSpaceModels: allSpaceModels, allTags: allTags, pageContext: pageContext, otherSpaceModels: otherSpaceModels, diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart index 6a072e4a..5426f8f0 100644 --- a/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart @@ -6,18 +6,23 @@ import 'subspace_event.dart'; import 'subspace_state.dart'; class SubSpaceBloc extends Bloc { - SubSpaceBloc() : super(SubSpaceState([], [], '')) { + SubSpaceBloc() : super(SubSpaceState([], [], '',{})) { on((event, emit) { - final existingNames = - state.subSpaces.map((e) => e.subspaceName).toSet(); + final existingNames = state.subSpaces.map((e) => e.subspaceName).toSet(); if (existingNames.contains(event.subSpace.subspaceName.toLowerCase())) { + final updatedDuplicates = Set.from(state.duplicates) + ..add(event.subSpace.subspaceName.toLowerCase()); + final updatedSubSpaces = List.from(state.subSpaces) + ..add(event.subSpace); emit(SubSpaceState( - state.subSpaces, + updatedSubSpaces, state.updatedSubSpaceModels, - 'Subspace name already exists.', + '*Duplicated sub-space name', + updatedDuplicates, )); } else { + // Add subspace if no duplicate exists final updatedSubSpaces = List.from(state.subSpaces) ..add(event.subSpace); @@ -25,6 +30,8 @@ class SubSpaceBloc extends Bloc { updatedSubSpaces, state.updatedSubSpaceModels, '', + state.duplicates, +// Clear error message )); } }); @@ -38,6 +45,16 @@ class SubSpaceBloc extends Bloc { state.updatedSubSpaceModels, ); + final nameOccurrences = {}; + for (final subSpace in updatedSubSpaces) { + final lowerName = subSpace.subspaceName.toLowerCase(); + nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1; + } + + final updatedDuplicates = nameOccurrences.entries + .where((entry) => entry.value > 1) + .map((entry) => entry.key) + .toSet(); if (event.subSpace.uuid?.isNotEmpty ?? false) { updatedSubspaceModels.add(UpdateSubspaceModel( action: Action.delete, @@ -45,13 +62,36 @@ class SubSpaceBloc extends Bloc { )); } - emit(SubSpaceState( - updatedSubSpaces, - updatedSubspaceModels, - '', // Clear error message - )); + emit(SubSpaceState(updatedSubSpaces, updatedSubspaceModels, '', + updatedDuplicates // Clear error message + )); }); // Handle UpdateSubSpace Event + + on((event, emit) { + final updatedSubSpaces = state.subSpaces.map((subSpace) { + if (subSpace.uuid == event.updatedSubSpace.uuid) { + return event.updatedSubSpace; + } + return subSpace; + }).toList(); + + final updatedSubspaceModels = List.from( + state.updatedSubSpaceModels, + ); + + updatedSubspaceModels.add(UpdateSubspaceModel( + action: Action.update, + uuid: event.updatedSubSpace.uuid!, + )); + + emit(SubSpaceState( + updatedSubSpaces, + updatedSubspaceModels, + '', + state.duplicates, + )); + }); } } diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart index d1374ea1..9521ff2b 100644 --- a/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart @@ -4,23 +4,26 @@ class SubSpaceState { final List subSpaces; final List updatedSubSpaceModels; final String errorMessage; + final Set duplicates; SubSpaceState( this.subSpaces, this.updatedSubSpaceModels, this.errorMessage, + this.duplicates, ); - SubSpaceState copyWith({ List? subSpaces, List? updatedSubSpaceModels, String? errorMessage, + Set? duplicates, }) { return SubSpaceState( subSpaces ?? this.subSpaces, updatedSubSpaceModels ?? this.updatedSubSpaceModels, errorMessage ?? this.errorMessage, + duplicates ?? this.duplicates, ); } } diff --git a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart index 6fd0b936..19babff9 100644 --- a/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart @@ -81,41 +81,64 @@ class CreateSubSpaceDialog extends StatelessWidget { spacing: 8.0, runSpacing: 8.0, children: [ - ...state.subSpaces.map( - (subSpace) => Chip( - label: Text( - subSpace.subspaceName, - style: const TextStyle( - color: ColorsManager.spaceColor), - ), - backgroundColor: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: const BorderSide( - color: ColorsManager.transparentColor, - width: 0, - ), - ), - deleteIcon: Container( - width: 24, - height: 24, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager.lightGrayColor, - width: 1.5, + ...state.subSpaces.asMap().entries.map( + (entry) { + final index = entry.key; + final subSpace = entry.value; + + final lowerName = + subSpace.subspaceName.toLowerCase(); + + final duplicateIndices = state.subSpaces + .asMap() + .entries + .where((e) => + e.value.subspaceName.toLowerCase() == + lowerName) + .map((e) => e.key) + .toList(); + final isDuplicate = + duplicateIndices.length > 1 && + duplicateIndices.indexOf(index) != 0; + + return Chip( + label: Text( + subSpace.subspaceName, + style: const TextStyle( + color: ColorsManager.spaceColor, ), ), - child: const Icon( - Icons.close, - size: 16, - color: ColorsManager.lightGrayColor, + backgroundColor: ColorsManager.whiteColors, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide( + color: isDuplicate + ? ColorsManager.red + : ColorsManager.transparentColor, + width: 0, + ), ), - ), - onDeleted: () => context - .read() - .add(RemoveSubSpace(subSpace)), - ), + deleteIcon: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager.lightGrayColor, + width: 1.5, + ), + ), + child: const Icon( + Icons.close, + size: 16, + color: ColorsManager.lightGrayColor, + ), + ), + onDeleted: () => context + .read() + .add(RemoveSubSpace(subSpace)), + ); + }, ), SizedBox( width: 200, @@ -142,27 +165,29 @@ class CreateSubSpaceDialog extends StatelessWidget { color: ColorsManager.blackColor), ), ), - if (state.errorMessage.isNotEmpty) - Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Text( - state.errorMessage, - style: const TextStyle( - color: ColorsManager.warningRed, - fontSize: 12, - ), - ), - ), ], ), ), + if (state.errorMessage.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text( + state.errorMessage, + style: const TextStyle( + color: ColorsManager.warningRed, + fontSize: 12, + ), + ), + ), const SizedBox(height: 16), Row( children: [ Expanded( child: CancelButton( label: 'Cancel', - onPressed: () async {}, + onPressed: () async { + Navigator.of(context).pop(); + }, ), ), const SizedBox(width: 10), diff --git a/lib/pages/spaces_management/helper/tag_helper.dart b/lib/pages/spaces_management/helper/tag_helper.dart index d4a0ea55..c0213622 100644 --- a/lib/pages/spaces_management/helper/tag_helper.dart +++ b/lib/pages/spaces_management/helper/tag_helper.dart @@ -1,3 +1,4 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.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/space_model/models/subspace_template_model.dart'; @@ -33,7 +34,7 @@ class TagHelper { return initialTags; } - static Map groupTags(List tags) { + static Map groupTags(List tags) { final Map groupedTags = {}; for (var tag in tags) { if (tag.product != null) { 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 d8b39216..5553d8b0 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 @@ -301,7 +301,7 @@ class CreateSpaceModelBloc if (newTags != null || prevTags != null) { // Case 1: Tags deleted if (prevTags != null && newTags != null) { - for (var prevTag in prevTags!) { + for (var prevTag in prevTags) { final existsInNew = newTags!.any((newTag) => newTag.uuid == prevTag.uuid); if (!existsInNew) { diff --git a/lib/pages/spaces_management/space_model/models/tag_model.dart b/lib/pages/spaces_management/space_model/models/tag_model.dart index 48f89167..c1ab4f40 100644 --- a/lib/pages/spaces_management/space_model/models/tag_model.dart +++ b/lib/pages/spaces_management/space_model/models/tag_model.dart @@ -1,22 +1,22 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:uuid/uuid.dart'; -class TagModel { - String? uuid; - String? tag; - final ProductModel? product; - String internalId; - String? location; - - TagModel( - {this.uuid, - required this.tag, - this.product, - String? internalId, - this.location}) - : internalId = internalId ?? const Uuid().v4(); - +class TagModel extends BaseTag { + TagModel({ + String? uuid, + required String? tag, + ProductModel? product, + String? internalId, + String? location, + }) : super( + uuid: uuid, + tag: tag, + product: product, + internalId: internalId, + location: location, + ); factory TagModel.fromJson(Map json) { final String internalId = json['internalId'] ?? const Uuid().v4(); @@ -30,16 +30,19 @@ class TagModel { ); } + @override TagModel copyWith( {String? tag, ProductModel? product, + String? uuid, String? location, - String? internalId}) { + String? internalId}) { return TagModel( tag: tag ?? this.tag, product: product ?? this.product, location: location ?? this.location, internalId: internalId ?? this.internalId, + uuid:uuid?? this.uuid ); } diff --git a/lib/pages/spaces_management/space_model/view/space_model_page.dart b/lib/pages/spaces_management/space_model/view/space_model_page.dart index ae623e81..cb7bc0c9 100644 --- a/lib/pages/spaces_management/space_model/view/space_model_page.dart +++ b/lib/pages/spaces_management/space_model/view/space_model_page.dart @@ -63,7 +63,8 @@ class SpaceModelPage extends StatelessWidget { } // Render existing space model final model = spaceModels[index]; - final otherModel = List.from(allSpaceModelNames); + final otherModel = + List.from(allSpaceModelNames); otherModel.remove(model.modelName); return GestureDetector( onTap: () { @@ -76,6 +77,7 @@ class SpaceModelPage extends StatelessWidget { spaceModel: model, otherSpaceModels: otherModel, pageContext: context, + allSpaceModels: spaceModels, ); }, ); diff --git a/lib/pages/spaces_management/space_model/widgets/button_content_widget.dart b/lib/pages/spaces_management/space_model/widgets/button_content_widget.dart index 81ecb674..a3ccad7c 100644 --- a/lib/pages/spaces_management/space_model/widgets/button_content_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/button_content_widget.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class ButtonContentWidget extends StatelessWidget { - final IconData icon; + final IconData? icon; final String label; + final String? svgAssets; - const ButtonContentWidget({ - Key? key, - required this.icon, - required this.label, - }) : super(key: key); + const ButtonContentWidget( + {Key? key, this.icon, required this.label, this.svgAssets}) + : super(key: key); @override Widget build(BuildContext context) { @@ -30,10 +30,20 @@ class ButtonContentWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0), child: Row( children: [ - Icon( - icon, - color: ColorsManager.spaceColor, - ), + if (icon != null) + Icon( + icon, + color: ColorsManager.spaceColor, + ), + if (svgAssets != null) + Padding( + padding: const EdgeInsets.only(left: 6.0), + child: SvgPicture.asset( + svgAssets!, + width: screenWidth * 0.015, // Adjust icon size + height: screenWidth * 0.015, + ), + ), const SizedBox(width: 10), Expanded( child: Text( 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 c1bea0fd..c653d1b3 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 @@ -22,6 +22,7 @@ class CreateSpaceModelDialog extends StatelessWidget { final SpaceTemplateModel? spaceModel; final BuildContext? pageContext; final List? otherSpaceModels; + final List? allSpaceModels; const CreateSpaceModelDialog( {Key? key, @@ -29,7 +30,8 @@ class CreateSpaceModelDialog extends StatelessWidget { this.allTags, this.spaceModel, this.pageContext, - this.otherSpaceModels}) + this.otherSpaceModels, + this.allSpaceModels}) : super(key: key); @override @@ -138,6 +140,7 @@ class CreateSpaceModelDialog extends StatelessWidget { spaceNameController: spaceNameController, pageContext: pageContext, otherSpaceModels: otherSpaceModels, + allSpaceModels: allSpaceModels, ), const SizedBox(height: 20), SizedBox( @@ -147,7 +150,8 @@ class CreateSpaceModelDialog extends StatelessWidget { Expanded( child: CancelButton( label: 'Cancel', - onPressed: () => Navigator.of(context).pop(), + onPressed: (){ + Navigator.of(context).pop();}, ), ), const SizedBox(width: 10), diff --git a/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart b/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart index 0dda53a6..8dc981da 100644 --- a/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/common/edit_chip.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class SubspaceModelCreate extends StatelessWidget { @@ -46,39 +48,13 @@ class SubspaceModelCreate extends StatelessWidget { spacing: 8.0, runSpacing: 8.0, children: [ - ...subspaces.map((subspace) => Container( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, vertical: 4.0), - decoration: BoxDecoration( - color: ColorsManager.whiteColors, - borderRadius: BorderRadius.circular(10), - border: Border.all( - color: ColorsManager.transparentColor), - ), - child: Text( - subspace.subspaceName, - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith(color: ColorsManager.spaceColor), - ), + ...subspaces.map((subspace) => SubspaceNameDisplayWidget( + text: subspace.subspaceName, )), - GestureDetector( + EditChip( onTap: () async { await _openDialog(context, 'Edit Sub-space'); }, - child: Chip( - label: const Text( - 'Edit', - style: TextStyle(color: ColorsManager.spaceColor), - ), - backgroundColor: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: - const BorderSide(color: ColorsManager.spaceColor), - ), - ), ), ], ), diff --git a/lib/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart b/lib/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart new file mode 100644 index 00000000..fd3c90b6 --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +class SubspaceNameDisplayWidget extends StatelessWidget { + final String text; + final TextStyle? textStyle; + final Color backgroundColor; + final Color borderColor; + final EdgeInsetsGeometry padding; + final BorderRadiusGeometry borderRadius; + + const SubspaceNameDisplayWidget({ + Key? key, + required this.text, + this.textStyle, + this.backgroundColor = Colors.white, + this.borderColor = Colors.transparent, + this.padding = const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), + this.borderRadius = const BorderRadius.all(Radius.circular(10)), + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + padding: padding, + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: borderRadius, + border: Border.all(color: borderColor), + ), + child: Text( + text, + style: textStyle ?? + Theme.of(context) + .textTheme + .bodySmall + ?.copyWith(color: Colors.black), + ), + ); + } +} diff --git a/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart b/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart index d4111031..76c65805 100644 --- a/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/common/edit_chip.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; @@ -18,6 +19,7 @@ class TagChipDisplay extends StatelessWidget { final TextEditingController spaceNameController; final BuildContext? pageContext; final List? otherSpaceModels; + final List? allSpaceModels; const TagChipDisplay(BuildContext context, {Key? key, @@ -28,7 +30,8 @@ class TagChipDisplay extends StatelessWidget { required this.allTags, required this.spaceNameController, this.pageContext, - this.otherSpaceModels}) + this.otherSpaceModels, + this.allSpaceModels}) : super(key: key); @override @@ -83,45 +86,31 @@ class TagChipDisplay extends StatelessWidget { ), ), ), - GestureDetector( - onTap: () async { - // Use the Navigator's context for showDialog - final navigatorContext = - Navigator.of(context).overlay?.context; + EditChip(onTap: () async { + // Use the Navigator's context for showDialog + Navigator.of(context).pop(); - if (navigatorContext != null) { - await showDialog( - barrierDismissible: false, - context: navigatorContext, - builder: (context) => AssignTagModelsDialog( - products: products, + await showDialog( + barrierDismissible: false, + context: context, + builder: (context) => AssignTagModelsDialog( + products: products, + allSpaceModels: allSpaceModels, + subspaces: subspaces, + pageContext: pageContext, + allTags: allTags, + spaceModel: spaceModel, + otherSpaceModels: otherSpaceModels, + initialTags: TagHelper.generateInitialTags( subspaces: subspaces, - pageContext: pageContext, - allTags: allTags, - spaceModel: spaceModel, - initialTags: TagHelper.generateInitialTags( - subspaces: subspaces, - spaceTagModels: spaceModel?.tags ?? []), - title: 'Edit Device', - addedProducts: - TagHelper.createInitialSelectedProducts( - spaceModel?.tags ?? [], subspaces), - spaceName: spaceModel?.modelName ?? '', - )); - } - }, - child: Chip( - label: const Text( - 'Edit', - style: TextStyle(color: ColorsManager.spaceColor), - ), - backgroundColor: ColorsManager.whiteColors, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: const BorderSide(color: ColorsManager.spaceColor), - ), - ), - ), + spaceTagModels: spaceModel?.tags ?? []), + title: 'Edit Device', + addedProducts: + TagHelper.createInitialSelectedProducts( + spaceModel?.tags ?? [], subspaces), + spaceName: spaceModel?.modelName ?? '', + )); + }) ], ), ), @@ -141,6 +130,7 @@ class TagChipDisplay extends StatelessWidget { pageContext: pageContext, isCreate: true, spaceModel: spaceModel, + otherSpaceModels: otherSpaceModels, ), ); }, diff --git a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart index a9d40147..1ef3da9b 100644 --- a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart +++ b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart @@ -26,6 +26,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { final List? otherSpaceModels; final BuildContext? pageContext; final SpaceTemplateModel? spaceModel; + final List? allSpaceModels; const AddDeviceTypeModelWidget( {super.key, @@ -38,7 +39,8 @@ class AddDeviceTypeModelWidget extends StatelessWidget { required this.isCreate, this.pageContext, this.otherSpaceModels, - this.spaceModel}); + this.spaceModel, + this.allSpaceModels}); @override Widget build(BuildContext context) { @@ -106,6 +108,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { context: context, builder: (BuildContext dialogContext) { return CreateSpaceModelDialog( + allSpaceModels: allSpaceModels, products: products, allTags: allTags, pageContext: pageContext, @@ -175,11 +178,12 @@ class AddDeviceTypeModelWidget extends StatelessWidget { context: context, builder: (context) => AssignTagModelsDialog( products: products, + allSpaceModels: allSpaceModels, subspaces: subspaces, addedProducts: state.selectedProducts, allTags: allTags, spaceName: spaceName, - initialTags: state.initialTag, + initialTags: initialTags, otherSpaceModels: otherSpaceModels, title: dialogTitle, spaceModel: spaceModel, @@ -216,13 +220,15 @@ class AddDeviceTypeModelWidget extends StatelessWidget { if (subspace.tags != null) { initialTags.addAll( subspace.tags!.map( - (tag) => tag.copyWith(location: subspace.subspaceName), + (tag) => tag.copyWith( + location: subspace.subspaceName, + tag: tag.tag, + internalId: tag.internalId), ), ); } } } - return initialTags; } }