diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart index 2b749a32..f8ed1b86 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart @@ -172,8 +172,12 @@ class SpaceManagementBloc }).toList(), ); + List 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.from( (previousState as dynamic).communities, ); + + final spaceModels = List.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')); diff --git a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart index 3ceafba9..635c244d 100644 --- a/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart +++ b/lib/pages/spaces_management/all_spaces/bloc/space_management_state.dart @@ -20,12 +20,15 @@ class SpaceManagementLoaded extends SpaceManagementState { final List products; CommunityModel? selectedCommunity; SpaceModel? selectedSpace; + List? 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 communities; final List products; + List? spaceModels; + BlankState({ required this.communities, required this.products, + this.spaceModels }); } diff --git a/lib/pages/spaces_management/all_spaces/model/space_model.dart b/lib/pages/spaces_management/all_spaces/model/space_model.dart index 4bdf4ece..ba5d83d6 100644 --- a/lib/pages/spaces_management/all_spaces/model/space_model.dart +++ b/lib/pages/spaces_management/all_spaces/model/space_model.dart @@ -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 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 json, diff --git a/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart b/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart index 77f878b7..24b1b5cb 100644 --- a/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart +++ b/lib/pages/spaces_management/all_spaces/view/spaces_management_page.dart @@ -51,19 +51,25 @@ class SpaceManagementPageState extends State { 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}')); } diff --git a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart index a929c6fd..9225f5a9 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart @@ -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? onSpaceSelected; final List communities; final List spaces; + final List? 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 { 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 { builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, + spaceModels: widget.spaceModels, parentSpace: parentIndex != null ? spaces[parentIndex] : null, onCreateSpace: (String name, String icon, List selectedProducts) { diff --git a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart index 25c3d5b3..f3d11d91 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/dialogs/create_space_dialog.dart @@ -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 selectedProducts; final SpaceModel? parentSpace; final SpaceModel? editSpace; + final List? 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 { 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 { (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'); + } + }, + ); + }, + ); + } } diff --git a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart index 84ed32f7..cdba0a5a 100644 --- a/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart +++ b/lib/pages/spaces_management/all_spaces/widgets/loaded_space_widget.dart @@ -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 communities; final CommunityModel? selectedCommunity; final SpaceModel? selectedSpace; final List? products; final List? 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 { @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 { 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, ), ], ), diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart new file mode 100644 index 00000000..aa9a446d --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart @@ -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 { + SpaceModelBloc() : super(SpaceModelInitial()) { + on((event, emit) { + emit(SpaceModelSelectedState(event.selectedIndex)); + }); + } +} \ No newline at end of file diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart new file mode 100644 index 00000000..8bff0202 --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart @@ -0,0 +1,7 @@ +abstract class SpaceModelEvent {} + +class SpaceModelSelectedEvent extends SpaceModelEvent { + final int selectedIndex; + + SpaceModelSelectedEvent(this.selectedIndex); +} diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart new file mode 100644 index 00000000..cc745e4d --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart @@ -0,0 +1,9 @@ +abstract class SpaceModelState {} + +class SpaceModelInitial extends SpaceModelState {} + +class SpaceModelSelectedState extends SpaceModelState { + final int selectedIndex; + + SpaceModelSelectedState(this.selectedIndex); +} diff --git a/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart b/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart new file mode 100644 index 00000000..44cbbc67 --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/view/link_space_model_dialog.dart @@ -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 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(); + 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( + 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( + 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'), + ), + ); + }, + ), + ], + ), + ), + ], + ); + }, + ), + ); + } +} diff --git a/lib/pages/spaces_management/space_model/view/space_model_page.dart b/lib/pages/spaces_management/space_model/view/space_model_page.dart index 6b9818c9..a76543ae 100644 --- a/lib/pages/spaces_management/space_model/view/space_model_page.dart +++ b/lib/pages/spaces_management/space_model/view/space_model_page.dart @@ -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; } }