LeaderboardModal.razor

A UI Razor component for a global leaderboard modal. It shows tabs for two stats, fetches leaderboard data from the Leaderboards service, displays top entries with rank, player name and score, and highlights the local player.

Networking
@using Sandbox;
@using Sandbox.UI;
@using Sandbox.Services;
@using System;
@using System.Threading.Tasks;
@using System.Collections.Generic;
@using System.Linq;
@inherits Panel

<root class="lb-root @(IsOpen ? "open" : "")">
    <div class="lb-card">
        <div class="lb-header">
            <div class="lb-title">GLOBAL LEADERBOARD</div>
            <button class="lb-close" onclick="@Toggle">✕</button>
        </div>

        <div class="lb-tabs">
            <button class="tab-btn @(CurrentStat == "pushes_count" ? "active" : "")" onclick="@(() => SelectStat("pushes_count"))">
                PUSHES
            </button>
            <button class="tab-btn @(CurrentStat == "rounds_won" ? "active" : "")" onclick="@(() => SelectStat("rounds_won"))">
                WINS
            </button>
        </div>

        <div class="lb-content">
            @if (IsLoading)
            {
                <div class="lb-status">LOADING DATA...</div>
            }
            else if (Entries == null || Entries.Count == 0)
            {
                <div class="lb-status">NO RECORD FOUND</div>
            }
            else
            {
                <div class="lb-table">
                    <div class="table-header">
                        <span class="col-rank">#</span>
                        <span class="col-player">PLAYER</span>
                        <span class="col-score">SCORE</span>
                    </div>
                    <div class="table-body">
                        @foreach (var entry in Entries)
                        {
                            <div class="table-row @(entry.SteamId == (Connection.Local?.SteamId ?? 0) ? "is-me" : "")">
                                <span class="col-rank">@GetRankBadge(entry.Rank)</span>
                                <span class="col-player">@entry.DisplayName</span>
                                <span class="col-score">@entry.Value.ToString("N0")</span>
                            </div>
                        }
                    </div>
                </div>
            }
        </div>
    </div>
</root>

@code {
    public class EntryItem
    {
        public int Rank { get; set; }
        public string DisplayName { get; set; }
        public double Value { get; set; }
        public ulong SteamId { get; set; }
    }

    public bool IsOpen { get; set; } = false;
    public bool IsLoading { get; set; } = true;
    public string CurrentStat { get; set; } = "pushes_count";
    public List<EntryItem> Entries { get; set; } = new();

    protected override void OnAfterTreeRender( bool firstTime )
    {
        base.OnAfterTreeRender( firstTime );
        if ( firstTime )
        {
            _ = RefreshData();
        }
    }

    public void Toggle()
    {
        IsOpen = !IsOpen;
        if (IsOpen)
        {
            _ = RefreshData();
        }
        StateHasChanged();
    }

    private async Task SelectStat(string statIdent)
    {
        if (CurrentStat == statIdent) return;
        CurrentStat = statIdent;
        await RefreshData();
    }

    private async Task RefreshData()
    {
        IsLoading = true;
        StateHasChanged();

        try
        {
            var board = Leaderboards.GetFromStat("seilordstudio.one_more_push", CurrentStat);
            board.SetAggregationSum();
            board.MaxEntries = 20;
            await board.Refresh();

            if (board.Entries != null)
            {
                Entries = board.Entries.Select(x => new EntryItem
                {
                    Rank = Convert.ToInt32(x.Rank),
                    DisplayName = x.DisplayName ?? "Player",
                    Value = Convert.ToDouble(x.Value),
                    SteamId = Convert.ToUInt64(x.SteamId)
                }).ToList();
            }
            else
            {
                Entries = new();
            }
        }
        catch (System.Exception ex)
        {
            Log.Warning($"Leaderboard fetch failed: {ex.Message}");
        }

        IsLoading = false;
        StateHasChanged();
    }

    private string GetRankBadge(int rank)
    {
        return rank switch
        {
            1 => "🥇 1",
            2 => "🥈 2",
            3 => "🥉 3",
            _ => rank.ToString()
        };
    }
}