mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 15:17:31 +00:00
Merged with dev
This commit is contained in:
@ -99,7 +99,7 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
_buildActionButton('Cancel', ColorsManager.boxColor, ColorsManager.blackColor, () {
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
_buildActionButton('Continue', ColorsManager.secondaryColor, Colors.white, () {
|
||||
_buildActionButton('Continue', ColorsManager.secondaryColor, ColorsManager.whiteColors, () {
|
||||
Navigator.of(context).pop();
|
||||
if (widget.onProductsSelected != null) {
|
||||
widget.onProductsSelected!(productCounts);
|
||||
@ -114,7 +114,7 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
Widget _buildDeviceTypeTile(ProductModel product, Size size) {
|
||||
final selectedProduct = productCounts.firstWhere(
|
||||
(p) => p.productId == product.uuid,
|
||||
orElse: () => SelectedProduct(productId: product.uuid, count: 0),
|
||||
orElse: () => SelectedProduct(productId: product.uuid, count: 0, productName: product.catName, product: product),
|
||||
);
|
||||
|
||||
return SizedBox(
|
||||
@ -137,13 +137,14 @@ class _AddDeviceWidgetState extends State<AddDeviceWidget> {
|
||||
_buildDeviceName(product, size),
|
||||
const SizedBox(height: 4),
|
||||
CounterWidget(
|
||||
isCreate: false,
|
||||
initialCount: selectedProduct.count,
|
||||
onCountChanged: (newCount) {
|
||||
setState(() {
|
||||
if (newCount > 0) {
|
||||
if (!productCounts.contains(selectedProduct)) {
|
||||
productCounts
|
||||
.add(SelectedProduct(productId: product.uuid, count: newCount));
|
||||
.add(SelectedProduct(productId: product.uuid, count: newCount, productName: product.catName, product: product));
|
||||
} else {
|
||||
selectedProduct.count = newCount;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/create_community/view/create_community_dialog.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
@ -17,6 +18,7 @@ class CommunityStructureHeader extends StatefulWidget {
|
||||
final ValueChanged<String> onNameSubmitted;
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? community;
|
||||
final SpaceModel? selectedSpace;
|
||||
|
||||
const CommunityStructureHeader(
|
||||
{super.key,
|
||||
@ -29,7 +31,8 @@ class CommunityStructureHeader extends StatefulWidget {
|
||||
required this.onEditName,
|
||||
required this.onNameSubmitted,
|
||||
this.community,
|
||||
required this.communities});
|
||||
required this.communities,
|
||||
this.selectedSpace});
|
||||
|
||||
@override
|
||||
State<CommunityStructureHeader> createState() =>
|
||||
@ -137,10 +140,8 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.isSave) ...[
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButtons(theme),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
_buildActionButtons(theme),
|
||||
],
|
||||
),
|
||||
],
|
||||
@ -152,11 +153,19 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
||||
alignment: WrapAlignment.end,
|
||||
spacing: 10,
|
||||
children: [
|
||||
if (widget.isSave)
|
||||
_buildButton(
|
||||
label: "Save",
|
||||
icon: const Icon(Icons.save,
|
||||
size: 18, color: ColorsManager.spaceColor),
|
||||
onPressed: widget.onSave,
|
||||
theme: theme),
|
||||
if(widget.selectedSpace!= null)
|
||||
_buildButton(
|
||||
label: "Save",
|
||||
icon: const Icon(Icons.save,
|
||||
size: 18, color: ColorsManager.spaceColor),
|
||||
onPressed: widget.onSave,
|
||||
label: "Delete",
|
||||
icon: const Icon(Icons.delete,
|
||||
size: 18, color: ColorsManager.warningRed),
|
||||
onPressed: widget.onDelete,
|
||||
theme: theme),
|
||||
],
|
||||
);
|
||||
@ -178,7 +187,7 @@ class _CommunityStructureHeaderState extends State<CommunityStructureHeader> {
|
||||
padding: 2.0,
|
||||
height: buttonHeight,
|
||||
elevation: 0,
|
||||
borderColor: Colors.grey.shade300,
|
||||
borderColor: ColorsManager.lightGrayColor,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
@ -11,12 +11,15 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_pr
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/connection_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/blank_community_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_header_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/curved_line_painter.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_card_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_container_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class CommunityStructureArea extends StatefulWidget {
|
||||
@ -26,6 +29,7 @@ class CommunityStructureArea extends StatefulWidget {
|
||||
final ValueChanged<SpaceModel?>? onSpaceSelected;
|
||||
final List<CommunityModel> communities;
|
||||
final List<SpaceModel> spaces;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
|
||||
CommunityStructureArea({
|
||||
this.selectedCommunity,
|
||||
@ -34,6 +38,7 @@ class CommunityStructureArea extends StatefulWidget {
|
||||
this.products,
|
||||
required this.spaces,
|
||||
this.onSpaceSelected,
|
||||
this.spaceModels,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -126,6 +131,7 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
isEditingName: isEditingName,
|
||||
nameController: _nameController,
|
||||
onSave: _saveSpaces,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
onDelete: _onDelete,
|
||||
onEditName: () {
|
||||
setState(() {
|
||||
@ -171,7 +177,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
painter: CurvedLinePainter([connection])),
|
||||
),
|
||||
for (var entry in spaces.asMap().entries)
|
||||
if (entry.value.status != SpaceStatus.deleted)
|
||||
if (entry.value.status != SpaceStatus.deleted &&
|
||||
entry.value.status != SpaceStatus.parentDeleted)
|
||||
Positioned(
|
||||
left: entry.value.position.dx,
|
||||
top: entry.value.position.dy,
|
||||
@ -284,12 +291,16 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
builder: (BuildContext context) {
|
||||
return CreateSpaceDialog(
|
||||
products: widget.products,
|
||||
spaceModels: widget.spaceModels,
|
||||
parentSpace: parentIndex != null ? spaces[parentIndex] : null,
|
||||
onCreateSpace: (String name, String icon,
|
||||
List<SelectedProduct> selectedProducts) {
|
||||
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 ?? _getCenterPosition(screenSize);
|
||||
SpaceModel newSpace = SpaceModel(
|
||||
@ -299,7 +310,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
isPrivate: false,
|
||||
children: [],
|
||||
status: SpaceStatus.newSpace,
|
||||
selectedProducts: selectedProducts);
|
||||
spaceModel: spaceModel,
|
||||
subspaces: subspaces,
|
||||
tags: tags);
|
||||
|
||||
if (parentIndex != null && direction != null) {
|
||||
SpaceModel parentSpace = spaces[parentIndex];
|
||||
@ -335,14 +348,19 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
icon: space.icon,
|
||||
editSpace: space,
|
||||
isEdit: true,
|
||||
selectedProducts: space.selectedProducts,
|
||||
onCreateSpace: (String name, String icon,
|
||||
List<SelectedProduct> selectedProducts) {
|
||||
onCreateSpace: (String name,
|
||||
String icon,
|
||||
List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel,
|
||||
List<SubspaceModel>? subspaces,
|
||||
List<Tag>? tags) {
|
||||
setState(() {
|
||||
// Update the space's properties
|
||||
space.name = name;
|
||||
space.icon = icon;
|
||||
space.selectedProducts = selectedProducts;
|
||||
space.spaceModel = spaceModel;
|
||||
space.subspaces = subspaces;
|
||||
space.tags = tags;
|
||||
|
||||
if (space.status != SpaceStatus.newSpace) {
|
||||
space.status = SpaceStatus.modified; // Mark as modified
|
||||
@ -365,10 +383,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
List<SpaceModel> result = [];
|
||||
|
||||
void flatten(SpaceModel space) {
|
||||
if (space.status == SpaceStatus.deleted) return;
|
||||
|
||||
if (space.status == SpaceStatus.deleted ||
|
||||
space.status == SpaceStatus.parentDeleted) {
|
||||
return;
|
||||
}
|
||||
result.add(space);
|
||||
|
||||
for (var child in space.children) {
|
||||
flatten(child);
|
||||
}
|
||||
@ -436,21 +455,15 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
}
|
||||
|
||||
void _onDelete() {
|
||||
if (widget.selectedCommunity != null &&
|
||||
widget.selectedCommunity?.uuid != null &&
|
||||
widget.selectedSpace == null) {
|
||||
context.read<SpaceManagementBloc>().add(DeleteCommunityEvent(
|
||||
communityUuid: widget.selectedCommunity!.uuid,
|
||||
));
|
||||
}
|
||||
if (widget.selectedSpace != null) {
|
||||
setState(() {
|
||||
for (var space in spaces) {
|
||||
if (space.uuid == widget.selectedSpace?.uuid) {
|
||||
if (space.internalId == widget.selectedSpace?.internalId) {
|
||||
space.status = SpaceStatus.deleted;
|
||||
_markChildrenAsDeleted(space);
|
||||
}
|
||||
}
|
||||
|
||||
_removeConnectionsForDeletedSpaces();
|
||||
});
|
||||
}
|
||||
@ -458,7 +471,8 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
|
||||
void _markChildrenAsDeleted(SpaceModel parent) {
|
||||
for (var child in parent.children) {
|
||||
child.status = SpaceStatus.deleted;
|
||||
child.status = SpaceStatus.parentDeleted;
|
||||
|
||||
_markChildrenAsDeleted(child);
|
||||
}
|
||||
}
|
||||
@ -466,7 +480,9 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
void _removeConnectionsForDeletedSpaces() {
|
||||
connections.removeWhere((connection) {
|
||||
return connection.startSpace.status == SpaceStatus.deleted ||
|
||||
connection.endSpace.status == SpaceStatus.deleted;
|
||||
connection.endSpace.status == SpaceStatus.deleted ||
|
||||
connection.startSpace.status == SpaceStatus.parentDeleted ||
|
||||
connection.endSpace.status == SpaceStatus.parentDeleted;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -4,12 +4,14 @@ import 'package:syncrow_web/utils/color_manager.dart';
|
||||
class CounterWidget extends StatefulWidget {
|
||||
final int initialCount;
|
||||
final ValueChanged<int> onCountChanged;
|
||||
final bool isCreate;
|
||||
|
||||
const CounterWidget({
|
||||
Key? key,
|
||||
this.initialCount = 0,
|
||||
required this.onCountChanged,
|
||||
}) : super(key: key);
|
||||
const CounterWidget(
|
||||
{Key? key,
|
||||
this.initialCount = 0,
|
||||
required this.onCountChanged,
|
||||
required this.isCreate})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<CounterWidget> createState() => _CounterWidgetState();
|
||||
@ -53,25 +55,26 @@ class _CounterWidgetState extends State<CounterWidget> {
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildCounterButton(Icons.remove, _decrementCounter),
|
||||
_buildCounterButton(Icons.remove, _decrementCounter,!widget.isCreate ),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: theme.textTheme.bodyLarge?.copyWith(color: ColorsManager.spaceColor),
|
||||
style: theme.textTheme.bodyLarge
|
||||
?.copyWith(color: ColorsManager.spaceColor),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
_buildCounterButton(Icons.add, _incrementCounter),
|
||||
_buildCounterButton(Icons.add, _incrementCounter, false),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCounterButton(IconData icon, VoidCallback onPressed) {
|
||||
Widget _buildCounterButton(IconData icon, VoidCallback onPressed, bool isDisabled) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
onTap: isDisabled? null: onPressed,
|
||||
child: Icon(
|
||||
icon,
|
||||
color: ColorsManager.spaceColor,
|
||||
color: isDisabled? ColorsManager.spaceColor.withOpacity(0.3): ColorsManager.spaceColor,
|
||||
size: 18,
|
||||
),
|
||||
);
|
||||
|
@ -2,19 +2,23 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.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';
|
||||
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/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/add_device_type_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/icon_selection_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/hoverable_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/create_subspace/views/create_subspace_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/constants/space_icon_const.dart';
|
||||
|
||||
class CreateSpaceDialog extends StatefulWidget {
|
||||
final Function(String, String, List<SelectedProduct> selectedProducts)
|
||||
onCreateSpace;
|
||||
final Function(String, String, List<SelectedProduct> selectedProducts,
|
||||
SpaceTemplateModel? spaceModel, List<SubspaceModel>? subspaces, List<Tag>? tags) onCreateSpace;
|
||||
final List<ProductModel>? products;
|
||||
final String? name;
|
||||
final String? icon;
|
||||
@ -22,6 +26,9 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final SpaceModel? parentSpace;
|
||||
final SpaceModel? editSpace;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
final List<SubspaceModel>? subspaces;
|
||||
final List<Tag>? tags;
|
||||
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
@ -32,7 +39,10 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
this.icon,
|
||||
this.isEdit = false,
|
||||
this.editSpace,
|
||||
this.selectedProducts = const []});
|
||||
this.selectedProducts = const [],
|
||||
this.spaceModels,
|
||||
this.subspaces,
|
||||
this.tags});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
@ -40,12 +50,15 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
|
||||
class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
String selectedIcon = Assets.location;
|
||||
SpaceTemplateModel? selectedSpaceModel;
|
||||
String enteredName = '';
|
||||
List<SelectedProduct> selectedProducts = [];
|
||||
late TextEditingController nameController;
|
||||
bool isOkButtonEnabled = false;
|
||||
bool isNameFieldInvalid = false;
|
||||
bool isNameFieldExist = false;
|
||||
List<SubspaceModel>? subspaces;
|
||||
List<Tag>? tags;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -58,196 +71,485 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
enteredName.isNotEmpty || nameController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return AlertDialog(
|
||||
title: widget.isEdit
|
||||
? const Text('Edit Space')
|
||||
: const Text('Create New Space'),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
content: SizedBox(
|
||||
width: screenWidth * 0.5, // Limit dialog width
|
||||
width: screenWidth * 0.5,
|
||||
child: SingleChildScrollView(
|
||||
// Scrollable content to prevent overflow
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: screenWidth * 0.1, // Adjusted width
|
||||
height: screenWidth * 0.1, // Adjusted height
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
selectedIcon,
|
||||
width: screenWidth * 0.04,
|
||||
height: screenWidth * 0.04,
|
||||
),
|
||||
Positioned(
|
||||
top: 6,
|
||||
right: 6,
|
||||
child: InkWell(
|
||||
onTap: _showIconSelectionDialog,
|
||||
child: Container(
|
||||
width: screenWidth * 0.020,
|
||||
height: screenWidth * 0.020,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.iconEdit,
|
||||
width: screenWidth * 0.06,
|
||||
height: screenWidth * 0.06,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
// Ensure the text field expands responsively
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
// crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 50),
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameController,
|
||||
onChanged: (value) {
|
||||
enteredName = value.trim();
|
||||
setState(() {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = false;
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (_isNameConflict(value)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
style: const TextStyle(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: ColorsManager.boxColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: isNameFieldInvalid || isNameFieldExist
|
||||
? ColorsManager.red
|
||||
: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
Container(
|
||||
width: screenWidth * 0.1,
|
||||
height: screenWidth * 0.1,
|
||||
decoration: const BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SvgPicture.asset(
|
||||
selectedIcon,
|
||||
width: screenWidth * 0.04,
|
||||
height: screenWidth * 0.04,
|
||||
),
|
||||
Positioned(
|
||||
top: 20,
|
||||
right: 20,
|
||||
child: InkWell(
|
||||
onTap: _showIconSelectionDialog,
|
||||
child: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.white,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(
|
||||
color: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
child: SvgPicture.asset(
|
||||
Assets.iconEdit,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isNameFieldInvalid)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'*Space name should not be empty.',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
if (isNameFieldExist)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'*Name already exist',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
if (selectedProducts.isNotEmpty)
|
||||
_buildSelectedProductsButtons(widget.products ?? [])
|
||||
else
|
||||
DefaultButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AddDeviceWidget(
|
||||
products: widget.products,
|
||||
onProductsSelected: (selectedProductsMap) {
|
||||
setState(() {
|
||||
selectedProducts = selectedProductsMap;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: Colors.black,
|
||||
borderColor: ColorsManager.neutralGray,
|
||||
borderRadius: 16.0,
|
||||
padding: 10.0, // Reduced padding for smaller size
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.addIcon,
|
||||
width: screenWidth *
|
||||
0.015, // Adjust icon size
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Add devices / Assign a space model',
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextField(
|
||||
controller: nameController,
|
||||
onChanged: (value) {
|
||||
enteredName = value.trim();
|
||||
setState(() {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = false;
|
||||
isNameFieldInvalid = value.isEmpty;
|
||||
|
||||
if (!isNameFieldInvalid) {
|
||||
if (_isNameConflict(value)) {
|
||||
isNameFieldExist = true;
|
||||
isOkButtonEnabled = false;
|
||||
} else {
|
||||
isNameFieldExist = false;
|
||||
isOkButtonEnabled = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
style: const TextStyle(color: Colors.black),
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Please enter the name',
|
||||
hintStyle: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
filled: true,
|
||||
fillColor: ColorsManager.boxColor,
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: BorderSide(
|
||||
color: isNameFieldInvalid || isNameFieldExist
|
||||
? ColorsManager.red
|
||||
: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderSide: const BorderSide(
|
||||
color: ColorsManager.boxColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isNameFieldInvalid)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'*Space name should not be empty.',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
if (isNameFieldExist)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Text(
|
||||
'*Name already exist',
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodySmall
|
||||
?.copyWith(color: ColorsManager.red),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
selectedSpaceModel == null
|
||||
? DefaultButton(
|
||||
onPressed: () {
|
||||
_showLinkSpaceModelDialog(context);
|
||||
},
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: Colors.black,
|
||||
borderColor: ColorsManager.neutralGray,
|
||||
borderRadius: 16.0,
|
||||
padding: 10.0, // Reduced padding for smaller size
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.link,
|
||||
width: screenWidth *
|
||||
0.015, // Adjust icon size
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Link a space model',
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
width: screenWidth * 0.35,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 10.0, horizontal: 16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.boxColor,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
Chip(
|
||||
label: Text(
|
||||
selectedSpaceModel?.modelName ?? '',
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.transparentColor,
|
||||
width: 0,
|
||||
),
|
||||
),
|
||||
deleteIcon: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: ColorsManager.lightGrayColor,
|
||||
width: 1.5,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.close,
|
||||
size: 16,
|
||||
color: ColorsManager.lightGrayColor,
|
||||
),
|
||||
),
|
||||
onDeleted: () => setState(() {
|
||||
this.selectedSpaceModel = null;
|
||||
})),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
const Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Divider(
|
||||
color: ColorsManager.neutralGray,
|
||||
thickness: 1.0,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Text(
|
||||
'OR',
|
||||
style: TextStyle(
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Divider(
|
||||
color: ColorsManager.neutralGray,
|
||||
thickness: 1.0,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
subspaces == null
|
||||
? DefaultButton(
|
||||
onPressed: () {
|
||||
_showSubSpaceDialog(context, enteredName, [],
|
||||
false, widget.products, subspaces);
|
||||
},
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: Colors.black,
|
||||
borderColor: ColorsManager.neutralGray,
|
||||
borderRadius: 16.0,
|
||||
padding: 10.0, // Reduced padding for smaller size
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.addIcon,
|
||||
width: screenWidth *
|
||||
0.015, // Adjust icon size
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Create sub space',
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: SizedBox(
|
||||
width: screenWidth * 0.35,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: 3.0, // Border width
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
if (subspaces != null)
|
||||
...subspaces!.map(
|
||||
(subspace) => Chip(
|
||||
label: Text(
|
||||
subspace.subspaceName,
|
||||
style: const TextStyle(
|
||||
color: ColorsManager
|
||||
.spaceColor), // Text color
|
||||
),
|
||||
backgroundColor: ColorsManager
|
||||
.whiteColors, // Chip background color
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(
|
||||
16), // Rounded chip
|
||||
side: const BorderSide(
|
||||
color: ColorsManager
|
||||
.spaceColor), // Border color
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
_showSubSpaceDialog(
|
||||
context,
|
||||
enteredName,
|
||||
[],
|
||||
false,
|
||||
widget.products,
|
||||
subspaces);
|
||||
},
|
||||
child: Chip(
|
||||
label: const Text(
|
||||
'Edit',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor:
|
||||
ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
(tags?.isNotEmpty == true ||
|
||||
subspaces?.any((subspace) =>
|
||||
subspace.tags?.isNotEmpty == true) ==
|
||||
true)
|
||||
? SizedBox(
|
||||
width: screenWidth * 0.25,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: 3.0, // Border width
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8.0,
|
||||
runSpacing: 8.0,
|
||||
children: [
|
||||
// Combine tags from spaceModel and subspaces
|
||||
..._groupTags([
|
||||
...?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',
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
label: Text(
|
||||
'x${entry.value}', // Show count
|
||||
style: const TextStyle(
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
),
|
||||
backgroundColor:
|
||||
ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
BorderRadius.circular(16),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.spaceColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
_showTagCreateDialog(context, enteredName,
|
||||
widget.products);
|
||||
// Edit action
|
||||
},
|
||||
child: Chip(
|
||||
label: const Text(
|
||||
'Edit',
|
||||
style: TextStyle(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
backgroundColor:
|
||||
ColorsManager.whiteColors,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: const BorderSide(
|
||||
color: ColorsManager.spaceColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: DefaultButton(
|
||||
onPressed: () {
|
||||
_showTagCreateDialog(
|
||||
context, enteredName, widget.products);
|
||||
},
|
||||
backgroundColor: ColorsManager.textFieldGreyColor,
|
||||
foregroundColor: Colors.black,
|
||||
borderColor: ColorsManager.neutralGray,
|
||||
borderRadius: 16.0,
|
||||
padding: 10.0, // Reduced padding for smaller size
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 6.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.addIcon,
|
||||
width: screenWidth *
|
||||
0.015, // Adjust icon size
|
||||
height: screenWidth * 0.015,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 3),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'Add devices',
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -277,8 +579,8 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
? enteredName
|
||||
: (widget.name ?? '');
|
||||
if (newName.isNotEmpty) {
|
||||
widget.onCreateSpace(
|
||||
newName, selectedIcon, selectedProducts);
|
||||
widget.onCreateSpace(newName, selectedIcon,
|
||||
selectedProducts, selectedSpaceModel,subspaces,tags);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
@ -313,74 +615,6 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSelectedProductsButtons(List<ProductModel> products) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Container(
|
||||
width: screenWidth * 0.6,
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (var i = 0; i < selectedProducts.length; i++) ...[
|
||||
HoverableButton(
|
||||
iconPath:
|
||||
_mapIconToProduct(selectedProducts[i].productId, products),
|
||||
text: 'x${selectedProducts[i].count}',
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedProducts.remove(selectedProducts[i]);
|
||||
});
|
||||
// Handle button tap
|
||||
},
|
||||
),
|
||||
if (i < selectedProducts.length - 1)
|
||||
const SizedBox(
|
||||
width: 2), // Add space except after the last button
|
||||
],
|
||||
const SizedBox(width: 2),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AddDeviceWidget(
|
||||
products: widget.products,
|
||||
initialSelectedProducts: selectedProducts,
|
||||
onProductsSelected: (selectedProductsMap) {
|
||||
setState(() {
|
||||
selectedProducts = selectedProductsMap;
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: ColorsManager.spaceColor,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isNameConflict(String value) {
|
||||
return (widget.parentSpace?.children.any((child) => child.name == value) ??
|
||||
false) ||
|
||||
@ -390,20 +624,133 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
false);
|
||||
}
|
||||
|
||||
String _mapIconToProduct(String uuid, List<ProductModel> products) {
|
||||
// Find the product with the matching UUID
|
||||
final product = products.firstWhere(
|
||||
(product) => product.uuid == uuid,
|
||||
orElse: () => ProductModel(
|
||||
uuid: '',
|
||||
catName: '',
|
||||
prodId: '',
|
||||
prodType: '',
|
||||
name: '',
|
||||
icon: Assets.presenceSensor,
|
||||
),
|
||||
void _showLinkSpaceModelDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return LinkSpaceModelDialog(
|
||||
spaceModels: widget.spaceModels ?? [],
|
||||
onSave: (selectedModel) {
|
||||
if (selectedModel != null) {
|
||||
setState(() {
|
||||
selectedSpaceModel = selectedModel;
|
||||
subspaces = null;
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return product.icon ?? Assets.presenceSensor;
|
||||
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) {
|
||||
return CreateSubSpaceDialog(
|
||||
spaceName: name,
|
||||
dialogTitle: 'Create Sub-space',
|
||||
spaceTags: spaceTags,
|
||||
isEdit: isEdit,
|
||||
products: products,
|
||||
existingSubSpaces: existingSubSpaces,
|
||||
onSave: (slectedSubspaces) {
|
||||
if (slectedSubspaces != null) {
|
||||
setState(() {
|
||||
subspaces = slectedSubspaces;
|
||||
selectedSpaceModel = null;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showTagCreateDialog(
|
||||
BuildContext context, String name, List<ProductModel>? products) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AddDeviceTypeWidget(
|
||||
spaceName: name,
|
||||
products: products,
|
||||
subspaces: subspaces,
|
||||
spaceTags: tags,
|
||||
allTags: [],
|
||||
initialSelectedProducts:
|
||||
createInitialSelectedProducts(tags, subspaces),
|
||||
onSave: (selectedSpaceTags, selectedSubspaces) {
|
||||
setState(() {
|
||||
tags = selectedSpaceTags;
|
||||
selectedSpaceModel = null;
|
||||
|
||||
if (selectedSubspaces != null) {
|
||||
if (subspaces != null) {
|
||||
for (final subspace in subspaces!) {
|
||||
for (final selectedSubspace in selectedSubspaces) {
|
||||
if (subspace.subspaceName ==
|
||||
selectedSubspace.subspaceName) {
|
||||
subspace.tags = selectedSubspace.tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
List<SelectedProduct> createInitialSelectedProducts(
|
||||
List<Tag>? tags, List<SubspaceModel>? subspaces) {
|
||||
final Map<ProductModel, int> productCounts = {};
|
||||
|
||||
if (tags != null) {
|
||||
for (var tag in tags) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] = (productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (subspaces != null) {
|
||||
for (var subspace in subspaces) {
|
||||
if (subspace.tags != null) {
|
||||
for (var tag in subspace.tags!) {
|
||||
if (tag.product != null) {
|
||||
productCounts[tag.product!] =
|
||||
(productCounts[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return productCounts.entries
|
||||
.map((entry) => SelectedProduct(
|
||||
productId: entry.key.uuid,
|
||||
count: entry.value,
|
||||
productName: entry.key.name ?? 'Unnamed',
|
||||
product: entry.key,
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
Map<ProductModel, int> _groupTags(List<Tag> tags) {
|
||||
final Map<ProductModel, int> groupedTags = {};
|
||||
for (var tag in tags) {
|
||||
if (tag.product != null) {
|
||||
groupedTags[tag.product!] = (groupedTags[tag.product!] ?? 0) + 1;
|
||||
}
|
||||
}
|
||||
return groupedTags;
|
||||
}
|
||||
}
|
||||
|
@ -40,8 +40,8 @@ void showDeleteConfirmationDialog(BuildContext context, VoidCallback onConfirm,
|
||||
Navigator.of(context).pop(); // Close the first dialog
|
||||
showProcessingPopup(context, isSpace, onConfirm);
|
||||
},
|
||||
style: _dialogButtonStyle(Colors.blue),
|
||||
child: const Text('Continue', style: TextStyle(color: Colors.white)),
|
||||
style: _dialogButtonStyle(ColorsManager.spaceColor),
|
||||
child: const Text('Continue', style: TextStyle(color: ColorsManager.whiteColors)),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -83,7 +83,7 @@ void showProcessingPopup(BuildContext context, bool isSpace, VoidCallback onDele
|
||||
ElevatedButton(
|
||||
onPressed: onDelete,
|
||||
style: _dialogButtonStyle(ColorsManager.warningRed),
|
||||
child: const Text('Delete', style: TextStyle(color: Colors.white)),
|
||||
child: const Text('Delete', style: TextStyle(color: ColorsManager.whiteColors)),
|
||||
),
|
||||
CancelButton(
|
||||
label: 'Cancel',
|
||||
@ -108,7 +108,7 @@ Widget _buildWarningIcon() {
|
||||
color: ColorsManager.warningRed,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.close, color: Colors.white, size: 40),
|
||||
child: const Icon(Icons.close, color: ColorsManager.whiteColors, size: 40),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ class IconSelectionDialog extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withOpacity(0.2), // Shadow color
|
||||
color: ColorsManager.blackColor.withOpacity(0.2), // Shadow color
|
||||
blurRadius: 20, // Spread of the blur
|
||||
offset: const Offset(0, 8), // Offset of the shadow
|
||||
),
|
||||
|
@ -1,16 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.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/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';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/view/space_model_page.dart';
|
||||
import 'package:syncrow_web/services/space_model_mang_api.dart';
|
||||
|
||||
class LoadedSpaceView extends StatefulWidget {
|
||||
class LoadedSpaceView extends StatelessWidget {
|
||||
final List<CommunityModel> communities;
|
||||
final CommunityModel? selectedCommunity;
|
||||
final SpaceModel? selectedSpace;
|
||||
final List<ProductModel>? products;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
final bool shouldNavigateToSpaceModelPage;
|
||||
|
||||
const LoadedSpaceView({
|
||||
super.key,
|
||||
@ -18,33 +25,43 @@ class LoadedSpaceView extends StatefulWidget {
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
this.spaceModels,
|
||||
required this.shouldNavigateToSpaceModelPage
|
||||
});
|
||||
|
||||
@override
|
||||
_LoadedStateViewState createState() => _LoadedStateViewState();
|
||||
}
|
||||
|
||||
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SidebarWidget(
|
||||
communities: widget.communities,
|
||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
||||
widget.selectedCommunity?.uuid ??
|
||||
'',
|
||||
),
|
||||
CommunityStructureArea(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||
products: widget.products,
|
||||
communities: widget.communities,
|
||||
communities: communities,
|
||||
selectedSpaceUuid:
|
||||
selectedSpace?.uuid ?? selectedCommunity?.uuid ?? '',
|
||||
),
|
||||
shouldNavigateToSpaceModelPage
|
||||
? Expanded(
|
||||
child: BlocProvider(
|
||||
create: (context) => SpaceModelBloc(
|
||||
api: SpaceModelManagementApi(),
|
||||
initialSpaceModels: spaceModels ?? [],
|
||||
),
|
||||
child: SpaceModelPage(
|
||||
products: products,
|
||||
),
|
||||
),
|
||||
)
|
||||
: CommunityStructureArea(
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
spaces: selectedCommunity?.spaces ?? [],
|
||||
products: products,
|
||||
communities: communities,
|
||||
spaceModels: spaceModels,
|
||||
),
|
||||
],
|
||||
),
|
||||
const GradientCanvasBorderWidget(),
|
||||
|
@ -45,7 +45,7 @@ class PlusButtonWidget extends StatelessWidget {
|
||||
color: ColorsManager.spaceColor,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.add, color: Colors.white, size: 20),
|
||||
child: const Icon(Icons.add, color: ColorsManager.whiteColors, size: 20),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -0,0 +1,118 @@
|
||||
import 'package:flutter/material.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/widgets/add_device_type_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/hoverable_button.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
|
||||
class SelectedProductsButtons extends StatelessWidget {
|
||||
final List<ProductModel> products;
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final Function(List<SelectedProduct>) onProductsUpdated;
|
||||
final BuildContext context;
|
||||
|
||||
const SelectedProductsButtons({
|
||||
Key? key,
|
||||
required this.products,
|
||||
required this.selectedProducts,
|
||||
required this.onProductsUpdated,
|
||||
required this.context,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
return Container(
|
||||
width: screenWidth * 0.6,
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: ColorsManager.neutralGray,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
..._buildSelectedProductButtons(),
|
||||
_buildAddButton(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _buildSelectedProductButtons() {
|
||||
return [
|
||||
for (var i = 0; i < selectedProducts.length; i++) ...[
|
||||
HoverableButton(
|
||||
iconPath: _mapIconToProduct(selectedProducts[i].productId, products),
|
||||
text: 'x${selectedProducts[i].count}',
|
||||
onTap: () {
|
||||
_removeProduct(i);
|
||||
},
|
||||
),
|
||||
if (i < selectedProducts.length - 1)
|
||||
const SizedBox(width: 2), // Add space except after the last button
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildAddButton() {
|
||||
return GestureDetector(
|
||||
onTap: _showAddDeviceDialog,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.add,
|
||||
color: ColorsManager.spaceColor,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showAddDeviceDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => AddDeviceWidget(
|
||||
products: products,
|
||||
initialSelectedProducts: selectedProducts,
|
||||
onProductsSelected: (selectedProductsMap) {
|
||||
onProductsUpdated(selectedProductsMap);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _removeProduct(int index) {
|
||||
final updatedProducts = [...selectedProducts];
|
||||
updatedProducts.removeAt(index);
|
||||
onProductsUpdated(updatedProducts);
|
||||
}
|
||||
|
||||
String _mapIconToProduct(String uuid, List<ProductModel> products) {
|
||||
// Find the product with the matching UUID
|
||||
final product = products.firstWhere(
|
||||
(product) => product.uuid == uuid,
|
||||
orElse: () => ProductModel(
|
||||
uuid: '',
|
||||
catName: '',
|
||||
prodId: '',
|
||||
prodType: '',
|
||||
name: '',
|
||||
icon: Assets.presenceSensor,
|
||||
),
|
||||
);
|
||||
|
||||
return product.icon ?? Assets.presenceSensor;
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_m
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_tile.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/space_tile_widget.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/structure_selector/bloc/center_body_event.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:syncrow_web/utils/style.dart';
|
||||
@ -116,7 +118,7 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
children: [
|
||||
Text('Communities',
|
||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||
color: Colors.black,
|
||||
color: ColorsManager.blackColor,
|
||||
)),
|
||||
GestureDetector(
|
||||
onTap: () => _navigateToBlank(context),
|
||||
@ -184,6 +186,8 @@ class _SidebarWidgetState extends State<SidebarWidget> {
|
||||
_selectedSpaceUuid = null; // Update the selected community
|
||||
});
|
||||
|
||||
context.read<CenterBodyBloc>().add(CommunitySelectedEvent());
|
||||
|
||||
context.read<SpaceManagementBloc>().add(
|
||||
SelectCommunityEvent(selectedCommunity: community),
|
||||
);
|
||||
|
@ -78,7 +78,7 @@ class SpaceContainerWidget extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
color: ColorsManager.lightGrayColor.withOpacity(0.5),
|
||||
spreadRadius: 2,
|
||||
blurRadius: 5,
|
||||
offset: const Offset(0, 3), // Shadow position
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class SpaceWidget extends StatelessWidget {
|
||||
final String name;
|
||||
@ -23,11 +24,11 @@ class SpaceWidget extends StatelessWidget {
|
||||
Container(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: ColorsManager.whiteColors,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.grey.withOpacity(0.5),
|
||||
color: ColorsManager.lightGrayColor.withOpacity(0.5),
|
||||
spreadRadius: 5,
|
||||
blurRadius: 7,
|
||||
offset: const Offset(0, 3),
|
||||
@ -36,7 +37,7 @@ class SpaceWidget extends StatelessWidget {
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.location_on, color: Colors.blue),
|
||||
const Icon(Icons.location_on, color: ColorsManager.spaceColor),
|
||||
const SizedBox(width: 8),
|
||||
Text(name, style: const TextStyle(fontSize: 16)),
|
||||
],
|
||||
|
Reference in New Issue
Block a user