import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.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/view/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/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'; 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 = []; @override void initState() { super.initState(); spaces = widget.spaces.isNotEmpty ? flattenSpaces(widget.spaces) : []; connections = widget.spaces.isNotEmpty ? createConnections(widget.spaces) : []; _adjustCanvasSizeForSpaces(); } @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) { 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 (spaces.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 spaces.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: spaces[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(spaces[index]); }, icon: spaces[index].icon ?? '', name: spaces[index].name, ); }, ), ), ], ), ), ), if (spaces.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) Text( widget.selectedCommunity?.name ?? '', style: const TextStyle(fontSize: 16, color: ColorsManager.blackColor), ), ], ), // Show "Save" button only if there are spaces if (spaces.isNotEmpty && widget.selectedCommunity != null) ElevatedButton.icon( onPressed: () { _saveSpaces(); }, icon: const Icon(Icons.save, size: 18), label: const Text("Save"), style: ElevatedButton.styleFrom( backgroundColor: ColorsManager.whiteColors, foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8.0), ), padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), side: BorderSide(color: Colors.grey.shade300), 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) { // Add the current space to the result result.add(space); // Recursively flatten child spaces for (var child in space.children) { flatten(child); } } // Process each top-level space for (var space in spaces) { flatten(space); } return result; } List createConnections(List spaces) { List connections = []; void addConnections(SpaceModel parent, String direction) { for (var child in parent.children) { // 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); } } // Process each top-level space for (var space in spaces) { addConnections(space, "down"); } return connections; } void _saveSpaces() { if (widget.selectedCommunity == null) { print("No community selected for saving spaces."); return; } List spacesToSave = spaces.where((space) { return space.status == SpaceStatus.newSpace || space.status == SpaceStatus.modified; }).toList(); if (spacesToSave.isEmpty) { print("No new or modified spaces to save."); return; } String communityUuid = widget.selectedCommunity!.uuid; context.read().add(SaveSpacesEvent( spaces: spacesToSave, communityUuid: communityUuid, )); } }