diff --git a/src/GmRelay.Shared/Features/Showcase/ShowcaseSessionDto.cs b/src/GmRelay.Shared/Features/Showcase/ShowcaseSessionDto.cs index 3c0e13e..4ff58c8 100644 --- a/src/GmRelay.Shared/Features/Showcase/ShowcaseSessionDto.cs +++ b/src/GmRelay.Shared/Features/Showcase/ShowcaseSessionDto.cs @@ -16,4 +16,5 @@ public sealed record ShowcaseSessionDto( int? MaxPlayers, int ActivePlayerCount, int WaitlistedPlayerCount, - bool AllowDirectRegistration); + bool AllowDirectRegistration, + string? Description); diff --git a/src/GmRelay.Web/Components/Pages/PublicSession.razor b/src/GmRelay.Web/Components/Pages/PublicSession.razor index 3abbe13..0fc2fae 100644 --- a/src/GmRelay.Web/Components/Pages/PublicSession.razor +++ b/src/GmRelay.Web/Components/Pages/PublicSession.razor @@ -2,6 +2,7 @@ @layout PublicLayout @inject ISessionStore SessionStore @inject NavigationManager Navigation +@using GmRelay.Shared.Features.Showcase @PageTitleText @@ -30,10 +31,29 @@ else if (session is not null) + @if (!string.IsNullOrWhiteSpace(session.CoverImageUrl)) + { +
+ } +
@TranslateStatus(session.Status)

@session.Title

@session.GroupName

+
+ @if (!string.IsNullOrWhiteSpace(session.System)) + { + @GetSystemDisplayName(session.System) + } + @if (session.IsOneShot) + { + Ваншот + } + @if (!string.IsNullOrWhiteSpace(session.Format)) + { + @TranslateFormat(session.Format) + } +
@@ -50,14 +70,33 @@ else if (session is not null) Статус @TranslateStatus(session.Status) + @if (session.DurationMinutes.HasValue) + { +
+ Длительность + @FormatDuration(session.DurationMinutes.Value) +
+ } + @if (!string.IsNullOrWhiteSpace(session.Description)) + { +
+

Описание

+

@session.Description

+
+ } +
@if (!string.IsNullOrWhiteSpace(session.GroupSlug)) { Расписание клуба } Ссылка на сессию + @if (session.AllowDirectRegistration) + { + Записаться + }
} @@ -65,7 +104,7 @@ else if (session is not null) @code { [Parameter] public Guid SessionId { get; set; } - private WebPublicSession? session; + private ShowcaseSessionDto? session; private bool loaded; private string PageTitleText => session is null ? "Публичная сессия — GM-Relay" : $"{session.Title} — GM-Relay"; @@ -75,11 +114,11 @@ else if (session is not null) protected override async Task OnParametersSetAsync() { loaded = false; - session = await SessionStore.GetPublicSessionAsync(SessionId); + session = await SessionStore.GetShowcaseSessionAsync(SessionId); loaded = true; } - private static string FormatSeats(WebPublicSession session) + private static string FormatSeats(ShowcaseSessionDto session) { var seats = session.MaxPlayers.HasValue ? $"{session.ActivePlayerCount}/{session.MaxPlayers.Value}" @@ -90,6 +129,35 @@ else if (session is not null) : 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 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 TranslateFormat(string format) => format switch + { + "Online" => "Онлайн", + "Offline" => "Офлайн", + "Hybrid" => "Гибрид", + _ => format + }; + private static string GetStatusClass(string status) => status switch { SessionStatus.Confirmed => "status-success", diff --git a/src/GmRelay.Web/Services/SessionService.cs b/src/GmRelay.Web/Services/SessionService.cs index 815512f..3e5f849 100644 --- a/src/GmRelay.Web/Services/SessionService.cs +++ b/src/GmRelay.Web/Services/SessionService.cs @@ -127,7 +127,8 @@ internal sealed record ShowcaseSessionRow( int? MaxPlayers, int ActivePlayerCount, int WaitlistedPlayerCount, - bool AllowDirectRegistration); + bool AllowDirectRegistration, + string? Description); public sealed class SessionService( NpgsqlDataSource dataSource, @@ -400,7 +401,8 @@ public sealed class SessionService( s.max_players AS MaxPlayers, COALESCE(active_counts.count, 0)::int AS ActivePlayerCount, COALESCE(waitlist_counts.count, 0)::int AS WaitlistedPlayerCount, - s.allow_direct_registration AS AllowDirectRegistration + s.allow_direct_registration AS AllowDirectRegistration, + s.description AS Description FROM sessions s JOIN game_groups g ON g.id = s.group_id LEFT JOIN LATERAL ( @@ -463,7 +465,8 @@ public sealed class SessionService( return rows.Select(r => new ShowcaseSessionDto( r.Id, r.GroupId, r.GroupName, r.GroupSlug, r.Title, r.ScheduledAt, r.Status, r.System, r.IsOneShot, r.Format, r.DurationMinutes, r.CoverImageUrl, - r.MaxPlayers, r.ActivePlayerCount, r.WaitlistedPlayerCount, r.AllowDirectRegistration)).ToList(); + r.MaxPlayers, r.ActivePlayerCount, r.WaitlistedPlayerCount, r.AllowDirectRegistration, + r.Description)).ToList(); } public async Task GetShowcaseSessionAsync(Guid sessionId) @@ -486,7 +489,8 @@ public sealed class SessionService( s.max_players AS MaxPlayers, COALESCE(active_counts.count, 0)::int AS ActivePlayerCount, COALESCE(waitlist_counts.count, 0)::int AS WaitlistedPlayerCount, - s.allow_direct_registration AS AllowDirectRegistration + s.allow_direct_registration AS AllowDirectRegistration, + s.description AS Description FROM sessions s JOIN game_groups g ON g.id = s.group_id LEFT JOIN LATERAL ( @@ -531,7 +535,8 @@ public sealed class SessionService( return new ShowcaseSessionDto( row.Id, row.GroupId, row.GroupName, row.GroupSlug, row.Title, row.ScheduledAt, row.Status, row.System, row.IsOneShot, row.Format, row.DurationMinutes, row.CoverImageUrl, - row.MaxPlayers, row.ActivePlayerCount, row.WaitlistedPlayerCount, row.AllowDirectRegistration); + row.MaxPlayers, row.ActivePlayerCount, row.WaitlistedPlayerCount, row.AllowDirectRegistration, + row.Description); } public async Task RegisterFromShowcaseAsync(Guid sessionId, string platform, string externalUserId, string displayName)