feat(#15): add SessionHistory.razor, navigation links, and bump version to 1.10.2
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>1.10.1</Version>
|
||||
<Version>1.10.2</Version>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<LangVersion>preview</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ services:
|
||||
retries: 10
|
||||
|
||||
bot:
|
||||
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.10.0
|
||||
image: git.codeanddice.ru/toutsu/gmrelay-bot:1.10.2
|
||||
restart: always
|
||||
depends_on:
|
||||
db:
|
||||
@@ -30,7 +30,7 @@ services:
|
||||
- gmrelay
|
||||
|
||||
web:
|
||||
image: git.codeanddice.ru/toutsu/gmrelay-web:1.10.0
|
||||
image: git.codeanddice.ru/toutsu/gmrelay-web:1.10.2
|
||||
restart: always
|
||||
depends_on:
|
||||
db:
|
||||
|
||||
@@ -252,6 +252,7 @@
|
||||
<a href="/session/edit/@session.Id" class="btn-gm btn-gm-outline">
|
||||
✏️ Изменить
|
||||
</a>
|
||||
<a href="/session/@session.Id/history" class="btn-gm btn-gm-outline">📜 История</a>
|
||||
@if (CanPromote(session))
|
||||
{
|
||||
<button type="button" class="btn-gm btn-gm-success" disabled="@(promotingSessionId == session.Id)" @onclick="() => PromoteWaitlisted(session.Id)">
|
||||
@@ -339,6 +340,7 @@
|
||||
<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>
|
||||
<a href="/session/@session.Id/history" class="btn-gm btn-gm-outline" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;">📜 История</a>
|
||||
@if (CanPromote(session))
|
||||
{
|
||||
<button type="button" class="btn-gm btn-gm-success" style="flex: 1; justify-content: center; font-size: 0.8125rem; padding: 0.5rem;" disabled="@(promotingSessionId == session.Id)" @onclick="() => PromoteWaitlisted(session.Id)">
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
@page "/session/{SessionId:guid}/history"
|
||||
@using GmRelay.Web.Services
|
||||
@using Microsoft.AspNetCore.Authorization
|
||||
@using Microsoft.AspNetCore.Components.Authorization
|
||||
@attribute [Authorize]
|
||||
@inject AuthorizedSessionService SessionService
|
||||
@inject AuthenticationStateProvider AuthStateProvider
|
||||
@inject NavigationManager Navigation
|
||||
|
||||
<PageTitle>История изменений — GM-Relay</PageTitle>
|
||||
|
||||
<div class="page-container">
|
||||
<ul class="gm-breadcrumb animate-fade-in">
|
||||
<li><a href="/">Главная</a></li>
|
||||
<li><a href="/group/@groupId">Группа</a></li>
|
||||
<li class="active">История изменений</li>
|
||||
</ul>
|
||||
|
||||
<div class="page-header animate-fade-in">
|
||||
<h2>📜 История изменений</h2>
|
||||
@if (sessionTitle is not null)
|
||||
{
|
||||
<p style="color: var(--text-muted); margin-top: 0.25rem;">@sessionTitle</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (entries is null)
|
||||
{
|
||||
<div class="glass-card" style="padding: 2rem;">
|
||||
<div class="skeleton skeleton-text" style="width: 50%; margin-bottom: 1.5rem;"></div>
|
||||
<div class="skeleton skeleton-text" style="width: 100%; height: 2.5rem; margin-bottom: 1.5rem;"></div>
|
||||
</div>
|
||||
}
|
||||
else if (entries.Count == 0)
|
||||
{
|
||||
<div class="glass-card animate-slide-up" style="padding: 2rem; text-align: center;">
|
||||
<p style="color: var(--text-muted);">История изменений пуста. Значимые изменения (время, ссылка, название, участники) будут отображаться здесь.</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="glass-card animate-slide-up">
|
||||
<div class="table-responsive">
|
||||
<table class="gm-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Время</th>
|
||||
<th>Актор</th>
|
||||
<th>Тип изменения</th>
|
||||
<th>Было</th>
|
||||
<th>Стало</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var entry in entries)
|
||||
{
|
||||
<tr>
|
||||
<td>@entry.ChangedAt.ToString("dd.MM.yyyy HH:mm") UTC</td>
|
||||
<td>@entry.ActorName (@entry.ActorTelegramId)</td>
|
||||
<td>
|
||||
<span class="status-badge @(GetBadgeClass(entry.ChangeType))">
|
||||
@GetChangeTypeLabel(entry.ChangeType)
|
||||
</span>
|
||||
</td>
|
||||
<td style="max-width: 200px; overflow-wrap: break-word; color: var(--text-muted);">@(entry.OldValue ?? "—")</td>
|
||||
<td style="max-width: 200px; overflow-wrap: break-word;">@(entry.NewValue ?? "—")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public Guid SessionId { get; set; }
|
||||
private List<SessionAuditLogEntry>? entries;
|
||||
private string? sessionTitle;
|
||||
private Guid? groupId;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
||||
if (!authState.User.TryGetTelegramId(out var telegramId))
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
return;
|
||||
}
|
||||
|
||||
var session = await SessionService.GetSessionForGmAsync(SessionId, telegramId);
|
||||
if (session is null)
|
||||
{
|
||||
Navigation.NavigateTo("/access-denied");
|
||||
return;
|
||||
}
|
||||
|
||||
sessionTitle = session.Title;
|
||||
groupId = session.GroupId;
|
||||
entries = await SessionService.GetSessionHistoryForGmAsync(SessionId, telegramId);
|
||||
}
|
||||
|
||||
private string GetChangeTypeLabel(string changeType) => changeType switch
|
||||
{
|
||||
"Title" => "Название",
|
||||
"Time" => "Время",
|
||||
"Link" => "Ссылка",
|
||||
"MaxPlayers" => "Лимит мест",
|
||||
"Status" => "Статус",
|
||||
"WaitlistPromote" => "Продвижение из листа ожидания",
|
||||
"PlayerRemoved" => "Исключение игрока",
|
||||
"BatchRescheduled" => "Перенос батча",
|
||||
"Cancelled" => "Отмена",
|
||||
_ => changeType
|
||||
};
|
||||
|
||||
private string GetBadgeClass(string changeType) => changeType switch
|
||||
{
|
||||
"Cancelled" or "PlayerRemoved" => "status-danger",
|
||||
"WaitlistPromote" => "status-success",
|
||||
"BatchRescheduled" or "Time" => "status-warning",
|
||||
_ => "status-info"
|
||||
};
|
||||
}
|
||||
@@ -241,6 +241,17 @@ public sealed class AuthorizedSessionService(ISessionStore sessionStore)
|
||||
return await sessionStore.GetSessionParticipantsAsync(sessionId);
|
||||
}
|
||||
|
||||
public async Task<List<SessionAuditLogEntry>?> GetSessionHistoryForGmAsync(Guid sessionId, long gmId)
|
||||
{
|
||||
var session = await GetSessionForGmAsync(sessionId, gmId);
|
||||
if (session is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return await sessionStore.GetSessionHistoryAsync(sessionId);
|
||||
}
|
||||
|
||||
public async Task RemovePlayerFromSessionForGmAsync(Guid sessionId, long gmId, Guid participantId)
|
||||
{
|
||||
var session = await GetSessionForGmAsync(sessionId, gmId);
|
||||
|
||||
Reference in New Issue
Block a user