mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-10 07:07:19 +00:00
added space model link to space dialog
This commit is contained in:
@ -172,8 +172,12 @@ class SpaceManagementBloc
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
List<SpaceTemplateModel> spaceModels =
|
||||
await _spaceModelApi.listSpaceModels(page: 1);
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: updatedCommunities, products: _cachedProducts ?? []));
|
||||
communities: updatedCommunities,
|
||||
products: _cachedProducts ?? [],
|
||||
spaceModels: spaceModels));
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error loading communities and spaces: $e'));
|
||||
}
|
||||
@ -276,12 +280,16 @@ class SpaceManagementBloc
|
||||
final communities = List<CommunityModel>.from(
|
||||
(previousState as dynamic).communities,
|
||||
);
|
||||
|
||||
final spaceModels = List<SpaceTemplateModel>.from(
|
||||
(previousState as dynamic).spaceModels,
|
||||
);
|
||||
emit(SpaceManagementLoaded(
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
));
|
||||
communities: communities,
|
||||
products: _cachedProducts ?? [],
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
spaceModels: spaceModels ?? []));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(SpaceManagementError('Error updating state: $e'));
|
||||
|
@ -20,12 +20,15 @@ class SpaceManagementLoaded extends SpaceManagementState {
|
||||
final List<ProductModel> products;
|
||||
CommunityModel? selectedCommunity;
|
||||
SpaceModel? selectedSpace;
|
||||
List<SpaceTemplateModel>? spaceModels;
|
||||
|
||||
SpaceManagementLoaded(
|
||||
{required this.communities,
|
||||
required this.products,
|
||||
this.selectedCommunity,
|
||||
this.selectedSpace});
|
||||
this.selectedSpace,
|
||||
this.spaceModels
|
||||
});
|
||||
}
|
||||
|
||||
class SpaceModelManagenetLoaded extends SpaceManagementState {
|
||||
@ -35,10 +38,13 @@ class SpaceModelManagenetLoaded extends SpaceManagementState {
|
||||
class BlankState extends SpaceManagementState {
|
||||
final List<CommunityModel> communities;
|
||||
final List<ProductModel> products;
|
||||
List<SpaceTemplateModel>? spaceModels;
|
||||
|
||||
|
||||
BlankState({
|
||||
required this.communities,
|
||||
required this.products,
|
||||
this.spaceModels
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:ui';
|
||||
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/selected_product_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/utils/constants/assets.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
@ -21,6 +21,7 @@ class SpaceModel {
|
||||
bool isHovered;
|
||||
SpaceStatus status;
|
||||
String internalId;
|
||||
SpaceTemplateModel? spaceModel;
|
||||
|
||||
List<Connection> outgoingConnections = []; // Connections from this space
|
||||
Connection? incomingConnection; // Connections to this space
|
||||
@ -40,6 +41,7 @@ class SpaceModel {
|
||||
this.isHovered = false,
|
||||
this.incomingConnection,
|
||||
this.status = SpaceStatus.unchanged,
|
||||
this.spaceModel,
|
||||
}) : internalId = internalId ?? const Uuid().v4();
|
||||
|
||||
factory SpaceModel.fromJson(Map<String, dynamic> json,
|
||||
|
@ -51,19 +51,25 @@ class SpaceManagementPageState extends State<SpaceManagementPage> {
|
||||
selectedCommunity: null,
|
||||
selectedSpace: null,
|
||||
products: state.products,
|
||||
shouldNavigateToSpaceModelPage: false,
|
||||
);
|
||||
} else if (state is SpaceManagementLoaded) {
|
||||
print("sdksndsnadf ${state.spaceModels}");
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
selectedCommunity: state.selectedCommunity,
|
||||
selectedSpace: state.selectedSpace,
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: false,
|
||||
);
|
||||
} else if (state is SpaceModelLoaded) {
|
||||
return LoadedSpaceView(
|
||||
communities: state.communities,
|
||||
products: state.products,
|
||||
spaceModels: state.spaceModels);
|
||||
spaceModels: state.spaceModels,
|
||||
shouldNavigateToSpaceModelPage: true,
|
||||
);
|
||||
} else if (state is SpaceManagementError) {
|
||||
return Center(child: Text('Error: ${state.errorMessage}'));
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/dialogs/c
|
||||
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 +27,8 @@ 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 +37,7 @@ class CommunityStructureArea extends StatefulWidget {
|
||||
this.products,
|
||||
required this.spaces,
|
||||
this.onSpaceSelected,
|
||||
this.spaceModels,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -49,9 +53,11 @@ class _CommunityStructureAreaState extends State<CommunityStructureArea> {
|
||||
bool isEditingName = false;
|
||||
late TransformationController _transformationController;
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
print("sxdsf ${widget.spaceModels}");
|
||||
spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : [];
|
||||
connections =
|
||||
widget.spaces.isNotEmpty ? createConnections(widget.spaces) : [];
|
||||
@ -284,6 +290,7 @@ 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) {
|
||||
|
@ -7,8 +7,8 @@ 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/widgets/add_device_type_widget.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/all_spaces/widgets/selected_products_button_widget.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';
|
||||
@ -23,6 +23,7 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
final List<SelectedProduct> selectedProducts;
|
||||
final SpaceModel? parentSpace;
|
||||
final SpaceModel? editSpace;
|
||||
final List<SpaceTemplateModel>? spaceModels;
|
||||
|
||||
const CreateSpaceDialog(
|
||||
{super.key,
|
||||
@ -33,7 +34,8 @@ class CreateSpaceDialog extends StatefulWidget {
|
||||
this.icon,
|
||||
this.isEdit = false,
|
||||
this.editSpace,
|
||||
this.selectedProducts = const []});
|
||||
this.selectedProducts = const [],
|
||||
this.spaceModels});
|
||||
|
||||
@override
|
||||
CreateSpaceDialogState createState() => CreateSpaceDialogState();
|
||||
@ -59,275 +61,289 @@ 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: [
|
||||
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),
|
||||
DefaultButton(
|
||||
onPressed: () {},
|
||||
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(
|
||||
'Link a space model',
|
||||
overflow: TextOverflow
|
||||
.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
const SizedBox(height: 30),
|
||||
DefaultButton(
|
||||
onPressed: () {},
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
if (selectedProducts.isNotEmpty)
|
||||
SelectedProductsButtons(
|
||||
products: widget.products ?? [],
|
||||
selectedProducts: selectedProducts,
|
||||
onProductsUpdated: (updatedProducts) {
|
||||
setState(() {
|
||||
selectedProducts = updatedProducts;
|
||||
});
|
||||
},
|
||||
context: context,
|
||||
)
|
||||
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),
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
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),
|
||||
DefaultButton(
|
||||
onPressed: () {},
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
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',
|
||||
overflow:
|
||||
TextOverflow.ellipsis, // Prevent overflow
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -401,4 +417,22 @@ class CreateSpaceDialogState extends State<CreateSpaceDialog> {
|
||||
(widget.editSpace?.children.any((child) => child.name == value) ??
|
||||
false);
|
||||
}
|
||||
|
||||
void _showLinkSpaceModelDialog(BuildContext context) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return LinkSpaceModelDialog(
|
||||
spaceModels: widget.spaceModels ?? [],
|
||||
onSave: (selectedModel) {
|
||||
if (selectedModel != null) {
|
||||
print('Selected Model: ${selectedModel.modelName}');
|
||||
} else {
|
||||
print('No model selected');
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,13 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/space_tem
|
||||
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,
|
||||
@ -25,17 +26,11 @@ class LoadedSpaceView extends StatefulWidget {
|
||||
this.selectedSpace,
|
||||
this.products,
|
||||
this.spaceModels,
|
||||
required this.shouldNavigateToSpaceModelPage
|
||||
});
|
||||
|
||||
@override
|
||||
_LoadedStateViewState createState() => _LoadedStateViewState();
|
||||
}
|
||||
|
||||
class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool hasSpaceModels =
|
||||
widget.spaceModels != null && widget.spaceModels!.isNotEmpty;
|
||||
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
@ -43,29 +38,29 @@ class _LoadedStateViewState extends State<LoadedSpaceView> {
|
||||
Row(
|
||||
children: [
|
||||
SidebarWidget(
|
||||
communities: widget.communities,
|
||||
selectedSpaceUuid: widget.selectedSpace?.uuid ??
|
||||
widget.selectedCommunity?.uuid ??
|
||||
'',
|
||||
communities: communities,
|
||||
selectedSpaceUuid:
|
||||
selectedSpace?.uuid ?? selectedCommunity?.uuid ?? '',
|
||||
),
|
||||
hasSpaceModels
|
||||
shouldNavigateToSpaceModelPage
|
||||
? Expanded(
|
||||
child: BlocProvider(
|
||||
create: (context) => SpaceModelBloc(
|
||||
api: SpaceModelManagementApi(),
|
||||
initialSpaceModels: widget.spaceModels ?? [],
|
||||
initialSpaceModels: spaceModels ?? [],
|
||||
),
|
||||
child: SpaceModelPage(
|
||||
products: widget.products,
|
||||
products: products,
|
||||
),
|
||||
),
|
||||
)
|
||||
: CommunityStructureArea(
|
||||
selectedCommunity: widget.selectedCommunity,
|
||||
selectedSpace: widget.selectedSpace,
|
||||
spaces: widget.selectedCommunity?.spaces ?? [],
|
||||
products: widget.products,
|
||||
communities: widget.communities,
|
||||
selectedCommunity: selectedCommunity,
|
||||
selectedSpace: selectedSpace,
|
||||
spaces: selectedCommunity?.spaces ?? [],
|
||||
products: products,
|
||||
communities: communities,
|
||||
spaceModels: spaceModels,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -0,0 +1,12 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart';
|
||||
|
||||
|
||||
class SpaceModelBloc extends Bloc<SpaceModelEvent, SpaceModelState> {
|
||||
SpaceModelBloc() : super(SpaceModelInitial()) {
|
||||
on<SpaceModelSelectedEvent>((event, emit) {
|
||||
emit(SpaceModelSelectedState(event.selectedIndex));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
abstract class SpaceModelEvent {}
|
||||
|
||||
class SpaceModelSelectedEvent extends SpaceModelEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedEvent(this.selectedIndex);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
abstract class SpaceModelState {}
|
||||
|
||||
class SpaceModelInitial extends SpaceModelState {}
|
||||
|
||||
class SpaceModelSelectedState extends SpaceModelState {
|
||||
final int selectedIndex;
|
||||
|
||||
SpaceModelSelectedState(this.selectedIndex);
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/cancel_button.dart';
|
||||
import 'package:syncrow_web/pages/common/buttons/default_button.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart';
|
||||
import 'package:syncrow_web/pages/spaces_management/space_model/widgets/space_model_card_widget.dart';
|
||||
import 'package:syncrow_web/utils/color_manager.dart';
|
||||
|
||||
class LinkSpaceModelDialog extends StatelessWidget {
|
||||
final Function(SpaceTemplateModel?)? onSave;
|
||||
final int? initialSelectedIndex;
|
||||
final List<SpaceTemplateModel> spaceModels;
|
||||
|
||||
const LinkSpaceModelDialog({
|
||||
Key? key,
|
||||
this.onSave,
|
||||
this.initialSelectedIndex,
|
||||
required this.spaceModels,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => SpaceModelBloc()
|
||||
..add(
|
||||
SpaceModelSelectedEvent(initialSelectedIndex ?? -1),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final bloc = context.read<SpaceModelBloc>();
|
||||
return AlertDialog(
|
||||
backgroundColor: ColorsManager.whiteColors,
|
||||
title: const Text('Link a space model'),
|
||||
content: spaceModels.isNotEmpty
|
||||
? Container(
|
||||
color: ColorsManager.textFieldGreyColor,
|
||||
width: MediaQuery.of(context).size.width * 0.7,
|
||||
height: MediaQuery.of(context).size.height * 0.6,
|
||||
child: BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
builder: (context, state) {
|
||||
int selectedIndex = -1;
|
||||
if (state is SpaceModelSelectedState) {
|
||||
selectedIndex = state.selectedIndex;
|
||||
}
|
||||
return GridView.builder(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
crossAxisSpacing: 10.0,
|
||||
mainAxisSpacing: 10.0,
|
||||
childAspectRatio: 3,
|
||||
),
|
||||
itemCount: spaceModels.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final model = spaceModels[index];
|
||||
final isSelected = selectedIndex == index;
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
bloc.add(SpaceModelSelectedEvent(index));
|
||||
},
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: isSelected
|
||||
? ColorsManager.spaceColor
|
||||
: Colors.transparent,
|
||||
width: 2.0,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: SpaceModelCardWidget(model: model),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const Text('No space models available.'),
|
||||
actions: [
|
||||
Center(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
CancelButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
label: 'Cancel',
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
BlocBuilder<SpaceModelBloc, SpaceModelState>(
|
||||
builder: (context, state) {
|
||||
final isEnabled = state is SpaceModelSelectedState &&
|
||||
state.selectedIndex >= 0;
|
||||
return SizedBox(
|
||||
width: 140,
|
||||
child: DefaultButton(
|
||||
height: 40,
|
||||
borderRadius: 10,
|
||||
onPressed: isEnabled
|
||||
? () {
|
||||
if (onSave != null) {
|
||||
final selectedModel =
|
||||
state is SpaceModelSelectedState
|
||||
? spaceModels[state.selectedIndex]
|
||||
: null;
|
||||
onSave!(selectedModel);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
: null,
|
||||
child: const Text('Save'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -94,14 +94,14 @@ class SpaceModelPage extends StatelessWidget {
|
||||
double _calculateChildAspectRatio(BuildContext context) {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
if (screenWidth > 1600) {
|
||||
return 2; // Taller cards for larger screens
|
||||
return 2;
|
||||
}
|
||||
if (screenWidth > 1200) {
|
||||
return 3; // Adjusted height for medium screens
|
||||
return 3;
|
||||
} else if (screenWidth > 800) {
|
||||
return 3.5; // Adjusted height for smaller screens
|
||||
return 3.5;
|
||||
} else {
|
||||
return 4.0; // Default ratio for smallest screens
|
||||
return 4.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user