added space model link to space dialog

This commit is contained in:
hannathkadher
2025-01-11 15:42:35 +04:00
parent e70df16de3
commit 8aa493a15e
12 changed files with 502 additions and 285 deletions

View File

@ -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'));

View File

@ -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
});
}

View File

@ -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,

View File

@ -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}'));
}

View File

@ -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) {

View File

@ -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');
}
},
);
},
);
}
}

View File

@ -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,
),
],
),

View File

@ -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));
});
}
}

View File

@ -0,0 +1,7 @@
abstract class SpaceModelEvent {}
class SpaceModelSelectedEvent extends SpaceModelEvent {
final int selectedIndex;
SpaceModelSelectedEvent(this.selectedIndex);
}

View File

@ -0,0 +1,9 @@
abstract class SpaceModelState {}
class SpaceModelInitial extends SpaceModelState {}
class SpaceModelSelectedState extends SpaceModelState {
final int selectedIndex;
SpaceModelSelectedState(this.selectedIndex);
}

View File

@ -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'),
),
);
},
),
],
),
),
],
);
},
),
);
}
}

View File

@ -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;
}
}