mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Merge pull request #85 from SyncrowIOT/bugfix/fix-tag-repeat
Fixed tag repeat
This commit is contained in:
@ -4,7 +4,9 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_e
|
|||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart';
|
||||||
|
|
||||||
class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||||
AssignTagBloc() : super(AssignTagInitial()) {
|
final List<String> allTags;
|
||||||
|
|
||||||
|
AssignTagBloc(this.allTags) : super(AssignTagInitial()) {
|
||||||
on<InitializeTags>((event, emit) {
|
on<InitializeTags>((event, emit) {
|
||||||
final initialTags = event.initialTags ?? [];
|
final initialTags = event.initialTags ?? [];
|
||||||
|
|
||||||
@ -16,25 +18,25 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final allTags = <Tag>[];
|
final tags = <Tag>[];
|
||||||
|
|
||||||
for (var selectedProduct in event.addedProducts) {
|
for (var selectedProduct in event.addedProducts) {
|
||||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||||
|
|
||||||
if (selectedProduct.count == 0 ||
|
if (selectedProduct.count == 0 ||
|
||||||
selectedProduct.count <= existingCount) {
|
selectedProduct.count <= existingCount) {
|
||||||
allTags.addAll(initialTags
|
tags.addAll(initialTags
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final missingCount = selectedProduct.count - existingCount;
|
final missingCount = selectedProduct.count - existingCount;
|
||||||
|
|
||||||
allTags.addAll(initialTags
|
tags.addAll(initialTags
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
|
|
||||||
if (missingCount > 0) {
|
if (missingCount > 0) {
|
||||||
allTags.addAll(List.generate(
|
tags.addAll(List.generate(
|
||||||
missingCount,
|
missingCount,
|
||||||
(index) => Tag(
|
(index) => Tag(
|
||||||
tag: '',
|
tag: '',
|
||||||
@ -45,10 +47,14 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: allTags,
|
tags: tags,
|
||||||
isSaveEnabled: _validateTags(allTags),
|
updatedTags: updatedTags,
|
||||||
errorMessage: ''));
|
isSaveEnabled: _validateTags(tags),
|
||||||
|
errorMessage: '',
|
||||||
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
on<UpdateTagEvent>((event, emit) {
|
on<UpdateTagEvent>((event, emit) {
|
||||||
@ -56,10 +62,13 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
|
|
||||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||||
final tags = List<Tag>.from(currentState.tags);
|
final tags = List<Tag>.from(currentState.tags);
|
||||||
tags[event.index].tag = event.tag;
|
tags[event.index] = tags[event.index].copyWith(tag: event.tag);
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: updatedTags,
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: _getValidationError(tags),
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
@ -72,12 +81,15 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||||
final tags = List<Tag>.from(currentState.tags);
|
final tags = List<Tag>.from(currentState.tags);
|
||||||
|
|
||||||
// Use copyWith for immutability
|
// Update the location
|
||||||
tags[event.index] =
|
tags[event.index] =
|
||||||
tags[event.index].copyWith(location: event.location);
|
tags[event.index].copyWith(location: event.location);
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: updatedTags,
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: _getValidationError(tags),
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
@ -92,6 +104,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
|
|
||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: _calculateAvailableTags(allTags, tags),
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: _getValidationError(tags),
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
@ -102,38 +115,37 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
final currentState = state;
|
final currentState = state;
|
||||||
|
|
||||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||||
final updatedTags = List<Tag>.from(currentState.tags)
|
final tags = List<Tag>.from(currentState.tags)
|
||||||
..remove(event.tagToDelete);
|
..remove(event.tagToDelete);
|
||||||
|
|
||||||
|
// Recalculate available tags
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagLoaded(
|
emit(AssignTagLoaded(
|
||||||
tags: updatedTags,
|
tags: tags,
|
||||||
isSaveEnabled: _validateTags(updatedTags),
|
updatedTags: updatedTags,
|
||||||
errorMessage: _getValidationError(updatedTags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
} else {
|
|
||||||
emit(const AssignTagLoaded(
|
|
||||||
tags: [],
|
|
||||||
isSaveEnabled: false,
|
|
||||||
errorMessage: 'Failed to delete tag'));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the tags for duplicates or empty values
|
||||||
bool _validateTags(List<Tag> tags) {
|
bool _validateTags(List<Tag> tags) {
|
||||||
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);
|
||||||
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
return uniqueTags.length == tags.length && !hasEmptyTag;
|
||||||
return isValid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get validation error for duplicate tags
|
||||||
String? _getValidationError(List<Tag> tags) {
|
String? _getValidationError(List<Tag> tags) {
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
final nonEmptyTags = tags
|
||||||
if (hasEmptyTag) {
|
|
||||||
return 'Tags cannot be empty.';
|
|
||||||
}
|
|
||||||
|
|
||||||
final duplicateTags = tags
|
|
||||||
.map((tag) => tag.tag?.trim() ?? '')
|
.map((tag) => tag.tag?.trim() ?? '')
|
||||||
|
.where((tag) => tag.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final duplicateTags = nonEmptyTags
|
||||||
.fold<Map<String, int>>({}, (map, tag) {
|
.fold<Map<String, int>>({}, (map, tag) {
|
||||||
map[tag] = (map[tag] ?? 0) + 1;
|
map[tag] = (map[tag] ?? 0) + 1;
|
||||||
return map;
|
return map;
|
||||||
@ -149,4 +161,15 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> _calculateAvailableTags(List<String> allTags, List<Tag> tags) {
|
||||||
|
final selectedTags = tags
|
||||||
|
.where((tag) => (tag.tag?.trim().isNotEmpty ?? false))
|
||||||
|
.map((tag) => tag.tag!.trim())
|
||||||
|
.toSet();
|
||||||
|
|
||||||
|
final availableTags =
|
||||||
|
allTags.where((tag) => !selectedTags.contains(tag.trim())).toList();
|
||||||
|
return availableTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.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/space_model/models/tag_model.dart';
|
|
||||||
|
|
||||||
abstract class AssignTagState extends Equatable {
|
abstract class AssignTagState extends Equatable {
|
||||||
const AssignTagState();
|
const AssignTagState();
|
||||||
@ -15,17 +14,21 @@ class AssignTagLoading extends AssignTagState {}
|
|||||||
|
|
||||||
class AssignTagLoaded extends AssignTagState {
|
class AssignTagLoaded extends AssignTagState {
|
||||||
final List<Tag> tags;
|
final List<Tag> tags;
|
||||||
|
final List<String> updatedTags;
|
||||||
|
|
||||||
final bool isSaveEnabled;
|
final bool isSaveEnabled;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
const AssignTagLoaded({
|
const AssignTagLoaded({
|
||||||
required this.tags,
|
required this.tags,
|
||||||
required this.isSaveEnabled,
|
required this.isSaveEnabled,
|
||||||
|
required this.updatedTags,
|
||||||
required this.errorMessage,
|
required this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object> get props => [tags, isSaveEnabled, errorMessage ?? ''];
|
List<Object> get props =>
|
||||||
|
[tags, updatedTags, isSaveEnabled, errorMessage ?? ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssignTagError extends AssignTagState {
|
class AssignTagError extends AssignTagState {
|
||||||
|
@ -14,6 +14,7 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_e
|
|||||||
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart';
|
import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.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/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.dart';
|
||||||
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
class AssignTagDialog extends StatelessWidget {
|
class AssignTagDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -47,7 +48,7 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
..add('Main Space');
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagBloc()
|
create: (_) => AssignTagBloc(allTags ?? [])
|
||||||
..add(InitializeTags(
|
..add(InitializeTags(
|
||||||
initialTags: initialTags,
|
initialTags: initialTags,
|
||||||
addedProducts: addedProducts,
|
addedProducts: addedProducts,
|
||||||
@ -119,8 +120,6 @@ 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: [
|
||||||
@ -180,7 +179,9 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
width: double
|
width: double
|
||||||
.infinity, // Ensure full width for dropdown
|
.infinity, // Ensure full width for dropdown
|
||||||
child: DialogTextfieldDropdown(
|
child: DialogTextfieldDropdown(
|
||||||
items: availableTags,
|
key: ValueKey(
|
||||||
|
'dropdown_${Uuid().v4()}_${index}'),
|
||||||
|
items: state.updatedTags,
|
||||||
initialValue: tag.tag,
|
initialValue: tag.tag,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
controller.text = value;
|
controller.text = value;
|
||||||
@ -306,15 +307,4 @@ class AssignTagDialog extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> getAvailableTags(
|
|
||||||
List<String> allTags, List<Tag> currentTags, Tag currentTag) {
|
|
||||||
List<String> availableTagsForTagModel = TagHelper.getAvailableTags<Tag>(
|
|
||||||
allTags: allTags,
|
|
||||||
currentTags: currentTags,
|
|
||||||
currentTag: currentTag,
|
|
||||||
getTag: (tag) => tag.tag ?? '',
|
|
||||||
);
|
|
||||||
return availableTagsForTagModel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model
|
|||||||
|
|
||||||
class AssignTagModelBloc
|
class AssignTagModelBloc
|
||||||
extends Bloc<AssignTagModelEvent, AssignTagModelState> {
|
extends Bloc<AssignTagModelEvent, AssignTagModelState> {
|
||||||
AssignTagModelBloc() : super(AssignTagModelInitial()) {
|
final List<String> allTags;
|
||||||
|
|
||||||
|
AssignTagModelBloc(this.allTags) : super(AssignTagModelInitial()) {
|
||||||
on<InitializeTagModels>((event, emit) {
|
on<InitializeTagModels>((event, emit) {
|
||||||
final initialTags = event.initialTags ?? [];
|
final initialTags = event.initialTags ?? [];
|
||||||
|
|
||||||
@ -17,25 +19,25 @@ class AssignTagModelBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final allTags = <TagModel>[];
|
final tags = <TagModel>[];
|
||||||
|
|
||||||
for (var selectedProduct in event.addedProducts) {
|
for (var selectedProduct in event.addedProducts) {
|
||||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||||
|
|
||||||
if (selectedProduct.count == 0 ||
|
if (selectedProduct.count == 0 ||
|
||||||
selectedProduct.count <= existingCount) {
|
selectedProduct.count <= existingCount) {
|
||||||
allTags.addAll(initialTags
|
tags.addAll(initialTags
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
final missingCount = selectedProduct.count - existingCount;
|
final missingCount = selectedProduct.count - existingCount;
|
||||||
|
|
||||||
allTags.addAll(initialTags
|
tags.addAll(initialTags
|
||||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||||
|
|
||||||
if (missingCount > 0) {
|
if (missingCount > 0) {
|
||||||
allTags.addAll(List.generate(
|
tags.addAll(List.generate(
|
||||||
missingCount,
|
missingCount,
|
||||||
(index) => TagModel(
|
(index) => TagModel(
|
||||||
tag: '',
|
tag: '',
|
||||||
@ -46,9 +48,12 @@ class AssignTagModelBloc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
emit(AssignTagModelLoaded(
|
||||||
tags: allTags,
|
tags: tags,
|
||||||
isSaveEnabled: _validateTags(allTags),
|
updatedTags: updatedTags,
|
||||||
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: ''));
|
errorMessage: ''));
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,9 +62,12 @@ class AssignTagModelBloc
|
|||||||
if (currentState is AssignTagModelLoaded &&
|
if (currentState is AssignTagModelLoaded &&
|
||||||
currentState.tags.isNotEmpty) {
|
currentState.tags.isNotEmpty) {
|
||||||
final tags = List<TagModel>.from(currentState.tags);
|
final tags = List<TagModel>.from(currentState.tags);
|
||||||
tags[event.index].tag = event.tag;
|
tags[event.index] = tags[event.index].copyWith(tag: event.tag);
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
emit(AssignTagModelLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: updatedTags,
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: _getValidationError(tags),
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
@ -77,9 +85,13 @@ class AssignTagModelBloc
|
|||||||
tags[event.index] =
|
tags[event.index] =
|
||||||
tags[event.index].copyWith(location: event.location);
|
tags[event.index].copyWith(location: event.location);
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
emit(AssignTagModelLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: updatedTags,
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -93,6 +105,7 @@ class AssignTagModelBloc
|
|||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
emit(AssignTagModelLoaded(
|
||||||
tags: tags,
|
tags: tags,
|
||||||
|
updatedTags: _calculateAvailableTags(allTags, tags),
|
||||||
isSaveEnabled: _validateTags(tags),
|
isSaveEnabled: _validateTags(tags),
|
||||||
errorMessage: _getValidationError(tags),
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
@ -104,24 +117,22 @@ class AssignTagModelBloc
|
|||||||
|
|
||||||
if (currentState is AssignTagModelLoaded &&
|
if (currentState is AssignTagModelLoaded &&
|
||||||
currentState.tags.isNotEmpty) {
|
currentState.tags.isNotEmpty) {
|
||||||
final updatedTags = List<TagModel>.from(currentState.tags)
|
final tags = List<TagModel>.from(currentState.tags)
|
||||||
..remove(event.tagToDelete);
|
..remove(event.tagToDelete);
|
||||||
|
|
||||||
|
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||||
|
|
||||||
emit(AssignTagModelLoaded(
|
emit(AssignTagModelLoaded(
|
||||||
tags: updatedTags,
|
tags: tags,
|
||||||
isSaveEnabled: _validateTags(updatedTags),
|
updatedTags: updatedTags,
|
||||||
|
isSaveEnabled: _validateTags(tags),
|
||||||
|
errorMessage: _getValidationError(tags),
|
||||||
));
|
));
|
||||||
} else {
|
}
|
||||||
emit(const AssignTagModelLoaded(
|
|
||||||
tags: [],
|
|
||||||
isSaveEnabled: false,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _validateTags(List<TagModel> tags) {
|
bool _validateTags(List<TagModel> tags) {
|
||||||
|
|
||||||
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);
|
||||||
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
||||||
@ -129,14 +140,14 @@ class AssignTagModelBloc
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? _getValidationError(List<TagModel> tags) {
|
String? _getValidationError(List<TagModel> tags) {
|
||||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
|
||||||
if (hasEmptyTag) {
|
|
||||||
return 'Tags cannot be empty.';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for duplicate tags
|
// Check for duplicate tags
|
||||||
final duplicateTags = tags
|
|
||||||
|
final nonEmptyTags = tags
|
||||||
.map((tag) => tag.tag?.trim() ?? '')
|
.map((tag) => tag.tag?.trim() ?? '')
|
||||||
|
.where((tag) => tag.isNotEmpty)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
final duplicateTags = nonEmptyTags
|
||||||
.fold<Map<String, int>>({}, (map, tag) {
|
.fold<Map<String, int>>({}, (map, tag) {
|
||||||
map[tag] = (map[tag] ?? 0) + 1;
|
map[tag] = (map[tag] ?? 0) + 1;
|
||||||
return map;
|
return map;
|
||||||
@ -152,4 +163,16 @@ class AssignTagModelBloc
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> _calculateAvailableTags(
|
||||||
|
List<String> allTags, List<TagModel> tags) {
|
||||||
|
final selectedTags = tags
|
||||||
|
.where((tag) => (tag.tag?.trim().isNotEmpty ?? false))
|
||||||
|
.map((tag) => tag.tag!.trim())
|
||||||
|
.toSet();
|
||||||
|
|
||||||
|
final availableTags =
|
||||||
|
allTags.where((tag) => !selectedTags.contains(tag.trim())).toList();
|
||||||
|
return availableTags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,17 @@ class AssignTagModelLoaded extends AssignTagModelState {
|
|||||||
final bool isSaveEnabled;
|
final bool isSaveEnabled;
|
||||||
final String? errorMessage;
|
final String? errorMessage;
|
||||||
|
|
||||||
|
final List<String> updatedTags;
|
||||||
|
|
||||||
const AssignTagModelLoaded({
|
const AssignTagModelLoaded({
|
||||||
required this.tags,
|
required this.tags,
|
||||||
required this.isSaveEnabled,
|
required this.isSaveEnabled,
|
||||||
|
required this.updatedTags,
|
||||||
this.errorMessage,
|
this.errorMessage,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [tags, isSaveEnabled, errorMessage];
|
List<Object?> get props => [tags, updatedTags, isSaveEnabled, errorMessage];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AssignTagModelError extends AssignTagModelState {
|
class AssignTagModelError extends AssignTagModelState {
|
||||||
|
@ -16,6 +16,7 @@ import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/c
|
|||||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
||||||
import 'package:syncrow_web/utils/color_manager.dart';
|
import 'package:syncrow_web/utils/color_manager.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:uuid/uuid.dart';
|
||||||
|
|
||||||
class AssignTagModelsDialog extends StatelessWidget {
|
class AssignTagModelsDialog extends StatelessWidget {
|
||||||
final List<ProductModel>? products;
|
final List<ProductModel>? products;
|
||||||
@ -56,7 +57,7 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
..add('Main Space');
|
..add('Main Space');
|
||||||
|
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
create: (_) => AssignTagModelBloc()
|
create: (_) => AssignTagModelBloc(allTags ?? [])
|
||||||
..add(InitializeTagModels(
|
..add(InitializeTagModels(
|
||||||
initialTags: initialTags,
|
initialTags: initialTags,
|
||||||
addedProducts: addedProducts,
|
addedProducts: addedProducts,
|
||||||
@ -134,9 +135,6 @@ class AssignTagModelsDialog 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 =
|
|
||||||
TagHelper.getAvailableTagModels(
|
|
||||||
allTags ?? [], state.tags, tag);
|
|
||||||
|
|
||||||
return DataRow(
|
return DataRow(
|
||||||
cells: [
|
cells: [
|
||||||
@ -196,7 +194,9 @@ class AssignTagModelsDialog extends StatelessWidget {
|
|||||||
width: double
|
width: double
|
||||||
.infinity, // Ensure full width for dropdown
|
.infinity, // Ensure full width for dropdown
|
||||||
child: DialogTextfieldDropdown(
|
child: DialogTextfieldDropdown(
|
||||||
items: availableTags,
|
key: ValueKey(
|
||||||
|
'dropdown_${Uuid().v4()}_${index}'),
|
||||||
|
items: state.updatedTags,
|
||||||
initialValue: tag.tag,
|
initialValue: tag.tag,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
controller.text = value;
|
controller.text = value;
|
||||||
|
Reference in New Issue
Block a user