mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
fix add subspace bugs and plusButton widget
This commit is contained in:
@ -67,7 +67,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
_nameController = TextEditingController(
|
_nameController = TextEditingController(
|
||||||
text: widget.selectedCommunity?.name ?? '',
|
text: widget.selectedCommunity?.name ?? '',
|
||||||
@ -96,13 +97,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
if (oldWidget.spaces != widget.spaces) {
|
if (oldWidget.spaces != widget.spaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||||
connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
connections =
|
||||||
|
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||||
_adjustCanvasSizeForSpaces();
|
_adjustCanvasSizeForSpaces();
|
||||||
realignTree();
|
realignTree();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) {
|
if (widget.selectedSpace != oldWidget.selectedSpace &&
|
||||||
|
widget.selectedSpace != null) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_moveToSpace(widget.selectedSpace!);
|
_moveToSpace(widget.selectedSpace!);
|
||||||
});
|
});
|
||||||
@ -185,7 +188,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
connection, widget.selectedSpace)
|
connection, widget.selectedSpace)
|
||||||
? 1.0
|
? 1.0
|
||||||
: 0.3, // Adjust opacity
|
: 0.3, // Adjust opacity
|
||||||
child: CustomPaint(painter: CurvedLinePainter([connection])),
|
child: CustomPaint(
|
||||||
|
painter: CurvedLinePainter([connection])),
|
||||||
),
|
),
|
||||||
for (var entry in spaces.asMap().entries)
|
for (var entry in spaces.asMap().entries)
|
||||||
if (entry.value.status != SpaceStatus.deleted &&
|
if (entry.value.status != SpaceStatus.deleted &&
|
||||||
@ -195,9 +199,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
top: entry.value.position.dy,
|
top: entry.value.position.dy,
|
||||||
child: SpaceCardWidget(
|
child: SpaceCardWidget(
|
||||||
index: entry.key,
|
index: entry.key,
|
||||||
onButtonTap: (int index, Offset newPosition, String direction) {
|
onButtonTap: (int index, Offset newPosition,
|
||||||
|
String direction) {
|
||||||
_showCreateSpaceDialog(screenSize,
|
_showCreateSpaceDialog(screenSize,
|
||||||
position: spaces[index].position + newPosition,
|
position:
|
||||||
|
spaces[index].position + newPosition,
|
||||||
parentIndex: index,
|
parentIndex: index,
|
||||||
direction: direction,
|
direction: direction,
|
||||||
projectTags: widget.projectTags);
|
projectTags: widget.projectTags);
|
||||||
@ -210,8 +216,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
_updateNodePosition(entry.value, newPosition);
|
_updateNodePosition(entry.value, newPosition);
|
||||||
},
|
},
|
||||||
buildSpaceContainer: (int index) {
|
buildSpaceContainer: (int index) {
|
||||||
final bool isHighlighted = SpaceHelper.isHighlightedSpace(
|
final bool isHighlighted =
|
||||||
spaces[index], widget.selectedSpace);
|
SpaceHelper.isHighlightedSpace(
|
||||||
|
spaces[index], widget.selectedSpace);
|
||||||
|
|
||||||
return Opacity(
|
return Opacity(
|
||||||
opacity: isHighlighted ? 1.0 : 0.3,
|
opacity: isHighlighted ? 1.0 : 0.3,
|
||||||
@ -299,19 +306,25 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
return CreateSpaceDialog(
|
return CreateSpaceDialog(
|
||||||
products: widget.products,
|
products: widget.products,
|
||||||
spaceModels: widget.spaceModels,
|
spaceModels: widget.spaceModels,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags:
|
||||||
|
TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
||||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||||
projectTags: projectTags,
|
projectTags: projectTags,
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
onCreateSpace: (String name,
|
||||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
String icon,
|
||||||
|
List<SelectedProduct> selectedProducts,
|
||||||
|
SpaceTemplateModel? spaceModel,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Set the first space in the center or use passed position
|
// Set the first space in the center or use passed position
|
||||||
Offset newPosition;
|
Offset newPosition;
|
||||||
if (parentIndex != null) {
|
if (parentIndex != null) {
|
||||||
newPosition =
|
newPosition = getBalancedChildPosition(
|
||||||
getBalancedChildPosition(spaces[parentIndex]); // Ensure balanced position
|
spaces[parentIndex]); // Ensure balanced position
|
||||||
} else {
|
} else {
|
||||||
newPosition = position ?? ConnectionHelper.getCenterPosition(screenSize);
|
newPosition =
|
||||||
|
position ?? ConnectionHelper.getCenterPosition(screenSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel newSpace = SpaceModel(
|
SpaceModel newSpace = SpaceModel(
|
||||||
@ -360,16 +373,21 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
name: widget.selectedSpace!.name,
|
name: widget.selectedSpace!.name,
|
||||||
icon: widget.selectedSpace!.icon,
|
icon: widget.selectedSpace!.icon,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
parentSpace:
|
parentSpace: SpaceHelper.findSpaceByInternalId(
|
||||||
SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces),
|
widget.selectedSpace?.parent?.internalId, spaces),
|
||||||
editSpace: widget.selectedSpace,
|
editSpace: widget.selectedSpace,
|
||||||
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
currentSpaceModel: widget.selectedSpace?.spaceModel,
|
||||||
tags: widget.selectedSpace?.tags,
|
tags: widget.selectedSpace?.tags,
|
||||||
subspaces: widget.selectedSpace?.subspaces,
|
subspaces: widget.selectedSpace?.subspaces,
|
||||||
isEdit: true,
|
isEdit: true,
|
||||||
allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels),
|
allTags: TagHelper.getAllTagValues(
|
||||||
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts,
|
widget.communities, widget.spaceModels),
|
||||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) {
|
onCreateSpace: (String name,
|
||||||
|
String icon,
|
||||||
|
List<SelectedProduct> selectedProducts,
|
||||||
|
SpaceTemplateModel? spaceModel,
|
||||||
|
List<SubspaceModel>? subspaces,
|
||||||
|
List<Tag>? tags) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// Update the space's properties
|
// Update the space's properties
|
||||||
widget.selectedSpace!.name = name;
|
widget.selectedSpace!.name = name;
|
||||||
@ -379,7 +397,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
widget.selectedSpace!.tags = tags;
|
widget.selectedSpace!.tags = tags;
|
||||||
|
|
||||||
if (widget.selectedSpace!.status != SpaceStatus.newSpace) {
|
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) {
|
for (var space in spaces) {
|
||||||
@ -410,7 +429,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
Map<String, SpaceModel> idToSpace = {};
|
Map<String, SpaceModel> idToSpace = {};
|
||||||
|
|
||||||
void flatten(SpaceModel space) {
|
void flatten(SpaceModel space) {
|
||||||
if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) {
|
if (space.status == SpaceStatus.deleted ||
|
||||||
|
space.status == SpaceStatus.parentDeleted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
result.add(space);
|
result.add(space);
|
||||||
@ -532,13 +552,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
void _selectSpace(BuildContext context, SpaceModel space) {
|
void _selectSpace(BuildContext context, SpaceModel space) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity,
|
||||||
|
selectedSpace: space),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deselectSpace(BuildContext context) {
|
void _deselectSpace(BuildContext context) {
|
||||||
context.read<SpaceManagementBloc>().add(
|
context.read<SpaceManagementBloc>().add(
|
||||||
SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
SelectSpaceEvent(
|
||||||
|
selectedCommunity: widget.selectedCommunity, selectedSpace: null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -708,7 +731,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
|
|
||||||
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
SpaceModel duplicated = _deepCloneSpaceTree(space, parent: parent);
|
||||||
|
|
||||||
duplicated.position = Offset(space.position.dx + 300, space.position.dy + 100);
|
duplicated.position =
|
||||||
|
Offset(space.position.dx + 300, space.position.dy + 100);
|
||||||
List<SpaceModel> duplicatedSubtree = [];
|
List<SpaceModel> duplicatedSubtree = [];
|
||||||
void collectSubtree(SpaceModel node) {
|
void collectSubtree(SpaceModel node) {
|
||||||
duplicatedSubtree.add(node);
|
duplicatedSubtree.add(node);
|
||||||
@ -739,7 +763,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
SpaceModel _deepCloneSpaceTree(SpaceModel original, {SpaceModel? parent}) {
|
||||||
final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
final duplicatedName =
|
||||||
|
SpaceHelper.generateUniqueSpaceName(original.name, spaces);
|
||||||
|
|
||||||
final newSpace = SpaceModel(
|
final newSpace = SpaceModel(
|
||||||
name: duplicatedName,
|
name: duplicatedName,
|
||||||
|
@ -82,8 +82,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
super.initState();
|
super.initState();
|
||||||
selectedIcon = widget.icon ?? Assets.location;
|
selectedIcon = widget.icon ?? Assets.location;
|
||||||
nameController = TextEditingController(text: widget.name ?? '');
|
nameController = TextEditingController(text: widget.name ?? '');
|
||||||
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
selectedProducts =
|
||||||
isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
|
||||||
|
isOkButtonEnabled =
|
||||||
|
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||||
if (widget.currentSpaceModel != null) {
|
if (widget.currentSpaceModel != null) {
|
||||||
subspaces = [];
|
subspaces = [];
|
||||||
tags = [];
|
tags = [];
|
||||||
@ -96,13 +98,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isSpaceModelDisabled =
|
bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty ||
|
||||||
(tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty);
|
subspaces != null && subspaces!.isNotEmpty);
|
||||||
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null);
|
||||||
|
|
||||||
final screenWidth = MediaQuery.of(context).size.width;
|
final screenWidth = MediaQuery.of(context).size.width;
|
||||||
return AlertDialog(
|
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,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
content: SizedBox(
|
content: SizedBox(
|
||||||
width: screenWidth * 0.5,
|
width: screenWidth * 0.5,
|
||||||
@ -176,8 +180,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
isNameFieldInvalid = value.isEmpty;
|
isNameFieldInvalid = value.isEmpty;
|
||||||
|
|
||||||
if (!isNameFieldInvalid) {
|
if (!isNameFieldInvalid) {
|
||||||
if (SpaceHelper.isNameConflict(
|
if (SpaceHelper.isNameConflict(value,
|
||||||
value, widget.parentSpace, widget.editSpace)) {
|
widget.parentSpace, widget.editSpace)) {
|
||||||
isNameFieldExist = true;
|
isNameFieldExist = true;
|
||||||
isOkButtonEnabled = false;
|
isOkButtonEnabled = false;
|
||||||
} else {
|
} else {
|
||||||
@ -244,7 +248,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context);
|
isSpaceModelDisabled
|
||||||
|
? null
|
||||||
|
: _showLinkSpaceModelDialog(context);
|
||||||
},
|
},
|
||||||
child: ButtonContentWidget(
|
child: ButtonContentWidget(
|
||||||
svgAssets: Assets.link,
|
svgAssets: Assets.link,
|
||||||
@ -254,7 +260,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
)
|
)
|
||||||
: Container(
|
: Container(
|
||||||
width: screenWidth * 0.25,
|
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(
|
decoration: BoxDecoration(
|
||||||
color: ColorsManager.boxColor,
|
color: ColorsManager.boxColor,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
@ -269,7 +276,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodyMedium!
|
.bodyMedium!
|
||||||
.copyWith(color: ColorsManager.spaceColor),
|
.copyWith(
|
||||||
|
color: ColorsManager.spaceColor),
|
||||||
),
|
),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor: ColorsManager.whiteColors,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
@ -340,12 +348,12 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
isTagsAndSubspaceModelDisabled
|
isTagsAndSubspaceModelDisabled
|
||||||
? null
|
? null
|
||||||
: _showSubSpaceDialog(
|
: _showSubSpaceDialog(context, enteredName,
|
||||||
context, enteredName, [], false, widget.products, subspaces);
|
[], false, widget.products, subspaces);
|
||||||
},
|
},
|
||||||
child: ButtonContentWidget(
|
child: ButtonContentWidget(
|
||||||
icon: Icons.add,
|
icon: Icons.add,
|
||||||
label: 'Create Sub Space',
|
label: 'Create Sub Spaces',
|
||||||
disabled: isTagsAndSubspaceModelDisabled,
|
disabled: isTagsAndSubspaceModelDisabled,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -368,16 +376,22 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null)
|
if (subspaces != null)
|
||||||
...subspaces!.map((subspace) {
|
...subspaces!.map((subspace) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment:
|
||||||
|
CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SubspaceNameDisplayWidget(
|
SubspaceNameDisplayWidget(
|
||||||
text: subspace.subspaceName,
|
text: subspace.subspaceName,
|
||||||
validateName: (updatedName) {
|
validateName: (updatedName) {
|
||||||
bool nameExists = subspaces!.any((s) {
|
bool nameExists =
|
||||||
bool isSameId = s.internalId == subspace.internalId;
|
subspaces!.any((s) {
|
||||||
bool isSameName =
|
bool isSameId = s.internalId ==
|
||||||
s.subspaceName.trim().toLowerCase() ==
|
subspace.internalId;
|
||||||
updatedName.trim().toLowerCase();
|
bool isSameName = s.subspaceName
|
||||||
|
.trim()
|
||||||
|
.toLowerCase() ==
|
||||||
|
updatedName
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
|
||||||
return !isSameId && isSameName;
|
return !isSameId && isSameName;
|
||||||
});
|
});
|
||||||
@ -386,7 +400,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
},
|
},
|
||||||
onNameChanged: (updatedName) {
|
onNameChanged: (updatedName) {
|
||||||
setState(() {
|
setState(() {
|
||||||
subspace.subspaceName = updatedName;
|
subspace.subspaceName =
|
||||||
|
updatedName;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -395,8 +410,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
}),
|
}),
|
||||||
EditChip(
|
EditChip(
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
_showSubSpaceDialog(context, enteredName, [], true,
|
_showSubSpaceDialog(context, enteredName,
|
||||||
widget.products, subspaces);
|
[], true, widget.products, subspaces);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -405,7 +420,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
(tags?.isNotEmpty == true ||
|
(tags?.isNotEmpty == true ||
|
||||||
subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true)
|
subspaces?.any((subspace) =>
|
||||||
|
subspace.tags?.isNotEmpty == true) ==
|
||||||
|
true)
|
||||||
? SizedBox(
|
? SizedBox(
|
||||||
width: screenWidth * 0.25,
|
width: screenWidth * 0.25,
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -425,14 +442,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
// Combine tags from spaceModel and subspaces
|
// Combine tags from spaceModel and subspaces
|
||||||
...TagHelper.groupTags([
|
...TagHelper.groupTags([
|
||||||
...?tags,
|
...?tags,
|
||||||
...?subspaces?.expand((subspace) => subspace.tags ?? [])
|
...?subspaces?.expand(
|
||||||
|
(subspace) => subspace.tags ?? [])
|
||||||
]).entries.map(
|
]).entries.map(
|
||||||
(entry) => Chip(
|
(entry) => Chip(
|
||||||
avatar: SizedBox(
|
avatar: SizedBox(
|
||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
entry.key.icon ?? 'assets/icons/gateway.svg',
|
entry.key.icon ??
|
||||||
|
'assets/icons/gateway.svg',
|
||||||
fit: BoxFit.contain,
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -441,11 +460,15 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.bodySmall
|
.bodySmall
|
||||||
?.copyWith(color: ColorsManager.spaceColor),
|
?.copyWith(
|
||||||
|
color: ColorsManager
|
||||||
|
.spaceColor),
|
||||||
),
|
),
|
||||||
backgroundColor: ColorsManager.whiteColors,
|
backgroundColor:
|
||||||
|
ColorsManager.whiteColors,
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius:
|
||||||
|
BorderRadius.circular(16),
|
||||||
side: const BorderSide(
|
side: const BorderSide(
|
||||||
color: ColorsManager.spaceColor,
|
color: ColorsManager.spaceColor,
|
||||||
),
|
),
|
||||||
@ -460,15 +483,18 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
products: widget.products,
|
products: widget.products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
allTags: widget.allTags,
|
allTags: widget.allTags,
|
||||||
addedProducts:
|
addedProducts: TagHelper
|
||||||
TagHelper.createInitialSelectedProductsForTags(
|
.createInitialSelectedProductsForTags(
|
||||||
tags ?? [], subspaces),
|
tags ?? [], subspaces),
|
||||||
title: 'Edit Device',
|
title: 'Edit Device',
|
||||||
initialTags: TagHelper.generateInitialForTags(
|
initialTags:
|
||||||
spaceTags: tags, subspaces: subspaces),
|
TagHelper.generateInitialForTags(
|
||||||
|
spaceTags: tags,
|
||||||
|
subspaces: subspaces),
|
||||||
spaceName: widget.name ?? '',
|
spaceName: widget.name ?? '',
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
onSave: (updatedTags, updatedSubspaces) {
|
onSave:
|
||||||
|
(updatedTags, updatedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = updatedTags;
|
tags = updatedTags;
|
||||||
subspaces = updatedSubspaces;
|
subspaces = updatedSubspaces;
|
||||||
@ -529,17 +555,25 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
} else if (isNameFieldExist) {
|
} else if (isNameFieldExist) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
|
String newName = enteredName.isNotEmpty
|
||||||
|
? enteredName
|
||||||
|
: (widget.name ?? '');
|
||||||
if (newName.isNotEmpty) {
|
if (newName.isNotEmpty) {
|
||||||
widget.onCreateSpace(newName, selectedIcon, selectedProducts,
|
widget.onCreateSpace(
|
||||||
selectedSpaceModel, subspaces, tags);
|
newName,
|
||||||
|
selectedIcon,
|
||||||
|
selectedProducts,
|
||||||
|
selectedSpaceModel,
|
||||||
|
subspaces,
|
||||||
|
tags);
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
backgroundColor:
|
backgroundColor: isOkButtonEnabled
|
||||||
isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor,
|
? ColorsManager.secondaryColor
|
||||||
|
: ColorsManager.grayColor,
|
||||||
foregroundColor: ColorsManager.whiteColors,
|
foregroundColor: ColorsManager.whiteColors,
|
||||||
child: const Text('OK'),
|
child: const Text('OK'),
|
||||||
),
|
),
|
||||||
@ -586,24 +620,31 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showSubSpaceDialog(BuildContext context, String name, final List<Tag>? spaceTags,
|
void _showSubSpaceDialog(
|
||||||
bool isEdit, List<ProductModel>? products, final List<SubspaceModel>? existingSubSpaces) {
|
BuildContext context,
|
||||||
|
String name,
|
||||||
|
final List<Tag>? spaceTags,
|
||||||
|
bool isEdit,
|
||||||
|
List<ProductModel>? products,
|
||||||
|
final List<SubspaceModel>? existingSubSpaces) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CreateSubSpaceDialog(
|
return CreateSubSpaceDialog(
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
dialogTitle: isEdit ? 'Edit Sub-space' : 'Create Sub-space',
|
dialogTitle: isEdit ? 'Edit Sub-spaces' : 'Create Sub-spaces',
|
||||||
products: products,
|
products: products,
|
||||||
existingSubSpaces: existingSubSpaces,
|
existingSubSpaces: existingSubSpaces,
|
||||||
onSave: (slectedSubspaces) {
|
onSave: (slectedSubspaces) {
|
||||||
final List<Tag> tagsToAppendToSpace = [];
|
final List<Tag> tagsToAppendToSpace = [];
|
||||||
|
|
||||||
if (slectedSubspaces != null) {
|
if (slectedSubspaces != null && slectedSubspaces.isNotEmpty) {
|
||||||
final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet();
|
final updatedIds =
|
||||||
|
slectedSubspaces.map((s) => s.internalId).toSet();
|
||||||
if (existingSubSpaces != null) {
|
if (existingSubSpaces != null) {
|
||||||
final deletedSubspaces =
|
final deletedSubspaces = existingSubSpaces
|
||||||
existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList();
|
.where((s) => !updatedIds.contains(s.internalId))
|
||||||
|
.toList();
|
||||||
for (var s in deletedSubspaces) {
|
for (var s in deletedSubspaces) {
|
||||||
if (s.tags != null) {
|
if (s.tags != null) {
|
||||||
tagsToAppendToSpace.addAll(s.tags!);
|
tagsToAppendToSpace.addAll(s.tags!);
|
||||||
@ -623,15 +664,16 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showTagCreateDialog(
|
void _showTagCreateDialog(BuildContext context, String name, bool isEdit,
|
||||||
BuildContext context, String name, bool isEdit, List<ProductModel>? products) {
|
List<ProductModel>? products) {
|
||||||
isEdit
|
isEdit
|
||||||
? showDialog(
|
? showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AssignTagDialog(
|
return AssignTagDialog(
|
||||||
title: 'Edit Device',
|
title: 'Edit Device',
|
||||||
addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
addedProducts: TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
spaceName: name,
|
spaceName: name,
|
||||||
products: products,
|
products: products,
|
||||||
subspaces: subspaces,
|
subspaces: subspaces,
|
||||||
@ -646,7 +688,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -670,7 +713,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
allTags: widget.allTags,
|
allTags: widget.allTags,
|
||||||
projectTags: widget.projectTags,
|
projectTags: widget.projectTags,
|
||||||
initialSelectedProducts:
|
initialSelectedProducts:
|
||||||
TagHelper.createInitialSelectedProductsForTags(tags, subspaces),
|
TagHelper.createInitialSelectedProductsForTags(
|
||||||
|
tags, subspaces),
|
||||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||||
setState(() {
|
setState(() {
|
||||||
tags = selectedSpaceTags;
|
tags = selectedSpaceTags;
|
||||||
@ -680,7 +724,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
|||||||
if (subspaces != null) {
|
if (subspaces != null) {
|
||||||
for (final subspace in subspaces!) {
|
for (final subspace in subspaces!) {
|
||||||
for (final selectedSubspace in selectedSubspaces) {
|
for (final selectedSubspace in selectedSubspaces) {
|
||||||
if (subspace.subspaceName == selectedSubspace.subspaceName) {
|
if (subspace.subspaceName ==
|
||||||
|
selectedSubspace.subspaceName) {
|
||||||
subspace.tags = selectedSubspace.tags;
|
subspace.tags = selectedSubspace.tags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,36 +17,33 @@ class PlusButtonWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Positioned(
|
return GestureDetector(
|
||||||
left: offset.dx,
|
onTap: () {
|
||||||
top: offset.dy,
|
Offset newPosition;
|
||||||
child: GestureDetector(
|
switch (direction) {
|
||||||
onTap: () {
|
case 'left':
|
||||||
Offset newPosition;
|
newPosition = const Offset(-200, 0);
|
||||||
switch (direction) {
|
break;
|
||||||
case 'left':
|
case 'right':
|
||||||
newPosition = const Offset(-200, 0);
|
newPosition = const Offset(200, 0);
|
||||||
break;
|
break;
|
||||||
case 'right':
|
case 'down':
|
||||||
newPosition = const Offset(200, 0);
|
newPosition = const Offset(0, 150);
|
||||||
break;
|
break;
|
||||||
case 'down':
|
default:
|
||||||
newPosition = const Offset(0, 150);
|
newPosition = Offset.zero;
|
||||||
break;
|
}
|
||||||
default:
|
onButtonTap(index, newPosition, direction);
|
||||||
newPosition = Offset.zero;
|
},
|
||||||
}
|
child: Container(
|
||||||
onButtonTap(index, newPosition, direction);
|
width: 30,
|
||||||
},
|
height: 30,
|
||||||
child: Container(
|
decoration: const BoxDecoration(
|
||||||
width: 30,
|
color: ColorsManager.spaceColor,
|
||||||
height: 30,
|
shape: BoxShape.circle,
|
||||||
decoration: const BoxDecoration(
|
|
||||||
color: ColorsManager.spaceColor,
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
|
||||||
),
|
),
|
||||||
|
child:
|
||||||
|
const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,35 +25,34 @@ class SpaceCardWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return MouseRegion(
|
||||||
behavior: HitTestBehavior.opaque,
|
onEnter: (_) => onHoverChanged(index, true),
|
||||||
onPanUpdate: (details) {
|
onExit: (_) => onHoverChanged(index, false),
|
||||||
// Call the provided callback to update the position
|
child: SizedBox(
|
||||||
final newPosition = position + details.delta;
|
width: 140, // Make sure this covers both card and plus button
|
||||||
onPositionChanged(newPosition);
|
height: 90,
|
||||||
},
|
|
||||||
child: MouseRegion(
|
|
||||||
onEnter: (_) {
|
|
||||||
// Call the provided callback to handle hover state
|
|
||||||
onHoverChanged(index, true);
|
|
||||||
},
|
|
||||||
onExit: (_) {
|
|
||||||
// Call the provided callback to handle hover state
|
|
||||||
onHoverChanged(index, false);
|
|
||||||
},
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
clipBehavior: Clip
|
clipBehavior: Clip.none,
|
||||||
.none, // Allow hovering elements to be displayed outside the boundary
|
|
||||||
children: [
|
children: [
|
||||||
buildSpaceContainer(index), // Build the space container
|
// Main card
|
||||||
if (isHovered) ...[
|
Container(
|
||||||
PlusButtonWidget(
|
width: 140,
|
||||||
index: index,
|
height: 80,
|
||||||
direction: 'down',
|
alignment: Alignment.center,
|
||||||
offset: const Offset(63, 50),
|
color: Colors.transparent,
|
||||||
onButtonTap: onButtonTap,
|
child: buildSpaceContainer(index),
|
||||||
|
),
|
||||||
|
// Plus button (NO inner Positioned!)
|
||||||
|
if (isHovered)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.bottomCenter,
|
||||||
|
child: PlusButtonWidget(
|
||||||
|
index: index,
|
||||||
|
direction: 'down',
|
||||||
|
offset: Offset.zero,
|
||||||
|
onButtonTap: onButtonTap,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -79,6 +79,22 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
color: ColorsManager.blackColor,
|
color: ColorsManager.blackColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'press Enter to Save',
|
||||||
|
style: context.textTheme.headlineLarge?.copyWith(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 10,
|
||||||
|
color: ColorsManager.grayColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
width: 5,
|
||||||
|
),
|
||||||
|
const Icon(Icons.save_as_sharp, size: 10),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Container(
|
Container(
|
||||||
width: context.screenWidth * 0.35,
|
width: context.screenWidth * 0.35,
|
||||||
@ -101,13 +117,15 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
final index = entry.key;
|
final index = entry.key;
|
||||||
final subSpace = entry.value;
|
final subSpace = entry.value;
|
||||||
|
|
||||||
final lowerName = subSpace.subspaceName.toLowerCase();
|
final lowerName =
|
||||||
|
subSpace.subspaceName.toLowerCase();
|
||||||
|
|
||||||
final duplicateIndices = state.subSpaces
|
final duplicateIndices = state.subSpaces
|
||||||
.asMap()
|
.asMap()
|
||||||
.entries
|
.entries
|
||||||
.where((e) =>
|
.where((e) =>
|
||||||
e.value.subspaceName.toLowerCase() == lowerName)
|
e.value.subspaceName.toLowerCase() ==
|
||||||
|
lowerName)
|
||||||
.map((e) => e.key)
|
.map((e) => e.key)
|
||||||
.toList();
|
.toList();
|
||||||
final isDuplicate = duplicateIndices.length > 1 &&
|
final isDuplicate = duplicateIndices.length > 1 &&
|
||||||
@ -182,10 +200,32 @@ class _CreateSubSpaceDialogState extends State<CreateSubSpaceDialog> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: DefaultButton(
|
child: DefaultButton(
|
||||||
onPressed: state.errorMessage.isEmpty
|
onPressed: state.errorMessage.isEmpty
|
||||||
? () {
|
? () async {
|
||||||
final subSpacesBloc = context.read<SubSpaceBloc>();
|
final trimmedValue =
|
||||||
final subSpaces = subSpacesBloc.state.subSpaces;
|
_subspaceNameController.text.trim();
|
||||||
|
|
||||||
|
final subSpacesBloc =
|
||||||
|
context.read<SubSpaceBloc>();
|
||||||
|
if (trimmedValue.isNotEmpty) {
|
||||||
|
subSpacesBloc.add(
|
||||||
|
AddSubSpace(
|
||||||
|
SubspaceModel(
|
||||||
|
subspaceName: trimmedValue,
|
||||||
|
disabled: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_subspaceNameController.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(milliseconds: 10));
|
||||||
|
final subSpaces =
|
||||||
|
subSpacesBloc.state.subSpaces;
|
||||||
|
// if (subSpaces.isNotEmpty) {
|
||||||
widget.onSave?.call(subSpaces);
|
widget.onSave?.call(subSpaces);
|
||||||
|
// }
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
Reference in New Issue
Block a user