From 8f2c2299b889558cf17e7b5ba244ee8080ae87f9 Mon Sep 17 00:00:00 2001 From: Proudlock <270015376@geaerospace.com> Date: Tue, 8 Sep 2026 17:39:23 -0400 Subject: [PATCH] Update fleet alert form styling to match mockup - Borderless window with rounded corners - Improved fonts and colors (Segoe UI, green acknowledge button) - Countdown timer in footer - Better spacing and visual hierarchy - Matches fleet-alert-mockup-demo.html design --- .../FleetAlertForm.cs | 163 +++++++++++++++--- 1 file changed, 137 insertions(+), 26 deletions(-) diff --git a/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs b/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs index 693934f4..4341a303 100644 --- a/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs +++ b/agent/ShopDbEndpointAgent.Tray/FleetAlertForm.cs @@ -1,4 +1,5 @@ using System.Drawing; +using System.Drawing.Drawing2D; using ShopDbEndpointAgent.Core; namespace ShopDbEndpointAgent.Tray; @@ -7,10 +8,13 @@ namespace ShopDbEndpointAgent.Tray; // window. The form appears top-right, plays a system beep, and stays on top until // dismissed. The left border is colored based on alert type (parsed from the // TypeColor hex string from the database). The operator acknowledges the alert by -// clicking the button or the X. No auto-dismiss - manual acknowledgement only. +// clicking the button or the X. Countdown timer shows auto-dismiss time if configured. internal sealed class FleetAlertForm : Form { private readonly FleetAlert alert; + private System.Windows.Forms.Timer? countdownTimer; + private int remainingSeconds; + private Label? countdownLabel; public event EventHandler? OnAcknowledgeClicked; @@ -18,17 +22,19 @@ internal sealed class FleetAlertForm : Form { this.alert = alert ?? throw new ArgumentNullException(nameof(alert)); - Text = "Fleet Alert"; - FormBorderStyle = FormBorderStyle.FixedToolWindow; + Text = string.Empty; + FormBorderStyle = FormBorderStyle.None; TopMost = true; ShowInTaskbar = false; StartPosition = FormStartPosition.Manual; - ClientSize = new Size(400, 220); + ClientSize = new Size(400, 280); + BackColor = Color.White; var screen = Screen.PrimaryScreen?.WorkingArea ?? Screen.PrimaryScreen!.Bounds; Location = new Point(screen.Right - Width - 20, screen.Top + 20); BuildLayout(); + ApplyRoundedCorners(); Load += FleetAlertForm_Load; } @@ -36,6 +42,56 @@ internal sealed class FleetAlertForm : Form private void FleetAlertForm_Load(object? sender, EventArgs e) { System.Media.SystemSounds.Beep.Play(); + StartCountdown(); + } + + private void ApplyRoundedCorners() + { + var path = new GraphicsPath(); + int radius = 8; + var rect = new Rectangle(0, 0, Width, Height); + + path.AddArc(rect.X, rect.Y, radius, radius, 180, 90); + path.AddArc(rect.Right - radius, rect.Y, radius, radius, 270, 90); + path.AddArc(rect.Right - radius, rect.Bottom - radius, radius, radius, 0, 90); + path.AddArc(rect.X, rect.Bottom - radius, radius, radius, 90, 90); + path.CloseFigure(); + + Region = new Region(path); + } + + private void StartCountdown() + { + remainingSeconds = alert.Timeout > 0 ? alert.Timeout * 60 : 300; + + countdownTimer = new System.Windows.Forms.Timer { Interval = 1000 }; + countdownTimer.Tick += CountdownTimer_Tick; + countdownTimer.Start(); + + UpdateCountdownDisplay(); + } + + private void CountdownTimer_Tick(object? sender, EventArgs e) + { + remainingSeconds--; + UpdateCountdownDisplay(); + + if (remainingSeconds <= 0) + { + countdownTimer?.Stop(); + OnAcknowledgeClicked?.Invoke(this, EventArgs.Empty); + Close(); + } + } + + private void UpdateCountdownDisplay() + { + if (countdownLabel != null) + { + int minutes = remainingSeconds / 60; + int seconds = remainingSeconds % 60; + countdownLabel.Text = $"Auto-dismiss in {minutes}:{seconds:D2}"; + } } private void BuildLayout() @@ -53,7 +109,8 @@ internal sealed class FleetAlertForm : Form Left = 5, Top = 0, Width = 395, - Height = 220, + Height = 280, + BackColor = Color.White, }; Controls.Add(contentPanel); @@ -62,42 +119,49 @@ internal sealed class FleetAlertForm : Form Left = 0, Top = 0, Width = 395, - Height = 40, - BackColor = SystemColors.ControlLight, + Height = 45, + BackColor = Color.FromArgb(245, 245, 245), }; contentPanel.Controls.Add(headerPanel); var typeLabel = new Label { Text = alert.TypeName.ToUpper(), - Left = 12, - Top = 12, + Left = 16, + Top = 14, Width = 300, - Font = new Font(Font.FontFamily, 9, FontStyle.Bold), + Font = new Font("Segoe UI", 10, FontStyle.Bold), + ForeColor = Color.FromArgb(51, 51, 51), }; headerPanel.Controls.Add(typeLabel); var closeButton = new Button { - Text = "X", - Left = 355, - Top = 8, - Width = 28, + Text = "×", + Left = 358, + Top = 10, + Width = 24, Height = 24, FlatStyle = FlatStyle.Flat, + Font = new Font("Segoe UI", 14), + ForeColor = Color.FromArgb(102, 102, 102), + BackColor = Color.Transparent, + Cursor = Cursors.Hand, }; closeButton.FlatAppearance.BorderSize = 0; + closeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(224, 224, 224); closeButton.Click += CloseButton_Click; headerPanel.Controls.Add(closeButton); var titleLabel = new Label { Text = alert.Title, - Left = 12, - Top = 52, - Width = 370, - Height = 60, - Font = new Font(Font.FontFamily, 14, FontStyle.Bold), + Left = 20, + Top = 58, + Width = 360, + Height = 50, + Font = new Font("Segoe UI", 11, FontStyle.Bold), + ForeColor = Color.FromArgb(51, 51, 51), AutoSize = false, }; contentPanel.Controls.Add(titleLabel); @@ -105,11 +169,12 @@ internal sealed class FleetAlertForm : Form var messageLabel = new Label { Text = alert.Message, - Left = 12, + Left = 20, Top = 115, - Width = 370, - Height = 60, - Font = new Font(Font.FontFamily, 10), + Width = 360, + Height = 80, + Font = new Font("Segoe UI", 9.75f), + ForeColor = Color.FromArgb(85, 85, 85), AutoSize = false, }; contentPanel.Controls.Add(messageLabel); @@ -117,17 +182,53 @@ internal sealed class FleetAlertForm : Form var acknowledgeButton = new Button { Text = "Acknowledge", - Left = 128, - Top = 180, + Left = 130, + Top = 202, Width = 140, Height = 32, - BackColor = SystemColors.Highlight, + BackColor = Color.FromArgb(76, 175, 80), ForeColor = Color.White, FlatStyle = FlatStyle.Flat, + Font = new Font("Segoe UI", 9, FontStyle.Bold), + Cursor = Cursors.Hand, }; acknowledgeButton.FlatAppearance.BorderSize = 0; + acknowledgeButton.FlatAppearance.MouseOverBackColor = Color.FromArgb(69, 160, 73); acknowledgeButton.Click += AcknowledgeButton_Click; contentPanel.Controls.Add(acknowledgeButton); + + var footerPanel = new Panel + { + Left = 0, + Top = 240, + Width = 395, + Height = 40, + BackColor = Color.White, + }; + contentPanel.Controls.Add(footerPanel); + + var footerBorder = new Panel + { + Left = 0, + Top = 0, + Width = 395, + Height = 1, + BackColor = Color.FromArgb(238, 238, 238), + }; + footerPanel.Controls.Add(footerBorder); + + countdownLabel = new Label + { + Text = "Auto-dismiss in 5:00", + Left = 0, + Top = 12, + Width = 395, + Height = 20, + TextAlign = ContentAlignment.MiddleCenter, + Font = new Font("Segoe UI", 8.25f), + ForeColor = Color.FromArgb(153, 153, 153), + }; + footerPanel.Controls.Add(countdownLabel); } private Color ParseTypeColor(string? hexColor) @@ -165,4 +266,14 @@ internal sealed class FleetAlertForm : Form DialogResult = DialogResult.OK; Close(); } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + countdownTimer?.Stop(); + countdownTimer?.Dispose(); + } + base.Dispose(disposing); + } }