Added group for the right side panel and improved get post patch delete ui for requests

This commit is contained in:
Ankitkumar Satapara
2026-04-18 23:55:40 +05:30
parent 9c3241cea4
commit 1eb917b450
7 changed files with 194 additions and 33 deletions

View File

@@ -91,6 +91,7 @@
</Storyboard>
<local:ItemToListConverter x:Key="ItemToListConverter" />
<local:UpperCaseConverter x:Key="UpperCaseConverter" />
<DataTemplate x:Key="ConnectionTemplate"
DataType="{x:Type local:ConnectionViewModel}">
@@ -590,8 +591,8 @@
<Border HorizontalAlignment="Right"
MinWidth="200"
MaxWidth="300"
MaxHeight="500"
MaxWidth="350"
MaxHeight="600"
Padding="7"
Margin="10"
CornerRadius="3"
@@ -612,18 +613,16 @@
FontWeight="Bold"
DockPanel.Dock="Top" />
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableOperations}"
<StackPanel>
<!-- System nodes (non-API) -->
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.SystemNodes}"
Focusable="False">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin"
Value="5" />
<Setter Property="FrameworkElement.HorizontalAlignment"
Value="Left" />
<Setter Property="FrameworkElement.Cursor"
Value="Hand" />
<Setter Property="FrameworkElement.ToolTip"
Value="Drag and drop into the editor" />
<Setter Property="FrameworkElement.Margin" Value="5" />
<Setter Property="FrameworkElement.HorizontalAlignment" Value="Left" />
<Setter Property="FrameworkElement.Cursor" Value="Hand" />
<Setter Property="FrameworkElement.ToolTip" Value="Drag and drop into the editor" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
@@ -640,6 +639,76 @@
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- Grouped Swagger API operations -->
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.GroupedSwaggerOperations}"
Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Expander IsExpanded="True" Margin="0 6 0 0">
<Expander.Header>
<TextBlock Text="{Binding GroupName}"
FontWeight="Bold"
FontSize="15"
Foreground="{DynamicResource ForegroundBrush}" />
</Expander.Header>
<ItemsControl ItemsSource="{Binding Operations}" Focusable="False" Margin="4 2 0 0">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="2 3" Padding="5 4" CornerRadius="4"
Cursor="Hand"
MouseMove="OnSwaggerItemDrag"
ToolTip="Drag and drop into the editor"
Tag="{Binding}">
<Border.Background>
<SolidColorBrush Color="{DynamicResource BackgroundColor}" Opacity="0.5" />
</Border.Background>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Border CornerRadius="3" Padding="6 2" Margin="0 0 6 0" MinWidth="50"
HorizontalAlignment="Left">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#61AFFE" />
<Style.Triggers>
<DataTrigger Binding="{Binding OPType}" Value="post">
<Setter Property="Background" Value="#49CC90" />
</DataTrigger>
<DataTrigger Binding="{Binding OPType}" Value="put">
<Setter Property="Background" Value="#FCA130" />
</DataTrigger>
<DataTrigger Binding="{Binding OPType}" Value="delete">
<Setter Property="Background" Value="#F93E3E" />
</DataTrigger>
<DataTrigger Binding="{Binding OPType}" Value="patch">
<Setter Property="Background" Value="#50E3C2" />
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock FontWeight="Bold" Foreground="White"
HorizontalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding OPType, Converter={StaticResource UpperCaseConverter}}" />
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<TextBlock Text="{Binding Title}"
Foreground="{DynamicResource ForegroundBrush}"
VerticalAlignment="Center"
TextTrimming="CharacterEllipsis"
MaxWidth="220" />
</StackPanel>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Expander>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</DockPanel>
</Border>

View File

@@ -83,6 +83,15 @@ namespace Nodify.Calculator
}
}
private void OnSwaggerItemDrag(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && sender is FrameworkElement fe && fe.Tag is OperationInfoViewModel operation)
{
var data = new DataObject(typeof(OperationInfoViewModel), operation);
DragDrop.DoDragDrop(this, data, DragDropEffects.Copy);
}
}
private void OpenContextMenu_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Source is NodifyEditor editor && editor.DataContext is CalculatorViewModel calculator)

View File

@@ -0,0 +1,8 @@
namespace Nodify.Calculator
{
public class SwaggerGroupViewModel
{
public string GroupName { get; set; } = string.Empty;
public NodifyObservableCollection<OperationInfoViewModel> Operations { get; set; } = new NodifyObservableCollection<OperationInfoViewModel>();
}
}

View File

@@ -10,5 +10,6 @@ namespace Nodify.Calculator.Models
public List<string> InputNames { get; set; } = new List<string>();
public string SwaggerFileName { get; set; } = string.Empty;
public string ResponseModelClassName { get; set; } = string.Empty;
public string SwaggerGroup { get; set; } = string.Empty;
}
}

View File

@@ -36,5 +36,6 @@ namespace Nodify.Calculator
public List<FunctionParameterInfo> FunctionInputs { get; set; } = new List<FunctionParameterInfo>();
public List<FunctionParameterInfo> FunctionOutputs { get; set; } = new List<FunctionParameterInfo>();
public string ResponseModelClassName { get; set; } = string.Empty;
public string SwaggerGroup { get; set; } = string.Empty;
}
}

View File

@@ -114,6 +114,12 @@ namespace Nodify.Calculator
public NodifyObservableCollection<OperationInfoViewModel> AvailableModels { get; }
public NodifyObservableCollection<OperationInfoViewModel> AvailableVariables { get; }
public NodifyObservableCollection<OperationInfoViewModel> AvailableFunctions { get; }
/// <summary>Swagger operations grouped by SwaggerGroup (tag), for the right-side panel.</summary>
public NodifyObservableCollection<SwaggerGroupViewModel> GroupedSwaggerOperations { get; } = new NodifyObservableCollection<SwaggerGroupViewModel>();
/// <summary>Non-API system nodes for the right-side panel.</summary>
public NodifyObservableCollection<OperationInfoViewModel> SystemNodes { get; } = new NodifyObservableCollection<OperationInfoViewModel>();
public INodifyCommand CreateOperationCommand { get; }
[Newtonsoft.Json.JsonIgnore]
@@ -150,8 +156,13 @@ namespace Nodify.Calculator
operations.AddRange(OperationFactory.GetSystemNodes());
//operations.AddRange(OperationFactory.GetOperationsInfo(typeof(OperationsContainer)));
// Populate system-only nodes for the right panel
foreach (var sysNode in operations)
SystemNodes.Add(sysNode);
SwaggerOperations = new NodifyObservableCollection<OperationInfoViewModel>();
LoadSwaggerNodesFromDb();
RebuildGroupedSwaggerOperations();
operations.AddRange(SwaggerOperations);
@@ -344,6 +355,8 @@ namespace Nodify.Calculator
MenuAvailableOperations.Add(node);
}
RebuildGroupedSwaggerOperations();
MessageBox.Show($"Successfully imported {nodes.Count} API endpoints from Swagger.", "Import Swagger", MessageBoxButton.OK, MessageBoxImage.Information);
}
catch (Exception ex)
@@ -362,11 +375,18 @@ namespace Nodify.Calculator
{
foreach (var method in path.Value)
{
// Determine swagger group from operation tags or path segments
var tag = method.Value.Tags?.FirstOrDefault();
var group = !string.IsNullOrEmpty(tag)
? tag
: ExtractGroupFromPath(path.Key);
var ovmodel = new OperationInfoViewModel
{
Title = path.Key,
OPType = method.Key,
Type = OperationType.API
Type = OperationType.API,
SwaggerGroup = group
};
var addedParams = new HashSet<string>();
@@ -522,6 +542,38 @@ namespace Nodify.Calculator
return new string(name.Where(c => char.IsLetterOrDigit(c) || c == '_').ToArray());
}
private static string ExtractGroupFromPath(string path)
{
// Extract group from path like "/api/Pizza/{id}" -> "Pizza"
var segments = path.Split('/', System.StringSplitOptions.RemoveEmptyEntries);
// Skip "api" prefix if present, take the first meaningful segment
foreach (var seg in segments)
{
if (seg.StartsWith("{") || string.Equals(seg, "api", System.StringComparison.OrdinalIgnoreCase))
continue;
// Capitalize first letter
return char.ToUpper(seg[0]) + seg.Substring(1);
}
return "Other";
}
private void RebuildGroupedSwaggerOperations()
{
GroupedSwaggerOperations.Clear();
var groups = SwaggerOperations
.GroupBy(op => string.IsNullOrEmpty(op.SwaggerGroup) ? "Other" : op.SwaggerGroup)
.OrderBy(g => g.Key);
foreach (var group in groups)
{
GroupedSwaggerOperations.Add(new SwaggerGroupViewModel
{
GroupName = group.Key,
Operations = new NodifyObservableCollection<OperationInfoViewModel>(group.ToList())
});
}
}
private void SaveSwaggerNodesToDb(List<OperationInfoViewModel> nodes, string swaggerFileName)
{
using var db = new LiteDbHelper<SwaggerNodeModel>("SwaggerNodes");
@@ -535,7 +587,8 @@ namespace Nodify.Calculator
OPType = node.OPType ?? string.Empty,
InputNames = new List<string>(node.Input),
SwaggerFileName = swaggerFileName,
ResponseModelClassName = node.ResponseModelClassName ?? string.Empty
ResponseModelClassName = node.ResponseModelClassName ?? string.Empty,
SwaggerGroup = node.SwaggerGroup ?? string.Empty
});
}
}
@@ -553,7 +606,8 @@ namespace Nodify.Calculator
{
Title = saved.Title,
OPType = saved.OPType,
Type = OperationType.API
Type = OperationType.API,
SwaggerGroup = saved.SwaggerGroup ?? string.Empty
};
foreach (var inputName in saved.InputNames)

View File

@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Nodify.Calculator
{
public class UpperCaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is string s ? s.ToUpper() : value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}