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
This commit is contained in:
Proudlock
2026-09-08 17:39:23 -04:00
parent 6340648ad9
commit 8f2c2299b8

View File

@@ -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);
}
}