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>
|
</Storyboard>
|
||||||
|
|
||||||
<local:ItemToListConverter x:Key="ItemToListConverter" />
|
<local:ItemToListConverter x:Key="ItemToListConverter" />
|
||||||
|
<local:UpperCaseConverter x:Key="UpperCaseConverter" />
|
||||||
|
|
||||||
<DataTemplate x:Key="ConnectionTemplate"
|
<DataTemplate x:Key="ConnectionTemplate"
|
||||||
DataType="{x:Type local:ConnectionViewModel}">
|
DataType="{x:Type local:ConnectionViewModel}">
|
||||||
@@ -590,8 +591,8 @@
|
|||||||
|
|
||||||
<Border HorizontalAlignment="Right"
|
<Border HorizontalAlignment="Right"
|
||||||
MinWidth="200"
|
MinWidth="200"
|
||||||
MaxWidth="300"
|
MaxWidth="350"
|
||||||
MaxHeight="500"
|
MaxHeight="600"
|
||||||
Padding="7"
|
Padding="7"
|
||||||
Margin="10"
|
Margin="10"
|
||||||
CornerRadius="3"
|
CornerRadius="3"
|
||||||
@@ -612,34 +613,102 @@
|
|||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
DockPanel.Dock="Top" />
|
DockPanel.Dock="Top" />
|
||||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||||
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.AvailableOperations}"
|
<StackPanel>
|
||||||
Focusable="False">
|
<!-- System nodes (non-API) -->
|
||||||
<ItemsControl.ItemContainerStyle>
|
<ItemsControl ItemsSource="{Binding Calculator.OperationsMenu.SystemNodes}"
|
||||||
<Style>
|
Focusable="False">
|
||||||
<Setter Property="FrameworkElement.Margin"
|
<ItemsControl.ItemContainerStyle>
|
||||||
Value="5" />
|
<Style>
|
||||||
<Setter Property="FrameworkElement.HorizontalAlignment"
|
<Setter Property="FrameworkElement.Margin" Value="5" />
|
||||||
Value="Left" />
|
<Setter Property="FrameworkElement.HorizontalAlignment" Value="Left" />
|
||||||
<Setter Property="FrameworkElement.Cursor"
|
<Setter Property="FrameworkElement.Cursor" Value="Hand" />
|
||||||
Value="Hand" />
|
<Setter Property="FrameworkElement.ToolTip" Value="Drag and drop into the editor" />
|
||||||
<Setter Property="FrameworkElement.ToolTip"
|
</Style>
|
||||||
Value="Drag and drop into the editor" />
|
</ItemsControl.ItemContainerStyle>
|
||||||
</Style>
|
<ItemsControl.ItemTemplate>
|
||||||
</ItemsControl.ItemContainerStyle>
|
<DataTemplate DataType="{x:Type local:OperationViewModel}">
|
||||||
<ItemsControl.ItemTemplate>
|
<nodify:Node Content="{Binding Title}"
|
||||||
<DataTemplate DataType="{x:Type local:OperationViewModel}">
|
Input="{Binding Input}"
|
||||||
<nodify:Node Content="{Binding Title}"
|
Output="{Binding Output}"
|
||||||
Input="{Binding Input}"
|
BorderBrush="{StaticResource AnimatedBrush}"
|
||||||
Output="{Binding Output}"
|
BorderThickness="2"
|
||||||
BorderBrush="{StaticResource AnimatedBrush}"
|
MouseMove="OnNodeDrag"
|
||||||
BorderThickness="2"
|
Focusable="True"
|
||||||
MouseMove="OnNodeDrag"
|
KeyboardNavigation.TabNavigation="None">
|
||||||
Focusable="True"
|
</nodify:Node>
|
||||||
KeyboardNavigation.TabNavigation="None">
|
</DataTemplate>
|
||||||
</nodify:Node>
|
</ItemsControl.ItemTemplate>
|
||||||
</DataTemplate>
|
</ItemsControl>
|
||||||
</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>
|
</ScrollViewer>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</Border>
|
</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)
|
private void OpenContextMenu_Executed(object sender, ExecutedRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.Source is NodifyEditor editor && editor.DataContext is CalculatorViewModel calculator)
|
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 List<string> InputNames { get; set; } = new List<string>();
|
||||||
public string SwaggerFileName { get; set; } = string.Empty;
|
public string SwaggerFileName { get; set; } = string.Empty;
|
||||||
public string ResponseModelClassName { 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> FunctionInputs { get; set; } = new List<FunctionParameterInfo>();
|
||||||
public List<FunctionParameterInfo> FunctionOutputs { get; set; } = new List<FunctionParameterInfo>();
|
public List<FunctionParameterInfo> FunctionOutputs { get; set; } = new List<FunctionParameterInfo>();
|
||||||
public string ResponseModelClassName { get; set; } = string.Empty;
|
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> AvailableModels { get; }
|
||||||
public NodifyObservableCollection<OperationInfoViewModel> AvailableVariables { get; }
|
public NodifyObservableCollection<OperationInfoViewModel> AvailableVariables { get; }
|
||||||
public NodifyObservableCollection<OperationInfoViewModel> AvailableFunctions { 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; }
|
public INodifyCommand CreateOperationCommand { get; }
|
||||||
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
@@ -150,8 +156,13 @@ namespace Nodify.Calculator
|
|||||||
operations.AddRange(OperationFactory.GetSystemNodes());
|
operations.AddRange(OperationFactory.GetSystemNodes());
|
||||||
//operations.AddRange(OperationFactory.GetOperationsInfo(typeof(OperationsContainer)));
|
//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>();
|
SwaggerOperations = new NodifyObservableCollection<OperationInfoViewModel>();
|
||||||
LoadSwaggerNodesFromDb();
|
LoadSwaggerNodesFromDb();
|
||||||
|
RebuildGroupedSwaggerOperations();
|
||||||
|
|
||||||
operations.AddRange(SwaggerOperations);
|
operations.AddRange(SwaggerOperations);
|
||||||
|
|
||||||
@@ -344,6 +355,8 @@ namespace Nodify.Calculator
|
|||||||
MenuAvailableOperations.Add(node);
|
MenuAvailableOperations.Add(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RebuildGroupedSwaggerOperations();
|
||||||
|
|
||||||
MessageBox.Show($"Successfully imported {nodes.Count} API endpoints from Swagger.", "Import Swagger", MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBox.Show($"Successfully imported {nodes.Count} API endpoints from Swagger.", "Import Swagger", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -362,11 +375,18 @@ namespace Nodify.Calculator
|
|||||||
{
|
{
|
||||||
foreach (var method in path.Value)
|
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
|
var ovmodel = new OperationInfoViewModel
|
||||||
{
|
{
|
||||||
Title = path.Key,
|
Title = path.Key,
|
||||||
OPType = method.Key,
|
OPType = method.Key,
|
||||||
Type = OperationType.API
|
Type = OperationType.API,
|
||||||
|
SwaggerGroup = group
|
||||||
};
|
};
|
||||||
|
|
||||||
var addedParams = new HashSet<string>();
|
var addedParams = new HashSet<string>();
|
||||||
@@ -522,6 +542,38 @@ namespace Nodify.Calculator
|
|||||||
return new string(name.Where(c => char.IsLetterOrDigit(c) || c == '_').ToArray());
|
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)
|
private void SaveSwaggerNodesToDb(List<OperationInfoViewModel> nodes, string swaggerFileName)
|
||||||
{
|
{
|
||||||
using var db = new LiteDbHelper<SwaggerNodeModel>("SwaggerNodes");
|
using var db = new LiteDbHelper<SwaggerNodeModel>("SwaggerNodes");
|
||||||
@@ -535,7 +587,8 @@ namespace Nodify.Calculator
|
|||||||
OPType = node.OPType ?? string.Empty,
|
OPType = node.OPType ?? string.Empty,
|
||||||
InputNames = new List<string>(node.Input),
|
InputNames = new List<string>(node.Input),
|
||||||
SwaggerFileName = swaggerFileName,
|
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,
|
Title = saved.Title,
|
||||||
OPType = saved.OPType,
|
OPType = saved.OPType,
|
||||||
Type = OperationType.API
|
Type = OperationType.API,
|
||||||
|
SwaggerGroup = saved.SwaggerGroup ?? string.Empty
|
||||||
};
|
};
|
||||||
|
|
||||||
foreach (var inputName in saved.InputNames)
|
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