mirror of
https://github.com/SyncrowIOT/web.git
synced 2026-10-23 09:47:55 +00:00
added duplicate
This commit is contained in:
@@ -1,38 +1,74 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
|
||||
|
||||
class AddDeviceTypeBloc
|
||||
extends Bloc<AddDeviceTypeEvent, List<SelectedProduct>> {
|
||||
AddDeviceTypeBloc(List<SelectedProduct> initialProducts)
|
||||
: super(initialProducts) {
|
||||
class AddDeviceTypeBloc extends Bloc<AddDeviceTypeEvent, AddDeviceState> {
|
||||
AddDeviceTypeBloc() : super(AddDeviceInitial()) {
|
||||
on<InitializeDevice>(_onInitializeTagModels);
|
||||
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
||||
}
|
||||
|
||||
void _onUpdateProductCount(
|
||||
UpdateProductCountEvent event, Emitter<List<SelectedProduct>> emit) {
|
||||
final existingProduct = state.firstWhere(
|
||||
(p) => p.productId == event.productId,
|
||||
orElse: () => SelectedProduct(productId: event.productId, count: 0,productName: event.productName,product: event.product ),
|
||||
);
|
||||
void _onInitializeTagModels(
|
||||
InitializeDevice event, Emitter<AddDeviceState> emit) {
|
||||
emit(AddDeviceLoaded(
|
||||
selectedProducts: event.addedProducts,
|
||||
initialTag: event.initialTags,
|
||||
));
|
||||
}
|
||||
|
||||
if (event.count > 0) {
|
||||
if (!state.contains(existingProduct)) {
|
||||
emit([
|
||||
...state,
|
||||
SelectedProduct(productId: event.productId, count: event.count, productName: event.productName, product: event.product)
|
||||
]);
|
||||
void _onUpdateProductCount(
|
||||
UpdateProductCountEvent event, Emitter<AddDeviceState> emit) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AddDeviceLoaded) {
|
||||
final existingProduct = currentState.selectedProducts.firstWhere(
|
||||
(p) => p.productId == event.productId,
|
||||
orElse: () => SelectedProduct(
|
||||
productId: event.productId,
|
||||
count: 0,
|
||||
productName: event.productName,
|
||||
product: event.product,
|
||||
),
|
||||
);
|
||||
|
||||
List<SelectedProduct> updatedProducts;
|
||||
|
||||
if (event.count > 0) {
|
||||
if (!currentState.selectedProducts.contains(existingProduct)) {
|
||||
updatedProducts = [
|
||||
...currentState.selectedProducts,
|
||||
SelectedProduct(
|
||||
productId: event.productId,
|
||||
count: event.count,
|
||||
productName: event.productName,
|
||||
product: event.product,
|
||||
),
|
||||
];
|
||||
} else {
|
||||
updatedProducts = currentState.selectedProducts.map((p) {
|
||||
if (p.productId == event.productId) {
|
||||
return SelectedProduct(
|
||||
productId: p.productId,
|
||||
count: event.count,
|
||||
productName: p.productName,
|
||||
product: p.product,
|
||||
);
|
||||
}
|
||||
return p;
|
||||
}).toList();
|
||||
}
|
||||
} else {
|
||||
final updatedList = state.map((p) {
|
||||
if (p.productId == event.productId) {
|
||||
return SelectedProduct(productId: p.productId, count: event.count, productName: p.productName,product: p.product);
|
||||
}
|
||||
return p;
|
||||
}).toList();
|
||||
emit(updatedList);
|
||||
// Remove the product if the count is 0
|
||||
updatedProducts = currentState.selectedProducts
|
||||
.where((p) => p.productId != event.productId)
|
||||
.toList();
|
||||
}
|
||||
} else {
|
||||
emit(state.where((p) => p.productId != event.productId).toList());
|
||||
|
||||
// Emit the updated state
|
||||
emit(AddDeviceLoaded(
|
||||
selectedProducts: updatedProducts,
|
||||
initialTag: currentState.initialTag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import 'package:equatable/equatable.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/tag.dart';
|
||||
|
||||
abstract class AddDeviceState extends Equatable {
|
||||
const AddDeviceState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class AddDeviceInitial extends AddDeviceState {}
|
||||
|
||||
class AddDeviceLoading extends AddDeviceState {}
|
||||
|
||||
class AddDeviceLoaded extends AddDeviceState {
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final List<Tag> initialTag;
|
||||
|
||||
const AddDeviceLoaded({
|
||||
required this.selectedProducts,
|
||||
required this.initialTag,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedProducts, initialTag];
|
||||
}
|
||||
|
||||
class AddDeviceError extends AddDeviceState {
|
||||
final String errorMessage;
|
||||
|
||||
const AddDeviceError(this.errorMessage);
|
||||
|
||||
@override
|
||||
List<Object> get props => [errorMessage];
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
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/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
|
||||
abstract class AddDeviceTypeEvent extends Equatable {
|
||||
const AddDeviceTypeEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
@@ -12,8 +16,26 @@ class UpdateProductCountEvent extends AddDeviceTypeEvent {
|
||||
final String productName;
|
||||
final ProductModel product;
|
||||
|
||||
UpdateProductCountEvent({required this.productId, required this.count, required this.productName, required this.product});
|
||||
UpdateProductCountEvent(
|
||||
{required this.productId,
|
||||
required this.count,
|
||||
required this.productName,
|
||||
required this.product});
|
||||
|
||||
@override
|
||||
List<Object> get props => [productId, count];
|
||||
}
|
||||
|
||||
|
||||
class InitializeDevice extends AddDeviceTypeEvent {
|
||||
final List<Tag> initialTags;
|
||||
final List<SelectedProduct> addedProducts;
|
||||
|
||||
const InitializeDevice({
|
||||
this.initialTags = const [],
|
||||
required this.addedProducts,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props => [initialTags, addedProducts];
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
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/add_device_type/bloc/add_device_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_type_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/scrollable_grid_view_widget.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/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/assign_tag/views/assign_tag_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/tag_model/widgets/action_button_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
|
||||
class AddDeviceTypeWidget extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
||||
@@ -20,8 +23,7 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
final List<Tag>? spaceTags;
|
||||
final List<String>? allTags;
|
||||
final String spaceName;
|
||||
final Function(List<Tag>,List<SubspaceModel>?)? onSave;
|
||||
|
||||
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
||||
|
||||
const AddDeviceTypeWidget(
|
||||
{super.key,
|
||||
@@ -44,30 +46,45 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
: 3;
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AddDeviceTypeBloc(initialSelectedProducts ?? []),
|
||||
create: (_) => AddDeviceTypeBloc()
|
||||
..add(InitializeDevice(
|
||||
initialTags: spaceTags ?? [],
|
||||
addedProducts: initialSelectedProducts ?? [],
|
||||
)),
|
||||
child: Builder(
|
||||
builder: (context) => AlertDialog(
|
||||
title: const Text('Add Devices'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SingleChildScrollView(
|
||||
child: Container(
|
||||
width: size.width * 0.9,
|
||||
height: size.height * 0.65,
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: ScrollableGridViewWidget(
|
||||
products: products, crossAxisCount: crossAxisCount),
|
||||
),
|
||||
content: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||
builder: (context, state) {
|
||||
if (state is AddDeviceLoading) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
if (state is AddDeviceLoaded) {
|
||||
return SingleChildScrollView(
|
||||
child: Container(
|
||||
width: size.width * 0.9,
|
||||
height: size.height * 0.65,
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: ScrollableGridViewWidget(
|
||||
products: products,
|
||||
crossAxisCount: crossAxisCount),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
@@ -78,43 +95,53 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
ActionButton(
|
||||
label: 'Continue',
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
onPressed: () async {
|
||||
final currentState =
|
||||
context.read<AddDeviceTypeBloc>().state;
|
||||
Navigator.of(context).pop();
|
||||
SizedBox(
|
||||
width: 140,
|
||||
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||
builder: (context, state) {
|
||||
final isDisabled = state is AddDeviceLoaded &&
|
||||
state.selectedProducts.isEmpty;
|
||||
return DefaultButton(
|
||||
borderRadius: 10,
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
foregroundColor: isDisabled
|
||||
? ColorsManager.whiteColorsWithOpacity
|
||||
: ColorsManager.whiteColors,
|
||||
onPressed: () async {
|
||||
if (state is AddDeviceLoaded &&
|
||||
state.selectedProducts.isNotEmpty) {
|
||||
final initialTags =
|
||||
TagHelper.generateInitialForTags(
|
||||
spaceTags: spaceTags,
|
||||
subspaces: subspaces,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
|
||||
if (currentState.isNotEmpty) {
|
||||
final initialTags = generateInitialTags(
|
||||
spaceTags: spaceTags,
|
||||
subspaces: subspaces,
|
||||
);
|
||||
|
||||
final dialogTitle = initialTags.isNotEmpty
|
||||
? 'Edit Device'
|
||||
: 'Assign Tags';
|
||||
await showDialog<bool>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (context) => AssignTagDialog(
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
addedProducts: currentState,
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
initialTags: initialTags,
|
||||
title: dialogTitle,
|
||||
onSave: (tags,subspaces){
|
||||
onSave!(tags,subspaces);
|
||||
final dialogTitle = initialTags.isNotEmpty
|
||||
? 'Edit Device'
|
||||
: 'Assign Tags';
|
||||
await showDialog<bool>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (context) => AssignTagDialog(
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
addedProducts: state.selectedProducts,
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
initialTags: initialTags,
|
||||
title: dialogTitle,
|
||||
onSave: (tags, subspaces) {
|
||||
onSave!(tags, subspaces);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
child: const Text('Next'),
|
||||
);
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
+6
-2
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/bloc/add_device_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/widgets/device_type_tile_widget.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';
|
||||
@@ -24,8 +25,11 @@ class ScrollableGridViewWidget extends StatelessWidget {
|
||||
return Scrollbar(
|
||||
controller: scrollController,
|
||||
thumbVisibility: true,
|
||||
child: BlocBuilder<AddDeviceTypeBloc, List<SelectedProduct>>(
|
||||
builder: (context, productCounts) {
|
||||
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||
builder: (context, state) {
|
||||
final productCounts = state is AddDeviceLoaded
|
||||
? state.selectedProducts
|
||||
: <SelectedProduct>[];
|
||||
return GridView.builder(
|
||||
controller: scrollController,
|
||||
shrinkWrap: true,
|
||||
|
||||
+20
-11
@@ -5,19 +5,23 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
||||
const CommunityStructureHeaderActionButtons({
|
||||
super.key,
|
||||
required this.theme,
|
||||
required this.isSave,
|
||||
required this.onSave,
|
||||
required this.onDelete,
|
||||
required this.selectedSpace,
|
||||
});
|
||||
const CommunityStructureHeaderActionButtons(
|
||||
{super.key,
|
||||
required this.theme,
|
||||
required this.isSave,
|
||||
required this.onSave,
|
||||
required this.onDelete,
|
||||
required this.selectedSpace,
|
||||
required this.onDuplicate,
|
||||
required this.onEdit});
|
||||
|
||||
final ThemeData theme;
|
||||
final bool isSave;
|
||||
final VoidCallback onSave;
|
||||
final VoidCallback onDelete;
|
||||
final VoidCallback onDuplicate;
|
||||
final VoidCallback onEdit;
|
||||
|
||||
final SpaceModel? selectedSpace;
|
||||
|
||||
@override
|
||||
@@ -42,13 +46,18 @@ class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
||||
CommunityStructureHeaderButton(
|
||||
label: "Edit",
|
||||
svgAsset: Assets.editSpace,
|
||||
onPressed: () => {},
|
||||
onPressed: onEdit,
|
||||
theme: theme,
|
||||
),
|
||||
CommunityStructureHeaderButton(
|
||||
label: "Duplicate",
|
||||
svgAsset: Assets.duplicate,
|
||||
onPressed: onDuplicate,
|
||||
theme: theme,
|
||||
),
|
||||
CommunityStructureHeaderButton(
|
||||
label: "Delete",
|
||||
icon: const Icon(Icons.delete,
|
||||
size: 18, color: ColorsManager.warningRed),
|
||||
svgAsset: Assets.spaceDelete,
|
||||
onPressed: onDelete,
|
||||
theme: theme,
|
||||
),
|
||||
|
||||
+4
-4
@@ -24,12 +24,12 @@ class CommunityStructureHeaderButton extends StatelessWidget {
|
||||
const double buttonHeight = 40;
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 100,
|
||||
maxWidth: 130,
|
||||
minHeight: buttonHeight,
|
||||
),
|
||||
child: DefaultButton(
|
||||
onPressed: onPressed,
|
||||
borderWidth: 3,
|
||||
borderWidth: 2,
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: ColorsManager.blackColor,
|
||||
borderRadius: 12.0,
|
||||
@@ -44,8 +44,8 @@ class CommunityStructureHeaderButton extends StatelessWidget {
|
||||
if (svgAsset != null)
|
||||
SvgPicture.asset(
|
||||
svgAsset!,
|
||||
width: 30,
|
||||
height: 30,
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Flexible(
|
||||
|
||||
+8
-1
@@ -14,6 +14,9 @@ class CommunityStructureHeader extends StatefulWidget {
|
||||
final TextEditingController nameController;
|
||||
final VoidCallback onSave;
|
||||
final VoidCallback onDelete;
|
||||
final VoidCallback onEdit;
|
||||
final VoidCallback onDuplicate;
|
||||
|
||||
final VoidCallback onEditName;
|
||||
final ValueChanged<String> onNameSubmitted;
|
||||
final List<CommunityModel> communities;
|
||||
@@ -32,7 +35,9 @@ class CommunityStructureHeader extends StatefulWidget {
|
||||
required this.onNameSubmitted,
|
||||
this.community,
|
||||
required this.communities,
|
||||
this.selectedSpace});
|
||||
this.selectedSpace,
|
||||
required this.onDuplicate,
|
||||
required this.onEdit});
|
||||
|
||||
@override
|
||||
State<CommunityStructureHeader> createState() =>
|
||||
@@ -146,6 +151,8 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
||||
isSave: widget.isSave,
|
||||
onSave: widget.onSave,
|
||||
onDelete: widget.onDelete,
|
||||
onDuplicate: widget.onDuplicate,
|
||||
onEdit: widget.onEdit,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
),
|
||||
],
|
||||
|
||||
@@ -4,6 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
// Syncrow project imports
|
||||
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
@@ -17,6 +19,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/blank_com
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/duplicate_process_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
@@ -133,6 +136,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
onSave: _saveSpaces,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
onDelete: _onDelete,
|
||||
onDuplicate: () => {_onDuplicate(context)},
|
||||
onEdit: () => {},
|
||||
onEditName: () {
|
||||
setState(() {
|
||||
isEditingName = !isEditingName;
|
||||
@@ -328,7 +333,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
parentSpace.addOutgoingConnection(newConnection);
|
||||
parentSpace.children.add(newSpace);
|
||||
}
|
||||
|
||||
spaces.add(newSpace);
|
||||
_updateNodePosition(newSpace, newSpace.position);
|
||||
});
|
||||
@@ -546,4 +550,175 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
space.status == SpaceStatus.modified ||
|
||||
space.status == SpaceStatus.deleted);
|
||||
}
|
||||
|
||||
void _onDuplicate(BuildContext parentContext) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
if (widget.selectedSpace != null) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Text(
|
||||
"Duplicate Space",
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineLarge
|
||||
?.copyWith(color: ColorsManager.blackColor),
|
||||
),
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text("Are you sure you want to duplicate?",
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.headlineSmall),
|
||||
const SizedBox(height: 15),
|
||||
Text("All the child spaces will be duplicated.",
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: ColorsManager.lightGrayColor)),
|
||||
const SizedBox(width: 15),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: CancelButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
label: "Cancel",
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
SizedBox(
|
||||
width: 200,
|
||||
child: DefaultButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return DuplicateProcessDialog(
|
||||
onDuplicate: () {
|
||||
_duplicateSpace(widget.selectedSpace!);
|
||||
_deselectSpace(parentContext);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
borderRadius: 10,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
),
|
||||
])
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _duplicateSpace(SpaceModel space) {
|
||||
final Map<SpaceModel, SpaceModel> originalToDuplicate = {};
|
||||
const double horizontalGap = 150.0;
|
||||
const double verticalGap = 100.0;
|
||||
|
||||
SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition) {
|
||||
// Find a new position for the duplicated space
|
||||
Offset newPosition = parentPosition + Offset(horizontalGap, 0);
|
||||
|
||||
// Avoid overlapping with existing spaces
|
||||
while (spaces.any((s) =>
|
||||
(s.position - newPosition).distance < horizontalGap &&
|
||||
s.status != SpaceStatus.deleted)) {
|
||||
newPosition += Offset(horizontalGap, 0);
|
||||
}
|
||||
|
||||
// Create the duplicated space
|
||||
final duplicated = SpaceModel(
|
||||
name: "${original.name} (Copy)",
|
||||
icon: original.icon,
|
||||
position: newPosition,
|
||||
isPrivate: original.isPrivate,
|
||||
children: [],
|
||||
status: SpaceStatus.newSpace,
|
||||
parent: original.parent,
|
||||
spaceModel: original.spaceModel,
|
||||
subspaces: original.subspaces,
|
||||
tags: original.tags,
|
||||
);
|
||||
|
||||
originalToDuplicate[original] = duplicated;
|
||||
|
||||
// Copy the children of the original space to the duplicated space
|
||||
Offset childStartPosition = newPosition + Offset(0, verticalGap);
|
||||
for (final child in original.children) {
|
||||
final duplicatedChild = duplicateRecursive(child, childStartPosition);
|
||||
duplicated.children.add(duplicatedChild);
|
||||
duplicatedChild.parent =
|
||||
duplicated; // Set the parent for the duplicated child
|
||||
childStartPosition += Offset(0, verticalGap);
|
||||
}
|
||||
|
||||
return duplicated;
|
||||
}
|
||||
|
||||
// Duplicate the selected space and its children
|
||||
final duplicatedSpace = duplicateRecursive(space, space.position);
|
||||
|
||||
// Ensure the duplicated space has the same parent as the original
|
||||
if (space.parent != null) {
|
||||
final parentSpace = space.parent!;
|
||||
final duplicatedParent = originalToDuplicate[parentSpace] ?? parentSpace;
|
||||
duplicatedSpace.parent = duplicatedParent;
|
||||
duplicatedParent.children.add(duplicatedSpace);
|
||||
}
|
||||
|
||||
// Flatten the hierarchy of the duplicated spaces
|
||||
List<SpaceModel> flattenHierarchy(SpaceModel root) {
|
||||
final List<SpaceModel> result = [];
|
||||
void traverse(SpaceModel node) {
|
||||
result.add(node);
|
||||
for (final child in node.children) {
|
||||
traverse(child);
|
||||
}
|
||||
}
|
||||
|
||||
traverse(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
final duplicatedSpacesList = flattenHierarchy(duplicatedSpace);
|
||||
|
||||
setState(() {
|
||||
spaces.addAll(duplicatedSpacesList);
|
||||
|
||||
// Duplicate the connections
|
||||
for (final connection in connections) {
|
||||
if (originalToDuplicate.containsKey(connection.startSpace) &&
|
||||
originalToDuplicate.containsKey(connection.endSpace)) {
|
||||
connections.add(
|
||||
Connection(
|
||||
startSpace: originalToDuplicate[connection.startSpace]!,
|
||||
endSpace: originalToDuplicate[connection.endSpace]!,
|
||||
direction: connection.direction,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model
|
||||
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/dialogs/icon_selection_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
||||
@@ -409,7 +410,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
_showTagCreateDialog(
|
||||
context,
|
||||
enteredName,
|
||||
widget.isEdit,
|
||||
widget.products,
|
||||
subspaces,
|
||||
);
|
||||
// Edit action
|
||||
})
|
||||
@@ -420,7 +423,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
: TextButton(
|
||||
onPressed: () {
|
||||
_showTagCreateDialog(
|
||||
context, enteredName, widget.products);
|
||||
context,
|
||||
enteredName,
|
||||
widget.isEdit,
|
||||
widget.products,
|
||||
subspaces,
|
||||
);
|
||||
},
|
||||
style: TextButton.styleFrom(
|
||||
padding: EdgeInsets.zero,
|
||||
@@ -558,85 +566,57 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(
|
||||
BuildContext context, String name, List<ProductModel>? products) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddDeviceTypeWidget(
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
spaceTags: tags,
|
||||
allTags: [],
|
||||
initialSelectedProducts:
|
||||
createInitialSelectedProducts(tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
selectedSpaceModel = null;
|
||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||
List<ProductModel>? products, List<SubspaceModel>? subspaces) {
|
||||
isEdit
|
||||
? showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AssignTagDialog(
|
||||
title: 'Edit Device',
|
||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
allTags: [],
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {},
|
||||
);
|
||||
},
|
||||
)
|
||||
: showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddDeviceTypeWidget(
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
spaceTags: tags,
|
||||
allTags: [],
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
selectedSpaceModel = null;
|
||||
|
||||
if (selectedSubspaces != null) {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
if (selectedSubspaces != null) {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<SelectedProduct> createInitialSelectedProducts(
|
||||
List<Tag>? tags, List<SubspaceModel>? subspaces) {
|
||||
final Map<ProductModel, int> productCounts = {};
|
||||
|
||||
if (tags != null) {
|
||||
for (var tag in tags) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
if (subspace.tags != null) {
|
||||
for (var tag in subspace.tags!) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] =
|
||||
(productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return productCounts.entries
|
||||
.map((entry) => SelectedProduct(
|
||||
productId: entry.key.uuid,
|
||||
count: entry.value,
|
||||
productName: entry.key.name ?? 'Unnamed',
|
||||
product: entry.key,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Map<ProductModel, int> _groupTags(List<Tag> tags) {
|
||||
final Map<ProductModel, int> groupedTags = {};
|
||||
for (var tag in tags) {
|
||||
if (tag.product != null) {
|
||||
groupedTags[tag.product!] = (groupedTags[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
return groupedTags;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class DuplicateProcessDialog extends StatefulWidget {
|
||||
final VoidCallback onDuplicate;
|
||||
|
||||
const DuplicateProcessDialog({required this.onDuplicate, Key? key})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_DuplicateProcessDialogState createState() => _DuplicateProcessDialogState();
|
||||
}
|
||||
|
||||
class _DuplicateProcessDialogState extends State<DuplicateProcessDialog> {
|
||||
bool isDuplicating = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_startDuplication();
|
||||
}
|
||||
|
||||
void _startDuplication() async {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
widget.onDuplicate();
|
||||
});
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
setState(() {
|
||||
isDuplicating = false;
|
||||
});
|
||||
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return AlertDialog(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isDuplicating) ...[
|
||||
const CircularProgressIndicator(
|
||||
color: ColorsManager.vividBlue,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
"Duplicating in progress",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(color: ColorsManager.primaryColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
] else ...[
|
||||
const Icon(
|
||||
Icons.check_circle,
|
||||
color: ColorsManager.vividBlue,
|
||||
size: 50,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
"Duplicating successful",
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineSmall
|
||||
?.copyWith(color: ColorsManager.primaryColor),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,9 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
}
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: allTags,
|
||||
isSaveEnabled: _validateTags(allTags),
|
||||
));
|
||||
tags: allTags,
|
||||
isSaveEnabled: _validateTags(allTags),
|
||||
errorMessage: ''));
|
||||
});
|
||||
|
||||
on<UpdateTagEvent>((event, emit) {
|
||||
@@ -117,12 +117,10 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
}
|
||||
|
||||
bool _validateTags(List<Tag> tags) {
|
||||
if (tags.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
||||
return uniqueTags.length == tags.length && !hasEmptyTag;
|
||||
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
String? _getValidationError(List<Tag> tags) {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
||||
import 'package:syncrow_web/common/dialog_textfield_dropdown.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
||||
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/subspace_model.dart';
|
||||
@@ -79,6 +82,7 @@ class AssignTagDialog extends StatelessWidget {
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
numeric: false,
|
||||
label: Text('Tag',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
@@ -109,10 +113,11 @@ class AssignTagDialog extends StatelessWidget {
|
||||
: 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(Text((index + 1).toString())),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
@@ -123,147 +128,80 @@ class AssignTagDialog extends StatelessWidget {
|
||||
tag.product?.name ?? 'Unknown',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close,
|
||||
color: ColorsManager.warningRed,
|
||||
size: 16),
|
||||
onPressed: () {
|
||||
context.read<AssignTagBloc>().add(
|
||||
DeleteTag(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
controller: controller,
|
||||
onChanged: (value) {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: value.trim(),
|
||||
));
|
||||
},
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Enter Tag',
|
||||
border: InputBorder.none,
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.blackColor,
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: MediaQuery.of(context)
|
||||
.size
|
||||
.width *
|
||||
0.15,
|
||||
child: PopupMenuButton<String>(
|
||||
color: ColorsManager.whiteColors,
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.arrow_drop_down,
|
||||
color:
|
||||
ColorsManager.blackColor),
|
||||
onSelected: (value) {
|
||||
controller.text = value;
|
||||
Icons.close,
|
||||
color: ColorsManager
|
||||
.lightGreyColor,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: value,
|
||||
));
|
||||
},
|
||||
itemBuilder: (context) {
|
||||
return (allTags ?? [])
|
||||
.where((tagValue) => !state
|
||||
.tags
|
||||
.map((e) => e.tag)
|
||||
.contains(tagValue))
|
||||
.map((tagValue) {
|
||||
return PopupMenuItem<String>(
|
||||
textStyle: const TextStyle(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor),
|
||||
value: tagValue,
|
||||
child: ConstrainedBox(
|
||||
constraints:
|
||||
BoxConstraints(
|
||||
minWidth: MediaQuery.of(
|
||||
context)
|
||||
.size
|
||||
.width *
|
||||
0.15,
|
||||
maxWidth: MediaQuery.of(
|
||||
context)
|
||||
.size
|
||||
.width *
|
||||
0.15,
|
||||
),
|
||||
child: Text(
|
||||
tagValue,
|
||||
overflow: TextOverflow
|
||||
.ellipsis,
|
||||
),
|
||||
));
|
||||
}).toList();
|
||||
.add(DeleteTag(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints:
|
||||
const BoxConstraints(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: tag.location ?? 'Main',
|
||||
dropdownColor: ColorsManager
|
||||
.whiteColors, // Dropdown background
|
||||
style: const TextStyle(
|
||||
color: Colors
|
||||
.black), // Style for selected text
|
||||
items: [
|
||||
const DropdownMenuItem<String>(
|
||||
value: 'Main Space',
|
||||
child: Text(
|
||||
'Main Space',
|
||||
style: TextStyle(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor),
|
||||
),
|
||||
),
|
||||
...locations.map((location) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: location,
|
||||
child: Text(
|
||||
location,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager
|
||||
.textPrimaryColor),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
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<AssignTagBloc>()
|
||||
.add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: value.trim(),
|
||||
));
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
DataCell(
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: DialogDropdown(
|
||||
items: locations,
|
||||
selectedValue:
|
||||
tag.location ?? 'Main Space',
|
||||
onSelected: (value) {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(UpdateLocation(
|
||||
index: index,
|
||||
location: value,
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
)),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -284,11 +222,33 @@ class AssignTagDialog extends StatelessWidget {
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: CancelButton(
|
||||
label: 'Cancel',
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Builder(
|
||||
builder: (buttonContext) => CancelButton(
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result = processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = result['subspaces'];
|
||||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
await showDialog<bool>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (dialogContext) => AddDeviceTypeWidget(
|
||||
products: products,
|
||||
subspaces: processedSubspaces,
|
||||
initialSelectedProducts: addedProducts,
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
spaceTags: processedTags,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
@@ -302,22 +262,16 @@ class AssignTagDialog extends StatelessWidget {
|
||||
onPressed: state.isSaveEnabled
|
||||
? () async {
|
||||
Navigator.of(context).pop();
|
||||
final assignedTags = <Tag>{};
|
||||
for (var tag in state.tags) {
|
||||
if (tag.location == null ||
|
||||
subspaces == null) {
|
||||
continue;
|
||||
}
|
||||
for (var subspace in subspaces!) {
|
||||
if (tag.location == subspace.subspaceName) {
|
||||
subspace.tags ??= [];
|
||||
subspace.tags!.add(tag);
|
||||
assignedTags.add(tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
onSave!(state.tags,subspaces);
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result =
|
||||
processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
result['subspaces'] as List<SubspaceModel>;
|
||||
|
||||
onSave!(processedTags, processedSubspaces);
|
||||
}
|
||||
: null,
|
||||
child: const Text('Save'),
|
||||
@@ -337,4 +291,110 @@ class AssignTagDialog extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<String> getAvailableTags(
|
||||
List<String> allTags, List<Tag> currentTags, Tag currentTag) {
|
||||
return allTags
|
||||
.where((tagValue) => !currentTags
|
||||
.where((e) => e != currentTag) // Exclude the current row
|
||||
.map((e) => e.tag)
|
||||
.contains(tagValue))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Map<String, dynamic> processTags(
|
||||
List<Tag> updatedTags, List<SubspaceModel>? subspaces) {
|
||||
final modifiedTags = List<Tag>.from(updatedTags);
|
||||
final modifiedSubspaces = List<SubspaceModel>.from(subspaces ?? []);
|
||||
|
||||
for (var tag in modifiedTags.toList()) {
|
||||
if (modifiedSubspaces.isEmpty) continue;
|
||||
|
||||
final prevIndice = checkTagExistInSubspace(tag, modifiedSubspaces);
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
(prevIndice == null ||
|
||||
modifiedSubspaces[prevIndice].subspaceName == 'Main Space')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
prevIndice == null) {
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location != modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
final newIndex = modifiedSubspaces
|
||||
.indexWhere((subspace) => subspace.subspaceName == tag.location);
|
||||
if (newIndex != -1) {
|
||||
if (modifiedSubspaces[newIndex]
|
||||
.tags
|
||||
?.any((t) => t.internalId == tag.internalId) !=
|
||||
true) {
|
||||
tag.location = modifiedSubspaces[newIndex].subspaceName;
|
||||
modifiedSubspaces[newIndex].tags?.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location != 'Main Space' && tag.location != null) &&
|
||||
tag.location == modifiedSubspaces[prevIndice!].subspaceName) {
|
||||
modifiedTags.removeWhere((t) => t.internalId == tag.internalId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((tag.location == 'Main Space' || tag.location == null) &&
|
||||
prevIndice != null) {
|
||||
modifiedSubspaces[prevIndice]
|
||||
.tags
|
||||
?.removeWhere((t) => t.internalId == tag.internalId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
'updatedTags': modifiedTags,
|
||||
'subspaces': modifiedSubspaces,
|
||||
};
|
||||
}
|
||||
|
||||
int? checkTagExistInSubspace(Tag tag, List<SubspaceModel>? subspaces) {
|
||||
if (subspaces == null) return null;
|
||||
for (int i = 0; i < subspaces.length; i++) {
|
||||
final subspace = subspaces[i];
|
||||
if (subspace.tags == null) continue;
|
||||
for (var t in subspace.tags!) {
|
||||
if (tag.internalId == t.internalId) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,6 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
.bodyMedium)),
|
||||
DataColumn(
|
||||
numeric: false,
|
||||
headingRowAlignment: MainAxisAlignment.start,
|
||||
label: Text('Tag',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
@@ -462,4 +461,5 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
'subspaces': modifiedSubspaces,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'subspace_event.dart';
|
||||
import 'subspace_state.dart';
|
||||
|
||||
class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||
SubSpaceBloc() : super(SubSpaceState([], [], '',{})) {
|
||||
SubSpaceBloc() : super(SubSpaceState([], [], '', {})) {
|
||||
on<AddSubSpace>((event, emit) {
|
||||
final existingNames = state.subSpaces.map((e) => e.subspaceName).toSet();
|
||||
|
||||
@@ -26,13 +26,22 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
||||
..add(event.subSpace);
|
||||
|
||||
emit(SubSpaceState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'',
|
||||
state.duplicates,
|
||||
if (state.duplicates.isNotEmpty) {
|
||||
emit(SubSpaceState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'*Duplicated sub-space name',
|
||||
state.duplicates,
|
||||
));
|
||||
} else {
|
||||
emit(SubSpaceState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'',
|
||||
state.duplicates,
|
||||
// Clear error message
|
||||
));
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,6 +54,13 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||
state.updatedSubSpaceModels,
|
||||
);
|
||||
|
||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||
updatedSubspaceModels.add(UpdateSubspaceModel(
|
||||
action: Action.delete,
|
||||
uuid: event.subSpace.uuid!,
|
||||
));
|
||||
}
|
||||
|
||||
final nameOccurrences = <String, int>{};
|
||||
for (final subSpace in updatedSubSpaces) {
|
||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||
@@ -55,19 +71,17 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
||||
.where((entry) => entry.value > 1)
|
||||
.map((entry) => entry.key)
|
||||
.toSet();
|
||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||
updatedSubspaceModels.add(UpdateSubspaceModel(
|
||||
action: Action.delete,
|
||||
uuid: event.subSpace.uuid!,
|
||||
));
|
||||
}
|
||||
final errorMessage =
|
||||
updatedDuplicates.isNotEmpty ? '*Duplicated sub-space name' : '';
|
||||
|
||||
emit(SubSpaceState(updatedSubSpaces, updatedSubspaceModels, '',
|
||||
updatedDuplicates // Clear error message
|
||||
));
|
||||
emit(SubSpaceState(
|
||||
updatedSubSpaces,
|
||||
updatedSubspaceModels,
|
||||
errorMessage,
|
||||
updatedDuplicates,
|
||||
));
|
||||
});
|
||||
|
||||
// Handle UpdateSubSpace Event
|
||||
|
||||
on<UpdateSubSpace>((event, emit) {
|
||||
final updatedSubSpaces = state.subSpaces.map((subSpace) {
|
||||
|
||||
@@ -25,22 +25,30 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
||||
updatedDuplicates,
|
||||
));
|
||||
} else {
|
||||
// Add subspace if no duplicate exists
|
||||
final updatedSubSpaces =
|
||||
List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||
..add(event.subSpace);
|
||||
|
||||
emit(SubSpaceModelState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'',
|
||||
state.duplicates,
|
||||
// Clear error message
|
||||
));
|
||||
if (state.duplicates.isNotEmpty) {
|
||||
emit(SubSpaceModelState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'*Duplicated sub-space name',
|
||||
state.duplicates,
|
||||
));
|
||||
} else {
|
||||
emit(SubSpaceModelState(
|
||||
updatedSubSpaces,
|
||||
state.updatedSubSpaceModels,
|
||||
'',
|
||||
state.duplicates,
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Handle RemoveSubSpaceModel Event
|
||||
|
||||
on<RemoveSubSpaceModel>((event, emit) {
|
||||
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||
..remove(event.subSpace);
|
||||
@@ -48,16 +56,6 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
||||
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
||||
state.updatedSubSpaceModels,
|
||||
);
|
||||
final nameOccurrences = <String, int>{};
|
||||
for (final subSpace in updatedSubSpaces) {
|
||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||
nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
final updatedDuplicates = nameOccurrences.entries
|
||||
.where((entry) => entry.value > 1)
|
||||
.map((entry) => entry.key)
|
||||
.toSet();
|
||||
|
||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
||||
@@ -66,12 +64,28 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
||||
));
|
||||
}
|
||||
|
||||
// Count occurrences of sub-space names to identify duplicates
|
||||
final nameOccurrences = <String, int>{};
|
||||
for (final subSpace in updatedSubSpaces) {
|
||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||
nameOccurrences[lowerName] = (nameOccurrences[lowerName] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// Identify duplicate names
|
||||
final updatedDuplicates = nameOccurrences.entries
|
||||
.where((entry) => entry.value > 1)
|
||||
.map((entry) => entry.key)
|
||||
.toSet();
|
||||
|
||||
// Determine the error message
|
||||
final errorMessage =
|
||||
updatedDuplicates.isNotEmpty ? '*Duplicated sub-space name' : '';
|
||||
|
||||
emit(SubSpaceModelState(
|
||||
updatedSubSpaces,
|
||||
updatedSubspaceModels,
|
||||
'',
|
||||
errorMessage,
|
||||
updatedDuplicates,
|
||||
// Clear error message
|
||||
));
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
|
||||
@@ -30,7 +32,36 @@ class TagHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return initialTags;
|
||||
}
|
||||
|
||||
static List<Tag> generateInitialForTags({
|
||||
List<Tag>? spaceTags,
|
||||
List<SubspaceModel>? subspaces,
|
||||
}) {
|
||||
final List<Tag> initialTags = [];
|
||||
|
||||
if (spaceTags != null) {
|
||||
initialTags.addAll(spaceTags);
|
||||
}
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
if (subspace.tags != null) {
|
||||
initialTags.addAll(
|
||||
subspace.tags!.map(
|
||||
(tag) => tag.copyWith(
|
||||
location: subspace.subspaceName,
|
||||
internalId: tag.internalId,
|
||||
tag: tag.tag,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return initialTags;
|
||||
}
|
||||
|
||||
@@ -79,4 +110,39 @@ class TagHelper {
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
static List<SelectedProduct> createInitialSelectedProductsForTags(
|
||||
List<Tag>? tags, List<SubspaceModel>? subspaces) {
|
||||
final Map<ProductModel, int> productCounts = {};
|
||||
|
||||
if (tags != null) {
|
||||
for (var tag in tags) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
if (subspace.tags != null) {
|
||||
for (var tag in subspace.tags!) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] =
|
||||
(productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return productCounts.entries
|
||||
.map((entry) => SelectedProduct(
|
||||
productId: entry.key.uuid,
|
||||
count: entry.value,
|
||||
productName: entry.key.name ?? 'Unnamed',
|
||||
product: entry.key,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,106 +201,101 @@ class CreateSpaceModelBloc
|
||||
|
||||
on<ModifySpaceTemplate>((event, emit) async {
|
||||
try {
|
||||
final prevSpaceModel = event.spaceTemplate;
|
||||
final newSpaceModel = event.updatedSpaceTemplate;
|
||||
String? spaceModelName;
|
||||
if (prevSpaceModel.modelName != newSpaceModel.modelName) {
|
||||
spaceModelName = newSpaceModel.modelName;
|
||||
}
|
||||
List<TagModelUpdate> tagUpdates = [];
|
||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||
final List<SubspaceTemplateModel>? prevSubspaces =
|
||||
prevSpaceModel.subspaceModels;
|
||||
final List<SubspaceTemplateModel>? newSubspaces =
|
||||
newSpaceModel.subspaceModels;
|
||||
if (event.spaceTemplate.uuid != null) {
|
||||
final prevSpaceModel =
|
||||
await _api.getSpaceModel(event.spaceTemplate.uuid ?? '');
|
||||
|
||||
tagUpdates = processTagUpdates(prevSpaceModel.tags, newSpaceModel.tags);
|
||||
final newSpaceModel = event.updatedSpaceTemplate;
|
||||
String? spaceModelName;
|
||||
if (prevSpaceModel?.modelName != newSpaceModel.modelName) {
|
||||
spaceModelName = newSpaceModel.modelName;
|
||||
}
|
||||
List<TagModelUpdate> tagUpdates = [];
|
||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||
final List<SubspaceTemplateModel>? prevSubspaces =
|
||||
prevSpaceModel?.subspaceModels;
|
||||
final List<SubspaceTemplateModel>? newSubspaces =
|
||||
newSpaceModel.subspaceModels;
|
||||
|
||||
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) {
|
||||
tagUpdates =
|
||||
processTagUpdates(prevSpaceModel?.tags, newSpaceModel.tags);
|
||||
|
||||
if (prevSubspaces != null || newSubspaces != null) {
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final existsInNew = newSubspaces
|
||||
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||
if (!existsInNew) {
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.delete, uuid: prevSubspace.uuid));
|
||||
}
|
||||
}
|
||||
} else if (prevSubspaces != null && newSubspaces == null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.delete, uuid: prevSubspace.uuid));
|
||||
}
|
||||
}
|
||||
} else if (prevSubspaces != null && newSubspaces == null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.delete, uuid: prevSubspace.uuid));
|
||||
}
|
||||
}
|
||||
|
||||
if (newSubspaces != null) {
|
||||
for (var newSubspace in newSubspaces!) {
|
||||
// Tag without UUID
|
||||
if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
|
||||
final List<TagModelUpdate> tagUpdates = [];
|
||||
if (newSubspaces != null) {
|
||||
for (var newSubspace in newSubspaces!) {
|
||||
// Tag without UUID
|
||||
if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
|
||||
final List<TagModelUpdate> tagUpdates = [];
|
||||
|
||||
if (newSubspace.tags != null) {
|
||||
for (var tag in newSubspace.tags!) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
||||
tag: tag.tag,
|
||||
productUuid: tag.product?.uuid));
|
||||
if (newSubspace.tags != null) {
|
||||
for (var tag in newSubspace.tags!) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
||||
tag: tag.tag,
|
||||
productUuid: tag.product?.uuid));
|
||||
}
|
||||
}
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.add,
|
||||
subspaceName: newSubspace.subspaceName,
|
||||
tags: tagUpdates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
final newSubspaceMap = {
|
||||
for (var subspace in newSubspaces!) subspace.uuid: subspace
|
||||
};
|
||||
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||
|
||||
if (newSubspace != null) {
|
||||
final List<TagModelUpdate> tagSubspaceUpdates =
|
||||
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.update,
|
||||
uuid: newSubspace.uuid,
|
||||
subspaceName: newSubspace.subspaceName,
|
||||
tags: tagSubspaceUpdates));
|
||||
}
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.add,
|
||||
subspaceName: newSubspace.subspaceName,
|
||||
tags: tagUpdates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
final newSubspaceMap = {
|
||||
for (var subspace in newSubspaces!) subspace.uuid: subspace
|
||||
};
|
||||
final spaceModelBody = CreateSpaceTemplateBodyModel(
|
||||
modelName: spaceModelName,
|
||||
tags: tagUpdates,
|
||||
subspaceModels: subspaceUpdates);
|
||||
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||
final res = await _api.updateSpaceModel(
|
||||
spaceModelBody, prevSpaceModel?.uuid ?? '');
|
||||
|
||||
if (newSubspace != null) {
|
||||
if(prevSubspace.tags!=null){
|
||||
for(var t in prevSubspace.tags!){
|
||||
print("old tags are ${t.tag} ${t.uuid}");
|
||||
}}
|
||||
|
||||
if(newSubspace.tags!=null){
|
||||
for(var t in newSubspace.tags!){
|
||||
print("new tags are ${t.tag} ${t.uuid}");
|
||||
}}
|
||||
|
||||
final List<TagModelUpdate> tagSubspaceUpdates =
|
||||
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: Action.update,
|
||||
uuid: newSubspace.uuid,
|
||||
subspaceName: newSubspace.subspaceName,
|
||||
tags: tagSubspaceUpdates));
|
||||
}
|
||||
if (res != null) {
|
||||
emit(CreateSpaceModelLoaded(newSpaceModel));
|
||||
if (event.onUpdate != null) {
|
||||
event.onUpdate!(event.updatedSpaceTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
@@ -314,6 +309,18 @@ class CreateSpaceModelBloc
|
||||
final List<TagModelUpdate> tagUpdates = [];
|
||||
final processedTags = <String?>{};
|
||||
|
||||
if (prevTags == null && newTags != null) {
|
||||
for (var newTag in newTags) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
tag: newTag.tag,
|
||||
uuid: newTag.uuid,
|
||||
productUuid: newTag.product?.uuid,
|
||||
));
|
||||
}
|
||||
return tagUpdates;
|
||||
}
|
||||
|
||||
if (newTags != null || prevTags != null) {
|
||||
// Case 1: Tags deleted
|
||||
if (prevTags != null && newTags != null) {
|
||||
@@ -334,9 +341,11 @@ class CreateSpaceModelBloc
|
||||
|
||||
// Case 2: Tags added
|
||||
if (newTags != null) {
|
||||
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
|
||||
|
||||
for (var newTag in newTags!) {
|
||||
// Tag without UUID
|
||||
if ((newTag.uuid == null || newTag.uuid!.isEmpty) &&
|
||||
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
|
||||
!processedTags.contains(newTag.tag)) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
|
||||
@@ -70,5 +70,5 @@ abstract class ColorsManager {
|
||||
static const Color invitedOrangeText = Color(0xFFFFBF00);
|
||||
static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
|
||||
//background: #F8F8F8;
|
||||
|
||||
static const Color vividBlue = Color(0xFF023DFE);
|
||||
}
|
||||
|
||||
@@ -399,5 +399,7 @@ class Assets {
|
||||
static const String ZtoAIcon = 'assets/icons/ztoa_icon.png';
|
||||
static const String AtoZIcon = 'assets/icons/atoz_icon.png';
|
||||
static const String link = 'assets/icons/link.svg';
|
||||
static const String duplicate = 'assets/icons/duplicate.svg';
|
||||
static const String spaceDelete = 'assets/icons/space_delete.svg';
|
||||
}
|
||||
//user_management.svg
|
||||
|
||||
Reference in New Issue
Block a user