Added group for the right side panel and improved get post patch delete ui for requests
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
19
Examples/Nodify.Calculator/UpperCaseConverter.cs
Normal file
19
Examples/Nodify.Calculator/UpperCaseConverter.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user