diff --git a/lib/pages/spaces_management/all_spaces/model/space_model.dart b/lib/pages/spaces_management/all_spaces/model/space_model.dart index ba5d83d6..64a98803 100644 --- a/lib/pages/spaces_management/all_spaces/model/space_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/space_model.dart @@ -1,6 +1,8 @@ import 'dart:ui'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:uuid/uuid.dart'; @@ -22,6 +24,8 @@ class SpaceModel { SpaceStatus status; String internalId; SpaceTemplateModel? spaceModel; + final List? tags; + List? subspaces; List outgoingConnections = []; // Connections from this space Connection? incomingConnection; // Connections to this space @@ -42,6 +46,8 @@ class SpaceModel { this.incomingConnection, this.status = SpaceStatus.unchanged, this.spaceModel, + this.tags, + this.subspaces, }) : internalId = internalId ?? const Uuid().v4(); factory SpaceModel.fromJson(Map json, @@ -64,6 +70,11 @@ class SpaceModel { name: json['spaceName'], isPrivate: json['isPrivate'] ?? false, invitationCode: json['invitationCode'], + subspaces: (json['subspaces'] as List?) + ?.where((e) => e is Map) // Validate type + .map((e) => SubspaceModel.fromJson(e as Map)) + .toList() ?? + [], parent: parentInternalId != null ? SpaceModel( internalId: parentInternalId, @@ -85,6 +96,11 @@ class SpaceModel { icon: json['icon'] ?? Assets.location, position: Offset(json['x'] ?? 0, json['y'] ?? 0), isHovered: false, + tags: (json['tags'] as List?) + ?.where((item) => item is Map) // Validate type + .map((item) => Tag.fromJson(item as Map)) + .toList() ?? + [], ); if (json['incomingConnections'] != null && @@ -110,6 +126,7 @@ class SpaceModel { 'isPrivate': isPrivate, 'invitationCode': invitationCode, 'parent': parent?.uuid, + 'subspaces': subspaces?.map((e) => e.toJson()).toList(), 'community': community?.toMap(), 'children': children.map((child) => child.toMap()).toList(), 'icon': icon, @@ -117,6 +134,7 @@ class SpaceModel { 'isHovered': isHovered, 'outgoingConnections': outgoingConnections.map((c) => c.toMap()).toList(), 'incomingConnection': incomingConnection?.toMap(), + 'tags': tags?.map((e) => e.toJson()).toList(), }; } @@ -124,3 +142,28 @@ class SpaceModel { outgoingConnections.add(connection); } } + +extension SpaceExtensions on SpaceModel { + List listAllTagValues() { + final List tagValues = []; + + if (tags != null) { + tagValues.addAll( + tags!.map((tag) => tag.tag ?? '').where((tag) => tag.isNotEmpty)); + } + + if (subspaces != null) { + for (final subspace in subspaces!) { + if (subspace.tags != null) { + tagValues.addAll( + subspace.tags! + .map((tag) => tag.tag ?? '') + .where((tag) => tag.isNotEmpty), + ); + } + } + } + + return tagValues; + } +} diff --git a/lib/pages/spaces_management/all_spaces/model/subspace_model.dart b/lib/pages/spaces_management/all_spaces/model/subspace_model.dart new file mode 100644 index 00000000..2c86523f --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/model/subspace_model.dart @@ -0,0 +1,110 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:syncrow_web/utils/constants/action_enum.dart'; + +import 'tag.dart'; + +class SubspaceModel { + final String? uuid; + String subspaceName; + final bool disabled; + List? tags; + + SubspaceModel({ + this.uuid, + required this.subspaceName, + required this.disabled, + this.tags, + }); + + factory SubspaceModel.fromJson(Map json) { + return SubspaceModel( + uuid: json['uuid'] ?? '', + subspaceName: json['subspaceName'] ?? '', + disabled: json['disabled'] ?? false, + tags: (json['tags'] as List?) + ?.map((item) => Tag.fromJson(item)) + .toList() ?? + [], + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'subspaceName': subspaceName, + 'disabled': disabled, + 'tags': tags?.map((e) => e.toJson()).toList() ?? [], + }; + } +} + +class UpdateSubspaceModel { + final String uuid; + final Action action; + final String? subspaceName; + final List? tags; + UpdateSubspaceModel({ + required this.action, + required this.uuid, + this.subspaceName, + this.tags, + }); + + factory UpdateSubspaceModel.fromJson(Map json) { + return UpdateSubspaceModel( + action: ActionExtension.fromValue(json['action']), + uuid: json['uuid'] ?? '', + subspaceName: json['subspaceName'] ?? '', + tags: (json['tags'] as List) + .map((item) => UpdateTag.fromJson(item)) + .toList(), + ); + } + + Map toJson() { + return { + 'action': action.value, + 'uuid': uuid, + 'subspaceName': subspaceName, + 'tags': tags?.map((e) => e.toJson()).toList() ?? [], + }; + } +} + +class UpdateTag { + final Action action; + final String? uuid; + final String tag; + final bool disabled; + final ProductModel? product; + + UpdateTag({ + required this.action, + this.uuid, + required this.tag, + required this.disabled, + this.product, + }); + + factory UpdateTag.fromJson(Map json) { + return UpdateTag( + action: ActionExtension.fromValue(json['action']), + uuid: json['uuid'] ?? '', + tag: json['tag'] ?? '', + disabled: json['disabled'] ?? false, + product: json['product'] != null + ? ProductModel.fromMap(json['product']) + : null, + ); + } + + Map toJson() { + return { + 'action': action.value, + 'uuid': uuid, + 'tag': tag, + 'disabled': disabled, + 'product': product?.toMap(), + }; + } +} \ No newline at end of file diff --git a/lib/pages/spaces_management/all_spaces/model/tag.dart b/lib/pages/spaces_management/all_spaces/model/tag.dart new file mode 100644 index 00000000..b70865c7 --- /dev/null +++ b/lib/pages/spaces_management/all_spaces/model/tag.dart @@ -0,0 +1,61 @@ +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 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(); + + factory Tag.fromJson(Map json) { + final String internalId = json['internalId'] ?? const Uuid().v4(); + + return Tag( + uuid: json['uuid'] ?? '', + internalId: internalId, + tag: json['tag'] ?? '', + product: json['product'] != null + ? ProductModel.fromMap(json['product']) + : null, + ); + } + + Tag copyWith({ + String? tag, + ProductModel? product, + String? location, + }) { + return Tag( + tag: tag ?? this.tag, + product: product ?? this.product, + location: location ?? this.location, + ); + } + + Map toJson() { + return { + 'uuid': uuid, + 'tag': tag, + 'product': product?.toMap(), + }; + } +} + +extension TagModelExtensions on Tag { + TagBodyModel toTagBodyModel() { + return TagBodyModel() + ..uuid = uuid ?? '' + ..tag = tag ?? '' + ..productUuid = product?.uuid; + } +} 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 e46b0d9e..ea6363de 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 @@ -5,8 +5,11 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/add_device_type_widget.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/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/utils/color_manager.dart'; @@ -24,6 +27,7 @@ class CreateSpaceDialog extends StatefulWidget { final SpaceModel? parentSpace; final SpaceModel? editSpace; final List? spaceModels; + final List? subspaces; const CreateSpaceDialog( {super.key, @@ -35,7 +39,8 @@ class CreateSpaceDialog extends StatefulWidget { this.isEdit = false, this.editSpace, this.selectedProducts = const [], - this.spaceModels}); + this.spaceModels, + this.subspaces}); @override CreateSpaceDialogState createState() => CreateSpaceDialogState(); @@ -50,6 +55,7 @@ class CreateSpaceDialogState extends State { bool isOkButtonEnabled = false; bool isNameFieldInvalid = false; bool isNameFieldExist = false; + List? subspaces; @override void initState() { @@ -202,93 +208,93 @@ class CreateSpaceDialogState extends State { ), ), const SizedBox(height: 10), - if (selectedSpaceModel == null) - DefaultButton( - 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, - ), - ), - ], - ), - ), - ), - if (selectedSpaceModel != null) - Container( - width: screenWidth * 0.35, - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 16.0), - decoration: BoxDecoration( - color: ColorsManager.boxColor, - borderRadius: BorderRadius.circular(10), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 8.0, - children: [ - Chip( - label: Text( - selectedSpaceModel?.modelName ?? '', - 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, + selectedSpaceModel == null + ? DefaultButton( + 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, + ), ), - ), - child: const Icon( - Icons.close, - size: 16, - color: ColorsManager.lightGrayColor, - ), + const SizedBox(width: 3), + Flexible( + child: Text( + 'Link a space model', + overflow: TextOverflow + .ellipsis, // Prevent overflow + style: Theme.of(context) + .textTheme + .bodyMedium, + ), + ), + ], ), - onDeleted: () => setState(() { - this.selectedSpaceModel = null; - })), - - ], - ), - ), + ), + ) + : Container( + width: screenWidth * 0.35, + padding: const EdgeInsets.symmetric( + vertical: 10.0, horizontal: 16.0), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(10), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: [ + Chip( + label: Text( + selectedSpaceModel?.modelName ?? '', + 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, + ), + ), + child: const Icon( + Icons.close, + size: 16, + color: ColorsManager.lightGrayColor, + ), + ), + onDeleted: () => setState(() { + this.selectedSpaceModel = null; + })), + ], + ), + ), const SizedBox(height: 25), const Row( children: [ @@ -317,39 +323,111 @@ class CreateSpaceDialogState extends State { ], ), const SizedBox(height: 25), - DefaultButton( - onPressed: () {}, - 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, + subspaces == null + ? DefaultButton( + onPressed: () { + _showSubSpaceModelDialog(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, + ), + ), + ], ), ), - const SizedBox(width: 3), - Flexible( - child: Text( - 'Create sub space', - overflow: - TextOverflow.ellipsis, // Prevent overflow - style: Theme.of(context).textTheme.bodyMedium, + ) + : SizedBox( + width: screenWidth * 0.35, + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: ColorsManager.textFieldGreyColor, + borderRadius: BorderRadius.circular(15), + border: Border.all( + color: ColorsManager.textFieldGreyColor, + width: 3.0, // Border width + ), + ), + child: Wrap( + spacing: 8.0, + runSpacing: 8.0, + 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( + onTap: () async { + _showSubSpaceModelDialog( + context, + enteredName, + [], + false, + 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), + ), + ), + ), + ], ), ), - ], - ), - ), - ), + ), const SizedBox(height: 10), DefaultButton( onPressed: () { @@ -481,15 +559,40 @@ class CreateSpaceDialogState extends State { onSave: (selectedModel) { if (selectedModel != null) { setState(() { - this.selectedSpaceModel = selectedModel; + selectedSpaceModel = selectedModel; }); - print('Selected Model: ${selectedModel.modelName}'); - } else { - print('No model selected'); } }, ); }, ); } + + void _showSubSpaceModelDialog( + BuildContext context, + String name, + final List? spaceTags, + bool isEdit, + List? products, + final List? existingSubSpaces) { + showDialog( + context: context, + builder: (BuildContext context) { + return CreateSubSpaceDialog( + spaceName: name, + dialogTitle: 'Create Sub-space', + spaceTags: spaceTags, + isEdit: isEdit, + products: products, + existingSubSpaces: existingSubSpaces, + onSave: (slectedSubspaces) { + if (slectedSubspaces != null) { + setState(() { + subspaces = slectedSubspaces; + }); + } + }); + }, + ); + } } diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart new file mode 100644 index 00000000..6a072e4a --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart @@ -0,0 +1,57 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/utils/constants/action_enum.dart'; + +import 'subspace_event.dart'; +import 'subspace_state.dart'; + +class SubSpaceBloc extends Bloc { + SubSpaceBloc() : super(SubSpaceState([], [], '')) { + on((event, emit) { + final existingNames = + state.subSpaces.map((e) => e.subspaceName).toSet(); + + if (existingNames.contains(event.subSpace.subspaceName.toLowerCase())) { + emit(SubSpaceState( + state.subSpaces, + state.updatedSubSpaceModels, + 'Subspace name already exists.', + )); + } else { + final updatedSubSpaces = List.from(state.subSpaces) + ..add(event.subSpace); + + emit(SubSpaceState( + updatedSubSpaces, + state.updatedSubSpaceModels, + '', + )); + } + }); + + // Handle RemoveSubSpace Event + on((event, emit) { + final updatedSubSpaces = List.from(state.subSpaces) + ..remove(event.subSpace); + + final updatedSubspaceModels = List.from( + state.updatedSubSpaceModels, + ); + + if (event.subSpace.uuid?.isNotEmpty ?? false) { + updatedSubspaceModels.add(UpdateSubspaceModel( + action: Action.delete, + uuid: event.subSpace.uuid!, + )); + } + + emit(SubSpaceState( + updatedSubSpaces, + updatedSubspaceModels, + '', // Clear error message + )); + }); + + // Handle UpdateSubSpace Event + } +} diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_event.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_event.dart new file mode 100644 index 00000000..cdb62ea1 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_event.dart @@ -0,0 +1,18 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; + +abstract class SubSpaceEvent {} + +class AddSubSpace extends SubSpaceEvent { + final SubspaceModel subSpace; + AddSubSpace(this.subSpace); +} + +class RemoveSubSpace extends SubSpaceEvent { + final SubspaceModel subSpace; + RemoveSubSpace(this.subSpace); +} + +class UpdateSubSpace extends SubSpaceEvent { + final SubspaceModel updatedSubSpace; + UpdateSubSpace(this.updatedSubSpace); +} diff --git a/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart b/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart new file mode 100644 index 00000000..d1374ea1 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/bloc/subspace_state.dart @@ -0,0 +1,26 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; + +class SubSpaceState { + final List subSpaces; + final List updatedSubSpaceModels; + final String errorMessage; + + SubSpaceState( + this.subSpaces, + this.updatedSubSpaceModels, + this.errorMessage, + ); + + + SubSpaceState copyWith({ + List? subSpaces, + List? updatedSubSpaceModels, + String? errorMessage, + }) { + return SubSpaceState( + subSpaces ?? this.subSpaces, + updatedSubSpaceModels ?? this.updatedSubSpaceModels, + errorMessage ?? this.errorMessage, + ); + } +} 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 new file mode 100644 index 00000000..6fd0b936 --- /dev/null +++ b/lib/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart @@ -0,0 +1,196 @@ +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/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_event.dart'; +import 'package:syncrow_web/pages/spaces_management/create_subspace/bloc/subspace_state.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class CreateSubSpaceDialog extends StatelessWidget { + final bool isEdit; + final String dialogTitle; + final List? existingSubSpaces; + final String? spaceName; + final List? spaceTags; + final List? products; + final Function(List?)? onSave; + + const CreateSubSpaceDialog( + {Key? key, + required this.isEdit, + required this.dialogTitle, + this.existingSubSpaces, + required this.spaceName, + required this.spaceTags, + required this.products, + required this.onSave}) + : super(key: key); + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + final textController = TextEditingController(); + + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + child: BlocProvider( + create: (_) { + final bloc = SubSpaceBloc(); + if (existingSubSpaces != null) { + for (var subSpace in existingSubSpaces!) { + bloc.add(AddSubSpace(subSpace)); + } + } + return bloc; + }, + child: BlocBuilder( + builder: (context, state) { + return Container( + color: ColorsManager.whiteColors, + child: SizedBox( + width: screenWidth * 0.35, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + dialogTitle, + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: ColorsManager.blackColor), + ), + const SizedBox(height: 16), + Container( + width: screenWidth * 0.35, + padding: const EdgeInsets.symmetric( + vertical: 10.0, horizontal: 16.0), + decoration: BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.circular(10), + ), + child: Wrap( + 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, + ), + ), + child: const Icon( + Icons.close, + size: 16, + color: ColorsManager.lightGrayColor, + ), + ), + onDeleted: () => context + .read() + .add(RemoveSubSpace(subSpace)), + ), + ), + SizedBox( + width: 200, + child: TextField( + controller: textController, + decoration: InputDecoration( + border: InputBorder.none, + hintText: state.subSpaces.isEmpty + ? 'Please enter the name' + : null, + hintStyle: const TextStyle( + color: ColorsManager.lightGrayColor), + ), + onSubmitted: (value) { + if (value.trim().isNotEmpty) { + context.read().add( + AddSubSpace(SubspaceModel( + subspaceName: value.trim(), + disabled: false))); + textController.clear(); + } + }, + style: const TextStyle( + 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, + ), + ), + ), + ], + ), + ), + const SizedBox(height: 16), + Row( + children: [ + Expanded( + child: CancelButton( + label: 'Cancel', + onPressed: () async {}, + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + onPressed: () async { + final subSpaces = context + .read() + .state + .subSpaces; + onSave!(subSpaces); + Navigator.of(context).pop(); + }, + backgroundColor: ColorsManager.secondaryColor, + borderRadius: 10, + foregroundColor: ColorsManager.whiteColors, + child: const Text('OK'), + ), + ), + ], + ), + ], + ), + ), + )); + }, + ), + ), + ); + } +} diff --git a/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart b/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart index 44cbbc67..2ab969df 100644 --- a/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart +++ b/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart @@ -106,9 +106,8 @@ class LinkSpaceModelDialog extends StatelessWidget { ? () { if (onSave != null) { final selectedModel = - state is SpaceModelSelectedState - ? spaceModels[state.selectedIndex] - : null; + spaceModels[state.selectedIndex]; + onSave!(selectedModel); } Navigator.of(context).pop();