From 00a9cb11880fe5a7410b0cb1a00f5e861784ada3 Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 10 Feb 2025 12:38:20 +0300 Subject: [PATCH 01/21] link_space model --- assets/icons/delete_space_link_icon.svg | 21 ++ assets/icons/space_link_icon.svg | 25 +++ .../add_user_dialog/view/build_tree_view.dart | 1 - .../models/space_template_model.dart | 3 + .../widgets/dialog/confirm_merge_dialog.dart | 84 ++++++++ .../dialog/confirm_overwrite_dialog.dart | 84 ++++++++ .../widgets/dialog/custom_loading_dialog.dart | 110 ++++++++++ .../link_space_model_spaces_dialog.dart | 190 ++++++++++++++++++ .../dialog/linking_attention_dialog.dart | 111 ++++++++++ .../widgets/dialog/overwrite_dialog.dart | 112 +++++++++++ .../widgets/space_model_card_widget.dart | 61 +++++- lib/services/space_mana_api.dart | 1 + lib/utils/color_manager.dart | 2 +- lib/utils/constants/assets.dart | 6 +- 14 files changed, 799 insertions(+), 12 deletions(-) create mode 100644 assets/icons/delete_space_link_icon.svg create mode 100644 assets/icons/space_link_icon.svg create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart diff --git a/assets/icons/delete_space_link_icon.svg b/assets/icons/delete_space_link_icon.svg new file mode 100644 index 00000000..a55d2e04 --- /dev/null +++ b/assets/icons/delete_space_link_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/space_link_icon.svg b/assets/icons/space_link_icon.svg new file mode 100644 index 00000000..f10c57ad --- /dev/null +++ b/assets/icons/space_link_icon.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart index b7fc1085..24ada87d 100644 --- a/lib/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart +++ b/lib/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart @@ -19,7 +19,6 @@ class TreeView extends StatelessWidget { @override Widget build(BuildContext context) { final _blocRole = BlocProvider.of(context); - debugPrint('TreeView constructed with userId = $userId'); return BlocProvider( create: (_) => UsersBloc(), // ..add(const LoadCommunityAndSpacesEvent()), diff --git a/lib/pages/spaces_management/space_model/models/space_template_model.dart b/lib/pages/spaces_management/space_model/models/space_template_model.dart index 22378f1f..aca986bf 100644 --- a/lib/pages/spaces_management/space_model/models/space_template_model.dart +++ b/lib/pages/spaces_management/space_model/models/space_template_model.dart @@ -11,6 +11,7 @@ class SpaceTemplateModel extends Equatable { List? subspaceModels; final List? tags; String internalId; + DateTime? createdAt; @override List get props => [modelName, subspaceModels, tags]; @@ -21,6 +22,7 @@ class SpaceTemplateModel extends Equatable { required this.modelName, this.subspaceModels, this.tags, + this.createdAt, }) : internalId = internalId ?? const Uuid().v4(); factory SpaceTemplateModel.fromJson(Map json) { @@ -28,6 +30,7 @@ class SpaceTemplateModel extends Equatable { return SpaceTemplateModel( uuid: json['uuid'] ?? '', + createdAt: json['createdAt'] ?? '', internalId: internalId, modelName: json['modelName'] ?? '', subspaceModels: (json['subspaceModels'] as List?) diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart new file mode 100644 index 00000000..2a39d67b --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ConfirmMergeDialog extends StatelessWidget { + const ConfirmMergeDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + title: Center( + child: Text( + 'Merge', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 30), + )), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want to merge?', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 18), + ), + const SizedBox(height: 25), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.boxColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Cancel", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.secondaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Ok", + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 16, + color: ColorsManager.whiteColors, + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart new file mode 100644 index 00000000..7e7c5641 --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart @@ -0,0 +1,84 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class ConfirmOverwriteDialog extends StatelessWidget { + const ConfirmOverwriteDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + title: Center( + child: Text( + 'Overwrite', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 30), + )), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Are you sure you want to overwrite?', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 18), + ), + const SizedBox(height: 25), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.pop(context); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.boxColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Cancel", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: ElevatedButton( + onPressed: () {}, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.secondaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Ok", + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 16, + color: ColorsManager.whiteColors, + ), + ), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart new file mode 100644 index 00000000..2d50d93b --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart @@ -0,0 +1,110 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; + +void showCustomLoadingDialog(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, // Prevent closing by tapping outside + builder: (BuildContext context) { + Future.delayed(Duration(seconds: 3), () { + Navigator.of(context).pop(); // Auto-close after 3 seconds + }); + + return Dialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + elevation: 10, + backgroundColor: Colors.white, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 30, horizontal: 50), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Custom loader + CustomLoadingIndicator(), + SizedBox(height: 20), + Text( + "Linking in progress", + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.black87, + ), + ), + ], + ), + ), + ); + }, + ); +} + +class CustomLoadingIndicator extends StatefulWidget { + @override + _CustomLoadingIndicatorState createState() => _CustomLoadingIndicatorState(); +} + +class _CustomLoadingIndicatorState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: Duration(seconds: 1), // Rotation speed + )..repeat(); // Infinite animation + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 50, + height: 50, + child: AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.rotate( + angle: _controller.value * 2 * pi, // Full rotation + child: CustomPaint( + painter: LoadingPainter(), + ), + ); + }, + ), + ); + } +} + +class LoadingPainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + final Paint paint = Paint() + ..strokeWidth = 5 + ..strokeCap = StrokeCap.round + ..style = PaintingStyle.stroke; + + final double radius = size.width / 2; + final Offset center = Offset(size.width / 2, size.height / 2); + + for (int i = 0; i < 12; i++) { + final double angle = (i * 30) * (pi / 180); + final double startX = center.dx + radius * cos(angle); + final double startY = center.dy + radius * sin(angle); + final double endX = center.dx + (radius - 8) * cos(angle); + final double endY = center.dy + (radius - 8) * sin(angle); + + paint.color = Colors.blue.withOpacity(i / 12); // Gradient effect + canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint); + } + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} 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 new file mode 100644 index 00000000..ff38152e --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/link_space_model_spaces_dialog.dart @@ -0,0 +1,190 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.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/extension/build_context_x.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class LinkSpaceModelSpacesDialog extends StatelessWidget { + final SpaceTemplateModel spaceModel; + LinkSpaceModelSpacesDialog({super.key, required this.spaceModel}); + + TextEditingController searchController = TextEditingController(); + + @override + Widget build(BuildContext context) { + return AlertDialog( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + backgroundColor: Colors.white, + contentPadding: const EdgeInsets.all(20), + content: SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title + 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), + + // Details Section + _buildDetailRow("Space model name:", spaceModel.modelName), + _buildDetailRow("Creation date and time:", spaceModel.modelName), + _buildDetailRow("Created by:", "Admin"), + const SizedBox(height: 12), + + // Link to Section + 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), + + // Spaces List + Expanded( + child: SizedBox( + child: Column( + children: [ + Expanded( + flex: 2, + child: Container( + decoration: const BoxDecoration( + color: ColorsManager.circleRolesBackground, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + topLeft: Radius.circular(20)), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(20)), + border: Border.all( + color: ColorsManager.grayBorder)), + child: TextFormField( + style: const TextStyle(color: Colors.black), + // controller: _blocRole.firstNameController, + onChanged: (value) { + // _blocRole.add(SearchAnode( + // nodes: _blocRole.updatedCommunities, + // searchTerm: value)); + }, + decoration: + textBoxDecoration(radios: 20)!.copyWith( + fillColor: Colors.white, + suffixIcon: Padding( + padding: + const EdgeInsets.only(right: 16), + child: SvgPicture.asset( + Assets.textFieldSearch, + width: 24, + height: 24, + ), + ), + hintStyle: context.textTheme.bodyMedium + ?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.textGray), + ), + ), + ), + ), + ], + ), + ), + ), + ), + Expanded( + flex: 7, + child: Container( + color: ColorsManager.circleRolesBackground, + padding: const EdgeInsets.all(8.0), + child: Container( + color: ColorsManager.whiteColors, + child: TreeView(userId: '')))) + ], + ), + ), + ), + + // Buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildButton("Cancel", Colors.grey, () { + Navigator.of(context).pop(); + }), + _buildButton("Confirm", Colors.blueAccent, () { + Navigator.of(context).pop(); + }), + ], + ), + ], + ), + ), + ); + } + + // 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), + ), + ), + ], + ), + ); + } + + // Button Widget + Widget _buildButton(String text, Color color, VoidCallback onPressed) { + return TextButton( + onPressed: onPressed, + style: TextButton.styleFrom( + padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), + backgroundColor: color.withOpacity(0.2), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + child: Text( + text, + style: TextStyle(color: color, fontWeight: FontWeight.bold), + ), + ); + } +} diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart new file mode 100644 index 00000000..a0807dba --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/confirm_merge_dialog.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 LinkingAttentionDialog extends StatelessWidget { + const LinkingAttentionDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + title: Center( + child: Text( + 'Linking Attention', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 30), + )), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Do you want to merge or overwrite?', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 18), + ), + const SizedBox(height: 8), + Text( + 'Selected spaces already have commissioned Devices', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 14), + ), + const SizedBox(height: 25), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // Cancel Button + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const ConfirmOverwriteDialog(); + }, + ); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.boxColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Overwrite", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 16), + + ), + ), + ), + const SizedBox(width: 10), + + // OK Button + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.pop(context); + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const ConfirmMergeDialog(); + }, + ); + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.symmetric(vertical: 14), + backgroundColor: ColorsManager.boxColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "Merge", + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 16), + ), + ), + ), + ], + ), + ], + ), + ); + } +} 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 new file mode 100644 index 00000000..3d207968 --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/overwrite_dialog.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; + +void showOverwriteDialog(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, // Prevent closing by tapping outside + builder: (BuildContext context) { + return Container( + child: Dialog( + shape: + RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), + elevation: 10, + backgroundColor: Colors.white, + child: Container( + width: MediaQuery.of(context).size.width * 0.3, + child: Padding( + padding: EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Title + Text( + "Overwrite", + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.bold, + color: Colors.black, + ), + ), + SizedBox(height: 15), + + // Description + Text( + "Are you sure you want to overwrite?", + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), + textAlign: TextAlign.center, + ), + SizedBox(height: 5), + Text( + "Selected spaces already have linked space model / sub-spaces and devices", + style: TextStyle( + fontSize: 14, + color: Colors.grey[600], + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 25), + + // Buttons + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // Cancel Button + Expanded( + child: ElevatedButton( + onPressed: () => Navigator.of(context).pop(), + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 14), + backgroundColor: Colors.grey[200], + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 0, + ), + child: Text( + "Cancel", + style: TextStyle( + fontSize: 16, + color: Colors.black, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + SizedBox(width: 10), + + // OK Button + Expanded( + child: ElevatedButton( + onPressed: () { + Navigator.of(context).pop(); + // Add action for OK button + }, + style: ElevatedButton.styleFrom( + padding: EdgeInsets.symmetric(vertical: 14), + backgroundColor: Colors.blue, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 3, + ), + child: Text( + "OK", + style: TextStyle( + fontSize: 16, + color: Colors.white, + fontWeight: FontWeight.w500, + ), + ), + ), + ), + ], + ), + ], + ), + ), + ), + ), + ); + }, + ); +} 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..52b0fa6c 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,8 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.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/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/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; @@ -34,7 +38,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 +55,53 @@ 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, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + model.modelName, + style: Theme.of(context).textTheme.headlineMedium?.copyWith( + color: Colors.black, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Row( + children: [ + InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return LinkSpaceModelSpacesDialog( + spaceModel: model, + ); + }, + ); + }, + child: SvgPicture.asset( + Assets.spaceLinkIcon, + fit: BoxFit.contain, + ), + ), + InkWell( + onTap: () { + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const LinkingAttentionDialog(); + }, + ); + }, + child: SvgPicture.asset( + Assets.deleteSpaceLinkIcon, + fit: BoxFit.contain, + ), + ), + ], + ) + ], ), if (!showOnlyName) ...[ const SizedBox(height: 10), diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index c4877c98..788b9455 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -281,6 +281,7 @@ class CommunitySpaceManagementApi { .replaceAll('{communityId}', communityId) .replaceAll('{projectId}', TempConst.projectId), expectedResponseModel: (json) { + print('json space=$json'); final spaceModels = (json['data'] as List) .map((spaceJson) => SpaceModel.fromJson(spaceJson)) .toList(); diff --git a/lib/utils/color_manager.dart b/lib/utils/color_manager.dart index 4d3dbb0c..63456ff7 100644 --- a/lib/utils/color_manager.dart +++ b/lib/utils/color_manager.dart @@ -52,7 +52,7 @@ abstract class ColorsManager { static const Color semiTransparentBlackColor = Color(0x3F000000); static const Color transparentColor = Color(0x00000000); static const Color spaceColor = Color(0xB2023DFE); - static const Color counterBackgroundColor = Color(0xCCF4F4F4); + static const Color counterBackgroundColor = Color.fromARGB(204, 105, 2, 2); static const Color neutralGray = Color(0xFFE5E5E5); static const Color warningRed = Color(0xFFFF6465); static const Color borderColor = Color(0xFFE5E5E5); diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index d5d216c5..e1b7658b 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -401,5 +401,9 @@ class Assets { static const String link = 'assets/icons/link.svg'; static const String duplicate = 'assets/icons/duplicate.svg'; static const String spaceDelete = 'assets/icons/space_delete.svg'; + + static const String deleteSpaceLinkIcon = + 'assets/icons/delete_space_link_icon.svg'; + static const String spaceLinkIcon = 'assets/icons/space_link_icon.svg'; } -//user_management.svg +//space_link_icon.svg From 009ede7d081b5d4dea38d45753fc06113e9ec502 Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 18 Feb 2025 12:37:00 +0300 Subject: [PATCH 02/21] overwrite dialog and progress indicator and success dialog --- assets/icons/success_icon.svg | 3 + .../ac/view/ac_device_control.dart | 10 +- .../space_tree/view/space_tree_view.dart | 169 +++++++++--- .../models/space_template_model.dart | 2 +- .../dialog/confirm_overwrite_dialog.dart | 12 +- .../widgets/dialog/custom_loading_dialog.dart | 43 +-- .../link_space_model_spaces_dialog.dart | 255 ++++++++++-------- .../dialog/linking_attention_dialog.dart | 1 - .../widgets/dialog/linking_successful.dart | 33 +++ .../widgets/dialog/overwrite_dialog.dart | 8 +- lib/services/space_mana_api.dart | 1 - lib/utils/constants/assets.dart | 3 +- 12 files changed, 337 insertions(+), 203 deletions(-) create mode 100644 assets/icons/success_icon.svg create mode 100644 lib/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart diff --git a/assets/icons/success_icon.svg b/assets/icons/success_icon.svg new file mode 100644 index 00000000..6f5dbf9e --- /dev/null +++ b/assets/icons/success_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/pages/device_managment/ac/view/ac_device_control.dart b/lib/pages/device_managment/ac/view/ac_device_control.dart index 5197d722..071344d7 100644 --- a/lib/pages/device_managment/ac/view/ac_device_control.dart +++ b/lib/pages/device_managment/ac/view/ac_device_control.dart @@ -24,7 +24,8 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout { final isLarge = isLargeScreenSize(context); final isMedium = isMediumScreenSize(context); return BlocProvider( - create: (context) => AcBloc(deviceId: device.uuid!)..add(AcFetchDeviceStatusEvent(device.uuid!)), + create: (context) => AcBloc(deviceId: device.uuid!) + ..add(AcFetchDeviceStatusEvent(device.uuid!)), child: BlocBuilder( builder: (context, state) { if (state is ACStatusLoaded) { @@ -98,7 +99,8 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout { ), Text( 'h', - style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor), + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blackColor), ), Text( '30', @@ -107,7 +109,9 @@ class AcDeviceControlsView extends StatelessWidget with HelperResponsiveLayout { fontWeight: FontWeight.bold, ), ), - Text('m', style: context.textTheme.bodySmall!.copyWith(color: ColorsManager.blackColor)), + Text('m', + style: context.textTheme.bodySmall! + .copyWith(color: ColorsManager.blackColor)), IconButton( padding: const EdgeInsets.all(0), onPressed: () {}, diff --git a/lib/pages/space_tree/view/space_tree_view.dart b/lib/pages/space_tree/view/space_tree_view.dart index de9d088e..3954f200 100644 --- a/lib/pages/space_tree/view/space_tree_view.dart +++ b/lib/pages/space_tree/view/space_tree_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/widgets/search_bar.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; @@ -8,11 +9,14 @@ import 'package:syncrow_web/pages/space_tree/view/custom_expansion.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; import 'package:syncrow_web/utils/style.dart'; class SpaceTreeView extends StatefulWidget { + final bool? isSide; final Function onSelect; - const SpaceTreeView({required this.onSelect, super.key}); + const SpaceTreeView({required this.onSelect, this.isSide, super.key}); @override State createState() => _SpaceTreeViewState(); @@ -29,20 +33,77 @@ class _SpaceTreeViewState extends State { @override Widget build(BuildContext context) { - return BlocBuilder(builder: (context, state) { - List list = state.isSearching ? state.filteredCommunity : state.communityList; + return BlocBuilder( + builder: (context, state) { + List list = + state.isSearching ? state.filteredCommunity : state.communityList; return Container( height: MediaQuery.sizeOf(context).height, - decoration: subSectionContainerDecoration, + decoration: + widget.isSide == true ? subSectionContainerDecoration : null, child: state is SpaceTreeLoadingState ? const Center(child: CircularProgressIndicator()) : Column( children: [ - CustomSearchBar( - onSearchChanged: (query) { - context.read().add(SearchQueryEvent(query)); - }, - ), + widget.isSide == true + ? Container( + decoration: const BoxDecoration( + color: ColorsManager.circleRolesBackground, + borderRadius: BorderRadius.only( + topRight: Radius.circular(20), + topLeft: Radius.circular(20)), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Expanded( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(20)), + border: Border.all( + color: ColorsManager.grayBorder)), + child: TextFormField( + style: + const TextStyle(color: Colors.black), + onChanged: (value) { + context + .read() + .add(SearchQueryEvent(value)); + }, + decoration: textBoxDecoration(radios: 20)! + .copyWith( + fillColor: Colors.white, + suffixIcon: Padding( + padding: + const EdgeInsets.only(right: 16), + child: SvgPicture.asset( + Assets.textFieldSearch, + width: 24, + height: 24, + ), + ), + hintStyle: context.textTheme.bodyMedium + ?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 12, + color: ColorsManager.textGray), + ), + ), + ), + ), + ], + ), + ), + ) + : CustomSearchBar( + onSearchChanged: (query) { + context + .read() + .add(SearchQueryEvent(query)); + }, + ), const SizedBox(height: 16), Expanded( child: ListView( @@ -56,14 +117,18 @@ class _SpaceTreeViewState extends State { ? Center( child: Text( 'No results found', - style: Theme.of(context).textTheme.bodySmall!.copyWith( + style: Theme.of(context) + .textTheme + .bodySmall! + .copyWith( color: ColorsManager.lightGrayColor, fontWeight: FontWeight.w400, ), ), ) : Scrollbar( - scrollbarOrientation: ScrollbarOrientation.left, + scrollbarOrientation: + ScrollbarOrientation.left, thumbVisibility: true, controller: _scrollController, child: Padding( @@ -73,47 +138,68 @@ class _SpaceTreeViewState extends State { shrinkWrap: true, children: list .map( - (community) => CustomExpansionTileSpaceTree( + (community) => + CustomExpansionTileSpaceTree( title: community.name, - isSelected: state.selectedCommunities + isSelected: state + .selectedCommunities .contains(community.uuid), - isSoldCheck: state.selectedCommunities + isSoldCheck: state + .selectedCommunities .contains(community.uuid), onExpansionChanged: () { context .read() - .add(OnCommunityExpanded(community.uuid)); + .add(OnCommunityExpanded( + community.uuid)); }, - isExpanded: state.expandedCommunities + isExpanded: state + .expandedCommunities .contains(community.uuid), onItemSelected: () { - context.read().add( - OnCommunitySelected( - community.uuid, community.spaces)); + context + .read() + .add(OnCommunitySelected( + community.uuid, + community.spaces)); widget.onSelect(); }, - children: community.spaces.map((space) { + children: + community.spaces.map((space) { return CustomExpansionTileSpaceTree( title: space.name, - isExpanded: - state.expandedSpaces.contains(space.uuid), + isExpanded: state + .expandedSpaces + .contains(space.uuid), onItemSelected: () { - context.read().add( - OnSpaceSelected(community.uuid, - space.uuid ?? '', space.children)); + context + .read() + .add(OnSpaceSelected( + community.uuid, + space.uuid ?? '', + space.children)); widget.onSelect(); }, onExpansionChanged: () { - context.read().add( - OnSpaceExpanded( - community.uuid, space.uuid ?? '')); + context + .read() + .add(OnSpaceExpanded( + community.uuid, + space.uuid ?? '')); }, - isSelected: - state.selectedSpaces.contains(space.uuid) || - state.soldCheck.contains(space.uuid), - isSoldCheck: state.soldCheck.contains(space.uuid), + isSelected: state + .selectedSpaces + .contains( + space.uuid) || + state.soldCheck + .contains(space.uuid), + isSoldCheck: state.soldCheck + .contains(space.uuid), children: _buildNestedSpaces( - context, state, space, community.uuid), + context, + state, + space, + community.uuid), ); }).toList(), ), @@ -195,23 +281,24 @@ class _SpaceTreeViewState extends State { }); } - List _buildNestedSpaces( - BuildContext context, SpaceTreeState state, SpaceModel space, String communityId) { + List _buildNestedSpaces(BuildContext context, SpaceTreeState state, + SpaceModel space, String communityId) { return space.children.map((child) { return CustomExpansionTileSpaceTree( - isSelected: - state.selectedSpaces.contains(child.uuid) || state.soldCheck.contains(child.uuid), + isSelected: state.selectedSpaces.contains(child.uuid) || + state.soldCheck.contains(child.uuid), isSoldCheck: state.soldCheck.contains(child.uuid), title: child.name, isExpanded: state.expandedSpaces.contains(child.uuid), onItemSelected: () { - context - .read() - .add(OnSpaceSelected(communityId, child.uuid ?? '', child.children)); + context.read().add( + OnSpaceSelected(communityId, child.uuid ?? '', child.children)); widget.onSelect(); }, onExpansionChanged: () { - context.read().add(OnSpaceExpanded(communityId, child.uuid ?? '')); + context + .read() + .add(OnSpaceExpanded(communityId, child.uuid ?? '')); }, children: _buildNestedSpaces(context, state, child, communityId), ); diff --git a/lib/pages/spaces_management/space_model/models/space_template_model.dart b/lib/pages/spaces_management/space_model/models/space_template_model.dart index aca986bf..3323fe6e 100644 --- a/lib/pages/spaces_management/space_model/models/space_template_model.dart +++ b/lib/pages/spaces_management/space_model/models/space_template_model.dart @@ -11,7 +11,7 @@ class SpaceTemplateModel extends Equatable { List? subspaceModels; final List? tags; String internalId; - DateTime? createdAt; + String? createdAt; @override List get props => [modelName, subspaceModels, tags]; diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart index 7e7c5641..ba1d261d 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class ConfirmOverwriteDialog extends StatelessWidget { @@ -56,7 +57,16 @@ class ConfirmOverwriteDialog extends StatelessWidget { const SizedBox(width: 10), Expanded( child: ElevatedButton( - onPressed: () {}, + onPressed: () { + Navigator.pop(context); + + showDialog( + context: context, + builder: (BuildContext dialogContext) { + return const LinkingSuccessful(); + }, + ); + }, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), backgroundColor: ColorsManager.secondaryColor, diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart index 2d50d93b..e0260887 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart @@ -1,42 +1,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart'; -void showCustomLoadingDialog(BuildContext context) { - showDialog( - context: context, - barrierDismissible: false, // Prevent closing by tapping outside - builder: (BuildContext context) { - Future.delayed(Duration(seconds: 3), () { - Navigator.of(context).pop(); // Auto-close after 3 seconds - }); - - return Dialog( - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), - elevation: 10, - backgroundColor: Colors.white, - child: Padding( - padding: EdgeInsets.symmetric(vertical: 30, horizontal: 50), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Custom loader - CustomLoadingIndicator(), - SizedBox(height: 20), - Text( - "Linking in progress", - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.black87, - ), - ), - ], - ), - ), - ); - }, - ); -} class CustomLoadingIndicator extends StatefulWidget { @override @@ -52,8 +17,8 @@ class _CustomLoadingIndicatorState extends State super.initState(); _controller = AnimationController( vsync: this, - duration: Duration(seconds: 1), // Rotation speed - )..repeat(); // Infinite animation + duration: const Duration(seconds: 1), + )..repeat(); } @override @@ -71,7 +36,7 @@ class _CustomLoadingIndicatorState extends State animation: _controller, builder: (context, child) { return Transform.rotate( - angle: _controller.value * 2 * pi, // Full rotation + angle: _controller.value * 2 * pi, child: CustomPaint( painter: LoadingPainter(), ), 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 ff38152e..abcbedd8 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,11 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:syncrow_web/pages/roles_and_permission/users_page/add_user_dialog/view/build_tree_view.dart'; +import 'package:syncrow_web/pages/space_tree/view/space_tree_view.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/utils/color_manager.dart'; -import 'package:syncrow_web/utils/constants/assets.dart'; -import 'package:syncrow_web/utils/extension/build_context_x.dart'; -import 'package:syncrow_web/utils/style.dart'; class LinkSpaceModelSpacesDialog extends StatelessWidget { final SpaceTemplateModel spaceModel; @@ -16,114 +14,69 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { @override Widget build(BuildContext context) { return AlertDialog( + contentPadding: EdgeInsets.zero, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), backgroundColor: Colors.white, - contentPadding: const EdgeInsets.all(20), content: SizedBox( width: MediaQuery.of(context).size.width * 0.4, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Title - 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), - - // Details Section - _buildDetailRow("Space model name:", spaceModel.modelName), - _buildDetailRow("Creation date and time:", spaceModel.modelName), - _buildDetailRow("Created by:", "Admin"), - const SizedBox(height: 12), - - // Link to Section - 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), - - // Spaces List Expanded( - child: SizedBox( + child: Padding( + padding: const EdgeInsets.all(15.0), child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( - flex: 2, - child: Container( - decoration: const BoxDecoration( - color: ColorsManager.circleRolesBackground, - borderRadius: BorderRadius.only( - topRight: Radius.circular(20), - topLeft: Radius.circular(20)), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - children: [ - Expanded( - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.all( - Radius.circular(20)), - border: Border.all( - color: ColorsManager.grayBorder)), - child: TextFormField( - style: const TextStyle(color: Colors.black), - // controller: _blocRole.firstNameController, - onChanged: (value) { - // _blocRole.add(SearchAnode( - // nodes: _blocRole.updatedCommunities, - // searchTerm: value)); - }, - decoration: - textBoxDecoration(radios: 20)!.copyWith( - fillColor: Colors.white, - suffixIcon: Padding( - padding: - const EdgeInsets.only(right: 16), - child: SvgPicture.asset( - Assets.textFieldSearch, - width: 24, - height: 24, - ), - ), - hintStyle: context.textTheme.bodyMedium - ?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 12, - color: ColorsManager.textGray), - ), - ), - ), - ), - ], - ), + 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:", + spaceModel.modelName), + _buildDetailRow("Creation date and time:", + 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( - flex: 7, - child: Container( - color: ColorsManager.circleRolesBackground, - padding: const EdgeInsets.all(8.0), - child: Container( - color: ColorsManager.whiteColors, - child: TreeView(userId: '')))) + child: SizedBox( + child: Column( + children: [ + Expanded( + flex: 7, + child: Container( + color: ColorsManager.whiteColors, + child: SpaceTreeView( + isSide: true, onSelect: () { + + })) + ) + ], + ), + ), + ), + const SizedBox( + height: 20, + ), ], ), ), @@ -131,14 +84,98 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { // Buttons Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - _buildButton("Cancel", Colors.grey, () { - Navigator.of(context).pop(); - }), - _buildButton("Confirm", Colors.blueAccent, () { - Navigator.of(context).pop(); - }), + 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, () { + 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(); + }, + ); + }); + }), + ), + ), ], ), ], @@ -176,14 +213,10 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { Widget _buildButton(String text, Color color, VoidCallback onPressed) { return TextButton( onPressed: onPressed, - style: TextButton.styleFrom( - padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), - backgroundColor: color.withOpacity(0.2), - shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), - ), child: Text( text, - style: TextStyle(color: color, fontWeight: FontWeight.bold), + style: + TextStyle(color: color, fontWeight: FontWeight.w400, fontSize: 14), ), ); } diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart index a0807dba..15d92029 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/linking_attention_dialog.dart @@ -67,7 +67,6 @@ class LinkingAttentionDialog extends StatelessWidget { .textTheme .bodyMedium ?.copyWith(fontWeight: FontWeight.w400, fontSize: 16), - ), ), ), diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart b/lib/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart new file mode 100644 index 00000000..6a228fc1 --- /dev/null +++ b/lib/pages/spaces_management/space_model/widgets/dialog/linking_successful.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/constants/assets.dart'; + +class LinkingSuccessful extends StatelessWidget { + const LinkingSuccessful({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + backgroundColor: ColorsManager.whiteColors, + title: Center( + child: SvgPicture.asset( + Assets.successIcon, + )), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + 'Linking successful', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(fontWeight: FontWeight.w400, fontSize: 18), + ), + const SizedBox(height: 25), + ], + ), + ); + } +} 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 3d207968..8659b7b5 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 @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; void showOverwriteDialog(BuildContext context) { showDialog( context: context, - barrierDismissible: false, // Prevent closing by tapping outside + barrierDismissible: false, builder: (BuildContext context) { return Container( child: Dialog( @@ -19,7 +19,7 @@ void showOverwriteDialog(BuildContext context) { mainAxisSize: MainAxisSize.min, children: [ // Title - Text( + const Text( "Overwrite", style: TextStyle( fontSize: 22, @@ -30,7 +30,7 @@ void showOverwriteDialog(BuildContext context) { SizedBox(height: 15), // Description - Text( + const Text( "Are you sure you want to overwrite?", style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500), textAlign: TextAlign.center, @@ -70,7 +70,7 @@ void showOverwriteDialog(BuildContext context) { fontWeight: FontWeight.w500, ), ), - ), + ), ), SizedBox(width: 10), diff --git a/lib/services/space_mana_api.dart b/lib/services/space_mana_api.dart index 788b9455..c4877c98 100644 --- a/lib/services/space_mana_api.dart +++ b/lib/services/space_mana_api.dart @@ -281,7 +281,6 @@ class CommunitySpaceManagementApi { .replaceAll('{communityId}', communityId) .replaceAll('{projectId}', TempConst.projectId), expectedResponseModel: (json) { - print('json space=$json'); final spaceModels = (json['data'] as List) .map((spaceJson) => SpaceModel.fromJson(spaceJson)) .toList(); diff --git a/lib/utils/constants/assets.dart b/lib/utils/constants/assets.dart index e1b7658b..79efdaaf 100644 --- a/lib/utils/constants/assets.dart +++ b/lib/utils/constants/assets.dart @@ -405,5 +405,6 @@ class Assets { static const String deleteSpaceLinkIcon = 'assets/icons/delete_space_link_icon.svg'; static const String spaceLinkIcon = 'assets/icons/space_link_icon.svg'; + static const String successIcon = 'assets/icons/success_icon.svg'; + } -//space_link_icon.svg From 1054970a6307d33cb5d5eca0d4f21555534084be Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 18 Feb 2025 16:34:29 +0300 Subject: [PATCH 03/21] validation LinkSpaceModelSpacesDialog --- .../bloc/link_space_model_bloc.dart | 60 ++++++++++++++++++- .../bloc/link_space_model_event.dart | 6 ++ .../dialog/confirm_overwrite_dialog.dart | 10 ++++ .../link_space_model_spaces_dialog.dart | 38 ++++++++---- .../widgets/space_model_card_widget.dart | 11 +++- 5 files changed, 108 insertions(+), 17 deletions(-) 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 aa9a446d..4018ae60 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,12 +1,68 @@ +import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.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/utils/navigation_service.dart'; class SpaceModelBloc extends Bloc { SpaceModelBloc() : super(SpaceModelInitial()) { + on(_getSpaceIds); on((event, emit) { emit(SpaceModelSelectedState(event.selectedIndex)); }); } -} \ No newline at end of file + + // 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'); + // } + // } + + bool hasSelectedSpaces = false; + + 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'); + } + } + // 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 { + print("No spaces selected."); + } + } catch (e) { + print("Error in _getSpaceIds: $e"); + } + } +} 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 8bff0202..87b1e368 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 @@ -5,3 +5,9 @@ class SpaceModelSelectedEvent extends SpaceModelEvent { SpaceModelSelectedEvent(this.selectedIndex); } + + + +class SpaceModelSelectedIdsEvent extends SpaceModelEvent { + +} diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart index ba1d261d..0497b570 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/confirm_overwrite_dialog.dart @@ -28,6 +28,16 @@ class ConfirmOverwriteDialog extends StatelessWidget { .bodyMedium ?.copyWith(fontWeight: FontWeight.w400, fontSize: 18), ), + Center( + child: Text( + 'Selected spaces already have linked space \nmodel / sub-spaces and devices', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + fontWeight: FontWeight.w400, + fontSize: 14, + color: ColorsManager.grayColor), + textAlign: TextAlign.center, + ), + ), const SizedBox(height: 25), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, 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 abcbedd8..d6482a05 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,5 +1,8 @@ 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/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart'; @@ -42,8 +45,7 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { ), const Divider(), const SizedBox(height: 16), - _buildDetailRow("Space model name:", - spaceModel.modelName), + _buildDetailRow("Space model name:", spaceModel.modelName), _buildDetailRow("Creation date and time:", spaceModel.createdAt.toString()), _buildDetailRow("Created by:", "Admin"), @@ -66,10 +68,11 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { child: Container( color: ColorsManager.whiteColors, child: SpaceTreeView( - isSide: true, onSelect: () { - - })) - ) + isSide: true, + onSelect: () { + context.read().add( + SpaceModelSelectedIdsEvent()); + }))) ], ), ), @@ -122,6 +125,15 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { ), 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, @@ -211,12 +223,14 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { // Button Widget Widget _buildButton(String text, Color color, VoidCallback onPressed) { - return TextButton( - onPressed: onPressed, - child: Text( - text, - style: - TextStyle(color: color, fontWeight: FontWeight.w400, fontSize: 14), + 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/space_model_card_widget.dart b/lib/pages/spaces_management/space_model/widgets/space_model_card_widget.dart index 52b0fa6c..6ae786be 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,5 +1,7 @@ import 'package:flutter/material.dart'; +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/space_model/models/space_template_model.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'; @@ -73,9 +75,12 @@ class SpaceModelCardWidget extends StatelessWidget { onTap: () { showDialog( context: context, - builder: (BuildContext dialogContext) { - return LinkSpaceModelSpacesDialog( - spaceModel: model, + builder: (context) { + return BlocProvider( + create: (_) => SpaceModelBloc(), + child: LinkSpaceModelSpacesDialog( + spaceModel: model, + ), ); }, ); From b1348235514492fc2cd99c3c81879469ec12b8ff Mon Sep 17 00:00:00 2001 From: mohammad Date: Tue, 18 Feb 2025 16:56:10 +0300 Subject: [PATCH 04/21] SpaceModelSelectedIds --- .../link_space_model_spaces_dialog.dart | 20 ++++++++++++++++--- .../widgets/space_model_card_widget.dart | 2 ++ 2 files changed, 19 insertions(+), 3 deletions(-) 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 d6482a05..668fee60 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 @@ -8,12 +8,25 @@ import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/c import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/custom_loading_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; -class LinkSpaceModelSpacesDialog extends StatelessWidget { +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( @@ -45,9 +58,10 @@ class LinkSpaceModelSpacesDialog extends StatelessWidget { ), const Divider(), const SizedBox(height: 16), - _buildDetailRow("Space model name:", spaceModel.modelName), + _buildDetailRow( + "Space model name:", widget.spaceModel.modelName), _buildDetailRow("Creation date and time:", - spaceModel.createdAt.toString()), + widget.spaceModel.createdAt.toString()), _buildDetailRow("Created by:", "Admin"), const SizedBox(height: 12), const Text( 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 6ae786be..c7d9c72f 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 @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; 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/space_model/models/space_template_model.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'; @@ -73,6 +74,7 @@ class SpaceModelCardWidget extends StatelessWidget { children: [ InkWell( onTap: () { + showDialog( context: context, builder: (context) { From d88ae9ea15e2f2ad48b655199a7ae3b4177af838 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 14:19:58 +0400 Subject: [PATCH 05/21] removed tag model and use tag only --- .../view/access_management.dart | 1 - lib/pages/auth/bloc/auth_bloc.dart | 1 - .../device_managment_bloc.dart | 3 - .../bloc/assign_tag_model_bloc.dart | 22 +++---- .../bloc/assign_tag_model_event.dart | 8 +-- .../bloc/assign_tag_model_state.dart | 4 +- .../views/assign_tag_models_dialog.dart | 16 ++--- .../spaces_management/helper/tag_helper.dart | 21 +++--- .../bloc/create_space_model_bloc.dart | 29 ++++----- .../bloc/create_space_model_event.dart | 4 +- .../models/space_template_model.dart | 8 +-- .../models/subspace_template_model.dart | 8 +-- .../space_model/models/tag_model.dart | 65 ------------------- .../widgets/subspace_model_create_widget.dart | 10 +-- .../bloc/add_device_model_state.dart | 4 +- .../bloc/add_device_type_model_event.dart | 4 +- .../views/add_device_type_model_widget.dart | 4 +- 17 files changed, 68 insertions(+), 144 deletions(-) delete mode 100644 lib/pages/spaces_management/space_model/models/tag_model.dart diff --git a/lib/pages/access_management/view/access_management.dart b/lib/pages/access_management/view/access_management.dart index c60b4bb2..d269c824 100644 --- a/lib/pages/access_management/view/access_management.dart +++ b/lib/pages/access_management/view/access_management.dart @@ -3,7 +3,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_bloc.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_event.dart'; import 'package:syncrow_web/pages/access_management/bloc/access_state.dart'; -import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/common/buttons/search_reset_buttons.dart'; import 'package:syncrow_web/pages/common/custom_table.dart'; diff --git a/lib/pages/auth/bloc/auth_bloc.dart b/lib/pages/auth/bloc/auth_bloc.dart index b22dae7b..9e0ac2f9 100644 --- a/lib/pages/auth/bloc/auth_bloc.dart +++ b/lib/pages/auth/bloc/auth_bloc.dart @@ -15,7 +15,6 @@ import 'package:syncrow_web/pages/space_tree/bloc/space_tree_event.dart'; import 'package:syncrow_web/services/auth_api.dart'; import 'package:syncrow_web/utils/constants/strings_manager.dart'; import 'package:syncrow_web/utils/helpers/shared_preferences_helper.dart'; -import 'package:syncrow_web/utils/navigation_service.dart'; import 'package:syncrow_web/utils/snack_bar.dart'; class AuthBloc extends Bloc { diff --git a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart index e52debb0..7ed3a377 100644 --- a/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart +++ b/lib/pages/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart @@ -5,9 +5,6 @@ import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/models/devices_model.dart'; import 'package:syncrow_web/pages/space_tree/bloc/space_tree_bloc.dart'; import 'package:syncrow_web/services/devices_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'; part 'device_managment_event.dart'; part 'device_managment_state.dart'; diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart index d0e37f6a..37747ecd 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart @@ -1,7 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; class AssignTagModelBloc extends Bloc { @@ -19,7 +19,7 @@ class AssignTagModelBloc } } - final tags = []; + final tags = []; for (var selectedProduct in event.addedProducts) { final existingCount = existingTagCounts[selectedProduct.productId] ?? 0; @@ -39,7 +39,7 @@ class AssignTagModelBloc if (missingCount > 0) { tags.addAll(List.generate( missingCount, - (index) => TagModel( + (index) => Tag( tag: '', product: selectedProduct.product, location: 'Main Space', @@ -61,7 +61,7 @@ class AssignTagModelBloc final currentState = state; if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags); + final tags = List.from(currentState.tags); tags[event.index] = tags[event.index].copyWith(tag: event.tag); final updatedTags = _calculateAvailableTags(allTags, tags); @@ -79,7 +79,7 @@ class AssignTagModelBloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags); + final tags = List.from(currentState.tags); // Use copyWith for immutability tags[event.index] = @@ -101,7 +101,7 @@ class AssignTagModelBloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags); + final tags = List.from(currentState.tags); emit(AssignTagModelLoaded( tags: tags, @@ -117,7 +117,7 @@ class AssignTagModelBloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags) + final tags = List.from(currentState.tags) ..remove(event.tagToDelete); final updatedTags = _calculateAvailableTags(allTags, tags); @@ -128,18 +128,18 @@ class AssignTagModelBloc isSaveEnabled: _validateTags(tags), errorMessage: _getValidationError(tags), )); - } + } }); } - bool _validateTags(List tags) { + bool _validateTags(List tags) { final uniqueTags = tags.map((tag) => tag.tag?.trim() ?? '').toSet(); final hasEmptyTag = tags.any((tag) => (tag.tag?.trim() ?? '').isEmpty); final isValid = uniqueTags.length == tags.length && !hasEmptyTag; return isValid; } - String? _getValidationError(List tags) { + String? _getValidationError(List tags) { // Check for duplicate tags final nonEmptyTags = tags @@ -165,7 +165,7 @@ class AssignTagModelBloc } List _calculateAvailableTags( - List allTags, List tags) { + List allTags, List tags) { final selectedTags = tags .where((tag) => (tag.tag?.trim().isNotEmpty ?? false)) .map((tag) => tag.tag!.trim()) diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart index 38642d96..23c70ab0 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; abstract class AssignTagModelEvent extends Equatable { @@ -10,7 +10,7 @@ abstract class AssignTagModelEvent extends Equatable { } class InitializeTagModels extends AssignTagModelEvent { - final List initialTags; + final List initialTags; final List addedProducts; const InitializeTagModels({ @@ -45,8 +45,8 @@ class UpdateLocation extends AssignTagModelEvent { class ValidateTagModels extends AssignTagModelEvent {} class DeleteTagModel extends AssignTagModelEvent { - final TagModel tagToDelete; - final List tags; + final Tag tagToDelete; + final List tags; const DeleteTagModel({required this.tagToDelete, required this.tags}); diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart index 167a6ac2..55604d3f 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; abstract class AssignTagModelState extends Equatable { const AssignTagModelState(); @@ -13,7 +13,7 @@ class AssignTagModelInitial extends AssignTagModelState {} class AssignTagModelLoading extends AssignTagModelState {} class AssignTagModelLoaded extends AssignTagModelState { - final List tags; + final List tags; final bool isSaveEnabled; final String? errorMessage; diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index d13766d4..9c4bb3b4 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -6,12 +6,12 @@ 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/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assign_tag_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/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; @@ -23,8 +23,8 @@ class AssignTagModelsDialog extends StatelessWidget { final List? subspaces; final SpaceTemplateModel? spaceModel; - final List initialTags; - final ValueChanged>? onTagsAssigned; + final List initialTags; + final ValueChanged>? onTagsAssigned; final List addedProducts; final List? allTags; final String spaceName; @@ -195,7 +195,7 @@ class AssignTagModelsDialog extends StatelessWidget { .infinity, // Ensure full width for dropdown child: DialogTextfieldDropdown( key: ValueKey( - 'dropdown_${Uuid().v4()}_${index}'), + 'dropdown_${Uuid().v4()}_$index'), items: state.updatedTags, initialValue: tag.tag, onSelected: (value) { @@ -255,13 +255,13 @@ class AssignTagModelsDialog extends StatelessWidget { label: 'Add New Device', onPressed: () async { final updatedTags = - List.from(state.tags); + List.from(state.tags); final result = TagHelper.updateSubspaceTagModels( updatedTags, subspaces); final processedTags = - result['updatedTags'] as List; + result['updatedTags'] as List; final processedSubspaces = List.from( result['subspaces'] as List); @@ -311,14 +311,14 @@ class AssignTagModelsDialog extends StatelessWidget { onPressed: state.isSaveEnabled ? () async { final updatedTags = - List.from(state.tags); + List.from(state.tags); final result = TagHelper.updateSubspaceTagModels( updatedTags, subspaces); final processedTags = - result['updatedTags'] as List; + result['updatedTags'] as List; final processedSubspaces = List.from( result['subspaces'] diff --git a/lib/pages/spaces_management/helper/tag_helper.dart b/lib/pages/spaces_management/helper/tag_helper.dart index 3e3353a3..e1dc3cc3 100644 --- a/lib/pages/spaces_management/helper/tag_helper.dart +++ b/lib/pages/spaces_management/helper/tag_helper.dart @@ -7,7 +7,6 @@ import 'package:syncrow_web/pages/spaces_management/all_spaces/model/subspace_mo import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; class TagHelper { static Map updateTags({ @@ -131,9 +130,9 @@ class TagHelper { } static List getAvailableTagModels( - List allTags, List currentTags, TagModel currentTag) { + List allTags, List currentTags, Tag currentTag) { List availableTagsForTagModel = - TagHelper.getAvailableTags( + TagHelper.getAvailableTags( allTags: allTags, currentTags: currentTags, currentTag: currentTag, @@ -142,11 +141,11 @@ class TagHelper { return availableTagsForTagModel; } - static List generateInitialTags({ - List? spaceTagModels, + static List generateInitialTags({ + List? spaceTagModels, List? subspaces, }) { - final List initialTags = []; + final List initialTags = []; if (spaceTagModels != null) { initialTags.addAll(spaceTagModels); @@ -212,7 +211,7 @@ class TagHelper { } static List createInitialSelectedProducts( - List? tags, List? subspaces) { + List? tags, List? subspaces) { final Map productCounts = {}; if (tags != null) { @@ -282,7 +281,7 @@ class TagHelper { } static int? checkTagExistInSubspaceModels( - TagModel tag, List? subspaces) { + Tag tag, List? subspaces) { if (subspaces == null) return null; for (int i = 0; i < subspaces.length; i++) { @@ -298,8 +297,8 @@ class TagHelper { } static Map updateSubspaceTagModels( - List updatedTags, List? subspaces) { - final result = TagHelper.updateTags( + List updatedTags, List? subspaces) { + final result = TagHelper.updateTags( updatedTags: updatedTags, subspaces: subspaces, getInternalId: (tag) => tag.internalId, @@ -311,7 +310,7 @@ class TagHelper { checkTagExistInSubspace: checkTagExistInSubspaceModels, ); - final processedTags = result['updatedTags'] as List; + final processedTags = result['updatedTags'] as List; final processedSubspaces = List.from(result['subspaces'] as List); diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart index 8237c172..d6801b95 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart @@ -1,11 +1,11 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/common/bloc/project_manager.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart'; @@ -94,14 +94,9 @@ class CreateSpaceModelBloc orElse: () => subspace, ); - // Update the subspace's tags - final eventTagIds = matchingEventSubspace.tags - ?.map((e) => e.internalId) - .toSet() ?? - {}; final updatedTags = [ - ...?subspace.tags?.map((tag) { + ...?subspace.tags?.map((tag) { final matchingTag = matchingEventSubspace.tags?.firstWhere( (e) => e.internalId == tag.internalId, @@ -112,14 +107,14 @@ class CreateSpaceModelBloc ? tag.copyWith(tag: matchingTag?.tag) : tag; }) ?? - [], + [], ...?matchingEventSubspace.tags?.where( (e) => subspace.tags ?.every((t) => t.internalId != e.internalId) ?? true, ) ?? - [], + [], ]; return subspace.copyWith( subspaceName: matchingEventSubspace.subspaceName, @@ -244,7 +239,7 @@ class CreateSpaceModelBloc } if (newSubspaces != null) { - for (var newSubspace in newSubspaces!) { + for (var newSubspace in newSubspaces) { // Tag without UUID if ((newSubspace.uuid == null || newSubspace.uuid!.isEmpty)) { final List tagUpdates = []; @@ -268,7 +263,7 @@ class CreateSpaceModelBloc if (prevSubspaces != null && newSubspaces != null) { final newSubspaceMap = { - for (var subspace in newSubspaces!) subspace.uuid: subspace + for (var subspace in newSubspaces) subspace.uuid: subspace }; for (var prevSubspace in prevSubspaces) { @@ -309,8 +304,8 @@ class CreateSpaceModelBloc } List processTagUpdates( - List? prevTags, - List? newTags, + List? prevTags, + List? newTags, ) { final List tagUpdates = []; final processedTags = {}; @@ -332,7 +327,7 @@ class CreateSpaceModelBloc if (prevTags != null && newTags != null) { for (var prevTag in prevTags) { final existsInNew = - newTags!.any((newTag) => newTag.uuid == prevTag.uuid); + newTags.any((newTag) => newTag.uuid == prevTag.uuid); if (!existsInNew) { tagUpdates .add(TagModelUpdate(action: Action.delete, uuid: prevTag.uuid)); @@ -349,7 +344,7 @@ class CreateSpaceModelBloc if (newTags != null) { final prevTagUuids = prevTags?.map((t) => t.uuid).toSet() ?? {}; - for (var newTag in newTags!) { + for (var newTag in newTags) { // Tag without UUID if ((newTag.uuid == null || !prevTagUuids.contains(newTag.uuid)) && !processedTags.contains(newTag.tag)) { @@ -365,9 +360,9 @@ class CreateSpaceModelBloc // Case 3: Tags updated if (prevTags != null && newTags != null) { - final newTagMap = {for (var tag in newTags!) tag.uuid: tag}; + final newTagMap = {for (var tag in newTags) tag.uuid: tag}; - for (var prevTag in prevTags!) { + for (var prevTag in prevTags) { final newTag = newTagMap[prevTag.uuid]; if (newTag != null) { tagUpdates.add(TagModelUpdate( diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart index d0cd245c..0d2a3a4f 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_event.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; abstract class CreateSpaceModelEvent extends Equatable { const CreateSpaceModelEvent(); @@ -49,7 +49,7 @@ class AddSubspacesToSpaceTemplate extends CreateSpaceModelEvent { } class AddTagsToSpaceTemplate extends CreateSpaceModelEvent { - final List tags; + final List tags; AddTagsToSpaceTemplate(this.tags); } diff --git a/lib/pages/spaces_management/space_model/models/space_template_model.dart b/lib/pages/spaces_management/space_model/models/space_template_model.dart index 22378f1f..90df55ea 100644 --- a/lib/pages/spaces_management/space_model/models/space_template_model.dart +++ b/lib/pages/spaces_management/space_model/models/space_template_model.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_update_model.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart'; import 'package:uuid/uuid.dart'; @@ -9,7 +9,7 @@ class SpaceTemplateModel extends Equatable { String? uuid; String modelName; List? subspaceModels; - final List? tags; + final List? tags; String internalId; @override @@ -38,7 +38,7 @@ class SpaceTemplateModel extends Equatable { [], tags: (json['tags'] as List?) ?.where((item) => item is Map) // Validate type - .map((item) => TagModel.fromJson(item as Map)) + .map((item) => Tag.fromJson(item as Map)) .toList() ?? [], ); @@ -47,7 +47,7 @@ class SpaceTemplateModel extends Equatable { String? uuid, String? modelName, List? subspaceModels, - List? tags, + List? tags, String? internalId, }) { return SpaceTemplateModel( diff --git a/lib/pages/spaces_management/space_model/models/subspace_template_model.dart b/lib/pages/spaces_management/space_model/models/subspace_template_model.dart index 9c69b4c8..130a477e 100644 --- a/lib/pages/spaces_management/space_model/models/subspace_template_model.dart +++ b/lib/pages/spaces_management/space_model/models/subspace_template_model.dart @@ -1,11 +1,11 @@ -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:uuid/uuid.dart'; class SubspaceTemplateModel { final String? uuid; String subspaceName; final bool disabled; - List? tags; + List? tags; String internalId; SubspaceTemplateModel({ @@ -25,7 +25,7 @@ class SubspaceTemplateModel { internalId: internalId, disabled: json['disabled'] ?? false, tags: (json['tags'] as List?) - ?.map((item) => TagModel.fromJson(item)) + ?.map((item) => Tag.fromJson(item)) .toList() ?? [], ); @@ -44,7 +44,7 @@ class SubspaceTemplateModel { String? uuid, String? subspaceName, bool? disabled, - List? tags, + List? tags, String? internalId, }) { return SubspaceTemplateModel( diff --git a/lib/pages/spaces_management/space_model/models/tag_model.dart b/lib/pages/spaces_management/space_model/models/tag_model.dart deleted file mode 100644 index 20bd50e2..00000000 --- a/lib/pages/spaces_management/space_model/models/tag_model.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:syncrow_web/pages/spaces_management/all_spaces/model/base_tag.dart'; -import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; -import 'package:uuid/uuid.dart'; - -class TagModel extends BaseTag { - TagModel({ - String? uuid, - required String? tag, - ProductModel? product, - String? internalId, - String? location, - }) : super( - uuid: uuid, - tag: tag, - product: product, - internalId: internalId, - location: location, - ); - factory TagModel.fromJson(Map json) { - final String internalId = json['internalId'] ?? const Uuid().v4(); - - return TagModel( - uuid: json['uuid'] , - internalId: internalId, - tag: json['tag'] ?? '', - product: json['product'] != null - ? ProductModel.fromMap(json['product']) - : null, - ); - } - - @override - TagModel copyWith( - {String? tag, - ProductModel? product, - String? uuid, - String? location, - String? internalId}) { - return TagModel( - tag: tag ?? this.tag, - product: product ?? this.product, - location: location ?? this.location, - internalId: internalId ?? this.internalId, - uuid:uuid?? this.uuid - ); - } - - Map toJson() { - return { - 'uuid': uuid, - 'tag': tag, - 'product': product?.toMap(), - }; - } -} - -extension TagModelExtensions on TagModel { - TagBodyModel toTagBodyModel() { - return TagBodyModel() - ..uuid = uuid - ..tag = tag ?? '' - ..productUuid = product?.uuid; - } -} diff --git a/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart b/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart index d8e27bec..4ebd65df 100644 --- a/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/subspace_model_create_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/common/edit_chip.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/button_content_widget.dart'; import 'package:syncrow_web/pages/spaces_management/create_subspace_model/views/create_subspace_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/subspace_name_label_widget.dart'; @@ -10,9 +10,9 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SubspaceModelCreate extends StatefulWidget { final List subspaces; final void Function( - List newSubspaces, List? tags)? + List newSubspaces, List? tags)? onSpaceModelUpdate; - final List tags; + final List tags; const SubspaceModelCreate({ Key? key, @@ -28,7 +28,7 @@ class SubspaceModelCreate extends StatefulWidget { class _SubspaceModelCreateState extends State { late List _subspaces; String? errorSubspaceId; - late List _tags; + late List _tags; @override void initState() { @@ -117,7 +117,7 @@ class _SubspaceModelCreateState extends State { .where((s) => !updatedIds.contains(s.internalId)) .toList(); - final List tagsToAppendToSpace = []; + final List tagsToAppendToSpace = []; for (var s in deletedSubspaces) { if (s.tags != null) { diff --git a/lib/pages/spaces_management/tag_model/bloc/add_device_model_state.dart b/lib/pages/spaces_management/tag_model/bloc/add_device_model_state.dart index f45471cd..c4e27051 100644 --- a/lib/pages/spaces_management/tag_model/bloc/add_device_model_state.dart +++ b/lib/pages/spaces_management/tag_model/bloc/add_device_model_state.dart @@ -1,6 +1,6 @@ import 'package:equatable/equatable.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/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; abstract class AddDeviceModelState extends Equatable { const AddDeviceModelState(); @@ -15,7 +15,7 @@ class AddDeviceModelLoading extends AddDeviceModelState {} class AddDeviceModelLoaded extends AddDeviceModelState { final List selectedProducts; - final List initialTag; + final List initialTag; const AddDeviceModelLoaded({ required this.selectedProducts, diff --git a/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart b/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart index 9b3a8b1e..b9018b2b 100644 --- a/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart +++ b/lib/pages/spaces_management/tag_model/bloc/add_device_type_model_event.dart @@ -1,7 +1,7 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_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/tag_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; abstract class AddDeviceTypeModelEvent extends Equatable { const AddDeviceTypeModelEvent(); @@ -25,7 +25,7 @@ class UpdateProductCountEvent extends AddDeviceTypeModelEvent { class InitializeDeviceTypeModel extends AddDeviceTypeModelEvent { - final List initialTags; + final List initialTags; final List addedProducts; const InitializeDeviceTypeModel({ diff --git a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart index 9d0eac96..84cd08a0 100644 --- a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart +++ b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart @@ -2,13 +2,13 @@ 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/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/subspace_template_model.dart'; -import 'package:syncrow_web/pages/spaces_management/space_model/models/tag_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/tag_model/bloc/add_device_model_state.dart'; @@ -20,7 +20,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { final List? products; final List? initialSelectedProducts; final List? subspaces; - final List? spaceTagModels; + final List? spaceTagModels; final List? allTags; final String spaceName; final bool isCreate; From 215dd9cfa4576c535af8af5e7422af94e182b335 Mon Sep 17 00:00:00 2001 From: mohammad Date: Wed, 5 Mar 2025 14:37:52 +0300 Subject: [PATCH 06/21] link space model api integration --- .../bloc/link_space_model_bloc.dart | 84 ++- .../bloc/link_space_model_event.dart | 15 +- .../bloc/link_space_model_state.dart | 32 +- .../link_space_model_spaces_dialog.dart | 660 ++++++++++++------ .../widgets/dialog/overwrite_dialog.dart | 42 +- .../widgets/space_model_card_widget.dart | 94 ++- lib/services/space_model_mang_api.dart | 36 + lib/utils/constants/api_const.dart | 68 +- 8 files changed, 758 insertions(+), 273 deletions(-) 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}'; From 5233c1b38e7d49b066e7ab1dfcab48eddad3fae7 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 16:41:57 +0400 Subject: [PATCH 07/21] format settings --- .vscode/settings.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index b87628bd..6ec78043 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,9 @@ { + "html.format.extraLiners": "", + "editor.rulers": [100], + "html.format.contentUnformatted": "", + "dart.lineLength": 100, + "files.autoSave": "afterDelay", "cSpell.words": [ "automations" ] From a7995bb2b834c0859d0fc5b5d753f564c176374b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 16:59:58 +0400 Subject: [PATCH 08/21] added tag api --- lib/services/space_model_mang_api.dart | 22 +++++++++++++++++----- lib/utils/constants/api_const.dart | 2 ++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index 15764083..9163095d 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -1,8 +1,8 @@ +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/create_space_template_body_model.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; class SpaceModelManagementApi { Future> listSpaceModels( @@ -33,8 +33,8 @@ class SpaceModelManagementApi { return response; } - Future updateSpaceModel(CreateSpaceTemplateBodyModel spaceModel, - String spaceModelUuid, String projectId) async { + Future updateSpaceModel( + CreateSpaceTemplateBodyModel spaceModel, String spaceModelUuid, String projectId) async { final response = await HTTPService().put( path: ApiEndpoints.updateSpaceModel .replaceAll('{projectId}', projectId) @@ -47,8 +47,7 @@ class SpaceModelManagementApi { return response; } - Future getSpaceModel( - String spaceModelUuid, String projectId) async { + Future getSpaceModel(String spaceModelUuid, String projectId) async { final response = await HTTPService().get( path: ApiEndpoints.getSpaceModel .replaceAll('{projectId}', projectId) @@ -73,4 +72,17 @@ class SpaceModelManagementApi { ); return response; } + + Future> listTags({required String projectId}) async { + final response = await HTTPService().get( + path: ApiEndpoints.listTags.replaceAll('{projectId}', projectId), + expectedResponseModel: (json) { + List jsonData = json['data']; + return jsonData.map((jsonItem) { + return Tag.fromJson(jsonItem); + }).toList(); + }, + ); + return response; + } } diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index 1022d734..94f25d3f 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -89,6 +89,8 @@ abstract class ApiEndpoints { 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}'; + //tag + static const String listTags = '/projects/{projectId}/tags'; static const String roleTypes = '/role/types'; static const String permission = '/permission/{roleUuid}'; From c9427b35beb1c00c12c4660cdcac50c93a7a77ce Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 17:12:02 +0400 Subject: [PATCH 09/21] added tags --- lib/main.dart | 1 - .../bloc/space_management_bloc.dart | 194 +++++++++--------- .../bloc/space_management_state.dart | 22 +- .../view/spaces_management_page.dart | 4 +- 4 files changed, 108 insertions(+), 113 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 20b0311e..f2f640e4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:go_router/go_router.dart'; -import 'package:syncrow_web/firebase_options_dev.dart'; import 'package:syncrow_web/firebase_options_prod.dart'; import 'package:syncrow_web/pages/auth/bloc/auth_bloc.dart'; import 'package:syncrow_web/pages/home/bloc/home_bloc.dart'; 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 7b62d0ac..671e446c 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 @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:developer'; 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'; @@ -20,8 +19,7 @@ import 'package:syncrow_web/services/space_mana_api.dart'; import 'package:syncrow_web/services/space_model_mang_api.dart'; import 'package:syncrow_web/utils/constants/action_enum.dart' as custom_action; -class SpaceManagementBloc - extends Bloc { +class SpaceManagementBloc extends Bloc { final CommunitySpaceManagementApi _api; final ProductApi _productApi; final SpaceModelManagementApi _spaceModelApi; @@ -29,6 +27,7 @@ class SpaceManagementBloc List? _cachedProducts; List? _cachedSpaceModels; final SpaceTreeBloc _spaceTreeBloc; + List? _cachedTags; SpaceManagementBloc( this._api, @@ -55,44 +54,38 @@ class SpaceManagementBloc UpdateSpaceModelCache event, Emitter emit) async { if (_cachedSpaceModels != null) { _cachedSpaceModels = _cachedSpaceModels!.map((model) { - return model.uuid == event.updatedModel.uuid - ? event.updatedModel - : model; + return model.uuid == event.updatedModel.uuid ? event.updatedModel : model; }).toList(); } else { _cachedSpaceModels = await fetchSpaceModels(); } + await fetchSpaceModels(); + emit(SpaceModelLoaded( - communities: state is SpaceManagementLoaded - ? (state as SpaceManagementLoaded).communities - : [], - products: _cachedProducts ?? [], - spaceModels: List.from(_cachedSpaceModels ?? []), - )); + communities: + state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [], + products: _cachedProducts ?? [], + spaceModels: List.from(_cachedSpaceModels ?? []), + allTags: _cachedTags ?? [])); } - void _deleteSpaceModelFromCache(DeleteSpaceModelFromCache event, - Emitter emit) async { + void _deleteSpaceModelFromCache( + DeleteSpaceModelFromCache event, Emitter emit) async { if (_cachedSpaceModels != null) { - _cachedSpaceModels = _cachedSpaceModels! - .where((model) => model.uuid != event.deletedUuid) - .toList(); + _cachedSpaceModels = + _cachedSpaceModels!.where((model) => model.uuid != event.deletedUuid).toList(); } else { _cachedSpaceModels = await fetchSpaceModels(); } + await fetchSpaceModels(); emit(SpaceModelLoaded( - communities: state is SpaceManagementLoaded - ? (state as SpaceManagementLoaded).communities - : [], - products: _cachedProducts ?? [], - spaceModels: List.from(_cachedSpaceModels ?? []), - )); - } - - void _logEvent(String eventName) { - log('Event Triggered: $eventName'); + communities: + state is SpaceManagementLoaded ? (state as SpaceManagementLoaded).communities : [], + products: _cachedProducts ?? [], + spaceModels: List.from(_cachedSpaceModels ?? []), + allTags: _cachedTags ?? [])); } void updateCachedSpaceModels(List updatedModels) { @@ -117,8 +110,8 @@ class SpaceManagementBloc int page = 1; while (hasNext) { - final spaceModels = await _spaceModelApi.listSpaceModels( - page: page, projectId: projectUuid); + final spaceModels = + await _spaceModelApi.listSpaceModels(page: page, projectId: projectUuid); if (spaceModels.isNotEmpty) { allSpaceModels.addAll(spaceModels); page++; @@ -135,6 +128,20 @@ class SpaceManagementBloc } } + Future> fetchTags() async { + try { + if (_cachedTags != null) { + return _cachedTags!; + } + final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + final allTags = await _spaceModelApi.listTags(projectId: projectUuid); + _cachedTags = allTags; + return allTags; + } catch (e) { + return []; + } + } + void _onUpdateCommunity( UpdateCommunityEvent event, Emitter emit, @@ -142,14 +149,13 @@ class SpaceManagementBloc final previousState = state; try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; + await fetchTags(); emit(SpaceManagementLoading()); - final success = await _api.updateCommunity( - event.communityUuid, event.name, projectUuid); + final success = await _api.updateCommunity(event.communityUuid, event.name, projectUuid); if (success) { if (previousState is SpaceManagementLoaded) { - final updatedCommunities = - List.from(previousState.communities); + final updatedCommunities = List.from(previousState.communities); for (var community in updatedCommunities) { if (community.uuid == event.communityUuid) { community.name = event.name; @@ -162,11 +168,11 @@ class SpaceManagementBloc var prevSpaceModels = await fetchSpaceModels(); emit(SpaceManagementLoaded( - communities: updatedCommunities, - products: previousState.products, - selectedCommunity: previousState.selectedCommunity, - spaceModels: prevSpaceModels, - )); + communities: updatedCommunities, + products: previousState.products, + selectedCommunity: previousState.selectedCommunity, + spaceModels: prevSpaceModels, + allTags: _cachedTags ?? [])); } } else { emit(const SpaceManagementError('Failed to update the community.')); @@ -194,8 +200,7 @@ class SpaceManagementBloc } } - Future> _fetchSpacesForCommunity( - String communityUuid) async { + Future> _fetchSpacesForCommunity(String communityUuid) async { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; return await _api.getSpaceHierarchy(communityUuid, projectUuid); @@ -206,7 +211,7 @@ class SpaceManagementBloc Emitter emit, ) async { try { - final previousState = state; + await fetchTags(); if (event.communities.isEmpty) { emit(const SpaceManagementError('No communities provided.')); @@ -216,33 +221,33 @@ class SpaceManagementBloc var prevSpaceModels = await fetchSpaceModels(); emit(BlankState( - communities: event.communities, - products: _cachedProducts ?? [], - spaceModels: prevSpaceModels, - )); + communities: event.communities, + products: _cachedProducts ?? [], + spaceModels: prevSpaceModels, + allTags: _cachedTags ?? [])); } catch (error) { emit(SpaceManagementError('Error loading communities: $error')); } } - Future _onBlankState( - BlankStateEvent event, Emitter emit) async { + Future _onBlankState(BlankStateEvent event, Emitter emit) async { try { final previousState = state; final projectUuid = await ProjectManager.getProjectUUID() ?? ''; var spaceBloc = event.context.read(); List communities = await _waitForCommunityList(spaceBloc); + await fetchSpaceModels(); + await fetchTags(); var prevSpaceModels = await fetchSpaceModels(); - if (previousState is SpaceManagementLoaded || - previousState is BlankState) { + if (previousState is SpaceManagementLoaded || previousState is BlankState) { final prevCommunities = (previousState as dynamic).communities ?? []; emit(BlankState( - communities: List.from(prevCommunities), - products: _cachedProducts ?? [], - spaceModels: prevSpaceModels, - )); + communities: List.from(prevCommunities), + products: _cachedProducts ?? [], + spaceModels: prevSpaceModels, + allTags: _cachedTags)); return; } @@ -251,8 +256,7 @@ class SpaceManagementBloc List updatedCommunities = await Future.wait( communities.map((community) async { - List spaces = - await _fetchSpacesForCommunity(community.uuid); + List spaces = await _fetchSpacesForCommunity(community.uuid); return CommunityModel( uuid: community.uuid, createdAt: community.createdAt, @@ -272,6 +276,7 @@ class SpaceManagementBloc spaceModels: prevSpaceModels, communities: communities, products: _cachedProducts ?? [], + allTags: _cachedTags ?? [], )); } catch (error) { emit(SpaceManagementError('Error loading communities: $error')); @@ -282,11 +287,9 @@ class SpaceManagementBloc LoadCommunityAndSpacesEvent event, Emitter emit, ) async { - _logEvent('LoadCommunityAndSpacesEvent'); - var spaceBloc = event.context.read(); _onloadProducts(); - + await fetchTags(); // Wait until `communityList` is loaded List communities = await _waitForCommunityList(spaceBloc); @@ -296,11 +299,11 @@ class SpaceManagementBloc communities: communities, products: _cachedProducts ?? [], spaceModels: prevSpaceModels, + allTags: _cachedTags ?? [] )); } - Future> _waitForCommunityList( - SpaceTreeBloc spaceBloc) async { + Future> _waitForCommunityList(SpaceTreeBloc spaceBloc) async { // Check if communityList is already populated if (spaceBloc.state.communityList.isNotEmpty) { return spaceBloc.state.communityList; @@ -327,8 +330,7 @@ class SpaceManagementBloc emit(SpaceManagementLoading()); final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - final success = - await _api.deleteCommunity(event.communityUuid, projectUuid); + final success = await _api.deleteCommunity(event.communityUuid, projectUuid); if (success) { // add(LoadCommunityAndSpacesEvent()); } else { @@ -349,14 +351,13 @@ class SpaceManagementBloc try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - - CommunityModel? newCommunity = await _api.createCommunity( - event.name, event.description, projectUuid); + await fetchTags(); + CommunityModel? newCommunity = + await _api.createCommunity(event.name, event.description, projectUuid); var prevSpaceModels = await fetchSpaceModels(); if (newCommunity != null) { - if (previousState is SpaceManagementLoaded || - previousState is BlankState) { + if (previousState is SpaceManagementLoaded || previousState is BlankState) { final prevCommunities = List.from( (previousState as dynamic).communities, ); @@ -368,7 +369,9 @@ class SpaceManagementBloc communities: updatedCommunities, products: _cachedProducts ?? [], selectedCommunity: newCommunity, - selectedSpace: null)); + selectedSpace: null, + allTags: _cachedTags ?? [], + )); } } else { emit(const SpaceManagementError('Error creating community')); @@ -408,11 +411,12 @@ class SpaceManagementBloc required Emitter emit, CommunityModel? selectedCommunity, SpaceModel? selectedSpace, - }) { + }) async { final previousState = state; emit(SpaceManagementLoading()); try { + await fetchTags(); if (previousState is SpaceManagementLoaded || previousState is BlankState || previousState is SpaceModelLoaded) { @@ -428,7 +432,8 @@ class SpaceManagementBloc products: _cachedProducts ?? [], selectedCommunity: selectedCommunity, selectedSpace: selectedSpace, - spaceModels: spaceModels)); + spaceModels: spaceModels, + allTags: _cachedTags ?? [])); } } catch (e) { emit(SpaceManagementError('Error updating state: $e')); @@ -443,8 +448,7 @@ class SpaceManagementBloc emit(SpaceManagementLoading()); try { - final updatedSpaces = - await saveSpacesHierarchically(event.spaces, event.communityUuid); + final updatedSpaces = await saveSpacesHierarchically(event.spaces, event.communityUuid); final allSpaces = await _fetchSpacesForCommunity(event.communityUuid); @@ -474,7 +478,7 @@ class SpaceManagementBloc Emitter emit, ) async { var prevSpaceModels = await fetchSpaceModels(); - + await fetchTags(); final communities = List.from(previousState.communities); for (var community in communities) { @@ -487,7 +491,8 @@ class SpaceManagementBloc products: _cachedProducts ?? [], selectedCommunity: community, selectedSpace: null, - spaceModels: prevSpaceModels)); + spaceModels: prevSpaceModels, + allTags: _cachedTags ??[])); return; } } @@ -518,8 +523,7 @@ class SpaceManagementBloc if (space.uuid != null && space.uuid!.isNotEmpty) { List tagUpdates = []; - final prevSpace = - await _api.getSpace(communityUuid, space.uuid!, projectUuid); + final prevSpace = await _api.getSpace(communityUuid, space.uuid!, projectUuid); final List subspaceUpdates = []; final List? prevSubspaces = prevSpace?.subspaces; final List? newSubspaces = space.subspaces; @@ -529,19 +533,17 @@ class SpaceManagementBloc if (prevSubspaces != null || newSubspaces != null) { if (prevSubspaces != null && newSubspaces != null) { for (var prevSubspace in prevSubspaces) { - final existsInNew = newSubspaces - .any((subspace) => subspace.uuid == prevSubspace.uuid); + final existsInNew = + newSubspaces.any((subspace) => subspace.uuid == prevSubspace.uuid); if (!existsInNew) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: custom_action.Action.delete, - uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, uuid: prevSubspace.uuid)); } } } else if (prevSubspaces != null && newSubspaces == null) { for (var prevSubspace in prevSubspaces) { subspaceUpdates.add(UpdateSubspaceTemplateModel( - action: custom_action.Action.delete, - uuid: prevSubspace.uuid)); + action: custom_action.Action.delete, uuid: prevSubspace.uuid)); } } @@ -569,9 +571,7 @@ class SpaceManagementBloc } if (prevSubspaces != null && newSubspaces != null) { - final newSubspaceMap = { - for (var subspace in newSubspaces) subspace.uuid: subspace - }; + final newSubspaceMap = {for (var subspace in newSubspaces) subspace.uuid: subspace}; for (var prevSubspace in prevSubspaces) { final newSubspace = newSubspaceMap[prevSubspace.uuid]; @@ -608,10 +608,8 @@ class SpaceManagementBloc : []; final createSubspaceBodyModels = space.subspaces?.map((subspace) { - final tagBodyModels = subspace.tags - ?.map((tag) => tag.toCreateTagBodyModel()) - .toList() ?? - []; + final tagBodyModels = + subspace.tags?.map((tag) => tag.toCreateTagBodyModel()).toList() ?? []; return CreateSubspaceModel() ..subspaceName = subspace.subspaceName ..tags = tagBodyModels; @@ -664,12 +662,11 @@ class SpaceManagementBloc return result.toList(); // Convert back to a list } - void _onLoadSpaceModel( - SpaceModelLoadEvent event, Emitter emit) async { + void _onLoadSpaceModel(SpaceModelLoadEvent event, Emitter emit) async { emit(SpaceManagementLoading()); try { - var prevState = state; + await fetchTags(); final projectUuid = await ProjectManager.getProjectUUID() ?? ''; var spaceBloc = event.context.read(); List communities = spaceBloc.state.communityList; @@ -681,8 +678,7 @@ class SpaceManagementBloc List updatedCommunities = await Future.wait( communities.map((community) async { - List spaces = - await _fetchSpacesForCommunity(community.uuid); + List spaces = await _fetchSpacesForCommunity(community.uuid); return CommunityModel( uuid: community.uuid, createdAt: community.createdAt, @@ -702,6 +698,7 @@ class SpaceManagementBloc communities: communities, products: _cachedProducts ?? [], spaceModels: prevSpaceModels, + allTags: _cachedTags ?? [] )); } catch (e) { emit(SpaceManagementError('Error loading communities and spaces: $e')); @@ -731,17 +728,14 @@ class SpaceManagementBloc // Case 1: Tags deleted if (prevTags != null && newTags != null) { for (var prevTag in prevTags) { - final existsInNew = - newTags.any((newTag) => newTag.uuid == prevTag.uuid); + final existsInNew = newTags.any((newTag) => newTag.uuid == prevTag.uuid); if (!existsInNew) { - tagUpdates.add(TagModelUpdate( - action: custom_action.Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid)); } } } else if (prevTags != null && newTags == null) { for (var prevTag in prevTags) { - tagUpdates.add(TagModelUpdate( - action: custom_action.Action.delete, uuid: prevTag.uuid)); + tagUpdates.add(TagModelUpdate(action: custom_action.Action.delete, uuid: prevTag.uuid)); } } 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 674fce4c..3890de81 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 @@ -2,6 +2,7 @@ import 'package:equatable/equatable.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; abstract class SpaceManagementState extends Equatable { @@ -21,22 +22,25 @@ class SpaceManagementLoaded extends SpaceManagementState { CommunityModel? selectedCommunity; SpaceModel? selectedSpace; List? spaceModels; + List allTags; SpaceManagementLoaded( {required this.communities, required this.products, this.selectedCommunity, this.selectedSpace, - this.spaceModels}); + this.spaceModels, + required this.allTags}); } class BlankState extends SpaceManagementState { final List communities; final List products; List? spaceModels; + final List? allTags; BlankState( - {required this.communities, required this.products, this.spaceModels}); + {required this.communities, required this.products, this.spaceModels, required this.allTags}); } class SpaceCreationSuccess extends SpaceManagementState { @@ -61,14 +65,14 @@ class SpaceModelLoaded extends SpaceManagementState { List spaceModels; final List products; final List communities; + final List allTags; - SpaceModelLoaded({ - required this.communities, - required this.products, - required this.spaceModels, - }); + SpaceModelLoaded( + {required this.communities, + required this.products, + required this.spaceModels, + required this.allTags}); @override - List get props => [communities, products, spaceModels]; + List get props => [communities, products, spaceModels, allTags]; } - 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 0a847837..8635d4c0 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 @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; @@ -62,9 +60,9 @@ class SpaceManagementPageState extends State { selectedSpace: null, products: state.products, shouldNavigateToSpaceModelPage: false, + ); } else if (state is SpaceManagementLoaded) { - return LoadedSpaceView( communities: state.communities, selectedCommunity: state.selectedCommunity, From 213ec329c08326fc2728cdbbea58e57f6399e4fc Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 17:16:00 +0400 Subject: [PATCH 10/21] passed tags to space model page --- .../bloc/space_management_bloc.dart | 2 +- .../bloc/space_management_state.dart | 2 +- .../view/spaces_management_page.dart | 4 ++- .../widgets/loaded_space_widget.dart | 29 ++++++++++--------- .../space_model/view/space_model_page.dart | 13 ++++----- 5 files changed, 26 insertions(+), 24 deletions(-) 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 671e446c..1035634a 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 @@ -247,7 +247,7 @@ class SpaceManagementBloc extends Bloc.from(prevCommunities), products: _cachedProducts ?? [], spaceModels: prevSpaceModels, - allTags: _cachedTags)); + allTags: _cachedTags ?? [])); return; } 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 3890de81..3efa7c00 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 @@ -37,7 +37,7 @@ class BlankState extends SpaceManagementState { final List communities; final List products; List? spaceModels; - final List? allTags; + final List allTags; BlankState( {required this.communities, required this.products, this.spaceModels, required this.allTags}); 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 8635d4c0..00b2cf44 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 @@ -60,7 +60,7 @@ class SpaceManagementPageState extends State { selectedSpace: null, products: state.products, shouldNavigateToSpaceModelPage: false, - + projectTags: state.allTags, ); } else if (state is SpaceManagementLoaded) { return LoadedSpaceView( @@ -70,6 +70,7 @@ class SpaceManagementPageState extends State { products: state.products, spaceModels: state.spaceModels, shouldNavigateToSpaceModelPage: false, + projectTags: state.allTags, ); } else if (state is SpaceModelLoaded) { return LoadedSpaceView( @@ -77,6 +78,7 @@ class SpaceManagementPageState extends State { products: state.products, spaceModels: state.spaceModels, shouldNavigateToSpaceModelPage: true, + projectTags: state.allTags, ); } else if (state is SpaceManagementError) { return Center(child: Text('Error: ${state.errorMessage}')); 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 4d6fc3fb..04bcf8d2 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 @@ -4,6 +4,7 @@ import 'package:syncrow_web/pages/space_tree/view/space_tree_view.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/space_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/community_structure_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/gradient_canvas_border_widget.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/widgets/sidebar_widget.dart'; @@ -19,16 +20,17 @@ class LoadedSpaceView extends StatefulWidget { final List? products; final List? spaceModels; final bool shouldNavigateToSpaceModelPage; + final List projectTags; - const LoadedSpaceView({ - super.key, - required this.communities, - this.selectedCommunity, - this.selectedSpace, - this.products, - this.spaceModels, - required this.shouldNavigateToSpaceModelPage, - }); + const LoadedSpaceView( + {super.key, + required this.communities, + this.selectedCommunity, + this.selectedSpace, + this.products, + this.spaceModels, + required this.shouldNavigateToSpaceModelPage, + required this.projectTags}); @override _LoadedSpaceViewState createState() => _LoadedSpaceViewState(); @@ -81,8 +83,7 @@ class _LoadedSpaceViewState extends State { ? _spaceModels.isNotEmpty ? Row( children: [ - SizedBox( - width: 300, child: SpaceTreeView(onSelect: () {})), + SizedBox(width: 300, child: SpaceTreeView(onSelect: () {})), Expanded( child: BlocProvider( create: (context) => SpaceModelBloc( @@ -92,6 +93,7 @@ class _LoadedSpaceViewState extends State { child: SpaceModelPage( products: widget.products, onSpaceModelsUpdated: _onSpaceModelsUpdated, + projectTags: widget.projectTags, ), ), ), @@ -102,9 +104,8 @@ class _LoadedSpaceViewState extends State { children: [ SidebarWidget( communities: widget.communities, - selectedSpaceUuid: widget.selectedSpace?.uuid ?? - widget.selectedCommunity?.uuid ?? - '', + selectedSpaceUuid: + widget.selectedSpace?.uuid ?? widget.selectedCommunity?.uuid ?? '', ), CommunityStructureArea( selectedCommunity: widget.selectedCommunity, 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 35611868..340a4acb 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 @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.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_state.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; @@ -12,8 +13,9 @@ import 'package:syncrow_web/utils/color_manager.dart'; class SpaceModelPage extends StatelessWidget { final List? products; final Function(List)? onSpaceModelsUpdated; + final List projectTags; - const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated}) + const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated, required this.projectTags}) : super(key: key); @override @@ -69,8 +71,7 @@ class SpaceModelPage extends StatelessWidget { } // Render existing space model final model = spaceModels[index]; - final otherModel = - List.from(allSpaceModelNames); + final otherModel = List.from(allSpaceModelNames); otherModel.remove(model.modelName); return GestureDetector( onTap: () { @@ -107,10 +108,8 @@ class SpaceModelPage extends StatelessWidget { return Center( child: Text( 'Error: ${state.message}', - style: Theme.of(context) - .textTheme - .bodySmall - ?.copyWith(color: ColorsManager.warningRed), + style: + Theme.of(context).textTheme.bodySmall?.copyWith(color: ColorsManager.warningRed), ), ); } From 5cea8eddb30ff59aa5b65cf1e911449fd4e43caa Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 21:26:36 +0400 Subject: [PATCH 11/21] added project tags to all dialogs --- .../view/device_managment_page.dart | 1 - .../bloc/assign_tag_model_bloc.dart | 3 +- .../views/assign_tag_models_dialog.dart | 165 +++++++----------- .../space_model/view/space_model_page.dart | 5 +- .../dialog/create_space_model_dialog.dart | 110 +++++------- .../widgets/tag_chips_display_widget.dart | 25 ++- .../views/add_device_type_model_widget.dart | 25 +-- 7 files changed, 134 insertions(+), 200 deletions(-) diff --git a/lib/pages/device_managment/all_devices/view/device_managment_page.dart b/lib/pages/device_managment/all_devices/view/device_managment_page.dart index 81a21046..45af9751 100644 --- a/lib/pages/device_managment/all_devices/view/device_managment_page.dart +++ b/lib/pages/device_managment/all_devices/view/device_managment_page.dart @@ -1,6 +1,5 @@ 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/device_managment/all_devices/bloc/device_mgmt_bloc/device_managment_bloc.dart'; import 'package:syncrow_web/pages/device_managment/all_devices/widgets/device_managment_body.dart'; import 'package:syncrow_web/pages/device_managment/shared/navigate_home_grid_view.dart'; diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart index 37747ecd..9754e4de 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart @@ -6,8 +6,9 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag_models/bloc/assig class AssignTagModelBloc extends Bloc { final List allTags; + final List projectTags; - AssignTagModelBloc(this.allTags) : super(AssignTagModelInitial()) { + AssignTagModelBloc(this.allTags, this.projectTags) : super(AssignTagModelInitial()) { on((event, emit) { final initialTags = event.initialTags ?? []; diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index 9c4bb3b4..81f851ed 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -32,6 +32,7 @@ class AssignTagModelsDialog extends StatelessWidget { final BuildContext? pageContext; final List? otherSpaceModels; final List? allSpaceModels; + final List projectTags; const AssignTagModelsDialog( {Key? key, @@ -46,18 +47,18 @@ class AssignTagModelsDialog extends StatelessWidget { this.pageContext, this.otherSpaceModels, this.spaceModel, - this.allSpaceModels}) + this.allSpaceModels, + required this.projectTags}) : super(key: key); @override Widget build(BuildContext context) { - final List locations = (subspaces ?? []) - .map((subspace) => subspace.subspaceName) - .toList() - ..add('Main Space'); + print(projectTags); + final List locations = + (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); return BlocProvider( - create: (_) => AssignTagModelBloc(allTags ?? []) + create: (_) => AssignTagModelBloc(allTags ?? [], projectTags) ..add(InitializeTagModels( initialTags: initialTags, addedProducts: addedProducts, @@ -81,8 +82,7 @@ class AssignTagModelsDialog extends StatelessWidget { ClipRRect( borderRadius: BorderRadius.circular(20), child: DataTable( - headingRowColor: WidgetStateProperty.all( - ColorsManager.dataHeaderGrey), + headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), key: ValueKey(state.tags.length), border: TableBorder.all( color: ColorsManager.dataHeaderGrey, @@ -91,26 +91,17 @@ class AssignTagModelsDialog extends StatelessWidget { ), columns: [ DataColumn( - label: Text('#', - style: Theme.of(context) - .textTheme - .bodyMedium)), + label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( label: Text('Device', - style: Theme.of(context) - .textTheme - .bodyMedium)), + style: Theme.of(context).textTheme.bodyMedium)), DataColumn( numeric: false, - label: Text('Tag', - style: Theme.of(context) - .textTheme - .bodyMedium)), + label: + Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( label: Text('Location', - style: Theme.of(context) - .textTheme - .bodyMedium)), + style: Theme.of(context).textTheme.bodyMedium)), ], rows: state.tags.isEmpty ? [ @@ -118,13 +109,10 @@ class AssignTagModelsDialog extends StatelessWidget { DataCell( Center( child: Text('No Devices Available', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager - .lightGrayColor, - )), + style: + Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.lightGrayColor, + )), ), ), const DataCell(SizedBox()), @@ -141,8 +129,7 @@ class AssignTagModelsDialog extends StatelessWidget { DataCell(Text((index + 1).toString())), DataCell( Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( @@ -156,31 +143,25 @@ class AssignTagModelsDialog extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: ColorsManager - .lightGrayColor, + color: ColorsManager.lightGrayColor, width: 1.0, ), ), child: IconButton( icon: const Icon( Icons.close, - color: ColorsManager - .lightGreyColor, + color: ColorsManager.lightGreyColor, size: 16, ), onPressed: () { - context - .read< - AssignTagModelBloc>() - .add(DeleteTagModel( - tagToDelete: tag, - tags: state.tags)); + context.read().add( + DeleteTagModel( + tagToDelete: tag, tags: state.tags)); controllers.removeAt(index); }, tooltip: 'Delete Tag', padding: EdgeInsets.zero, - constraints: - const BoxConstraints(), + constraints: const BoxConstraints(), ), ), ], @@ -191,19 +172,15 @@ class AssignTagModelsDialog extends StatelessWidget { alignment: Alignment .centerLeft, // Align cell content to the left child: SizedBox( - width: double - .infinity, // Ensure full width for dropdown + width: + double.infinity, // Ensure full width for dropdown child: DialogTextfieldDropdown( - key: ValueKey( - 'dropdown_${Uuid().v4()}_$index'), + key: ValueKey('dropdown_${Uuid().v4()}_$index'), items: state.updatedTags, initialValue: tag.tag, onSelected: (value) { controller.text = value; - context - .read< - AssignTagModelBloc>() - .add(UpdateTag( + context.read().add(UpdateTag( index: index, tag: value, )); @@ -217,12 +194,10 @@ class AssignTagModelsDialog extends StatelessWidget { width: double.infinity, child: DialogDropdown( items: locations, - selectedValue: tag.location ?? - 'Main Space', + selectedValue: tag.location ?? 'Main Space', onSelected: (value) { context - .read< - AssignTagModelBloc>() + .read() .add(UpdateLocation( index: index, location: value, @@ -254,17 +229,13 @@ class AssignTagModelsDialog extends StatelessWidget { builder: (buttonContext) => CancelButton( label: 'Add New Device', onPressed: () async { - final updatedTags = - List.from(state.tags); + final updatedTags = List.from(state.tags); final result = - TagHelper.updateSubspaceTagModels( - updatedTags, subspaces); + TagHelper.updateSubspaceTagModels(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = - List.from( - result['subspaces'] as List); + final processedTags = result['updatedTags'] as List; + final processedSubspaces = List.from( + result['subspaces'] as List); if (context.mounted) { Navigator.of(context).pop(); @@ -272,28 +243,25 @@ class AssignTagModelsDialog extends StatelessWidget { await showDialog( barrierDismissible: false, context: context, - builder: (dialogContext) => - AddDeviceTypeModelWidget( - products: products, - subspaces: processedSubspaces, - isCreate: false, - initialSelectedProducts: TagHelper - .createInitialSelectedProducts( - processedTags, - processedSubspaces), - allTags: allTags, - spaceName: spaceName, - otherSpaceModels: otherSpaceModels, - spaceTagModels: processedTags, - pageContext: pageContext, - spaceModel: SpaceTemplateModel( - modelName: spaceName, - tags: updatedTags, - uuid: spaceModel?.uuid, - internalId: - spaceModel?.internalId, - subspaceModels: - processedSubspaces)), + builder: (dialogContext) => AddDeviceTypeModelWidget( + products: products, + subspaces: processedSubspaces, + isCreate: false, + initialSelectedProducts: + TagHelper.createInitialSelectedProducts( + processedTags, processedSubspaces), + allTags: allTags, + spaceName: spaceName, + otherSpaceModels: otherSpaceModels, + spaceTagModels: processedTags, + pageContext: pageContext, + projectTags: projectTags, + spaceModel: SpaceTemplateModel( + modelName: spaceName, + tags: updatedTags, + uuid: spaceModel?.uuid, + internalId: spaceModel?.internalId, + subspaceModels: processedSubspaces)), ); } }, @@ -310,22 +278,16 @@ class AssignTagModelsDialog extends StatelessWidget { : ColorsManager.whiteColorsWithOpacity, onPressed: state.isSaveEnabled ? () async { - final updatedTags = - List.from(state.tags); + final updatedTags = List.from(state.tags); final result = - TagHelper.updateSubspaceTagModels( - updatedTags, subspaces); + TagHelper.updateSubspaceTagModels(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = - List.from( - result['subspaces'] - as List); + final processedTags = result['updatedTags'] as List; + final processedSubspaces = List.from( + result['subspaces'] as List); - Navigator.of(context) - .popUntil((route) => route.isFirst); + Navigator.of(context).popUntil((route) => route.isFirst); await showDialog( context: context, @@ -334,16 +296,15 @@ class AssignTagModelsDialog extends StatelessWidget { products: products, allSpaceModels: allSpaceModels, allTags: allTags, + projectTags: projectTags, pageContext: pageContext, otherSpaceModels: otherSpaceModels, spaceModel: SpaceTemplateModel( modelName: spaceName, tags: processedTags, uuid: spaceModel?.uuid, - internalId: - spaceModel?.internalId, - subspaceModels: - processedSubspaces), + internalId: spaceModel?.internalId, + subspaceModels: processedSubspaces), ); }, ); 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 340a4acb..e1aa7a92 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 @@ -15,7 +15,8 @@ class SpaceModelPage extends StatelessWidget { final Function(List)? onSpaceModelsUpdated; final List projectTags; - const SpaceModelPage({Key? key, this.products, this.onSpaceModelsUpdated, required this.projectTags}) + const SpaceModelPage( + {Key? key, this.products, this.onSpaceModelsUpdated, required this.projectTags}) : super(key: key); @override @@ -62,6 +63,7 @@ class SpaceModelPage extends StatelessWidget { allTags: allTagValues, pageContext: context, otherSpaceModels: allSpaceModelNames, + projectTags: projectTags, ); }, ); @@ -85,6 +87,7 @@ class SpaceModelPage extends StatelessWidget { otherSpaceModels: otherModel, pageContext: context, allSpaceModels: spaceModels, + projectTags: projectTags, ); }, ); diff --git a/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart b/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart index 69e619b7..98251382 100644 --- a/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart +++ b/lib/pages/spaces_management/space_model/widgets/dialog/create_space_model_dialog.dart @@ -5,6 +5,7 @@ import 'package:syncrow_web/pages/common/buttons/default_button.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/bloc/create_space_model_state.dart'; @@ -25,6 +26,7 @@ class CreateSpaceModelDialog extends StatelessWidget { final BuildContext? pageContext; final List? otherSpaceModels; final List? allSpaceModels; + final List projectTags; const CreateSpaceModelDialog( {Key? key, @@ -33,7 +35,8 @@ class CreateSpaceModelDialog extends StatelessWidget { this.spaceModel, this.pageContext, this.otherSpaceModels, - this.allSpaceModels}) + this.allSpaceModels, + required this.projectTags}) : super(key: key); @override @@ -68,8 +71,7 @@ class CreateSpaceModelDialog extends StatelessWidget { spaceNameController.addListener(() { bloc.add(UpdateSpaceTemplateName( - name: spaceNameController.text, - allModels: otherSpaceModels ?? [])); + name: spaceNameController.text, allModels: otherSpaceModels ?? [])); }); return bloc; @@ -87,9 +89,7 @@ class CreateSpaceModelDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - spaceModel?.uuid == null - ? 'Create New Space Model' - : 'Edit Space Model', + spaceModel?.uuid == null ? 'Create New Space Model' : 'Edit Space Model', style: Theme.of(context) .textTheme .headlineLarge @@ -101,10 +101,8 @@ class CreateSpaceModelDialog extends StatelessWidget { child: TextField( controller: spaceNameController, onChanged: (value) { - context.read().add( - UpdateSpaceTemplateName( - name: value, - allModels: otherSpaceModels ?? [])); + context.read().add(UpdateSpaceTemplateName( + name: value, allModels: otherSpaceModels ?? [])); }, style: Theme.of(context) .textTheme @@ -157,6 +155,7 @@ class CreateSpaceModelDialog extends StatelessWidget { pageContext: pageContext, otherSpaceModels: otherSpaceModels, allSpaceModels: allSpaceModels, + projectTags: projectTags, ), const SizedBox(height: 20), SizedBox( @@ -179,84 +178,55 @@ class CreateSpaceModelDialog extends StatelessWidget { !isNameValid) ? null : () { - final updatedSpaceTemplate = - updatedSpaceModel.copyWith( - modelName: - spaceNameController.text.trim(), + final updatedSpaceTemplate = updatedSpaceModel.copyWith( + modelName: spaceNameController.text.trim(), ); if (updatedSpaceModel.uuid == null) { - context - .read() - .add( + context.read().add( CreateSpaceTemplate( - spaceTemplate: - updatedSpaceTemplate, + spaceTemplate: updatedSpaceTemplate, onCreate: (newModel) { if (pageContext != null) { + pageContext!.read().add( + CreateSpaceModel(newSpaceModel: newModel)); pageContext! - .read() - .add(CreateSpaceModel( - newSpaceModel: - newModel)); - pageContext! - .read< - SpaceManagementBloc>() - .add( - UpdateSpaceModelCache( - newModel)); + .read() + .add(UpdateSpaceModelCache(newModel)); } - Navigator.of(context) - .pop(); // Close the dialog + Navigator.of(context).pop(); // Close the dialog }, ), ); } else { if (pageContext != null) { - final currentState = pageContext! - .read() - .state; - if (currentState - is SpaceModelLoaded) { - final spaceModels = - List.from( - currentState.spaceModels); + final currentState = + pageContext!.read().state; + if (currentState is SpaceModelLoaded) { + final spaceModels = List.from( + currentState.spaceModels); - final SpaceTemplateModel? - currentSpaceModel = spaceModels - .cast() - .firstWhere( - (sm) => - sm?.uuid == - updatedSpaceModel - .uuid, + final SpaceTemplateModel? currentSpaceModel = + spaceModels.cast().firstWhere( + (sm) => sm?.uuid == updatedSpaceModel.uuid, orElse: () => null, ); if (currentSpaceModel != null) { context .read() .add(ModifySpaceTemplate( - spaceTemplate: - currentSpaceModel, - updatedSpaceTemplate: - updatedSpaceTemplate, + spaceTemplate: currentSpaceModel, + updatedSpaceTemplate: updatedSpaceTemplate, onUpdate: (newModel) { - if (pageContext != - null) { - pageContext! - .read< - SpaceModelBloc>() - .add(UpdateSpaceModel( + if (pageContext != null) { + pageContext!.read().add( + UpdateSpaceModel( spaceModelUuid: - newModel.uuid ?? - '')); + newModel.uuid ?? '')); pageContext! - .read< - SpaceManagementBloc>() - .add(UpdateSpaceModelCache( - newModel)); + .read() + .add(UpdateSpaceModelCache(newModel)); } - Navigator.of(context) - .pop(); + Navigator.of(context).pop(); })); } } @@ -265,11 +235,11 @@ class CreateSpaceModelDialog extends StatelessWidget { }, backgroundColor: ColorsManager.secondaryColor, borderRadius: 10, - foregroundColor: ((state.errorMessage != null && - state.errorMessage != '') || - !isNameValid) - ? ColorsManager.whiteColorsWithOpacity - : ColorsManager.whiteColors, + foregroundColor: + ((state.errorMessage != null && state.errorMessage != '') || + !isNameValid) + ? ColorsManager.whiteColorsWithOpacity + : ColorsManager.whiteColors, child: const Text('OK'), ), ), diff --git a/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart b/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart index a07f9b29..fc6a8c88 100644 --- a/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart +++ b/lib/pages/spaces_management/space_model/widgets/tag_chips_display_widget.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/common/edit_chip.dart'; import 'package:syncrow_web/pages/spaces_management/all_spaces/model/product_model.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; import 'package:syncrow_web/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/helper/tag_helper.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; @@ -20,6 +21,7 @@ class TagChipDisplay extends StatelessWidget { final BuildContext? pageContext; final List? otherSpaceModels; final List? allSpaceModels; + final List projectTags; const TagChipDisplay(BuildContext context, {Key? key, @@ -31,14 +33,14 @@ class TagChipDisplay extends StatelessWidget { required this.spaceNameController, this.pageContext, this.otherSpaceModels, - this.allSpaceModels}) + this.allSpaceModels, + required this.projectTags}) : super(key: key); @override Widget build(BuildContext context) { return (spaceModel?.tags?.isNotEmpty == true || - spaceModel?.subspaceModels - ?.any((subspace) => subspace.tags?.isNotEmpty == true) == + spaceModel?.subspaceModels?.any((subspace) => subspace.tags?.isNotEmpty == true) == true) ? SizedBox( width: screenWidth * 0.25, @@ -59,8 +61,7 @@ class TagChipDisplay extends StatelessWidget { // Combine tags from spaceModel and subspaces ...TagHelper.groupTags([ ...?spaceModel?.tags, - ...?spaceModel?.subspaceModels - ?.expand((subspace) => subspace.tags ?? []) + ...?spaceModel?.subspaceModels?.expand((subspace) => subspace.tags ?? []) ]).entries.map( (entry) => Chip( avatar: SizedBox( @@ -76,9 +77,7 @@ class TagChipDisplay extends StatelessWidget { style: Theme.of(context) .textTheme .bodySmall! - .copyWith( - color: - ColorsManager.spaceColor), + .copyWith(color: ColorsManager.spaceColor), ), backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -105,13 +104,12 @@ class TagChipDisplay extends StatelessWidget { spaceModel: spaceModel, otherSpaceModels: otherSpaceModels, initialTags: TagHelper.generateInitialTags( - subspaces: subspaces, - spaceTagModels: spaceModel?.tags ?? []), + subspaces: subspaces, spaceTagModels: spaceModel?.tags ?? []), title: 'Edit Device', - addedProducts: - TagHelper.createInitialSelectedProducts( - spaceModel?.tags ?? [], subspaces), + addedProducts: TagHelper.createInitialSelectedProducts( + spaceModel?.tags ?? [], subspaces), spaceName: spaceModel?.modelName ?? '', + projectTags: projectTags, )); }) ], @@ -134,6 +132,7 @@ class TagChipDisplay extends StatelessWidget { isCreate: true, spaceModel: spaceModel, otherSpaceModels: otherSpaceModels, + projectTags: projectTags, ), ); }, diff --git a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart index 84cd08a0..c0226ba8 100644 --- a/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart +++ b/lib/pages/spaces_management/tag_model/views/add_device_type_model_widget.dart @@ -28,6 +28,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { final BuildContext? pageContext; final SpaceTemplateModel? spaceModel; final List? allSpaceModels; + final List projectTags; const AddDeviceTypeModelWidget( {super.key, @@ -41,7 +42,8 @@ class AddDeviceTypeModelWidget extends StatelessWidget { this.pageContext, this.otherSpaceModels, this.spaceModel, - this.allSpaceModels}); + this.allSpaceModels, + required this.projectTags}); @override Widget build(BuildContext context) { @@ -78,8 +80,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { const SizedBox(height: 16), Expanded( child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20.0), + padding: const EdgeInsets.symmetric(horizontal: 20.0), child: ScrollableGridViewWidget( isCreate: isCreate, products: products, @@ -112,6 +113,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { allSpaceModels: allSpaceModels, products: products, allTags: allTags, + projectTags: projectTags, pageContext: pageContext, otherSpaceModels: otherSpaceModels, spaceModel: SpaceTemplateModel( @@ -137,6 +139,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { subspaces: subspaces, addedProducts: initialSelectedProducts ?? [], allTags: allTags, + projectTags: projectTags, spaceName: spaceName, initialTags: initialTags, otherSpaceModels: otherSpaceModels, @@ -149,11 +152,10 @@ class AddDeviceTypeModelWidget extends StatelessWidget { ), SizedBox( width: 140, - child: - BlocBuilder( + child: BlocBuilder( builder: (context, state) { - final isDisabled = state is AddDeviceModelLoaded && - state.selectedProducts.isEmpty; + final isDisabled = + state is AddDeviceModelLoaded && state.selectedProducts.isEmpty; return DefaultButton( backgroundColor: ColorsManager.secondaryColor, @@ -166,15 +168,13 @@ class AddDeviceTypeModelWidget extends StatelessWidget { : () async { if (state is AddDeviceModelLoaded && state.selectedProducts.isNotEmpty) { - final initialTags = - TagHelper.generateInitialTags( + final initialTags = TagHelper.generateInitialTags( spaceTagModels: spaceTagModels, subspaces: subspaces, ); - final dialogTitle = initialTags.isNotEmpty - ? 'Edit Device' - : 'Assign Tags'; + final dialogTitle = + initialTags.isNotEmpty ? 'Edit Device' : 'Assign Tags'; Navigator.of(context).pop(); await showDialog( context: context, @@ -184,6 +184,7 @@ class AddDeviceTypeModelWidget extends StatelessWidget { subspaces: subspaces, addedProducts: state.selectedProducts, allTags: allTags, + projectTags: projectTags, spaceName: spaceName, initialTags: initialTags, otherSpaceModels: otherSpaceModels, From d2ff909bf2b4fb5eb97cfad53ae8d2280c731318 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Wed, 5 Mar 2025 22:24:45 +0400 Subject: [PATCH 12/21] updated tag dialogue selection --- lib/common/tag_dialog_textfield_dropdown.dart | 185 ++++++++++++++++++ .../bloc/space_management_bloc.dart | 4 +- .../all_spaces/model/tag.dart | 5 +- .../bloc/assign_tag_model_bloc.dart | 75 ++++--- .../bloc/assign_tag_model_event.dart | 2 +- .../bloc/assign_tag_model_state.dart | 2 +- .../views/assign_tag_models_dialog.dart | 15 +- .../create_space_template_body_model.dart | 2 +- 8 files changed, 237 insertions(+), 53 deletions(-) create mode 100644 lib/common/tag_dialog_textfield_dropdown.dart diff --git a/lib/common/tag_dialog_textfield_dropdown.dart b/lib/common/tag_dialog_textfield_dropdown.dart new file mode 100644 index 00000000..219e03ce --- /dev/null +++ b/lib/common/tag_dialog_textfield_dropdown.dart @@ -0,0 +1,185 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/spaces_management/all_spaces/model/tag.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; + +class TagDialogTextfieldDropdown extends StatefulWidget { + final List items; + final ValueChanged onSelected; + final Tag? initialValue; + final String product; + + const TagDialogTextfieldDropdown({ + Key? key, + required this.items, + required this.onSelected, + this.initialValue, + required this.product, + }) : super(key: key); + + @override + _DialogTextfieldDropdownState createState() => _DialogTextfieldDropdownState(); +} + +class _DialogTextfieldDropdownState extends State { + bool _isOpen = false; + OverlayEntry? _overlayEntry; + final TextEditingController _controller = TextEditingController(); + final FocusNode _focusNode = FocusNode(); + List _filteredItems = []; + + @override + void initState() { + super.initState(); + _controller.text = widget.initialValue?.tag ?? ''; + + _filterItems(); + + _focusNode.addListener(() { + if (!_focusNode.hasFocus) { + _closeDropdown(); + } + }); + } + + void _filterItems() { + setState(() { + _filteredItems = widget.items.where((tag) => tag.product?.uuid == widget.product).toList(); + }); + } + + void _toggleDropdown() { + if (_isOpen) { + _closeDropdown(); + } else { + _openDropdown(); + } + } + + void _openDropdown() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry!); + _isOpen = true; + } + + void _closeDropdown() { + if (_isOpen && _overlayEntry != null) { + _overlayEntry!.remove(); + _overlayEntry = null; + _isOpen = false; + } + } + + OverlayEntry _createOverlayEntry() { + final renderBox = context.findRenderObject() as RenderBox; + final size = renderBox.size; + final offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry( + builder: (context) { + return GestureDetector( + onTap: _closeDropdown, + behavior: HitTestBehavior.translucent, + child: Stack( + children: [ + Positioned( + left: offset.dx, + top: offset.dy + size.height, + width: size.width, + child: Material( + elevation: 4.0, + child: Container( + color: ColorsManager.whiteColors, + constraints: const BoxConstraints(maxHeight: 200.0), + child: StatefulBuilder( + builder: (context, setStateDropdown) { + return ListView.builder( + shrinkWrap: true, + itemCount: _filteredItems.length, + itemBuilder: (context, index) { + final tag = _filteredItems[index]; + + return Container( + decoration: const BoxDecoration( + border: Border( + bottom: BorderSide( + color: ColorsManager.lightGrayBorderColor, + width: 1.0, + ), + ), + ), + child: ListTile( + title: Text(tag.tag ?? '', + style: Theme.of(context) + .textTheme + .bodyMedium + ?.copyWith(color: ColorsManager.textPrimaryColor)), + onTap: () { + _controller.text = tag.tag ?? ''; + widget.onSelected(tag); + setState(() { + _filteredItems.remove(tag); + }); + _closeDropdown(); + }, + ), + ); + }, + ); + }, + ), + ), + ), + ), + ], + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => FocusScope.of(context).unfocus(), + behavior: HitTestBehavior.opaque, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), + decoration: BoxDecoration( + border: Border.all(color: ColorsManager.transparentColor), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: TextFormField( + controller: _controller, + focusNode: _focusNode, + onFieldSubmitted: (value) { + final selectedTag = _filteredItems.firstWhere((tag) => tag.tag == value, + orElse: () => Tag(tag: value)); + widget.onSelected(selectedTag); + _closeDropdown(); + }, + onTapOutside: (event) { + widget.onSelected(_filteredItems.firstWhere((tag) => tag.tag == _controller.text, + orElse: () => Tag(tag: _controller.text))); + _closeDropdown(); + }, + style: Theme.of(context).textTheme.bodyMedium, + decoration: const InputDecoration( + hintText: 'Enter or Select a tag', + border: InputBorder.none, + ), + ), + ), + GestureDetector( + onTap: _toggleDropdown, + child: const Icon(Icons.arrow_drop_down), + ), + ], + ), + ), + ); + } +} 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 1035634a..5dcd3e89 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 @@ -60,7 +60,7 @@ class SpaceManagementBloc extends Bloc { - final List allTags; +class AssignTagModelBloc extends Bloc { final List projectTags; - AssignTagModelBloc(this.allTags, this.projectTags) : super(AssignTagModelInitial()) { + AssignTagModelBloc(this.projectTags) : super(AssignTagModelInitial()) { on((event, emit) { final initialTags = event.initialTags ?? []; final existingTagCounts = {}; for (var tag in initialTags) { if (tag.product != null) { - existingTagCounts[tag.product!.uuid] = - (existingTagCounts[tag.product!.uuid] ?? 0) + 1; + existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1; } } @@ -25,17 +22,14 @@ class AssignTagModelBloc for (var selectedProduct in event.addedProducts) { final existingCount = existingTagCounts[selectedProduct.productId] ?? 0; - if (selectedProduct.count == 0 || - selectedProduct.count <= existingCount) { - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) { + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); continue; } final missingCount = selectedProduct.count - existingCount; - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); if (missingCount > 0) { tags.addAll(List.generate( @@ -49,7 +43,7 @@ class AssignTagModelBloc } } - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( tags: tags, @@ -60,11 +54,20 @@ class AssignTagModelBloc on((event, emit) { final currentState = state; - if (currentState is AssignTagModelLoaded && - currentState.tags.isNotEmpty) { + if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - tags[event.index] = tags[event.index].copyWith(tag: event.tag); - final updatedTags = _calculateAvailableTags(allTags, tags); + + if (event.index < 0 || event.index >= tags.length) return; + + tags[event.index] = tags[event.index].copyWith( + tag: event.tag.tag, + uuid: event.tag.uuid, + product: event.tag.product, + internalId: event.tag.internalId, + location: event.tag.location, + ); + + final updatedTags = _calculateAvailableTags(projectTags ?? [], tags); emit(AssignTagModelLoaded( tags: tags, @@ -78,15 +81,13 @@ class AssignTagModelBloc on((event, emit) { final currentState = state; - if (currentState is AssignTagModelLoaded && - currentState.tags.isNotEmpty) { + if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); // Use copyWith for immutability - tags[event.index] = - tags[event.index].copyWith(location: event.location); + tags[event.index] = tags[event.index].copyWith(location: event.location); - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( tags: tags, @@ -100,13 +101,12 @@ class AssignTagModelBloc on((event, emit) { final currentState = state; - if (currentState is AssignTagModelLoaded && - currentState.tags.isNotEmpty) { + if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); emit(AssignTagModelLoaded( tags: tags, - updatedTags: _calculateAvailableTags(allTags, tags), + updatedTags: _calculateAvailableTags(projectTags, tags), isSaveEnabled: _validateTags(tags), errorMessage: _getValidationError(tags), )); @@ -116,12 +116,10 @@ class AssignTagModelBloc on((event, emit) { final currentState = state; - if (currentState is AssignTagModelLoaded && - currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags) - ..remove(event.tagToDelete); + if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { + final tags = List.from(currentState.tags)..remove(event.tagToDelete); - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( tags: tags, @@ -143,10 +141,8 @@ class AssignTagModelBloc String? _getValidationError(List tags) { // Check for duplicate tags - final nonEmptyTags = tags - .map((tag) => tag.tag?.trim() ?? '') - .where((tag) => tag.isNotEmpty) - .toList(); + final nonEmptyTags = + tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList(); final duplicateTags = nonEmptyTags .fold>({}, (map, tag) { @@ -165,15 +161,16 @@ class AssignTagModelBloc return null; } - List _calculateAvailableTags( - List allTags, List tags) { - final selectedTags = tags + List _calculateAvailableTags(List allTags, List selectedTags) { + final selectedTagSet = selectedTags .where((tag) => (tag.tag?.trim().isNotEmpty ?? false)) .map((tag) => tag.tag!.trim()) .toSet(); - final availableTags = - allTags.where((tag) => !selectedTags.contains(tag.trim())).toList(); + final availableTags = allTags + .where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim())) + .toList(); + return availableTags; } } diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart index 23c70ab0..cb878bde 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_event.dart @@ -24,7 +24,7 @@ class InitializeTagModels extends AssignTagModelEvent { class UpdateTag extends AssignTagModelEvent { final int index; - final String tag; + final Tag tag; const UpdateTag({required this.index, required this.tag}); diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart index 55604d3f..08168e6d 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_state.dart @@ -17,7 +17,7 @@ class AssignTagModelLoaded extends AssignTagModelState { final bool isSaveEnabled; final String? errorMessage; - final List updatedTags; + final List updatedTags; const AssignTagModelLoaded({ required this.tags, diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index 81f851ed..df978a6e 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/common/dialog_textfield_dropdown.dart'; +import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.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/all_spaces/model/product_model.dart'; @@ -53,12 +53,11 @@ class AssignTagModelsDialog extends StatelessWidget { @override Widget build(BuildContext context) { - print(projectTags); final List locations = (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); return BlocProvider( - create: (_) => AssignTagModelBloc(allTags ?? [], projectTags) + create: (_) => AssignTagModelBloc( projectTags) ..add(InitializeTagModels( initialTags: initialTags, addedProducts: addedProducts, @@ -174,12 +173,14 @@ class AssignTagModelsDialog extends StatelessWidget { child: SizedBox( width: double.infinity, // Ensure full width for dropdown - child: DialogTextfieldDropdown( - key: ValueKey('dropdown_${Uuid().v4()}_$index'), + child: TagDialogTextfieldDropdown( + key: ValueKey( + 'dropdown_${const Uuid().v4()}_$index'), + product: tag.product?.uuid ?? 'Unknown', items: state.updatedTags, - initialValue: tag.tag, + initialValue: tag, onSelected: (value) { - controller.text = value; + controller.text = value.tag ?? ''; context.read().add(UpdateTag( index: index, tag: value, diff --git a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart index ad0770d5..161bbf3d 100644 --- a/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart +++ b/lib/pages/spaces_management/space_model/models/create_space_template_body_model.dart @@ -6,7 +6,7 @@ class TagBodyModel { Map toJson() { return { 'uuid': uuid, - 'tag': tag, + 'name': tag, 'productUuid': productUuid, }; } From fecab17cbef5d17b5f5663276985036005deadab Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 6 Mar 2025 11:41:36 +0400 Subject: [PATCH 13/21] updated body for update space model --- .../all_spaces/model/tag.dart | 8 +++--- .../bloc/assign_tag_model_bloc.dart | 4 ++- .../views/assign_tag_models_dialog.dart | 2 +- .../bloc/create_space_model_bloc.dart | 9 ++++--- .../space_model/bloc/space_model_bloc.dart | 25 ++++++------------- .../space_model/models/tag_body_model.dart | 2 +- .../space_model/models/tag_update_model.dart | 7 ++++-- 7 files changed, 26 insertions(+), 31 deletions(-) diff --git a/lib/pages/spaces_management/all_spaces/model/tag.dart b/lib/pages/spaces_management/all_spaces/model/tag.dart index c0f21325..8959986c 100644 --- a/lib/pages/spaces_management/all_spaces/model/tag.dart +++ b/lib/pages/spaces_management/all_spaces/model/tag.dart @@ -26,9 +26,7 @@ class Tag extends BaseTag { uuid: json['uuid'] ?? '', internalId: internalId, tag: json['name'] ?? '', - product: json['product'] != null - ? ProductModel.fromMap(json['product']) - : null, + product: json['product'] != null ? ProductModel.fromMap(json['product']) : null, ); } @@ -41,7 +39,7 @@ class Tag extends BaseTag { String? internalId, }) { return Tag( - uuid: uuid, + uuid: uuid ?? this.uuid, tag: tag ?? this.tag, product: product ?? this.product, location: location ?? this.location, @@ -61,7 +59,7 @@ class Tag extends BaseTag { extension TagModelExtensions on Tag { TagBodyModel toTagBodyModel() { return TagBodyModel() - ..uuid = uuid + ..uuid = uuid ..tag = tag ?? '' ..productUuid = product?.uuid; } diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart index 92699559..51b89859 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart @@ -83,10 +83,12 @@ class AssignTagModelBloc extends Bloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - + for (var t in tags) { + } // Use copyWith for immutability tags[event.index] = tags[event.index].copyWith(location: event.location); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index df978a6e..c9d0b57a 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -57,7 +57,7 @@ class AssignTagModelsDialog extends StatelessWidget { (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); return BlocProvider( - create: (_) => AssignTagModelBloc( projectTags) + create: (_) => AssignTagModelBloc(projectTags) ..add(InitializeTagModels( initialTags: initialTags, addedProducts: addedProducts, diff --git a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart index d6801b95..5428c343 100644 --- a/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart +++ b/lib/pages/spaces_management/space_model/bloc/create_space_model_bloc.dart @@ -248,7 +248,7 @@ class CreateSpaceModelBloc for (var tag in newSubspace.tags!) { tagUpdates.add(TagModelUpdate( action: Action.add, - uuid: tag.uuid == '' ? null : tag.uuid, + newTagUuid: tag.uuid == '' ? null : tag.uuid, tag: tag.tag, productUuid: tag.product?.uuid)); } @@ -315,7 +315,7 @@ class CreateSpaceModelBloc tagUpdates.add(TagModelUpdate( action: Action.add, tag: newTag.tag, - uuid: newTag.uuid, + newTagUuid: newTag.uuid, productUuid: newTag.product?.uuid, )); } @@ -351,7 +351,7 @@ class CreateSpaceModelBloc tagUpdates.add(TagModelUpdate( action: Action.add, tag: newTag.tag, - uuid: newTag.uuid == '' ? null : newTag.uuid, + newTagUuid: newTag.uuid == '' ? null : newTag.uuid, productUuid: newTag.product?.uuid)); processedTags.add(newTag.tag); } @@ -367,7 +367,8 @@ class CreateSpaceModelBloc if (newTag != null) { tagUpdates.add(TagModelUpdate( action: Action.update, - uuid: newTag.uuid, + uuid: prevTag.uuid, + newTagUuid: newTag.uuid, tag: newTag.tag, )); } else {} 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 ed6ffa71..d94751d9 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 @@ -14,28 +14,23 @@ class SpaceModelBloc extends Bloc { required this.api, required List initialSpaceModels, }) : super(SpaceModelLoaded(spaceModels: initialSpaceModels)) { - log('Initial Space Models in: ${initialSpaceModels.map((e) => e.toJson()).toList()}'); - on(_onCreateSpaceModel); on(_onUpdateSpaceModel); on(_onDeleteSpaceModel); } - Future _onCreateSpaceModel( - CreateSpaceModel event, Emitter emit) async { + Future _onCreateSpaceModel(CreateSpaceModel event, Emitter emit) async { final currentState = state; if (currentState is SpaceModelLoaded) { try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - final newSpaceModel = await api.getSpaceModel( - event.newSpaceModel.uuid ?? '', projectUuid); + final newSpaceModel = await api.getSpaceModel(event.newSpaceModel.uuid ?? '', projectUuid); if (newSpaceModel != null) { - final updatedSpaceModels = - List.from(currentState.spaceModels) - ..add(newSpaceModel); + final updatedSpaceModels = List.from(currentState.spaceModels) + ..add(newSpaceModel); emit(SpaceModelLoaded(spaceModels: updatedSpaceModels)); } } catch (e) { @@ -44,15 +39,13 @@ class SpaceModelBloc extends Bloc { } } - Future _onUpdateSpaceModel( - UpdateSpaceModel event, Emitter emit) async { + Future _onUpdateSpaceModel(UpdateSpaceModel event, Emitter emit) async { final currentState = state; if (currentState is SpaceModelLoaded) { try { final projectUuid = await ProjectManager.getProjectUUID() ?? ''; - final newSpaceModel = - await api.getSpaceModel(event.spaceModelUuid, projectUuid); + final newSpaceModel = await api.getSpaceModel(event.spaceModelUuid, projectUuid); if (newSpaceModel != null) { final updatedSpaceModels = currentState.spaceModels.map((model) { return model.uuid == event.spaceModelUuid ? newSpaceModel : model; @@ -65,16 +58,14 @@ class SpaceModelBloc extends Bloc { } } - Future _onDeleteSpaceModel( - DeleteSpaceModel event, Emitter emit) async { + 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); + final deletedSuccessfully = await api.deleteSpaceModel(event.spaceModelUuid, projectUuid); if (deletedSuccessfully) { final updatedSpaceModels = currentState.spaceModels diff --git a/lib/pages/spaces_management/space_model/models/tag_body_model.dart b/lib/pages/spaces_management/space_model/models/tag_body_model.dart index d66e2884..49947831 100644 --- a/lib/pages/spaces_management/space_model/models/tag_body_model.dart +++ b/lib/pages/spaces_management/space_model/models/tag_body_model.dart @@ -4,7 +4,7 @@ class CreateTagBodyModel { Map toJson() { return { - 'tag': tag, + 'name': tag, 'productUuid': productUuid, }; } diff --git a/lib/pages/spaces_management/space_model/models/tag_update_model.dart b/lib/pages/spaces_management/space_model/models/tag_update_model.dart index c7190dc8..c2177058 100644 --- a/lib/pages/spaces_management/space_model/models/tag_update_model.dart +++ b/lib/pages/spaces_management/space_model/models/tag_update_model.dart @@ -5,12 +5,14 @@ class TagModelUpdate { final String? uuid; final String? tag; final String? productUuid; + final String? newTagUuid; TagModelUpdate({ required this.action, this.uuid, this.tag, this.productUuid, + this.newTagUuid, }); factory TagModelUpdate.fromJson(Map json) { @@ -26,9 +28,10 @@ class TagModelUpdate { Map toJson() { return { 'action': action.value, - 'uuid': uuid, // Nullable field - 'tag': tag, + 'tagUuid': uuid, + 'name': tag, 'productUuid': productUuid, + 'newTagUuid': newTagUuid }; } } From 048a7c0e8fcad2303bb9c9de8df014c570aab714 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 6 Mar 2025 11:10:53 +0300 Subject: [PATCH 14/21] change model name --- .../bloc/link_space_model_event.dart | 22 ----- .../bloc/link_space_model_state.dart | 35 -------- ...loc.dart => link_space_to_model_bloc.dart} | 21 ++--- .../bloc/link_space_to_model_event.dart | 22 +++++ .../bloc/link_space_to_model_state.dart | 35 ++++++++ .../view/link_space_model_dialog.dart | 18 ++-- .../link_space_model_spaces_dialog.dart | 86 ++----------------- .../widgets/dialog/overwrite_dialog.dart | 6 +- .../widgets/space_model_card_widget.dart | 16 ++-- 9 files changed, 97 insertions(+), 164 deletions(-) delete mode 100644 lib/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart delete mode 100644 lib/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart rename lib/pages/spaces_management/link_space_model/bloc/{link_space_model_bloc.dart => link_space_to_model_bloc.dart} (86%) create mode 100644 lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart create mode 100644 lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart 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 deleted file mode 100644 index 1595e695..00000000 --- a/lib/pages/spaces_management/link_space_model/bloc/link_space_model_event.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter/material.dart'; - -abstract class SpaceModelEvent {} - -class SpaceModelSelectedEvent extends SpaceModelEvent { - final int selectedIndex; - - SpaceModelSelectedEvent(this.selectedIndex); -} - -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 deleted file mode 100644 index b7cb7c3f..00000000 --- a/lib/pages/spaces_management/link_space_model/bloc/link_space_model_state.dart +++ /dev/null @@ -1,35 +0,0 @@ -abstract class SpaceModelState { - const SpaceModelState(); -} - -class SpaceModelInitial extends SpaceModelState {} - -class SpaceModelLoading extends SpaceModelState {} - -class SpaceModelSelectedState extends SpaceModelState { - final int 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/link_space_model/bloc/link_space_model_bloc.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart similarity index 86% rename from lib/pages/spaces_management/link_space_model/bloc/link_space_model_bloc.dart rename to lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart index cb9f5d43..98491538 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_to_model_bloc.dart @@ -3,17 +3,18 @@ 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/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_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); +class LinkSpaceToModelBloc + extends Bloc { + LinkSpaceToModelBloc() : super(SpaceModelInitial()) { + on(_getSpaceIds); on(_handleLinkSpaceModel); on(_validateLinkSpaceModel); - on((event, emit) { + on((event, emit) { emit(SpaceModelSelectedState(event.selectedIndex)); }); } @@ -22,8 +23,8 @@ class SpaceModelBloc extends Bloc { bool hasSelectedSpaces = false; String validate = ''; - Future _getSpaceIds( - SpaceModelSelectedIdsEvent event, Emitter emit) async { + Future _getSpaceIds(LinkSpaceModelSelectedIdsEvent event, + Emitter emit) async { try { BuildContext context = NavigationService.navigatorKey.currentContext!; var spaceBloc = context.read(); @@ -53,7 +54,7 @@ class SpaceModelBloc extends Bloc { Future _handleLinkSpaceModel( LinkSpaceModelEvent event, - Emitter emit, + Emitter emit, ) async { emit(SpaceModelLoading()); try { @@ -74,7 +75,7 @@ class SpaceModelBloc extends Bloc { Future _validateLinkSpaceModel( ValidateSpaceModelEvent event, - Emitter emit, + Emitter emit, ) async { emit(SpaceModelLoading()); try { diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart new file mode 100644 index 00000000..694358db --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +abstract class LinkSpaceToModelEvent {} + +class LinkSpaceModelSelectedEvent extends LinkSpaceToModelEvent { + final int selectedIndex; + + LinkSpaceModelSelectedEvent(this.selectedIndex); +} + +class LinkSpaceModelSelectedIdsEvent extends LinkSpaceToModelEvent {} + +class LinkSpaceModelEvent extends LinkSpaceToModelEvent { + final String? selectedSpaceMode; + final bool isOverWrite; + LinkSpaceModelEvent({this.selectedSpaceMode, this.isOverWrite = false}); +} + +class ValidateSpaceModelEvent extends LinkSpaceToModelEvent { + BuildContext? context; + ValidateSpaceModelEvent({this.context}); +} diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart new file mode 100644 index 00000000..047567a9 --- /dev/null +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_state.dart @@ -0,0 +1,35 @@ +abstract class LinkSpaceToModelState { + const LinkSpaceToModelState(); +} + +class SpaceModelInitial extends LinkSpaceToModelState {} + +class SpaceModelLoading extends LinkSpaceToModelState {} + +class SpaceModelSelectedState extends LinkSpaceToModelState { + final int selectedIndex; + const SpaceModelSelectedState(this.selectedIndex); +} + +class SpaceModelSelectionUpdated extends LinkSpaceToModelState { + final bool hasSelectedSpaces; + const SpaceModelSelectionUpdated(this.hasSelectedSpaces); +} + +class SpaceValidationSuccess extends LinkSpaceToModelState {} + +class SpaceModelLinkSuccess extends LinkSpaceToModelState {} + +class ValidationError extends LinkSpaceToModelState { + final String message; + const ValidationError(this.message); +} + +class SpaceModelOperationFailure extends LinkSpaceToModelState { + final String message; + const SpaceModelOperationFailure(this.message); +} + +class AlreadyHaveLinkedState extends LinkSpaceToModelState { + const AlreadyHaveLinkedState(); +} 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 index 69023857..bbd0de36 100644 --- 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 @@ -2,9 +2,9 @@ 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/link_space_model/bloc/link_space_to_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_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'; @@ -24,13 +24,13 @@ class LinkSpaceModelDialog extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (context) => SpaceModelBloc() + create: (context) => LinkSpaceToModelBloc() ..add( - SpaceModelSelectedEvent(initialSelectedIndex ?? -1), + LinkSpaceModelSelectedEvent(initialSelectedIndex ?? -1), ), child: Builder( builder: (context) { - final bloc = context.read(); + final bloc = context.read(); return AlertDialog( backgroundColor: ColorsManager.whiteColors, title: const Text('Link a space model'), @@ -39,7 +39,7 @@ class LinkSpaceModelDialog extends StatelessWidget { color: ColorsManager.textFieldGreyColor, width: MediaQuery.of(context).size.width * 0.7, height: MediaQuery.of(context).size.height * 0.6, - child: BlocBuilder( + child: BlocBuilder( builder: (context, state) { int selectedIndex = -1; if (state is SpaceModelSelectedState) { @@ -59,7 +59,7 @@ class LinkSpaceModelDialog extends StatelessWidget { final isSelected = selectedIndex == index; return GestureDetector( onTap: () { - bloc.add(SpaceModelSelectedEvent(index)); + bloc.add(LinkSpaceModelSelectedEvent(index)); }, child: Container( margin: const EdgeInsets.all(10.0), @@ -93,7 +93,7 @@ class LinkSpaceModelDialog extends StatelessWidget { label: 'Cancel', ), const SizedBox(width: 10), - BlocBuilder( + BlocBuilder( builder: (context, state) { final isEnabled = state is SpaceModelSelectedState && state.selectedIndex >= 0; 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 844513d3..f67f8b2c 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 @@ -224,9 +224,9 @@ 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/link_space_model/bloc/link_space_to_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_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/overwrite_dialog.dart'; @@ -249,7 +249,7 @@ class _LinkSpaceModelSpacesDialogState @override void initState() { - context.read().add(SpaceModelSelectedIdsEvent()); + context.read().add(LinkSpaceModelSelectedIdsEvent()); super.initState(); } @@ -328,8 +328,10 @@ class _LinkSpaceModelSpacesDialogState child: SpaceTreeView( isSide: true, onSelect: () { - context.read().add( - SpaceModelSelectedIdsEvent()); + context + .read() + .add( + LinkSpaceModelSelectedIdsEvent()); }))) ], ), @@ -391,7 +393,7 @@ class _LinkSpaceModelSpacesDialogState "Confirm", ColorsManager.onSecondaryColor, () { - final spaceModelBloc = context.read(); + final spaceModelBloc = context.read(); if (!spaceModelBloc.hasSelectedSpaces) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( @@ -408,78 +410,8 @@ class _LinkSpaceModelSpacesDialogState ], ); } - - 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), 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 6a34002f..9f57a4b1 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,10 +1,10 @@ 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/link_space_model/bloc/link_space_to_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; void showOverwriteDialog( - BuildContext context, SpaceModelBloc bloc, SpaceTemplateModel model) { + BuildContext context, LinkSpaceToModelBloc bloc, SpaceTemplateModel model) { showDialog( context: context, barrierDismissible: false, 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 56ba5d3b..c563a6e2 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,9 +1,9 @@ import 'package:flutter/material.dart'; 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/link_space_model/bloc/link_space_to_model_bloc.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; +import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_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'; @@ -81,13 +81,13 @@ class SpaceModelCardWidget extends StatelessWidget { showDialog( context: context, builder: (BuildContext dialogContext) { - return BlocProvider( - create: (_) => SpaceModelBloc(), - child: BlocListener( + return BlocProvider( + create: (_) => LinkSpaceToModelBloc(), + child: BlocListener( listener: (context, state) { final _bloc = - BlocProvider.of( + BlocProvider.of( context); if (state is SpaceModelLoading) { showDialog( From add34b327f5eca2e2e645c794f1a89271804bc41 Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 6 Mar 2025 11:50:47 +0300 Subject: [PATCH 15/21] space model changes --- .../widgets/space_model_card_widget.dart | 104 +++++++++--------- 1 file changed, 50 insertions(+), 54 deletions(-) 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 51386cb4..1ecc247c 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 @@ -188,63 +188,59 @@ class SpaceModelCardWidget extends StatelessWidget { fit: BoxFit.contain, ), ), - InkWell( - onTap: () { - showDialog( - context: context, - builder: (BuildContext dialogContext) { - return const LinkingAttentionDialog(); - }, - ); - }, - child: SvgPicture.asset( - Assets.deleteSpaceLinkIcon, - fit: BoxFit.contain, - ), - ), - ], - ), - Expanded( - child: Text( - model.modelName, - style: - Theme.of(context).textTheme.headlineMedium?.copyWith( - color: Colors.black, - fontWeight: FontWeight.bold, - ), - 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( + if (!topActionsDisabled) + InkWell( + onTap: () { + _showDeleteDialog(context); + }, child: SvgPicture.asset( - Assets.deleteSpaceModel, // Your actual SVG path - width: 20, - height: 20, - colorFilter: const ColorFilter.mode( - Colors.grey, BlendMode.srcIn), + Assets.deleteSpaceLinkIcon, + fit: BoxFit.contain, ), ), - ), - ), + ], + ), + // Expanded( + // child: Text( + // model.modelName, + // style: + // Theme.of(context).textTheme.headlineMedium?.copyWith( + // color: Colors.black, + // fontWeight: FontWeight.bold, + // ), + // 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) ...[ From d624dd767b5a04551932d1411152fd90f757fd19 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 6 Mar 2025 12:52:41 +0400 Subject: [PATCH 16/21] add tags --- .../views/add_device_type_widget.dart | 8 +- .../bloc/space_management_bloc.dart | 41 +++-- .../widgets/community_structure_widget.dart | 129 ++++++--------- .../widgets/dialogs/create_space_dialog.dart | 150 +++++++----------- .../widgets/loaded_space_widget.dart | 1 + .../assign_tag/bloc/assign_tag_bloc.dart | 59 +++---- .../assign_tag/bloc/assign_tag_event.dart | 2 +- .../assign_tag/bloc/assign_tag_state.dart | 9 +- .../assign_tag/views/assign_tag_dialog.dart | 115 +++++--------- .../bloc/assign_tag_model_bloc.dart | 7 +- .../views/assign_tag_models_dialog.dart | 3 +- 11 files changed, 214 insertions(+), 310 deletions(-) diff --git a/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart b/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart index 1930963b..ede6afb9 100644 --- a/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart +++ b/lib/pages/spaces_management/add_device_type/views/add_device_type_widget.dart @@ -24,6 +24,8 @@ class AddDeviceTypeWidget extends StatelessWidget { final String spaceName; final bool isCreate; final Function(List, List?)? onSave; + final List projectTags; + const AddDeviceTypeWidget( {super.key, @@ -35,7 +37,8 @@ class AddDeviceTypeWidget extends StatelessWidget { this.allTags, this.spaceTags, this.onSave, - required this.spaceName}); + required this.spaceName, + required this.projectTags}); @override Widget build(BuildContext context) { @@ -134,7 +137,8 @@ class AddDeviceTypeWidget extends StatelessWidget { spaceName: spaceName, initialTags: initialTags, title: dialogTitle, - onSave: onSave), + onSave: onSave, + projectTags: projectTags), ); } }, 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 5dcd3e89..337e9950 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 @@ -296,11 +296,10 @@ class SpaceManagementBloc extends Bloc> _waitForCommunityList(SpaceTreeBloc spaceBloc) async { @@ -365,13 +364,13 @@ class SpaceManagementBloc extends Bloc communities; final List spaces; final List? spaceModels; + final List projectTags; - CommunityStructureArea({ - this.selectedCommunity, - this.selectedSpace, - required this.communities, - this.products, - required this.spaces, - this.onSpaceSelected, - this.spaceModels, - }); + CommunityStructureArea( + {this.selectedCommunity, + this.selectedSpace, + required this.communities, + this.products, + required this.spaces, + this.onSpaceSelected, + this.spaceModels, + required this.projectTags}); @override _CommunityStructureAreaState createState() => _CommunityStructureAreaState(); @@ -64,8 +65,7 @@ class _CommunityStructureAreaState extends State { void initState() { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = - widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); _nameController = TextEditingController( text: widget.selectedCommunity?.name ?? '', @@ -92,14 +92,12 @@ class _CommunityStructureAreaState extends State { if (oldWidget.spaces != widget.spaces) { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; - connections = - widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; + connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); }); } - if (widget.selectedSpace != oldWidget.selectedSpace && - widget.selectedSpace != null) { + if (widget.selectedSpace != oldWidget.selectedSpace && widget.selectedSpace != null) { WidgetsBinding.instance.addPostFrameCallback((_) { _moveToSpace(widget.selectedSpace!); }); @@ -182,8 +180,7 @@ class _CommunityStructureAreaState extends State { connection, widget.selectedSpace) ? 1.0 : 0.3, // Adjust opacity - child: CustomPaint( - painter: CurvedLinePainter([connection])), + child: CustomPaint(painter: CurvedLinePainter([connection])), ), for (var entry in spaces.asMap().entries) if (entry.value.status != SpaceStatus.deleted && @@ -193,15 +190,12 @@ class _CommunityStructureAreaState extends State { top: entry.value.position.dy, child: SpaceCardWidget( index: entry.key, - onButtonTap: (int index, Offset newPosition, - String direction) { - _showCreateSpaceDialog( - screenSize, - position: - spaces[index].position + newPosition, - parentIndex: index, - direction: direction, - ); + onButtonTap: (int index, Offset newPosition, String direction) { + _showCreateSpaceDialog(screenSize, + position: spaces[index].position + newPosition, + parentIndex: index, + direction: direction, + projectTags: widget.projectTags); }, position: entry.value.position, isHovered: entry.value.isHovered, @@ -211,9 +205,8 @@ class _CommunityStructureAreaState extends State { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { - final bool isHighlighted = - SpaceHelper.isHighlightedSpace( - spaces[index], widget.selectedSpace); + final bool isHighlighted = SpaceHelper.isHighlightedSpace( + spaces[index], widget.selectedSpace); return Opacity( opacity: isHighlighted ? 1.0 : 0.3, @@ -238,7 +231,8 @@ class _CommunityStructureAreaState extends State { onTap: () { _showCreateSpaceDialog(screenSize, canvasHeight: canvasHeight, - canvasWidth: canvasWidth); + canvasWidth: canvasWidth, + projectTags: widget.projectTags); }, ), ), @@ -292,26 +286,22 @@ class _CommunityStructureAreaState extends State { int? parentIndex, String? direction, double? canvasWidth, - double? canvasHeight}) { + double? canvasHeight, + required List projectTags}) { showDialog( context: context, builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, spaceModels: widget.spaceModels, - allTags: - TagHelper.getAllTagValues(widget.communities, widget.spaceModels), + allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), parentSpace: parentIndex != null ? spaces[parentIndex] : null, - onCreateSpace: (String name, - String icon, - List selectedProducts, - SpaceTemplateModel? spaceModel, - List? subspaces, - List? tags) { + projectTags: projectTags, + onCreateSpace: (String name, String icon, List selectedProducts, + SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { setState(() { // Set the first space in the center or use passed position - Offset centerPosition = - position ?? ConnectionHelper.getCenterPosition(screenSize); + Offset centerPosition = position ?? ConnectionHelper.getCenterPosition(screenSize); SpaceModel newSpace = SpaceModel( name: name, icon: icon, @@ -356,21 +346,17 @@ class _CommunityStructureAreaState extends State { spaceModels: widget.spaceModels, name: widget.selectedSpace!.name, icon: widget.selectedSpace!.icon, - parentSpace: SpaceHelper.findSpaceByInternalId( - widget.selectedSpace?.parent?.internalId, spaces), + projectTags: widget.projectTags, + parentSpace: + SpaceHelper.findSpaceByInternalId(widget.selectedSpace?.parent?.internalId, spaces), editSpace: widget.selectedSpace, currentSpaceModel: widget.selectedSpace?.spaceModel, tags: widget.selectedSpace?.tags, subspaces: widget.selectedSpace?.subspaces, isEdit: true, - allTags: TagHelper.getAllTagValues( - widget.communities, widget.spaceModels), - onCreateSpace: (String name, - String icon, - List selectedProducts, - SpaceTemplateModel? spaceModel, - List? subspaces, - List? tags) { + allTags: TagHelper.getAllTagValues(widget.communities, widget.spaceModels), + onCreateSpace: (String name, String icon, List selectedProducts, + SpaceTemplateModel? spaceModel, List? subspaces, List? tags) { setState(() { // Update the space's properties widget.selectedSpace!.name = name; @@ -380,8 +366,7 @@ class _CommunityStructureAreaState extends State { widget.selectedSpace!.tags = tags; if (widget.selectedSpace!.status != SpaceStatus.newSpace) { - widget.selectedSpace!.status = - SpaceStatus.modified; // Mark as modified + widget.selectedSpace!.status = SpaceStatus.modified; // Mark as modified } for (var space in spaces) { @@ -411,8 +396,7 @@ class _CommunityStructureAreaState extends State { List result = []; void flatten(SpaceModel space) { - if (space.status == SpaceStatus.deleted || - space.status == SpaceStatus.parentDeleted) { + if (space.status == SpaceStatus.deleted || space.status == SpaceStatus.parentDeleted) { return; } result.add(space); @@ -527,16 +511,13 @@ class _CommunityStructureAreaState extends State { void _selectSpace(BuildContext context, SpaceModel space) { context.read().add( - SelectSpaceEvent( - selectedCommunity: widget.selectedCommunity, - selectedSpace: space), + SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: space), ); } void _deselectSpace(BuildContext context) { context.read().add( - SelectSpaceEvent( - selectedCommunity: widget.selectedCommunity, selectedSpace: null), + SelectSpaceEvent(selectedCommunity: widget.selectedCommunity, selectedSpace: null), ); } @@ -625,19 +606,16 @@ class _CommunityStructureAreaState extends State { const double horizontalGap = 200.0; const double verticalGap = 100.0; - SpaceModel duplicateRecursive(SpaceModel original, Offset parentPosition, - SpaceModel? duplicatedParent) { - Offset newPosition = - Offset(parentPosition.dx + horizontalGap, original.position.dy); + SpaceModel duplicateRecursive( + SpaceModel original, Offset parentPosition, SpaceModel? duplicatedParent) { + Offset newPosition = Offset(parentPosition.dx + horizontalGap, original.position.dy); while (spaces.any((s) => - (s.position - newPosition).distance < horizontalGap && - s.status != SpaceStatus.deleted)) { + (s.position - newPosition).distance < horizontalGap && s.status != SpaceStatus.deleted)) { newPosition += Offset(horizontalGap, 0); } - final duplicatedName = - SpaceHelper.generateUniqueSpaceName(original.name, spaces); + final duplicatedName = SpaceHelper.generateUniqueSpaceName(original.name, spaces); final List? duplicatedSubspaces; final List? duplicatedTags; @@ -681,8 +659,7 @@ class _CommunityStructureAreaState extends State { if (original.parent != null && duplicatedParent == null) { final originalParent = original.parent!; - final duplicatedParent = - originalToDuplicate[originalParent] ?? originalParent; + final duplicatedParent = originalToDuplicate[originalParent] ?? originalParent; final parentConnection = Connection( startSpace: duplicatedParent, @@ -698,8 +675,7 @@ class _CommunityStructureAreaState extends State { final childrenWithDownDirection = original.children .where((child) => - child.incomingConnection?.direction == "down" && - child.status != SpaceStatus.deleted) + child.incomingConnection?.direction == "down" && child.status != SpaceStatus.deleted) .toList(); Offset childStartPosition = childrenWithDownDirection.length == 1 @@ -707,8 +683,7 @@ class _CommunityStructureAreaState extends State { : newPosition + Offset(0, verticalGap); for (final child in original.children) { - final isDownDirection = - child.incomingConnection?.direction == "down" ?? false; + final isDownDirection = child.incomingConnection?.direction == "down" ?? false; if (isDownDirection && childrenWithDownDirection.length == 1) { childStartPosition = duplicated.position + Offset(0, verticalGap); @@ -716,8 +691,7 @@ class _CommunityStructureAreaState extends State { childStartPosition = duplicated.position + Offset(horizontalGap, 0); } - final duplicatedChild = - duplicateRecursive(child, childStartPosition, duplicated); + final duplicatedChild = duplicateRecursive(child, childStartPosition, duplicated); duplicated.children.add(duplicatedChild); childStartPosition += Offset(0, verticalGap); } @@ -728,8 +702,7 @@ class _CommunityStructureAreaState extends State { if (space.parent == null) { duplicateRecursive(space, space.position, null); } else { - final duplicatedParent = - originalToDuplicate[space.parent!] ?? space.parent!; + final duplicatedParent = originalToDuplicate[space.parent!] ?? space.parent!; duplicateRecursive(space, space.position, duplicatedParent); } } 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 f3df1637..1c14a1f1 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 @@ -42,6 +42,7 @@ class CreateSpaceDialog extends StatefulWidget { final List? tags; final List? allTags; final SpaceTemplateModel? currentSpaceModel; + final List projectTags; const CreateSpaceDialog( {super.key, @@ -57,7 +58,8 @@ class CreateSpaceDialog extends StatefulWidget { this.spaceModels, this.subspaces, this.tags, - this.currentSpaceModel}); + this.currentSpaceModel, + required this.projectTags}); @override CreateSpaceDialogState createState() => CreateSpaceDialogState(); @@ -80,10 +82,8 @@ class CreateSpaceDialogState extends State { super.initState(); selectedIcon = widget.icon ?? Assets.location; nameController = TextEditingController(text: widget.name ?? ''); - selectedProducts = - widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; - isOkButtonEnabled = - enteredName.isNotEmpty || nameController.text.isNotEmpty; + selectedProducts = widget.selectedProducts.isNotEmpty ? widget.selectedProducts : []; + isOkButtonEnabled = enteredName.isNotEmpty || nameController.text.isNotEmpty; if (widget.currentSpaceModel != null) { subspaces = []; tags = []; @@ -96,15 +96,13 @@ class CreateSpaceDialogState extends State { @override Widget build(BuildContext context) { - bool isSpaceModelDisabled = (tags != null && tags!.isNotEmpty || - subspaces != null && subspaces!.isNotEmpty); + bool isSpaceModelDisabled = + (tags != null && tags!.isNotEmpty || subspaces != null && subspaces!.isNotEmpty); bool isTagsAndSubspaceModelDisabled = (selectedSpaceModel != null); final screenWidth = MediaQuery.of(context).size.width; return AlertDialog( - title: widget.isEdit - ? const Text('Edit Space') - : const Text('Create New Space'), + title: widget.isEdit ? const Text('Edit Space') : const Text('Create New Space'), backgroundColor: ColorsManager.whiteColors, content: SizedBox( width: screenWidth * 0.5, @@ -178,7 +176,8 @@ class CreateSpaceDialogState extends State { isNameFieldInvalid = value.isEmpty; if (!isNameFieldInvalid) { - if (SpaceHelper.isNameConflict(value, widget.parentSpace, widget.editSpace)) { + if (SpaceHelper.isNameConflict( + value, widget.parentSpace, widget.editSpace)) { isNameFieldExist = true; isOkButtonEnabled = false; } else { @@ -245,9 +244,7 @@ class CreateSpaceDialogState extends State { padding: EdgeInsets.zero, ), onPressed: () { - isSpaceModelDisabled - ? null - : _showLinkSpaceModelDialog(context); + isSpaceModelDisabled ? null : _showLinkSpaceModelDialog(context); }, child: ButtonContentWidget( svgAssets: Assets.link, @@ -257,8 +254,7 @@ class CreateSpaceDialogState extends State { ) : Container( width: screenWidth * 0.25, - padding: const EdgeInsets.symmetric( - vertical: 10.0, horizontal: 16.0), + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 16.0), decoration: BoxDecoration( color: ColorsManager.boxColor, borderRadius: BorderRadius.circular(10), @@ -273,8 +269,7 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodyMedium! - .copyWith( - color: ColorsManager.spaceColor), + .copyWith(color: ColorsManager.spaceColor), ), backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( @@ -343,8 +338,8 @@ class CreateSpaceDialogState extends State { onPressed: () async { isTagsAndSubspaceModelDisabled ? null - : _showSubSpaceDialog(context, enteredName, - [], false, widget.products, subspaces); + : _showSubSpaceDialog( + context, enteredName, [], false, widget.products, subspaces); }, child: ButtonContentWidget( icon: Icons.add, @@ -371,22 +366,16 @@ class CreateSpaceDialogState extends State { if (subspaces != null) ...subspaces!.map((subspace) { return Column( - crossAxisAlignment: - CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, children: [ SubspaceNameDisplayWidget( text: subspace.subspaceName, validateName: (updatedName) { - bool nameExists = - subspaces!.any((s) { - bool isSameId = s.internalId == - subspace.internalId; - bool isSameName = s.subspaceName - .trim() - .toLowerCase() == - updatedName - .trim() - .toLowerCase(); + bool nameExists = subspaces!.any((s) { + bool isSameId = s.internalId == subspace.internalId; + bool isSameName = + s.subspaceName.trim().toLowerCase() == + updatedName.trim().toLowerCase(); return !isSameId && isSameName; }); @@ -395,8 +384,7 @@ class CreateSpaceDialogState extends State { }, onNameChanged: (updatedName) { setState(() { - subspace.subspaceName = - updatedName; + subspace.subspaceName = updatedName; }); }, ), @@ -405,8 +393,8 @@ class CreateSpaceDialogState extends State { }), EditChip( onTap: () async { - _showSubSpaceDialog(context, enteredName, - [], true, widget.products, subspaces); + _showSubSpaceDialog(context, enteredName, [], true, + widget.products, subspaces); }, ) ], @@ -415,9 +403,7 @@ class CreateSpaceDialogState extends State { ), const SizedBox(height: 10), (tags?.isNotEmpty == true || - subspaces?.any((subspace) => - subspace.tags?.isNotEmpty == true) == - true) + subspaces?.any((subspace) => subspace.tags?.isNotEmpty == true) == true) ? SizedBox( width: screenWidth * 0.25, child: Container( @@ -437,16 +423,14 @@ class CreateSpaceDialogState extends State { // Combine tags from spaceModel and subspaces ...TagHelper.groupTags([ ...?tags, - ...?subspaces?.expand( - (subspace) => subspace.tags ?? []) + ...?subspaces?.expand((subspace) => subspace.tags ?? []) ]).entries.map( (entry) => Chip( avatar: SizedBox( width: 24, height: 24, child: SvgPicture.asset( - entry.key.icon ?? - 'assets/icons/gateway.svg', + entry.key.icon ?? 'assets/icons/gateway.svg', fit: BoxFit.contain, ), ), @@ -455,15 +439,11 @@ class CreateSpaceDialogState extends State { style: Theme.of(context) .textTheme .bodySmall - ?.copyWith( - color: ColorsManager - .spaceColor), + ?.copyWith(color: ColorsManager.spaceColor), ), - backgroundColor: - ColorsManager.whiteColors, + backgroundColor: ColorsManager.whiteColors, shape: RoundedRectangleBorder( - borderRadius: - BorderRadius.circular(16), + borderRadius: BorderRadius.circular(16), side: const BorderSide( color: ColorsManager.spaceColor, ), @@ -472,23 +452,21 @@ class CreateSpaceDialogState extends State { ), EditChip(onTap: () async { - final result = await showDialog( + await showDialog( context: context, builder: (context) => AssignTagDialog( products: widget.products, subspaces: subspaces, allTags: widget.allTags, - addedProducts: TagHelper - .createInitialSelectedProductsForTags( + addedProducts: + TagHelper.createInitialSelectedProductsForTags( tags ?? [], subspaces), title: 'Edit Device', - initialTags: - TagHelper.generateInitialForTags( - spaceTags: tags, - subspaces: subspaces), + initialTags: TagHelper.generateInitialForTags( + spaceTags: tags, subspaces: subspaces), spaceName: widget.name ?? '', - onSave: - (updatedTags, updatedSubspaces) { + projectTags: widget.projectTags, + onSave: (updatedTags, updatedSubspaces) { setState(() { tags = updatedTags; subspaces = updatedSubspaces; @@ -547,25 +525,17 @@ class CreateSpaceDialogState extends State { }); return; } else { - String newName = enteredName.isNotEmpty - ? enteredName - : (widget.name ?? ''); + String newName = enteredName.isNotEmpty ? enteredName : (widget.name ?? ''); if (newName.isNotEmpty) { - widget.onCreateSpace( - newName, - selectedIcon, - selectedProducts, - selectedSpaceModel, - subspaces, - tags); + widget.onCreateSpace(newName, selectedIcon, selectedProducts, + selectedSpaceModel, subspaces, tags); Navigator.of(context).pop(); } } }, borderRadius: 10, - backgroundColor: isOkButtonEnabled - ? ColorsManager.secondaryColor - : ColorsManager.grayColor, + backgroundColor: + isOkButtonEnabled ? ColorsManager.secondaryColor : ColorsManager.grayColor, foregroundColor: ColorsManager.whiteColors, child: const Text('OK'), ), @@ -592,7 +562,6 @@ class CreateSpaceDialogState extends State { ); } - void _showLinkSpaceModelDialog(BuildContext context) { showDialog( context: context, @@ -613,13 +582,8 @@ class CreateSpaceDialogState extends State { ); } - void _showSubSpaceDialog( - BuildContext context, - String name, - final List? spaceTags, - bool isEdit, - List? products, - final List? existingSubSpaces) { + void _showSubSpaceDialog(BuildContext context, String name, final List? spaceTags, + bool isEdit, List? products, final List? existingSubSpaces) { showDialog( context: context, builder: (BuildContext context) { @@ -634,12 +598,10 @@ class CreateSpaceDialogState extends State { final List tagsToAppendToSpace = []; if (slectedSubspaces != null) { - final updatedIds = - slectedSubspaces.map((s) => s.internalId).toSet(); + final updatedIds = slectedSubspaces.map((s) => s.internalId).toSet(); if (existingSubSpaces != null) { - final deletedSubspaces = existingSubSpaces - .where((s) => !updatedIds.contains(s.internalId)) - .toList(); + final deletedSubspaces = + existingSubSpaces.where((s) => !updatedIds.contains(s.internalId)).toList(); for (var s in deletedSubspaces) { if (s.tags != null) { tagsToAppendToSpace.addAll(s.tags!); @@ -659,20 +621,20 @@ class CreateSpaceDialogState extends State { ); } - void _showTagCreateDialog(BuildContext context, String name, bool isEdit, - List? products) { + void _showTagCreateDialog( + BuildContext context, String name, bool isEdit, List? products) { isEdit ? showDialog( context: context, builder: (BuildContext context) { return AssignTagDialog( title: 'Edit Device', - addedProducts: TagHelper.createInitialSelectedProductsForTags( - tags, subspaces), + addedProducts: TagHelper.createInitialSelectedProductsForTags(tags, subspaces), spaceName: name, products: products, subspaces: subspaces, allTags: widget.allTags, + projectTags: widget.projectTags, onSave: (selectedSpaceTags, selectedSubspaces) { setState(() { tags = selectedSpaceTags; @@ -682,8 +644,7 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == - selectedSubspace.subspaceName) { + if (subspace.subspaceName == selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } @@ -705,9 +666,9 @@ class CreateSpaceDialogState extends State { spaceTags: tags, isCreate: true, allTags: widget.allTags, + projectTags: widget.projectTags, initialSelectedProducts: - TagHelper.createInitialSelectedProductsForTags( - tags, subspaces), + TagHelper.createInitialSelectedProductsForTags(tags, subspaces), onSave: (selectedSpaceTags, selectedSubspaces) { setState(() { tags = selectedSpaceTags; @@ -717,8 +678,7 @@ class CreateSpaceDialogState extends State { if (subspaces != null) { for (final subspace in subspaces!) { for (final selectedSubspace in selectedSubspaces) { - if (subspace.subspaceName == - selectedSubspace.subspaceName) { + if (subspace.subspaceName == selectedSubspace.subspaceName) { subspace.tags = selectedSubspace.tags; } } 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 04bcf8d2..fef2a4c9 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 @@ -114,6 +114,7 @@ class _LoadedSpaceViewState extends State { products: widget.products, communities: widget.communities, spaceModels: _spaceModels, + projectTags: widget.projectTags, ), ], ), diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart index a06e6977..74161b6f 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_bloc.dart @@ -4,17 +4,16 @@ import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_e import 'package:syncrow_web/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart'; class AssignTagBloc extends Bloc { - final List allTags; + final List projectTags; - AssignTagBloc(this.allTags) : super(AssignTagInitial()) { + AssignTagBloc(this.projectTags) : super(AssignTagInitial()) { on((event, emit) { final initialTags = event.initialTags ?? []; final existingTagCounts = {}; for (var tag in initialTags) { if (tag.product != null) { - existingTagCounts[tag.product!.uuid] = - (existingTagCounts[tag.product!.uuid] ?? 0) + 1; + existingTagCounts[tag.product!.uuid] = (existingTagCounts[tag.product!.uuid] ?? 0) + 1; } } @@ -23,17 +22,14 @@ class AssignTagBloc extends Bloc { for (var selectedProduct in event.addedProducts) { final existingCount = existingTagCounts[selectedProduct.productId] ?? 0; - if (selectedProduct.count == 0 || - selectedProduct.count <= existingCount) { - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + if (selectedProduct.count == 0 || selectedProduct.count <= existingCount) { + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); continue; } final missingCount = selectedProduct.count - existingCount; - tags.addAll(initialTags - .where((tag) => tag.product?.uuid == selectedProduct.productId)); + tags.addAll(initialTags.where((tag) => tag.product?.uuid == selectedProduct.productId)); if (missingCount > 0) { tags.addAll(List.generate( @@ -47,11 +43,11 @@ class AssignTagBloc extends Bloc { } } - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, - updatedTags: updatedTags, + updatedTags: updatedTags, isSaveEnabled: _validateTags(tags), errorMessage: '', )); @@ -62,9 +58,16 @@ class AssignTagBloc extends Bloc { if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - tags[event.index] = tags[event.index].copyWith(tag: event.tag); + if (event.index < 0 || event.index >= tags.length) return; - final updatedTags = _calculateAvailableTags(allTags, tags); + tags[event.index] = tags[event.index].copyWith( + tag: event.tag.tag, + uuid: event.tag.uuid, + product: event.tag.product, + internalId: event.tag.internalId, + location: event.tag.location, + ); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -82,10 +85,9 @@ class AssignTagBloc extends Bloc { final tags = List.from(currentState.tags); // Update the location - tags[event.index] = - tags[event.index].copyWith(location: event.location); + tags[event.index] = tags[event.index].copyWith(location: event.location); - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -104,7 +106,7 @@ class AssignTagBloc extends Bloc { emit(AssignTagLoaded( tags: tags, - updatedTags: _calculateAvailableTags(allTags, tags), + updatedTags: _calculateAvailableTags(projectTags, tags), isSaveEnabled: _validateTags(tags), errorMessage: _getValidationError(tags), )); @@ -115,11 +117,10 @@ class AssignTagBloc extends Bloc { final currentState = state; if (currentState is AssignTagLoaded && currentState.tags.isNotEmpty) { - final tags = List.from(currentState.tags) - ..remove(event.tagToDelete); + final tags = List.from(currentState.tags)..remove(event.tagToDelete); // Recalculate available tags - final updatedTags = _calculateAvailableTags(allTags, tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagLoaded( tags: tags, @@ -140,10 +141,8 @@ class AssignTagBloc extends Bloc { // Get validation error for duplicate tags String? _getValidationError(List tags) { - final nonEmptyTags = tags - .map((tag) => tag.tag?.trim() ?? '') - .where((tag) => tag.isNotEmpty) - .toList(); + final nonEmptyTags = + tags.map((tag) => tag.tag?.trim() ?? '').where((tag) => tag.isNotEmpty).toList(); final duplicateTags = nonEmptyTags .fold>({}, (map, tag) { @@ -162,14 +161,16 @@ class AssignTagBloc extends Bloc { return null; } - List _calculateAvailableTags(List allTags, List tags) { - final selectedTags = tags + List _calculateAvailableTags(List allTags, List selectedTags) { + final selectedTagSet = selectedTags .where((tag) => (tag.tag?.trim().isNotEmpty ?? false)) .map((tag) => tag.tag!.trim()) .toSet(); - final availableTags = - allTags.where((tag) => !selectedTags.contains(tag.trim())).toList(); + final availableTags = allTags + .where((tag) => tag.tag != null && !selectedTagSet.contains(tag.tag!.trim())) + .toList(); + return availableTags; } } diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart index 9116b094..7d81ffdb 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_event.dart @@ -24,7 +24,7 @@ class InitializeTags extends AssignTagEvent { class UpdateTagEvent extends AssignTagEvent { final int index; - final String tag; + final Tag tag; const UpdateTagEvent({required this.index, required this.tag}); diff --git a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart index 6a2dae4b..53700a33 100644 --- a/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart +++ b/lib/pages/spaces_management/assign_tag/bloc/assign_tag_state.dart @@ -5,7 +5,7 @@ abstract class AssignTagState extends Equatable { const AssignTagState(); @override - List get props => []; + List get props => []; } class AssignTagInitial extends AssignTagState {} @@ -14,7 +14,7 @@ class AssignTagLoading extends AssignTagState {} class AssignTagLoaded extends AssignTagState { final List tags; - final List updatedTags; + final List updatedTags; final bool isSaveEnabled; final String? errorMessage; @@ -27,8 +27,7 @@ class AssignTagLoaded extends AssignTagState { }); @override - List get props => - [tags, updatedTags, isSaveEnabled, errorMessage ?? '']; + List get props => [tags, updatedTags, isSaveEnabled, errorMessage ?? '']; } class AssignTagError extends AssignTagState { @@ -37,5 +36,5 @@ class AssignTagError extends AssignTagState { const AssignTagError(this.errorMessage); @override - List get props => [errorMessage]; + List get props => [errorMessage]; } diff --git a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart index 3b794b61..fd1454e5 100644 --- a/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart +++ b/lib/pages/spaces_management/assign_tag/views/assign_tag_dialog.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:syncrow_web/common/dialog_dropdown.dart'; -import 'package:syncrow_web/common/dialog_textfield_dropdown.dart'; +import 'package:syncrow_web/common/tag_dialog_textfield_dropdown.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/add_device_type/views/add_device_type_widget.dart'; @@ -26,6 +26,7 @@ class AssignTagDialog extends StatelessWidget { final String spaceName; final String title; final Function(List, List?)? onSave; + final List projectTags; const AssignTagDialog( {Key? key, @@ -37,18 +38,17 @@ class AssignTagDialog extends StatelessWidget { this.allTags, required this.spaceName, required this.title, - this.onSave}) + this.onSave, + required this.projectTags}) : super(key: key); @override Widget build(BuildContext context) { - final List locations = (subspaces ?? []) - .map((subspace) => subspace.subspaceName) - .toList() - ..add('Main Space'); + final List locations = + (subspaces ?? []).map((subspace) => subspace.subspaceName).toList()..add('Main Space'); return BlocProvider( - create: (_) => AssignTagBloc(allTags ?? []) + create: (_) => AssignTagBloc(projectTags) ..add(InitializeTags( initialTags: initialTags, addedProducts: addedProducts, @@ -70,8 +70,7 @@ class AssignTagDialog extends StatelessWidget { ClipRRect( borderRadius: BorderRadius.circular(20), child: DataTable( - headingRowColor: WidgetStateProperty.all( - ColorsManager.dataHeaderGrey), + headingRowColor: WidgetStateProperty.all(ColorsManager.dataHeaderGrey), key: ValueKey(state.tags.length), border: TableBorder.all( color: ColorsManager.dataHeaderGrey, @@ -80,22 +79,15 @@ class AssignTagDialog extends StatelessWidget { ), columns: [ DataColumn( - label: Text('#', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('#', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( - label: Text('Device', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('Device', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( numeric: false, - label: Text('Tag', - style: - Theme.of(context).textTheme.bodyMedium)), + label: Text('Tag', style: Theme.of(context).textTheme.bodyMedium)), DataColumn( - label: Text('Location', - style: - Theme.of(context).textTheme.bodyMedium)), + label: + Text('Location', style: Theme.of(context).textTheme.bodyMedium)), ], rows: state.tags.isEmpty ? [ @@ -103,12 +95,8 @@ class AssignTagDialog extends StatelessWidget { DataCell( Center( child: Text('No Data Available', - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager - .lightGrayColor, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: ColorsManager.lightGrayColor, )), ), ), @@ -126,8 +114,7 @@ class AssignTagDialog extends StatelessWidget { DataCell(Text((index + 1).toString())), DataCell( Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( @@ -141,31 +128,25 @@ class AssignTagDialog extends StatelessWidget { decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: ColorsManager - .lightGrayColor, + color: ColorsManager.lightGrayColor, width: 1.0, ), ), child: IconButton( icon: const Icon( Icons.close, - color: ColorsManager - .lightGreyColor, + color: ColorsManager.lightGreyColor, size: 16, ), onPressed: () { - context - .read() - .add(DeleteTag( - tagToDelete: tag, - tags: state.tags)); + context.read().add( + DeleteTag(tagToDelete: tag, tags: state.tags)); controllers.removeAt(index); }, tooltip: 'Delete Tag', padding: EdgeInsets.zero, - constraints: - const BoxConstraints(), + constraints: const BoxConstraints(), ), ), ], @@ -173,23 +154,20 @@ class AssignTagDialog extends StatelessWidget { ), DataCell( Container( - alignment: Alignment - .centerLeft, // Align cell content to the left + alignment: + Alignment.centerLeft, // Align cell content to the left child: SizedBox( - width: double - .infinity, // Ensure full width for dropdown - child: DialogTextfieldDropdown( - key: ValueKey( - 'dropdown_${Uuid().v4()}_${index}'), + width: double.infinity, + child: TagDialogTextfieldDropdown( + key: ValueKey('dropdown_${const Uuid().v4()}_$index'), items: state.updatedTags, - initialValue: tag.tag, + product: tag.product?.uuid ?? 'Unknown', + initialValue: tag, onSelected: (value) { - controller.text = value; - context - .read() - .add(UpdateTagEvent( + controller.text = value.tag ?? ''; + context.read().add(UpdateTagEvent( index: index, - tag: value.trim(), + tag: value, )); }, ), @@ -201,12 +179,9 @@ class AssignTagDialog extends StatelessWidget { width: double.infinity, child: DialogDropdown( items: locations, - selectedValue: - tag.location ?? 'Main Space', + selectedValue: tag.location ?? 'Main Space', onSelected: (value) { - context - .read() - .add(UpdateLocation( + context.read().add(UpdateLocation( index: index, location: value, )); @@ -238,13 +213,11 @@ class AssignTagDialog extends StatelessWidget { label: 'Add New Device', onPressed: () async { final updatedTags = List.from(state.tags); - final result = - TagHelper.processTags(updatedTags, subspaces); + final result = TagHelper.processTags(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; - final processedSubspaces = List.from( - result['subspaces'] as List); + final processedTags = result['updatedTags'] as List; + final processedSubspaces = + List.from(result['subspaces'] as List); Navigator.of(context).pop(); @@ -253,8 +226,9 @@ class AssignTagDialog extends StatelessWidget { builder: (context) => AddDeviceTypeWidget( products: products, subspaces: processedSubspaces, - initialSelectedProducts: TagHelper - .createInitialSelectedProductsForTags( + projectTags: projectTags, + initialSelectedProducts: + TagHelper.createInitialSelectedProductsForTags( processedTags, processedSubspaces), spaceName: spaceName, spaceTags: processedTags, @@ -278,14 +252,11 @@ class AssignTagDialog extends StatelessWidget { onPressed: state.isSaveEnabled ? () async { final updatedTags = List.from(state.tags); - final result = TagHelper.processTags( - updatedTags, subspaces); + final result = TagHelper.processTags(updatedTags, subspaces); - final processedTags = - result['updatedTags'] as List; + final processedTags = result['updatedTags'] as List; final processedSubspaces = - List.from( - result['subspaces'] as List); + List.from(result['subspaces'] as List); onSave?.call(processedTags, processedSubspaces); Navigator.of(context).pop(); } diff --git a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart index 51b89859..7df82b5e 100644 --- a/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart +++ b/lib/pages/spaces_management/assign_tag_models/bloc/assign_tag_model_bloc.dart @@ -8,7 +8,7 @@ class AssignTagModelBloc extends Bloc AssignTagModelBloc(this.projectTags) : super(AssignTagModelInitial()) { on((event, emit) { - final initialTags = event.initialTags ?? []; + final initialTags = event.initialTags; final existingTagCounts = {}; for (var tag in initialTags) { @@ -67,7 +67,7 @@ class AssignTagModelBloc extends Bloc location: event.tag.location, ); - final updatedTags = _calculateAvailableTags(projectTags ?? [], tags); + final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( tags: tags, @@ -83,12 +83,9 @@ class AssignTagModelBloc extends Bloc if (currentState is AssignTagModelLoaded && currentState.tags.isNotEmpty) { final tags = List.from(currentState.tags); - for (var t in tags) { - } // Use copyWith for immutability tags[event.index] = tags[event.index].copyWith(location: event.location); - final updatedTags = _calculateAvailableTags(projectTags, tags); emit(AssignTagModelLoaded( diff --git a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart index c9d0b57a..85be3bf3 100644 --- a/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart +++ b/lib/pages/spaces_management/assign_tag_models/views/assign_tag_models_dialog.dart @@ -171,8 +171,7 @@ class AssignTagModelsDialog extends StatelessWidget { alignment: Alignment .centerLeft, // Align cell content to the left child: SizedBox( - width: - double.infinity, // Ensure full width for dropdown + width: double.infinity, child: TagDialogTextfieldDropdown( key: ValueKey( 'dropdown_${const Uuid().v4()}_$index'), From efccac4d19c4d5d2efb7a9205c9c30503e2a871b Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Thu, 6 Mar 2025 13:29:28 +0400 Subject: [PATCH 17/21] removed unused widget --- lib/common/dialog_textfield_dropdown.dart | 176 ---------------------- 1 file changed, 176 deletions(-) delete mode 100644 lib/common/dialog_textfield_dropdown.dart diff --git a/lib/common/dialog_textfield_dropdown.dart b/lib/common/dialog_textfield_dropdown.dart deleted file mode 100644 index ac88e5dc..00000000 --- a/lib/common/dialog_textfield_dropdown.dart +++ /dev/null @@ -1,176 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:syncrow_web/utils/color_manager.dart'; - -class DialogTextfieldDropdown extends StatefulWidget { - final List items; - final ValueChanged onSelected; - final String? initialValue; - - const DialogTextfieldDropdown({ - Key? key, - required this.items, - required this.onSelected, - this.initialValue, - }) : super(key: key); - - @override - _DialogTextfieldDropdownState createState() => - _DialogTextfieldDropdownState(); -} - -class _DialogTextfieldDropdownState extends State { - bool _isOpen = false; - OverlayEntry? _overlayEntry; - final TextEditingController _controller = TextEditingController(); - final FocusNode _focusNode = FocusNode(); - List _filteredItems = []; - - @override - void initState() { - super.initState(); - _controller.text = widget.initialValue ?? ''; - _filteredItems = List.from(widget.items); - - _focusNode.addListener(() { - if (!_focusNode.hasFocus) { - _closeDropdown(); - } - }); - } - - void _toggleDropdown() { - if (_isOpen) { - _closeDropdown(); - } else { - _openDropdown(); - } - } - - void _openDropdown() { - _overlayEntry = _createOverlayEntry(); - Overlay.of(context).insert(_overlayEntry!); - _isOpen = true; - } - - void _closeDropdown() { - if (_isOpen && _overlayEntry != null) { - _overlayEntry!.remove(); - _overlayEntry = null; - _isOpen = false; - } - } - - OverlayEntry _createOverlayEntry() { - final renderBox = context.findRenderObject() as RenderBox; - final size = renderBox.size; - final offset = renderBox.localToGlobal(Offset.zero); - - return OverlayEntry( - builder: (context) { - return GestureDetector( - onTap: _closeDropdown, - behavior: HitTestBehavior.translucent, - child: Stack( - children: [ - Positioned( - left: offset.dx, - top: offset.dy + size.height, - width: size.width, - child: Material( - elevation: 4.0, - child: Container( - color: ColorsManager.whiteColors, - constraints: const BoxConstraints(maxHeight: 200.0), - child: StatefulBuilder( - builder: (context, setStateDropdown) { - return ListView.builder( - shrinkWrap: true, - itemCount: _filteredItems.length, - itemBuilder: (context, index) { - final item = _filteredItems[index]; - - return Container( - decoration: const BoxDecoration( - border: Border( - bottom: BorderSide( - color: ColorsManager.lightGrayBorderColor, - width: 1.0, - ), - ), - ), - child: ListTile( - title: Text(item, - style: Theme.of(context) - .textTheme - .bodyMedium - ?.copyWith( - color: ColorsManager - .textPrimaryColor)), - onTap: () { - _controller.text = item; - widget.onSelected(item); - setState(() { - _filteredItems - .remove(item); // Remove selected item - }); - _closeDropdown(); - }, - ), - ); - }, - ); - }, - ), - ), - ), - ), - ], - ), - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: () => FocusScope.of(context).unfocus(), - behavior: HitTestBehavior.opaque, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), - decoration: BoxDecoration( - border: Border.all(color: ColorsManager.transparentColor), - borderRadius: BorderRadius.circular(8.0), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: TextFormField( - controller: _controller, - focusNode: _focusNode, - onFieldSubmitted: (value) { - widget.onSelected(value); - _closeDropdown(); - }, - onTapOutside: (event) { - widget.onSelected(_controller.text); - _closeDropdown(); - }, - style: Theme.of(context).textTheme.bodyMedium, - decoration: const InputDecoration( - hintText: 'Enter or Select a tag', - border: InputBorder.none, - ), - ), - ), - GestureDetector( - onTap: _toggleDropdown, - child: const Icon(Icons.arrow_drop_down), - ), - ], - ), - ), - ); - } -} From 54a250ea2fcb8b50b7efcecc89f8299390cd4b2c Mon Sep 17 00:00:00 2001 From: mohammad Date: Thu, 6 Mar 2025 12:51:30 +0300 Subject: [PATCH 18/21] remove unused code --- .../bloc/link_space_to_model_bloc.dart | 9 +- .../link_space_model_spaces_dialog.dart | 234 +----------------- lib/services/space_model_mang_api.dart | 7 +- 3 files changed, 7 insertions(+), 243 deletions(-) diff --git a/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart index 98491538..c789c2a9 100644 --- a/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart +++ b/lib/pages/spaces_management/link_space_model/bloc/link_space_to_model_bloc.dart @@ -32,9 +32,6 @@ class LinkSpaceToModelBloc List spacesList = spaceBloc.state.selectedCommunityAndSpaces[communityId] ?? []; spacesListIds = spacesList; - for (var spaceId in spacesList) { - print('spaceId===$spaceId'); - } } hasSelectedSpaces = spaceBloc.state.selectedCommunities.any((communityId) { @@ -43,12 +40,12 @@ class LinkSpaceToModelBloc return spacesList.isNotEmpty; }); if (hasSelectedSpaces) { - print("At least one space is selected."); + debugPrint("At least one space is selected."); } else { - print("No spaces selected."); + debugPrint("No spaces selected."); } } catch (e) { - print("Error in _getSpaceIds: $e"); + debugPrint("Error in _getSpaceIds: $e"); } } 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 f67f8b2c..8c6ef3e9 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,235 +1,9 @@ -// 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_to_model_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_model_event.dart'; -import 'package:syncrow_web/pages/spaces_management/link_space_model/bloc/link_space_to_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/overwrite_dialog.dart'; import 'package:syncrow_web/utils/color_manager.dart'; class LinkSpaceModelSpacesDialog extends StatefulWidget { @@ -244,9 +18,6 @@ class LinkSpaceModelSpacesDialog extends StatefulWidget { class _LinkSpaceModelSpacesDialogState extends State { - final TextEditingController _searchController = TextEditingController(); - bool _isLoading = false; - @override void initState() { context.read().add(LinkSpaceModelSelectedIdsEvent()); @@ -420,14 +191,15 @@ Widget _buildDetailRow(String label, String value) { Expanded( child: Text( label, - style: TextStyle(fontWeight: FontWeight.bold), + style: const TextStyle(fontWeight: FontWeight.bold), ), ), const SizedBox(width: 8), Expanded( child: Text( value, - style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black), + style: const TextStyle( + fontWeight: FontWeight.bold, color: Colors.black), ), ), ], diff --git a/lib/services/space_model_mang_api.dart b/lib/services/space_model_mang_api.dart index d4fa06f4..5253c73e 100644 --- a/lib/services/space_model_mang_api.dart +++ b/lib/services/space_model_mang_api.dart @@ -2,7 +2,6 @@ import 'package:syncrow_web/pages/spaces_management/space_model/models/create_sp import 'package:syncrow_web/pages/spaces_management/space_model/models/space_template_model.dart'; import 'package:syncrow_web/services/api/http_service.dart'; import 'package:syncrow_web/utils/constants/api_const.dart'; -import 'package:syncrow_web/utils/constants/temp_const.dart'; class SpaceModelManagementApi { Future> listSpaceModels( @@ -66,11 +65,6 @@ class SpaceModelManagementApi { 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) @@ -93,6 +87,7 @@ class SpaceModelManagementApi { expectedResponseModel: (json) { return json; }); + return response; } Future deleteSpaceModel(String spaceModelUuid, String projectId) async { From 1b8a87e942e9bf8376d1aaff90f939a52145bc22 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 7 Mar 2025 00:01:35 +0400 Subject: [PATCH 19/21] initcommunity on save space --- .../all_spaces/bloc/space_management_bloc.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 337e9950..47fa5508 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 @@ -483,7 +483,7 @@ class SpaceManagementBloc extends Bloc Date: Fri, 7 Mar 2025 11:18:36 +0400 Subject: [PATCH 20/21] resolved conflict --- assets/images/delete_space_link_icon.svg | 21 ++++++++ assets/images/space_link_icon.svg | 25 ++++++++++ assets/images/success_icon.svg | 3 ++ lib/utils/constants/api_const.dart | 62 ++++++++---------------- 4 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 assets/images/delete_space_link_icon.svg create mode 100644 assets/images/space_link_icon.svg create mode 100644 assets/images/success_icon.svg diff --git a/assets/images/delete_space_link_icon.svg b/assets/images/delete_space_link_icon.svg new file mode 100644 index 00000000..a55d2e04 --- /dev/null +++ b/assets/images/delete_space_link_icon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/space_link_icon.svg b/assets/images/space_link_icon.svg new file mode 100644 index 00000000..f10c57ad --- /dev/null +++ b/assets/images/space_link_icon.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/success_icon.svg b/assets/images/success_icon.svg new file mode 100644 index 00000000..6f5dbf9e --- /dev/null +++ b/assets/images/success_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/lib/utils/constants/api_const.dart b/lib/utils/constants/api_const.dart index c7f0bf3a..5aa188d9 100644 --- a/lib/utils/constants/api_const.dart +++ b/lib/utils/constants/api_const.dart @@ -9,19 +9,15 @@ 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'; @@ -43,45 +39,32 @@ 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'; @@ -92,8 +75,7 @@ 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}'; @@ -109,12 +91,11 @@ abstract class ApiEndpoints { static const String updateSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}'; //tag static const String listTags = '/projects/{projectId}/tags'; - + static const String linkSpaceModel = '/projects/{projectId}/space-models/{spaceModelUuid}/spaces/link'; - static const String validateSpaceModel = - '/projects/{projectId}/spaces/validate'; + static const String validateSpaceModel = '/projects/{projectId}/spaces/validate'; static const String roleTypes = '/role/types'; static const String permission = '/permission/{roleUuid}'; @@ -125,8 +106,7 @@ abstract class ApiEndpoints { static const String getUserById = '/projects/{projectId}/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}'; From 788d36541b65f182d8c9c981d1cd3e6fb5d697a4 Mon Sep 17 00:00:00 2001 From: hannathkadher Date: Fri, 7 Mar 2025 11:19:46 +0400 Subject: [PATCH 21/21] revert settings.json --- .vscode/settings.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6ec78043..b87628bd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,4 @@ { - "html.format.extraLiners": "", - "editor.rulers": [100], - "html.format.contentUnformatted": "", - "dart.lineLength": 100, - "files.autoSave": "afterDelay", "cSpell.words": [ "automations" ]