v1.1.0: Полный редизайн фронтенда, усиление безопасности и обновление версии
Deploy Telegram Bot / build-and-push (push) Successful in 5m19s
Deploy Telegram Bot / deploy (push) Successful in 10s

This commit is contained in:
2026-04-21 15:21:18 +03:00
parent b6af5f047c
commit 176f1105ab
18 changed files with 1392 additions and 413 deletions
@@ -5,60 +5,105 @@
@attribute [Authorize]
@inject SessionService SessionService
<PageTitle>Сессии группы - GM-Relay</PageTitle>
<PageTitle>Сессии группы GM-Relay</PageTitle>
<div class="container mt-4">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Главная</a></li>
<li class="breadcrumb-item active">Сессии группы</li>
</ol>
</nav>
<div class="page-container">
<ul class="gm-breadcrumb animate-fade-in">
<li><a href="/">Главная</a></li>
<li class="active">Сессии группы</li>
</ul>
<h2>Предстоящие игры</h2>
<div class="mt-4">
@if (sessions == null)
{
<p>Загрузка сессий...</p>
}
else if (sessions.Count == 0)
{
<div class="alert alert-info">Для этой группы не найдено предстоящих сессий.</div>
}
else
{
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Название</th>
<th>Время (МСК)</th>
<th>Статус</th>
<th>Ссылка</th>
<th>Действие</th>
</tr>
</thead>
<tbody>
@foreach (var session in sessions)
{
<tr>
<td>@session.Title</td>
<td>@session.ScheduledAt.FormatMoscow()</td>
<td>
<span class="badge @GetStatusClass(session.Status)">@TranslateStatus(session.Status)</span>
</td>
<td><a href="@session.JoinLink" target="_blank" class="text-truncate d-inline-block" style="max-width: 150px;">Ссылка</a></td>
<td>
<a href="/session/edit/@session.Id" class="btn btn-sm btn-outline-secondary">Изменить</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
<div class="page-header animate-fade-in">
<h2>📅 Предстоящие игры</h2>
</div>
@if (sessions == null)
{
<div class="glass-card" style="padding: 2rem;">
<div class="skeleton skeleton-text" style="width: 80%; margin-bottom: 1rem;"></div>
<div class="skeleton skeleton-text" style="width: 60%; margin-bottom: 0.75rem;"></div>
<div class="skeleton skeleton-text" style="width: 70%; margin-bottom: 0.75rem;"></div>
<div class="skeleton skeleton-text" style="width: 50%;"></div>
</div>
}
else if (sessions.Count == 0)
{
<div class="glass-card">
<div class="empty-state">
<div class="empty-state-icon">🎯</div>
<div class="empty-state-title">Нет предстоящих сессий</div>
<p class="empty-state-text">Для этой группы пока не запланировано игровых сессий.</p>
</div>
</div>
}
else
{
@* Desktop table *@
<div class="glass-card session-table-desktop animate-slide-up" style="padding: 0; overflow: hidden;">
<table class="gm-table">
<thead>
<tr>
<th>Название</th>
<th>Время (МСК)</th>
<th>Статус</th>
<th>Ссылка</th>
<th>Действие</th>
</tr>
</thead>
<tbody>
@foreach (var session in sessions)
{
<tr>
<td style="color: var(--text-primary); font-weight: 500;">@session.Title</td>
<td>@session.ScheduledAt.FormatMoscow()</td>
<td>
<span class="status-badge @GetStatusClass(session.Status)">@TranslateStatus(session.Status)</span>
</td>
<td>
<a href="@session.JoinLink" target="_blank" rel="noopener noreferrer"
style="max-width: 150px; display: inline-block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
Подключиться ↗
</a>
</td>
<td>
<a href="/session/edit/@session.Id" class="btn-gm btn-gm-outline" style="font-size: 0.8125rem; padding: 0.375rem 0.75rem;">
✏️ Изменить
</a>
</td>
</tr>
}
</tbody>
</table>
</div>
@* Mobile cards *@
<div class="session-card-mobile stagger-children">
@foreach (var session in sessions)
{
<div class="session-card">
<div class="session-card-header">
<span class="session-card-title">@session.Title</span>
<span class="status-badge @GetStatusClass(session.Status)">@TranslateStatus(session.Status)</span>
</div>
<div class="session-card-body">
<div class="session-card-row">
<span>🕐 Время</span>
<span style="color: var(--text-primary);">@session.ScheduledAt.FormatMoscow()</span>
</div>
<div class="session-card-row">
<span>🔗 Ссылка</span>
<a href="@session.JoinLink" target="_blank" rel="noopener noreferrer">Подключиться ↗</a>
</div>
</div>
<div class="session-card-actions">
<a href="/session/edit/@session.Id" class="btn-gm btn-gm-outline" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;">
✏️ Изменить
</a>
</div>
</div>
}
</div>
}
</div>
@code {
@@ -72,10 +117,12 @@
private string GetStatusClass(string status) => status switch
{
SessionStatus.Confirmed => "bg-success",
SessionStatus.Cancelled => "bg-danger",
SessionStatus.ConfirmationSent => "bg-warning text-dark",
_ => "bg-secondary"
SessionStatus.Confirmed => "status-success",
SessionStatus.Cancelled => "status-danger",
SessionStatus.ConfirmationSent => "status-warning",
"Recruiting" => "status-info",
"RecruitmentClosed" => "status-info",
_ => "status-neutral"
};
private string TranslateStatus(string status) => status switch
@@ -83,7 +130,7 @@
"Recruiting" => "Набор",
"RecruitmentClosed" => "Набор закрыт",
SessionStatus.Planned => "Запланировано",
SessionStatus.ConfirmationSent => "Ждем подтверждения",
SessionStatus.ConfirmationSent => "Ждём подтверждения",
SessionStatus.Confirmed => "Подтверждено",
SessionStatus.Cancelled => "Отменено",
_ => status