added edit space option

This commit is contained in:
hannathkadher
2024-11-21 10:04:07 +04:00
parent 6fd845a9fc
commit 6bc6097a7e
11 changed files with 291 additions and 120 deletions

View File

@ -0,0 +1,35 @@
<svg width="38" height="47" viewBox="0 0 38 47" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_5442_18595)">
<g filter="url(#filter1_i_5442_18595)">
<path d="M5 7.15639C5 4.66815 6.82886 2.53419 9.30515 2.29062C13.3497 1.89279 16.6065 1.91012 20.6058 2.30139C23.094 2.54481 24.9399 4.68366 24.9399 7.18371V33.6749C24.9399 36.1374 23.1483 38.2593 20.7006 38.529C16.6673 38.9735 13.3539 38.9769 9.24395 38.5259C6.79422 38.2571 5 36.1349 5 33.6705V7.15639Z" fill="#EAF6FF"/>
</g>
<path d="M22.049 24.6587C22.1741 24.6587 22.2992 24.611 22.3947 24.5155C22.5856 24.3246 22.5856 24.0151 22.3947 23.8242C20.7605 22.1901 20.7605 19.5311 22.3947 17.897C22.5856 17.706 22.5856 17.3965 22.3947 17.2056C22.2037 17.0148 21.8943 17.0148 21.7034 17.2056C19.688 19.221 19.688 22.5002 21.7034 24.5156C21.7988 24.611 21.9239 24.6587 22.049 24.6587Z" fill="#8AC9FE"/>
<path d="M23.0772 22.7595C23.1822 22.7595 23.2872 22.7195 23.3673 22.6394C23.5274 22.4792 23.5274 22.2195 23.3672 22.0594C23.047 21.7392 22.8707 21.3134 22.8707 20.8604C22.8707 20.4076 23.047 19.9818 23.3672 19.6616C23.5274 19.5014 23.5274 19.2417 23.3673 19.0816C23.207 18.9214 22.9473 18.9214 22.7873 19.0815C22.3121 19.5567 22.0504 20.1885 22.0504 20.8604C22.0504 21.5325 22.312 22.1642 22.7873 22.6394C22.8673 22.7194 22.9723 22.7595 23.0772 22.7595Z" fill="#8AC9FE"/>
<path d="M15.837 31.6211L15.837 33.4829C15.837 33.8534 15.5367 34.1538 15.1661 34.1538C14.7955 34.1538 14.4952 33.8534 14.4952 33.4829L14.4952 31.6211C14.4952 31.2505 14.7955 30.9502 15.1661 30.9502C15.5367 30.9502 15.837 31.2505 15.837 31.6211Z" fill="#B3DAFE"/>
<path d="M25.8903 10.5772C25.8903 9.69265 26.4679 8.89349 27.3345 8.71634C28.6715 8.44303 29.7509 8.4541 31.0716 8.72152C31.9473 8.89886 32.5369 9.70267 32.5369 10.5962V32.0694C32.5369 32.9344 31.9848 33.7213 31.1421 33.9165C29.784 34.231 28.6723 34.2337 27.2885 33.9148C26.4442 33.7202 25.8903 32.9329 25.8903 32.0664V10.5772Z" fill="#EAF6FF"/>
<path d="M28.2773 24.6579C28.1523 24.6579 28.0271 24.6102 27.9316 24.5148C27.7407 24.3238 27.7407 24.0144 27.9316 23.8235C29.5658 22.1893 29.5658 19.5304 27.9316 17.8962C27.7407 17.7053 27.7407 17.3958 27.9316 17.2049C28.1226 17.0141 28.4321 17.0141 28.623 17.2049C30.6383 19.2203 30.6383 22.4995 28.623 24.5149C28.5275 24.6102 28.4024 24.6579 28.2773 24.6579Z" fill="#8AC9FE"/>
<path d="M27.2491 22.7588C27.1441 22.7588 27.0392 22.7187 26.9591 22.6386C26.7989 22.4785 26.7989 22.2187 26.9591 22.0586C27.2793 21.7384 27.4557 21.3126 27.4557 20.8597C27.4557 20.4069 27.2793 19.9811 26.9591 19.6609C26.7989 19.5007 26.7989 19.241 26.9591 19.0809C27.1193 18.9206 27.379 18.9207 27.5391 19.0808C28.0142 19.556 28.276 20.1877 28.276 20.8597C28.276 21.5318 28.0143 22.1635 27.5391 22.6387C27.4591 22.7187 27.3541 22.7588 27.2491 22.7588Z" fill="#8AC9FE"/>
</g>
<defs>
<filter id="filter0_d_5442_18595" x="0.5" y="0.5" width="36.5369" height="45.8633" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="2.25"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.07 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_5442_18595"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_5442_18595" result="shape"/>
</filter>
<filter id="filter1_i_5442_18595" x="4" y="2" width="20.9399" height="36.8633" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dx="-1"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0 0.538295 0 0 0 0.3 0"/>
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_5442_18595"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -119,7 +119,6 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
try { try {
final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid); final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid);
emit(SpaceCreationSuccess(spaces: updatedSpaces)); emit(SpaceCreationSuccess(spaces: updatedSpaces));
add(LoadCommunityAndSpacesEvent()); add(LoadCommunityAndSpacesEvent());
} catch (e) { } catch (e) {
@ -158,6 +157,8 @@ class SpaceManagementBloc extends Bloc<SpaceManagementEvent, SpaceManagementStat
position: space.position, position: space.position,
icon: space.icon, icon: space.icon,
direction: space.incomingConnection?.direction, direction: space.incomingConnection?.direction,
products: space.selectedProducts
); );
space.uuid = response?.uuid; space.uuid = response?.uuid;
} }

View File

@ -0,0 +1,13 @@
class SelectedProduct {
final String productId;
int count;
SelectedProduct({required this.productId, required this.count});
Map<String, dynamic> toJson() {
return {
'productId': productId,
'count': count,
};
}
}

View File

@ -1,14 +1,15 @@
import 'dart:ui'; import 'dart:ui';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
enum SpaceStatus { newSpace, modified, unchanged } enum SpaceStatus { newSpace, modified, unchanged }
class SpaceModel { class SpaceModel {
String? uuid; String? uuid;
final String? icon; String? icon;
final String? spaceTuyaUuid; final String? spaceTuyaUuid;
final String name; String name;
final bool isPrivate; final bool isPrivate;
final String? invitationCode; final String? invitationCode;
SpaceModel? parent; SpaceModel? parent;
@ -17,6 +18,7 @@ class SpaceModel {
Offset position; Offset position;
bool isHovered; bool isHovered;
SpaceStatus status; SpaceStatus status;
List<SelectedProduct> selectedProducts;
List<Connection> outgoingConnections = []; // Connections from this space List<Connection> outgoingConnections = []; // Connections from this space
Connection? incomingConnection; // Connections to this space Connection? incomingConnection; // Connections to this space
@ -35,6 +37,7 @@ class SpaceModel {
this.isHovered = false, this.isHovered = false,
this.incomingConnection, this.incomingConnection,
this.status = SpaceStatus.unchanged, this.status = SpaceStatus.unchanged,
this.selectedProducts = const [],
}); });
factory SpaceModel.fromJson(Map<String, dynamic> json) { factory SpaceModel.fromJson(Map<String, dynamic> json) {
@ -72,19 +75,6 @@ class SpaceModel {
return instance; return instance;
} }
@override
String toString() {
return '''
SpaceModel {
uuid: $uuid,
name: $name,
isPrivate: $isPrivate,
position: {dx: ${position.dx}, dy: ${position.dy}},
parentUuid: ${parent?.uuid},
children: ${children.map((child) => child.name).toList()},
isHovered: $isHovered
}''';
}
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
return { return {

View File

@ -3,15 +3,28 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/add_device_type_widget.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/hoverable_button.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class CreateSpaceDialog extends StatefulWidget { class CreateSpaceDialog extends StatefulWidget {
final Function(String, String) onCreateSpace; final Function(String, String, List<SelectedProduct> selectedProducts) onCreateSpace;
final List<ProductModel>? products; final List<ProductModel>? products;
final String? name;
final String? icon;
final bool isEdit;
final List<SelectedProduct> selectedProducts;
const CreateSpaceDialog({super.key, required this.onCreateSpace, this.products}); const CreateSpaceDialog(
{super.key,
required this.onCreateSpace,
this.products,
this.name,
this.icon,
this.isEdit = false,
this.selectedProducts = const []});
@override @override
CreateSpaceDialogState createState() => CreateSpaceDialogState(); CreateSpaceDialogState createState() => CreateSpaceDialogState();
@ -20,12 +33,21 @@ class CreateSpaceDialog extends StatefulWidget {
class CreateSpaceDialogState extends State<CreateSpaceDialog> { class CreateSpaceDialogState extends State<CreateSpaceDialog> {
String selectedIcon = Assets.location; String selectedIcon = Assets.location;
String enteredName = ''; String enteredName = '';
Map<String, int> selectedProducts = {}; List<SelectedProduct> selectedProducts = [];
late TextEditingController nameController;
@override
void initState() {
super.initState();
selectedIcon = widget.icon ?? Assets.location;
nameController = TextEditingController(text: widget.name ?? '');
selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : [];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( return AlertDialog(
title: const Text('Create New Space'), title: widget.isEdit ? Text('Edit Space') : Text('Create new Space'),
backgroundColor: ColorsManager.whiteColors, backgroundColor: ColorsManager.whiteColors,
content: SizedBox( content: SizedBox(
width: 600, width: 600,
@ -76,6 +98,7 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
children: [ children: [
// Name input field // Name input field
TextField( TextField(
controller: nameController,
onChanged: (value) { onChanged: (value) {
enteredName = value; enteredName = value;
}, },
@ -208,8 +231,10 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
Expanded( Expanded(
child: DefaultButton( child: DefaultButton(
onPressed: () { onPressed: () {
if (enteredName.isNotEmpty) { late String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? '');
widget.onCreateSpace(enteredName, selectedIcon); // Pass the name and icon back if (newName.isNotEmpty) {
widget.onCreateSpace(
newName, selectedIcon, selectedProducts); // Pass the name and icon back
Navigator.of(context).pop(); // Close the dialog Navigator.of(context).pop(); // Close the dialog
} }
}, },
@ -270,49 +295,31 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
Widget _buildSelectedProductsButtons(List<ProductModel> products) { Widget _buildSelectedProductsButtons(List<ProductModel> products) {
return Container( return Container(
width: 600,
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
color: ColorsManager.textFieldGreyColor, color: ColorsManager.textFieldGreyColor,
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(
color: ColorsManager.neutralGray,
width: 2, // Set the border width
),
), ),
child: Wrap( child: Wrap(
spacing: 8, // Horizontal spacing between buttons spacing: 8, // Horizontal spacing between buttons
runSpacing: 8, // Vertical spacing between rows runSpacing: 8, // Vertical spacing between rows
children: [ children: [
// Dynamically create a button for each selected product // Dynamically create a button for each selected product
for (var entry in selectedProducts.entries) for (var product in selectedProducts)
GestureDetector( HoverableButton(
iconPath: _mapIconToProduct(product.productId, products),
text: 'x${product.count}',
onTap: () { onTap: () {
setState(() {
selectedProducts.remove(product);
});
// Handle button tap
}, },
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
// Display the product icon
SvgPicture.asset(
_mapIconToProduct(entry.key, products),
width: 24,
height: 24,
),
const SizedBox(width: 8),
// Display the product count
Text(
'x${entry.value}',
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: ColorsManager.spaceColor,
),
),
],
),
),
), ),
// Add Button // Add Button
GestureDetector( GestureDetector(
@ -331,10 +338,9 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
); );
}, },
child: Container( child: Container(
width: 50, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: ColorsManager.textFieldGreyColor, color: ColorsManager.whiteColors,
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
child: const Icon( child: const Icon(

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/counter_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
@ -9,8 +10,8 @@ import 'package:syncrow_web/utils/extension/build_context_x.dart';
class AddDeviceWidget extends StatefulWidget { class AddDeviceWidget extends StatefulWidget {
final List<ProductModel>? products; final List<ProductModel>? products;
final ValueChanged<Map<String, int>>? onProductsSelected; final ValueChanged<List<SelectedProduct>>? onProductsSelected;
final Map<String, int>? initialSelectedProducts; final List<SelectedProduct>? initialSelectedProducts;
const AddDeviceWidget( const AddDeviceWidget(
{super.key, this.products, this.initialSelectedProducts, this.onProductsSelected}); {super.key, this.products, this.initialSelectedProducts, this.onProductsSelected});
@ -21,15 +22,14 @@ class AddDeviceWidget extends StatefulWidget {
class _AddDeviceWidgetState extends State<AddDeviceWidget> { class _AddDeviceWidgetState extends State<AddDeviceWidget> {
late ScrollController _scrollController; late ScrollController _scrollController;
Map<String, int> productCounts = {}; List<SelectedProduct> productCounts = [];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_scrollController = ScrollController(); _scrollController = ScrollController();
print(widget.initialSelectedProducts);
if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) { if (widget.initialSelectedProducts != null && widget.initialSelectedProducts!.isNotEmpty) {
productCounts = widget.initialSelectedProducts!; productCounts = List.from(widget.initialSelectedProducts!);
} }
} }
@ -112,6 +112,12 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
} }
Widget _buildDeviceTypeTile(ProductModel? deviceType) { Widget _buildDeviceTypeTile(ProductModel? deviceType) {
SelectedProduct? existingProduct = productCounts.firstWhere(
(product) => product.productId == deviceType?.uuid,
orElse: () => SelectedProduct(productId: deviceType!.uuid, count: 0),
);
return SizedBox( return SizedBox(
width: 75, width: 75,
height: 90, // Increase height if needed height: 90, // Increase height if needed
@ -161,11 +167,15 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
const SizedBox(height: 8), const SizedBox(height: 8),
// The custom counter widget aligned at the bottom // The custom counter widget aligned at the bottom
CounterWidget( CounterWidget(
initialCount: productCounts[deviceType!.uuid] ?? 0, initialCount: existingProduct.count,
onCountChanged: (newCount) { onCountChanged: (newCount) {
setState(() { setState(() {
if (newCount > 0) { if (newCount > 0) {
productCounts[deviceType!.uuid] = newCount; if (!productCounts.contains(existingProduct)) {
productCounts.add(SelectedProduct(productId: deviceType!.uuid, count: newCount));
} else {
existingProduct.count = newCount;
}
} else { } else {
productCounts.remove(deviceType!.uuid); productCounts.remove(deviceType!.uuid);
} }

View File

@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/common/buttons/add_space_button.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart';
import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart';
import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/view/dialogs/create_space_dialog.dart';
import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart';
@ -114,6 +115,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
buildSpaceContainer: (int index) { buildSpaceContainer: (int index) {
return SpaceContainerWidget( return SpaceContainerWidget(
index: index, index: index,
onDoubleTap: () {
_showEditSpaceDialog(spaces[index]);
},
icon: spaces[index].icon ?? '', icon: spaces[index].icon ?? '',
name: spaces[index].name, name: spaces[index].name,
); );
@ -240,7 +244,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
builder: (BuildContext context) { builder: (BuildContext context) {
return CreateSpaceDialog( return CreateSpaceDialog(
products: widget.products, products: widget.products,
onCreateSpace: (String name, String icon) { onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
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 centerPosition = position ?? Offset centerPosition = position ??
@ -255,7 +259,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
isPrivate: false, isPrivate: false,
children: [], children: [],
status: SpaceStatus.newSpace, status: SpaceStatus.newSpace,
); selectedProducts: selectedProducts);
if (parentIndex != null && direction != null) { if (parentIndex != null && direction != null) {
SpaceModel parentSpace = spaces[parentIndex]; SpaceModel parentSpace = spaces[parentIndex];
@ -280,6 +284,35 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
); );
} }
void _showEditSpaceDialog(SpaceModel space) {
showDialog(
context: context,
builder: (BuildContext context) {
return CreateSpaceDialog(
products: widget.products,
name: space.name,
icon: space.icon,
isEdit: true,
selectedProducts: space.selectedProducts,
onCreateSpace: (String name, String icon, List<SelectedProduct> selectedProducts) {
setState(() {
// Update the space's properties
space.name = name;
space.icon = icon;
space.selectedProducts = selectedProducts;
if (space.status != SpaceStatus.newSpace) {
space.status = SpaceStatus.modified; // Mark as modified
}
});
},
// Pre-fill the dialog with current space data
key: Key(space.name), // Add a unique key to ensure dialog refresh
);
},
);
}
void _handleHoverChanged(int index, bool isHovered) { void _handleHoverChanged(int index, bool isHovered) {
setState(() { setState(() {
spaces[index].isHovered = isHovered; spaces[index].isHovered = isHovered;

View File

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class HoverableButton extends StatefulWidget {
final String iconPath;
final String text;
final VoidCallback onTap;
const HoverableButton({
Key? key,
required this.iconPath,
required this.text,
required this.onTap,
}) : super(key: key);
@override
_HoverableButtonState createState() => _HoverableButtonState();
}
class _HoverableButtonState extends State<HoverableButton> {
bool isHovered = false; // Track hover state
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: widget.onTap,
child: MouseRegion(
onEnter: (_) => setState(() => isHovered = true),
onExit: (_) => setState(() => isHovered = false),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.symmetric(horizontal: 13, vertical: 8),
decoration: BoxDecoration(
color: isHovered ? ColorsManager.warningRed : Colors.white, // Change color on hover
borderRadius: BorderRadius.circular(16),
boxShadow: [
if (isHovered)
BoxShadow(
color: ColorsManager.warningRed,
),
],
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (!isHovered)
SvgPicture.asset(
widget.iconPath,
width: 24,
height: 24,
)
else
Center(
child: const Icon(
Icons.close, // Display "X" on hover
color: Colors.white,
size: 24,
)),
const SizedBox(width: 8),
if (!isHovered) ...[
Text(
widget.text,
style: TextStyle(
fontSize: 17,
fontWeight: FontWeight.w500,
color: ColorsManager.spaceColor,
),
),
],
],
),
),
),
);
}
}

View File

@ -2,22 +2,25 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
class SpaceContainerWidget extends StatelessWidget { class SpaceContainerWidget extends StatelessWidget {
final int index; final int index;
final String icon; final String icon;
final String name; final String name;
final VoidCallback? onDoubleTap;
const SpaceContainerWidget({ const SpaceContainerWidget({
super.key, super.key,
required this.index, required this.index,
required this.icon, required this.icon,
required this.name, required this.name,
this.onDoubleTap,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return GestureDetector(
onDoubleTap: onDoubleTap,
child: Container(
width: 150, width: 150,
height: 60, height: 60,
decoration: BoxDecoration( decoration: BoxDecoration(
@ -63,7 +66,6 @@ class SpaceContainerWidget extends StatelessWidget {
), ),
], ],
), ),
));
);
} }
} }

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart';
import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_response_model.dart';
import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/services/api/http_service.dart';
@ -144,6 +145,7 @@ class CommunitySpaceManagementApi {
bool isPrivate = false, bool isPrivate = false,
required Offset position, required Offset position,
String? icon, String? icon,
required List<SelectedProduct> products,
}) async { }) async {
try { try {
final body = { final body = {
@ -152,7 +154,8 @@ class CommunitySpaceManagementApi {
'x': position.dx, 'x': position.dx,
'y': position.dy, 'y': position.dy,
'direction': direction, 'direction': direction,
'icon': icon 'icon': icon,
'products': products.map((product) => product.toJson()).toList(),
}; };
if (parentId != null) { if (parentId != null) {
body['parentUuid'] = parentId; body['parentUuid'] = parentId;

View File

@ -51,6 +51,7 @@ abstract class ColorsManager {
static const Color spaceColor = Color(0xB2023DFE); static const Color spaceColor = Color(0xB2023DFE);
static const Color counterBackgroundColor = Color(0xCCF4F4F4); static const Color counterBackgroundColor = Color(0xCCF4F4F4);
static const Color neutralGray = Color(0xFFE5E5E5); static const Color neutralGray = Color(0xFFE5E5E5);
static const Color warningRed = Color(0xFFFF6465);
} }
//background: #background: #5D5D5D; //background: #background: #5D5D5D;