diff --git a/assets/icons/delete_space_model.svg b/assets/icons/delete_space_model.svg new file mode 100644 index 00000000..7fb9a9b0 --- /dev/null +++ b/assets/icons/delete_space_model.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart index facfe64f..16ba41c0 100644 --- a/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/space_model_bloc.dart @@ -4,9 +4,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_state.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; -import 'package:syncrow_web/utils/constants/strings_manager.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; -import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; class SpaceModelBloc extends Bloc { final SpaceModelManagementApi api; @@ -17,6 +14,7 @@ class SpaceModelBloc extends Bloc { }) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) { on(_onCreateSpaceModel); on(_onUpdateSpaceModel); + on(_onDeleteSpaceModel); } Future _onCreateSpaceModel( @@ -50,7 +48,7 @@ class SpaceModelBloc extends Bloc { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; final newSpaceModel = - await api.getSpaceModel(event.spaceModelUuid ?? '', projectUuid); + await api.getSpaceModel(event.spaceModelUuid, projectUuid); if (newSpaceModel != null) { final updatedSpaceModels = currentState.spaceModels.map((model) { return model.uuid == event.spaceModelUuid ? newSpaceModel : model; @@ -62,4 +60,28 @@ class SpaceModelBloc extends Bloc { } } } + + Future _onDeleteSpaceModel( + DeleteSpaceModel event, Emitter emit) async { + final currentState = state; + + if (currentState is SpaceModelLoaded) { + try { + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + + final deletedSuccessfully = + await api.deleteSpaceModel(event.spaceModelUuid, projectUuid); + + if (deletedSuccessfully) { + final updatedSpaceModels = currentState.spaceModels + .where((model) => model.uuid != event.spaceModelUuid) + .toList(); + + emit(SpaceModelLoaded(spaceModels: updatedSpaceModels)); + } + } catch (e) { + emit(SpaceModelError(message: e.toString())); + } + } + } } diff --git a/lib/pages/spaces_management/space_model/bloc/space_model_event.dart b/lib/pages/spaces_management/space_model/bloc/space_model_event.dart index 8f71e611..33856427 100644 --- a/lib/pages/spaces_management/space_model/bloc/space_model_event.dart +++ b/lib/pages/spaces_management/space_model/bloc/space_model_event.dart @@ -34,3 +34,12 @@ class UpdateSpaceModel extends SpaceModelEvent { @override List get props => [spaceModelUuid]; } + +class DeleteSpaceModel extends SpaceModelEvent { + final String spaceModelUuid; + + DeleteSpaceModel({required this.spaceModelUuid}); + + @override + List get props => [spaceModelUuid]; +} 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 8f466a5e..35611868 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 @@ -90,7 +90,11 @@ class SpaceModelPage extends StatelessWidget { }, child: Container( margin: const EdgeInsets.all(8.0), - child: SpaceModelCardWidget(model: model), + child: SpaceModelCardWidget( + model: model, + pageContext: context, + topActionsDisabled: false, + ), )); }, ), diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart new file mode 100644 index 00000000..8349baa4 --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/delete_space_model_dialog.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.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/utils/color_manager.dart'; + +class DeleteSpaceModelDialog extends StatelessWidget { + final VoidCallback onConfirmDelete; + + const DeleteSpaceModelDialog({Key? key, required this.onConfirmDelete}) + : super(key: key); + + @override + Widget build(BuildContext context) { + final screenWidth = MediaQuery.of(context).size.width; + + return AlertDialog( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + backgroundColor: ColorsManager.whiteColors, + title: Center( + child: Text( + "Delete Space Model", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .headlineLarge + ?.copyWith(color: ColorsManager.blackColor), + ), + ), + content: SizedBox( + width: screenWidth * 0.4, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Are you sure you want to delete?", + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineSmall, + ), + const SizedBox(height: 15), + Text( + "The existing sub-spaces, devices, and routines will remain associated with the spaces, but the connection will be removed.", + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: ColorsManager.lightGrayColor), + ), + ], + ), + ), + actions: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + width: 200, + child: CancelButton( + onPressed: () { + Navigator.of(context).pop(); // Close dialog + }, + label: "Cancel", + ), + ), + const SizedBox(width: 10), + SizedBox( + width: 200, + child: DefaultButton( + onPressed: () { + Navigator.of(context).pop(); // Close dialog + onConfirmDelete(); // Execute delete action + }, + backgroundColor: ColorsManager.semiTransparentRed, + borderRadius: 10, + foregroundColor: ColorsManager.whiteColors, + child: const Text('Delete'), + ), + ), + ], + ), + ], + ); + } +} 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 0056c96f..ab65af44 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 @@ -1,13 +1,30 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/common/buttons/cancel_button.dart'; +import 'package:syncrow_web/pages/common/buttons/default_button.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/bloc/space_model_bloc.dart'; + +import 'package:syncrow_web/pages/spaces_management/space_model/bloc/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/delete_space_model_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'; +import 'package:syncrow_web/utils/constants/assets.dart'; class SpaceModelCardWidget extends StatelessWidget { final SpaceTemplateModel model; + final BuildContext? pageContext; + final bool topActionsDisabled; - const SpaceModelCardWidget({Key? key, required this.model}) : super(key: key); + const SpaceModelCardWidget({ + Key? key, + required this.model, + this.pageContext, + this.topActionsDisabled = true, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -34,7 +51,7 @@ class SpaceModelCardWidget extends StatelessWidget { return LayoutBuilder( builder: (context, constraints) { bool showOnlyName = constraints.maxWidth < 250; - return Container( + return Container( padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Colors.white, @@ -51,14 +68,50 @@ class SpaceModelCardWidget extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - model.modelName, - style: Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Colors.black, - fontWeight: FontWeight.bold, + Row( + children: [ + Expanded( + child: Text( + model.modelName, + style: + Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - maxLines: 1, - overflow: TextOverflow.ellipsis, + ), + if (!topActionsDisabled) + GestureDetector( + onTap: () => _showDeleteDialog(context), + child: Container( + width: 36, // Adjust size as needed + height: 36, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.1), + spreadRadius: 2, + blurRadius: 5, + offset: const Offset(0, 2), + ), + ], + ), + child: Center( + child: SvgPicture.asset( + Assets.deleteSpaceModel, // Your actual SVG path + width: 20, + height: 20, + colorFilter: const ColorFilter.mode( + Colors.grey, BlendMode.srcIn), + ), + ), + ), + ), + ], ), if (!showOnlyName) ...[ const SizedBox(height: 10), @@ -117,4 +170,22 @@ class SpaceModelCardWidget extends StatelessWidget { }, ); } + + void _showDeleteDialog(BuildContext context) { + showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext dialogContext) { + return DeleteSpaceModelDialog( + onConfirmDelete: () { + if (pageContext != null) { + pageContext!.read().add( + DeleteSpaceModel(spaceModelUuid: model.uuid ?? ''), + ); + } + }, + ); + }, + ); + } } diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index d9e295e0..15764083 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -60,4 +60,17 @@ class SpaceModelManagementApi { ); return response; } + + Future deleteSpaceModel(String spaceModelUuid, String projectId) async { + final response = await HTTPService().delete( + path: ApiEndpoints.getSpaceModel + .replaceAll('{projectId}', projectId) + .replaceAll('{spaceModelUuid}', spaceModelUuid), + showServerMessage: true, + expectedResponseModel: (json) { + return json['success'] ?? false; + }, + ); + return response; + } } diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 4d3dbb0c..a4bcc0da 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -71,4 +71,5 @@ abstract class ColorsManager { static const Color lightGrayBorderColor = Color(0xB2D5D5D5); //background: #F8F8F8; static const Color vividBlue = Color(0xFF023DFE); + static const Color semiTransparentRed = Color(0x99FF0000); } diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index d5d216c5..d7b4d283 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -258,6 +258,7 @@ class Assets { static const String doorSensor = 'assets/icons/door_sensor.svg'; static const String delete = 'assets/icons/delete.svg'; + static const String deleteSpaceModel = 'assets/icons/delete_space_model.svg'; static const String edit = 'assets/icons/edit.svg'; static const String editSpace = 'assets/icons/edit_space.svg'; //assets/icons/routine/tab_to_run.svg