mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
added duplicate
This commit is contained in:
16
assets/icons/duplicate.svg
Normal file
16
assets/icons/duplicate.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M17.9807 2.67199L16.6413 0.219727H7.91051C7.43122 0.219727 7.04266 0.608281 7.04266 1.08758V16.605C7.04266 17.0843 7.43122 17.4729 7.91051 17.4729H18.2257C18.705 17.4729 19.0935 17.0843 19.0935 16.605V16.5459H19.7312V3.8127L17.9807 2.67199Z" fill="#60B7FF"/>
|
||||||
|
<path d="M18.1513 6.23445H9.12553C8.95881 6.23445 8.82373 6.09934 8.82373 5.93266C8.82373 5.76598 8.95885 5.63086 9.12553 5.63086H18.1513C18.318 5.63086 18.4531 5.76598 18.4531 5.93266C18.4531 6.09934 18.318 6.23445 18.1513 6.23445Z" fill="#0055A3"/>
|
||||||
|
<path d="M18.1513 8.54891H9.12553C8.95881 8.54891 8.82373 8.41379 8.82373 8.24711C8.82373 8.08043 8.95885 7.94531 9.12553 7.94531H18.1513C18.318 7.94531 18.4531 8.08043 18.4531 8.24711C18.4531 8.41379 18.318 8.54891 18.1513 8.54891Z" fill="#0055A3"/>
|
||||||
|
<path d="M18.1513 10.8634H9.12553C8.95881 10.8634 8.82373 10.7282 8.82373 10.5616C8.82373 10.3949 8.95885 10.2598 9.12553 10.2598H18.1513C18.318 10.2598 18.4531 10.3949 18.4531 10.5616C18.4531 10.7282 18.318 10.8634 18.1513 10.8634Z" fill="#0055A3"/>
|
||||||
|
<path d="M18.1513 13.1778H9.12556C8.95884 13.1778 8.82376 13.0427 8.82376 12.876C8.82376 12.7093 8.95888 12.5742 9.12556 12.5742H18.1513C18.3181 12.5742 18.4531 12.7093 18.4531 12.876C18.4531 13.0427 18.3181 13.1778 18.1513 13.1778Z" fill="#0055A3"/>
|
||||||
|
<path d="M19.0935 3.39648V16.6044C19.0935 17.0837 18.7049 17.4722 18.2256 17.4722H19.3663C19.8456 17.4722 20.2342 17.0837 20.2342 16.6044V3.81203L19.0935 3.39648Z" fill="#26A6FE"/>
|
||||||
|
<path d="M17.5091 3.8127H20.2342L16.6413 0.219727V2.94484C16.6413 3.42414 17.0298 3.8127 17.5091 3.8127Z" fill="#004281"/>
|
||||||
|
<path d="M11.4172 19.7805C11.8965 19.7805 12.2851 19.392 12.2851 18.9127V18.8937H12.8297V6.12031L11.039 4.78906L9.83279 2.52734H1.10204C0.622747 2.52734 0.234192 2.9159 0.234192 3.3952V18.9127C0.234192 19.392 0.622747 19.7805 1.10204 19.7805H11.4172Z" fill="#D5EDFF"/>
|
||||||
|
<path d="M12.285 4.97852V6.11922V18.9116C12.285 19.3909 11.8964 19.7794 11.4171 19.7794H12.5578C13.0371 19.7794 13.4257 19.3909 13.4257 18.9116V6.11922L12.285 4.97852Z" fill="#D8ECFE"/>
|
||||||
|
<path d="M10.7006 6.12031H13.4258L9.83279 2.52734V5.25246C9.83276 5.73176 10.2213 6.12031 10.7006 6.12031Z" fill="#B3DAFE"/>
|
||||||
|
<path d="M11.3429 8.54891H2.31709C2.15037 8.54891 2.01529 8.41379 2.01529 8.24711C2.01529 8.08043 2.15041 7.94531 2.31709 7.94531H11.3429C11.5096 7.94531 11.6447 8.08043 11.6447 8.24711C11.6447 8.41379 11.5096 8.54891 11.3429 8.54891Z" fill="#82AEE3"/>
|
||||||
|
<path d="M11.3428 10.8634H2.31706C2.15034 10.8634 2.01526 10.7282 2.01526 10.5616C2.01526 10.3949 2.15038 10.2598 2.31706 10.2598H11.3428C11.5096 10.2598 11.6446 10.3949 11.6446 10.5616C11.6446 10.7282 11.5096 10.8634 11.3428 10.8634Z" fill="#82AEE3"/>
|
||||||
|
<path d="M11.3428 13.1778H2.31706C2.15034 13.1778 2.01526 13.0427 2.01526 12.876C2.01526 12.7093 2.15038 12.5742 2.31706 12.5742H11.3428C11.5096 12.5742 11.6446 12.7093 11.6446 12.876C11.6446 13.0427 11.5096 13.1778 11.3428 13.1778Z" fill="#82AEE3"/>
|
||||||
|
<path d="M11.3428 15.4923H2.31706C2.15034 15.4923 2.01526 15.3571 2.01526 15.1905C2.01526 15.0238 2.15038 14.8887 2.31706 14.8887H11.3428C11.5096 14.8887 11.6446 15.0238 11.6446 15.1905C11.6447 15.3571 11.5096 15.4923 11.3428 15.4923Z" fill="#82AEE3"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
9
assets/icons/space_delete.svg
Normal file
9
assets/icons/space_delete.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<svg width="16" height="20" viewBox="0 0 16 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.8069 19.9998H3.50035C2.16411 19.9998 1.08087 18.9165 1.08087 17.5804V4.51562H14.2262V17.5804C14.2262 18.9165 13.1432 19.9998 11.8069 19.9998Z" fill="#D8D8D8"/>
|
||||||
|
<path d="M1.08087 4.51562V5.48335H12.3715C12.8168 5.48335 13.1779 5.84438 13.1779 6.2898V17.3384C13.1779 18.2292 12.4557 18.9513 11.5649 18.9513H2.4519C2.05409 18.9513 1.67887 18.8547 1.34775 18.6844C1.74907 19.4652 2.56207 19.9998 3.50035 19.9998H11.8069C13.1432 19.9998 14.2262 18.9165 14.2262 17.5803V4.51562H1.08087Z" fill="#BABABA"/>
|
||||||
|
<path d="M14.2667 1.77417H10.7439L10.1633 0.612956C9.9742 0.234837 9.5941 0 9.17142 0H6.13594C5.71311 0 5.33316 0.234837 5.1441 0.612956L4.56349 1.77417H1.04063C0.595221 1.77417 0.234192 2.1352 0.234192 2.58061V3.70978C0.234192 4.15504 0.595221 4.51622 1.04063 4.51622H14.2667C14.7121 4.51622 15.0732 4.15504 15.0732 3.70978V2.58077C15.0732 2.1352 14.7121 1.77417 14.2667 1.77417ZM5.68503 0.8835C5.77094 0.71153 5.94368 0.604869 6.13594 0.604869H9.17142C9.36354 0.604869 9.53627 0.71153 9.62218 0.8835L10.0676 1.77417H5.23977L5.68503 0.8835Z" fill="#757575"/>
|
||||||
|
<path d="M14.2668 1.77441H12.9763C13.4217 1.77441 13.7829 2.13544 13.7829 2.58086V3.71003C13.7829 4.15529 13.4217 4.51647 12.9763 4.51647H14.2668C14.7122 4.51647 15.0732 4.15529 15.0732 3.71003V2.58101C15.0732 2.13544 14.7122 1.77441 14.2668 1.77441Z" fill="#595959"/>
|
||||||
|
<path d="M11.3634 17.5C10.918 17.5 10.5569 17.139 10.5569 16.6935V9.15312C10.5569 8.70771 10.918 8.34668 11.3634 8.34668C11.8088 8.34668 12.1698 8.70771 12.1698 9.15312V16.6935C12.1698 17.139 11.8088 17.5 11.3634 17.5Z" fill="#757575"/>
|
||||||
|
<path d="M3.94398 17.5C4.38924 17.5 4.75043 17.139 4.75043 16.6935V9.15312C4.75043 8.70771 4.38924 8.34668 3.94398 8.34668C3.49857 8.34668 3.13739 8.70771 3.13739 9.15312V16.6935C3.13739 17.139 3.49857 17.5 3.94398 17.5Z" fill="#757575"/>
|
||||||
|
<path d="M7.65361 17.5C7.2082 17.5 6.84717 17.139 6.84717 16.6935V9.15312C6.84717 8.70771 7.2082 8.34668 7.65361 8.34668C8.09902 8.34668 8.46005 8.70771 8.46005 9.15312V16.6935C8.46005 17.139 8.09902 17.5 7.65361 17.5Z" fill="#757575"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
@ -1,38 +1,74 @@
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
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/all_spaces/model/selected_product_model.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/bloc/add_device_type_model_event.dart';
|
||||||
|
|
||||||
class AddDeviceTypeBloc
|
class AddDeviceTypeBloc extends Bloc<AddDeviceTypeEvent, AddDeviceState> {
|
||||||
extends Bloc<AddDeviceTypeEvent, List<SelectedProduct>> {
|
AddDeviceTypeBloc() : super(AddDeviceInitial()) {
|
||||||
AddDeviceTypeBloc(List<SelectedProduct> initialProducts)
|
on<InitializeDevice>(_onInitializeTagModels);
|
||||||
: super(initialProducts) {
|
|
||||||
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
on<UpdateProductCountEvent>(_onUpdateProductCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onInitializeTagModels(
|
||||||
|
InitializeDevice event, Emitter<AddDeviceState> emit) {
|
||||||
|
emit(AddDeviceLoaded(
|
||||||
|
selectedProducts: event.addedProducts,
|
||||||
|
initialTag: event.initialTags,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
void _onUpdateProductCount(
|
void _onUpdateProductCount(
|
||||||
UpdateProductCountEvent event, Emitter<List<SelectedProduct>> emit) {
|
UpdateProductCountEvent event, Emitter<AddDeviceState> emit) {
|
||||||
final existingProduct = state.firstWhere(
|
final currentState = state;
|
||||||
|
|
||||||
|
if (currentState is AddDeviceLoaded) {
|
||||||
|
final existingProduct = currentState.selectedProducts.firstWhere(
|
||||||
(p) => p.productId == event.productId,
|
(p) => p.productId == event.productId,
|
||||||
orElse: () => SelectedProduct(productId: event.productId, count: 0,productName: event.productName,product: event.product ),
|
orElse: () => SelectedProduct(
|
||||||
|
productId: event.productId,
|
||||||
|
count: 0,
|
||||||
|
productName: event.productName,
|
||||||
|
product: event.product,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
List<SelectedProduct> updatedProducts;
|
||||||
|
|
||||||
if (event.count > 0) {
|
if (event.count > 0) {
|
||||||
if (!state.contains(existingProduct)) {
|
if (!currentState.selectedProducts.contains(existingProduct)) {
|
||||||
emit([
|
updatedProducts = [
|
||||||
...state,
|
...currentState.selectedProducts,
|
||||||
SelectedProduct(productId: event.productId, count: event.count, productName: event.productName, product: event.product)
|
SelectedProduct(
|
||||||
]);
|
productId: event.productId,
|
||||||
|
count: event.count,
|
||||||
|
productName: event.productName,
|
||||||
|
product: event.product,
|
||||||
|
),
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
final updatedList = state.map((p) {
|
updatedProducts = currentState.selectedProducts.map((p) {
|
||||||
if (p.productId == event.productId) {
|
if (p.productId == event.productId) {
|
||||||
return SelectedProduct(productId: p.productId, count: event.count, productName: p.productName,product: p.product);
|
return SelectedProduct(
|
||||||
|
productId: p.productId,
|
||||||
|
count: event.count,
|
||||||
|
productName: p.productName,
|
||||||
|
product: p.product,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
}).toList();
|
}).toList();
|
||||||
emit(updatedList);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
emit(state.where((p) => p.productId != event.productId).toList());
|
// Remove the product if the count is 0
|
||||||
|
updatedProducts = currentState.selectedProducts
|
||||||
|
.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: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/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 {
|
abstract class AddDeviceTypeEvent extends Equatable {
|
||||||
|
const AddDeviceTypeEvent();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [];
|
List<Object> get props => [];
|
||||||
}
|
}
|
||||||
@ -12,8 +16,26 @@ class UpdateProductCountEvent extends AddDeviceTypeEvent {
|
|||||||
final String productName;
|
final String productName;
|
||||||
final ProductModel product;
|
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
|
@override
|
||||||
List<Object> get props => [productId, count];
|
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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/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_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/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/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/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/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/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/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/pages/spaces_management/tag_model/widgets/action_button_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
|
||||||
|
|
||||||
class AddDeviceTypeWidget extends StatelessWidget {
|
class AddDeviceTypeWidget extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
final ValueChanged<List<SelectedProduct>>? onProductsSelected;
|
||||||
@ -22,7 +25,6 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
final String spaceName;
|
final String spaceName;
|
||||||
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
||||||
|
|
||||||
|
|
||||||
const AddDeviceTypeWidget(
|
const AddDeviceTypeWidget(
|
||||||
{super.key,
|
{super.key,
|
||||||
this.products,
|
this.products,
|
||||||
@ -44,12 +46,22 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
: 3;
|
: 3;
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AddDeviceTypeBloc(initialSelectedProducts ?? []),
|
create: (_) => AddDeviceTypeBloc()
|
||||||
|
..add(InitializeDevice(
|
||||||
|
initialTags: spaceTags ?? [],
|
||||||
|
addedProducts: initialSelectedProducts ?? [],
|
||||||
|
)),
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => AlertDialog(
|
builder: (context) => AlertDialog(
|
||||||
title: const Text('Add Devices'),
|
title: const Text('Add Devices'),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
content: SingleChildScrollView(
|
content: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is AddDeviceLoading) {
|
||||||
|
return const Center(child: CircularProgressIndicator());
|
||||||
|
}
|
||||||
|
if (state is AddDeviceLoaded) {
|
||||||
|
return SingleChildScrollView(
|
||||||
child: Container(
|
child: Container(
|
||||||
width: size.width * 0.9,
|
width: size.width * 0.9,
|
||||||
height: size.height * 0.65,
|
height: size.height * 0.65,
|
||||||
@ -59,15 +71,20 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
padding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 20.0),
|
||||||
child: ScrollableGridViewWidget(
|
child: ScrollableGridViewWidget(
|
||||||
products: products, crossAxisCount: crossAxisCount),
|
products: products,
|
||||||
|
crossAxisCount: crossAxisCount),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
|
}
|
||||||
|
return const SizedBox();
|
||||||
|
}),
|
||||||
actions: [
|
actions: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -78,20 +95,27 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ActionButton(
|
SizedBox(
|
||||||
label: 'Continue',
|
width: 140,
|
||||||
|
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final isDisabled = state is AddDeviceLoaded &&
|
||||||
|
state.selectedProducts.isEmpty;
|
||||||
|
return DefaultButton(
|
||||||
|
borderRadius: 10,
|
||||||
backgroundColor: ColorsManager.secondaryColor,
|
backgroundColor: ColorsManager.secondaryColor,
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: isDisabled
|
||||||
|
? ColorsManager.whiteColorsWithOpacity
|
||||||
|
: ColorsManager.whiteColors,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final currentState =
|
if (state is AddDeviceLoaded &&
|
||||||
context.read<AddDeviceTypeBloc>().state;
|
state.selectedProducts.isNotEmpty) {
|
||||||
Navigator.of(context).pop();
|
final initialTags =
|
||||||
|
TagHelper.generateInitialForTags(
|
||||||
if (currentState.isNotEmpty) {
|
|
||||||
final initialTags = generateInitialTags(
|
|
||||||
spaceTags: spaceTags,
|
spaceTags: spaceTags,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
);
|
);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
|
||||||
final dialogTitle = initialTags.isNotEmpty
|
final dialogTitle = initialTags.isNotEmpty
|
||||||
? 'Edit Device'
|
? 'Edit Device'
|
||||||
@ -102,7 +126,7 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
builder: (context) => AssignTagDialog(
|
builder: (context) => AssignTagDialog(
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
addedProducts: currentState,
|
addedProducts: state.selectedProducts,
|
||||||
allTags: allTags,
|
allTags: allTags,
|
||||||
spaceName: spaceName,
|
spaceName: spaceName,
|
||||||
initialTags: initialTags,
|
initialTags: initialTags,
|
||||||
@ -114,7 +138,10 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
child: const Text('Next'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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_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/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/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/selected_product_model.dart';
|
||||||
@ -24,8 +25,11 @@ class ScrollableGridViewWidget extends StatelessWidget {
|
|||||||
return Scrollbar(
|
return Scrollbar(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
thumbVisibility: true,
|
thumbVisibility: true,
|
||||||
child: BlocBuilder<AddDeviceTypeBloc, List<SelectedProduct>>(
|
child: BlocBuilder<AddDeviceTypeBloc, AddDeviceState>(
|
||||||
builder: (context, productCounts) {
|
builder: (context, state) {
|
||||||
|
final productCounts = state is AddDeviceLoaded
|
||||||
|
? state.selectedProducts
|
||||||
|
: <SelectedProduct>[];
|
||||||
return GridView.builder(
|
return GridView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
|
@ -5,19 +5,23 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
|||||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||||
|
|
||||||
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
||||||
const CommunityStructureHeaderActionButtons({
|
const CommunityStructureHeaderActionButtons(
|
||||||
super.key,
|
{super.key,
|
||||||
required this.theme,
|
required this.theme,
|
||||||
required this.isSave,
|
required this.isSave,
|
||||||
required this.onSave,
|
required this.onSave,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
required this.selectedSpace,
|
required this.selectedSpace,
|
||||||
});
|
required this.onDuplicate,
|
||||||
|
required this.onEdit});
|
||||||
|
|
||||||
final ThemeData theme;
|
final ThemeData theme;
|
||||||
final bool isSave;
|
final bool isSave;
|
||||||
final VoidCallback onSave;
|
final VoidCallback onSave;
|
||||||
final VoidCallback onDelete;
|
final VoidCallback onDelete;
|
||||||
|
final VoidCallback onDuplicate;
|
||||||
|
final VoidCallback onEdit;
|
||||||
|
|
||||||
final SpaceModel? selectedSpace;
|
final SpaceModel? selectedSpace;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -42,13 +46,18 @@ class CommunityStructureHeaderActionButtons extends StatelessWidget {
|
|||||||
CommunityStructureHeaderButton(
|
CommunityStructureHeaderButton(
|
||||||
label: "Edit",
|
label: "Edit",
|
||||||
svgAsset: Assets.editSpace,
|
svgAsset: Assets.editSpace,
|
||||||
onPressed: () => {},
|
onPressed: onEdit,
|
||||||
|
theme: theme,
|
||||||
|
),
|
||||||
|
CommunityStructureHeaderButton(
|
||||||
|
label: "Duplicate",
|
||||||
|
svgAsset: Assets.duplicate,
|
||||||
|
onPressed: onDuplicate,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
),
|
),
|
||||||
CommunityStructureHeaderButton(
|
CommunityStructureHeaderButton(
|
||||||
label: "Delete",
|
label: "Delete",
|
||||||
icon: const Icon(Icons.delete,
|
svgAsset: Assets.spaceDelete,
|
||||||
size: 18, color: ColorsManager.warningRed),
|
|
||||||
onPressed: onDelete,
|
onPressed: onDelete,
|
||||||
theme: theme,
|
theme: theme,
|
||||||
),
|
),
|
||||||
|
@ -24,12 +24,12 @@ class CommunityStructureHeaderButton extends StatelessWidget {
|
|||||||
const double buttonHeight = 40;
|
const double buttonHeight = 40;
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
maxWidth: 100,
|
maxWidth: 130,
|
||||||
minHeight: buttonHeight,
|
minHeight: buttonHeight,
|
||||||
),
|
),
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
borderWidth: 3,
|
borderWidth: 2,
|
||||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||||
foregroundColor: ColorsManager.blackColor,
|
foregroundColor: ColorsManager.blackColor,
|
||||||
borderRadius: 12.0,
|
borderRadius: 12.0,
|
||||||
@ -44,8 +44,8 @@ class CommunityStructureHeaderButton extends StatelessWidget {
|
|||||||
if (svgAsset != null)
|
if (svgAsset != null)
|
||||||
SvgPicture.asset(
|
SvgPicture.asset(
|
||||||
svgAsset!,
|
svgAsset!,
|
||||||
width: 30,
|
width: 20,
|
||||||
height: 30,
|
height: 20,
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Flexible(
|
Flexible(
|
||||||
|
@ -14,6 +14,9 @@ class CommunityStructureHeader extends StatefulWidget {
|
|||||||
final TextEditingController nameController;
|
final TextEditingController nameController;
|
||||||
final VoidCallback onSave;
|
final VoidCallback onSave;
|
||||||
final VoidCallback onDelete;
|
final VoidCallback onDelete;
|
||||||
|
final VoidCallback onEdit;
|
||||||
|
final VoidCallback onDuplicate;
|
||||||
|
|
||||||
final VoidCallback onEditName;
|
final VoidCallback onEditName;
|
||||||
final ValueChanged<String> onNameSubmitted;
|
final ValueChanged<String> onNameSubmitted;
|
||||||
final List<CommunityModel> communities;
|
final List<CommunityModel> communities;
|
||||||
@ -32,7 +35,9 @@ class CommunityStructureHeader extends StatefulWidget {
|
|||||||
required this.onNameSubmitted,
|
required this.onNameSubmitted,
|
||||||
this.community,
|
this.community,
|
||||||
required this.communities,
|
required this.communities,
|
||||||
this.selectedSpace});
|
this.selectedSpace,
|
||||||
|
required this.onDuplicate,
|
||||||
|
required this.onEdit});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CommunityStructureHeader> createState() =>
|
State<CommunityStructureHeader> createState() =>
|
||||||
@ -146,6 +151,8 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
|||||||
isSave: widget.isSave,
|
isSave: widget.isSave,
|
||||||
onSave: widget.onSave,
|
onSave: widget.onSave,
|
||||||
onDelete: widget.onDelete,
|
onDelete: widget.onDelete,
|
||||||
|
onDuplicate: widget.onDuplicate,
|
||||||
|
onEdit: widget.onEdit,
|
||||||
selectedSpace: widget.selectedSpace,
|
selectedSpace: widget.selectedSpace,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -4,6 +4,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
|||||||
|
|
||||||
// Syncrow project imports
|
// Syncrow project imports
|
||||||
import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
|
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_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/bloc/space_management_event.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.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/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/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/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_card_widget.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_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';
|
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,
|
onSave: _saveSpaces,
|
||||||
selectedSpace: widget.selectedSpace,
|
selectedSpace: widget.selectedSpace,
|
||||||
onDelete: _onDelete,
|
onDelete: _onDelete,
|
||||||
|
onDuplicate: () => {_onDuplicate(context)},
|
||||||
|
onEdit: () => {},
|
||||||
onEditName: () {
|
onEditName: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
isEditingName = !isEditingName;
|
isEditingName = !isEditingName;
|
||||||
@ -328,7 +333,6 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
parentSpace.addOutgoingConnection(newConnection);
|
parentSpace.addOutgoingConnection(newConnection);
|
||||||
parentSpace.children.add(newSpace);
|
parentSpace.children.add(newSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
spaces.add(newSpace);
|
spaces.add(newSpace);
|
||||||
_updateNodePosition(newSpace, newSpace.position);
|
_updateNodePosition(newSpace, newSpace.position);
|
||||||
});
|
});
|
||||||
@ -546,4 +550,175 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
space.status == SpaceStatus.modified ||
|
space.status == SpaceStatus.modified ||
|
||||||
space.status == SpaceStatus.deleted);
|
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/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/tag.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.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/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/helper/tag_helper.dart';
|
||||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
||||||
@ -409,7 +410,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
_showTagCreateDialog(
|
_showTagCreateDialog(
|
||||||
context,
|
context,
|
||||||
enteredName,
|
enteredName,
|
||||||
|
widget.isEdit,
|
||||||
widget.products,
|
widget.products,
|
||||||
|
subspaces,
|
||||||
);
|
);
|
||||||
// Edit action
|
// Edit action
|
||||||
})
|
})
|
||||||
@ -420,7 +423,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
: TextButton(
|
: TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_showTagCreateDialog(
|
_showTagCreateDialog(
|
||||||
context, enteredName, widget.products);
|
context,
|
||||||
|
enteredName,
|
||||||
|
widget.isEdit,
|
||||||
|
widget.products,
|
||||||
|
subspaces,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
style: TextButton.styleFrom(
|
style: TextButton.styleFrom(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
@ -558,9 +566,25 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagCreateDialog(
|
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||||
BuildContext context, String name, List<ProductModel>? products) {
|
List<ProductModel>? products, List<SubspaceModel>? subspaces) {
|
||||||
showDialog(
|
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,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AddDeviceTypeWidget(
|
return AddDeviceTypeWidget(
|
||||||
@ -570,7 +594,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
spaceTags: tags,
|
spaceTags: tags,
|
||||||
allTags: [],
|
allTags: [],
|
||||||
initialSelectedProducts:
|
initialSelectedProducts:
|
||||||
createInitialSelectedProducts(tags, subspaces),
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = selectedSpaceTags;
|
tags = selectedSpaceTags;
|
||||||
@ -594,49 +619,4 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -48,7 +48,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: allTags,
|
tags: allTags,
|
||||||
isSaveEnabled: _validateTags(allTags),
|
isSaveEnabled: _validateTags(allTags),
|
||||||
));
|
errorMessage: ''));
|
||||||
});
|
});
|
||||||
|
|
||||||
on<UpdateTagEvent>((event, emit) {
|
on<UpdateTagEvent>((event, emit) {
|
||||||
@ -117,12 +117,10 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _validateTags(List<Tag> tags) {
|
bool _validateTags(List<Tag> tags) {
|
||||||
if (tags.isEmpty) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
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) {
|
String? _getValidationError(List<Tag> tags) {
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.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/cancel_button.dart';
|
||||||
import 'package:syncrow_web/pages/common/buttons/default_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/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/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/subspace_model.dart';
|
||||||
@ -79,6 +82,7 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
Theme.of(context).textTheme.bodyMedium)),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
|
numeric: false,
|
||||||
label: Text('Tag',
|
label: Text('Tag',
|
||||||
style:
|
style:
|
||||||
Theme.of(context).textTheme.bodyMedium)),
|
Theme.of(context).textTheme.bodyMedium)),
|
||||||
@ -109,10 +113,11 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
: List.generate(state.tags.length, (index) {
|
: List.generate(state.tags.length, (index) {
|
||||||
final tag = state.tags[index];
|
final tag = state.tags[index];
|
||||||
final controller = controllers[index];
|
final controller = controllers[index];
|
||||||
|
final availableTags = getAvailableTags(
|
||||||
|
allTags ?? [], state.tags, tag);
|
||||||
return DataRow(
|
return DataRow(
|
||||||
cells: [
|
cells: [
|
||||||
DataCell(Text(index.toString())),
|
DataCell(Text((index + 1).toString())),
|
||||||
DataCell(
|
DataCell(
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
@ -123,28 +128,53 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
tag.product?.name ?? 'Unknown',
|
tag.product?.name ?? 'Unknown',
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
)),
|
)),
|
||||||
IconButton(
|
const SizedBox(width: 10),
|
||||||
icon: const Icon(Icons.close,
|
Container(
|
||||||
color: ColorsManager.warningRed,
|
width: 20.0,
|
||||||
size: 16),
|
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: () {
|
onPressed: () {
|
||||||
context.read<AssignTagBloc>().add(
|
context
|
||||||
DeleteTag(
|
.read<AssignTagBloc>()
|
||||||
|
.add(DeleteTag(
|
||||||
tagToDelete: tag,
|
tagToDelete: tag,
|
||||||
tags: state.tags));
|
tags: state.tags));
|
||||||
},
|
},
|
||||||
tooltip: 'Delete Tag',
|
tooltip: 'Delete Tag',
|
||||||
)
|
padding: EdgeInsets.zero,
|
||||||
|
constraints:
|
||||||
|
const BoxConstraints(),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
Row(
|
Container(
|
||||||
children: [
|
alignment: Alignment
|
||||||
Expanded(
|
.centerLeft, // Align cell content to the left
|
||||||
child: TextFormField(
|
child: SizedBox(
|
||||||
controller: controller,
|
width: double
|
||||||
onChanged: (value) {
|
.infinity, // Ensure full width for dropdown
|
||||||
|
child: DialogTextfieldDropdown(
|
||||||
|
items: availableTags,
|
||||||
|
initialValue: tag.tag,
|
||||||
|
onSelected: (value) {
|
||||||
|
controller.text = value;
|
||||||
context
|
context
|
||||||
.read<AssignTagBloc>()
|
.read<AssignTagBloc>()
|
||||||
.add(UpdateTagEvent(
|
.add(UpdateTagEvent(
|
||||||
@ -152,118 +182,26 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
tag: value.trim(),
|
tag: value.trim(),
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
decoration: const InputDecoration(
|
|
||||||
hintText: 'Enter Tag',
|
|
||||||
border: InputBorder.none,
|
|
||||||
),
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: ColorsManager.blackColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(
|
|
||||||
width: MediaQuery.of(context)
|
|
||||||
.size
|
|
||||||
.width *
|
|
||||||
0.15,
|
|
||||||
child: PopupMenuButton<String>(
|
|
||||||
color: ColorsManager.whiteColors,
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.arrow_drop_down,
|
|
||||||
color:
|
|
||||||
ColorsManager.blackColor),
|
|
||||||
onSelected: (value) {
|
|
||||||
controller.text = value;
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
DataCell(
|
DataCell(
|
||||||
DropdownButtonHideUnderline(
|
SizedBox(
|
||||||
child: DropdownButton<String>(
|
width: double.infinity,
|
||||||
value: tag.location ?? 'Main',
|
child: DialogDropdown(
|
||||||
dropdownColor: ColorsManager
|
items: locations,
|
||||||
.whiteColors, // Dropdown background
|
selectedValue:
|
||||||
style: const TextStyle(
|
tag.location ?? 'Main Space',
|
||||||
color: Colors
|
onSelected: (value) {
|
||||||
.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) {
|
|
||||||
context
|
context
|
||||||
.read<AssignTagBloc>()
|
.read<AssignTagBloc>()
|
||||||
.add(UpdateLocation(
|
.add(UpdateLocation(
|
||||||
index: index,
|
index: index,
|
||||||
location: value,
|
location: value,
|
||||||
));
|
));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
),
|
)),
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -284,13 +222,35 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: CancelButton(
|
child: Builder(
|
||||||
label: 'Cancel',
|
builder: (buttonContext) => CancelButton(
|
||||||
|
label: 'Add New Device',
|
||||||
onPressed: () async {
|
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();
|
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),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
@ -302,22 +262,16 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
onPressed: state.isSaveEnabled
|
onPressed: state.isSaveEnabled
|
||||||
? () async {
|
? () async {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
final assignedTags = <Tag>{};
|
final updatedTags = List<Tag>.from(state.tags);
|
||||||
for (var tag in state.tags) {
|
final result =
|
||||||
if (tag.location == null ||
|
processTags(updatedTags, subspaces);
|
||||||
subspaces == null) {
|
|
||||||
continue;
|
final processedTags =
|
||||||
}
|
result['updatedTags'] as List<Tag>;
|
||||||
for (var subspace in subspaces!) {
|
final processedSubspaces =
|
||||||
if (tag.location == subspace.subspaceName) {
|
result['subspaces'] as List<SubspaceModel>;
|
||||||
subspace.tags ??= [];
|
|
||||||
subspace.tags!.add(tag);
|
onSave!(processedTags, processedSubspaces);
|
||||||
assignedTags.add(tag);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onSave!(state.tags,subspaces);
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
child: const Text('Save'),
|
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)),
|
.bodyMedium)),
|
||||||
DataColumn(
|
DataColumn(
|
||||||
numeric: false,
|
numeric: false,
|
||||||
headingRowAlignment: MainAxisAlignment.start,
|
|
||||||
label: Text('Tag',
|
label: Text('Tag',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
@ -462,4 +461,5 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
'subspaces': modifiedSubspaces,
|
'subspaces': modifiedSubspaces,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,14 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
final updatedSubSpaces = List<SubspaceModel>.from(state.subSpaces)
|
||||||
..add(event.subSpace);
|
..add(event.subSpace);
|
||||||
|
|
||||||
|
if (state.duplicates.isNotEmpty) {
|
||||||
|
emit(SubSpaceState(
|
||||||
|
updatedSubSpaces,
|
||||||
|
state.updatedSubSpaceModels,
|
||||||
|
'*Duplicated sub-space name',
|
||||||
|
state.duplicates,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
emit(SubSpaceState(
|
emit(SubSpaceState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
@ -34,6 +42,7 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
// Clear error message
|
// Clear error message
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle RemoveSubSpace Event
|
// Handle RemoveSubSpace Event
|
||||||
@ -45,6 +54,13 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||||
|
updatedSubspaceModels.add(UpdateSubspaceModel(
|
||||||
|
action: Action.delete,
|
||||||
|
uuid: event.subSpace.uuid!,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
final nameOccurrences = <String, int>{};
|
final nameOccurrences = <String, int>{};
|
||||||
for (final subSpace in updatedSubSpaces) {
|
for (final subSpace in updatedSubSpaces) {
|
||||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
final lowerName = subSpace.subspaceName.toLowerCase();
|
||||||
@ -55,19 +71,17 @@ class SubSpaceBloc extends Bloc<SubSpaceEvent, SubSpaceState> {
|
|||||||
.where((entry) => entry.value > 1)
|
.where((entry) => entry.value > 1)
|
||||||
.map((entry) => entry.key)
|
.map((entry) => entry.key)
|
||||||
.toSet();
|
.toSet();
|
||||||
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
final errorMessage =
|
||||||
updatedSubspaceModels.add(UpdateSubspaceModel(
|
updatedDuplicates.isNotEmpty ? '*Duplicated sub-space name' : '';
|
||||||
action: Action.delete,
|
|
||||||
uuid: event.subSpace.uuid!,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(SubSpaceState(updatedSubSpaces, updatedSubspaceModels, '',
|
emit(SubSpaceState(
|
||||||
updatedDuplicates // Clear error message
|
updatedSubSpaces,
|
||||||
|
updatedSubspaceModels,
|
||||||
|
errorMessage,
|
||||||
|
updatedDuplicates,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle UpdateSubSpace Event
|
|
||||||
|
|
||||||
on<UpdateSubSpace>((event, emit) {
|
on<UpdateSubSpace>((event, emit) {
|
||||||
final updatedSubSpaces = state.subSpaces.map((subSpace) {
|
final updatedSubSpaces = state.subSpaces.map((subSpace) {
|
||||||
|
@ -25,22 +25,30 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|||||||
updatedDuplicates,
|
updatedDuplicates,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
// Add subspace if no duplicate exists
|
|
||||||
final updatedSubSpaces =
|
final updatedSubSpaces =
|
||||||
List<SubspaceTemplateModel>.from(state.subSpaces)
|
List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||||
..add(event.subSpace);
|
..add(event.subSpace);
|
||||||
|
|
||||||
|
if (state.duplicates.isNotEmpty) {
|
||||||
|
emit(SubSpaceModelState(
|
||||||
|
updatedSubSpaces,
|
||||||
|
state.updatedSubSpaceModels,
|
||||||
|
'*Duplicated sub-space name',
|
||||||
|
state.duplicates,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
emit(SubSpaceModelState(
|
emit(SubSpaceModelState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
state.updatedSubSpaceModels,
|
state.updatedSubSpaceModels,
|
||||||
'',
|
'',
|
||||||
state.duplicates,
|
state.duplicates,
|
||||||
// Clear error message
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle RemoveSubSpaceModel Event
|
// Handle RemoveSubSpaceModel Event
|
||||||
|
|
||||||
on<RemoveSubSpaceModel>((event, emit) {
|
on<RemoveSubSpaceModel>((event, emit) {
|
||||||
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
final updatedSubSpaces = List<SubspaceTemplateModel>.from(state.subSpaces)
|
||||||
..remove(event.subSpace);
|
..remove(event.subSpace);
|
||||||
@ -48,16 +56,6 @@ class SubSpaceModelBloc extends Bloc<SubSpaceModelEvent, SubSpaceModelState> {
|
|||||||
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
final updatedSubspaceModels = List<UpdateSubspaceTemplateModel>.from(
|
||||||
state.updatedSubSpaceModels,
|
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) {
|
if (event.subSpace.uuid?.isNotEmpty ?? false) {
|
||||||
updatedSubspaceModels.add(UpdateSubspaceTemplateModel(
|
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(
|
emit(SubSpaceModelState(
|
||||||
updatedSubSpaces,
|
updatedSubSpaces,
|
||||||
updatedSubspaceModels,
|
updatedSubspaceModels,
|
||||||
'',
|
errorMessage,
|
||||||
updatedDuplicates,
|
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/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/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/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/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_model.dart';
|
||||||
|
|
||||||
@ -34,6 +36,35 @@ class TagHelper {
|
|||||||
return initialTags;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static Map<ProductModel, int> groupTags(List<BaseTag> tags) {
|
static Map<ProductModel, int> groupTags(List<BaseTag> tags) {
|
||||||
final Map<ProductModel, int> groupedTags = {};
|
final Map<ProductModel, int> groupedTags = {};
|
||||||
for (var tag in tags) {
|
for (var tag in tags) {
|
||||||
@ -79,4 +110,39 @@ class TagHelper {
|
|||||||
))
|
))
|
||||||
.toList();
|
.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,26 +201,30 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
on<ModifySpaceTemplate>((event, emit) async {
|
on<ModifySpaceTemplate>((event, emit) async {
|
||||||
try {
|
try {
|
||||||
final prevSpaceModel = event.spaceTemplate;
|
if (event.spaceTemplate.uuid != null) {
|
||||||
|
final prevSpaceModel =
|
||||||
|
await _api.getSpaceModel(event.spaceTemplate.uuid ?? '');
|
||||||
|
|
||||||
final newSpaceModel = event.updatedSpaceTemplate;
|
final newSpaceModel = event.updatedSpaceTemplate;
|
||||||
String? spaceModelName;
|
String? spaceModelName;
|
||||||
if (prevSpaceModel.modelName != newSpaceModel.modelName) {
|
if (prevSpaceModel?.modelName != newSpaceModel.modelName) {
|
||||||
spaceModelName = newSpaceModel.modelName;
|
spaceModelName = newSpaceModel.modelName;
|
||||||
}
|
}
|
||||||
List<TagModelUpdate> tagUpdates = [];
|
List<TagModelUpdate> tagUpdates = [];
|
||||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||||
final List<SubspaceTemplateModel>? prevSubspaces =
|
final List<SubspaceTemplateModel>? prevSubspaces =
|
||||||
prevSpaceModel.subspaceModels;
|
prevSpaceModel?.subspaceModels;
|
||||||
final List<SubspaceTemplateModel>? newSubspaces =
|
final List<SubspaceTemplateModel>? newSubspaces =
|
||||||
newSpaceModel.subspaceModels;
|
newSpaceModel.subspaceModels;
|
||||||
|
|
||||||
tagUpdates = processTagUpdates(prevSpaceModel.tags, newSpaceModel.tags);
|
tagUpdates =
|
||||||
|
processTagUpdates(prevSpaceModel?.tags, newSpaceModel.tags);
|
||||||
|
|
||||||
if (prevSubspaces != null || newSubspaces != null) {
|
if (prevSubspaces != null || newSubspaces != null) {
|
||||||
if (prevSubspaces != null && newSubspaces != null) {
|
if (prevSubspaces != null && newSubspaces != null) {
|
||||||
for (var prevSubspace in prevSubspaces) {
|
for (var prevSubspace in prevSubspaces) {
|
||||||
final existsInNew = newSubspaces
|
final existsInNew = newSubspaces
|
||||||
.any((newTag) => newTag.uuid == prevSubspace.uuid);
|
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||||
if (!existsInNew) {
|
if (!existsInNew) {
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
action: Action.delete, uuid: prevSubspace.uuid));
|
action: Action.delete, uuid: prevSubspace.uuid));
|
||||||
@ -265,16 +269,6 @@ class CreateSpaceModelBloc
|
|||||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||||
|
|
||||||
if (newSubspace != null) {
|
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 =
|
final List<TagModelUpdate> tagSubspaceUpdates =
|
||||||
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
processTagUpdates(prevSubspace.tags, newSubspace.tags);
|
||||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||||
@ -293,7 +287,7 @@ class CreateSpaceModelBloc
|
|||||||
subspaceModels: subspaceUpdates);
|
subspaceModels: subspaceUpdates);
|
||||||
|
|
||||||
final res = await _api.updateSpaceModel(
|
final res = await _api.updateSpaceModel(
|
||||||
spaceModelBody, prevSpaceModel.uuid ?? '');
|
spaceModelBody, prevSpaceModel?.uuid ?? '');
|
||||||
|
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
emit(CreateSpaceModelLoaded(newSpaceModel));
|
emit(CreateSpaceModelLoaded(newSpaceModel));
|
||||||
@ -301,6 +295,7 @@ class CreateSpaceModelBloc
|
|||||||
event.onUpdate!(event.updatedSpaceTemplate);
|
event.onUpdate!(event.updatedSpaceTemplate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(CreateSpaceModelError('Error creating space model'));
|
emit(CreateSpaceModelError('Error creating space model'));
|
||||||
}
|
}
|
||||||
@ -314,6 +309,18 @@ class CreateSpaceModelBloc
|
|||||||
final List<TagModelUpdate> tagUpdates = [];
|
final List<TagModelUpdate> tagUpdates = [];
|
||||||
final processedTags = <String?>{};
|
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) {
|
if (newTags != null || prevTags != null) {
|
||||||
// Case 1: Tags deleted
|
// Case 1: Tags deleted
|
||||||
if (prevTags != null && newTags != null) {
|
if (prevTags != null && newTags != null) {
|
||||||
@ -334,9 +341,11 @@ class CreateSpaceModelBloc
|
|||||||
|
|
||||||
// Case 2: Tags added
|
// Case 2: Tags added
|
||||||
if (newTags != null) {
|
if (newTags != null) {
|
||||||
|
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
|
||||||
|
|
||||||
for (var newTag in newTags!) {
|
for (var newTag in newTags!) {
|
||||||
// Tag without UUID
|
// Tag without UUID
|
||||||
if ((newTag.uuid == null || newTag.uuid!.isEmpty) &&
|
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
|
||||||
!processedTags.contains(newTag.tag)) {
|
!processedTags.contains(newTag.tag)) {
|
||||||
tagUpdates.add(TagModelUpdate(
|
tagUpdates.add(TagModelUpdate(
|
||||||
action: Action.add,
|
action: Action.add,
|
||||||
|
@ -70,5 +70,5 @@ abstract class ColorsManager {
|
|||||||
static const Color invitedOrangeText = Color(0xFFFFBF00);
|
static const Color invitedOrangeText = Color(0xFFFFBF00);
|
||||||
static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
|
static const Color lightGrayBorderColor = Color(0xB2D5D5D5);
|
||||||
//background: #F8F8F8;
|
//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 ZtoAIcon = 'assets/icons/ztoa_icon.png';
|
||||||
static const String AtoZIcon = 'assets/icons/atoz_icon.png';
|
static const String AtoZIcon = 'assets/icons/atoz_icon.png';
|
||||||
static const String link = 'assets/icons/link.svg';
|
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
|
//user_management.svg
|
||||||
|
Reference in New Issue
Block a user