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 index 4018ae60..cb9f5d43 100644 --- 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 @@ -1,36 +1,26 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_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/services/space_model_mang_api.dart'; import 'package:syncrow_web/utils/navigation_service.dart'; class SpaceModelBloc extends Bloc { SpaceModelBloc() : super(SpaceModelInitial()) { on(_getSpaceIds); + on(_handleLinkSpaceModel); + on(_validateLinkSpaceModel); on((event, emit) { emit(SpaceModelSelectedState(event.selectedIndex)); }); } - // Future getSpaceIds( - // SpaceModelSelectedIdsEvent event, Emitter emit) async { - // try { - // BuildContext context = NavigationService.navigatorKey.currentContext!; - // var spaceBloc = context.read(); - // for (var communityId in spaceBloc.state.selectedCommunities) { - // List spacesList = - // spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; - // for (var spaceId in spacesList) { - // print('spaceId===$spaceId'); - // } - // } - // } catch (e) { - // print('Error fetching space IDs: $e'); - // } - // } - + List spacesListIds = []; bool hasSelectedSpaces = false; + String validate = ''; Future _getSpaceIds( SpaceModelSelectedIdsEvent event, Emitter emit) async { @@ -40,22 +30,17 @@ class SpaceModelBloc extends Bloc { for (var communityId in spaceBloc.state.selectedCommunities) { List spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; + spacesListIds = spacesList; for (var spaceId in spacesList) { print('spaceId===$spaceId'); } } - // Check if any community has selected spaces hasSelectedSpaces = spaceBloc.state.selectedCommunities.any((communityId) { List spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; return spacesList.isNotEmpty; }); - - // Optionally, you could emit a state here if you want to rebuild the UI - // emit(SpaceModelSelectionUpdatedState(hasSelectedSpaces)); - - // Debug output if (hasSelectedSpaces) { print("At least one space is selected."); } else { @@ -65,4 +50,57 @@ class SpaceModelBloc extends Bloc { print("Error in _getSpaceIds: $e"); } } + + Future _handleLinkSpaceModel( + LinkSpaceModelEvent event, + Emitter emit, + ) async { + emit(SpaceModelLoading()); + try { + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + await SpaceModelManagementApi().linkSpaceModel( + spaceModelUuid: event.selectedSpaceMode!, + projectId: projectUuid, + spaceUuids: spacesListIds, + isOverWrite: event.isOverWrite); + emit(SpaceModelLinkSuccess()); + } on DioException catch (e) { + final errorMessage = _parseDioError(e); + emit(SpaceModelOperationFailure(errorMessage)); + } catch (e) { + emit(SpaceModelOperationFailure('Unexpected error: $e')); + } + } + + Future _validateLinkSpaceModel( + ValidateSpaceModelEvent event, + Emitter emit, + ) async { + emit(SpaceModelLoading()); + try { + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + await SpaceModelManagementApi().validateSpaceModel( + projectUuid, + spacesListIds, + ); + emit(SpaceValidationSuccess()); + } on DioException catch (e) { + final errorMessage = _parseDioError(e); + if (errorMessage == + 'Selected spaces already have linked space model / sub-spaces and devices') { + emit(const AlreadyHaveLinkedState()); + } else { + emit(SpaceModelOperationFailure(errorMessage)); + } + } catch (e) { + emit(SpaceModelOperationFailure('Unexpected error: $e')); + } + } + + String _parseDioError(DioException e) { + if (e.response?.data is Map) { + return e.response!.data['error']['message'] ?? 'Unknown error occurred'; + } + return e.message ?? 'Network request failed'; + } } 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 index 87b1e368..1595e695 100644 --- 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 @@ -1,3 +1,5 @@ +import 'package:flutter/material.dart'; + abstract class SpaceModelEvent {} class SpaceModelSelectedEvent extends SpaceModelEvent { @@ -6,8 +8,15 @@ class SpaceModelSelectedEvent extends SpaceModelEvent { SpaceModelSelectedEvent(this.selectedIndex); } +class SpaceModelSelectedIdsEvent extends SpaceModelEvent {} - -class SpaceModelSelectedIdsEvent extends SpaceModelEvent { - +class LinkSpaceModelEvent extends SpaceModelEvent { + final String? selectedSpaceMode; + final bool isOverWrite; + LinkSpaceModelEvent({this.selectedSpaceMode, this.isOverWrite = false}); +} + +class ValidateSpaceModelEvent extends SpaceModelEvent { + BuildContext? context; + ValidateSpaceModelEvent({this.context}); } 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 index cc745e4d..b7cb7c3f 100644 --- 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 @@ -1,9 +1,35 @@ -abstract class SpaceModelState {} +abstract class SpaceModelState { + const SpaceModelState(); +} class SpaceModelInitial extends SpaceModelState {} +class SpaceModelLoading extends SpaceModelState {} + class SpaceModelSelectedState extends SpaceModelState { final int selectedIndex; - - SpaceModelSelectedState(this.selectedIndex); + const SpaceModelSelectedState(this.selectedIndex); +} + +class SpaceModelSelectionUpdated extends SpaceModelState { + final bool hasSelectedSpaces; + const SpaceModelSelectionUpdated(this.hasSelectedSpaces); +} + +class SpaceValidationSuccess extends SpaceModelState {} + +class SpaceModelLinkSuccess extends SpaceModelState {} + +class ValidationError extends SpaceModelState { + final String message; + const ValidationError(this.message); +} + +class SpaceModelOperationFailure extends SpaceModelState { + final String message; + const SpaceModelOperationFailure(this.message); +} + +class AlreadyHaveLinkedState extends SpaceModelState { + const AlreadyHaveLinkedState(); } diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart index 668fee60..844513d3 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart @@ -1,16 +1,241 @@ +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:syncrow_web/pages/space_tree/view/space_tree_view.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/space_model/models/space_template_model.dart'; +// import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart'; +// import 'package:syncrow_web/utils/color_manager.dart'; + +// class LinkSpaceModelSpacesDialog extends StatefulWidget { +// final SpaceTemplateModel spaceModel; +// LinkSpaceModelSpacesDialog({super.key, required this.spaceModel}); + +// @override +// State createState() => +// _LinkSpaceModelSpacesDialogState(); +// } + +// class _LinkSpaceModelSpacesDialogState +// extends State { +// TextEditingController searchController = TextEditingController(); + +// @override +// void initState() { +// context.read().add(SpaceModelSelectedIdsEvent()); +// super.initState(); +// } + +// @override +// Widget build(BuildContext context) { +// return AlertDialog( +// contentPadding: EdgeInsets.zero, +// shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), +// backgroundColor: Colors.white, +// content: SizedBox( +// width: MediaQuery.of(context).size.width * 0.4, +// child: Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Expanded( +// child: Padding( +// padding: const EdgeInsets.all(15.0), +// child: Column( +// mainAxisSize: MainAxisSize.min, +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// const Center( +// child: Text( +// "Link Space Model to Spaces", +// style: TextStyle( +// fontSize: 18, +// fontWeight: FontWeight.bold, +// color: Colors.blueAccent, +// ), +// ), +// ), +// const Divider(), +// const SizedBox(height: 16), +// _buildDetailRow( +// "Space model name:", widget.spaceModel.modelName), +// _buildDetailRow("Creation date and time:", +// widget.spaceModel.createdAt.toString()), +// _buildDetailRow("Created by:", "Admin"), +// const SizedBox(height: 12), +// const Text( +// "Link to:", +// style: TextStyle(fontWeight: FontWeight.bold), +// ), +// const Text( +// "Please select all the spaces where you would like to link the Routine.", +// style: TextStyle(fontSize: 12, color: Colors.grey), +// ), +// const SizedBox(height: 8), +// Expanded( +// child: SizedBox( +// child: Column( +// children: [ +// Expanded( +// flex: 7, +// child: Container( +// color: ColorsManager.whiteColors, +// child: SpaceTreeView( +// isSide: true, +// onSelect: () { +// context.read().add( +// SpaceModelSelectedIdsEvent()); +// }))) +// ], +// ), +// ), +// ), +// const SizedBox( +// height: 20, +// ), +// ], +// ), +// ), +// ), + +// // Buttons +// Row( +// children: [ +// Expanded( +// child: Container( +// height: 50, +// decoration: const BoxDecoration( +// border: Border( +// right: BorderSide( +// color: ColorsManager.grayColor, +// width: 0.5, +// ), +// top: BorderSide( +// color: ColorsManager.grayColor, +// width: 1, +// ), +// ), +// ), +// child: _buildButton("Cancel", Colors.grey, () { +// Navigator.of(context).pop(); +// }), +// ), +// ), +// Expanded( +// child: Container( +// height: 50, +// decoration: const BoxDecoration( +// border: Border( +// left: BorderSide( +// color: ColorsManager.grayColor, +// width: 0.5, +// ), +// top: BorderSide( +// color: ColorsManager.grayColor, +// width: 1.0, +// ), +// ), +// ), +// child: _buildButton( +// "Confirm", ColorsManager.onSecondaryColor, () { +// final spaceModelBloc = context.read(); +// if (!spaceModelBloc.hasSelectedSpaces) { +// ScaffoldMessenger.of(context).showSnackBar( +// const SnackBar( +// content: +// Text("Please select at least one space")), +// ); +// return; +// } else { +// // spaceModelBloc.add(LinkSpaceModelEvent( +// // selectedSpaceMode: widget.spaceModel.uuid)); + +// spaceModelBloc.add(ValidateSpaceModelEvent(context: context)); +// } + +// Future.delayed(const Duration(seconds: 3), () { +// Navigator.of(context).pop(); +// Navigator.of(context).pop(); + +// // showDialog( +// // context: context, +// // builder: (BuildContext dialogContext) { +// // return const LinkingSuccessful(); +// // }, +// // ); + +// showDialog( +// context: context, +// builder: (BuildContext dialogContext) { +// return const ConfirmOverwriteDialog(); +// }, +// ); +// }); +// }), +// ), +// ), +// ], +// ), +// ], +// ), +// ), +// ); +// } + +// // Method to build a detail row +// Widget _buildDetailRow(String label, String value) { +// return Padding( +// padding: const EdgeInsets.symmetric(vertical: 4), +// child: Row( +// children: [ +// Expanded( +// child: Text( +// label, +// style: TextStyle(fontWeight: FontWeight.bold), +// ), +// ), +// const SizedBox(width: 8), +// Expanded( +// child: Text( +// value, +// style: +// TextStyle(fontWeight: FontWeight.bold, color: Colors.black), +// ), +// ), +// ], +// ), +// ); +// } + +// Widget _buildButton(String text, Color color, VoidCallback onPressed) { +// return InkWell( +// onTap: onPressed, +// child: Center( +// child: Text( +// text, +// style: TextStyle( +// color: color, fontWeight: FontWeight.w400, fontSize: 14), +// ), +// ), +// ); +// } +// } + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/space_tree/view/space_tree_view.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/dialog/confirm_overwrite_dialog.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class LinkSpaceModelSpacesDialog extends StatefulWidget { final SpaceTemplateModel spaceModel; - LinkSpaceModelSpacesDialog({super.key, required this.spaceModel}); + + const LinkSpaceModelSpacesDialog({super.key, required this.spaceModel}); @override State createState() => @@ -19,7 +244,8 @@ class LinkSpaceModelSpacesDialog extends StatefulWidget { class _LinkSpaceModelSpacesDialogState extends State { - TextEditingController searchController = TextEditingController(); + final TextEditingController _searchController = TextEditingController(); + bool _isLoading = false; @override void initState() { @@ -39,213 +265,253 @@ class _LinkSpaceModelSpacesDialogState mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Center( - child: Text( - "Link Space Model to Spaces", - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - color: Colors.blueAccent, - ), - ), - ), - const Divider(), - const SizedBox(height: 16), - _buildDetailRow( - "Space model name:", widget.spaceModel.modelName), - _buildDetailRow("Creation date and time:", - widget.spaceModel.createdAt.toString()), - _buildDetailRow("Created by:", "Admin"), - const SizedBox(height: 12), - const Text( - "Link to:", - style: TextStyle(fontWeight: FontWeight.bold), - ), - const Text( - "Please select all the spaces where you would like to link the Routine.", - style: TextStyle(fontSize: 12, color: Colors.grey), - ), - const SizedBox(height: 8), - Expanded( - child: SizedBox( - child: Column( - children: [ - Expanded( - flex: 7, - child: Container( - color: ColorsManager.whiteColors, - child: SpaceTreeView( - isSide: true, - onSelect: () { - context.read().add( - SpaceModelSelectedIdsEvent()); - }))) - ], - ), - ), - ), - const SizedBox( - height: 20, - ), - ], - ), - ), - ), - - // Buttons - Row( - children: [ - Expanded( - child: Container( - height: 50, - decoration: const BoxDecoration( - border: Border( - right: BorderSide( - color: ColorsManager.grayColor, - width: 0.5, - ), - top: BorderSide( - color: ColorsManager.grayColor, - width: 1, - ), - ), - ), - child: _buildButton("Cancel", Colors.grey, () { - Navigator.of(context).pop(); - }), - ), - ), - Expanded( - child: Container( - height: 50, - decoration: const BoxDecoration( - border: Border( - left: BorderSide( - color: ColorsManager.grayColor, - width: 0.5, - ), - top: BorderSide( - color: ColorsManager.grayColor, - width: 1.0, - ), - ), - ), - child: _buildButton( - "Confirm", ColorsManager.onSecondaryColor, () { - final spaceModelBloc = context.read(); - if (!spaceModelBloc.hasSelectedSpaces) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: - Text("Please select at least one space")), - ); - return; - } - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return Dialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20)), - elevation: 10, - backgroundColor: Colors.white, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 30, horizontal: 50), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - CustomLoadingIndicator(), - const SizedBox(height: 20), - const Text( - "Linking in progress", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.black87, - ), - ), - ], - ), - ), - ); - }, - ).then( - (value) {}, - ); - Future.delayed(const Duration(seconds: 3), () { - Navigator.of(context).pop(); - Navigator.of(context).pop(); - - // showDialog( - // context: context, - // builder: (BuildContext dialogContext) { - // return const LinkingSuccessful(); - // }, - // ); - - showDialog( - context: context, - builder: (BuildContext dialogContext) { - return const ConfirmOverwriteDialog(); - }, - ); - }); - }), - ), - ), - ], - ), + _buildDialogContent(), + _buildActionButtons(), ], ), ), ); } - // Method to build a detail row - Widget _buildDetailRow(String label, String value) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 4), - child: Row( - children: [ - Expanded( - child: Text( - label, - style: TextStyle(fontWeight: FontWeight.bold), - ), + Widget _buildDialogContent() { + return Expanded( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.all(15.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Center( + child: Text( + "Link Space Model to Spaces", + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Colors.blueAccent, + ), + ), + ), + const Divider(), + const SizedBox(height: 16), + _buildDetailRow( + "Space model name:", widget.spaceModel.modelName), + _buildDetailRow("Creation date and time:", + widget.spaceModel.createdAt.toString()), + _buildDetailRow("Created by:", "Admin"), + const SizedBox(height: 12), + const Text( + "Link to:", + style: TextStyle(fontWeight: FontWeight.bold), + ), + const Text( + "Please select all the spaces where you would like to link the Routine.", + style: TextStyle(fontSize: 12, color: Colors.grey), + ), + const SizedBox(height: 8), + Expanded( + child: SizedBox( + child: Column( + children: [ + Expanded( + flex: 7, + child: Container( + color: ColorsManager.whiteColors, + child: SpaceTreeView( + isSide: true, + onSelect: () { + context.read().add( + SpaceModelSelectedIdsEvent()); + }))) + ], + ), + ), + ), + const SizedBox( + height: 20, + ), + ], + ), + ), + ), + ], ), - const SizedBox(width: 8), - Expanded( - child: Text( - value, - style: - TextStyle(fontWeight: FontWeight.bold, color: Colors.black), - ), - ), - ], + ), ), ); } - // Button Widget - Widget _buildButton(String text, Color color, VoidCallback onPressed) { - return InkWell( - onTap: onPressed, - child: Center( - child: Text( - text, - style: TextStyle( - color: color, fontWeight: FontWeight.w400, fontSize: 14), + Widget _buildActionButtons() { + return Row( + children: [ + Expanded( + child: Container( + height: 50, + decoration: const BoxDecoration( + border: Border( + right: BorderSide( + color: ColorsManager.grayColor, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.grayColor, + width: 1, + ), + ), + ), + child: _buildButton("Cancel", Colors.grey, () { + Navigator.of(context).pop(); + }), + ), + ), + Expanded( + child: Container( + height: 50, + decoration: const BoxDecoration( + border: Border( + left: BorderSide( + color: ColorsManager.grayColor, + width: 0.5, + ), + top: BorderSide( + color: ColorsManager.grayColor, + width: 1.0, + ), + ), + ), + child: _buildButton( + "Confirm", + ColorsManager.onSecondaryColor, + () { + final spaceModelBloc = context.read(); + if (!spaceModelBloc.hasSelectedSpaces) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text("Please select at least one space")), + ); + return; + } else { + spaceModelBloc.add(ValidateSpaceModelEvent(context: context)); + } + }, + ), + ), + ), + ], + ); + } + + void _handleConfirm() { + final bloc = context.read(); + if (!bloc.hasSelectedSpaces) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text("Please select at least one space")), + ); + return; + } + + // Trigger validation + bloc.add(ValidateSpaceModelEvent()); + } + + void _showLoadingDialog() { + showDialog( + context: context, + barrierDismissible: false, + builder: (context) => const _LoadingDialog(), + ); + } + + void _handleValidationSuccess() { + Navigator.of(context).pop(); // Close loading dialog + + // Show overwrite confirmation + showDialog( + context: context, + builder: (context) => const ConfirmOverwriteDialog(), + ).then((_) { + // Close main dialog after confirmation + if (mounted) Navigator.of(context).pop(); + }); + } + + void _handleValidationError(String message) { + Navigator.of(context).pop(); // Close loading dialog + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(message)), + ); + } + + void _handleCancel() { + if (mounted) Navigator.of(context).pop(); + } + + // Rest of your helper methods (_buildDetailRow, _buildButton, etc.) +} + +class _LoadingDialog extends StatelessWidget { + const _LoadingDialog(); + + @override + Widget build(BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + child: const Padding( + padding: EdgeInsets.all(20.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CircularProgressIndicator(), + SizedBox(height: 16), + Text("Linking in progress..."), + ], ), ), ); } } + +// Method to build a detail row +Widget _buildDetailRow(String label, String value) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: Row( + children: [ + Expanded( + child: Text( + label, + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + value, + style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black), + ), + ), + ], + ), + ); +} + +Widget _buildButton(String text, Color color, VoidCallback onPressed) { + return InkWell( + onTap: onPressed, + child: Center( + child: Text( + text, + style: + TextStyle(color: color, fontWeight: FontWeight.w400, fontSize: 14), + ), + ), + ); +} diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart index 8659b7b5..6a34002f 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart @@ -1,24 +1,27 @@ import 'package:flutter/material.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/space_model/models/space_template_model.dart'; -void showOverwriteDialog(BuildContext context) { +void showOverwriteDialog( + BuildContext context, SpaceModelBloc bloc, SpaceTemplateModel model) { showDialog( context: context, - barrierDismissible: false, + barrierDismissible: false, builder: (BuildContext context) { - return Container( + return SizedBox( child: Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), elevation: 10, backgroundColor: Colors.white, - child: Container( + child: SizedBox( width: MediaQuery.of(context).size.width * 0.3, child: Padding( - padding: EdgeInsets.symmetric(vertical: 30, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 20), child: Column( mainAxisSize: MainAxisSize.min, children: [ - // Title const Text( "Overwrite", style: TextStyle( @@ -27,15 +30,13 @@ void showOverwriteDialog(BuildContext context) { color: Colors.black, ), ), - SizedBox(height: 15), - - // Description + const SizedBox(height: 15), const Text( "Are you sure you want to overwrite?", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), textAlign: TextAlign.center, ), - SizedBox(height: 5), + const SizedBox(height: 5), Text( "Selected spaces already have linked space model / sub-spaces and devices", style: TextStyle( @@ -44,25 +45,22 @@ void showOverwriteDialog(BuildContext context) { ), textAlign: TextAlign.center, ), - SizedBox(height: 25), - - // Buttons + const SizedBox(height: 25), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - // Cancel Button Expanded( child: ElevatedButton( onPressed: () => Navigator.of(context).pop(), style: ElevatedButton.styleFrom( - padding: EdgeInsets.symmetric(vertical: 14), + padding: const EdgeInsets.symmetric(vertical: 14), backgroundColor: Colors.grey[200], shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), - child: Text( + child: const Text( "Cancel", style: TextStyle( fontSize: 16, @@ -72,24 +70,24 @@ void showOverwriteDialog(BuildContext context) { ), ), ), - SizedBox(width: 10), - - // OK Button + const SizedBox(width: 10), Expanded( child: ElevatedButton( onPressed: () { + bloc.add(LinkSpaceModelEvent( + isOverWrite: true, + selectedSpaceMode: model.uuid)); Navigator.of(context).pop(); - // Add action for OK button }, style: ElevatedButton.styleFrom( - padding: EdgeInsets.symmetric(vertical: 14), + padding: const EdgeInsets.symmetric(vertical: 14), backgroundColor: Colors.blue, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 3, ), - child: Text( + child: const Text( "OK", style: TextStyle( fontSize: 16, diff --git a/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart b/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart index c7d9c72f..56ba5d3b 100644 --- a/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart @@ -3,9 +3,13 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.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/dialog/custom_loading_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_product_widget.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dynamic_room_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -74,14 +78,96 @@ class SpaceModelCardWidget extends StatelessWidget { children: [ InkWell( onTap: () { - showDialog( context: context, - builder: (context) { + builder: (BuildContext dialogContext) { return BlocProvider( create: (_) => SpaceModelBloc(), - child: LinkSpaceModelSpacesDialog( - spaceModel: model, + child: BlocListener( + listener: (context, state) { + final _bloc = + BlocProvider.of( + context); + if (state is SpaceModelLoading) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return Dialog( + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.circular(20)), + elevation: 10, + backgroundColor: Colors.white, + child: Padding( + padding: + const EdgeInsets.symmetric( + vertical: 30, + horizontal: 50), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + CustomLoadingIndicator(), + const SizedBox(height: 20), + const Text( + "Linking in progress", + style: TextStyle( + fontSize: 16, + fontWeight: + FontWeight.w500, + color: Colors.black87, + ), + ), + ], + ), + ), + ); + }, + ); + } else if (state + is AlreadyHaveLinkedState) { + Navigator.of(dialogContext).pop(); + showOverwriteDialog( + context, _bloc, model); + } else if (state + is SpaceValidationSuccess) { + _bloc.add(LinkSpaceModelEvent( + isOverWrite: false, + selectedSpaceMode: model.uuid)); + + Future.delayed(const Duration(seconds: 1), + () { + Navigator.of(dialogContext).pop(); + Navigator.of(dialogContext).pop(); + Navigator.of(dialogContext).pop(); + }); + + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const LinkingSuccessful(); + }, + ).then((v) { + Future.delayed( + const Duration(seconds: 2), () { + Navigator.of(dialogContext).pop(); + }); + }); + } else if (state is SpaceModelLinkSuccess) { + Navigator.of(dialogContext).pop(); + Navigator.of(dialogContext).pop(); + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const LinkingSuccessful(); + }, + ); + } + }, + child: LinkSpaceModelSpacesDialog( + spaceModel: model, + ), ), ); }, diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index d9e295e0..4af8caa9 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -60,4 +60,40 @@ class SpaceModelManagementApi { ); return response; } + + Future linkSpaceModel( + {required String spaceModelUuid, + required String projectId, + required List spaceUuids, + required bool isOverWrite}) async { + print(spaceModelUuid); + print(projectId); + print(spaceUuids); + print(isOverWrite); + + final response = await HTTPService().post( + path: ApiEndpoints.linkSpaceModel + .replaceAll('{projectId}', projectId) + .replaceAll('{spaceModelUuid}', spaceModelUuid), + showServerMessage: true, + body: {"spaceUuids": spaceUuids, "overwrite": isOverWrite}, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } + + Future validateSpaceModel(String projectId, List spaceUuids) async { + final response = await HTTPService().post( + path: + ApiEndpoints.validateSpaceModel.replaceAll('{projectId}', projectId), + showServerMessage: true, + body: {"spacesUuids": spaceUuids}, + expectedResponseModel: (json) { + return json; + }, + ); + return response; + } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 72a2b778..d3cd6001 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -9,15 +9,19 @@ abstract class ApiEndpoints { static const String sendOtp = '/authentication/user/send-otp'; static const String verifyOtp = '/authentication/user/verify-otp'; static const String getRegion = '/region'; - static const String visitorPassword = '/projects/{projectId}/visitor-password'; - static const String getDevices = '/projects/{projectId}/visitor-password/devices'; + static const String visitorPassword = + '/projects/{projectId}/visitor-password'; + static const String getDevices = + '/projects/{projectId}/visitor-password/devices'; - static const String sendOnlineOneTime = '/visitor-password/temporary-password/online/one-time'; + static const String sendOnlineOneTime = + '/visitor-password/temporary-password/online/one-time'; static const String sendOnlineMultipleTime = '/visitor-password/temporary-password/online/multiple-time'; //offline Password - static const String sendOffLineOneTime = '/visitor-password/temporary-password/offline/one-time'; + static const String sendOffLineOneTime = + '/visitor-password/temporary-password/offline/one-time'; static const String sendOffLineMultipleTime = '/visitor-password/temporary-password/offline/multiple-time'; @@ -39,32 +43,45 @@ abstract class ApiEndpoints { static const String getDeviceLogs = '/device/report-logs/{uuid}?code={code}'; // Space Module - static const String createSpace = '/projects/{projectId}/communities/{communityId}/spaces'; - static const String listSpaces = '/projects/{projectId}/communities/{communityId}/spaces'; + static const String createSpace = + '/projects/{projectId}/communities/{communityId}/spaces'; + static const String listSpaces = + '/projects/{projectId}/communities/{communityId}/spaces'; static const String deleteSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}'; static const String updateSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}'; - static const String getSpace = '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}'; - static const String getSpaceHierarchy = '/projects/{projectId}/communities/{communityId}/spaces'; + static const String getSpace = + '/projects/{projectId}/communities/{communityId}/spaces/{spaceId}'; + static const String getSpaceHierarchy = + '/projects/{projectId}/communities/{communityId}/spaces'; // Community Module static const String createCommunity = '/projects/{projectId}/communities'; static const String getCommunityList = '/projects/{projectId}/communities'; - static const String getCommunityById = '/projects/{projectId}/communities/{communityId}'; - static const String updateCommunity = '/projects/{projectId}/communities/{communityId}'; - static const String deleteCommunity = '/projects/{projectId}communities/{communityId}'; - static const String getUserCommunities = '/projects/{projectId}/communities/user/{userUuid}'; - static const String createUserCommunity = '/projects/{projectId}/communities/user'; + static const String getCommunityById = + '/projects/{projectId}/communities/{communityId}'; + static const String updateCommunity = + '/projects/{projectId}/communities/{communityId}'; + static const String deleteCommunity = + '/projects/{projectId}communities/{communityId}'; + static const String getUserCommunities = + '/projects/{projectId}/communities/user/{userUuid}'; + static const String createUserCommunity = + '/projects/{projectId}/communities/user'; static const String getDeviceLogsByDate = '/device/report-logs/{uuid}?code={code}&startTime={startTime}&endTime={endTime}'; static const String scheduleByDeviceId = '/schedule/{deviceUuid}'; - static const String getScheduleByDeviceId = '/schedule/{deviceUuid}?category={category}'; - static const String deleteScheduleByDeviceId = '/schedule/{deviceUuid}/{scheduleUuid}'; - static const String updateScheduleByDeviceId = '/schedule/enable/{deviceUuid}'; + static const String getScheduleByDeviceId = + '/schedule/{deviceUuid}?category={category}'; + static const String deleteScheduleByDeviceId = + '/schedule/{deviceUuid}/{scheduleUuid}'; + static const String updateScheduleByDeviceId = + '/schedule/enable/{deviceUuid}'; static const String factoryReset = '/device/factory/reset/{deviceUuid}'; - static const String powerClamp = '/device/{powerClampUuid}/power-clamp/status'; + static const String powerClamp = + '/device/{powerClampUuid}/power-clamp/status'; //product static const String listProducts = '/products'; @@ -75,7 +92,8 @@ abstract class ApiEndpoints { static const String createAutomation = '/automation'; static const String getUnitScenes = '/projects/{projectId}/communities/{communityUuid}/spaces/{spaceUuid}/scenes'; - static const String getAutomationDetails = '/automation/details/{automationId}'; + static const String getAutomationDetails = + '/automation/details/{automationId}'; static const String getScene = '/scene/tap-to-run/{sceneId}'; static const String deleteScene = '/scene/tap-to-run/{sceneId}'; @@ -87,8 +105,15 @@ abstract class ApiEndpoints { //space model static const String listSpaceModels = '/projects/{projectId}/space-models'; static const String createSpaceModel = '/projects/{projectId}/space-models'; - static const String getSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}'; - static const String updateSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}'; + static const String getSpaceModel = + '/projects/{projectId}/space-models/{spaceModelUuid}'; + static const String updateSpaceModel = + '/projects/{projectId}/space-models/{spaceModelUuid}'; + static const String linkSpaceModel = + '/projects/{projectId}/space-models/{spaceModelUuid}/spaces/link'; + + static const String validateSpaceModel = + '/projects/{projectId}/spaces/validate'; static const String roleTypes = '/role/types'; static const String permission = '/permission/{roleUuid}'; @@ -99,7 +124,8 @@ abstract class ApiEndpoints { static const String getUserById = '/projects/{projectUuid}/user/{userUuid}'; static const String editUser = '/invite-user/{inviteUserUuid}'; static const String deleteUser = '/invite-user/{inviteUserUuid}'; - static const String changeUserStatus = '/invite-user/{invitedUserUuid}/disable'; + static const String changeUserStatus = + '/invite-user/{invitedUserUuid}/disable'; static const String terms = '/terms'; static const String policy = '/policy'; static const String userAgreements = '/user/agreements/web/{userUuid}';