import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/svg.dart'; import 'package:syncrow_web/pages/common/buttons/add_space_button.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_bloc.dart'; import 'package:syncrow_web/pages/spaces_management/bloc/space_management_event.dart'; import 'package:syncrow_web/pages/spaces_management/model/product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/selected_product_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/space_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/create_space_dialog.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/curved_line_painter.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/dialogs/delete_dialogue.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_card_widget.dart'; import 'package:syncrow_web/pages/spaces_management/model/community_model.dart'; import 'package:syncrow_web/pages/spaces_management/model/connection_model.dart'; import 'package:syncrow_web/pages/spaces_management/widgets/space_container_widget.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; class CommunityStructureArea extends StatefulWidget { final CommunityModel? selectedCommunity; final SpaceModel? selectedSpace; final List? products; final List spaces; final List connections; CommunityStructureArea({ this.selectedCommunity, this.selectedSpace, this.products, required this.spaces, required this.connections, }); @override _CommunityStructureAreaState createState() => _CommunityStructureAreaState(); } class _CommunityStructureAreaState extends State { double canvasWidth = 1000; double canvasHeight = 1000; List spaces = []; List connections = []; late TextEditingController _nameController; bool isEditingName = false; @override void initState() { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); _nameController = TextEditingController( text: widget.selectedCommunity?.name ?? '', ); } @override void dispose() { _nameController.dispose(); super.dispose(); } @override void didUpdateWidget(covariant CommunityStructureArea oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.spaces != widget.spaces || oldWidget.connections != widget.connections) { setState(() { spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); }); } } @override Widget build(BuildContext context) { final visibleSpaces = flattenSpaces(widget.spaces); final visibleConnections = createConnections(widget.spaces); Size screenSize = MediaQuery.of(context).size; return Expanded( child: Container( decoration: const BoxDecoration( border: Border( left: BorderSide(color: ColorsManager.whiteColors, width: 1.0), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), Flexible( child: Stack( children: [ if (visibleSpaces.isNotEmpty) InteractiveViewer( boundaryMargin: EdgeInsets.all(500), minScale: 0.5, maxScale: 3.0, constrained: false, child: Container( width: canvasWidth, height: canvasHeight, child: Stack( children: [ for (var connection in connections) CustomPaint(painter: CurvedLinePainter([connection])), for (var entry in visibleSpaces.asMap().entries) Positioned( left: entry.value.position.dx, top: entry.value.position.dy, child: SpaceCardWidget( index: entry.key, onButtonTap: (int index, Offset newPosition, String direction) { _showCreateSpaceDialog( screenSize, position: visibleSpaces[index].position + newPosition, parentIndex: index, direction: direction, ); }, position: entry.value.position, isHovered: entry.value.isHovered, screenSize: screenSize, onHoverChanged: _handleHoverChanged, onPositionChanged: (newPosition) { _updateNodePosition(entry.value, newPosition); }, buildSpaceContainer: (int index) { return SpaceContainerWidget( index: index, onDoubleTap: () { _showEditSpaceDialog(visibleSpaces[index]); }, icon: visibleSpaces[index].icon ?? '', name: visibleSpaces[index].name, ); }, ), ), ], ), ), ), if (visibleSpaces.isEmpty) Center( child: AddSpaceButton( onTap: () { _showCreateSpaceDialog(screenSize); }, ), ), ], )), ], ), ), ); } Widget _buildHeader() { return Container( padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 27.0), width: double.infinity, decoration: BoxDecoration( color: ColorsManager.whiteColors, boxShadow: [ BoxShadow( color: ColorsManager.shadowBlackColor, spreadRadius: 0, blurRadius: 8, offset: const Offset(0, 4), ), ], ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Community Structure', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), if (widget.selectedCommunity != null) Row( children: [ // Show Text widget when not editing if (!isEditingName) Text( widget.selectedCommunity?.name ?? '', style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), ), if (isEditingName) // Show TextField when editing SizedBox( width: 200, // Adjusted width to make TextField visible child: TextField( controller: _nameController, decoration: const InputDecoration( border: InputBorder.none, isDense: true, // Reduce the height of the TextField ), style: const TextStyle( fontSize: 16, color: ColorsManager.blackColor, ), onSubmitted: (value) { context.read().add( UpdateCommunityEvent( communityUuid: widget.selectedCommunity!.uuid, name: value, ), ); setState(() { widget.selectedCommunity?.name = value; // Update the name isEditingName = false; // Exit edit mode }); }, ), ), const SizedBox(width: 8), if (!isEditingName) GestureDetector( onTap: () { setState(() { isEditingName = !isEditingName; // Toggle edit mode }); if (isEditingName) { _nameController.text = widget.selectedCommunity?.name ?? ''; // Pre-fill } }, child: SvgPicture.asset( Assets.iconEdit, // Path to the edit icon SVG asset width: 16, height: 16, ), ), ], ), ], ), // Show "Save" button only if there are spaces if (widget.spaces.isNotEmpty && widget.selectedCommunity != null) Row(children: [ ElevatedButton.icon( onPressed: () { _saveSpaces(); }, icon: const Icon( Icons.save, size: 18, color: ColorsManager.spaceColor, ), label: const Text("Save"), style: ElevatedButton.styleFrom( backgroundColor: ColorsManager.textFieldGreyColor, foregroundColor: ColorsManager.blackColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), side: BorderSide(color: Colors.grey.shade300), elevation: 0, ), ), const SizedBox(width: 10), ElevatedButton.icon( onPressed: () { showDeleteConfirmationDialog(context, () { // Handle the delete action here Navigator.of(context).pop(); // Close the dialog after confirming _onDelete(); }, widget.selectedSpace != null); }, icon: SvgPicture.asset( Assets.delete, // Path to your SVG asset width: 18, // Adjust width as needed height: 18, // Adjust height as needed ), label: const Text("Delete"), style: ElevatedButton.styleFrom( backgroundColor: ColorsManager.textFieldGreyColor, foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), side: BorderSide(color: ColorsManager.neutralGray), elevation: 0, ), ), ]) ], ), ); } void _updateNodePosition(SpaceModel node, Offset newPosition) { setState(() { node.position = newPosition; if (node.status != SpaceStatus.newSpace) { node.status = SpaceStatus.modified; // Mark as modified } if (node.position.dx >= canvasWidth - 200) { canvasWidth += 200; } if (node.position.dy >= canvasHeight - 200) { canvasHeight += 200; } if (node.position.dx <= 200) { double shiftAmount = 200; canvasWidth += shiftAmount; for (var n in spaces) { n.position = Offset(n.position.dx + shiftAmount, n.position.dy); } } if (node.position.dy < 0) { node.position = Offset(node.position.dx, 0); } }); } void _adjustCanvasSizeForSpaces() { for (var space in spaces) { if (space.position.dx >= canvasWidth - 200) { canvasWidth = space.position.dx + 200; } if (space.position.dy >= canvasHeight - 200) { canvasHeight = space.position.dy + 200; } } } void _showCreateSpaceDialog(Size screenSize, {Offset? position, int? parentIndex, String? direction}) { showDialog( context: context, builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, onCreateSpace: (String name, String icon, List selectedProducts) { setState(() { // Set the first space in the center or use passed position Offset centerPosition = position ?? Offset( screenSize.width / 2 - 75, // Center horizontally screenSize.height / 2 - 50, // Slightly above the center vertically ); SpaceModel newSpace = SpaceModel( name: name, icon: icon, position: centerPosition, isPrivate: false, children: [], status: SpaceStatus.newSpace, selectedProducts: selectedProducts); if (parentIndex != null && direction != null) { SpaceModel parentSpace = spaces[parentIndex]; newSpace.parent = parentSpace; final newConnection = Connection( startSpace: parentSpace, endSpace: newSpace, direction: direction, ); connections.add(newConnection); newSpace.incomingConnection = newConnection; parentSpace.addOutgoingConnection(newConnection); parentSpace.children.add(newSpace); } spaces.add(newSpace); _updateNodePosition(newSpace, newSpace.position); }); }, ); }, ); } void _showEditSpaceDialog(SpaceModel space) { showDialog( context: context, builder: (BuildContext context) { return CreateSpaceDialog( products: widget.products, name: space.name, icon: space.icon, isEdit: true, selectedProducts: space.selectedProducts, onCreateSpace: (String name, String icon, List selectedProducts) { setState(() { // Update the space's properties space.name = name; space.icon = icon; space.selectedProducts = selectedProducts; if (space.status != SpaceStatus.newSpace) { space.status = SpaceStatus.modified; // Mark as modified } }); }, // Pre-fill the dialog with current space data key: Key(space.name), // Add a unique key to ensure dialog refresh ); }, ); } void _handleHoverChanged(int index, bool isHovered) { setState(() { spaces[index].isHovered = isHovered; }); } List flattenSpaces(List spaces) { List result = []; void flatten(SpaceModel space) { if (space.status == SpaceStatus.deleted) return; result.add(space); for (var child in space.children) { flatten(child); } } for (var space in spaces) { flatten(space); } return result; } List createConnections(List spaces) { List connections = []; void addConnections(SpaceModel parent, String direction) { if (parent.status == SpaceStatus.deleted) return; for (var child in parent.children) { if (child.status == SpaceStatus.deleted) continue; // Create a connection object connections.add( Connection( startSpace: parent, endSpace: child, direction: child.incomingConnection?.direction ?? "down", // Assuming "down" for all direct children ), ); // Recursively process the child's children addConnections(child, direction); } } for (var space in spaces) { addConnections(space, "down"); } return connections; } void _saveSpaces() { if (widget.selectedCommunity == null) { debugPrint("No community selected for saving spaces."); return; } List spacesToSave = spaces.where((space) { return space.status == SpaceStatus.newSpace || space.status == SpaceStatus.modified || space.status == SpaceStatus.deleted; }).toList(); if (spacesToSave.isEmpty) { debugPrint("No new or modified spaces to save."); return; } String communityUuid = widget.selectedCommunity!.uuid; context.read().add(SaveSpacesEvent( spaces: spacesToSave, communityUuid: communityUuid, )); } void _onDelete() { if (widget.selectedCommunity != null && widget.selectedCommunity?.uuid != null && widget.selectedSpace == null) { context.read().add(DeleteCommunityEvent( communityUuid: widget.selectedCommunity!.uuid, )); } if (widget.selectedSpace != null) { setState(() { for (var space in spaces) { if (space.uuid == widget.selectedSpace?.uuid) { space.status = SpaceStatus.deleted; _markChildrenAsDeleted(space); } } _removeConnectionsForDeletedSpaces(); }); } } void _markChildrenAsDeleted(SpaceModel parent) { for (var child in parent.children) { child.status = SpaceStatus.deleted; _markChildrenAsDeleted(child); } } void _removeConnectionsForDeletedSpaces() { connections.removeWhere((connection) { return connection.startSpace.status == SpaceStatus.deleted || connection.endSpace.status == SpaceStatus.deleted; }); } }