@page "/portfolio/{Slug}" @layout PublicLayout @inject IPortfolioStore PortfolioStore @inject AuthorizedPortfolioService AuthorizedPortfolio @inject NavigationManager Navigation @inject AuthenticationStateProvider AuthStateProvider @using GmRelay.Shared.Domain @using GmRelay.Web.Services.Portfolio @PageTitleText @if (loaded && game is null) {
Недоступно

Портфолио не найдено

Эта игра скрыта, ещё не опубликована или короткий адрес больше не используется.

} else if (!loaded) {
} else if (game is not null) { @if (!string.IsNullOrWhiteSpace(game.CoverPath)) {
}
Завершено

@game.Title

Завершено @game.CompletedAt.ToLocalTime().FormatMoscow()

@if (!string.IsNullOrWhiteSpace(game.System)) { @GetSystemDisplayName(game.System) } @if (!string.IsNullOrWhiteSpace(game.Format)) { @TranslateFormat(game.Format) }
@if (!string.IsNullOrWhiteSpace(game.Description)) {

Описание

@game.Description

} @if (game.Masters.Count > 0) { } @if (!string.IsNullOrWhiteSpace(game.ClubSlug) && !string.IsNullOrWhiteSpace(game.ClubName)) { }
Ссылка на портфолио

Отзывы игроков

@if (game.Reviews.Count == 0) {

Пока нет одобренных отзывов.

} else { }

Оставить отзыв

@switch (submissionState) { case PortfolioReviewSubmissionState.RequiresAuthentication:

Войдите, чтобы оставить отзыв об этом приключении.

Войти
break; case PortfolioReviewSubmissionState.Ineligible:

Отзыв могут оставить только игроки, участвовавшие в этом приключении.

break; case PortfolioReviewSubmissionState.AlreadySubmitted:

Отзыв отправлен на модерацию.

break; case PortfolioReviewSubmissionState.Eligible:
@if (!string.IsNullOrWhiteSpace(submissionError)) {

@submissionError

}
break; }
} @code { [Parameter] public string? Slug { get; set; } private PublicPortfolioGame? game; private PortfolioReviewSubmissionState submissionState = PortfolioReviewSubmissionState.RequiresAuthentication; private ReviewFormModel reviewModel = new(); private string? submissionError; private bool isSubmitting; private bool loaded; private string PageTitleText => game is null ? "Портфолио — GM-Relay" : $"{game.Title} — GM-Relay"; private string PublicPortfolioUrl => Navigation.ToAbsoluteUri($"/portfolio/{Slug}").ToString(); private string GetLoginUrl() => $"/login?returnUrl={Uri.EscapeDataString($"/portfolio/{Slug}")}"; protected override async Task OnParametersSetAsync() { loaded = false; var trimmedSlug = string.IsNullOrWhiteSpace(Slug) ? null : Slug.Trim(); game = trimmedSlug is null ? null : await PortfolioStore.GetPublicPortfolioGameBySlugAsync(trimmedSlug); if (game is not null) { submissionState = await AuthorizedPortfolio.GetReviewSubmissionStateForCurrentUserAsync(game.Slug); } reviewModel = new ReviewFormModel(); submissionError = null; isSubmitting = false; loaded = true; } private async Task SubmitReviewAsync() { if (game is null) { return; } if (!reviewModel.PublicationConsent) { submissionError = "Нужно подтвердить согласие на публикацию."; return; } if (string.IsNullOrWhiteSpace(reviewModel.Body) || reviewModel.Body.Trim().Length < 10) { submissionError = "Отзыв должен содержать не меньше 10 символов."; return; } isSubmitting = true; submissionError = null; try { await AuthorizedPortfolio.SubmitReviewForCurrentUserAsync( game.Slug, reviewModel.Body, reviewModel.PublicationConsent); submissionState = PortfolioReviewSubmissionState.AlreadySubmitted; reviewModel = new ReviewFormModel(); } catch (Exception ex) { submissionError = ex.Message; } finally { isSubmitting = false; } } 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 sealed class ReviewFormModel { public string Body { get; set; } = string.Empty; public bool PublicationConsent { get; set; } } }