From 81e9e58627cd4fa5166417feacf64fb12f9287d8 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 21 Jan 2025 15:21:25 +0400 Subject: [PATCH 1/8] fixed duplicate tag issue --- .../bloc/assign_tag_model_bloc.dart | 17 +- .../bloc/assign_tag_model_state.dart | 8 +- .../views/assign_tag_models_dialog.dart | 512 +++++++++--------- .../bloc/create_space_model_bloc.dart | 3 +- .../dialog/create_space_model_dialog.dart | 4 + 5 files changed, 283 insertions(+), 261 deletions(-) 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 b5251868..92d7c0e1 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 @@ -47,14 +47,13 @@ class AssignTagModelBloc } emit(AssignTagModelLoaded( - tags: allTags, - isSaveEnabled: _validateTags(allTags), - )); + tags: allTags, + isSaveEnabled: _validateTags(allTags), + errorMessage: '')); }); on((event, emit) { final currentState = state; - if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); @@ -122,9 +121,7 @@ class AssignTagModelBloc } bool _validateTags(List tags) { - if (tags.isEmpty) { - return false; - } + final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet(); final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty); final isValid = uniqueTags.length == tags.length && !hasEmptyTag; @@ -133,7 +130,11 @@ class AssignTagModelBloc String? _getValidationError(List tags) { final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty); - if (hasEmptyTag) return 'Tags cannot be empty.'; + if (hasEmptyTag) { + return 'Tags cannot be empty.'; + } + + // Check for duplicate tags final duplicateTags = tags .map((tag) => tag.tag?.trim() ?? '') .fold>({}, (map, tag) { diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart index 9812a293..a51a9e8f 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart @@ -5,7 +5,7 @@ abstract class AssignTagModelState extends Equatable { const AssignTagModelState(); @override - List get props => []; + List get props => []; } class AssignTagModelInitial extends AssignTagModelState {} @@ -15,7 +15,7 @@ class AssignTagModelLoading extends AssignTagModelState {} class AssignTagModelLoaded extends AssignTagModelState { final List tags; final bool isSaveEnabled; - final String? errorMessage; + final String? errorMessage; const AssignTagModelLoaded({ required this.tags, @@ -24,7 +24,7 @@ class AssignTagModelLoaded extends AssignTagModelState { }); @override - List get props => [tags, isSaveEnabled]; + List get props => [tags, isSaveEnabled, errorMessage]; } class AssignTagModelError extends AssignTagModelState { @@ -33,5 +33,5 @@ class AssignTagModelError extends AssignTagModelState { const AssignTagModelError(this.errorMessage); @override - List get props => [errorMessage]; + List get props => [errorMessage]; } 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 a6b6b7f0..3a2499f9 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 @@ -47,249 +47,202 @@ class AssignTagModelsDialog extends StatelessWidget { ..add('Main Space'); return BlocProvider( - create: (_) => AssignTagModelBloc() - ..add(InitializeTagModels( - initialTags: initialTags, - addedProducts: addedProducts, - )), - child: BlocBuilder( - builder: (context, state) { - if (state is AssignTagModelLoaded) { - final controllers = List.generate( - state.tags.length, - (index) => TextEditingController(text: state.tags[index].tag), - ); + create: (_) => AssignTagModelBloc() + ..add(InitializeTagModels( + initialTags: initialTags, + addedProducts: addedProducts, + )), + child: BlocListener( + listener: (context, state) {}, + child: BlocBuilder( + builder: (context, state) { + if (state is AssignTagModelLoaded) { + final controllers = List.generate( + state.tags.length, + (index) => TextEditingController(text: state.tags[index].tag), + ); - return AlertDialog( - title: Text(title), - backgroundColor: ColorsManager.whiteColors, - content: SingleChildScrollView( - child: Column( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(20), - child: DataTable( - headingRowColor: WidgetStateProperty.all( - ColorsManager.dataHeaderGrey), - border: TableBorder.all( - color: ColorsManager.dataHeaderGrey, - width: 1, + return AlertDialog( + title: Text(title), + backgroundColor: ColorsManager.whiteColors, + content: SingleChildScrollView( + child: Column( + children: [ + ClipRRect( borderRadius: BorderRadius.circular(20), - ), - columns: [ - DataColumn( - label: Text('#', - style: - Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Device', - style: - Theme.of(context).textTheme.bodyMedium)), - DataColumn( - numeric: false, - headingRowAlignment: MainAxisAlignment.start, - label: Text('Tag', - style: - Theme.of(context).textTheme.bodyMedium)), - DataColumn( - label: Text('Location', - style: - Theme.of(context).textTheme.bodyMedium)), - ], - rows: state.tags.isEmpty - ? [ - const DataRow(cells: [ - DataCell( - Center( - child: Text( - 'No Data Available', - style: TextStyle( - fontSize: 14, - color: ColorsManager.lightGrayColor, + child: DataTable( + headingRowColor: WidgetStateProperty.all( + ColorsManager.dataHeaderGrey), + border: TableBorder.all( + color: ColorsManager.dataHeaderGrey, + width: 1, + borderRadius: BorderRadius.circular(20), + ), + columns: [ + DataColumn( + label: Text('#', + style: Theme.of(context) + .textTheme + .bodyMedium)), + DataColumn( + label: Text('Device', + style: Theme.of(context) + .textTheme + .bodyMedium)), + DataColumn( + numeric: false, + headingRowAlignment: MainAxisAlignment.start, + label: Text('Tag', + style: Theme.of(context) + .textTheme + .bodyMedium)), + DataColumn( + label: Text('Location', + style: Theme.of(context) + .textTheme + .bodyMedium)), + ], + rows: state.tags.isEmpty + ? [ + const DataRow(cells: [ + DataCell( + Center( + child: Text( + 'No Data Available', + style: TextStyle( + fontSize: 14, + color: + ColorsManager.lightGrayColor, + ), + ), ), ), - ), - ), - DataCell(SizedBox()), - DataCell(SizedBox()), - DataCell(SizedBox()), - ]) - ] - : List.generate(state.tags.length, (index) { - final tag = state.tags[index]; - final controller = controllers[index]; - final availableTags = getAvailableTags( - allTags ?? [], state.tags, tag); + DataCell(SizedBox()), + DataCell(SizedBox()), + DataCell(SizedBox()), + ]) + ] + : List.generate(state.tags.length, (index) { + final tag = state.tags[index]; + final controller = controllers[index]; + final availableTags = getAvailableTags( + allTags ?? [], state.tags, tag); - return DataRow( - cells: [ - DataCell(Text(index.toString())), - DataCell( - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - tag.product?.name ?? 'Unknown', - overflow: TextOverflow.ellipsis, - )), - const SizedBox(width: 10), + return DataRow( + cells: [ + DataCell(Text(index.toString())), + DataCell( + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + tag.product?.name ?? 'Unknown', + overflow: TextOverflow.ellipsis, + )), + const SizedBox(width: 10), + Container( + width: 20.0, + height: 20.0, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: ColorsManager + .lightGrayColor, + width: 1.0, + ), + ), + child: IconButton( + icon: const Icon( + Icons.close, + color: ColorsManager + .lightGreyColor, + size: 16, + ), + onPressed: () { + context + .read< + AssignTagModelBloc>() + .add(DeleteTagModel( + tagToDelete: tag, + tags: state.tags)); + }, + tooltip: 'Delete Tag', + padding: EdgeInsets.zero, + constraints: + const BoxConstraints(), + ), + ), + ], + ), + ), + DataCell( Container( - width: 20.0, - height: 20.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - color: ColorsManager - .lightGrayColor, - width: 1.0, + alignment: Alignment + .centerLeft, // Align cell content to the left + child: SizedBox( + width: double + .infinity, // Ensure full width for dropdown + child: DialogTextfieldDropdown( + items: availableTags, + initialValue: tag.tag, + onSelected: (value) { + controller.text = value; + context + .read< + AssignTagModelBloc>() + .add(UpdateTag( + index: index, + tag: value, + )); + }, ), ), - child: IconButton( - icon: const Icon( - Icons.close, - color: ColorsManager - .lightGreyColor, - size: 16, - ), - onPressed: () { - context - .read() - .add(DeleteTagModel( - tagToDelete: tag, - tags: state.tags)); - }, - tooltip: 'Delete Tag', - padding: EdgeInsets.zero, - constraints: - const BoxConstraints(), - ), - ), - ], - ), - ), - DataCell( - Container( - alignment: Alignment - .centerLeft, // Align cell content to the left - child: SizedBox( - width: double - .infinity, // Ensure full width for dropdown - child: DialogTextfieldDropdown( - items: availableTags, - initialValue: tag.tag, - onSelected: (value) { - controller.text = value; - context - .read() - .add(UpdateTag( - index: index, - tag: value, - )); - }, ), ), - ), - ), - DataCell( - SizedBox( - width: double.infinity, - child: DialogDropdown( - items: locations, - selectedValue: - tag.location ?? 'None', - onSelected: (value) { - context - .read() - .add(UpdateLocation( - index: index, - location: value, - )); - }, - )), - ), - ], - ); - }), - ), - ), - if (state.errorMessage != null) - Text( - state.errorMessage!, - style: const TextStyle(color: ColorsManager.warningRed), - ), - ], - ), - ), - actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - const SizedBox(width: 10), - Expanded( - child: Builder( - builder: (buttonContext) => CancelButton( - label: 'Add New Device', - onPressed: () async { - for (var tag in state.tags) { - if (tag.location == null || subspaces == null) { - continue; - } - - final previousTagSubspace = - checkTagExistInSubspace(tag, subspaces ?? []); - - if (tag.location == 'Main Space') { - removeTagFromSubspace(tag, previousTagSubspace); - } else if (tag.location != - previousTagSubspace?.subspaceName) { - removeTagFromSubspace(tag, previousTagSubspace); - moveToNewSubspace(tag, subspaces ?? []); - state.tags.removeWhere( - (t) => t.internalId == tag.internalId); - } else { - updateTagInSubspace(tag, previousTagSubspace); - state.tags.removeWhere( - (t) => t.internalId == tag.internalId); - } - } - if (context.mounted) { - await showDialog( - barrierDismissible: false, - context: context, - builder: (dialogContext) => - AddDeviceTypeModelWidget( - products: products, - subspaces: subspaces, - isCreate: false, - initialSelectedProducts: addedProducts, - allTags: allTags, - spaceName: spaceName, - spaceTagModels: state.tags, - onUpdate: (tags, subspaces) { - onUpdate?.call(state.tags, subspaces); - Navigator.of(context).pop(); - }, - ), - ); - } - }, + DataCell( + SizedBox( + width: double.infinity, + child: DialogDropdown( + items: locations, + selectedValue: + tag.location ?? 'None', + onSelected: (value) { + context + .read< + AssignTagModelBloc>() + .add(UpdateLocation( + index: index, + location: value, + )); + }, + )), + ), + ], + ); + }), + ), ), - ), + if (state.errorMessage != null) + Text( + state.errorMessage!, + style: const TextStyle( + color: ColorsManager.warningRed), + ), + ], ), - const SizedBox(width: 10), - Expanded( - child: DefaultButton( - borderRadius: 10, - backgroundColor: state.isSaveEnabled - ? ColorsManager.secondaryColor - : ColorsManager.grayColor, - foregroundColor: ColorsManager.whiteColors, - onPressed: state.isSaveEnabled - ? () async { - Navigator.of(context).pop(); - + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + const SizedBox(width: 10), + Expanded( + child: Builder( + builder: (buttonContext) => CancelButton( + label: 'Add New Device', + onPressed: () async { for (var tag in state.tags) { if (tag.location == null || subspaces == null) { @@ -317,26 +270,89 @@ class AssignTagModelsDialog extends StatelessWidget { (t) => t.internalId == tag.internalId); } } + if (context.mounted) { + await showDialog( + barrierDismissible: false, + context: context, + builder: (dialogContext) => + AddDeviceTypeModelWidget( + products: products, + subspaces: subspaces, + isCreate: false, + initialSelectedProducts: addedProducts, + allTags: allTags, + spaceName: spaceName, + spaceTagModels: state.tags, + onUpdate: (tags, subspaces) { + onUpdate?.call(state.tags, subspaces); + Navigator.of(context).pop(); + }, + ), + ); + } + }, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: DefaultButton( + borderRadius: 10, + backgroundColor: state.isSaveEnabled + ? ColorsManager.secondaryColor + : ColorsManager.grayColor, + foregroundColor: ColorsManager.whiteColors, + onPressed: state.isSaveEnabled + ? () async { + Navigator.of(context).pop(); - onUpdate?.call(state.tags, subspaces); - } - : null, - child: const Text('Save'), - ), + for (var tag in state.tags) { + if (tag.location == null || + subspaces == null) { + continue; + } + + final previousTagSubspace = + checkTagExistInSubspace( + tag, subspaces ?? []); + + if (tag.location == 'Main Space') { + removeTagFromSubspace( + tag, previousTagSubspace); + } else if (tag.location != + previousTagSubspace?.subspaceName) { + removeTagFromSubspace( + tag, previousTagSubspace); + moveToNewSubspace(tag, subspaces ?? []); + state.tags.removeWhere((t) => + t.internalId == tag.internalId); + } else { + updateTagInSubspace( + tag, previousTagSubspace); + state.tags.removeWhere((t) => + t.internalId == tag.internalId); + } + } + + onUpdate?.call(state.tags, subspaces); + } + : null, + child: const Text('Save'), + ), + ), + const SizedBox(width: 10), + ], ), - const SizedBox(width: 10), ], - ), - ], - ); - } else if (state is AssignTagModelLoading) { - return const Center(child: CircularProgressIndicator()); - } else { - return const Center(child: Text('Something went wrong.')); - } - }, - ), - ); + ); + } else if (state is AssignTagModelLoading) { + return const Center(child: CircularProgressIndicator()); + } else { + return const Center(child: Text('Something went wrong.')); + } + }, + ), + )); } List getAvailableTags( 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 6f2f2018..33defb52 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 @@ -3,7 +3,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_spac import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.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/space_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; @@ -53,6 +52,8 @@ class CreateSpaceModelBloc } }); + + on((event, emit) { emit(CreateSpaceModelLoading()); Future.delayed(const Duration(seconds: 1), () { 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 e671410c..a3f34e31 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 @@ -166,6 +166,10 @@ class CreateSpaceModelDialog extends StatelessWidget { modelName: spaceNameController.text.trim(), ); + if(updatedSpaceTemplate.uuid != null){ + + } + context.read().add( CreateSpaceTemplate( spaceTemplate: From e47f3d6d59d2982fa495ba2a6283ba7323a6359a Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 21 Jan 2025 20:26:30 +0400 Subject: [PATCH 2/8] fixed space model creation --- .../views/assign_tag_models_dialog.dart | 37 ++++-- .../bloc/create_space_model_bloc.dart | 11 +- .../bloc/create_space_model_event.dart | 5 +- .../space_model/view/space_model_page.dart | 20 ++-- .../dialog/create_space_model_dialog.dart | 111 +++++++++--------- .../widgets/tag_chips_display_widget.dart | 49 ++++---- .../views/add_device_type_model_widget.dart | 37 +++--- 7 files changed, 144 insertions(+), 126 deletions(-) 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 3a2499f9..006a5f7c 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 @@ -9,8 +9,10 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_pr import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,8 +25,8 @@ class AssignTagModelsDialog extends StatelessWidget { final List? allTags; final String spaceName; final String title; - final void Function( - List? tags, List? subspaces)? onUpdate; + final BuildContext? pageContext; + final List? otherSpaceModels; const AssignTagModelsDialog( {Key? key, @@ -36,7 +38,8 @@ class AssignTagModelsDialog extends StatelessWidget { this.allTags, required this.spaceName, required this.title, - this.onUpdate}) + this.pageContext, + this.otherSpaceModels}) : super(key: key); @override @@ -271,6 +274,8 @@ class AssignTagModelsDialog extends StatelessWidget { } } if (context.mounted) { + Navigator.of(context).pop(); + await showDialog( barrierDismissible: false, context: context, @@ -282,11 +287,9 @@ class AssignTagModelsDialog extends StatelessWidget { initialSelectedProducts: addedProducts, allTags: allTags, spaceName: spaceName, + otherSpaceModels: otherSpaceModels, spaceTagModels: state.tags, - onUpdate: (tags, subspaces) { - onUpdate?.call(state.tags, subspaces); - Navigator.of(context).pop(); - }, + pageContext: pageContext, ), ); } @@ -304,8 +307,6 @@ class AssignTagModelsDialog extends StatelessWidget { foregroundColor: ColorsManager.whiteColors, onPressed: state.isSaveEnabled ? () async { - Navigator.of(context).pop(); - for (var tag in state.tags) { if (tag.location == null || subspaces == null) { @@ -333,8 +334,24 @@ class AssignTagModelsDialog extends StatelessWidget { t.internalId == tag.internalId); } } + Navigator.of(context) + .popUntil((route) => route.isFirst); - onUpdate?.call(state.tags, subspaces); + await showDialog( + context: context, + builder: (BuildContext dialogContext) { + return CreateSpaceModelDialog( + products: products, + allTags: allTags, + pageContext: pageContext, + otherSpaceModels: otherSpaceModels, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: state.tags, + subspaceModels: subspaces), + ); + }, + ); } : null, child: const Text('Save'), 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 33defb52..417ba1fd 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 @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart'; @@ -52,8 +54,6 @@ class CreateSpaceModelBloc } }); - - on((event, emit) { emit(CreateSpaceModelLoading()); Future.delayed(const Duration(seconds: 1), () { @@ -173,7 +173,12 @@ class CreateSpaceModelBloc on((event, emit) { final currentState = state; if (currentState is CreateSpaceModelLoaded) { - if (event.name.trim().isEmpty) { + if (event.allModels.contains(event.name) == true) { + emit(CreateSpaceModelLoaded( + currentState.space, + errorMessage: "Duplicate Model name", + )); + } else if (event.name.trim().isEmpty) { emit(CreateSpaceModelLoaded( currentState.space, errorMessage: "Model name cannot be empty", diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart index 2bcb12b6..a78ae78f 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart @@ -34,11 +34,12 @@ class CreateSpaceTemplate extends CreateSpaceModelEvent { class UpdateSpaceTemplateName extends CreateSpaceModelEvent { final String name; + final List allModels; - UpdateSpaceTemplateName({required this.name}); + UpdateSpaceTemplateName({required this.name, required this.allModels}); @override - List get props => [name]; + List get props => [name,allModels]; } class AddSubspacesToSpaceTemplate extends CreateSpaceModelEvent { 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 d0b5a300..47bea955 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 @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.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/add_space_model_widget.dart'; @@ -24,6 +23,7 @@ class SpaceModelPage extends StatelessWidget { } else if (state is SpaceModelLoaded) { final spaceModels = state.spaceModels; final allTagValues = _getAllTagValues(spaceModels); + final allSpaceModelNames = _getAllSpaceModelName(spaceModels); return Scaffold( backgroundColor: ColorsManager.whiteColors, @@ -52,12 +52,8 @@ class SpaceModelPage extends StatelessWidget { return CreateSpaceModelDialog( products: products, allTags: allTagValues, - onLoad: (newModel) { - context.read().add( - CreateSpaceModel( - newSpaceModel: newModel), - ); - }, + pageContext: context, + otherSpaceModels: allSpaceModelNames, ); }, ); @@ -76,7 +72,7 @@ class SpaceModelPage extends StatelessWidget { products: products, allTags: allTagValues, spaceModel: model, - onLoad: (newModel) {}, + otherSpaceModels: allSpaceModelNames, ); }, ); @@ -128,4 +124,12 @@ class SpaceModelPage extends StatelessWidget { } return allTags; } + + List _getAllSpaceModelName(List spaceModels) { + final List names = []; + for (final spaceModel in spaceModels) { + names.add(spaceModel.modelName); + } + return names; + } } 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 a3f34e31..4c4b8a7c 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 @@ -8,6 +8,8 @@ import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_spac import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.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/tag_chips_display_widget.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; @@ -17,15 +19,17 @@ class CreateSpaceModelDialog extends StatelessWidget { final List? products; final List? allTags; final SpaceTemplateModel? spaceModel; - final void Function(SpaceTemplateModel newModel)? onLoad; + final BuildContext? pageContext; + final List? otherSpaceModels; - const CreateSpaceModelDialog({ - Key? key, - this.products, - this.allTags, - this.spaceModel, - this.onLoad, - }) : super(key: key); + const CreateSpaceModelDialog( + {Key? key, + this.products, + this.allTags, + this.spaceModel, + this.pageContext, + this.otherSpaceModels}) + : super(key: key); @override Widget build(BuildContext context) { @@ -44,17 +48,17 @@ class CreateSpaceModelDialog extends StatelessWidget { child: BlocProvider( create: (_) { final bloc = CreateSpaceModelBloc(_spaceModelApi); - if (spaceModel != null) { - bloc.add(UpdateSpaceTemplate(spaceModel!)); - } else { - bloc.add(UpdateSpaceTemplate(SpaceTemplateModel( - modelName: '', - subspaceModels: const [], - ))); - } - + if (spaceModel != null) { + bloc.add(UpdateSpaceTemplate(spaceModel!)); + } else { + bloc.add(UpdateSpaceTemplate(SpaceTemplateModel( + modelName: '', + subspaceModels: const [], + ))); + } + spaceNameController.addListener(() { - bloc.add(UpdateSpaceTemplateName(name: spaceNameController.text)); + bloc.add(UpdateSpaceTemplateName(name: spaceNameController.text,allModels: otherSpaceModels ??[])); }); return bloc; @@ -86,9 +90,10 @@ class CreateSpaceModelDialog extends StatelessWidget { child: TextField( controller: spaceNameController, onChanged: (value) { - context - .read() - .add(UpdateSpaceTemplateName(name: value)); + context.read().add( + UpdateSpaceTemplateName( + name: value, + allModels: otherSpaceModels ?? [])); }, style: const TextStyle(color: ColorsManager.blackColor), decoration: InputDecoration( @@ -128,21 +133,8 @@ class CreateSpaceModelDialog extends StatelessWidget { subspaces: subspaces, allTags: allTags, spaceNameController: spaceNameController, - onLoad: (tags, subspaces) { - if (context.read().state - is CreateSpaceModelLoaded) { - if (subspaces != null) { - context - .read() - .add(AddSubspacesToSpaceTemplate(subspaces)); - } - if (tags != null) { - context - .read() - .add(AddTagsToSpaceTemplate(tags)); - } - } - }, + pageContext: pageContext, + otherSpaceModels: otherSpaceModels, ), const SizedBox(height: 20), SizedBox( @@ -161,26 +153,35 @@ class CreateSpaceModelDialog extends StatelessWidget { onPressed: state.errorMessage == null || isNameValid ? () { - final updatedSpaceTemplate = - updatedSpaceModel.copyWith( - modelName: - spaceNameController.text.trim(), - ); - if(updatedSpaceTemplate.uuid != null){ - + if (updatedSpaceModel.uuid == null) { + final updatedSpaceTemplate = + updatedSpaceModel.copyWith( + modelName: + spaceNameController.text.trim(), + ); + if (updatedSpaceTemplate.uuid != + null) {} + + context + .read() + .add( + CreateSpaceTemplate( + spaceTemplate: + updatedSpaceTemplate, + onCreate: (newModel) { + if (pageContext != null) { + pageContext! + .read() + .add(CreateSpaceModel( + newSpaceModel: + newModel)); + } + Navigator.of(context) + .pop(); // Close the dialog + }, + ), + ); } - - context.read().add( - CreateSpaceTemplate( - spaceTemplate: - updatedSpaceTemplate, - onCreate: (newModel) { - onLoad!(newModel); - Navigator.of(context) - .pop(); // Close the dialog - }, - ), - ); } : null, backgroundColor: ColorsManager.secondaryColor, 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 18efd501..0c46076f 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 @@ -5,7 +5,6 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assi import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -17,8 +16,8 @@ class TagChipDisplay extends StatelessWidget { final List? subspaces; final List? allTags; final TextEditingController spaceNameController; - final void Function( - List? tags, List? subspaces)? onLoad; + final BuildContext? pageContext; + final List? otherSpaceModels; const TagChipDisplay(BuildContext context, {Key? key, @@ -28,7 +27,8 @@ class TagChipDisplay extends StatelessWidget { required this.subspaces, required this.allTags, required this.spaceNameController, - this.onLoad}) + this.pageContext, + this.otherSpaceModels}) : super(key: key); @override @@ -91,24 +91,22 @@ class TagChipDisplay extends StatelessWidget { if (navigatorContext != null) { await showDialog( - barrierDismissible: false, - context: navigatorContext, - builder: (context) => AssignTagModelsDialog( - products: products, - subspaces: subspaces, - allTags: allTags, - initialTags: TagHelper.generateInitialTags( + barrierDismissible: false, + context: navigatorContext, + builder: (context) => AssignTagModelsDialog( + products: products, subspaces: subspaces, - spaceTagModels: spaceModel?.tags ?? []), - title: 'Edit Device', - addedProducts: - TagHelper.createInitialSelectedProducts( - spaceModel?.tags ?? [], subspaces), - spaceName: spaceModel?.modelName ?? '', - onUpdate: (tags, subspaces) { - onLoad?.call(tags, subspaces); - }), - ); + pageContext: pageContext, + allTags: allTags, + initialTags: TagHelper.generateInitialTags( + subspaces: subspaces, + spaceTagModels: spaceModel?.tags ?? []), + title: 'Edit Device', + addedProducts: + TagHelper.createInitialSelectedProducts( + spaceModel?.tags ?? [], subspaces), + spaceName: spaceModel?.modelName ?? '', + )); } }, child: Chip( @@ -129,6 +127,8 @@ class TagChipDisplay extends StatelessWidget { ) : TextButton( onPressed: () async { + Navigator.of(context).pop(); + await showDialog( barrierDismissible: false, context: context, @@ -137,13 +137,8 @@ class TagChipDisplay extends StatelessWidget { subspaces: subspaces, allTags: allTags, spaceName: spaceNameController.text, + pageContext: pageContext, isCreate: true, - onUpdate: (tags, subspaces) { - onLoad?.call(tags, subspaces); - }, - onLoad: (tags, subspaces) { - onLoad?.call(tags, subspaces); - }, ), ); }, 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 7944eb4f..ddb9b5a3 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 @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.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/link_space_model/bloc/link_space_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart'; @@ -21,28 +22,23 @@ class AddDeviceTypeModelWidget extends StatelessWidget { final List? allTags; final String spaceName; final bool isCreate; - final void Function( - List? tags, List? subspaces)? onLoad; - final void Function( - List? tags, List? subspaces)? onUpdate; + final List? otherSpaceModels; + final BuildContext? pageContext; - const AddDeviceTypeModelWidget({ - super.key, - this.products, - this.initialSelectedProducts, - this.subspaces, - this.allTags, - this.spaceTagModels, - required this.spaceName, - required this.isCreate, - this.onLoad, - this.onUpdate, - }); + const AddDeviceTypeModelWidget( + {super.key, + this.products, + this.initialSelectedProducts, + this.subspaces, + this.allTags, + this.spaceTagModels, + required this.spaceName, + required this.isCreate, + this.pageContext, + this.otherSpaceModels}); @override Widget build(BuildContext context) { - - final size = MediaQuery.of(context).size; final crossAxisCount = size.width > 1200 ? 8 @@ -142,10 +138,9 @@ class AddDeviceTypeModelWidget extends StatelessWidget { allTags: allTags, spaceName: spaceName, initialTags: state.initialTag, + otherSpaceModels: otherSpaceModels, title: dialogTitle, - onUpdate: (tags, subspaces) { - onUpdate?.call(tags, subspaces); - }, + pageContext: pageContext, ), ); } From 44d95f57011aeb3bb1ead79d9ad4e34b60afecc9 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 21 Jan 2025 20:29:15 +0400 Subject: [PATCH 3/8] fixed index --- .../assign_tag_models/views/assign_tag_models_dialog.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 006a5f7c..d345a8c6 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 @@ -133,7 +133,7 @@ class AssignTagModelsDialog extends StatelessWidget { return DataRow( cells: [ - DataCell(Text(index.toString())), + DataCell(Text((index + 1).toString())), DataCell( Row( mainAxisAlignment: From 18afc4f563767f5bc429065015d031f21ffbeb11 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Tue, 21 Jan 2025 20:37:21 +0400 Subject: [PATCH 4/8] fixed issues --- .../spaces_management/space_model/view/space_model_page.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 47bea955..33509998 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,6 +63,9 @@ class SpaceModelPage extends StatelessWidget { } // Render existing space model final model = spaceModels[index]; + final otherModel = List.from(allSpaceModelNames); + otherModel.remove(model.modelName); + return GestureDetector( onTap: () { showDialog( @@ -72,7 +75,7 @@ class SpaceModelPage extends StatelessWidget { products: products, allTags: allTagValues, spaceModel: model, - otherSpaceModels: allSpaceModelNames, + otherSpaceModels: otherModel, ); }, ); From 7ffdc67016ae96b49e06bb1fb629d5bc929677ca Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 22 Jan 2025 12:48:46 +0400 Subject: [PATCH 5/8] added product comparison --- .../all_spaces/model/product_model.dart | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/pages/spaces_management/all_spaces/model/product_model.dart b/lib/pages/spaces_management/all_spaces/model/product_model.dart index 557c106b..a4ebd550 100644 --- a/lib/pages/spaces_management/all_spaces/model/product_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/product_model.dart @@ -66,4 +66,25 @@ class ProductModel { String toString() { return 'ProductModel(uuid: $uuid, catName: $catName, prodId: $prodId, prodType: $prodType, name: $name, icon: $icon)'; } + + @override + bool operator ==(Object other) => + identical(this, other) || + other is ProductModel && + runtimeType == other.runtimeType && + uuid == other.uuid && + catName == other.catName && + prodId == other.prodId && + prodType == other.prodType && + name == other.name && + icon == other.icon; + + @override + int get hashCode => + uuid.hashCode ^ + catName.hashCode ^ + prodId.hashCode ^ + prodType.hashCode ^ + name.hashCode ^ + icon.hashCode; } From f35b699d4c7b2afb433e255d79db53a2ca185e46 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 22 Jan 2025 12:49:47 +0400 Subject: [PATCH 6/8] fixed the edit flow for space model --- .../widgets/device_type_tile_widget.dart | 1 + .../bloc/space_management_state.dart | 15 +- .../widgets/add_device_type_widget.dart | 1 + .../all_spaces/widgets/counter_widget.dart | 25 +- .../views/assign_tag_models_dialog.dart | 37 ++- .../views/create_subspace_model_dialog.dart | 6 +- .../spaces_management/helper/tag_helper.dart | 3 +- .../bloc/create_space_model_bloc.dart | 228 +++++++++++++++++- .../bloc/create_space_model_event.dart | 15 +- .../space_model/bloc/space_model_bloc.dart | 20 ++ .../space_model/bloc/space_model_event.dart | 18 ++ .../create_space_template_body_model.dart | 4 +- .../models/space_template_model.dart | 47 +--- .../space_model/models/tag_update_model.dart | 34 +++ .../space_model/view/space_model_page.dart | 2 +- .../dialog/create_space_model_dialog.dart | 64 ++++- .../widgets/subspace_chip_widget.dart | 3 - .../widgets/subspace_model_create_widget.dart | 34 +-- .../widgets/tag_chips_display_widget.dart | 2 + .../views/add_device_type_model_widget.dart | 47 +++- .../widgets/device_type_tile_widget.dart | 12 +- .../widgets/scrollable_grid_view_widget.dart | 5 +- lib/services/space_model_mang_api.dart | 14 ++ lib/utils/constants/api_const.dart | 7 +- 24 files changed, 517 insertions(+), 127 deletions(-) create mode 100644 lib/pages/spaces_management/space_model/models/tag_update_model.dart diff --git a/lib/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart b/lib/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart index 2feea7d9..08ad79ac 100644 --- a/lib/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart +++ b/lib/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.dart @@ -48,6 +48,7 @@ class DeviceTypeTileWidget extends StatelessWidget { DeviceNameWidget(name: product.name), const SizedBox(height: 4), CounterWidget( + isCreate: false, initialCount: selectedProduct.count, onCountChanged: (newCount) { context.read().add( diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart index 635c244d..571651e5 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart @@ -27,8 +27,7 @@ class SpaceManagementLoaded extends SpaceManagementState { required this.products, this.selectedCommunity, this.selectedSpace, - this.spaceModels - }); + this.spaceModels}); } class SpaceModelManagenetLoaded extends SpaceManagementState { @@ -38,14 +37,10 @@ class SpaceModelManagenetLoaded extends SpaceManagementState { class BlankState extends SpaceManagementState { final List communities; final List products; - List? spaceModels; + List? spaceModels; - - BlankState({ - required this.communities, - required this.products, - this.spaceModels - }); + BlankState( + {required this.communities, required this.products, this.spaceModels}); } class SpaceCreationSuccess extends SpaceManagementState { @@ -67,7 +62,7 @@ class SpaceManagementError extends SpaceManagementState { } class SpaceModelLoaded extends SpaceManagementState { - final List spaceModels; + List spaceModels; final List products; final List communities; diff --git a/lib/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart index 351eacce..0e9f4bd1 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart @@ -137,6 +137,7 @@ class _AddDeviceWidgetState extends State { _buildDeviceName(product, size), const SizedBox(height: 4), CounterWidget( + isCreate: false, initialCount: selectedProduct.count, onCountChanged: (newCount) { setState(() { diff --git a/lib/pages/spaces_management/all_spaces/widgets/counter_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/counter_widget.dart index 66935b12..2289819b 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/counter_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/counter_widget.dart @@ -4,12 +4,14 @@ import 'package:syncrow_web/utils/color_manager.dart'; class CounterWidget extends StatefulWidget { final int initialCount; final ValueChanged onCountChanged; + final bool isCreate; - const CounterWidget({ - Key? key, - this.initialCount = 0, - required this.onCountChanged, - }) : super(key: key); + const CounterWidget( + {Key? key, + this.initialCount = 0, + required this.onCountChanged, + required this.isCreate}) + : super(key: key); @override State createState() => _CounterWidgetState(); @@ -53,25 +55,26 @@ class _CounterWidgetState extends State { child: Row( mainAxisSize: MainAxisSize.min, children: [ - _buildCounterButton(Icons.remove, _decrementCounter), + _buildCounterButton(Icons.remove, _decrementCounter,!widget.isCreate ), const SizedBox(width: 8), Text( '$_counter', - style: theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.spaceColor), + style: theme.textTheme.bodyLarge + ?.copyWith(color: ColorsManager.spaceColor), ), const SizedBox(width: 8), - _buildCounterButton(Icons.add, _incrementCounter), + _buildCounterButton(Icons.add, _incrementCounter, false), ], ), ); } - Widget _buildCounterButton(IconData icon, VoidCallback onPressed) { + Widget _buildCounterButton(IconData icon, VoidCallback onPressed, bool isDisabled) { return GestureDetector( - onTap: onPressed, + onTap: isDisabled? null: onPressed, child: Icon( icon, - color: ColorsManager.spaceColor, + color: isDisabled? ColorsManager.spaceColor.withOpacity(0.3): ColorsManager.spaceColor, size: 18, ), ); 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 d345a8c6..fc778436 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 @@ -19,6 +19,8 @@ import 'package:syncrow_web/utils/color_manager.dart'; class AssignTagModelsDialog extends StatelessWidget { final List? products; final List? subspaces; + final SpaceTemplateModel? spaceModel; + final List initialTags; final ValueChanged>? onTagsAssigned; final List addedProducts; @@ -39,7 +41,8 @@ class AssignTagModelsDialog extends StatelessWidget { required this.spaceName, required this.title, this.pageContext, - this.otherSpaceModels}) + this.otherSpaceModels, + this.spaceModel}) : super(key: key); @override @@ -210,7 +213,7 @@ class AssignTagModelsDialog extends StatelessWidget { child: DialogDropdown( items: locations, selectedValue: - tag.location ?? 'None', + tag.location ?? 'Main Space', onSelected: (value) { context .read< @@ -281,16 +284,23 @@ class AssignTagModelsDialog extends StatelessWidget { context: context, builder: (dialogContext) => AddDeviceTypeModelWidget( - products: products, - subspaces: subspaces, - isCreate: false, - initialSelectedProducts: addedProducts, - allTags: allTags, - spaceName: spaceName, - otherSpaceModels: otherSpaceModels, - spaceTagModels: state.tags, - pageContext: pageContext, - ), + products: products, + subspaces: subspaces, + isCreate: false, + initialSelectedProducts: + addedProducts, + allTags: allTags, + spaceName: spaceName, + otherSpaceModels: otherSpaceModels, + spaceTagModels: state.tags, + pageContext: pageContext, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: state.tags, + uuid: spaceModel?.uuid, + internalId: + spaceModel?.internalId, + subspaceModels: subspaces)), ); } }, @@ -348,6 +358,9 @@ class AssignTagModelsDialog extends StatelessWidget { spaceModel: SpaceTemplateModel( modelName: spaceName, tags: state.tags, + uuid: spaceModel?.uuid, + internalId: + spaceModel?.internalId, subspaceModels: subspaces), ); }, diff --git a/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart b/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart index 82aa3684..4c0cb99f 100644 --- a/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart +++ b/lib/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart @@ -186,8 +186,7 @@ class CreateSubSpaceModelDialog extends StatelessWidget { const SizedBox(width: 10), Expanded( child: DefaultButton( - onPressed: (state.subSpaces.isEmpty || - state.errorMessage.isNotEmpty) + onPressed: (state.errorMessage.isNotEmpty) ? null : () async { final subSpaces = context @@ -201,8 +200,7 @@ class CreateSubSpaceModelDialog extends StatelessWidget { }, backgroundColor: ColorsManager.secondaryColor, borderRadius: 10, - foregroundColor: state.subSpaces.isEmpty || - state.errorMessage.isNotEmpty + foregroundColor: state.errorMessage.isNotEmpty ? ColorsManager.whiteColorsWithOpacity : ColorsManager.whiteColors, child: const Text('OK'), diff --git a/lib/pages/spaces_management/helper/tag_helper.dart b/lib/pages/spaces_management/helper/tag_helper.dart index bfff02a9..d4a0ea55 100644 --- a/lib/pages/spaces_management/helper/tag_helper.dart +++ b/lib/pages/spaces_management/helper/tag_helper.dart @@ -37,7 +37,8 @@ class TagHelper { final Map groupedTags = {}; for (var tag in tags) { if (tag.product != null) { - groupedTags[tag.product!] = (groupedTags[tag.product!] ?? 0) + 1; + final product = tag.product!; + groupedTags[product] = (groupedTags[product] ?? 0) + 1; } } return groupedTags; 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 417ba1fd..40db384a 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 @@ -1,12 +1,12 @@ -import 'dart:math'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.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/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; +import 'package:syncrow_web/utils/constants/action_enum.dart'; class CreateSpaceModelBloc extends Bloc { @@ -193,5 +193,229 @@ class CreateSpaceModelBloc emit(CreateSpaceModelError("Space template not initialized")); } }); + + on((event, emit) async { + try { + final prevSpaceModel = event.spaceTemplate; + final newSpaceModel = event.updatedSpaceTemplate; + String? spaceModelName; + if (prevSpaceModel.modelName != newSpaceModel.modelName) { + spaceModelName = newSpaceModel.modelName; + } + List tagUpdates = []; + final List subspaceUpdates = []; + + tagUpdates = processTagUpdates(prevSpaceModel.tags, newSpaceModel.tags); + + if (prevSpaceModel.subspaceModels != null) { + for (var prevSubspace in prevSpaceModel.subspaceModels!) { + if (newSpaceModel.subspaceModels == null) { + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.delete, + uuid: prevSubspace.uuid, + )); + continue; + } + + final subspaceExistsInNew = newSpaceModel.subspaceModels! + .any((newSubspace) => newSubspace.uuid == prevSubspace.uuid); + if (!subspaceExistsInNew) { + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.delete, + uuid: prevSubspace.uuid, + )); + } + } + } + + if (newSpaceModel.subspaceModels != null) { + for (var newSubspaceModel in newSpaceModel.subspaceModels!) { + if (newSubspaceModel.uuid == null || + newSubspaceModel.uuid!.isEmpty) { + final List tagUpdatesInSubspace = []; + + if (newSubspaceModel.tags != null) { + for (var tag in newSubspaceModel.tags!) { + tagUpdatesInSubspace.add(TagModelUpdate( + action: Action.add, + uuid: tag.uuid, + tag: tag.tag, + productUuid: tag.product?.uuid, + )); + } + } + + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.add, + subspaceName: newSubspaceModel.subspaceName, + tags: tagUpdatesInSubspace, + )); + } + } + } + + if (newSpaceModel.subspaceModels != null && + prevSpaceModel.subspaceModels != null) { + final prevSubspaceMap = { + for (var subspace in prevSpaceModel.subspaceModels!) + subspace.uuid: subspace + }; + + for (var newSubspace in newSpaceModel.subspaceModels!) { + if (newSubspace.uuid != null && + prevSubspaceMap.containsKey(newSubspace.uuid)) { + final prevSubspace = prevSubspaceMap[newSubspace.uuid]!; + + // Check if modelName has changed + if (newSubspace.subspaceName != prevSubspace.subspaceName) { + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.update, + uuid: newSubspace.uuid, + subspaceName: newSubspace.subspaceName, + )); + } + + // Compare tags within the subspace + final List tagUpdatesInSubspace = []; + if (prevSubspace.tags != null && newSubspace.tags != null) { + final prevTagMap = { + for (var tag in prevSubspace.tags!) tag.uuid: tag + }; + + // Check for deleted tags + for (var prevTag in prevSubspace.tags!) { + if (!newSubspace.tags! + .any((newTag) => newTag.uuid == prevTag.uuid)) { + tagUpdatesInSubspace.add(TagModelUpdate( + action: Action.delete, + uuid: prevTag.uuid, + )); + } + } + + // Check for added tags, including those without a UUID + for (var newTag in newSubspace.tags!) { + if (newTag.uuid == null || newTag.uuid!.isEmpty) { + // Add new tag without UUID + tagUpdatesInSubspace.add(TagModelUpdate( + action: Action.add, + uuid: null, // or generate a new UUID if required + tag: newTag.tag, + productUuid: newTag.product?.uuid, + )); + } else if (!prevSubspace.tags! + .any((prevTag) => prevTag.uuid == newTag.uuid)) { + // Add new tag with UUID + tagUpdatesInSubspace.add(TagModelUpdate( + action: Action.add, + uuid: newTag.uuid, + tag: newTag.tag, + productUuid: newTag.product?.uuid, + )); + } + } + + // Check for updated tags + for (var prevTag in prevSubspace.tags!) { + final newTag = newSubspace.tags!.cast().firstWhere( + (tag) => tag?.uuid == prevTag.uuid, + orElse: () => null, + ); + if (newTag != null && newTag.tag != prevTag.tag) { + tagUpdatesInSubspace.add(TagModelUpdate( + action: Action.update, + uuid: newTag.uuid, + tag: newTag.tag, + )); + } + } + } + + // Add the subspace with updated tags if necessary + if (tagUpdatesInSubspace.isNotEmpty) { + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.update, + uuid: newSubspace.uuid, + subspaceName: newSubspace.subspaceName, + tags: tagUpdatesInSubspace, + )); + } + } + } + } + + final spaceModelBody = CreateSpaceTemplateBodyModel( + modelName: spaceModelName, + tags: tagUpdates, + subspaceModels: subspaceUpdates); + + final res = await _api.updateSpaceModel( + spaceModelBody, prevSpaceModel.uuid ?? ''); + + if (res != null) { + emit(CreateSpaceModelLoaded(newSpaceModel)); + if (event.onUpdate != null) { + event.onUpdate!(event.updatedSpaceTemplate); + } + } + } catch (e) { + emit(CreateSpaceModelError('Error creating space model')); + } + }); + } + + List processTagUpdates( + List? prevTags, + List? newTags, + ) { + final List tagUpdates = []; + final processedTags = {}; + + if (newTags != null || prevTags != null) { + // Case 1: Tags deleted + if (prevTags != null && newTags != null) { + for (var prevTag in prevTags!) { + final existsInNew = + newTags!.any((newTag) => newTag.uuid == prevTag.uuid); + if (!existsInNew) { + tagUpdates + .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); + } + } + } + + // Case 2: Tags added + if (newTags != null) { + for (var newTag in newTags!) { + // Tag without UUID + if ((newTag.uuid == null || newTag.uuid!.isEmpty) && + !processedTags.contains(newTag.tag)) { + tagUpdates.add(TagModelUpdate( + action: Action.add, + tag: newTag.tag, + productUuid: newTag.product?.uuid)); + processedTags.add(newTag.tag); + } + } + } + + // Case 3: Tags updated + if (prevTags != null && newTags != null) { + final newTagMap = {for (var tag in newTags!) tag.uuid: tag}; + + for (var prevTag in prevTags!) { + final newTag = newTagMap[prevTag.uuid]; + if (newTag != null) { + tagUpdates.add(TagModelUpdate( + action: Action.update, + uuid: newTag.uuid, + tag: newTag.tag, + )); + } else {} + } + } + } + + return tagUpdates; } } diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart index a78ae78f..22828941 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart @@ -22,7 +22,6 @@ class CreateSpaceTemplate extends CreateSpaceModelEvent { final SpaceTemplateModel spaceTemplate; final Function(SpaceTemplateModel)? onCreate; - const CreateSpaceTemplate({ required this.spaceTemplate, this.onCreate, @@ -39,7 +38,7 @@ class UpdateSpaceTemplateName extends CreateSpaceModelEvent { UpdateSpaceTemplateName({required this.name, required this.allModels}); @override - List get props => [name,allModels]; + List get props => [name, allModels]; } class AddSubspacesToSpaceTemplate extends CreateSpaceModelEvent { @@ -54,9 +53,19 @@ class AddTagsToSpaceTemplate extends CreateSpaceModelEvent { AddTagsToSpaceTemplate(this.tags); } - class ValidateSpaceTemplateName extends CreateSpaceModelEvent { final String name; ValidateSpaceTemplateName({required this.name}); } + +class ModifySpaceTemplate extends CreateSpaceModelEvent { + final SpaceTemplateModel spaceTemplate; + final SpaceTemplateModel updatedSpaceTemplate; + final Function(SpaceTemplateModel)? onUpdate; + + ModifySpaceTemplate( + {required this.spaceTemplate, + required this.updatedSpaceTemplate, + this.onUpdate}); +} diff --git a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart index e383610d..090dfa13 100644 --- a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart @@ -12,6 +12,7 @@ class SpaceModelBloc extends Bloc { required List initialSpaceModels, }) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) { on(_onCreateSpaceModel); + on(_onUpdateSpaceModel); } Future _onCreateSpaceModel( @@ -33,4 +34,23 @@ class SpaceModelBloc extends Bloc { } } } + + Future _onUpdateSpaceModel( + UpdateSpaceModel event, Emitter emit) async { + final currentState = state; + if (currentState is SpaceModelLoaded) { + try { + final newSpaceModel = + await api.getSpaceModel(event.spaceModelUuid ?? ''); + if (newSpaceModel != null) { + final updatedSpaceModels = currentState.spaceModels.map((model) { + return model.uuid == event.spaceModelUuid ? newSpaceModel : model; + }).toList(); + emit(SpaceModelLoaded(spaceModels: updatedSpaceModels)); + } + } catch (e) { + emit(SpaceModelError(message: e.toString())); + } + } + } } diff --git a/lib/pages/spaces_management/space_model/bloc/space_model_event.dart b/lib/pages/spaces_management/space_model/bloc/space_model_event.dart index 78331f3c..8f71e611 100644 --- a/lib/pages/spaces_management/space_model/bloc/space_model_event.dart +++ b/lib/pages/spaces_management/space_model/bloc/space_model_event.dart @@ -16,3 +16,21 @@ class CreateSpaceModel extends SpaceModelEvent { @override List get props => [newSpaceModel]; } + +class GetSpaceModel extends SpaceModelEvent { + final String spaceModelUuid; + + GetSpaceModel({required this.spaceModelUuid}); + + @override + List get props => [spaceModelUuid]; +} + +class UpdateSpaceModel extends SpaceModelEvent { + final String spaceModelUuid; + + UpdateSpaceModel({required this.spaceModelUuid}); + + @override + List get props => [spaceModelUuid]; +} diff --git a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart index e481a8b8..cb8d0aac 100644 --- a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart +++ b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart @@ -30,12 +30,12 @@ class CreateSubspaceTemplateModel { } class CreateSpaceTemplateBodyModel { - final String modelName; + final String? modelName; final List? tags; final List? subspaceModels; CreateSpaceTemplateBodyModel({ - required this.modelName, + this.modelName, this.tags, this.subspaceModels, }); diff --git a/lib/pages/spaces_management/space_model/models/space_template_model.dart b/lib/pages/spaces_management/space_model/models/space_template_model.dart index 84f568a5..5edf912f 100644 --- a/lib/pages/spaces_management/space_model/models/space_template_model.dart +++ b/lib/pages/spaces_management/space_model/models/space_template_model.dart @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart'; import 'package:uuid/uuid.dart'; @@ -70,14 +71,14 @@ class SpaceTemplateModel extends Equatable { } class UpdateSubspaceTemplateModel { - final String uuid; + final String? uuid; final Action action; final String? subspaceName; - final List? tags; + final List? tags; UpdateSubspaceTemplateModel({ required this.action, - required this.uuid, + this.uuid, this.subspaceName, this.tags, }); @@ -88,7 +89,7 @@ class UpdateSubspaceTemplateModel { uuid: json['uuid'] ?? '', subspaceName: json['subspaceName'] ?? '', tags: (json['tags'] as List) - .map((item) => UpdateTagModel.fromJson(item)) + .map((item) => TagModelUpdate.fromJson(item)) .toList(), ); } @@ -103,44 +104,6 @@ class UpdateSubspaceTemplateModel { } } -class UpdateTagModel { - final Action action; - final String? uuid; - final String tag; - final bool disabled; - final ProductModel? product; - - UpdateTagModel({ - required this.action, - this.uuid, - required this.tag, - required this.disabled, - this.product, - }); - - factory UpdateTagModel.fromJson(Map json) { - return UpdateTagModel( - 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(), - }; - } -} - extension SpaceTemplateExtensions on SpaceTemplateModel { List listAllTagValues() { final List tagValues = []; diff --git a/lib/pages/spaces_management/space_model/models/tag_update_model.dart b/lib/pages/spaces_management/space_model/models/tag_update_model.dart new file mode 100644 index 00000000..c7190dc8 --- /dev/null +++ b/lib/pages/spaces_management/space_model/models/tag_update_model.dart @@ -0,0 +1,34 @@ +import 'package:syncrow_web/utils/constants/action_enum.dart'; + +class TagModelUpdate { + final Action action; + final String? uuid; + final String? tag; + final String? productUuid; + + TagModelUpdate({ + required this.action, + this.uuid, + this.tag, + this.productUuid, + }); + + factory TagModelUpdate.fromJson(Map json) { + return TagModelUpdate( + action: json['action'], + uuid: json['uuid'], + tag: json['tag'], + productUuid: json['productUuid'], + ); + } + + // Method to convert an instance to JSON + Map toJson() { + return { + 'action': action.value, + 'uuid': uuid, // Nullable field + 'tag': tag, + 'productUuid': productUuid, + }; + } +} 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 33509998..ae623e81 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 @@ -65,7 +65,6 @@ class SpaceModelPage extends StatelessWidget { final model = spaceModels[index]; final otherModel = List.from(allSpaceModelNames); otherModel.remove(model.modelName); - return GestureDetector( onTap: () { showDialog( @@ -76,6 +75,7 @@ class SpaceModelPage extends StatelessWidget { allTags: allTagValues, spaceModel: model, otherSpaceModels: otherModel, + pageContext: context, ); }, ); 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 4c4b8a7c..c1bea0fd 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 @@ -6,6 +6,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_mod import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.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/tag_chips_display_widget.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart'; @@ -58,7 +59,9 @@ class CreateSpaceModelDialog extends StatelessWidget { } spaceNameController.addListener(() { - bloc.add(UpdateSpaceTemplateName(name: spaceNameController.text,allModels: otherSpaceModels ??[])); + bloc.add(UpdateSpaceTemplateName( + name: spaceNameController.text, + allModels: otherSpaceModels ?? [])); }); return bloc; @@ -153,15 +156,12 @@ class CreateSpaceModelDialog extends StatelessWidget { onPressed: state.errorMessage == null || isNameValid ? () { + final updatedSpaceTemplate = + updatedSpaceModel.copyWith( + modelName: + spaceNameController.text.trim(), + ); if (updatedSpaceModel.uuid == null) { - final updatedSpaceTemplate = - updatedSpaceModel.copyWith( - modelName: - spaceNameController.text.trim(), - ); - if (updatedSpaceTemplate.uuid != - null) {} - context .read() .add( @@ -181,6 +181,52 @@ class CreateSpaceModelDialog extends StatelessWidget { }, ), ); + } else { + if (pageContext != null) { + final currentState = pageContext! + .read() + .state; + if (currentState + is SpaceModelLoaded) { + final spaceModels = + List.from( + currentState.spaceModels); + + final SpaceTemplateModel? + currentSpaceModel = spaceModels + .cast() + .firstWhere( + (sm) => + sm?.uuid == + updatedSpaceModel + .uuid, + orElse: () => null, + ); + if (currentSpaceModel != null) { + context + .read() + .add(ModifySpaceTemplate( + spaceTemplate: + currentSpaceModel, + updatedSpaceTemplate: + updatedSpaceTemplate, + onUpdate: (newModel) { + if (pageContext != + null) { + pageContext! + .read< + SpaceModelBloc>() + .add(UpdateSpaceModel( + spaceModelUuid: + newModel.uuid ?? + '')); + } + Navigator.of(context) + .pop(); + })); + } + } + } } } : null, diff --git a/lib/pages/spaces_management/space_model/widgets/subspace_chip_widget.dart b/lib/pages/spaces_management/space_model/widgets/subspace_chip_widget.dart index 8f987c51..70ac6e24 100644 --- a/lib/pages/spaces_management/space_model/widgets/subspace_chip_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/subspace_chip_widget.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -import 'package:flutter/material.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; - class SubspaceChipWidget extends StatelessWidget { final String subspace; 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 7781bb5e..0dda53a6 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 @@ -46,23 +46,23 @@ class SubspaceModelCreate extends StatelessWidget { spacing: 8.0, runSpacing: 8.0, children: [ - ...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 - ), - ), - ), + ...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), + ), + )), GestureDetector( onTap: () async { await _openDialog(context, 'Edit Sub-space'); 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 0c46076f..d4111031 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 @@ -98,6 +98,7 @@ class TagChipDisplay extends StatelessWidget { subspaces: subspaces, pageContext: pageContext, allTags: allTags, + spaceModel: spaceModel, initialTags: TagHelper.generateInitialTags( subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []), @@ -139,6 +140,7 @@ class TagChipDisplay extends StatelessWidget { spaceName: spaceNameController.text, pageContext: pageContext, isCreate: true, + spaceModel: spaceModel, ), ); }, 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 ddb9b5a3..a9d40147 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 @@ -5,9 +5,10 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.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/link_space_model/bloc/link_space_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_state.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart'; @@ -24,6 +25,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { final bool isCreate; final List? otherSpaceModels; final BuildContext? pageContext; + final SpaceTemplateModel? spaceModel; const AddDeviceTypeModelWidget( {super.key, @@ -35,7 +37,8 @@ class AddDeviceTypeModelWidget extends StatelessWidget { required this.spaceName, required this.isCreate, this.pageContext, - this.otherSpaceModels}); + this.otherSpaceModels, + this.spaceModel}); @override Widget build(BuildContext context) { @@ -75,6 +78,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 20.0), child: ScrollableGridViewWidget( + isCreate: isCreate, products: products, crossAxisCount: crossAxisCount, initialProductCounts: state.selectedProducts, @@ -98,6 +102,44 @@ class AddDeviceTypeModelWidget extends StatelessWidget { onPressed: () async { if (isCreate) { Navigator.of(context).pop(); + await showDialog( + context: context, + builder: (BuildContext dialogContext) { + return CreateSpaceModelDialog( + products: products, + allTags: allTags, + pageContext: pageContext, + otherSpaceModels: otherSpaceModels, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: spaceModel?.tags ?? [], + uuid: spaceModel?.uuid, + internalId: spaceModel?.internalId, + subspaceModels: subspaces), + ); + }, + ); + } else { + final initialTags = generateInitialTags( + spaceTagModels: spaceTagModels, + subspaces: subspaces, + ); + + Navigator.of(context).pop(); + await showDialog( + context: context, + builder: (context) => AssignTagModelsDialog( + products: products, + subspaces: subspaces, + addedProducts: initialSelectedProducts ?? [], + allTags: allTags, + spaceName: spaceName, + initialTags: initialTags, + otherSpaceModels: otherSpaceModels, + title: 'Edit Device', + spaceModel: spaceModel, + pageContext: pageContext, + )); } }, ), @@ -140,6 +182,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { initialTags: state.initialTag, otherSpaceModels: otherSpaceModels, title: dialogTitle, + spaceModel: spaceModel, pageContext: pageContext, ), ); diff --git a/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart b/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart index c2d38d0b..7d103cdb 100644 --- a/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart +++ b/lib/pages/spaces_management/tag_model/widgets/device_type_tile_widget.dart @@ -13,12 +13,13 @@ import 'package:syncrow_web/utils/constants/assets.dart'; class DeviceTypeTileWidget extends StatelessWidget { final ProductModel product; final List productCounts; + final bool isCreate; - const DeviceTypeTileWidget({ - super.key, - required this.product, - required this.productCounts, - }); + const DeviceTypeTileWidget( + {super.key, + required this.product, + required this.productCounts, + required this.isCreate}); @override Widget build(BuildContext context) { @@ -48,6 +49,7 @@ class DeviceTypeTileWidget extends StatelessWidget { DeviceNameWidget(name: product.name), const SizedBox(height: 4), CounterWidget( + isCreate: isCreate, initialCount: selectedProduct.count, onCountChanged: (newCount) { context.read().add( diff --git a/lib/pages/spaces_management/tag_model/widgets/scrollable_grid_view_widget.dart b/lib/pages/spaces_management/tag_model/widgets/scrollable_grid_view_widget.dart index 3e32ccd8..d1775c66 100644 --- a/lib/pages/spaces_management/tag_model/widgets/scrollable_grid_view_widget.dart +++ b/lib/pages/spaces_management/tag_model/widgets/scrollable_grid_view_widget.dart @@ -10,12 +10,14 @@ class ScrollableGridViewWidget extends StatelessWidget { final List? products; final int crossAxisCount; final List? initialProductCounts; + final bool isCreate; const ScrollableGridViewWidget({ super.key, required this.products, required this.crossAxisCount, this.initialProductCounts, + required this.isCreate }); @override @@ -30,7 +32,7 @@ class ScrollableGridViewWidget extends StatelessWidget { final productCounts = state is AddDeviceModelLoaded ? state.selectedProducts : []; - + return GridView.builder( controller: scrollController, shrinkWrap: true, @@ -47,6 +49,7 @@ class ScrollableGridViewWidget extends StatelessWidget { return DeviceTypeTileWidget( product: product, + isCreate: isCreate, productCounts: initialProductCount != null ? [...productCounts, initialProductCount] : productCounts, diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index ee241189..eb896432 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -34,6 +34,20 @@ class SpaceModelManagementApi { return response; } + + Future updateSpaceModel( + CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid) async { + final response = await HTTPService().put( + path: ApiEndpoints.updateSpaceModel + .replaceAll('{projectId}', TempConst.projectId).replaceAll('{spaceModelUuid}', spaceModelUuid), + body: spaceModel.toJson(), + expectedResponseModel: (json) { + return json['message']; + }, + ); + return response; + } + Future getSpaceModel(String spaceModelUuid) async { final response = await HTTPService().get( path: ApiEndpoints.getSpaceModel diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 92a2581c..35020151 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -101,8 +101,11 @@ abstract class ApiEndpoints { //space model static const String listSpaceModels = '/projects/{projectId}/space-models'; static const String createSpaceModel = '/projects/{projectId}/space-models'; - static const String getSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}'; - + static const String getSpaceModel = + '/projects/{projectId}/space-models/{spaceModelUuid}'; + static const String updateSpaceModel = + '/projects/{projectId}/space-models/{spaceModelUuid}'; + static const String roleTypes = '/role/types'; static const String permission = '/permission/{roleUuid}'; static const String inviteUser = '/invite-user'; From ba7db3a5fb57496b8257ce1d568cd51c6a4c9030 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 22 Jan 2025 17:21:35 +0400 Subject: [PATCH 7/8] updated subspace edit flow --- .../bloc/space_management_bloc.dart | 5 +- .../widgets/community_structure_widget.dart | 21 +-- .../bloc/create_space_model_bloc.dart | 172 ++++++------------ .../create_space_template_body_model.dart | 5 + lib/services/space_model_mang_api.dart | 1 + 5 files changed, 70 insertions(+), 134 deletions(-) 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 26e444ca..7e9c6ce3 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 @@ -408,7 +408,9 @@ class SpaceManagementBloc Future> saveSpacesHierarchically( List spaces, String communityUuid) async { + print("space"); final orderedSpaces = flattenHierarchy(spaces); + print("parent"); final parentsToDelete = orderedSpaces.where((space) => space.status == SpaceStatus.deleted && @@ -420,9 +422,10 @@ class SpaceManagementBloc await _api.deleteSpace(communityUuid, parent.uuid!); } } catch (e) { - rethrow; // Decide whether to stop execution or continue + rethrow; } } + orderedSpaces.removeWhere((space) => parentsToDelete.contains(space)); for (var space in orderedSpaces) { try { 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 05a80780..dc0ad1cd 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 @@ -177,7 +177,7 @@ class _CommunityStructureAreaState extends State { painter: CurvedLinePainter([connection])), ), for (var entry in spaces.asMap().entries) - if (entry.value.status != SpaceStatus.deleted || + if (entry.value.status != SpaceStatus.deleted && entry.value.status != SpaceStatus.parentDeleted) Positioned( left: entry.value.position.dx, @@ -301,7 +301,6 @@ class _CommunityStructureAreaState extends State { List? tags) { setState(() { // Set the first space in the center or use passed position - Offset centerPosition = position ?? _getCenterPosition(screenSize); SpaceModel newSpace = SpaceModel( @@ -385,10 +384,10 @@ class _CommunityStructureAreaState extends State { void flatten(SpaceModel space) { if (space.status == SpaceStatus.deleted || - space.status == SpaceStatus.parentDeleted) return; - + space.status == SpaceStatus.parentDeleted) { + return; + } result.add(space); - for (var child in space.children) { flatten(child); } @@ -456,21 +455,17 @@ class _CommunityStructureAreaState extends State { } void _onDelete() { - if (widget.selectedCommunity != null && - widget.selectedCommunity?.uuid != null && - widget.selectedSpace == null) { - context.read().add(DeleteCommunityEvent( - communityUuid: widget.selectedCommunity!.uuid, - )); - } if (widget.selectedSpace != null) { setState(() { for (var space in spaces) { - if (space.uuid == widget.selectedSpace?.uuid) { + if (space.internalId == widget.selectedSpace?.internalId) { space.status = SpaceStatus.deleted; _markChildrenAsDeleted(space); } } + for (var space in spaces) { + print("space ${space.name} and ${space.status}"); + } _removeConnectionsForDeletedSpaces(); }); } 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 40db384a..d8b39216 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 @@ -3,6 +3,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_spac import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.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/space_template_model.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; @@ -204,142 +205,68 @@ class CreateSpaceModelBloc } List tagUpdates = []; final List subspaceUpdates = []; + final List? prevSubspaces = + prevSpaceModel.subspaceModels; + final List? newSubspaces = + newSpaceModel.subspaceModels; tagUpdates = processTagUpdates(prevSpaceModel.tags, newSpaceModel.tags); - if (prevSpaceModel.subspaceModels != null) { - for (var prevSubspace in prevSpaceModel.subspaceModels!) { - if (newSpaceModel.subspaceModels == null) { - subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.delete, - uuid: prevSubspace.uuid, - )); - continue; + if (prevSubspaces != null || newSubspaces != null) { + if (prevSubspaces != null && newSubspaces != null) { + for (var prevSubspace in prevSubspaces!) { + final existsInNew = newSubspaces! + .any((newTag) => newTag.uuid == prevSubspace.uuid); + if (!existsInNew) { + subspaceUpdates.add(UpdateSubspaceTemplateModel( + action: Action.delete, uuid: prevSubspace.uuid)); + } } - - final subspaceExistsInNew = newSpaceModel.subspaceModels! - .any((newSubspace) => newSubspace.uuid == prevSubspace.uuid); - if (!subspaceExistsInNew) { + } else if (prevSubspaces != null && newSubspaces == null) { + for (var prevSubspace in prevSubspaces) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.delete, - uuid: prevSubspace.uuid, - )); + action: Action.delete, uuid: prevSubspace.uuid)); } } - } - if (newSpaceModel.subspaceModels != null) { - for (var newSubspaceModel in newSpaceModel.subspaceModels!) { - if (newSubspaceModel.uuid == null || - newSubspaceModel.uuid!.isEmpty) { - final List tagUpdatesInSubspace = []; + if (newSubspaces != null) { + for (var newSubspace in newSubspaces!) { + // Tag without UUID + if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) { + final List tagUpdates = []; - if (newSubspaceModel.tags != null) { - for (var tag in newSubspaceModel.tags!) { - tagUpdatesInSubspace.add(TagModelUpdate( + if (newSubspace.tags != null) { + for (var tag in newSubspace.tags!) { + tagUpdates.add(TagModelUpdate( + action: Action.add, + tag: tag.tag, + productUuid: tag.product?.uuid)); + } + } + subspaceUpdates.add(UpdateSubspaceTemplateModel( action: Action.add, - uuid: tag.uuid, - tag: tag.tag, - productUuid: tag.product?.uuid, - )); - } + subspaceName: newSubspace.subspaceName, + tags: tagUpdates)); } - - subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.add, - subspaceName: newSubspaceModel.subspaceName, - tags: tagUpdatesInSubspace, - )); } } - } - if (newSpaceModel.subspaceModels != null && - prevSpaceModel.subspaceModels != null) { - final prevSubspaceMap = { - for (var subspace in prevSpaceModel.subspaceModels!) - subspace.uuid: subspace - }; + if (prevSubspaces != null && newSubspaces != null) { + final newSubspaceMap = { + for (var subspace in newSubspaces!) subspace.uuid: subspace + }; - for (var newSubspace in newSpaceModel.subspaceModels!) { - if (newSubspace.uuid != null && - prevSubspaceMap.containsKey(newSubspace.uuid)) { - final prevSubspace = prevSubspaceMap[newSubspace.uuid]!; - - // Check if modelName has changed - if (newSubspace.subspaceName != prevSubspace.subspaceName) { + for (var prevSubspace in prevSubspaces!) { + final newSubspace = newSubspaceMap[prevSubspace.uuid]; + if (newSubspace != null) { + final List tagSubspaceUpdates = + processTagUpdates(prevSubspace.tags, newSubspace.tags); subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.update, - uuid: newSubspace.uuid, - subspaceName: newSubspace.subspaceName, - )); - } - - // Compare tags within the subspace - final List tagUpdatesInSubspace = []; - if (prevSubspace.tags != null && newSubspace.tags != null) { - final prevTagMap = { - for (var tag in prevSubspace.tags!) tag.uuid: tag - }; - - // Check for deleted tags - for (var prevTag in prevSubspace.tags!) { - if (!newSubspace.tags! - .any((newTag) => newTag.uuid == prevTag.uuid)) { - tagUpdatesInSubspace.add(TagModelUpdate( - action: Action.delete, - uuid: prevTag.uuid, - )); - } - } - - // Check for added tags, including those without a UUID - for (var newTag in newSubspace.tags!) { - if (newTag.uuid == null || newTag.uuid!.isEmpty) { - // Add new tag without UUID - tagUpdatesInSubspace.add(TagModelUpdate( - action: Action.add, - uuid: null, // or generate a new UUID if required - tag: newTag.tag, - productUuid: newTag.product?.uuid, - )); - } else if (!prevSubspace.tags! - .any((prevTag) => prevTag.uuid == newTag.uuid)) { - // Add new tag with UUID - tagUpdatesInSubspace.add(TagModelUpdate( - action: Action.add, - uuid: newTag.uuid, - tag: newTag.tag, - productUuid: newTag.product?.uuid, - )); - } - } - - // Check for updated tags - for (var prevTag in prevSubspace.tags!) { - final newTag = newSubspace.tags!.cast().firstWhere( - (tag) => tag?.uuid == prevTag.uuid, - orElse: () => null, - ); - if (newTag != null && newTag.tag != prevTag.tag) { - tagUpdatesInSubspace.add(TagModelUpdate( - action: Action.update, - uuid: newTag.uuid, - tag: newTag.tag, - )); - } - } - } - - // Add the subspace with updated tags if necessary - if (tagUpdatesInSubspace.isNotEmpty) { - subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: Action.update, - uuid: newSubspace.uuid, - subspaceName: newSubspace.subspaceName, - tags: tagUpdatesInSubspace, - )); - } + action: Action.update, + uuid: newSubspace.uuid, + subspaceName: newSubspace.subspaceName, + tags: tagSubspaceUpdates)); + } else {} } } } @@ -382,6 +309,11 @@ class CreateSpaceModelBloc .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); } } + } else if (prevTags != null && newTags == null) { + for (var prevTag in prevTags) { + tagUpdates + .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); + } } // Case 2: Tags added diff --git a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart index cb8d0aac..9b61f1b0 100644 --- a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart +++ b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart @@ -47,4 +47,9 @@ class CreateSpaceTemplateBodyModel { 'subspaceModels': subspaceModels, }; } + + @override + String toString() { + return toJson().toString(); + } } diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index eb896432..625397c7 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -37,6 +37,7 @@ class SpaceModelManagementApi { Future updateSpaceModel( CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid) async { + print(spaceModel.toJson().toString()); final response = await HTTPService().put( path: ApiEndpoints.updateSpaceModel .replaceAll('{projectId}', TempConst.projectId).replaceAll('{spaceModelUuid}', spaceModelUuid), From 830725254f62d8ad850d17e99b331eb44c64ec68 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 22 Jan 2025 17:22:58 +0400 Subject: [PATCH 8/8] removed logs --- .../all_spaces/bloc/space_management_bloc.dart | 2 -- .../all_spaces/widgets/community_structure_widget.dart | 4 +--- lib/services/space_model_mang_api.dart | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) 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 7e9c6ce3..ff584f52 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 @@ -408,9 +408,7 @@ class SpaceManagementBloc Future> saveSpacesHierarchically( List spaces, String communityUuid) async { - print("space"); final orderedSpaces = flattenHierarchy(spaces); - print("parent"); final parentsToDelete = orderedSpaces.where((space) => space.status == SpaceStatus.deleted && 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 dc0ad1cd..f569d252 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 @@ -463,9 +463,7 @@ class _CommunityStructureAreaState extends State { _markChildrenAsDeleted(space); } } - for (var space in spaces) { - print("space ${space.name} and ${space.status}"); - } + _removeConnectionsForDeletedSpaces(); }); } diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index 625397c7..eb896432 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -37,7 +37,6 @@ class SpaceModelManagementApi { Future updateSpaceModel( CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid) async { - print(spaceModel.toJson().toString()); final response = await HTTPService().put( path: ApiEndpoints.updateSpaceModel .replaceAll('{projectId}', TempConst.projectId).replaceAll('{spaceModelUuid}', spaceModelUuid),