diff --git a/Dashboard/Controls/AlertsHistoryContent.xaml b/Dashboard/Controls/AlertsHistoryContent.xaml
new file mode 100644
index 00000000..41793f4c
--- /dev/null
+++ b/Dashboard/Controls/AlertsHistoryContent.xaml
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Dashboard/Controls/AlertsHistoryContent.xaml.cs b/Dashboard/Controls/AlertsHistoryContent.xaml.cs
new file mode 100644
index 00000000..9741b2aa
--- /dev/null
+++ b/Dashboard/Controls/AlertsHistoryContent.xaml.cs
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2026 Erik Darling, Darling Data LLC
+ *
+ * This file is part of the SQL Server Performance Monitor.
+ *
+ * Licensed under the MIT License. See LICENSE file in the project root for full license information.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using Microsoft.Win32;
+using PerformanceMonitorDashboard.Helpers;
+using PerformanceMonitorDashboard.Models;
+using PerformanceMonitorDashboard.Services;
+
+namespace PerformanceMonitorDashboard.Controls
+{
+ public partial class AlertsHistoryContent : UserControl
+ {
+ private List _allAlerts = new();
+
+ /* Column filter state */
+ private readonly Dictionary _columnFilters = new();
+ private Popup? _filterPopup;
+ private ColumnFilterPopup? _filterPopupContent;
+
+ public AlertsHistoryContent()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Refreshes the alert history from the in-memory log.
+ ///
+ public void RefreshAlerts()
+ {
+ LoadAlerts();
+ }
+
+ private void LoadAlerts()
+ {
+ var service = EmailAlertService.Current;
+ if (service == null)
+ {
+ AlertsDataGrid.ItemsSource = null;
+ NoAlertsMessage.Visibility = Visibility.Visible;
+ AlertCountIndicator.Text = "";
+ return;
+ }
+
+ var hoursBack = GetSelectedHoursBack();
+ var entries = service.GetAlertHistory(hoursBack > 0 ? hoursBack : 8760, 500);
+
+ _allAlerts = entries.Select(e => new AlertHistoryDisplayItem
+ {
+ AlertTime = e.AlertTime,
+ ServerName = e.ServerName,
+ MetricName = e.MetricName,
+ CurrentValue = e.CurrentValue,
+ ThresholdValue = e.ThresholdValue,
+ NotificationType = e.NotificationType,
+ StatusDisplay = GetStatusDisplay(e),
+ IsResolved = e.MetricName.Contains("Cleared") || e.MetricName.Contains("Resolved"),
+ IsCritical = e.MetricName.Contains("Deadlock") || e.MetricName.Contains("Poison"),
+ IsWarning = !e.MetricName.Contains("Cleared") && !e.MetricName.Contains("Resolved")
+ && !e.MetricName.Contains("Deadlock") && !e.MetricName.Contains("Poison")
+ }).ToList();
+
+ ApplyFilters();
+ }
+
+ private void ApplyFilters()
+ {
+ var filtered = _allAlerts.AsEnumerable();
+
+ /* Server filter */
+ if (ServerFilterComboBox.SelectedIndex > 0 &&
+ ServerFilterComboBox.SelectedItem is ComboBoxItem serverItem)
+ {
+ var serverName = serverItem.Content?.ToString();
+ if (!string.IsNullOrEmpty(serverName))
+ filtered = filtered.Where(a => a.ServerName == serverName);
+ }
+
+ /* Column filters */
+ if (_columnFilters.Count > 0)
+ {
+ filtered = filtered.Where(item =>
+ {
+ foreach (var filter in _columnFilters.Values)
+ {
+ if (filter.IsActive && !DataGridFilterService.MatchesFilter(item, filter))
+ return false;
+ }
+ return true;
+ });
+ }
+
+ var list = filtered.ToList();
+ AlertsDataGrid.ItemsSource = list;
+ NoAlertsMessage.Visibility = list.Count == 0 ? Visibility.Visible : Visibility.Collapsed;
+ AlertCountIndicator.Text = list.Count > 0 ? $"{list.Count} alert(s)" : "";
+
+ /* Populate server filter if needed */
+ PopulateServerFilter();
+ }
+
+ private void PopulateServerFilter()
+ {
+ var servers = _allAlerts
+ .Select(a => a.ServerName)
+ .Where(s => !string.IsNullOrEmpty(s))
+ .Distinct()
+ .OrderBy(s => s)
+ .ToList();
+
+ var currentSelection = ServerFilterComboBox.SelectedIndex > 0
+ ? (ServerFilterComboBox.SelectedItem as ComboBoxItem)?.Content?.ToString()
+ : null;
+
+ /* Only rebuild if the list changed */
+ var existingServers = ServerFilterComboBox.Items
+ .OfType()
+ .Skip(1)
+ .Select(i => i.Content?.ToString())
+ .ToList();
+
+ if (servers.SequenceEqual(existingServers)) return;
+
+ ServerFilterComboBox.SelectionChanged -= ServerFilterComboBox_SelectionChanged;
+
+ while (ServerFilterComboBox.Items.Count > 1)
+ ServerFilterComboBox.Items.RemoveAt(1);
+
+ foreach (var server in servers)
+ {
+ ServerFilterComboBox.Items.Add(new ComboBoxItem { Content = server });
+ }
+
+ /* Restore selection */
+ if (currentSelection != null)
+ {
+ for (int i = 1; i < ServerFilterComboBox.Items.Count; i++)
+ {
+ if ((ServerFilterComboBox.Items[i] as ComboBoxItem)?.Content?.ToString() == currentSelection)
+ {
+ ServerFilterComboBox.SelectedIndex = i;
+ break;
+ }
+ }
+ }
+
+ ServerFilterComboBox.SelectionChanged += ServerFilterComboBox_SelectionChanged;
+ }
+
+ private int GetSelectedHoursBack()
+ {
+ if (TimeRangeComboBox.SelectedItem is ComboBoxItem item && item.Tag is string tagStr)
+ {
+ return int.TryParse(tagStr, out var hours) ? hours : 24;
+ }
+ return 24;
+ }
+
+ private static string GetStatusDisplay(AlertLogEntry entry)
+ {
+ if (entry.NotificationType == "email")
+ {
+ if (entry.AlertSent) return "Sent";
+ return !string.IsNullOrEmpty(entry.SendError) ? "Failed" : "Not sent";
+ }
+ return entry.AlertSent ? "Delivered" : "Shown";
+ }
+
+ #region Event Handlers
+
+ private void TimeRangeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (IsLoaded)
+ LoadAlerts();
+ }
+
+ private void ServerFilterComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ if (IsLoaded)
+ ApplyFilters();
+ }
+
+ private void RefreshButton_Click(object sender, RoutedEventArgs e)
+ {
+ LoadAlerts();
+ }
+
+ private void AlertsDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
+ {
+ DismissSelectedButton.IsEnabled = AlertsDataGrid.SelectedItems.Count > 0;
+ }
+
+ private void DismissSelected_Click(object sender, RoutedEventArgs e)
+ {
+ var service = EmailAlertService.Current;
+ if (service == null) return;
+
+ var selected = AlertsDataGrid.SelectedItems
+ .OfType()
+ .ToList();
+
+ if (selected.Count == 0) return;
+
+ var keys = selected
+ .Select(s => (s.AlertTime, s.ServerName, s.MetricName))
+ .ToList();
+
+ service.HideAlerts(keys);
+ LoadAlerts();
+ }
+
+ private void DismissAll_Click(object sender, RoutedEventArgs e)
+ {
+ var service = EmailAlertService.Current;
+ if (service == null) return;
+
+ var displayCount = AlertsDataGrid.ItemsSource is ICollection coll ? coll.Count : 0;
+ if (displayCount == 0) return;
+
+ var result = MessageBox.Show(
+ $"Dismiss all {displayCount} visible alert(s)?\n\nDismissed alerts are hidden from this view but preserved in the log.",
+ "Dismiss All Alerts",
+ MessageBoxButton.YesNo,
+ MessageBoxImage.Question);
+
+ if (result != MessageBoxResult.Yes) return;
+
+ var hoursBack = GetSelectedHoursBack();
+ string? serverName = null;
+ if (ServerFilterComboBox.SelectedIndex > 0 &&
+ ServerFilterComboBox.SelectedItem is ComboBoxItem serverItem)
+ {
+ serverName = serverItem.Content?.ToString();
+ }
+
+ service.HideAllAlerts(hoursBack > 0 ? hoursBack : 8760, serverName);
+ LoadAlerts();
+ }
+
+ #endregion
+
+ #region Column Filter Handlers
+
+ private void AlertFilter_Click(object sender, RoutedEventArgs e)
+ {
+ if (sender is not Button button || button.Tag is not string columnName) return;
+ ShowFilterPopup(button, columnName);
+ }
+
+ private void ShowFilterPopup(Button button, string columnName)
+ {
+ 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
+ };
+ }
+
+ _columnFilters.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)
+ _columnFilters[e.FilterState.ColumnName] = e.FilterState;
+ else
+ _columnFilters.Remove(e.FilterState.ColumnName);
+
+ ApplyFilters();
+ UpdateFilterButtonStyles();
+ }
+
+ private void FilterPopup_FilterCleared(object? sender, EventArgs e)
+ {
+ if (_filterPopup != null)
+ _filterPopup.IsOpen = false;
+ }
+
+ private void UpdateFilterButtonStyles()
+ {
+ foreach (var column in AlertsDataGrid.Columns)
+ {
+ if (column.Header is StackPanel stackPanel)
+ {
+ var filterButton = stackPanel.Children.OfType