Skip to content
Merged
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
4 changes: 3 additions & 1 deletion Dashboard/Controls/CriticalIssuesContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
Expand Down Expand Up @@ -107,6 +108,7 @@
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="CriticalIssuesNoDataMessage" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="CriticalIssuesNoDataMessage" Style="{StaticResource EmptyStateMessage}" Text="No critical issues detected in selected time range"/>
<controls:LoadingOverlay x:Name="CriticalIssuesLoading"/>
</Grid>
</UserControl>
11 changes: 11 additions & 0 deletions Dashboard/Controls/CriticalIssuesContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ private async System.Threading.Tasks.Task LoadCriticalIssuesAsync()

try
{
// Only show loading overlay on initial load (no existing data)
if (CriticalIssuesDataGrid.ItemsSource == null)
{
CriticalIssuesLoading.IsLoading = true;
CriticalIssuesNoDataMessage.Visibility = Visibility.Collapsed;
}

var data = await _databaseService.GetCriticalIssuesAsync(
_criticalIssuesHoursBack,
_criticalIssuesFromDate,
Expand All @@ -111,6 +118,10 @@ private async System.Threading.Tasks.Task LoadCriticalIssuesAsync()
{
Logger.Error($"Error loading critical issues: {ex.Message}");
}
finally
{
CriticalIssuesLoading.IsLoading = false;
}
}

#region Filter Popup Handlers
Expand Down
5 changes: 4 additions & 1 deletion Dashboard/Controls/DailySummaryContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
Expand Down Expand Up @@ -144,7 +145,9 @@
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="DailySummaryNoDataMessage" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="DailySummaryNoDataMessage" Style="{StaticResource EmptyStateMessage}"
Text="No daily summary data. Try selecting a different date."/>
<controls:LoadingOverlay x:Name="DailySummaryLoading"/>
</Grid>
</Grid>
</UserControl>
11 changes: 11 additions & 0 deletions Dashboard/Controls/DailySummaryContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ private async System.Threading.Tasks.Task LoadDailySummaryAsync()

try
{
// Only show loading overlay on initial load (no existing data)
if (DailySummaryDataGrid.ItemsSource == null)
{
DailySummaryLoading.IsLoading = true;
DailySummaryNoDataMessage.Visibility = Visibility.Collapsed;
}

var data = await _databaseService.GetDailySummaryAsync(_dailySummaryDate);

// Store unfiltered data and reset filters when new data is loaded
Expand All @@ -100,6 +107,10 @@ private async System.Threading.Tasks.Task LoadDailySummaryAsync()
{
Logger.Error($"Error loading daily summary: {ex.Message}");
}
finally
{
DailySummaryLoading.IsLoading = false;
}
}

#region Event Handlers
Expand Down
48 changes: 48 additions & 0 deletions Dashboard/Controls/LoadingOverlay.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<UserControl x:Class="PerformanceMonitorDashboard.Controls.LoadingOverlay"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Visibility="Collapsed"
d:DesignHeight="200" d:DesignWidth="400">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/DarkTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{DynamicResource BackgroundBrush}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock x:Name="SpinnerText"
Text="&#x27F3;"
FontSize="24"
Foreground="{DynamicResource AccentBrush}"
HorizontalAlignment="Center"
RenderTransformOrigin="0.5,0.5">
<TextBlock.RenderTransform>
<RotateTransform x:Name="SpinnerRotation"/>
</TextBlock.RenderTransform>
<TextBlock.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation
Storyboard.TargetName="SpinnerRotation"
Storyboard.TargetProperty="Angle"
From="0" To="360"
Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</TextBlock.Triggers>
</TextBlock>
<TextBlock x:Name="MessageText"
FontSize="14"
Foreground="{DynamicResource ForegroundMutedBrush}"
HorizontalAlignment="Center"
Margin="0,8,0,0"/>
</StackPanel>
</Grid>
</UserControl>
41 changes: 41 additions & 0 deletions Dashboard/Controls/LoadingOverlay.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Windows;
using System.Windows.Controls;
using PerformanceMonitorDashboard.Helpers;

namespace PerformanceMonitorDashboard.Controls
{
public partial class LoadingOverlay : UserControl
{
public static readonly DependencyProperty IsLoadingProperty =
DependencyProperty.Register(
nameof(IsLoading),
typeof(bool),
typeof(LoadingOverlay),
new PropertyMetadata(false, OnIsLoadingChanged));

public bool IsLoading
{
get => (bool)GetValue(IsLoadingProperty);
set => SetValue(IsLoadingProperty, value);
}

public LoadingOverlay()
{
InitializeComponent();
}

private static void OnIsLoadingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is LoadingOverlay overlay)
{
var isLoading = (bool)e.NewValue;
overlay.Visibility = isLoading ? Visibility.Visible : Visibility.Collapsed;

if (isLoading)
{
overlay.MessageText.Text = LoadingMessages.GetRandom();
}
}
}
}
}
9 changes: 7 additions & 2 deletions Dashboard/Controls/MemoryContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ScottPlot="clr-namespace:ScottPlot.WPF;assembly=ScottPlot.WPF"
xmlns:controls="clr-namespace:PerformanceMonitorDashboard.Controls"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="1200">
<UserControl.Resources>
Expand Down Expand Up @@ -90,7 +91,9 @@
<TextBlock x:Name="MemoryGrantsForcedText" Text="0" Foreground="{DynamicResource ForegroundBrush}" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<TextBlock x:Name="MemoryGrantsNoDataMessage" Grid.Row="1" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="MemoryGrantsNoDataMessage" Grid.Row="1" Style="{StaticResource EmptyStateMessage}"
Text="No memory grant data in selected time range"/>
<controls:LoadingOverlay x:Name="MemoryGrantsLoading" Grid.Row="1"/>
</Grid>
</Border>
</TabItem>
Expand All @@ -116,7 +119,9 @@
<TextBlock x:Name="MemoryClerksTopText" Text="N/A" Foreground="{DynamicResource ForegroundBrush}" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<TextBlock x:Name="MemoryClerksNoDataMessage" Grid.Row="1" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="MemoryClerksNoDataMessage" Grid.Row="1" Style="{StaticResource EmptyStateMessage}"
Text="No memory clerk data in selected time range"/>
<controls:LoadingOverlay x:Name="MemoryClerksLoading" Grid.Row="1"/>
</Grid>
</Border>
</TabItem>
Expand Down
22 changes: 22 additions & 0 deletions Dashboard/Controls/MemoryContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,13 @@ private async System.Threading.Tasks.Task RefreshMemoryGrantsAsync()

try
{
// Only show loading overlay on initial load (no existing chart data)
if (!MemoryGrantsChart.Plot.GetPlottables().Any())
{
MemoryGrantsLoading.IsLoading = true;
MemoryGrantsNoDataMessage.Visibility = Visibility.Collapsed;
}

var data = await _databaseService.GetMemoryGrantStatsAsync(_memoryGrantsHoursBack, _memoryGrantsFromDate, _memoryGrantsToDate);
var dataList = data.ToList();
MemoryGrantsNoDataMessage.Visibility = dataList.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
Expand All @@ -392,6 +399,10 @@ private async System.Threading.Tasks.Task RefreshMemoryGrantsAsync()
{
Logger.Error($"Error loading memory grants: {ex.Message}");
}
finally
{
MemoryGrantsLoading.IsLoading = false;
}
}

private void LoadMemoryGrantsChart(IEnumerable<MemoryGrantStatsItem> data, int hoursBack, DateTime? fromDate, DateTime? toDate)
Expand Down Expand Up @@ -485,6 +496,13 @@ private async System.Threading.Tasks.Task RefreshMemoryClerksAsync()

try
{
// Only show loading overlay on initial load (no existing chart data)
if (!MemoryClerksChart.Plot.GetPlottables().Any())
{
MemoryClerksLoading.IsLoading = true;
MemoryClerksNoDataMessage.Visibility = Visibility.Collapsed;
}

var data = await _databaseService.GetMemoryClerksTopNAsync(5, _memoryClerksHoursBack, _memoryClerksFromDate, _memoryClerksToDate);
var dataList = data.ToList();
MemoryClerksNoDataMessage.Visibility = dataList.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
Expand All @@ -494,6 +512,10 @@ private async System.Threading.Tasks.Task RefreshMemoryClerksAsync()
{
Logger.Error($"Error loading memory clerks: {ex.Message}");
}
finally
{
MemoryClerksLoading.IsLoading = false;
}
}

private void LoadMemoryClerksChart(List<MemoryClerksItem> data, int hoursBack, DateTime? fromDate, DateTime? toDate)
Expand Down
8 changes: 6 additions & 2 deletions Dashboard/Controls/QueryPerformanceContent.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,9 @@
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="ActiveQueriesNoDataMessage" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="ActiveQueriesNoDataMessage" Style="{StaticResource EmptyStateMessage}"
Text="No active queries found"/>
<local:LoadingOverlay x:Name="ActiveQueriesLoading"/>
</Grid>
</TabItem>

Expand Down Expand Up @@ -585,7 +587,9 @@
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="QueryStatsNoDataMessage" Style="{StaticResource NoDataMessage}"/>
<TextBlock x:Name="QueryStatsNoDataMessage" Style="{StaticResource EmptyStateMessage}"
Text="No query statistics in selected time range"/>
<local:LoadingOverlay x:Name="QueryStatsLoading"/>
</Grid>
</TabItem>

Expand Down
22 changes: 22 additions & 0 deletions Dashboard/Controls/QueryPerformanceContent.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,13 @@ public async Task RefreshAllDataAsync()

if (_databaseService == null) return;

// Only show loading overlay on initial load (no existing data)
if (QueryStatsDataGrid.ItemsSource == null)
{
QueryStatsLoading.IsLoading = true;
QueryStatsNoDataMessage.Visibility = Visibility.Collapsed;
}

// Fetch grid data (summary views aggregated per query/procedure)
var queryStatsTask = _databaseService.GetQueryStatsAsync(_queryStatsHoursBack, _queryStatsFromDate, _queryStatsToDate);
var procStatsTask = _databaseService.GetProcedureStatsAsync(_procStatsHoursBack, _procStatsFromDate, _procStatsToDate);
Expand Down Expand Up @@ -263,6 +270,10 @@ await Task.WhenAll(
{
Logger.Error($"Error refreshing QueryPerformance data: {ex.Message}", ex);
}
finally
{
QueryStatsLoading.IsLoading = false;
}
}

private void SetStatus(string message)
Expand Down Expand Up @@ -367,7 +378,14 @@ private async Task RefreshActiveQueriesAsync()

try
{
// Only show loading overlay on initial load (no existing data)
if (ActiveQueriesDataGrid.ItemsSource == null)
{
ActiveQueriesLoading.IsLoading = true;
ActiveQueriesNoDataMessage.Visibility = Visibility.Collapsed;
}
SetStatus("Loading active queries...");

var data = await _databaseService.GetQuerySnapshotsAsync(_activeQueriesHoursBack, _activeQueriesFromDate, _activeQueriesToDate);
ActiveQueriesDataGrid.ItemsSource = data;
ActiveQueriesNoDataMessage.Visibility = data.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
Expand All @@ -378,6 +396,10 @@ private async Task RefreshActiveQueriesAsync()
Logger.Error($"Error loading active queries: {ex.Message}");
SetStatus("Error loading active queries");
}
finally
{
ActiveQueriesLoading.IsLoading = false;
}
}

private void ActiveQueriesFilter_Click(object sender, RoutedEventArgs e)
Expand Down
46 changes: 46 additions & 0 deletions Dashboard/Helpers/LoadingMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;

namespace PerformanceMonitorDashboard.Helpers
{
/// <summary>
/// Randomized loading messages displayed while data is being fetched.
/// </summary>
public static class LoadingMessages
{
private static readonly string[] Messages =
[
"Reticulating splines...",
"Consulting the oracle...",
"Asking the database nicely...",
"Crunching numbers...",
"Thinking really hard...",
"Mulling it over...",
"Communing with SQL Server...",
"Summoning data spirits...",
"Decoding the matrix...",
"Interrogating indexes...",
"Parsing the void...",
"Calibrating flux capacitors...",
"Negotiating with stored procedures...",
"Convincing queries to run faster...",
"Herding cursors...",
"Massaging execution plans...",
"Whispering to wait stats...",
"Befriending buffer pools...",
"Coaxing data from disk...",
"Pondering performance...",
"Untangling spaghetti queries...",
"Poking the query optimizer...",
"Warming up the plan cache...",
"Dusting off statistics...",
"Reasoning with RESOURCE_SEMAPHORE...",
"Apologizing to tempdb...",
"Flattering the cardinality estimator...",
"Bribing the lock manager...",
"Decrypting wait stats hieroglyphics...",
"Teaching cursors to fly...",
];

public static string GetRandom() => Messages[Random.Shared.Next(Messages.Length)];
}
}
Loading