@page "/showcase" @layout PublicLayout @inject ISessionStore SessionStore @inject NavigationManager Navigation @using GmRelay.Shared.Features.Showcase Каталог игр — GM-Relay

Каталог игр

Найдите настольную ролевую игру по душе — ваншоты, кампании, онлайн и офлайн.

Когда
Места
Тип
Формат
@if (loading && sessions.Count == 0) {
@for (var i = 0; i < 6; i++) {
}
} else if (!loading && sessions.Count == 0) {

Игры не найдены

Попробуйте изменить фильтры или загляните позже — новые сессии появляются каждый день.

} else {
@foreach (var session in sessions) {
@if (!string.IsNullOrWhiteSpace(session.System)) { @GetSystemDisplayName(session.System) } @if (session.IsOneShot) { Ваншот } @if (!string.IsNullOrWhiteSpace(session.Format)) { @TranslateFormat(session.Format) }

@session.Title

@session.ScheduledAt.FormatMoscow() @if (session.DurationMinutes.HasValue) { @FormatDuration(session.DurationMinutes.Value) }
@FormatSeats(session)
@session.GroupName
@if (!string.IsNullOrWhiteSpace(session.MasterProfileSlug)) { }
Подробнее @if (session.AllowDirectRegistration) { Записаться }
}
@if (hasMore) {
} } @code { private ShowcaseFilter filter = new(); private List sessions = new(); private bool loading; private bool hasMore; private int page = 1; private const int PageSize = 12; protected override async Task OnInitializedAsync() { await LoadAsync(); } private async Task LoadAsync() { loading = true; try { page = 1; sessions.Clear(); var results = await SessionStore.GetShowcaseSessionsAsync(filter, page, PageSize); sessions.AddRange(results); hasMore = results.Count == PageSize; } finally { loading = false; } } private async Task LoadMoreAsync() { if (loading) return; loading = true; try { page++; var results = await SessionStore.GetShowcaseSessionsAsync(filter, page, PageSize); sessions.AddRange(results); hasMore = results.Count == PageSize; } finally { loading = false; } } private async Task OnFilterChanged() { await LoadAsync(); } private async Task SetDate(DateFilter value) { filter = filter with { Date = value }; await OnFilterChanged(); } private async Task SetSeats(SeatFilter value) { filter = filter with { Seats = value }; await OnFilterChanged(); } private async Task OnSystemChanged(ChangeEventArgs e) { var value = e.Value?.ToString(); filter = filter with { System = string.IsNullOrWhiteSpace(value) ? null : value }; await OnFilterChanged(); } private async Task SetOneShot(bool? value) { filter = filter with { IsOneShot = value }; await OnFilterChanged(); } private async Task SetFormat(string? value) { filter = filter with { Format = value }; await OnFilterChanged(); } private static string GetGradientStyle(Guid id) { var bytes = id.ToByteArray(); var hue1 = bytes[0] % 360; var hue2 = (bytes[1] + 120) % 360; return $"linear-gradient(135deg, hsl({hue1}, 55%, 28%) 0%, hsl({hue2}, 55%, 20%) 100%)"; } private static string GetSystemDisplayName(string? system) { if (string.IsNullOrWhiteSpace(system)) return system ?? string.Empty; if (Enum.TryParse(system, out var gs)) return gs.ToDisplayName(); return system; } private static string FormatSeats(ShowcaseSessionDto session) { var seats = session.MaxPlayers.HasValue ? $"{session.ActivePlayerCount}/{session.MaxPlayers.Value}" : $"{session.ActivePlayerCount} игроков"; if (session.WaitlistedPlayerCount > 0) seats += $", ожидание {session.WaitlistedPlayerCount}"; return seats; } private static string FormatDuration(int minutes) { if (minutes < 60) return $"{minutes} мин"; var hours = minutes / 60; var mins = minutes % 60; return mins > 0 ? $"{hours} ч {mins} мин" : $"{hours} ч"; } private static string MasterProfilePath(string slug) => $"/gm/{slug}"; private static string TranslateFormat(string format) => format switch { "Online" => "Онлайн", "Offline" => "Офлайн", "Hybrid" => "Гибрид", _ => format }; }