mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Merge branch 'feat/update-space-model' of https://github.com/SyncrowIOT/web into dev
This commit is contained in:
@ -24,6 +24,8 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
final String spaceName;
|
||||
final bool isCreate;
|
||||
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
|
||||
const AddDeviceTypeWidget(
|
||||
{super.key,
|
||||
@ -35,7 +37,8 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
this.allTags,
|
||||
this.spaceTags,
|
||||
this.onSave,
|
||||
required this.spaceName});
|
||||
required this.spaceName,
|
||||
required this.projectTags});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -134,7 +137,8 @@ class AddDeviceTypeWidget extends StatelessWidget {
|
||||
spaceName: spaceName,
|
||||
initialTags: initialTags,
|
||||
title: dialogTitle,
|
||||
onSave: onSave),
|
||||
onSave: onSave,
|
||||
projectTags: projectTags),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -19,8 +19,7 @@ import 'package:syncrow_web/services/space_mana_api.dart';
|
||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action;
|
||||
|
||||
class SpaceManagementBloc
|
||||
extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||
class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementState> {
|
||||
final CommunitySpaceManagementApi _api;
|
||||
final ProductApi _productApi;
|
||||
final SpaceModelManagementApi _spaceModelApi;
|
||||
@ -28,6 +27,7 @@ class SpaceManagementBloc
|
||||
List<ProductModel>? _cachedProducts;
|
||||
List<SpaceTemplateModel>? _cachedSpaceModels;
|
||||
final SpaceTreeBloc _spaceTreeBloc;
|
||||
List<Tag>? _cachedTags;
|
||||
|
||||
SpaceManagementBloc(
|
||||
this._api,
|
||||
@ -54,40 +54,38 @@ class SpaceManagementBloc
|
||||
UpdateSpaceModelCache event, Emitter<SpaceManagementState> emit) async {
|
||||
if (_cachedSpaceModels != null) {
|
||||
_cachedSpaceModels = _cachedSpaceModels!.map((model) {
|
||||
return model.uuid == event.updatedModel.uuid
|
||||
? event.updatedModel
|
||||
: model;
|
||||
return model.uuid == event.updatedModel.uuid ? event.updatedModel : model;
|
||||
}).toList();
|
||||
} else {
|
||||
_cachedSpaceModels = await fetchSpaceModels();
|
||||
}
|
||||
|
||||
await fetchTags();
|
||||
|
||||
emit(SpaceModelLoaded(
|
||||
communities: state is SpaceManagementLoaded
|
||||
? (state as SpaceManagementLoaded).communities
|
||||
: [],
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||
));
|
||||
communities:
|
||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||
allTags: _cachedTags ?? []));
|
||||
}
|
||||
|
||||
void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event,
|
||||
Emitter<SpaceManagementState> emit) async {
|
||||
void _deleteSpaceModelFromCache(
|
||||
DeleteSpaceModelFromCache event, Emitter<SpaceManagementState> emit) async {
|
||||
if (_cachedSpaceModels != null) {
|
||||
_cachedSpaceModels = _cachedSpaceModels!
|
||||
.where((model) => model.uuid != event.deletedUuid)
|
||||
.toList();
|
||||
_cachedSpaceModels =
|
||||
_cachedSpaceModels!.where((model) => model.uuid != event.deletedUuid).toList();
|
||||
} else {
|
||||
_cachedSpaceModels = await fetchSpaceModels();
|
||||
}
|
||||
await fetchTags();
|
||||
|
||||
emit(SpaceModelLoaded(
|
||||
communities: state is SpaceManagementLoaded
|
||||
? (state as SpaceManagementLoaded).communities
|
||||
: [],
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||
));
|
||||
communities:
|
||||
state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [],
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: List.from(_cachedSpaceModels ?? []),
|
||||
allTags: _cachedTags ?? []));
|
||||
}
|
||||
|
||||
void updateCachedSpaceModels(List<SpaceTemplateModel> updatedModels) {
|
||||
@ -112,8 +110,8 @@ class SpaceManagementBloc
|
||||
int page = 1;
|
||||
|
||||
while (hasNext) {
|
||||
final spaceModels = await _spaceModelApi.listSpaceModels(
|
||||
page: page, projectId: projectUuid);
|
||||
final spaceModels =
|
||||
await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid);
|
||||
if (spaceModels.isNotEmpty) {
|
||||
allSpaceModels.addAll(spaceModels);
|
||||
page++;
|
||||
@ -130,6 +128,20 @@ class SpaceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Tag>> fetchTags() async {
|
||||
try {
|
||||
if (_cachedTags != null) {
|
||||
return _cachedTags!;
|
||||
}
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
final allTags = await _spaceModelApi.listTags(projectId: projectUuid);
|
||||
_cachedTags = allTags;
|
||||
return allTags;
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
void _onUpdateCommunity(
|
||||
UpdateCommunityEvent event,
|
||||
Emitter<SpaceManagementState> emit,
|
||||
@ -137,14 +149,13 @@ class SpaceManagementBloc
|
||||
final previousState = state;
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await fetchTags();
|
||||
|
||||
emit(SpaceManagementLoading());
|
||||
final success = await _api.updateCommunity(
|
||||
event.communityUuid, event.name, projectUuid);
|
||||
final success = await _api.updateCommunity(event.communityUuid, event.name, projectUuid);
|
||||
if (success) {
|
||||
if (previousState is SpaceManagementLoaded) {
|
||||
final updatedCommunities =
|
||||
List<CommunityModel>.from(previousState.communities);
|
||||
final updatedCommunities = List<CommunityModel>.from(previousState.communities);
|
||||
for (var community in updatedCommunities) {
|
||||
if (community.uuid == event.communityUuid) {
|
||||
community.name = event.name;
|
||||
@ -157,11 +168,11 @@ class SpaceManagementBloc
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: updatedCommunities,
|
||||
products: previousState.products,
|
||||
selectedCommunity: previousState.selectedCommunity,
|
||||
spaceModels: prevSpaceModels,
|
||||
));
|
||||
communities: updatedCommunities,
|
||||
products: previousState.products,
|
||||
selectedCommunity: previousState.selectedCommunity,
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
}
|
||||
} else {
|
||||
emit(const SpaceManagementError('Failed to update the community.'));
|
||||
@ -189,8 +200,7 @@ class SpaceManagementBloc
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(
|
||||
String communityUuid) async {
|
||||
Future<List<SpaceModel>> _fetchSpacesForCommunity(String communityUuid) async {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
|
||||
return await _api.getSpaceHierarchy(communityUuid, projectUuid);
|
||||
@ -201,7 +211,7 @@ class SpaceManagementBloc
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) async {
|
||||
try {
|
||||
final previousState = state;
|
||||
await fetchTags();
|
||||
|
||||
if (event.communities.isEmpty) {
|
||||
emit(const SpaceManagementError('No communities provided.'));
|
||||
@ -211,33 +221,33 @@ class SpaceManagementBloc
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
emit(BlankState(
|
||||
communities: event.communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
));
|
||||
communities: event.communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
} catch (error) {
|
||||
emit(SpaceManagementError('Error loading communities: $error'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onBlankState(
|
||||
BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
Future<void> _onBlankState(BlankStateEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
try {
|
||||
final previousState = state;
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
||||
await fetchSpaceModels();
|
||||
await fetchTags();
|
||||
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState) {
|
||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
||||
final prevCommunities = (previousState as dynamic).communities ?? [];
|
||||
emit(BlankState(
|
||||
communities: List<CommunityModel>.from(prevCommunities),
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
));
|
||||
communities: List<CommunityModel>.from(prevCommunities),
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -246,8 +256,7 @@ class SpaceManagementBloc
|
||||
|
||||
List<CommunityModel> updatedCommunities = await Future.wait(
|
||||
communities.map((community) async {
|
||||
List<SpaceModel> spaces =
|
||||
await _fetchSpacesForCommunity(community.uuid);
|
||||
List<SpaceModel> spaces = await _fetchSpacesForCommunity(community.uuid);
|
||||
return CommunityModel(
|
||||
uuid: community.uuid,
|
||||
createdAt: community.createdAt,
|
||||
@ -267,6 +276,7 @@ class SpaceManagementBloc
|
||||
spaceModels: prevSpaceModels,
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
allTags: _cachedTags ?? [],
|
||||
));
|
||||
} catch (error) {
|
||||
emit(SpaceManagementError('Error loading communities: $error'));
|
||||
@ -279,21 +289,20 @@ class SpaceManagementBloc
|
||||
) async {
|
||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||
_onloadProducts();
|
||||
|
||||
await fetchTags();
|
||||
// Wait until `communityList` is loaded
|
||||
List<CommunityModel> communities = await _waitForCommunityList(spaceBloc);
|
||||
|
||||
// Fetch space models after communities are available
|
||||
final prevSpaceModels = await fetchSpaceModels();
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
));
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
}
|
||||
|
||||
Future<List<CommunityModel>> _waitForCommunityList(
|
||||
SpaceTreeBloc spaceBloc) async {
|
||||
Future<List<CommunityModel>> _waitForCommunityList(SpaceTreeBloc spaceBloc) async {
|
||||
// Check if communityList is already populated
|
||||
if (spaceBloc.state.communityList.isNotEmpty) {
|
||||
return spaceBloc.state.communityList;
|
||||
@ -320,8 +329,7 @@ class SpaceManagementBloc
|
||||
emit(SpaceManagementLoading());
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
|
||||
final success =
|
||||
await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||
final success = await _api.deleteCommunity(event.communityUuid, projectUuid);
|
||||
if (success) {
|
||||
// add(LoadCommunityAndSpacesEvent());
|
||||
} else {
|
||||
@ -342,14 +350,13 @@ class SpaceManagementBloc
|
||||
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
|
||||
CommunityModel? newCommunity = await _api.createCommunity(
|
||||
event.name, event.description, projectUuid);
|
||||
await fetchTags();
|
||||
CommunityModel? newCommunity =
|
||||
await _api.createCommunity(event.name, event.description, projectUuid);
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
if (newCommunity != null) {
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState) {
|
||||
if (previousState is SpaceManagementLoaded || previousState is BlankState) {
|
||||
final prevCommunities = List<CommunityModel>.from(
|
||||
(previousState as dynamic).communities,
|
||||
);
|
||||
@ -357,11 +364,13 @@ class SpaceManagementBloc
|
||||
_spaceTreeBloc.add(OnCommunityAdded(newCommunity));
|
||||
|
||||
emit(SpaceManagementLoaded(
|
||||
spaceModels: prevSpaceModels,
|
||||
communities: updatedCommunities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: newCommunity,
|
||||
selectedSpace: null));
|
||||
spaceModels: prevSpaceModels,
|
||||
communities: updatedCommunities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: newCommunity,
|
||||
selectedSpace: null,
|
||||
allTags: _cachedTags ?? [],
|
||||
));
|
||||
}
|
||||
} else {
|
||||
emit(const SpaceManagementError('Error creating community'));
|
||||
@ -401,11 +410,12 @@ class SpaceManagementBloc
|
||||
required Emitter<SpaceManagementState> emit,
|
||||
CommunityModel? selectedCommunity,
|
||||
SpaceModel? selectedSpace,
|
||||
}) {
|
||||
}) async {
|
||||
final previousState = state;
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
await fetchTags();
|
||||
if (previousState is SpaceManagementLoaded ||
|
||||
previousState is BlankState ||
|
||||
previousState is SpaceModelLoaded) {
|
||||
@ -421,7 +431,8 @@ class SpaceManagementBloc
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
spaceModels: spaceModels));
|
||||
spaceModels: spaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error updating state: $e'));
|
||||
@ -436,8 +447,7 @@ class SpaceManagementBloc
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
final updatedSpaces =
|
||||
await saveSpacesHierarchically(event.spaces, event.communityUuid);
|
||||
final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid);
|
||||
|
||||
final allSpaces = await _fetchSpacesForCommunity(event.communityUuid);
|
||||
|
||||
@ -467,20 +477,21 @@ class SpaceManagementBloc
|
||||
Emitter<SpaceManagementState> emit,
|
||||
) async {
|
||||
var prevSpaceModels = await fetchSpaceModels();
|
||||
|
||||
await fetchTags();
|
||||
final communities = List<CommunityModel>.from(previousState.communities);
|
||||
|
||||
for (var community in communities) {
|
||||
if (community.uuid == communityUuid) {
|
||||
community.spaces = allSpaces;
|
||||
_spaceTreeBloc.add(OnCommunityUpdated(community));
|
||||
_spaceTreeBloc.add(InitialEvent());
|
||||
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: community,
|
||||
selectedSpace: null,
|
||||
spaceModels: prevSpaceModels));
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -511,8 +522,7 @@ class SpaceManagementBloc
|
||||
if (space.uuid != null && space.uuid!.isNotEmpty) {
|
||||
List<TagModelUpdate> tagUpdates = [];
|
||||
|
||||
final prevSpace =
|
||||
await _api.getSpace(communityUuid, space.uuid!, projectUuid);
|
||||
final prevSpace = await _api.getSpace(communityUuid, space.uuid!, projectUuid);
|
||||
final List<UpdateSubspaceTemplateModel> subspaceUpdates = [];
|
||||
final List<SubspaceModel>? prevSubspaces = prevSpace?.subspaces;
|
||||
final List<SubspaceModel>? newSubspaces = space.subspaces;
|
||||
@ -522,19 +532,17 @@ class SpaceManagementBloc
|
||||
if (prevSubspaces != null || newSubspaces != null) {
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final existsInNew = newSubspaces
|
||||
.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||
final existsInNew =
|
||||
newSubspaces.any((subspace) => subspace.uuid == prevSubspace.uuid);
|
||||
if (!existsInNew) {
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: custom_action.Action.delete,
|
||||
uuid: prevSubspace.uuid));
|
||||
action: custom_action.Action.delete, uuid: prevSubspace.uuid));
|
||||
}
|
||||
}
|
||||
} else if (prevSubspaces != null && newSubspaces == null) {
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
subspaceUpdates.add(UpdateSubspaceTemplateModel(
|
||||
action: custom_action.Action.delete,
|
||||
uuid: prevSubspace.uuid));
|
||||
action: custom_action.Action.delete, uuid: prevSubspace.uuid));
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,7 +556,7 @@ class SpaceManagementBloc
|
||||
for (var tag in newSubspace.tags!) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.add,
|
||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
||||
newTagUuid: tag.uuid == '' ? null : tag.uuid,
|
||||
tag: tag.tag,
|
||||
productUuid: tag.product?.uuid));
|
||||
}
|
||||
@ -562,9 +570,7 @@ class SpaceManagementBloc
|
||||
}
|
||||
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
final newSubspaceMap = {
|
||||
for (var subspace in newSubspaces) subspace.uuid: subspace
|
||||
};
|
||||
final newSubspaceMap = {for (var subspace in newSubspaces) subspace.uuid: subspace};
|
||||
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
final newSubspace = newSubspaceMap[prevSubspace.uuid];
|
||||
@ -601,10 +607,8 @@ class SpaceManagementBloc
|
||||
: [];
|
||||
|
||||
final createSubspaceBodyModels = space.subspaces?.map((subspace) {
|
||||
final tagBodyModels = subspace.tags
|
||||
?.map((tag) => tag.toCreateTagBodyModel())
|
||||
.toList() ??
|
||||
[];
|
||||
final tagBodyModels =
|
||||
subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? [];
|
||||
return CreateSubspaceModel()
|
||||
..subspaceName = subspace.subspaceName
|
||||
..tags = tagBodyModels;
|
||||
@ -657,12 +661,11 @@ class SpaceManagementBloc
|
||||
return result.toList(); // Convert back to a list
|
||||
}
|
||||
|
||||
void _onLoadSpaceModel(
|
||||
SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
void _onLoadSpaceModel(SpaceModelLoadEvent event, Emitter<SpaceManagementState> emit) async {
|
||||
emit(SpaceManagementLoading());
|
||||
|
||||
try {
|
||||
var prevState = state;
|
||||
await fetchTags();
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
var spaceBloc = event.context.read<SpaceTreeBloc>();
|
||||
List<CommunityModel> communities = spaceBloc.state.communityList;
|
||||
@ -674,8 +677,7 @@ class SpaceManagementBloc
|
||||
|
||||
List<CommunityModel> updatedCommunities = await Future.wait(
|
||||
communities.map((community) async {
|
||||
List<SpaceModel> spaces =
|
||||
await _fetchSpacesForCommunity(community.uuid);
|
||||
List<SpaceModel> spaces = await _fetchSpacesForCommunity(community.uuid);
|
||||
return CommunityModel(
|
||||
uuid: community.uuid,
|
||||
createdAt: community.createdAt,
|
||||
@ -692,10 +694,10 @@ class SpaceManagementBloc
|
||||
}
|
||||
|
||||
emit(SpaceModelLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
));
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: prevSpaceModels,
|
||||
allTags: _cachedTags ?? []));
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
||||
}
|
||||
@ -713,7 +715,7 @@ class SpaceManagementBloc
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.add,
|
||||
tag: newTag.tag,
|
||||
uuid: newTag.uuid,
|
||||
newTagUuid: newTag.uuid,
|
||||
productUuid: newTag.product?.uuid,
|
||||
));
|
||||
}
|
||||
@ -724,17 +726,14 @@ class SpaceManagementBloc
|
||||
// Case 1: Tags deleted
|
||||
if (prevTags != null && newTags != null) {
|
||||
for (var prevTag in prevTags) {
|
||||
final existsInNew =
|
||||
newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||
final existsInNew = newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||
if (!existsInNew) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||
tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||
}
|
||||
}
|
||||
} else if (prevTags != null && newTags == null) {
|
||||
for (var prevTag in prevTags) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||
tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid));
|
||||
}
|
||||
}
|
||||
|
||||
@ -749,7 +748,7 @@ class SpaceManagementBloc
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.add,
|
||||
tag: newTag.tag,
|
||||
uuid: newTag.uuid == '' ? null : newTag.uuid,
|
||||
newTagUuid: newTag.uuid == '' ? null : newTag.uuid,
|
||||
productUuid: newTag.product?.uuid));
|
||||
processedTags.add(newTag.tag);
|
||||
}
|
||||
@ -766,6 +765,7 @@ class SpaceManagementBloc
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: custom_action.Action.update,
|
||||
uuid: newTag.uuid,
|
||||
newTagUuid: newTag.uuid,
|
||||
tag: newTag.tag,
|
||||
));
|
||||
} else {}
|
||||
|
@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_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/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
|
||||
abstract class SpaceManagementState extends Equatable {
|
||||
@ -21,22 +22,25 @@ class SpaceManagementLoaded extends SpaceManagementState {
|
||||
CommunityModel? selectedCommunity;
|
||||
SpaceModel? selectedSpace;
|
||||
List<SpaceTemplateModel>? spaceModels;
|
||||
List<Tag> allTags;
|
||||
|
||||
SpaceManagementLoaded(
|
||||
{required this.communities,
|
||||
required this.products,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
this.spaceModels});
|
||||
this.spaceModels,
|
||||
required this.allTags});
|
||||
}
|
||||
|
||||
class BlankState extends SpaceManagementState {
|
||||
final List<CommunityModel> communities;
|
||||
final List<ProductModel> products;
|
||||
List<SpaceTemplateModel>? spaceModels;
|
||||
final List<Tag> allTags;
|
||||
|
||||
BlankState(
|
||||
{required this.communities, required this.products, this.spaceModels});
|
||||
{required this.communities, required this.products, this.spaceModels, required this.allTags});
|
||||
}
|
||||
|
||||
class SpaceCreationSuccess extends SpaceManagementState {
|
||||
@ -61,14 +65,14 @@ class SpaceModelLoaded extends SpaceManagementState {
|
||||
List<SpaceTemplateModel> spaceModels;
|
||||
final List<ProductModel> products;
|
||||
final List<CommunityModel> communities;
|
||||
final List<Tag> allTags;
|
||||
|
||||
SpaceModelLoaded({
|
||||
required this.communities,
|
||||
required this.products,
|
||||
required this.spaceModels,
|
||||
});
|
||||
SpaceModelLoaded(
|
||||
{required this.communities,
|
||||
required this.products,
|
||||
required this.spaceModels,
|
||||
required this.allTags});
|
||||
|
||||
@override
|
||||
List<Object> get props => [communities, products, spaceModels];
|
||||
List<Object> get props => [communities, products, spaceModels, allTags];
|
||||
}
|
||||
|
||||
|
@ -25,22 +25,21 @@ class Tag extends BaseTag {
|
||||
return Tag(
|
||||
uuid: json['uuid'] ?? '',
|
||||
internalId: internalId,
|
||||
tag: json['tag'] ?? '',
|
||||
product: json['product'] != null
|
||||
? ProductModel.fromMap(json['product'])
|
||||
: null,
|
||||
tag: json['name'] ?? '',
|
||||
product: json['product'] != null ? ProductModel.fromMap(json['product']) : null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Tag copyWith({
|
||||
String? uuid,
|
||||
String? tag,
|
||||
ProductModel? product,
|
||||
String? location,
|
||||
String? internalId,
|
||||
}) {
|
||||
return Tag(
|
||||
uuid: uuid,
|
||||
uuid: uuid ?? this.uuid,
|
||||
tag: tag ?? this.tag,
|
||||
product: product ?? this.product,
|
||||
location: location ?? this.location,
|
||||
@ -60,7 +59,7 @@ class Tag extends BaseTag {
|
||||
extension TagModelExtensions on Tag {
|
||||
TagBodyModel toTagBodyModel() {
|
||||
return TagBodyModel()
|
||||
..uuid = uuid ?? ''
|
||||
..uuid = uuid
|
||||
..tag = tag ?? ''
|
||||
..productUuid = product?.uuid;
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart';
|
||||
@ -62,9 +60,9 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
selectedSpace: null,
|
||||
products: state.products,
|
||||
shouldNavigateToSpaceModelPage: false,
|
||||
projectTags: state.allTags,
|
||||
);
|
||||
} else if (state is SpaceManagementLoaded) {
|
||||
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
selectedCommunity: state.selectedCommunity,
|
||||
@ -72,6 +70,7 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: false,
|
||||
projectTags: state.allTags,
|
||||
);
|
||||
} else if (state is SpaceModelLoaded) {
|
||||
return LoadedSpaceView(
|
||||
@ -79,6 +78,7 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: true,
|
||||
projectTags: state.allTags,
|
||||
);
|
||||
} else if (state is SpaceManagementError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
|
@ -36,16 +36,17 @@ class CommunityStructureArea extends StatefulWidget {
|
||||
final List<CommunityModel> communities;
|
||||
final List<SpaceModel> spaces;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
CommunityStructureArea({
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
required this.communities,
|
||||
this.products,
|
||||
required this.spaces,
|
||||
this.onSpaceSelected,
|
||||
this.spaceModels,
|
||||
});
|
||||
CommunityStructureArea(
|
||||
{this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
required this.communities,
|
||||
this.products,
|
||||
required this.spaces,
|
||||
this.onSpaceSelected,
|
||||
this.spaceModels,
|
||||
required this.projectTags});
|
||||
|
||||
@override
|
||||
_CommunityStructureAreaState createState() => _CommunityStructureAreaState();
|
||||
@ -64,8 +65,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
_nameController = TextEditingController(
|
||||
text: widget.selectedCommunity?.name ?? '',
|
||||
@ -92,14 +92,12 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
if (oldWidget.spaces != widget.spaces) {
|
||||
setState(() {
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
_adjustCanvasSizeForSpaces();
|
||||
});
|
||||
}
|
||||
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||
widget.selectedSpace != null) {
|
||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_moveToSpace(widget.selectedSpace!);
|
||||
});
|
||||
@ -182,8 +180,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
connection, widget.selectedSpace)
|
||||
? 1.0
|
||||
: 0.3, // Adjust opacity
|
||||
child: CustomPaint(
|
||||
painter: CurvedLinePainter([connection])),
|
||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
||||
),
|
||||
for (var entry in spaces.asMap().entries)
|
||||
if (entry.value.status != SpaceStatus.deleted &&
|
||||
@ -193,15 +190,12 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
top: entry.value.position.dy,
|
||||
child: SpaceCardWidget(
|
||||
index: entry.key,
|
||||
onButtonTap: (int index, Offset newPosition,
|
||||
String direction) {
|
||||
_showCreateSpaceDialog(
|
||||
screenSize,
|
||||
position:
|
||||
spaces[index].position + newPosition,
|
||||
parentIndex: index,
|
||||
direction: direction,
|
||||
);
|
||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
||||
_showCreateSpaceDialog(screenSize,
|
||||
position: spaces[index].position + newPosition,
|
||||
parentIndex: index,
|
||||
direction: direction,
|
||||
projectTags: widget.projectTags);
|
||||
},
|
||||
position: entry.value.position,
|
||||
isHovered: entry.value.isHovered,
|
||||
@ -211,9 +205,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
_updateNodePosition(entry.value, newPosition);
|
||||
},
|
||||
buildSpaceContainer: (int index) {
|
||||
final bool isHighlighted =
|
||||
SpaceHelper.isHighlightedSpace(
|
||||
spaces[index], widget.selectedSpace);
|
||||
final bool isHighlighted = SpaceHelper.isHighlightedSpace(
|
||||
spaces[index], widget.selectedSpace);
|
||||
|
||||
return Opacity(
|
||||
opacity: isHighlighted ? 1.0 : 0.3,
|
||||
@ -238,7 +231,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
onTap: () {
|
||||
_showCreateSpaceDialog(screenSize,
|
||||
canvasHeight: canvasHeight,
|
||||
canvasWidth: canvasWidth);
|
||||
canvasWidth: canvasWidth,
|
||||
projectTags: widget.projectTags);
|
||||
},
|
||||
),
|
||||
),
|
||||
@ -292,26 +286,22 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
int? parentIndex,
|
||||
String? direction,
|
||||
double? canvasWidth,
|
||||
double? canvasHeight}) {
|
||||
double? canvasHeight,
|
||||
required List<Tag> projectTags}) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
spaceModels: widget.spaceModels,
|
||||
allTags:
|
||||
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
projectTags: projectTags,
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Set the first space in the center or use passed position
|
||||
Offset centerPosition =
|
||||
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
Offset centerPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
name: name,
|
||||
icon: icon,
|
||||
@ -356,21 +346,17 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
spaceModels: widget.spaceModels,
|
||||
name: widget.selectedSpace!.name,
|
||||
icon: widget.selectedSpace!.icon,
|
||||
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||
widget.selectedSpace?.parent?.internalId, spaces),
|
||||
projectTags: widget.projectTags,
|
||||
parentSpace:
|
||||
SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces),
|
||||
editSpace: widget.selectedSpace,
|
||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||
tags: widget.selectedSpace?.tags,
|
||||
subspaces: widget.selectedSpace?.subspaces,
|
||||
isEdit: true,
|
||||
allTags: TagHelper.getAllTagValues(
|
||||
widget.communities, widget.spaceModels),
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
widget.selectedSpace!.name = name;
|
||||
@ -380,8 +366,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
widget.selectedSpace!.tags = tags;
|
||||
|
||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
||||
widget.selectedSpace!.status =
|
||||
SpaceStatus.modified; // Mark as modified
|
||||
widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified
|
||||
}
|
||||
|
||||
for (var space in spaces) {
|
||||
@ -411,8 +396,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
List<SpaceModel> result = [];
|
||||
|
||||
void flatten(SpaceModel space) {
|
||||
if (space.status == SpaceStatus.deleted ||
|
||||
space.status == SpaceStatus.parentDeleted) {
|
||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
||||
return;
|
||||
}
|
||||
result.add(space);
|
||||
@ -527,16 +511,13 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: space),
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
||||
);
|
||||
}
|
||||
|
||||
void _deselectSpace(BuildContext context) {
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectSpaceEvent(
|
||||
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||
);
|
||||
}
|
||||
|
||||
@ -625,19 +606,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
const double horizontalGap = 200.0;
|
||||
const double verticalGap = 100.0;
|
||||
|
||||
SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition,
|
||||
SpaceModel? duplicatedParent) {
|
||||
Offset newPosition =
|
||||
Offset(parentPosition.dx + horizontalGap, original.position.dy);
|
||||
SpaceModel duplicateRecursive(
|
||||
SpaceModel original, Offset parentPosition, SpaceModel? duplicatedParent) {
|
||||
Offset newPosition = Offset(parentPosition.dx + horizontalGap, original.position.dy);
|
||||
|
||||
while (spaces.any((s) =>
|
||||
(s.position - newPosition).distance < horizontalGap &&
|
||||
s.status != SpaceStatus.deleted)) {
|
||||
(s.position - newPosition).distance < horizontalGap && s.status != SpaceStatus.deleted)) {
|
||||
newPosition += Offset(horizontalGap, 0);
|
||||
}
|
||||
|
||||
final duplicatedName =
|
||||
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||
|
||||
final List<SubspaceModel>? duplicatedSubspaces;
|
||||
final List<Tag>? duplicatedTags;
|
||||
@ -681,8 +659,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
if (original.parent != null && duplicatedParent == null) {
|
||||
final originalParent = original.parent!;
|
||||
final duplicatedParent =
|
||||
originalToDuplicate[originalParent] ?? originalParent;
|
||||
final duplicatedParent = originalToDuplicate[originalParent] ?? originalParent;
|
||||
|
||||
final parentConnection = Connection(
|
||||
startSpace: duplicatedParent,
|
||||
@ -698,8 +675,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
final childrenWithDownDirection = original.children
|
||||
.where((child) =>
|
||||
child.incomingConnection?.direction == "down" &&
|
||||
child.status != SpaceStatus.deleted)
|
||||
child.incomingConnection?.direction == "down" && child.status != SpaceStatus.deleted)
|
||||
.toList();
|
||||
|
||||
Offset childStartPosition = childrenWithDownDirection.length == 1
|
||||
@ -707,8 +683,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
: newPosition + Offset(0, verticalGap);
|
||||
|
||||
for (final child in original.children) {
|
||||
final isDownDirection =
|
||||
child.incomingConnection?.direction == "down" ?? false;
|
||||
final isDownDirection = child.incomingConnection?.direction == "down" ?? false;
|
||||
|
||||
if (isDownDirection && childrenWithDownDirection.length == 1) {
|
||||
childStartPosition = duplicated.position + Offset(0, verticalGap);
|
||||
@ -716,8 +691,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
childStartPosition = duplicated.position + Offset(horizontalGap, 0);
|
||||
}
|
||||
|
||||
final duplicatedChild =
|
||||
duplicateRecursive(child, childStartPosition, duplicated);
|
||||
final duplicatedChild = duplicateRecursive(child, childStartPosition, duplicated);
|
||||
duplicated.children.add(duplicatedChild);
|
||||
childStartPosition += Offset(0, verticalGap);
|
||||
}
|
||||
@ -728,8 +702,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
if (space.parent == null) {
|
||||
duplicateRecursive(space, space.position, null);
|
||||
} else {
|
||||
final duplicatedParent =
|
||||
originalToDuplicate[space.parent!] ?? space.parent!;
|
||||
final duplicatedParent = originalToDuplicate[space.parent!] ?? space.parent!;
|
||||
duplicateRecursive(space, space.position, duplicatedParent);
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
final List<Tag>? tags;
|
||||
final List<String>? allTags;
|
||||
final SpaceTemplateModel? currentSpaceModel;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
@ -57,7 +58,8 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
this.spaceModels,
|
||||
this.subspaces,
|
||||
this.tags,
|
||||
this.currentSpaceModel});
|
||||
this.currentSpaceModel,
|
||||
required this.projectTags});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
@ -80,10 +82,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
super.initState();
|
||||
selectedIcon = widget.icon ?? Assets.location;
|
||||
nameController = TextEditingController(text: widget.name ?? '');
|
||||
selectedProducts =
|
||||
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled =
|
||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
if (widget.currentSpaceModel != null) {
|
||||
subspaces = [];
|
||||
tags = [];
|
||||
@ -96,15 +96,13 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||
subspaces != null && subspaces!.isNotEmpty);
|
||||
bool isSpaceModelDisabled =
|
||||
(tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty);
|
||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
return AlertDialog(
|
||||
title: widget.isEdit
|
||||
? const Text('Edit Space')
|
||||
: const Text('Create New Space'),
|
||||
title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.5,
|
||||
@ -178,7 +176,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (SpaceHelper.isNameConflict(value, widget.parentSpace, widget.editSpace)) {
|
||||
if (SpaceHelper.isNameConflict(
|
||||
value, widget.parentSpace, widget.editSpace)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
@ -245,9 +244,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
padding: EdgeInsets.zero,
|
||||
),
|
||||
onPressed: () {
|
||||
isSpaceModelDisabled
|
||||
? null
|
||||
: _showLinkSpaceModelDialog(context);
|
||||
isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
svgAssets: Assets.link,
|
||||
@ -257,8 +254,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
)
|
||||
: Container(
|
||||
width: screenWidth * 0.25,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0, horizontal: 16.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
@ -273,8 +269,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium!
|
||||
.copyWith(
|
||||
color: ColorsManager.spaceColor),
|
||||
.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -343,8 +338,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
onPressed: () async {
|
||||
isTagsAndSubspaceModelDisabled
|
||||
? null
|
||||
: _showSubSpaceDialog(context, enteredName,
|
||||
[], false, widget.products, subspaces);
|
||||
: _showSubSpaceDialog(
|
||||
context, enteredName, [], false, widget.products, subspaces);
|
||||
},
|
||||
child: ButtonContentWidget(
|
||||
icon: Icons.add,
|
||||
@ -371,22 +366,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null)
|
||||
...subspaces!.map((subspace) {
|
||||
return Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SubspaceNameDisplayWidget(
|
||||
text: subspace.subspaceName,
|
||||
validateName: (updatedName) {
|
||||
bool nameExists =
|
||||
subspaces!.any((s) {
|
||||
bool isSameId = s.internalId ==
|
||||
subspace.internalId;
|
||||
bool isSameName = s.subspaceName
|
||||
.trim()
|
||||
.toLowerCase() ==
|
||||
updatedName
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
bool nameExists = subspaces!.any((s) {
|
||||
bool isSameId = s.internalId == subspace.internalId;
|
||||
bool isSameName =
|
||||
s.subspaceName.trim().toLowerCase() ==
|
||||
updatedName.trim().toLowerCase();
|
||||
|
||||
return !isSameId && isSameName;
|
||||
});
|
||||
@ -395,8 +384,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
},
|
||||
onNameChanged: (updatedName) {
|
||||
setState(() {
|
||||
subspace.subspaceName =
|
||||
updatedName;
|
||||
subspace.subspaceName = updatedName;
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -405,8 +393,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
}),
|
||||
EditChip(
|
||||
onTap: () async {
|
||||
_showSubSpaceDialog(context, enteredName,
|
||||
[], true, widget.products, subspaces);
|
||||
_showSubSpaceDialog(context, enteredName, [], true,
|
||||
widget.products, subspaces);
|
||||
},
|
||||
)
|
||||
],
|
||||
@ -415,9 +403,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
(tags?.isNotEmpty == true ||
|
||||
subspaces?.any((subspace) =>
|
||||
subspace.tags?.isNotEmpty == true) ==
|
||||
true)
|
||||
subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true)
|
||||
? SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: Container(
|
||||
@ -437,16 +423,14 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
// Combine tags from spaceModel and subspaces
|
||||
...TagHelper.groupTags([
|
||||
...?tags,
|
||||
...?subspaces?.expand(
|
||||
(subspace) => subspace.tags ?? [])
|
||||
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
||||
]).entries.map(
|
||||
(entry) => Chip(
|
||||
avatar: SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.asset(
|
||||
entry.key.icon ??
|
||||
'assets/icons/gateway.svg',
|
||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
@ -455,15 +439,11 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(
|
||||
color: ColorsManager
|
||||
.spaceColor),
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor:
|
||||
ColorsManager.whiteColors,
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(16),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
@ -472,23 +452,21 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
),
|
||||
|
||||
EditChip(onTap: () async {
|
||||
final result = await showDialog(
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) => AssignTagDialog(
|
||||
products: widget.products,
|
||||
subspaces: subspaces,
|
||||
allTags: widget.allTags,
|
||||
addedProducts: TagHelper
|
||||
.createInitialSelectedProductsForTags(
|
||||
addedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
tags ?? [], subspaces),
|
||||
title: 'Edit Device',
|
||||
initialTags:
|
||||
TagHelper.generateInitialForTags(
|
||||
spaceTags: tags,
|
||||
subspaces: subspaces),
|
||||
initialTags: TagHelper.generateInitialForTags(
|
||||
spaceTags: tags, subspaces: subspaces),
|
||||
spaceName: widget.name ?? '',
|
||||
onSave:
|
||||
(updatedTags, updatedSubspaces) {
|
||||
projectTags: widget.projectTags,
|
||||
onSave: (updatedTags, updatedSubspaces) {
|
||||
setState(() {
|
||||
tags = updatedTags;
|
||||
subspaces = updatedSubspaces;
|
||||
@ -547,25 +525,17 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
String newName = enteredName.isNotEmpty
|
||||
? enteredName
|
||||
: (widget.name ?? '');
|
||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
||||
if (newName.isNotEmpty) {
|
||||
widget.onCreateSpace(
|
||||
newName,
|
||||
selectedIcon,
|
||||
selectedProducts,
|
||||
selectedSpaceModel,
|
||||
subspaces,
|
||||
tags);
|
||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts,
|
||||
selectedSpaceModel, subspaces, tags);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
},
|
||||
borderRadius: 10,
|
||||
backgroundColor: isOkButtonEnabled
|
||||
? ColorsManager.secondaryColor
|
||||
: ColorsManager.grayColor,
|
||||
backgroundColor:
|
||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
||||
foregroundColor: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
@ -592,7 +562,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void _showLinkSpaceModelDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
@ -613,13 +582,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showSubSpaceDialog(
|
||||
BuildContext context,
|
||||
String name,
|
||||
final List<Tag>? spaceTags,
|
||||
bool isEdit,
|
||||
List<ProductModel>? products,
|
||||
final List<SubspaceModel>? existingSubSpaces) {
|
||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
@ -634,12 +598,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
final List<Tag> tagsToAppendToSpace = [];
|
||||
|
||||
if (slectedSubspaces != null) {
|
||||
final updatedIds =
|
||||
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||
final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet();
|
||||
if (existingSubSpaces != null) {
|
||||
final deletedSubspaces = existingSubSpaces
|
||||
.where((s) => !updatedIds.contains(s.internalId))
|
||||
.toList();
|
||||
final deletedSubspaces =
|
||||
existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList();
|
||||
for (var s in deletedSubspaces) {
|
||||
if (s.tags != null) {
|
||||
tagsToAppendToSpace.addAll(s.tags!);
|
||||
@ -659,20 +621,20 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||
List<ProductModel>? products) {
|
||||
void _showTagCreateDialog(
|
||||
BuildContext context, String name, bool isEdit, List<ProductModel>? products) {
|
||||
isEdit
|
||||
? showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AssignTagDialog(
|
||||
title: 'Edit Device',
|
||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
allTags: widget.allTags,
|
||||
projectTags: widget.projectTags,
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
@ -682,8 +644,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
@ -705,9 +666,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
spaceTags: tags,
|
||||
isCreate: true,
|
||||
allTags: widget.allTags,
|
||||
projectTags: widget.projectTags,
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
tags, subspaces),
|
||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
@ -717,8 +678,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_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/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart';
|
||||
@ -20,16 +21,17 @@ class LoadedSpaceView extends StatefulWidget {
|
||||
final List<ProductModel>? products;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
final bool shouldNavigateToSpaceModelPage;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const LoadedSpaceView({
|
||||
super.key,
|
||||
required this.communities,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
this.spaceModels,
|
||||
required this.shouldNavigateToSpaceModelPage,
|
||||
});
|
||||
const LoadedSpaceView(
|
||||
{super.key,
|
||||
required this.communities,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
this.spaceModels,
|
||||
required this.shouldNavigateToSpaceModelPage,
|
||||
required this.projectTags});
|
||||
|
||||
@override
|
||||
_LoadedSpaceViewState createState() => _LoadedSpaceViewState();
|
||||
@ -93,6 +95,7 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
||||
child: SpaceModelPage(
|
||||
products: widget.products,
|
||||
onSpaceModelsUpdated: _onSpaceModelsUpdated,
|
||||
projectTags: widget.projectTags,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -113,6 +116,7 @@ class _LoadedSpaceViewState extends State<LoadedSpaceView> {
|
||||
products: widget.products,
|
||||
communities: widget.communities,
|
||||
spaceModels: _spaceModels,
|
||||
projectTags: widget.projectTags,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -4,17 +4,16 @@ 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';
|
||||
|
||||
class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final List<String> allTags;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
AssignTagBloc(this.allTags) : super(AssignTagInitial()) {
|
||||
AssignTagBloc(this.projectTags) : super(AssignTagInitial()) {
|
||||
on<InitializeTags>((event, emit) {
|
||||
final initialTags = event.initialTags ?? [];
|
||||
|
||||
final existingTagCounts = <String, int>{};
|
||||
for (var tag in initialTags) {
|
||||
if (tag.product != null) {
|
||||
existingTagCounts[tag.product!.uuid] =
|
||||
(existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||
existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,17 +22,14 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
for (var selectedProduct in event.addedProducts) {
|
||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||
|
||||
if (selectedProduct.count == 0 ||
|
||||
selectedProduct.count <= existingCount) {
|
||||
tags.addAll(initialTags
|
||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) {
|
||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
continue;
|
||||
}
|
||||
|
||||
final missingCount = selectedProduct.count - existingCount;
|
||||
|
||||
tags.addAll(initialTags
|
||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
|
||||
if (missingCount > 0) {
|
||||
tags.addAll(List.generate(
|
||||
@ -47,11 +43,11 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
}
|
||||
}
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
updatedTags: updatedTags,
|
||||
updatedTags: updatedTags,
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
errorMessage: '',
|
||||
));
|
||||
@ -62,9 +58,16 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
|
||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
tags[event.index] = tags[event.index].copyWith(tag: event.tag);
|
||||
if (event.index < 0 || event.index >= tags.length) return;
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
tags[event.index] = tags[event.index].copyWith(
|
||||
tag: event.tag.tag,
|
||||
uuid: event.tag.uuid,
|
||||
product: event.tag.product,
|
||||
internalId: event.tag.internalId,
|
||||
location: event.tag.location,
|
||||
);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
@ -82,10 +85,9 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
|
||||
// Update the location
|
||||
tags[event.index] =
|
||||
tags[event.index].copyWith(location: event.location);
|
||||
tags[event.index] = tags[event.index].copyWith(location: event.location);
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
@ -104,7 +106,7 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
updatedTags: _calculateAvailableTags(allTags, tags),
|
||||
updatedTags: _calculateAvailableTags(projectTags, tags),
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
errorMessage: _getValidationError(tags),
|
||||
));
|
||||
@ -115,11 +117,10 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags)
|
||||
..remove(event.tagToDelete);
|
||||
final tags = List<Tag>.from(currentState.tags)..remove(event.tagToDelete);
|
||||
|
||||
// Recalculate available tags
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagLoaded(
|
||||
tags: tags,
|
||||
@ -140,10 +141,8 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
|
||||
// Get validation error for duplicate tags
|
||||
String? _getValidationError(List<Tag> tags) {
|
||||
final nonEmptyTags = tags
|
||||
.map((tag) => tag.tag?.trim() ?? '')
|
||||
.where((tag) => tag.isNotEmpty)
|
||||
.toList();
|
||||
final nonEmptyTags =
|
||||
tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList();
|
||||
|
||||
final duplicateTags = nonEmptyTags
|
||||
.fold<Map<String, int>>({}, (map, tag) {
|
||||
@ -162,14 +161,16 @@ class AssignTagBloc extends Bloc<AssignTagEvent, AssignTagState> {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> _calculateAvailableTags(List<String> allTags, List<Tag> tags) {
|
||||
final selectedTags = tags
|
||||
List<Tag> _calculateAvailableTags(List<Tag> allTags, List<Tag> selectedTags) {
|
||||
final selectedTagSet = selectedTags
|
||||
.where((tag) => (tag.tag?.trim().isNotEmpty ?? false))
|
||||
.map((tag) => tag.tag!.trim())
|
||||
.toSet();
|
||||
|
||||
final availableTags =
|
||||
allTags.where((tag) => !selectedTags.contains(tag.trim())).toList();
|
||||
final availableTags = allTags
|
||||
.where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
||||
.toList();
|
||||
|
||||
return availableTags;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ class InitializeTags extends AssignTagEvent {
|
||||
|
||||
class UpdateTagEvent extends AssignTagEvent {
|
||||
final int index;
|
||||
final String tag;
|
||||
final Tag tag;
|
||||
|
||||
const UpdateTagEvent({required this.index, required this.tag});
|
||||
|
||||
|
@ -5,7 +5,7 @@ abstract class AssignTagState extends Equatable {
|
||||
const AssignTagState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class AssignTagInitial extends AssignTagState {}
|
||||
@ -14,7 +14,7 @@ class AssignTagLoading extends AssignTagState {}
|
||||
|
||||
class AssignTagLoaded extends AssignTagState {
|
||||
final List<Tag> tags;
|
||||
final List<String> updatedTags;
|
||||
final List<Tag> updatedTags;
|
||||
|
||||
final bool isSaveEnabled;
|
||||
final String? errorMessage;
|
||||
@ -27,8 +27,7 @@ class AssignTagLoaded extends AssignTagState {
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object> get props =>
|
||||
[tags, updatedTags, isSaveEnabled, errorMessage ?? ''];
|
||||
List<Object?> get props => [tags, updatedTags, isSaveEnabled, errorMessage ?? ''];
|
||||
}
|
||||
|
||||
class AssignTagError extends AssignTagState {
|
||||
@ -37,5 +36,5 @@ class AssignTagError extends AssignTagState {
|
||||
const AssignTagError(this.errorMessage);
|
||||
|
||||
@override
|
||||
List<Object> get props => [errorMessage];
|
||||
List<Object?> get props => [errorMessage];
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
||||
import 'package:syncrow_web/common/dialog_textfield_dropdown.dart';
|
||||
import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/add_device_type/views/add_device_type_widget.dart';
|
||||
@ -26,6 +26,7 @@ class AssignTagDialog extends StatelessWidget {
|
||||
final String spaceName;
|
||||
final String title;
|
||||
final Function(List<Tag>, List<SubspaceModel>?)? onSave;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const AssignTagDialog(
|
||||
{Key? key,
|
||||
@ -37,18 +38,17 @@ class AssignTagDialog extends StatelessWidget {
|
||||
this.allTags,
|
||||
required this.spaceName,
|
||||
required this.title,
|
||||
this.onSave})
|
||||
this.onSave,
|
||||
required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<String> locations = (subspaces ?? [])
|
||||
.map((subspace) => subspace.subspaceName)
|
||||
.toList()
|
||||
..add('Main Space');
|
||||
final List<String> locations =
|
||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AssignTagBloc(allTags ?? [])
|
||||
create: (_) => AssignTagBloc(projectTags)
|
||||
..add(InitializeTags(
|
||||
initialTags: initialTags,
|
||||
addedProducts: addedProducts,
|
||||
@ -70,8 +70,7 @@ class AssignTagDialog extends StatelessWidget {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateProperty.all(
|
||||
ColorsManager.dataHeaderGrey),
|
||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
||||
key: ValueKey(state.tags.length),
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.dataHeaderGrey,
|
||||
@ -80,22 +79,15 @@ class AssignTagDialog extends StatelessWidget {
|
||||
),
|
||||
columns: [
|
||||
DataColumn(
|
||||
label: Text('#',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
label: Text('Device',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
label: Text('Device', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
numeric: false,
|
||||
label: Text('Tag',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
label: Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
label: Text('Location',
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium)),
|
||||
label:
|
||||
Text('Location', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
],
|
||||
rows: state.tags.isEmpty
|
||||
? [
|
||||
@ -103,12 +95,8 @@ class AssignTagDialog extends StatelessWidget {
|
||||
DataCell(
|
||||
Center(
|
||||
child: Text('No Data Available',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: ColorsManager
|
||||
.lightGrayColor,
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
)),
|
||||
),
|
||||
),
|
||||
@ -126,8 +114,7 @@ class AssignTagDialog extends StatelessWidget {
|
||||
DataCell(Text((index + 1).toString())),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
@ -141,31 +128,25 @@ class AssignTagDialog extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager
|
||||
.lightGrayColor,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: ColorsManager
|
||||
.lightGreyColor,
|
||||
color: ColorsManager.lightGreyColor,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(DeleteTag(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
context.read<AssignTagBloc>().add(
|
||||
DeleteTag(tagToDelete: tag, tags: state.tags));
|
||||
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints:
|
||||
const BoxConstraints(),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -173,23 +154,20 @@ class AssignTagDialog extends StatelessWidget {
|
||||
),
|
||||
DataCell(
|
||||
Container(
|
||||
alignment: Alignment
|
||||
.centerLeft, // Align cell content to the left
|
||||
alignment:
|
||||
Alignment.centerLeft, // Align cell content to the left
|
||||
child: SizedBox(
|
||||
width: double
|
||||
.infinity, // Ensure full width for dropdown
|
||||
child: DialogTextfieldDropdown(
|
||||
key: ValueKey(
|
||||
'dropdown_${Uuid().v4()}_${index}'),
|
||||
width: double.infinity,
|
||||
child: TagDialogTextfieldDropdown(
|
||||
key: ValueKey('dropdown_${const Uuid().v4()}_$index'),
|
||||
items: state.updatedTags,
|
||||
initialValue: tag.tag,
|
||||
product: tag.product?.uuid ?? 'Unknown',
|
||||
initialValue: tag,
|
||||
onSelected: (value) {
|
||||
controller.text = value;
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(UpdateTagEvent(
|
||||
controller.text = value.tag ?? '';
|
||||
context.read<AssignTagBloc>().add(UpdateTagEvent(
|
||||
index: index,
|
||||
tag: value.trim(),
|
||||
tag: value,
|
||||
));
|
||||
},
|
||||
),
|
||||
@ -201,12 +179,9 @@ class AssignTagDialog extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
child: DialogDropdown(
|
||||
items: locations,
|
||||
selectedValue:
|
||||
tag.location ?? 'Main Space',
|
||||
selectedValue: tag.location ?? 'Main Space',
|
||||
onSelected: (value) {
|
||||
context
|
||||
.read<AssignTagBloc>()
|
||||
.add(UpdateLocation(
|
||||
context.read<AssignTagBloc>().add(UpdateLocation(
|
||||
index: index,
|
||||
location: value,
|
||||
));
|
||||
@ -238,13 +213,11 @@ class AssignTagDialog extends StatelessWidget {
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result =
|
||||
TagHelper.processTags(updatedTags, subspaces);
|
||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
|
||||
@ -253,8 +226,9 @@ class AssignTagDialog extends StatelessWidget {
|
||||
builder: (context) => AddDeviceTypeWidget(
|
||||
products: products,
|
||||
subspaces: processedSubspaces,
|
||||
initialSelectedProducts: TagHelper
|
||||
.createInitialSelectedProductsForTags(
|
||||
projectTags: projectTags,
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProductsForTags(
|
||||
processedTags, processedSubspaces),
|
||||
spaceName: spaceName,
|
||||
spaceTags: processedTags,
|
||||
@ -278,14 +252,11 @@ class AssignTagDialog extends StatelessWidget {
|
||||
onPressed: state.isSaveEnabled
|
||||
? () async {
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result = TagHelper.processTags(
|
||||
updatedTags, subspaces);
|
||||
final result = TagHelper.processTags(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<Tag>;
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
List<SubspaceModel>.from(result['subspaces'] as List<dynamic>);
|
||||
onSave?.call(processedTags, processedSubspaces);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
@ -1,45 +1,40 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
|
||||
class AssignTagModelBloc
|
||||
extends Bloc<AssignTagModelEvent, AssignTagModelState> {
|
||||
final List<String> allTags;
|
||||
class AssignTagModelBloc extends Bloc<AssignTagModelEvent, AssignTagModelState> {
|
||||
final List<Tag> projectTags;
|
||||
|
||||
AssignTagModelBloc(this.allTags) : super(AssignTagModelInitial()) {
|
||||
AssignTagModelBloc(this.projectTags) : super(AssignTagModelInitial()) {
|
||||
on<InitializeTagModels>((event, emit) {
|
||||
final initialTags = event.initialTags ?? [];
|
||||
final initialTags = event.initialTags;
|
||||
|
||||
final existingTagCounts = <String, int>{};
|
||||
for (var tag in initialTags) {
|
||||
if (tag.product != null) {
|
||||
existingTagCounts[tag.product!.uuid] =
|
||||
(existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||
existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
final tags = <TagModel>[];
|
||||
final tags = <Tag>[];
|
||||
|
||||
for (var selectedProduct in event.addedProducts) {
|
||||
final existingCount = existingTagCounts[selectedProduct.productId] ?? 0;
|
||||
|
||||
if (selectedProduct.count == 0 ||
|
||||
selectedProduct.count <= existingCount) {
|
||||
tags.addAll(initialTags
|
||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) {
|
||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
continue;
|
||||
}
|
||||
|
||||
final missingCount = selectedProduct.count - existingCount;
|
||||
|
||||
tags.addAll(initialTags
|
||||
.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId));
|
||||
|
||||
if (missingCount > 0) {
|
||||
tags.addAll(List.generate(
|
||||
missingCount,
|
||||
(index) => TagModel(
|
||||
(index) => Tag(
|
||||
tag: '',
|
||||
product: selectedProduct.product,
|
||||
location: 'Main Space',
|
||||
@ -48,7 +43,7 @@ class AssignTagModelBloc
|
||||
}
|
||||
}
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagModelLoaded(
|
||||
tags: tags,
|
||||
@ -59,11 +54,20 @@ class AssignTagModelBloc
|
||||
|
||||
on<UpdateTag>((event, emit) {
|
||||
final currentState = state;
|
||||
if (currentState is AssignTagModelLoaded &&
|
||||
currentState.tags.isNotEmpty) {
|
||||
final tags = List<TagModel>.from(currentState.tags);
|
||||
tags[event.index] = tags[event.index].copyWith(tag: event.tag);
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
|
||||
if (event.index < 0 || event.index >= tags.length) return;
|
||||
|
||||
tags[event.index] = tags[event.index].copyWith(
|
||||
tag: event.tag.tag,
|
||||
uuid: event.tag.uuid,
|
||||
product: event.tag.product,
|
||||
internalId: event.tag.internalId,
|
||||
location: event.tag.location,
|
||||
);
|
||||
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagModelLoaded(
|
||||
tags: tags,
|
||||
@ -77,15 +81,12 @@ class AssignTagModelBloc
|
||||
on<UpdateLocation>((event, emit) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AssignTagModelLoaded &&
|
||||
currentState.tags.isNotEmpty) {
|
||||
final tags = List<TagModel>.from(currentState.tags);
|
||||
|
||||
if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
// Use copyWith for immutability
|
||||
tags[event.index] =
|
||||
tags[event.index].copyWith(location: event.location);
|
||||
tags[event.index] = tags[event.index].copyWith(location: event.location);
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagModelLoaded(
|
||||
tags: tags,
|
||||
@ -99,13 +100,12 @@ class AssignTagModelBloc
|
||||
on<ValidateTagModels>((event, emit) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AssignTagModelLoaded &&
|
||||
currentState.tags.isNotEmpty) {
|
||||
final tags = List<TagModel>.from(currentState.tags);
|
||||
if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags);
|
||||
|
||||
emit(AssignTagModelLoaded(
|
||||
tags: tags,
|
||||
updatedTags: _calculateAvailableTags(allTags, tags),
|
||||
updatedTags: _calculateAvailableTags(projectTags, tags),
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
errorMessage: _getValidationError(tags),
|
||||
));
|
||||
@ -115,12 +115,10 @@ class AssignTagModelBloc
|
||||
on<DeleteTagModel>((event, emit) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is AssignTagModelLoaded &&
|
||||
currentState.tags.isNotEmpty) {
|
||||
final tags = List<TagModel>.from(currentState.tags)
|
||||
..remove(event.tagToDelete);
|
||||
if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) {
|
||||
final tags = List<Tag>.from(currentState.tags)..remove(event.tagToDelete);
|
||||
|
||||
final updatedTags = _calculateAvailableTags(allTags, tags);
|
||||
final updatedTags = _calculateAvailableTags(projectTags, tags);
|
||||
|
||||
emit(AssignTagModelLoaded(
|
||||
tags: tags,
|
||||
@ -128,24 +126,22 @@ class AssignTagModelBloc
|
||||
isSaveEnabled: _validateTags(tags),
|
||||
errorMessage: _getValidationError(tags),
|
||||
));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _validateTags(List<TagModel> tags) {
|
||||
bool _validateTags(List<Tag> tags) {
|
||||
final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet();
|
||||
final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty);
|
||||
final isValid = uniqueTags.length == tags.length && !hasEmptyTag;
|
||||
return isValid;
|
||||
}
|
||||
|
||||
String? _getValidationError(List<TagModel> tags) {
|
||||
String? _getValidationError(List<Tag> tags) {
|
||||
// Check for duplicate tags
|
||||
|
||||
final nonEmptyTags = tags
|
||||
.map((tag) => tag.tag?.trim() ?? '')
|
||||
.where((tag) => tag.isNotEmpty)
|
||||
.toList();
|
||||
final nonEmptyTags =
|
||||
tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList();
|
||||
|
||||
final duplicateTags = nonEmptyTags
|
||||
.fold<Map<String, int>>({}, (map, tag) {
|
||||
@ -164,15 +160,16 @@ class AssignTagModelBloc
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> _calculateAvailableTags(
|
||||
List<String> allTags, List<TagModel> tags) {
|
||||
final selectedTags = tags
|
||||
List<Tag> _calculateAvailableTags(List<Tag> allTags, List<Tag> selectedTags) {
|
||||
final selectedTagSet = selectedTags
|
||||
.where((tag) => (tag.tag?.trim().isNotEmpty ?? false))
|
||||
.map((tag) => tag.tag!.trim())
|
||||
.toSet();
|
||||
|
||||
final availableTags =
|
||||
allTags.where((tag) => !selectedTags.contains(tag.trim())).toList();
|
||||
final availableTags = allTags
|
||||
.where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim()))
|
||||
.toList();
|
||||
|
||||
return availableTags;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
|
||||
abstract class AssignTagModelEvent extends Equatable {
|
||||
@ -10,7 +10,7 @@ abstract class AssignTagModelEvent extends Equatable {
|
||||
}
|
||||
|
||||
class InitializeTagModels extends AssignTagModelEvent {
|
||||
final List<TagModel> initialTags;
|
||||
final List<Tag> initialTags;
|
||||
final List<SelectedProduct> addedProducts;
|
||||
|
||||
const InitializeTagModels({
|
||||
@ -24,7 +24,7 @@ class InitializeTagModels extends AssignTagModelEvent {
|
||||
|
||||
class UpdateTag extends AssignTagModelEvent {
|
||||
final int index;
|
||||
final String tag;
|
||||
final Tag tag;
|
||||
|
||||
const UpdateTag({required this.index, required this.tag});
|
||||
|
||||
@ -45,8 +45,8 @@ class UpdateLocation extends AssignTagModelEvent {
|
||||
class ValidateTagModels extends AssignTagModelEvent {}
|
||||
|
||||
class DeleteTagModel extends AssignTagModelEvent {
|
||||
final TagModel tagToDelete;
|
||||
final List<TagModel> tags;
|
||||
final Tag tagToDelete;
|
||||
final List<Tag> tags;
|
||||
|
||||
const DeleteTagModel({required this.tagToDelete, required this.tags});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
|
||||
abstract class AssignTagModelState extends Equatable {
|
||||
const AssignTagModelState();
|
||||
@ -13,11 +13,11 @@ class AssignTagModelInitial extends AssignTagModelState {}
|
||||
class AssignTagModelLoading extends AssignTagModelState {}
|
||||
|
||||
class AssignTagModelLoaded extends AssignTagModelState {
|
||||
final List<TagModel> tags;
|
||||
final List<Tag> tags;
|
||||
final bool isSaveEnabled;
|
||||
final String? errorMessage;
|
||||
|
||||
final List<String> updatedTags;
|
||||
final List<Tag> updatedTags;
|
||||
|
||||
const AssignTagModelLoaded({
|
||||
required this.tags,
|
||||
|
@ -1,17 +1,17 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/common/dialog_dropdown.dart';
|
||||
import 'package:syncrow_web/common/dialog_textfield_dropdown.dart';
|
||||
import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/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';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
@ -23,8 +23,8 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
final List<SubspaceTemplateModel>? subspaces;
|
||||
final SpaceTemplateModel? spaceModel;
|
||||
|
||||
final List<TagModel> initialTags;
|
||||
final ValueChanged<List<TagModel>>? onTagsAssigned;
|
||||
final List<Tag> initialTags;
|
||||
final ValueChanged<List<Tag>>? onTagsAssigned;
|
||||
final List<SelectedProduct> addedProducts;
|
||||
final List<String>? allTags;
|
||||
final String spaceName;
|
||||
@ -32,6 +32,7 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
final BuildContext? pageContext;
|
||||
final List<String>? otherSpaceModels;
|
||||
final List<SpaceTemplateModel>? allSpaceModels;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const AssignTagModelsDialog(
|
||||
{Key? key,
|
||||
@ -46,18 +47,17 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
this.pageContext,
|
||||
this.otherSpaceModels,
|
||||
this.spaceModel,
|
||||
this.allSpaceModels})
|
||||
this.allSpaceModels,
|
||||
required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final List<String> locations = (subspaces ?? [])
|
||||
.map((subspace) => subspace.subspaceName)
|
||||
.toList()
|
||||
..add('Main Space');
|
||||
final List<String> locations =
|
||||
(subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space');
|
||||
|
||||
return BlocProvider(
|
||||
create: (_) => AssignTagModelBloc(allTags ?? [])
|
||||
create: (_) => AssignTagModelBloc(projectTags)
|
||||
..add(InitializeTagModels(
|
||||
initialTags: initialTags,
|
||||
addedProducts: addedProducts,
|
||||
@ -81,8 +81,7 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
child: DataTable(
|
||||
headingRowColor: WidgetStateProperty.all(
|
||||
ColorsManager.dataHeaderGrey),
|
||||
headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey),
|
||||
key: ValueKey(state.tags.length),
|
||||
border: TableBorder.all(
|
||||
color: ColorsManager.dataHeaderGrey,
|
||||
@ -91,26 +90,17 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
),
|
||||
columns: [
|
||||
DataColumn(
|
||||
label: Text('#',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium)),
|
||||
label: Text('#', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
label: Text('Device',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium)),
|
||||
style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
numeric: false,
|
||||
label: Text('Tag',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium)),
|
||||
label:
|
||||
Text('Tag', style: Theme.of(context).textTheme.bodyMedium)),
|
||||
DataColumn(
|
||||
label: Text('Location',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium)),
|
||||
style: Theme.of(context).textTheme.bodyMedium)),
|
||||
],
|
||||
rows: state.tags.isEmpty
|
||||
? [
|
||||
@ -118,13 +108,10 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
DataCell(
|
||||
Center(
|
||||
child: Text('No Devices Available',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: ColorsManager
|
||||
.lightGrayColor,
|
||||
)),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
)),
|
||||
),
|
||||
),
|
||||
const DataCell(SizedBox()),
|
||||
@ -141,8 +128,7 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
DataCell(Text((index + 1).toString())),
|
||||
DataCell(
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
@ -156,31 +142,25 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager
|
||||
.lightGrayColor,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
child: IconButton(
|
||||
icon: const Icon(
|
||||
Icons.close,
|
||||
color: ColorsManager
|
||||
.lightGreyColor,
|
||||
color: ColorsManager.lightGreyColor,
|
||||
size: 16,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<
|
||||
AssignTagModelBloc>()
|
||||
.add(DeleteTagModel(
|
||||
tagToDelete: tag,
|
||||
tags: state.tags));
|
||||
context.read<AssignTagModelBloc>().add(
|
||||
DeleteTagModel(
|
||||
tagToDelete: tag, tags: state.tags));
|
||||
controllers.removeAt(index);
|
||||
},
|
||||
tooltip: 'Delete Tag',
|
||||
padding: EdgeInsets.zero,
|
||||
constraints:
|
||||
const BoxConstraints(),
|
||||
constraints: const BoxConstraints(),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -191,19 +171,16 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
alignment: Alignment
|
||||
.centerLeft, // Align cell content to the left
|
||||
child: SizedBox(
|
||||
width: double
|
||||
.infinity, // Ensure full width for dropdown
|
||||
child: DialogTextfieldDropdown(
|
||||
key: ValueKey(
|
||||
'dropdown_${Uuid().v4()}_${index}'),
|
||||
width: double.infinity,
|
||||
child: TagDialogTextfieldDropdown(
|
||||
key: ValueKey(
|
||||
'dropdown_${const Uuid().v4()}_$index'),
|
||||
product: tag.product?.uuid ?? 'Unknown',
|
||||
items: state.updatedTags,
|
||||
initialValue: tag.tag,
|
||||
initialValue: tag,
|
||||
onSelected: (value) {
|
||||
controller.text = value;
|
||||
context
|
||||
.read<
|
||||
AssignTagModelBloc>()
|
||||
.add(UpdateTag(
|
||||
controller.text = value.tag ?? '';
|
||||
context.read<AssignTagModelBloc>().add(UpdateTag(
|
||||
index: index,
|
||||
tag: value,
|
||||
));
|
||||
@ -217,12 +194,10 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
width: double.infinity,
|
||||
child: DialogDropdown(
|
||||
items: locations,
|
||||
selectedValue: tag.location ??
|
||||
'Main Space',
|
||||
selectedValue: tag.location ?? 'Main Space',
|
||||
onSelected: (value) {
|
||||
context
|
||||
.read<
|
||||
AssignTagModelBloc>()
|
||||
.read<AssignTagModelBloc>()
|
||||
.add(UpdateLocation(
|
||||
index: index,
|
||||
location: value,
|
||||
@ -254,17 +229,13 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
builder: (buttonContext) => CancelButton(
|
||||
label: 'Add New Device',
|
||||
onPressed: () async {
|
||||
final updatedTags =
|
||||
List<TagModel>.from(state.tags);
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<TagModel>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
if (context.mounted) {
|
||||
Navigator.of(context).pop();
|
||||
@ -272,28 +243,25 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
await showDialog<bool>(
|
||||
barrierDismissible: false,
|
||||
context: context,
|
||||
builder: (dialogContext) =>
|
||||
AddDeviceTypeModelWidget(
|
||||
products: products,
|
||||
subspaces: processedSubspaces,
|
||||
isCreate: false,
|
||||
initialSelectedProducts: TagHelper
|
||||
.createInitialSelectedProducts(
|
||||
processedTags,
|
||||
processedSubspaces),
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
spaceTagModels: processedTags,
|
||||
pageContext: pageContext,
|
||||
spaceModel: SpaceTemplateModel(
|
||||
modelName: spaceName,
|
||||
tags: updatedTags,
|
||||
uuid: spaceModel?.uuid,
|
||||
internalId:
|
||||
spaceModel?.internalId,
|
||||
subspaceModels:
|
||||
processedSubspaces)),
|
||||
builder: (dialogContext) => AddDeviceTypeModelWidget(
|
||||
products: products,
|
||||
subspaces: processedSubspaces,
|
||||
isCreate: false,
|
||||
initialSelectedProducts:
|
||||
TagHelper.createInitialSelectedProducts(
|
||||
processedTags, processedSubspaces),
|
||||
allTags: allTags,
|
||||
spaceName: spaceName,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
spaceTagModels: processedTags,
|
||||
pageContext: pageContext,
|
||||
projectTags: projectTags,
|
||||
spaceModel: SpaceTemplateModel(
|
||||
modelName: spaceName,
|
||||
tags: updatedTags,
|
||||
uuid: spaceModel?.uuid,
|
||||
internalId: spaceModel?.internalId,
|
||||
subspaceModels: processedSubspaces)),
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -310,22 +278,16 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
: ColorsManager.whiteColorsWithOpacity,
|
||||
onPressed: state.isSaveEnabled
|
||||
? () async {
|
||||
final updatedTags =
|
||||
List<TagModel>.from(state.tags);
|
||||
final updatedTags = List<Tag>.from(state.tags);
|
||||
|
||||
final result =
|
||||
TagHelper.updateSubspaceTagModels(
|
||||
updatedTags, subspaces);
|
||||
TagHelper.updateSubspaceTagModels(updatedTags, subspaces);
|
||||
|
||||
final processedTags =
|
||||
result['updatedTags'] as List<TagModel>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(
|
||||
result['subspaces']
|
||||
as List<dynamic>);
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces = List<SubspaceTemplateModel>.from(
|
||||
result['subspaces'] as List<dynamic>);
|
||||
|
||||
Navigator.of(context)
|
||||
.popUntil((route) => route.isFirst);
|
||||
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||
|
||||
await showDialog(
|
||||
context: context,
|
||||
@ -334,16 +296,15 @@ class AssignTagModelsDialog extends StatelessWidget {
|
||||
products: products,
|
||||
allSpaceModels: allSpaceModels,
|
||||
allTags: allTags,
|
||||
projectTags: projectTags,
|
||||
pageContext: pageContext,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
spaceModel: SpaceTemplateModel(
|
||||
modelName: spaceName,
|
||||
tags: processedTags,
|
||||
uuid: spaceModel?.uuid,
|
||||
internalId:
|
||||
spaceModel?.internalId,
|
||||
subspaceModels:
|
||||
processedSubspaces),
|
||||
internalId: spaceModel?.internalId,
|
||||
subspaceModels: processedSubspaces),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -7,7 +7,6 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_mo
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
|
||||
class TagHelper {
|
||||
static Map<String, dynamic> updateTags<T>({
|
||||
@ -131,9 +130,9 @@ class TagHelper {
|
||||
}
|
||||
|
||||
static List<String> getAvailableTagModels(
|
||||
List<String> allTags, List<TagModel> currentTags, TagModel currentTag) {
|
||||
List<String> allTags, List<Tag> currentTags, Tag currentTag) {
|
||||
List<String> availableTagsForTagModel =
|
||||
TagHelper.getAvailableTags<TagModel>(
|
||||
TagHelper.getAvailableTags<Tag>(
|
||||
allTags: allTags,
|
||||
currentTags: currentTags,
|
||||
currentTag: currentTag,
|
||||
@ -142,11 +141,11 @@ class TagHelper {
|
||||
return availableTagsForTagModel;
|
||||
}
|
||||
|
||||
static List<TagModel> generateInitialTags({
|
||||
List<TagModel>? spaceTagModels,
|
||||
static List<Tag> generateInitialTags({
|
||||
List<Tag>? spaceTagModels,
|
||||
List<SubspaceTemplateModel>? subspaces,
|
||||
}) {
|
||||
final List<TagModel> initialTags = [];
|
||||
final List<Tag> initialTags = [];
|
||||
|
||||
if (spaceTagModels != null) {
|
||||
initialTags.addAll(spaceTagModels);
|
||||
@ -212,7 +211,7 @@ class TagHelper {
|
||||
}
|
||||
|
||||
static List<SelectedProduct> createInitialSelectedProducts(
|
||||
List<TagModel>? tags, List<SubspaceTemplateModel>? subspaces) {
|
||||
List<Tag>? tags, List<SubspaceTemplateModel>? subspaces) {
|
||||
final Map<ProductModel, int> productCounts = {};
|
||||
|
||||
if (tags != null) {
|
||||
@ -282,7 +281,7 @@ class TagHelper {
|
||||
}
|
||||
|
||||
static int? checkTagExistInSubspaceModels(
|
||||
TagModel tag, List<dynamic>? subspaces) {
|
||||
Tag tag, List<dynamic>? subspaces) {
|
||||
if (subspaces == null) return null;
|
||||
|
||||
for (int i = 0; i < subspaces.length; i++) {
|
||||
@ -298,8 +297,8 @@ class TagHelper {
|
||||
}
|
||||
|
||||
static Map<String, dynamic> updateSubspaceTagModels(
|
||||
List<TagModel> updatedTags, List<SubspaceTemplateModel>? subspaces) {
|
||||
final result = TagHelper.updateTags<TagModel>(
|
||||
List<Tag> updatedTags, List<SubspaceTemplateModel>? subspaces) {
|
||||
final result = TagHelper.updateTags<Tag>(
|
||||
updatedTags: updatedTags,
|
||||
subspaces: subspaces,
|
||||
getInternalId: (tag) => tag.internalId,
|
||||
@ -311,7 +310,7 @@ class TagHelper {
|
||||
checkTagExistInSubspace: checkTagExistInSubspaceModels,
|
||||
);
|
||||
|
||||
final processedTags = result['updatedTags'] as List<TagModel>;
|
||||
final processedTags = result['updatedTags'] as List<Tag>;
|
||||
final processedSubspaces =
|
||||
List<SubspaceTemplateModel>.from(result['subspaces'] as List<dynamic>);
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart';
|
||||
|
||||
|
||||
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
|
||||
SpaceModelBloc() : super(SpaceModelInitial()) {
|
||||
on<SpaceModelSelectedEvent>((event, emit) {
|
||||
emit(SpaceModelSelectedState(event.selectedIndex));
|
||||
});
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
abstract class SpaceModelEvent {}
|
||||
|
||||
class SpaceModelSelectedEvent extends SpaceModelEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedEvent(this.selectedIndex);
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
abstract class SpaceModelState {}
|
||||
|
||||
class SpaceModelInitial extends SpaceModelState {}
|
||||
|
||||
class SpaceModelSelectedState extends SpaceModelState {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedState(this.selectedIndex);
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart';
|
||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/navigation_service.dart';
|
||||
|
||||
class LinkSpaceToModelBloc
|
||||
extends Bloc<LinkSpaceToModelEvent, LinkSpaceToModelState> {
|
||||
LinkSpaceToModelBloc() : super(SpaceModelInitial()) {
|
||||
on<LinkSpaceModelSelectedIdsEvent>(_getSpaceIds);
|
||||
on<LinkSpaceModelEvent>(_handleLinkSpaceModel);
|
||||
on<ValidateSpaceModelEvent>(_validateLinkSpaceModel);
|
||||
on<LinkSpaceModelSelectedEvent>((event, emit) {
|
||||
emit(SpaceModelSelectedState(event.selectedIndex));
|
||||
});
|
||||
}
|
||||
|
||||
List<String> spacesListIds = [];
|
||||
bool hasSelectedSpaces = false;
|
||||
String validate = '';
|
||||
|
||||
Future<void> _getSpaceIds(LinkSpaceModelSelectedIdsEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit) async {
|
||||
try {
|
||||
BuildContext context = NavigationService.navigatorKey.currentContext!;
|
||||
var spaceBloc = context.read<SpaceTreeBloc>();
|
||||
for (var communityId in spaceBloc.state.selectedCommunities) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
spacesListIds = spacesList;
|
||||
}
|
||||
hasSelectedSpaces =
|
||||
spaceBloc.state.selectedCommunities.any((communityId) {
|
||||
List<String> spacesList =
|
||||
spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? [];
|
||||
return spacesList.isNotEmpty;
|
||||
});
|
||||
if (hasSelectedSpaces) {
|
||||
debugPrint("At least one space is selected.");
|
||||
} else {
|
||||
debugPrint("No spaces selected.");
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint("Error in _getSpaceIds: $e");
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleLinkSpaceModel(
|
||||
LinkSpaceModelEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit,
|
||||
) async {
|
||||
emit(SpaceModelLoading());
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await SpaceModelManagementApi().linkSpaceModel(
|
||||
spaceModelUuid: event.selectedSpaceMode!,
|
||||
projectId: projectUuid,
|
||||
spaceUuids: spacesListIds,
|
||||
isOverWrite: event.isOverWrite);
|
||||
emit(SpaceModelLinkSuccess());
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = _parseDioError(e);
|
||||
emit(SpaceModelOperationFailure(errorMessage));
|
||||
} catch (e) {
|
||||
emit(SpaceModelOperationFailure('Unexpected error: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _validateLinkSpaceModel(
|
||||
ValidateSpaceModelEvent event,
|
||||
Emitter<LinkSpaceToModelState> emit,
|
||||
) async {
|
||||
emit(SpaceModelLoading());
|
||||
try {
|
||||
final projectUuid = await ProjectManager.getProjectUUID() ?? '';
|
||||
await SpaceModelManagementApi().validateSpaceModel(
|
||||
projectUuid,
|
||||
spacesListIds,
|
||||
);
|
||||
emit(SpaceValidationSuccess());
|
||||
} on DioException catch (e) {
|
||||
final errorMessage = _parseDioError(e);
|
||||
if (errorMessage ==
|
||||
'Selected spaces already have linked space model / sub-spaces and devices') {
|
||||
emit(const AlreadyHaveLinkedState());
|
||||
} else {
|
||||
emit(SpaceModelOperationFailure(errorMessage));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceModelOperationFailure('Unexpected error: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
String _parseDioError(DioException e) {
|
||||
if (e.response?.data is Map<String, dynamic>) {
|
||||
return e.response!.data['error']['message'] ?? 'Unknown error occurred';
|
||||
}
|
||||
return e.message ?? 'Network request failed';
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class LinkSpaceToModelEvent {}
|
||||
|
||||
class LinkSpaceModelSelectedEvent extends LinkSpaceToModelEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
LinkSpaceModelSelectedEvent(this.selectedIndex);
|
||||
}
|
||||
|
||||
class LinkSpaceModelSelectedIdsEvent extends LinkSpaceToModelEvent {}
|
||||
|
||||
class LinkSpaceModelEvent extends LinkSpaceToModelEvent {
|
||||
final String? selectedSpaceMode;
|
||||
final bool isOverWrite;
|
||||
LinkSpaceModelEvent({this.selectedSpaceMode, this.isOverWrite = false});
|
||||
}
|
||||
|
||||
class ValidateSpaceModelEvent extends LinkSpaceToModelEvent {
|
||||
BuildContext? context;
|
||||
ValidateSpaceModelEvent({this.context});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
abstract class LinkSpaceToModelState {
|
||||
const LinkSpaceToModelState();
|
||||
}
|
||||
|
||||
class SpaceModelInitial extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelLoading extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelSelectedState extends LinkSpaceToModelState {
|
||||
final int selectedIndex;
|
||||
const SpaceModelSelectedState(this.selectedIndex);
|
||||
}
|
||||
|
||||
class SpaceModelSelectionUpdated extends LinkSpaceToModelState {
|
||||
final bool hasSelectedSpaces;
|
||||
const SpaceModelSelectionUpdated(this.hasSelectedSpaces);
|
||||
}
|
||||
|
||||
class SpaceValidationSuccess extends LinkSpaceToModelState {}
|
||||
|
||||
class SpaceModelLinkSuccess extends LinkSpaceToModelState {}
|
||||
|
||||
class ValidationError extends LinkSpaceToModelState {
|
||||
final String message;
|
||||
const ValidationError(this.message);
|
||||
}
|
||||
|
||||
class SpaceModelOperationFailure extends LinkSpaceToModelState {
|
||||
final String message;
|
||||
const SpaceModelOperationFailure(this.message);
|
||||
}
|
||||
|
||||
class AlreadyHaveLinkedState extends LinkSpaceToModelState {
|
||||
const AlreadyHaveLinkedState();
|
||||
}
|
@ -2,9 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/space_model_card_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
@ -24,13 +24,13 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SpaceModelBloc()
|
||||
create: (context) => LinkSpaceToModelBloc()
|
||||
..add(
|
||||
SpaceModelSelectedEvent(initialSelectedIndex ?? -1),
|
||||
LinkSpaceModelSelectedEvent(initialSelectedIndex ?? -1),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final bloc = context.read<SpaceModelBloc>();
|
||||
final bloc = context.read<LinkSpaceToModelBloc>();
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: const Text('Link a space model'),
|
||||
@ -39,7 +39,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: MediaQuery.of(context).size.width * 0.7,
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
child: BlocBuilder<LinkSpaceToModelBloc, LinkSpaceToModelState>(
|
||||
builder: (context, state) {
|
||||
int selectedIndex = -1;
|
||||
if (state is SpaceModelSelectedState) {
|
||||
@ -59,7 +59,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
final isSelected = selectedIndex == index;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
bloc.add(SpaceModelSelectedEvent(index));
|
||||
bloc.add(LinkSpaceModelSelectedEvent(index));
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
@ -93,7 +93,7 @@ class LinkSpaceModelDialog extends StatelessWidget {
|
||||
label: 'Cancel',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
BlocBuilder<LinkSpaceToModelBloc, LinkSpaceToModelState>(
|
||||
builder: (context, state) {
|
||||
final isEnabled = state is SpaceModelSelectedState &&
|
||||
state.selectedIndex >= 0;
|
||||
|
@ -1,11 +1,11 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/bloc/project_manager.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
||||
@ -94,14 +94,9 @@ class CreateSpaceModelBloc
|
||||
orElse: () => subspace,
|
||||
);
|
||||
|
||||
// Update the subspace's tags
|
||||
final eventTagIds = matchingEventSubspace.tags
|
||||
?.map((e) => e.internalId)
|
||||
.toSet() ??
|
||||
{};
|
||||
|
||||
final updatedTags = [
|
||||
...?subspace.tags?.map<TagModel>((tag) {
|
||||
...?subspace.tags?.map<Tag>((tag) {
|
||||
final matchingTag =
|
||||
matchingEventSubspace.tags?.firstWhere(
|
||||
(e) => e.internalId == tag.internalId,
|
||||
@ -112,14 +107,14 @@ class CreateSpaceModelBloc
|
||||
? tag.copyWith(tag: matchingTag?.tag)
|
||||
: tag;
|
||||
}) ??
|
||||
<TagModel>[],
|
||||
<Tag>[],
|
||||
...?matchingEventSubspace.tags?.where(
|
||||
(e) =>
|
||||
subspace.tags
|
||||
?.every((t) => t.internalId != e.internalId) ??
|
||||
true,
|
||||
) ??
|
||||
<TagModel>[],
|
||||
<Tag>[],
|
||||
];
|
||||
return subspace.copyWith(
|
||||
subspaceName: matchingEventSubspace.subspaceName,
|
||||
@ -244,7 +239,7 @@ class CreateSpaceModelBloc
|
||||
}
|
||||
|
||||
if (newSubspaces != null) {
|
||||
for (var newSubspace in newSubspaces!) {
|
||||
for (var newSubspace in newSubspaces) {
|
||||
// Tag without UUID
|
||||
if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) {
|
||||
final List<TagModelUpdate> tagUpdates = [];
|
||||
@ -253,7 +248,7 @@ class CreateSpaceModelBloc
|
||||
for (var tag in newSubspace.tags!) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
uuid: tag.uuid == '' ? null : tag.uuid,
|
||||
newTagUuid: tag.uuid == '' ? null : tag.uuid,
|
||||
tag: tag.tag,
|
||||
productUuid: tag.product?.uuid));
|
||||
}
|
||||
@ -268,7 +263,7 @@ class CreateSpaceModelBloc
|
||||
|
||||
if (prevSubspaces != null && newSubspaces != null) {
|
||||
final newSubspaceMap = {
|
||||
for (var subspace in newSubspaces!) subspace.uuid: subspace
|
||||
for (var subspace in newSubspaces) subspace.uuid: subspace
|
||||
};
|
||||
|
||||
for (var prevSubspace in prevSubspaces) {
|
||||
@ -309,8 +304,8 @@ class CreateSpaceModelBloc
|
||||
}
|
||||
|
||||
List<TagModelUpdate> processTagUpdates(
|
||||
List<TagModel>? prevTags,
|
||||
List<TagModel>? newTags,
|
||||
List<Tag>? prevTags,
|
||||
List<Tag>? newTags,
|
||||
) {
|
||||
final List<TagModelUpdate> tagUpdates = [];
|
||||
final processedTags = <String?>{};
|
||||
@ -320,7 +315,7 @@ class CreateSpaceModelBloc
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
tag: newTag.tag,
|
||||
uuid: newTag.uuid,
|
||||
newTagUuid: newTag.uuid,
|
||||
productUuid: newTag.product?.uuid,
|
||||
));
|
||||
}
|
||||
@ -332,7 +327,7 @@ class CreateSpaceModelBloc
|
||||
if (prevTags != null && newTags != null) {
|
||||
for (var prevTag in prevTags) {
|
||||
final existsInNew =
|
||||
newTags!.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||
newTags.any((newTag) => newTag.uuid == prevTag.uuid);
|
||||
if (!existsInNew) {
|
||||
tagUpdates
|
||||
.add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid));
|
||||
@ -349,14 +344,14 @@ class CreateSpaceModelBloc
|
||||
if (newTags != null) {
|
||||
final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {};
|
||||
|
||||
for (var newTag in newTags!) {
|
||||
for (var newTag in newTags) {
|
||||
// Tag without UUID
|
||||
if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) &&
|
||||
!processedTags.contains(newTag.tag)) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.add,
|
||||
tag: newTag.tag,
|
||||
uuid: newTag.uuid == '' ? null : newTag.uuid,
|
||||
newTagUuid: newTag.uuid == '' ? null : newTag.uuid,
|
||||
productUuid: newTag.product?.uuid));
|
||||
processedTags.add(newTag.tag);
|
||||
}
|
||||
@ -365,14 +360,15 @@ class CreateSpaceModelBloc
|
||||
|
||||
// Case 3: Tags updated
|
||||
if (prevTags != null && newTags != null) {
|
||||
final newTagMap = {for (var tag in newTags!) tag.uuid: tag};
|
||||
final newTagMap = {for (var tag in newTags) tag.uuid: tag};
|
||||
|
||||
for (var prevTag in prevTags!) {
|
||||
for (var prevTag in prevTags) {
|
||||
final newTag = newTagMap[prevTag.uuid];
|
||||
if (newTag != null) {
|
||||
tagUpdates.add(TagModelUpdate(
|
||||
action: Action.update,
|
||||
uuid: newTag.uuid,
|
||||
uuid: prevTag.uuid,
|
||||
newTagUuid: newTag.uuid,
|
||||
tag: newTag.tag,
|
||||
));
|
||||
} else {}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
|
||||
abstract class CreateSpaceModelEvent extends Equatable {
|
||||
const CreateSpaceModelEvent();
|
||||
@ -49,7 +49,7 @@ class AddSubspacesToSpaceTemplate extends CreateSpaceModelEvent {
|
||||
}
|
||||
|
||||
class AddTagsToSpaceTemplate extends CreateSpaceModelEvent {
|
||||
final List<TagModel> tags;
|
||||
final List<Tag> tags;
|
||||
|
||||
AddTagsToSpaceTemplate(this.tags);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ class TagBodyModel {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'tag': tag,
|
||||
'name': tag,
|
||||
'productUuid': productUuid,
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart';
|
||||
import 'package:syncrow_web/utils/constants/action_enum.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
@ -9,8 +9,9 @@ class SpaceTemplateModel extends Equatable {
|
||||
String? uuid;
|
||||
String modelName;
|
||||
List<SubspaceTemplateModel>? subspaceModels;
|
||||
final List<TagModel>? tags;
|
||||
final List<Tag>? tags;
|
||||
String internalId;
|
||||
String? createdAt;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [modelName, subspaceModels, tags];
|
||||
@ -21,6 +22,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
required this.modelName,
|
||||
this.subspaceModels,
|
||||
this.tags,
|
||||
this.createdAt,
|
||||
}) : internalId = internalId ?? const Uuid().v4();
|
||||
|
||||
factory SpaceTemplateModel.fromJson(Map<String, dynamic> json) {
|
||||
@ -28,6 +30,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
|
||||
return SpaceTemplateModel(
|
||||
uuid: json['uuid'] ?? '',
|
||||
createdAt: json['createdAt'] ?? '',
|
||||
internalId: internalId,
|
||||
modelName: json['modelName'] ?? '',
|
||||
subspaceModels: (json['subspaceModels'] as List<dynamic>?)
|
||||
@ -38,7 +41,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
[],
|
||||
tags: (json['tags'] as List<dynamic>?)
|
||||
?.where((item) => item is Map<String, dynamic>) // Validate type
|
||||
.map((item) => TagModel.fromJson(item as Map<String, dynamic>))
|
||||
.map((item) => Tag.fromJson(item as Map<String, dynamic>))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
@ -47,7 +50,7 @@ class SpaceTemplateModel extends Equatable {
|
||||
String? uuid,
|
||||
String? modelName,
|
||||
List<SubspaceTemplateModel>? subspaceModels,
|
||||
List<TagModel>? tags,
|
||||
List<Tag>? tags,
|
||||
String? internalId,
|
||||
}) {
|
||||
return SpaceTemplateModel(
|
||||
|
@ -1,11 +1,11 @@
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class SubspaceTemplateModel {
|
||||
final String? uuid;
|
||||
String subspaceName;
|
||||
final bool disabled;
|
||||
List<TagModel>? tags;
|
||||
List<Tag>? tags;
|
||||
String internalId;
|
||||
|
||||
SubspaceTemplateModel({
|
||||
@ -25,7 +25,7 @@ class SubspaceTemplateModel {
|
||||
internalId: internalId,
|
||||
disabled: json['disabled'] ?? false,
|
||||
tags: (json['tags'] as List<dynamic>?)
|
||||
?.map((item) => TagModel.fromJson(item))
|
||||
?.map((item) => Tag.fromJson(item))
|
||||
.toList() ??
|
||||
[],
|
||||
);
|
||||
@ -44,7 +44,7 @@ class SubspaceTemplateModel {
|
||||
String? uuid,
|
||||
String? subspaceName,
|
||||
bool? disabled,
|
||||
List<TagModel>? tags,
|
||||
List<Tag>? tags,
|
||||
String? internalId,
|
||||
}) {
|
||||
return SubspaceTemplateModel(
|
||||
|
@ -4,7 +4,7 @@ class CreateTagBodyModel {
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'tag': tag,
|
||||
'name': tag,
|
||||
'productUuid': productUuid,
|
||||
};
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
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/space_model/models/create_space_template_body_model.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class TagModel extends BaseTag {
|
||||
TagModel({
|
||||
String? uuid,
|
||||
required String? tag,
|
||||
ProductModel? product,
|
||||
String? internalId,
|
||||
String? location,
|
||||
}) : super(
|
||||
uuid: uuid,
|
||||
tag: tag,
|
||||
product: product,
|
||||
internalId: internalId,
|
||||
location: location,
|
||||
);
|
||||
factory TagModel.fromJson(Map<String, dynamic> json) {
|
||||
final String internalId = json['internalId'] ?? const Uuid().v4();
|
||||
|
||||
return TagModel(
|
||||
uuid: json['uuid'] ,
|
||||
internalId: internalId,
|
||||
tag: json['tag'] ?? '',
|
||||
product: json['product'] != null
|
||||
? ProductModel.fromMap(json['product'])
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
TagModel copyWith(
|
||||
{String? tag,
|
||||
ProductModel? product,
|
||||
String? uuid,
|
||||
String? location,
|
||||
String? internalId}) {
|
||||
return TagModel(
|
||||
tag: tag ?? this.tag,
|
||||
product: product ?? this.product,
|
||||
location: location ?? this.location,
|
||||
internalId: internalId ?? this.internalId,
|
||||
uuid:uuid?? this.uuid
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'uuid': uuid,
|
||||
'tag': tag,
|
||||
'product': product?.toMap(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extension TagModelExtensions on TagModel {
|
||||
TagBodyModel toTagBodyModel() {
|
||||
return TagBodyModel()
|
||||
..uuid = uuid
|
||||
..tag = tag ?? ''
|
||||
..productUuid = product?.uuid;
|
||||
}
|
||||
}
|
@ -5,12 +5,14 @@ class TagModelUpdate {
|
||||
final String? uuid;
|
||||
final String? tag;
|
||||
final String? productUuid;
|
||||
final String? newTagUuid;
|
||||
|
||||
TagModelUpdate({
|
||||
required this.action,
|
||||
this.uuid,
|
||||
this.tag,
|
||||
this.productUuid,
|
||||
this.newTagUuid,
|
||||
});
|
||||
|
||||
factory TagModelUpdate.fromJson(Map<String, dynamic> json) {
|
||||
@ -26,9 +28,10 @@ class TagModelUpdate {
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'action': action.value,
|
||||
'uuid': uuid, // Nullable field
|
||||
'tag': tag,
|
||||
'tagUuid': uuid,
|
||||
'name': tag,
|
||||
'productUuid': productUuid,
|
||||
'newTagUuid': newTagUuid
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
@ -12,8 +13,10 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
class SpaceModelPage extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
final Function(List<SpaceTemplateModel>)? onSpaceModelsUpdated;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated})
|
||||
const SpaceModelPage(
|
||||
{Key? key, this.products, this.onSpaceModelsUpdated, required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -60,6 +63,7 @@ class SpaceModelPage extends StatelessWidget {
|
||||
allTags: allTagValues,
|
||||
pageContext: context,
|
||||
otherSpaceModels: allSpaceModelNames,
|
||||
projectTags: projectTags,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -69,8 +73,7 @@ class SpaceModelPage extends StatelessWidget {
|
||||
}
|
||||
// Render existing space model
|
||||
final model = spaceModels[index];
|
||||
final otherModel =
|
||||
List<String>.from(allSpaceModelNames);
|
||||
final otherModel = List<String>.from(allSpaceModelNames);
|
||||
otherModel.remove(model.modelName);
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
@ -84,6 +87,7 @@ class SpaceModelPage extends StatelessWidget {
|
||||
otherSpaceModels: otherModel,
|
||||
pageContext: context,
|
||||
allSpaceModels: spaceModels,
|
||||
projectTags: projectTags,
|
||||
);
|
||||
},
|
||||
);
|
||||
@ -107,10 +111,8 @@ class SpaceModelPage extends StatelessWidget {
|
||||
return Center(
|
||||
child: Text(
|
||||
'Error: ${state.message}',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.warningRed),
|
||||
style:
|
||||
Theme.of(context).textTheme.bodySmall?.copyWith(color: ColorsManager.warningRed),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConfirmMergeDialog extends StatelessWidget {
|
||||
const ConfirmMergeDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Merge',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Are you sure you want to merge?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color: ColorsManager.whiteColors,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class ConfirmOverwriteDialog extends StatelessWidget {
|
||||
const ConfirmOverwriteDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Overwrite',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Are you sure you want to overwrite?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
'Selected spaces already have linked space \nmodel / sub-spaces and devices',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 14,
|
||||
color: ColorsManager.grayColor),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Cancel",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const LinkingSuccessful();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Ok",
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 16,
|
||||
color: ColorsManager.whiteColors,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart';
|
||||
@ -25,6 +26,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
final BuildContext? pageContext;
|
||||
final List<String>? otherSpaceModels;
|
||||
final List<SpaceTemplateModel>? allSpaceModels;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const CreateSpaceModelDialog(
|
||||
{Key? key,
|
||||
@ -33,7 +35,8 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
this.spaceModel,
|
||||
this.pageContext,
|
||||
this.otherSpaceModels,
|
||||
this.allSpaceModels})
|
||||
this.allSpaceModels,
|
||||
required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -68,8 +71,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
|
||||
spaceNameController.addListener(() {
|
||||
bloc.add(UpdateSpaceTemplateName(
|
||||
name: spaceNameController.text,
|
||||
allModels: otherSpaceModels ?? []));
|
||||
name: spaceNameController.text, allModels: otherSpaceModels ?? []));
|
||||
});
|
||||
|
||||
return bloc;
|
||||
@ -87,9 +89,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
spaceModel?.uuid == null
|
||||
? 'Create New Space Model'
|
||||
: 'Edit Space Model',
|
||||
spaceModel?.uuid == null ? 'Create New Space Model' : 'Edit Space Model',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.headlineLarge
|
||||
@ -101,10 +101,8 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
child: TextField(
|
||||
controller: spaceNameController,
|
||||
onChanged: (value) {
|
||||
context.read<CreateSpaceModelBloc>().add(
|
||||
UpdateSpaceTemplateName(
|
||||
name: value,
|
||||
allModels: otherSpaceModels ?? []));
|
||||
context.read<CreateSpaceModelBloc>().add(UpdateSpaceTemplateName(
|
||||
name: value, allModels: otherSpaceModels ?? []));
|
||||
},
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
@ -157,6 +155,7 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
pageContext: pageContext,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
allSpaceModels: allSpaceModels,
|
||||
projectTags: projectTags,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(
|
||||
@ -179,84 +178,55 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
!isNameValid)
|
||||
? null
|
||||
: () {
|
||||
final updatedSpaceTemplate =
|
||||
updatedSpaceModel.copyWith(
|
||||
modelName:
|
||||
spaceNameController.text.trim(),
|
||||
final updatedSpaceTemplate = updatedSpaceModel.copyWith(
|
||||
modelName: spaceNameController.text.trim(),
|
||||
);
|
||||
if (updatedSpaceModel.uuid == null) {
|
||||
context
|
||||
.read<CreateSpaceModelBloc>()
|
||||
.add(
|
||||
context.read<CreateSpaceModelBloc>().add(
|
||||
CreateSpaceTemplate(
|
||||
spaceTemplate:
|
||||
updatedSpaceTemplate,
|
||||
spaceTemplate: updatedSpaceTemplate,
|
||||
onCreate: (newModel) {
|
||||
if (pageContext != null) {
|
||||
pageContext!.read<SpaceModelBloc>().add(
|
||||
CreateSpaceModel(newSpaceModel: newModel));
|
||||
pageContext!
|
||||
.read<SpaceModelBloc>()
|
||||
.add(CreateSpaceModel(
|
||||
newSpaceModel:
|
||||
newModel));
|
||||
pageContext!
|
||||
.read<
|
||||
SpaceManagementBloc>()
|
||||
.add(
|
||||
UpdateSpaceModelCache(
|
||||
newModel));
|
||||
.read<SpaceManagementBloc>()
|
||||
.add(UpdateSpaceModelCache(newModel));
|
||||
}
|
||||
Navigator.of(context)
|
||||
.pop(); // Close the dialog
|
||||
Navigator.of(context).pop(); // Close the dialog
|
||||
},
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (pageContext != null) {
|
||||
final currentState = pageContext!
|
||||
.read<SpaceModelBloc>()
|
||||
.state;
|
||||
if (currentState
|
||||
is SpaceModelLoaded) {
|
||||
final spaceModels =
|
||||
List<SpaceTemplateModel>.from(
|
||||
currentState.spaceModels);
|
||||
final currentState =
|
||||
pageContext!.read<SpaceModelBloc>().state;
|
||||
if (currentState is SpaceModelLoaded) {
|
||||
final spaceModels = List<SpaceTemplateModel>.from(
|
||||
currentState.spaceModels);
|
||||
|
||||
final SpaceTemplateModel?
|
||||
currentSpaceModel = spaceModels
|
||||
.cast<SpaceTemplateModel?>()
|
||||
.firstWhere(
|
||||
(sm) =>
|
||||
sm?.uuid ==
|
||||
updatedSpaceModel
|
||||
.uuid,
|
||||
final SpaceTemplateModel? currentSpaceModel =
|
||||
spaceModels.cast<SpaceTemplateModel?>().firstWhere(
|
||||
(sm) => sm?.uuid == updatedSpaceModel.uuid,
|
||||
orElse: () => null,
|
||||
);
|
||||
if (currentSpaceModel != null) {
|
||||
context
|
||||
.read<CreateSpaceModelBloc>()
|
||||
.add(ModifySpaceTemplate(
|
||||
spaceTemplate:
|
||||
currentSpaceModel,
|
||||
updatedSpaceTemplate:
|
||||
updatedSpaceTemplate,
|
||||
spaceTemplate: currentSpaceModel,
|
||||
updatedSpaceTemplate: updatedSpaceTemplate,
|
||||
onUpdate: (newModel) {
|
||||
if (pageContext !=
|
||||
null) {
|
||||
pageContext!
|
||||
.read<
|
||||
SpaceModelBloc>()
|
||||
.add(UpdateSpaceModel(
|
||||
if (pageContext != null) {
|
||||
pageContext!.read<SpaceModelBloc>().add(
|
||||
UpdateSpaceModel(
|
||||
spaceModelUuid:
|
||||
newModel.uuid ??
|
||||
''));
|
||||
newModel.uuid ?? ''));
|
||||
pageContext!
|
||||
.read<
|
||||
SpaceManagementBloc>()
|
||||
.add(UpdateSpaceModelCache(
|
||||
newModel));
|
||||
.read<SpaceManagementBloc>()
|
||||
.add(UpdateSpaceModelCache(newModel));
|
||||
}
|
||||
Navigator.of(context)
|
||||
.pop();
|
||||
Navigator.of(context).pop();
|
||||
}));
|
||||
}
|
||||
}
|
||||
@ -265,11 +235,11 @@ class CreateSpaceModelDialog extends StatelessWidget {
|
||||
},
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
borderRadius: 10,
|
||||
foregroundColor: ((state.errorMessage != null &&
|
||||
state.errorMessage != '') ||
|
||||
!isNameValid)
|
||||
? ColorsManager.whiteColorsWithOpacity
|
||||
: ColorsManager.whiteColors,
|
||||
foregroundColor:
|
||||
((state.errorMessage != null && state.errorMessage != '') ||
|
||||
!isNameValid)
|
||||
? ColorsManager.whiteColorsWithOpacity
|
||||
: ColorsManager.whiteColors,
|
||||
child: const Text('OK'),
|
||||
),
|
||||
),
|
||||
|
@ -0,0 +1,75 @@
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
|
||||
|
||||
class CustomLoadingIndicator extends StatefulWidget {
|
||||
@override
|
||||
_CustomLoadingIndicatorState createState() => _CustomLoadingIndicatorState();
|
||||
}
|
||||
|
||||
class _CustomLoadingIndicatorState extends State<CustomLoadingIndicator>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: const Duration(seconds: 1),
|
||||
)..repeat();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
width: 50,
|
||||
height: 50,
|
||||
child: AnimatedBuilder(
|
||||
animation: _controller,
|
||||
builder: (context, child) {
|
||||
return Transform.rotate(
|
||||
angle: _controller.value * 2 * pi,
|
||||
child: CustomPaint(
|
||||
painter: LoadingPainter(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoadingPainter extends CustomPainter {
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint()
|
||||
..strokeWidth = 5
|
||||
..strokeCap = StrokeCap.round
|
||||
..style = PaintingStyle.stroke;
|
||||
|
||||
final double radius = size.width / 2;
|
||||
final Offset center = Offset(size.width / 2, size.height / 2);
|
||||
|
||||
for (int i = 0; i < 12; i++) {
|
||||
final double angle = (i * 30) * (pi / 180);
|
||||
final double startX = center.dx + radius * cos(angle);
|
||||
final double startY = center.dy + radius * sin(angle);
|
||||
final double endX = center.dx + (radius - 8) * cos(angle);
|
||||
final double endY = center.dy + (radius - 8) * sin(angle);
|
||||
|
||||
paint.color = Colors.blue.withOpacity(i / 12); // Gradient effect
|
||||
canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(CustomPainter oldDelegate) => true;
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class LinkSpaceModelSpacesDialog extends StatefulWidget {
|
||||
final SpaceTemplateModel spaceModel;
|
||||
|
||||
const LinkSpaceModelSpacesDialog({super.key, required this.spaceModel});
|
||||
|
||||
@override
|
||||
State<LinkSpaceModelSpacesDialog> createState() =>
|
||||
_LinkSpaceModelSpacesDialogState();
|
||||
}
|
||||
|
||||
class _LinkSpaceModelSpacesDialogState
|
||||
extends State<LinkSpaceModelSpacesDialog> {
|
||||
@override
|
||||
void initState() {
|
||||
context.read<LinkSpaceToModelBloc>().add(LinkSpaceModelSelectedIdsEvent());
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
backgroundColor: Colors.white,
|
||||
content: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDialogContent(),
|
||||
_buildActionButtons(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDialogContent() {
|
||||
return Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.4,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Center(
|
||||
child: Text(
|
||||
"Link Space Model to Spaces",
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.blueAccent,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
const SizedBox(height: 16),
|
||||
_buildDetailRow(
|
||||
"Space model name:", widget.spaceModel.modelName),
|
||||
_buildDetailRow("Creation date and time:",
|
||||
widget.spaceModel.createdAt.toString()),
|
||||
_buildDetailRow("Created by:", "Admin"),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
"Link to:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Text(
|
||||
"Please select all the spaces where you would like to link the Routine.",
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 7,
|
||||
child: Container(
|
||||
color: ColorsManager.whiteColors,
|
||||
child: SpaceTreeView(
|
||||
isSide: true,
|
||||
onSelect: () {
|
||||
context
|
||||
.read<LinkSpaceToModelBloc>()
|
||||
.add(
|
||||
LinkSpaceModelSelectedIdsEvent());
|
||||
})))
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
right: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _buildButton("Cancel", Colors.grey, () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 50,
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 0.5,
|
||||
),
|
||||
top: BorderSide(
|
||||
color: ColorsManager.grayColor,
|
||||
width: 1.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
child: _buildButton(
|
||||
"Confirm",
|
||||
ColorsManager.onSecondaryColor,
|
||||
() {
|
||||
final spaceModelBloc = context.read<LinkSpaceToModelBloc>();
|
||||
if (!spaceModelBloc.hasSelectedSpaces) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text("Please select at least one space")),
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
spaceModelBloc.add(ValidateSpaceModelEvent(context: context));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
label,
|
||||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, color: Colors.black),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildButton(String text, Color color, VoidCallback onPressed) {
|
||||
return InkWell(
|
||||
onTap: onPressed,
|
||||
child: Center(
|
||||
child: Text(
|
||||
text,
|
||||
style:
|
||||
TextStyle(color: color, fontWeight: FontWeight.w400, fontSize: 14),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class LinkingAttentionDialog extends StatelessWidget {
|
||||
const LinkingAttentionDialog({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: Text(
|
||||
'Linking Attention',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 30),
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Do you want to merge or overwrite?',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'Selected spaces already have commissioned Devices',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 14),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// Cancel Button
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const ConfirmOverwriteDialog();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Overwrite",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
|
||||
// OK Button
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const ConfirmMergeDialog();
|
||||
},
|
||||
);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: ColorsManager.boxColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: Text(
|
||||
"Merge",
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class LinkingSuccessful extends StatelessWidget {
|
||||
const LinkingSuccessful({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.successIcon,
|
||||
)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'Linking successful',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w400, fontSize: 18),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
|
||||
void showOverwriteDialog(
|
||||
BuildContext context, LinkSpaceToModelBloc bloc, SpaceTemplateModel model) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return SizedBox(
|
||||
child: Dialog(
|
||||
shape:
|
||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
elevation: 10,
|
||||
backgroundColor: Colors.white,
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.3,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text(
|
||||
"Overwrite",
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
const Text(
|
||||
"Are you sure you want to overwrite?",
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
"Selected spaces already have linked space model / sub-spaces and devices",
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: Colors.grey[200],
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child: const Text(
|
||||
"Cancel",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
bloc.add(LinkSpaceModelEvent(
|
||||
isOverWrite: true,
|
||||
selectedSpaceMode: model.uuid));
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
backgroundColor: Colors.blue,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 3,
|
||||
),
|
||||
child: const Text(
|
||||
"OK",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
@ -1,13 +1,18 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.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/space_model/bloc/space_model_bloc.dart';
|
||||
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_product_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart';
|
||||
@ -69,48 +74,174 @@ class SpaceModelCardWidget extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
model.modelName,
|
||||
style:
|
||||
Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (!topActionsDisabled)
|
||||
GestureDetector(
|
||||
onTap: () => _showDeleteDialog(context),
|
||||
child: Container(
|
||||
width: 36, // Adjust size as needed
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
model.modelName,
|
||||
style: Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
child: Center(
|
||||
child: SvgPicture.asset(
|
||||
Assets.deleteSpaceModel, // Your actual SVG path
|
||||
width: 20,
|
||||
height: 20,
|
||||
colorFilter: const ColorFilter.mode(
|
||||
Colors.grey, BlendMode.srcIn),
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return BlocProvider<LinkSpaceToModelBloc>(
|
||||
create: (_) => LinkSpaceToModelBloc(),
|
||||
child: BlocListener<LinkSpaceToModelBloc,
|
||||
LinkSpaceToModelState>(
|
||||
listener: (context, state) {
|
||||
final _bloc =
|
||||
BlocProvider.of<LinkSpaceToModelBloc>(
|
||||
context);
|
||||
if (state is SpaceModelLoading) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(20)),
|
||||
elevation: 10,
|
||||
backgroundColor: Colors.white,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(
|
||||
vertical: 30,
|
||||
horizontal: 50),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
CustomLoadingIndicator(),
|
||||
const SizedBox(height: 20),
|
||||
const Text(
|
||||
"Linking in progress",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.w500,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (state
|
||||
is AlreadyHaveLinkedState) {
|
||||
Navigator.of(dialogContext).pop();
|
||||
showOverwriteDialog(
|
||||
context, _bloc, model);
|
||||
} else if (state
|
||||
is SpaceValidationSuccess) {
|
||||
_bloc.add(LinkSpaceModelEvent(
|
||||
isOverWrite: false,
|
||||
selectedSpaceMode: model.uuid));
|
||||
|
||||
Future.delayed(const Duration(seconds: 1),
|
||||
() {
|
||||
Navigator.of(dialogContext).pop();
|
||||
Navigator.of(dialogContext).pop();
|
||||
Navigator.of(dialogContext).pop();
|
||||
});
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const LinkingSuccessful();
|
||||
},
|
||||
).then((v) {
|
||||
Future.delayed(
|
||||
const Duration(seconds: 2), () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
});
|
||||
});
|
||||
} else if (state is SpaceModelLinkSuccess) {
|
||||
Navigator.of(dialogContext).pop();
|
||||
Navigator.of(dialogContext).pop();
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext dialogContext) {
|
||||
return const LinkingSuccessful();
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
child: LinkSpaceModelSpacesDialog(
|
||||
spaceModel: model,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.spaceLinkIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!topActionsDisabled)
|
||||
InkWell(
|
||||
onTap: () {
|
||||
_showDeleteDialog(context);
|
||||
},
|
||||
child: SvgPicture.asset(
|
||||
Assets.deleteSpaceLinkIcon,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Expanded(
|
||||
// child: Text(
|
||||
// model.modelName,
|
||||
// style:
|
||||
// Theme.of(context).textTheme.headlineMedium?.copyWith(
|
||||
// color: Colors.black,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// maxLines: 1,
|
||||
// overflow: TextOverflow.ellipsis,
|
||||
// ),
|
||||
// ),
|
||||
// if (!topActionsDisabled)
|
||||
// GestureDetector(
|
||||
// onTap: () => _showDeleteDialog(context),
|
||||
// child: Container(
|
||||
// width: 36, // Adjust size as needed
|
||||
// height: 36,
|
||||
// decoration: BoxDecoration(
|
||||
// shape: BoxShape.circle,
|
||||
// color: Colors.white,
|
||||
// boxShadow: [
|
||||
// BoxShadow(
|
||||
// color: Colors.black.withOpacity(0.1),
|
||||
// spreadRadius: 2,
|
||||
// blurRadius: 5,
|
||||
// offset: const Offset(0, 2),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// child: Center(
|
||||
// child: SvgPicture.asset(
|
||||
// Assets.deleteSpaceModel, // Your actual SVG path
|
||||
// width: 20,
|
||||
// height: 20,
|
||||
// colorFilter: const ColorFilter.mode(
|
||||
// Colors.grey, BlendMode.srcIn),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
if (!showOnlyName) ...[
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/common/edit_chip.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart';
|
||||
@ -10,9 +10,9 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
class SubspaceModelCreate extends StatefulWidget {
|
||||
final List<SubspaceTemplateModel> subspaces;
|
||||
final void Function(
|
||||
List<SubspaceTemplateModel> newSubspaces, List<TagModel>? tags)?
|
||||
List<SubspaceTemplateModel> newSubspaces, List<Tag>? tags)?
|
||||
onSpaceModelUpdate;
|
||||
final List<TagModel> tags;
|
||||
final List<Tag> tags;
|
||||
|
||||
const SubspaceModelCreate({
|
||||
Key? key,
|
||||
@ -28,7 +28,7 @@ class SubspaceModelCreate extends StatefulWidget {
|
||||
class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
||||
late List<SubspaceTemplateModel> _subspaces;
|
||||
String? errorSubspaceId;
|
||||
late List<TagModel> _tags;
|
||||
late List<Tag> _tags;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -117,7 +117,7 @@ class _SubspaceModelCreateState extends State<SubspaceModelCreate> {
|
||||
.where((s) => !updatedIds.contains(s.internalId))
|
||||
.toList();
|
||||
|
||||
final List<TagModel> tagsToAppendToSpace = [];
|
||||
final List<Tag> tagsToAppendToSpace = [];
|
||||
|
||||
for (var s in deletedSubspaces) {
|
||||
if (s.tags != null) {
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:syncrow_web/common/edit_chip.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
@ -20,6 +21,7 @@ class TagChipDisplay extends StatelessWidget {
|
||||
final BuildContext? pageContext;
|
||||
final List<String>? otherSpaceModels;
|
||||
final List<SpaceTemplateModel>? allSpaceModels;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const TagChipDisplay(BuildContext context,
|
||||
{Key? key,
|
||||
@ -31,14 +33,14 @@ class TagChipDisplay extends StatelessWidget {
|
||||
required this.spaceNameController,
|
||||
this.pageContext,
|
||||
this.otherSpaceModels,
|
||||
this.allSpaceModels})
|
||||
this.allSpaceModels,
|
||||
required this.projectTags})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return (spaceModel?.tags?.isNotEmpty == true ||
|
||||
spaceModel?.subspaceModels
|
||||
?.any((subspace) => subspace.tags?.isNotEmpty == true) ==
|
||||
spaceModel?.subspaceModels?.any((subspace) => subspace.tags?.isNotEmpty == true) ==
|
||||
true)
|
||||
? SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
@ -59,8 +61,7 @@ class TagChipDisplay extends StatelessWidget {
|
||||
// Combine tags from spaceModel and subspaces
|
||||
...TagHelper.groupTags([
|
||||
...?spaceModel?.tags,
|
||||
...?spaceModel?.subspaceModels
|
||||
?.expand((subspace) => subspace.tags ?? [])
|
||||
...?spaceModel?.subspaceModels?.expand((subspace) => subspace.tags ?? [])
|
||||
]).entries.map(
|
||||
(entry) => Chip(
|
||||
avatar: SizedBox(
|
||||
@ -76,9 +77,7 @@ class TagChipDisplay extends StatelessWidget {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall!
|
||||
.copyWith(
|
||||
color:
|
||||
ColorsManager.spaceColor),
|
||||
.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -105,13 +104,12 @@ class TagChipDisplay extends StatelessWidget {
|
||||
spaceModel: spaceModel,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
initialTags: TagHelper.generateInitialTags(
|
||||
subspaces: subspaces,
|
||||
spaceTagModels: spaceModel?.tags ?? []),
|
||||
subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []),
|
||||
title: 'Edit Device',
|
||||
addedProducts:
|
||||
TagHelper.createInitialSelectedProducts(
|
||||
spaceModel?.tags ?? [], subspaces),
|
||||
addedProducts: TagHelper.createInitialSelectedProducts(
|
||||
spaceModel?.tags ?? [], subspaces),
|
||||
spaceName: spaceModel?.modelName ?? '',
|
||||
projectTags: projectTags,
|
||||
));
|
||||
})
|
||||
],
|
||||
@ -134,6 +132,7 @@ class TagChipDisplay extends StatelessWidget {
|
||||
isCreate: true,
|
||||
spaceModel: spaceModel,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
projectTags: projectTags,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
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/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
|
||||
abstract class AddDeviceModelState extends Equatable {
|
||||
const AddDeviceModelState();
|
||||
@ -15,7 +15,7 @@ class AddDeviceModelLoading extends AddDeviceModelState {}
|
||||
|
||||
class AddDeviceModelLoaded extends AddDeviceModelState {
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final List<TagModel> initialTag;
|
||||
final List<Tag> initialTag;
|
||||
|
||||
const AddDeviceModelLoaded({
|
||||
required this.selectedProducts,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
|
||||
abstract class AddDeviceTypeModelEvent extends Equatable {
|
||||
const AddDeviceTypeModelEvent();
|
||||
@ -25,7 +25,7 @@ class UpdateProductCountEvent extends AddDeviceTypeModelEvent {
|
||||
|
||||
|
||||
class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent {
|
||||
final List<TagModel> initialTags;
|
||||
final List<Tag> initialTags;
|
||||
final List<SelectedProduct> addedProducts;
|
||||
|
||||
const InitializeDeviceTypeModel({
|
||||
|
@ -2,13 +2,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_state.dart';
|
||||
@ -20,7 +20,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
final List<ProductModel>? products;
|
||||
final List<SelectedProduct>? initialSelectedProducts;
|
||||
final List<SubspaceTemplateModel>? subspaces;
|
||||
final List<TagModel>? spaceTagModels;
|
||||
final List<Tag>? spaceTagModels;
|
||||
final List<String>? allTags;
|
||||
final String spaceName;
|
||||
final bool isCreate;
|
||||
@ -28,6 +28,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
final BuildContext? pageContext;
|
||||
final SpaceTemplateModel? spaceModel;
|
||||
final List<SpaceTemplateModel>? allSpaceModels;
|
||||
final List<Tag> projectTags;
|
||||
|
||||
const AddDeviceTypeModelWidget(
|
||||
{super.key,
|
||||
@ -41,7 +42,8 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
this.pageContext,
|
||||
this.otherSpaceModels,
|
||||
this.spaceModel,
|
||||
this.allSpaceModels});
|
||||
this.allSpaceModels,
|
||||
required this.projectTags});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -78,8 +80,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
||||
child: ScrollableGridViewWidget(
|
||||
isCreate: isCreate,
|
||||
products: products,
|
||||
@ -112,6 +113,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
allSpaceModels: allSpaceModels,
|
||||
products: products,
|
||||
allTags: allTags,
|
||||
projectTags: projectTags,
|
||||
pageContext: pageContext,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
spaceModel: SpaceTemplateModel(
|
||||
@ -137,6 +139,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
subspaces: subspaces,
|
||||
addedProducts: initialSelectedProducts ?? [],
|
||||
allTags: allTags,
|
||||
projectTags: projectTags,
|
||||
spaceName: spaceName,
|
||||
initialTags: initialTags,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
@ -149,11 +152,10 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
),
|
||||
SizedBox(
|
||||
width: 140,
|
||||
child:
|
||||
BlocBuilder<AddDeviceTypeModelBloc, AddDeviceModelState>(
|
||||
child: BlocBuilder<AddDeviceTypeModelBloc, AddDeviceModelState>(
|
||||
builder: (context, state) {
|
||||
final isDisabled = state is AddDeviceModelLoaded &&
|
||||
state.selectedProducts.isEmpty;
|
||||
final isDisabled =
|
||||
state is AddDeviceModelLoaded && state.selectedProducts.isEmpty;
|
||||
|
||||
return DefaultButton(
|
||||
backgroundColor: ColorsManager.secondaryColor,
|
||||
@ -166,15 +168,13 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
: () async {
|
||||
if (state is AddDeviceModelLoaded &&
|
||||
state.selectedProducts.isNotEmpty) {
|
||||
final initialTags =
|
||||
TagHelper.generateInitialTags(
|
||||
final initialTags = TagHelper.generateInitialTags(
|
||||
spaceTagModels: spaceTagModels,
|
||||
subspaces: subspaces,
|
||||
);
|
||||
|
||||
final dialogTitle = initialTags.isNotEmpty
|
||||
? 'Edit Device'
|
||||
: 'Assign Tags';
|
||||
final dialogTitle =
|
||||
initialTags.isNotEmpty ? 'Edit Device' : 'Assign Tags';
|
||||
Navigator.of(context).pop();
|
||||
await showDialog<bool>(
|
||||
context: context,
|
||||
@ -184,6 +184,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget {
|
||||
subspaces: subspaces,
|
||||
addedProducts: state.selectedProducts,
|
||||
allTags: allTags,
|
||||
projectTags: projectTags,
|
||||
spaceName: spaceName,
|
||||
initialTags: initialTags,
|
||||
otherSpaceModels: otherSpaceModels,
|
||||
|
Reference in New Issue
Block a user