diff --git a/Dashboard/ProcedureHistoryWindow.xaml b/Dashboard/ProcedureHistoryWindow.xaml
index 5ec2a1ad..613b308b 100644
--- a/Dashboard/ProcedureHistoryWindow.xaml
+++ b/Dashboard/ProcedureHistoryWindow.xaml
@@ -10,6 +10,23 @@
Background="{DynamicResource BackgroundBrush}">
+
+
+
+
+
+
+
+
+
@@ -65,62 +82,322 @@
RowBackground="{DynamicResource BackgroundBrush}"
GridLinesVisibility="All"
RowHeight="35"
+ ContextMenu="{DynamicResource DataGridContextMenu}"
Margin="10">
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
diff --git a/Dashboard/ProcedureHistoryWindow.xaml.cs b/Dashboard/ProcedureHistoryWindow.xaml.cs
index a8356ac0..0a193ca8 100644
--- a/Dashboard/ProcedureHistoryWindow.xaml.cs
+++ b/Dashboard/ProcedureHistoryWindow.xaml.cs
@@ -11,10 +11,13 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
using Microsoft.Win32;
+using PerformanceMonitorDashboard.Helpers;
using PerformanceMonitorDashboard.Models;
using PerformanceMonitorDashboard.Services;
using ScottPlot;
@@ -32,6 +35,12 @@ public partial class ProcedureHistoryWindow : Window
private readonly DateTime? _toDate;
private List _historyData = new();
+ // Filter state
+ private Dictionary _filters = new();
+ private List? _unfilteredData;
+ private Popup? _filterPopup;
+ private ColumnFilterPopup? _filterPopupContent;
+
public ProcedureHistoryWindow(
DatabaseService databaseService,
string databaseName,
@@ -85,7 +94,10 @@ private async Task LoadHistoryAsync()
{
_historyData = await _databaseService.GetProcedureStatsHistoryAsync(_databaseName, _objectId, _hoursBack, _fromDate, _toDate);
+ _unfilteredData = _historyData;
+ _filters.Clear();
HistoryDataGrid.ItemsSource = _historyData;
+ UpdateFilterButtonStyles();
if (_historyData.Count > 0)
{
@@ -221,5 +233,195 @@ private async void DownloadPlan_Click(object sender, RoutedEventArgs e)
}
}
}
+
+ #region Column Filter Popup
+
+ private void Filter_Click(object sender, RoutedEventArgs e)
+ {
+ if (sender is not Button button || button.Tag is not string columnName) return;
+
+ if (_filterPopup == null)
+ {
+ _filterPopupContent = new ColumnFilterPopup();
+ _filterPopupContent.FilterApplied += FilterPopup_FilterApplied;
+ _filterPopupContent.FilterCleared += FilterPopup_FilterCleared;
+
+ _filterPopup = new Popup
+ {
+ Child = _filterPopupContent,
+ StaysOpen = false,
+ Placement = PlacementMode.Bottom,
+ AllowsTransparency = true
+ };
+ }
+
+ _filters.TryGetValue(columnName, out var existingFilter);
+ _filterPopupContent!.Initialize(columnName, existingFilter);
+
+ _filterPopup.PlacementTarget = button;
+ _filterPopup.IsOpen = true;
+ }
+
+ private void FilterPopup_FilterApplied(object? sender, FilterAppliedEventArgs e)
+ {
+ if (_filterPopup != null) _filterPopup.IsOpen = false;
+
+ if (e.FilterState.IsActive)
+ _filters[e.FilterState.ColumnName] = e.FilterState;
+ else
+ _filters.Remove(e.FilterState.ColumnName);
+
+ ApplyFilters();
+ UpdateFilterButtonStyles();
+ }
+
+ private void FilterPopup_FilterCleared(object? sender, EventArgs e)
+ {
+ if (_filterPopup != null) _filterPopup.IsOpen = false;
+ }
+
+ private void ApplyFilters()
+ {
+ if (_unfilteredData == null) return;
+
+ if (_filters.Count == 0)
+ {
+ HistoryDataGrid.ItemsSource = _unfilteredData;
+ return;
+ }
+
+ var filtered = _unfilteredData.Where(item =>
+ {
+ foreach (var filter in _filters.Values)
+ {
+ if (filter.IsActive && !DataGridFilterService.MatchesFilter(item, filter))
+ return false;
+ }
+ return true;
+ }).ToList();
+
+ HistoryDataGrid.ItemsSource = filtered;
+ }
+
+ private void UpdateFilterButtonStyles()
+ {
+ foreach (var column in HistoryDataGrid.Columns)
+ {
+ if (column.Header is StackPanel stackPanel)
+ {
+ var filterButton = stackPanel.Children.OfType