Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions src/PlanViewer.App/Controls/QueryStoreGridControl.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
xmlns:local="using:PlanViewer.App.Controls"
x:Class="PlanViewer.App.Controls.QueryStoreGridControl"
Background="{DynamicResource BackgroundBrush}">
<Grid RowDefinitions="Auto,*">
<Grid RowDefinitions="Auto,Auto,*">
<!-- Time Range Slicer -->
<local:TimeRangeSlicerControl x:Name="TimeRangeSlicer" Grid.Row="0"/>

<!-- Toolbar -->
<Border Grid.Row="0" Background="{DynamicResource BackgroundDarkBrush}" Padding="8,6"
<Border Grid.Row="1" Background="{DynamicResource BackgroundDarkBrush}" Padding="8,6"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<StackPanel Orientation="Horizontal" Spacing="12">
<StackPanel Spacing="4">
Expand All @@ -27,12 +30,6 @@
Width="120" Height="36" FontSize="14" FormatString="0"
HorizontalContentAlignment="Center"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Hours back" Foreground="{DynamicResource ForegroundBrush}" FontSize="11"/>
<NumericUpDown x:Name="HoursBackBox" Value="24" Minimum="1" Maximum="168"
Width="120" Height="36" FontSize="14" FormatString="0"
HorizontalContentAlignment="Center"/>
</StackPanel>
<StackPanel Spacing="4">
<TextBlock Text="Order by" Foreground="{DynamicResource ForegroundBrush}" FontSize="11"/>
<ComboBox x:Name="OrderByBox" Width="160" Height="36" FontSize="13"
Expand Down Expand Up @@ -86,7 +83,7 @@
</Border>

<!-- DataGrid -->
<DataGrid Grid.Row="1" x:Name="ResultsGrid"
<DataGrid Grid.Row="2" x:Name="ResultsGrid"
AutoGenerateColumns="False"
CanUserSortColumns="True"
CanUserReorderColumns="True"
Expand Down
45 changes: 38 additions & 7 deletions src/PlanViewer.App/Controls/QueryStoreGridControl.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public partial class QueryStoreGridControl : UserControl
private ColumnFilterPopup? _filterPopupContent;
private string? _sortedColumnTag;
private bool _sortAscending;
private DateTime? _slicerStartUtc;
private DateTime? _slicerEndUtc;

public event EventHandler<List<QueryStorePlan>>? PlansSelected;
public event EventHandler<string>? DatabaseChanged;
Expand All @@ -50,6 +52,11 @@ public QueryStoreGridControl(ServerConnection serverConnection, ICredentialServi
EnsureFilterPopup();
SetupColumnHeaders();
PopulateDatabaseBox(databases, initialDatabase);
TimeRangeSlicer.RangeChanged += OnTimeRangeChanged;
TimeRangeSlicer.IsExpanded = true;

// Auto-fetch with default settings on connect
Avalonia.Threading.Dispatcher.UIThread.Post(() => Fetch_Click(null, new RoutedEventArgs()), Avalonia.Threading.DispatcherPriority.Loaded);
}

private void PopulateDatabaseBox(List<string> databases, string selectedDatabase)
Expand Down Expand Up @@ -102,7 +109,6 @@ private async void Fetch_Click(object? sender, RoutedEventArgs e)
var ct = _fetchCts.Token;

var topN = (int)(TopNBox.Value ?? 25);
var hoursBack = (int)(HoursBackBox.Value ?? 24);
var orderBy = (OrderByBox.SelectedItem as ComboBoxItem)?.Tag?.ToString() ?? "cpu";
var filter = BuildSearchFilter();

Expand All @@ -115,7 +121,7 @@ private async void Fetch_Click(object? sender, RoutedEventArgs e)
try
{
var plans = await QueryStoreService.FetchTopPlansAsync(
_connectionString, topN, orderBy, hoursBack, filter, ct);
_connectionString, topN, orderBy, 8760, filter, ct);

if (plans.Count == 0)
{
Expand All @@ -129,6 +135,9 @@ private async void Fetch_Click(object? sender, RoutedEventArgs e)
ApplyFilters();
LoadButton.IsEnabled = true;
SelectToggleButton.Content = "Select None";

// Load time-slicer data in the background
_ = LoadTimeSlicerDataAsync(orderBy, ct);
}
catch (OperationCanceledException)
{
Expand Down Expand Up @@ -200,6 +209,24 @@ private void ClearSearch_Click(object? sender, RoutedEventArgs e)
SearchValueBox.Text = "";
}

private async System.Threading.Tasks.Task LoadTimeSlicerDataAsync(string metric, CancellationToken ct)
{
try
{
var sliceData = await QueryStoreService.FetchTimeSliceDataAsync(_connectionString, metric, ct);
if (!ct.IsCancellationRequested && sliceData.Count > 0)
TimeRangeSlicer.LoadData(sliceData, metric);
}
catch { /* non-critical — slicer just stays empty */ }
}

private void OnTimeRangeChanged(object? sender, TimeRangeChangedEventArgs e)
{
_slicerStartUtc = e.StartUtc;
_slicerEndUtc = e.EndUtc;
ApplySortAndFilters();
}

private void SelectToggle_Click(object? sender, RoutedEventArgs e)
{
var allSelected = _filteredRows.Count > 0 && _filteredRows.All(r => r.IsSelected);
Expand All @@ -225,14 +252,11 @@ private async void ViewHistory_Click(object? sender, RoutedEventArgs e)
{
if (ResultsGrid.SelectedItem is not QueryStoreRow row) return;

var hoursBack = (int)(HoursBackBox.Value ?? 24);

var window = new QueryStoreHistoryWindow(
_connectionString,
row.QueryId,
row.FullQueryText,
_database,
hoursBack);
_database);

var topLevel = Avalonia.Controls.TopLevel.GetTopLevel(this);
if (topLevel is Window parentWindow)
Expand Down Expand Up @@ -581,14 +605,21 @@ private void ApplySortAndFilters()
{
IEnumerable<QueryStoreRow> source = _rows.Where(RowMatchesAllFilters);

// Apply time-range slicer filter
if (_slicerStartUtc.HasValue && _slicerEndUtc.HasValue)
{
var start = _slicerStartUtc.Value;
var end = _slicerEndUtc.Value;
source = source.Where(r => r.Plan.LastExecutedUtc >= start && r.Plan.LastExecutedUtc <= end);
}

if (_sortedColumnTag != null)
{
source = _sortAscending
? source.OrderBy(r => GetSortKey(_sortedColumnTag, r))
: source.OrderByDescending(r => GetSortKey(_sortedColumnTag, r));
}


_filteredRows.Clear();
foreach (var row in source)
_filteredRows.Add(row);
Expand Down
41 changes: 41 additions & 0 deletions src/PlanViewer.App/Controls/TimeRangeSlicerControl.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PlanViewer.App.Controls.TimeRangeSlicerControl">
<Border Background="{DynamicResource SlicerBackgroundBrush}"
BorderBrush="{DynamicResource SlicerBorderBrush}"
BorderThickness="0,0,0,1">
<Grid RowDefinitions="Auto,Auto">
<!-- Toggle header -->
<Button x:Name="ToggleButton" Grid.Row="0"
Background="Transparent" BorderThickness="0"
HorizontalAlignment="Stretch" HorizontalContentAlignment="Left"
Padding="8,4" Cursor="Hand"
Click="Toggle_Click">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Name="ToggleIcon" Text="▾" FontSize="12"
Foreground="{DynamicResource SlicerToggleBrush}"
VerticalAlignment="Center"/>
<TextBlock x:Name="ToggleLabel" Text="Time Range"
FontSize="11" FontWeight="SemiBold"
Foreground="{DynamicResource SlicerToggleBrush}"
VerticalAlignment="Center"/>
<TextBlock x:Name="RangeLabel" Text=""
FontSize="11"
Foreground="{DynamicResource SlicerLabelBrush}"
VerticalAlignment="Center" Margin="12,0,0,0"/>
</StackPanel>
</Button>
<!-- Slicer canvas area -->
<Border x:Name="SlicerBorder" Grid.Row="1" Height="120" Margin="8,0,8,6"
Background="{DynamicResource SlicerBackgroundBrush}"
CornerRadius="4" ClipToBounds="True">
<Canvas x:Name="SlicerCanvas"
Background="Transparent"
PointerPressed="Canvas_PointerPressed"
PointerMoved="Canvas_PointerMoved"
PointerReleased="Canvas_PointerReleased"
PointerWheelChanged="Canvas_PointerWheelChanged"/>
</Border>
</Grid>
</Border>
</UserControl>
Loading
Loading