📋 Overview

BeepStatCard is a sophisticated data visualization control designed for displaying key performance indicators (KPIs), metrics, and statistical data in dashboard and analytics applications. It combines multiple visual elements including header text, main values, percentage changes, trend indicators, and descriptive information in a cohesive, professionally styled card layout.

Built on the BeepControl foundation, BeepStatCard automatically adapts to themes, provides smooth layout management, and supports both SVG and traditional image formats for icons and trend indicators.

🚀 Key Features

📈 Trend Visualization

Built-in support for upward and downward trend indicators with customizable SVG icons

🎯 Multi-Component Layout

Header, main value, percentage change, trend text, and information sections

🖼️ Flexible Iconography

Support for card icons and trend icons with SVG file path configuration

🎨 Professional Theming

Full integration with Beep theming system for consistent dashboard appearance

📊 Data Binding Ready

BoundProperty support for seamless integration with data sources

📱 Responsive Layout

Automatic layout adjustment based on card size and content

👁️ Visual Preview

Card Layout Structure

Total Revenue 💼
+12.5%
$1,250.00
Trending up this month 📈
Visitors for the last 6 months

Component Structure

┌─────────────────────────────────────┐
│ [Header Text]              [Icon]   │  ← Header Section
├─────────────────────────────────────┤
│                      [+12.5%]       │  ← Percentage Section  
│                                     │
│           [$1,250.00]               │  ← Main Value (Center)
│                                     │
│ [Trending up] ──────────── [📈]     │  ← Trend Section
│                                     │
│ [Additional Information]            │  ← Info Section
└─────────────────────────────────────┘

🚀 Basic Usage

Simple Stat Card

// Create a basic statistical card
var revenueCard = new BeepStatCard
{
    Size = new Size(300, 150),
    Location = new Point(10, 10),
    
    // Basic content
    HeaderText = "Monthly Revenue",
    ValueText = "$45,231.00",
    PercentageText = "+8.2%",
    TrendText = "Up from last month",
    InfoText = "Based on confirmed sales",
    
    // Trend configuration
    IsTrendingUp = true
};

// Add to form
this.Controls.Add(revenueCard);

Card with Custom Icons

// Create card with custom SVG icons
var userCard = new BeepStatCard
{
    Size = new Size(280, 160),
    Location = new Point(320, 10),
    
    // Content
    HeaderText = "Active Users",
    ValueText = "2,847",
    PercentageText = "+15.3%",
    TrendText = "Growing steadily",
    InfoText = "Users online in last 24h",
    
    // Custom icons
    Icon = @"Assets\icons\users.svg",
    TrendUpSvgPath = @"Assets\icons\trend-up.svg",
    TrendDownSvgPath = @"Assets\icons\trend-down.svg",
    
    // Trend state
    IsTrendingUp = true
};

this.Controls.Add(userCard);

Multiple Cards Dashboard

public void CreateDashboardCards()
{
    var cardData = new[]
    {
        new { Header = "Revenue", Value = "$125,430", Percent = "+12.5%", 
              Trend = "Strong growth", Info = "Monthly total", Up = true },
        new { Header = "Orders", Value = "1,847", Percent = "+5.8%", 
              Trend = "Steady increase", Info = "This week", Up = true },
        new { Header = "Customers", Value = "23,591", Percent = "-2.1%", 
              Trend = "Slight decline", Info = "Active users", Up = false },
        new { Header = "Conversion", Value = "3.24%", Percent = "+0.8%", 
              Trend = "Improving", Info = "Sales funnel", Up = true }
    };
    
    for (int i = 0; i < cardData.Length; i++)
    {
        var data = cardData[i];
        var card = new BeepStatCard
        {
            Size = new Size(250, 140),
            Location = new Point(10 + (i * 260), 10),
            
            HeaderText = data.Header,
            ValueText = data.Value,
            PercentageText = data.Percent,
            TrendText = data.Trend,
            InfoText = data.Info,
            IsTrendingUp = data.Up,
            
            // Apply theme
            Theme = "MaterialDesignTheme"
        };
        
        this.Controls.Add(card);
    }
}
💡 Layout Tip

BeepStatCard automatically adjusts its internal layout based on the control size. For best results, use sizes between 200x120 and 400x180 pixels.

🧩 Card Components

Component Breakdown

Component Property Position Purpose
Header Label HeaderText Top-left Main title/category of the metric
Card Icon Icon Top-right Visual representation of the metric type
Percentage Label PercentageText Upper-right area Change percentage with trend coloring
Value Label ValueText Center Main statistical value (large, prominent)
Trend Label TrendText Lower-left Descriptive trend information
Trend Icon TrendUpSvgPath/TrendDownSvgPath Lower-right Visual trend indicator
Info Label InfoText Bottom Additional context or time period

Individual Component Configuration

var salesCard = new BeepStatCard();

// Configure each component individually
salesCard.HeaderText = "Q4 Sales Target";        // Header section
salesCard.ValueText = "87.3%";                   // Main value (center)
salesCard.PercentageText = "+23.1%";             // Percentage change
salesCard.TrendText = "Ahead of schedule";       // Trend description  
salesCard.InfoText = "Target: $500K by Dec 31"; // Additional info

// Icon configuration
salesCard.Icon = @"Assets\sales-target.svg";               // Main card icon
salesCard.TrendUpSvgPath = @"Assets\arrow-up.svg";         // Positive trend
salesCard.TrendDownSvgPath = @"Assets\arrow-down.svg";     // Negative trend

// Set trend direction
salesCard.IsTrendingUp = true; // Will use TrendUpSvgPath and green color

Dynamic Component Updates

// Method to update card with new data
public void UpdateStatCard(BeepStatCard card, StatData data)
{
    // Update main content
    card.HeaderText = data.Title;
    card.ValueText = data.FormattedValue;
    card.PercentageText = $"{(data.ChangePercent >= 0 ? "+" : "")}{data.ChangePercent:F1}%";
    
    // Update trend information
    card.IsTrendingUp = data.ChangePercent >= 0;
    card.TrendText = data.ChangePercent >= 0 ? "Trending up" : "Trending down";
    
    // Update time context
    card.InfoText = $"Data from {data.Period}";
    
    // The card will automatically refresh its layout
    card.Invalidate();
}

public class StatData
{
    public string Title { get; set; }
    public double Value { get; set; }
    public string FormattedValue => Value.ToString("N0");
    public double ChangePercent { get; set; }
    public string Period { get; set; }
}

📈 Trend Indicators

Trend Configuration

// Configure trend indicators
var performanceCard = new BeepStatCard
{
    HeaderText = "Website Performance",
    ValueText = "98.7%",
    PercentageText = "+0.3%",
    
    // Trend state affects multiple elements
    IsTrendingUp = true, // Controls icon and percentage color
    
    // Custom trend icons (SVG recommended)
    TrendUpSvgPath = @"Assets\icons\trending-up.svg",
    TrendDownSvgPath = @"Assets\icons\trending-down.svg"
};

// Dynamically change trend direction
public void UpdateTrend(BeepStatCard card, double changeValue)
{
    card.PercentageText = $"{(changeValue >= 0 ? "+" : "")}{changeValue:F1}%";
    card.IsTrendingUp = changeValue >= 0;
    
    // Trend text changes automatically based on direction
    card.TrendText = changeValue >= 0 
        ? "Performance improving" 
        : "Needs attention";
}

Advanced Trend Logic

public enum TrendType
{
    StronglyPositive,  // +5% or more
    Positive,          // +0.1% to +4.9%
    Neutral,           // -0.1% to +0.1%
    Negative,          // -0.1% to -4.9%
    StronglyNegative   // -5% or less
}

public void ApplyTrendStyling(BeepStatCard card, double changePercent)
{
    var trendType = GetTrendType(changePercent);
    
    // Update percentage text with appropriate formatting
    card.PercentageText = FormatPercentageChange(changePercent);
    
    // Set trend direction for icon selection
    card.IsTrendingUp = changePercent >= 0;
    
    // Apply trend-specific styling
    switch (trendType)
    {
        case TrendType.StronglyPositive:
            card.TrendText = "Excellent growth";
            break;
        case TrendType.Positive:
            card.TrendText = "Growing steadily";
            break;
        case TrendType.Neutral:
            card.TrendText = "Stable";
            break;
        case TrendType.Negative:
            card.TrendText = "Declining";
            break;
        case TrendType.StronglyNegative:
            card.TrendText = "Needs immediate attention";
            break;
    }
}

private TrendType GetTrendType(double changePercent)
{
    return changePercent switch
    {
        >= 5.0 => TrendType.StronglyPositive,
        >= 0.1 => TrendType.Positive,
        >= -0.1 and <= 0.1 => TrendType.Neutral,
        >= -5.0 => TrendType.Negative,
        _ => TrendType.StronglyNegative
    };
}

private string FormatPercentageChange(double changePercent)
{
    string sign = changePercent >= 0 ? "+" : "";
    return $"{sign}{changePercent:F1}%";
}
✅ Trend Best Practices
  • Color Consistency: Green for positive trends, red for negative trends
  • Icon Clarity: Use clear, recognizable trend icons (arrows, charts)
  • Context Provision: Always provide context for what the trend represents
  • Threshold Management: Define clear thresholds for trend categories

🖼️ Icons & Assets

SVG Icon Configuration

// Configure SVG icons for professional appearance
var analyticsCard = new BeepStatCard
{
    HeaderText = "Analytics",
    ValueText = "15,847",
    
    // Main card icon (top-right)
    Icon = @"Assets\icons\analytics.svg",
    
    // Trend icons (bottom-right, contextual)
    TrendUpSvgPath = @"Assets\icons\trend-up.svg",
    TrendDownSvgPath = @"Assets\icons\trend-down.svg"
};

// Icons are automatically themed and sized
// Card icon: 32x32 pixels
// Trend icon: 32x32 pixels

Icon Asset Management

public static class CardIconAssets
{
    public const string IconsPath = @"Assets\icons";
    
    // Main card icons
    public static readonly Dictionary CardIcons = new()
    {
        { "revenue", $@"{IconsPath}\money.svg" },
        { "users", $@"{IconsPath}\users.svg" },
        { "orders", $@"{IconsPath}\shopping-cart.svg" },
        { "performance", $@"{IconsPath}\gauge.svg" },
        { "growth", $@"{IconsPath}\growth.svg" },
        { "traffic", $@"{IconsPath}\traffic.svg" }
    };
    
    // Trend icons
    public static readonly Dictionary TrendIcons = new()
    {
        { "up", $@"{IconsPath}\arrow-up.svg" },
        { "down", $@"{IconsPath}\arrow-down.svg" },
        { "up-strong", $@"{IconsPath}\trending-up.svg" },
        { "down-strong", $@"{IconsPath}\trending-down.svg" }
    };
    
    public static void ApplyIconSet(BeepStatCard card, string iconType, bool strongTrend = false)
    {
        // Apply main card icon
        if (CardIcons.TryGetValue(iconType, out string cardIcon))
        {
            card.Icon = cardIcon;
        }
        
        // Apply trend icons
        if (strongTrend)
        {
            card.TrendUpSvgPath = TrendIcons["up-strong"];
            card.TrendDownSvgPath = TrendIcons["down-strong"];
        }
        else
        {
            card.TrendUpSvgPath = TrendIcons["up"];
            card.TrendDownSvgPath = TrendIcons["down"];
        }
    }
}

// Usage
var salesCard = new BeepStatCard();
CardIconAssets.ApplyIconSet(salesCard, "revenue", strongTrend: true);

Dynamic Icon Loading

public class IconManager
{
    private readonly Dictionary _iconCache = new();
    
    public void SetCardIcon(BeepStatCard card, string category)
    {
        string iconPath = GetIconPath(category);
        if (!string.IsNullOrEmpty(iconPath) && File.Exists(iconPath))
        {
            card.Icon = iconPath;
        }
        else
        {
            // Fallback to default icon
            card.Icon = @"Assets\icons\default.svg";
        }
    }
    
    private string GetIconPath(string category)
    {
        if (_iconCache.TryGetValue(category, out string cached))
        {
            return cached;
        }
        
        // Build icon path
        string iconPath = Path.Combine("Assets", "icons", $"{category}.svg");
        
        // Cache the result
        _iconCache[category] = iconPath;
        
        return iconPath;
    }
    
    public void PreloadIcons(params string[] categories)
    {
        foreach (string category in categories)
        {
            GetIconPath(category); // Loads into cache
        }
    }
}
📝 Icon Guidelines
  • Format: SVG files are recommended for scalability and theming
  • Size: Icons are automatically sized to 32x32 pixels
  • Style: Use consistent icon style across all cards (outline, filled, etc.)
  • Colors: SVG icons can be automatically themed to match card colors
  • Fallbacks: Always provide fallback icons for missing assets

🔗 Data Binding

Simple Data Binding

public class KPIData
{
    public string Title { get; set; }
    public decimal Value { get; set; }
    public double ChangePercent { get; set; }
    public string Description { get; set; }
    public DateTime LastUpdated { get; set; }
    
    public string FormattedValue => Value.ToString("C0");
    public string FormattedChange => $"{(ChangePercent >= 0 ? "+" : "")}{ChangePercent:F1}%";
    public bool IsTrendingUp => ChangePercent >= 0;
}

// Bind data to card
public void BindDataToCard(BeepStatCard card, KPIData data)
{
    card.HeaderText = data.Title;
    card.ValueText = data.FormattedValue;
    card.PercentageText = data.FormattedChange;
    card.IsTrendingUp = data.IsTrendingUp;
    card.TrendText = data.IsTrendingUp ? "Trending up" : "Trending down";
    card.InfoText = $"Updated {data.LastUpdated:MMM dd, yyyy}";
}

// Example usage
var revenueData = new KPIData
{
    Title = "Monthly Revenue",
    Value = 125430m,
    ChangePercent = 12.5,
    Description = "Total sales revenue",
    LastUpdated = DateTime.Now
};

BindDataToCard(revenueCard, revenueData);

Observable Data Binding

public class ObservableKPIData : INotifyPropertyChanged
{
    private decimal _value;
    private double _changePercent;
    private string _title;
    
    public string Title
    {
        get => _title;
        set { _title = value; OnPropertyChanged(); }
    }
    
    public decimal Value
    {
        get => _value;
        set { _value = value; OnPropertyChanged(); OnPropertyChanged(nameof(FormattedValue)); }
    }
    
    public double ChangePercent
    {
        get => _changePercent;
        set { _changePercent = value; OnPropertyChanged(); OnPropertyChanged(nameof(FormattedChange)); }
    }
    
    public string FormattedValue => Value.ToString("C0");
    public string FormattedChange => $"{(ChangePercent >= 0 ? "+" : "")}{ChangePercent:F1}%";
    
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

// Data-bound card wrapper
public class DataBoundStatCard : BeepStatCard
{
    private ObservableKPIData _dataSource;
    
    public ObservableKPIData DataSource
    {
        get => _dataSource;
        set
        {
            if (_dataSource != null)
                _dataSource.PropertyChanged -= OnDataChanged;
                
            _dataSource = value;
            
            if (_dataSource != null)
            {
                _dataSource.PropertyChanged += OnDataChanged;
                UpdateFromDataSource();
            }
        }
    }
    
    private void OnDataChanged(object sender, PropertyChangedEventArgs e)
    {
        if (InvokeRequired)
        {
            Invoke(new Action(UpdateFromDataSource));
        }
        else
        {
            UpdateFromDataSource();
        }
    }
    
    private void UpdateFromDataSource()
    {
        if (_dataSource == null) return;
        
        HeaderText = _dataSource.Title;
        ValueText = _dataSource.FormattedValue;
        PercentageText = _dataSource.FormattedChange;
        IsTrendingUp = _dataSource.ChangePercent >= 0;
    }
}

Real-Time Data Updates

public class RealTimeKPIManager
{
    private readonly Timer _updateTimer;
    private readonly List _cards;
    private readonly Random _random = new();
    
    public RealTimeKPIManager()
    {
        _cards = new List();
        _updateTimer = new Timer(5000); // Update every 5 seconds
        _updateTimer.Elapsed += UpdateData;
        _updateTimer.Start();
    }
    
    public void RegisterCard(DataBoundStatCard card, string dataSource)
    {
        card.Tag = dataSource; // Store data source identifier
        _cards.Add(card);
    }
    
    private void UpdateData(object sender, ElapsedEventArgs e)
    {
        foreach (var card in _cards)
        {
            if (card.DataSource != null && card.Tag is string dataSourceId)
            {
                // Simulate real-time data update
                var newData = FetchLatestData(dataSourceId);
                
                // Update data source (will automatically update card)
                card.DataSource.Value = newData.Value;
                card.DataSource.ChangePercent = newData.ChangePercent;
            }
        }
    }
    
    private (decimal Value, double ChangePercent) FetchLatestData(string dataSourceId)
    {
        // Simulate API call or database query
        return dataSourceId switch
        {
            "revenue" => (125000m + (decimal)(_random.NextDouble() * 10000), _random.NextDouble() * 20 - 10),
            "users" => (2500 + _random.Next(100), _random.NextDouble() * 10 - 5),
            "orders" => (450 + _random.Next(50), _random.NextDouble() * 15 - 7.5),
            _ => (0, 0)
        };
    }
}

🎨 Theming

Theme Integration

// BeepStatCard automatically applies theme colors:

public override void ApplyTheme()
{
    if (_currentTheme == null) return;
    
    // Card background
    BackColor = _currentTheme.CardBackColor;
    ParentBackColor = _currentTheme.CardBackColor;
    
    // Header styling
    headerLabel.ForeColor = _currentTheme.CardHeaderStyle.TextColor;
    headerLabel.TextFont = BeepThemesManager.ToFont(_currentTheme.CardHeaderStyle);
    headerLabel.BackColor = _currentTheme.CardBackColor;
    
    // Percentage with trend-based coloring
    percentageLabel.ForeColor = IsTrendingUp ? Color.Green : Color.Red;
    percentageLabel.TextFont = BeepThemesManager.ToFont(_currentTheme.SmallText);
    
    // Main value (prominent display)
    valueLabel.ForeColor = _currentTheme.CardTextForeColor;
    valueLabel.TextFont = new Font(
        BeepThemesManager.ToFont(_currentTheme.CardparagraphStyle).FontFamily, 
        24, FontStyle.Bold);
    
    // Trend text
    trendLabel.ForeColor = _currentTheme.CardTextForeColor;
    trendLabel.TextFont = BeepThemesManager.ToFont(_currentTheme.CardparagraphStyle);
    
    // Info text (smaller)
    infoLabel.ForeColor = _currentTheme.CardTextForeColor;
    infoLabel.TextFont = new Font(
        BeepThemesManager.ToFont(_currentTheme.CardparagraphStyle).FontFamily, 8);
}

Custom Theme Overrides

public class CustomStyledStatCard : BeepStatCard
{
    private Color _positiveColor = Color.Green;
    private Color _negativeColor = Color.Red;
    private Color _neutralColor = Color.Gray;
    
    public Color PositiveColor
    {
        get => _positiveColor;
        set { _positiveColor = value; UpdateTrendColors(); }
    }
    
    public Color NegativeColor
    {
        get => _negativeColor;
        set { _negativeColor = value; UpdateTrendColors(); }
    }
    
    public Color NeutralColor
    {
        get => _neutralColor;
        set { _neutralColor = value; UpdateTrendColors(); }
    }
    
    private void UpdateTrendColors()
    {
        if (percentageLabel != null)
        {
            percentageLabel.ForeColor = IsTrendingUp ? PositiveColor : NegativeColor;
        }
    }
    
    public override void ApplyTheme()
    {
        base.ApplyTheme();
        UpdateTrendColors();
    }
    
    // Custom styling for different card types
    public void ApplyCardTypeStyle(StatCardType cardType)
    {
        switch (cardType)
        {
            case StatCardType.Revenue:
                PositiveColor = Color.FromArgb(40, 167, 69);  // Success green
                NegativeColor = Color.FromArgb(220, 53, 69);  // Danger red
                break;
                
            case StatCardType.Performance:
                PositiveColor = Color.FromArgb(0, 123, 255);  // Primary blue
                NegativeColor = Color.FromArgb(255, 193, 7);  // Warning yellow
                break;
                
            case StatCardType.Users:
                PositiveColor = Color.FromArgb(111, 66, 193); // Purple
                NegativeColor = Color.FromArgb(108, 117, 125); // Gray
                break;
        }
        
        UpdateTrendColors();
    }
}

public enum StatCardType
{
    Revenue,
    Performance,
    Users,
    Orders,
    Traffic
}
🎨 Theming Tips
  • Consistency: Use the same theme across all stat cards in a dashboard
  • Contrast: Ensure adequate contrast between text and background colors
  • Brand Colors: Customize trend colors to match your brand palette
  • Accessibility: Test with high contrast themes for accessibility

🏢 Real-World Examples

Executive Dashboard

public class ExecutiveDashboard : UserControl
{
    private BeepStatCard[] kpiCards;
    
    public ExecutiveDashboard()
    {
        InitializeDashboard();
        LoadRealTimeData();
    }
    
    private void InitializeDashboard()
    {
        var kpiDefinitions = new[]
        {
            new KPIDefinition
            {
                Title = "Quarterly Revenue",
                Icon = "revenue",
                Format = "C0",
                DataSource = "sales.quarterly_revenue",
                Target = 2500000m
            },
            new KPIDefinition
            {
                Title = "Customer Acquisition",
                Icon = "users",
                Format = "N0",
                DataSource = "marketing.new_customers",
                Target = 1500
            },
            new KPIDefinition
            {
                Title = "Operational Efficiency",
                Icon = "performance",
                Format = "P1",
                DataSource = "operations.efficiency_score",
                Target = 0.95m
            },
            new KPIDefinition
            {
                Title = "Market Share",
                Icon = "growth",
                Format = "P2",
                DataSource = "market.share_percentage",
                Target = 0.125m
            }
        };
        
        kpiCards = new BeepStatCard[kpiDefinitions.Length];
        
        for (int i = 0; i < kpiDefinitions.Length; i++)
        {
            var def = kpiDefinitions[i];
            var card = new BeepStatCard
            {
                Size = new Size(280, 160),
                Location = new Point(20 + (i % 2) * 300, 20 + (i / 2) * 180),
                HeaderText = def.Title,
                Theme = "CorporateTheme"
            };
            
            CardIconAssets.ApplyIconSet(card, def.Icon);
            kpiCards[i] = card;
            this.Controls.Add(card);
        }
    }
    
    private async void LoadRealTimeData()
    {
        var dataService = new KPIDataService();
        
        while (this.IsHandleCreated)
        {
            try
            {
                var kpiData = await dataService.GetLatestKPIsAsync();
                
                for (int i = 0; i < kpiCards.Length && i < kpiData.Count; i++)
                {
                    var card = kpiCards[i];
                    var data = kpiData[i];
                    
                    if (InvokeRequired)
                    {
                        Invoke(() => UpdateKPICard(card, data));
                    }
                    else
                    {
                        UpdateKPICard(card, data);
                    }
                }
            }
            catch (Exception ex)
            {
                // Log error and continue
              //  Console.WriteLine($"Error updating KPI data: {ex.Message}");
            }
            
            await Task.Delay(30000); // Update every 30 seconds
        }
    }
    
    private void UpdateKPICard(BeepStatCard card, KPIData data)
    {
        card.ValueText = data.Value.ToString(data.Format ?? "N0");
        card.PercentageText = $"{(data.ChangePercent >= 0 ? "+" : "")}{data.ChangePercent:F1}%";
        card.IsTrendingUp = data.ChangePercent >= 0;
        
        // Contextual trend messages
        if (Math.Abs(data.ChangePercent) < 1)
            card.TrendText = "Stable performance";
        else if (data.ChangePercent >= 5)
            card.TrendText = "Excellent growth";
        else if (data.ChangePercent >= 1)
            card.TrendText = "Positive trend";
        else if (data.ChangePercent <= -5)
            card.TrendText = "Immediate attention needed";
        else
            card.TrendText = "Below expectations";
            
        card.InfoText = $"Updated {DateTime.Now:HH:mm}";
    }
}

E-commerce Analytics

public class EcommerceAnalytics : UserControl
{
    private readonly Dictionary _metricCards;
    private readonly Timer _refreshTimer;
    
    public EcommerceAnalytics()
    {
        _metricCards = new Dictionary();
        CreateAnalyticsCards();
        
        _refreshTimer = new Timer(60000); // Refresh every minute
        _refreshTimer.Elapsed += RefreshMetrics;
        _refreshTimer.Start();
    }
    
    private void CreateAnalyticsCards()
    {
        var metrics = new[]
        {
            new { Key = "revenue", Title = "Today's Revenue", Icon = "money" },
            new { Key = "orders", Title = "Orders Placed", Icon = "shopping-cart" },
            new { Key = "visitors", Title = "Unique Visitors", Icon = "users" },
            new { Key = "conversion", Title = "Conversion Rate", Icon = "percentage" },
            new { Key = "avgOrder", Title = "Average Order Value", Icon = "receipt" },
            new { Key = "returnRate", Title = "Return Rate", Icon = "return" }
        };
        
        int col = 0, row = 0;
        
        foreach (var metric in metrics)
        {
            var card = new BeepStatCard
            {
                Size = new Size(250, 140),
                Location = new Point(20 + col * 270, 20 + row * 160),
                HeaderText = metric.Title,
                Theme = "MaterialDesignTheme"
            };
            
            CardIconAssets.ApplyIconSet(card, metric.Icon);
            _metricCards[metric.Key] = card;
            this.Controls.Add(card);
            
            col++;
            if (col >= 3) { col = 0; row++; }
        }
    }
    
    private async void RefreshMetrics(object sender, ElapsedEventArgs e)
    {
        try
        {
            var analytics = await GetEcommerceAnalytics();
            
            if (InvokeRequired)
            {
                Invoke(() => UpdateAnalyticsDisplay(analytics));
            }
            else
            {
                UpdateAnalyticsDisplay(analytics);
            }
        }
        catch (Exception ex)
        {
            // Handle error gracefully
            LogError("Failed to refresh analytics", ex);
        }
    }
    
    private void UpdateAnalyticsDisplay(EcommerceAnalytics analytics)
    {
        // Revenue card
        UpdateCard("revenue", 
            analytics.TodayRevenue.ToString("C0"),
            analytics.RevenueChangePercent,
            GetRevenueContext(analytics.TodayRevenue, analytics.YesterdayRevenue));
        
        // Orders card
        UpdateCard("orders",
            analytics.OrdersToday.ToString("N0"),
            analytics.OrdersChangePercent,
            $"vs {analytics.OrdersYesterday} yesterday");
        
        // Visitors card
        UpdateCard("visitors",
            analytics.UniqueVisitors.ToString("N0"),
            analytics.VisitorsChangePercent,
            "24-hour unique visitors");
        
        // Conversion rate
        UpdateCard("conversion",
            analytics.ConversionRate.ToString("P2"),
            analytics.ConversionChangePercent,
            "Visitor to purchase rate");
        
        // Average order value
        UpdateCard("avgOrder",
            analytics.AverageOrderValue.ToString("C0"),
            analytics.AOVChangePercent,
            "Per transaction average");
        
        // Return rate
        UpdateCard("returnRate",
            analytics.ReturnRate.ToString("P1"),
            analytics.ReturnRateChangePercent,
            "30-day return rate");
    }
    
    private void UpdateCard(string key, string value, double changePercent, string context)
    {
        if (!_metricCards.TryGetValue(key, out var card)) return;
        
        card.ValueText = value;
        card.PercentageText = $"{(changePercent >= 0 ? "+" : "")}{changePercent:F1}%";
        card.IsTrendingUp = changePercent >= 0;
        card.InfoText = context;
        
        // Dynamic trend messaging
        card.TrendText = GetTrendMessage(changePercent);
    }
    
    private string GetTrendMessage(double changePercent)
    {
        return Math.Abs(changePercent) switch
        {
            < 1 => "Steady",
            < 5 => changePercent > 0 ? "Growing" : "Declining",
            < 10 => changePercent > 0 ? "Strong growth" : "Sharp decline",
            _ => changePercent > 0 ? "Exceptional growth" : "Critical decline"
        };
    }
}

⭐ Best Practices

📊 Data Presentation

  • Clear Hierarchy: Use font sizes and colors to establish visual hierarchy
  • Meaningful Metrics: Display only actionable and relevant metrics
  • Context Provision: Always provide context for what the numbers represent
  • Consistent Formatting: Use consistent number formatting across similar metrics

🎨 Visual Design

  • Color Psychology: Use green for positive trends, red for negative, gray for neutral
  • Icon Consistency: Maintain consistent icon style and sizing across all cards
  • White Space: Provide adequate spacing between elements for readability
  • Responsive Layout: Design cards to work well at different sizes

⚡ Performance

  • Efficient Updates: Batch updates and use appropriate refresh intervals
  • Data Caching: Cache frequently accessed data to reduce API calls
  • Lazy Loading: Load card data only when cards become visible
  • Memory Management: Properly dispose of timers and event handlers

🔄 Real-Time Updates

  • Update Frequency: Choose appropriate refresh intervals (not too frequent)
  • Visual Feedback: Indicate when data is being updated
  • Error Handling: Gracefully handle network errors and data issues
  • Thread Safety: Use proper thread synchronization for UI updates
⚠️ Common Pitfalls
  • Information Overload: Don't crowd too much information into a single card
  • Misleading Trends: Ensure trend indicators accurately represent the data
  • Poor Context: Avoid showing percentages without indicating what they're relative to
  • Update Frequency: Don't update too frequently; it can be distracting
✅ Success Indicators
  • Quick Comprehension: Users can understand the metric within 2-3 seconds
  • Actionable Insights: Cards provide enough context for decision-making
  • Visual Consistency: All cards follow the same design patterns
  • Responsive Updates: Data updates smoothly without disrupting user experience