feat: implement Blazor web interface for GM session management
Deploy Telegram Bot / deploy (push) Has been cancelled

- Created GmRelay.Web project (Blazor Server)
- Created GmRelay.Shared library for domain models and rendering
- Refactored GmRelay.Bot to use the Shared library
- Integrated Telegram Login widget with server-side HMAC verification
- Added Dashboard, Group Details, and Edit Session pages
- Enabled bot notifications and in-place message updates from web actions
- Updated .NET Aspire orchestration and Docker Compose configuration
This commit is contained in:
2026-04-17 11:06:59 +03:00
parent c27456e726
commit 988133e389
93 changed files with 61016 additions and 34 deletions
@@ -0,0 +1,80 @@
@page "/group/{GroupId:guid}"
@using GmRelay.Web.Services
@using GmRelay.Shared.Domain
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inject SessionService SessionService
<PageTitle>Group Sessions - GM-Relay</PageTitle>
<div class="container mt-4">
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">Dashboard</a></li>
<li class="breadcrumb-item active">Group Sessions</li>
</ol>
</nav>
<h2>Upcoming Sessions</h2>
<div class="mt-4">
@if (sessions == null)
{
<p>Loading sessions...</p>
}
else if (sessions.Count == 0)
{
<div class="alert alert-info">No upcoming sessions found for this group.</div>
}
else
{
<div class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-light">
<tr>
<th>Title</th>
<th>Time (MSK)</th>
<th>Status</th>
<th>Link</th>
<th>Action</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)">@session.Status</span>
</td>
<td><a href="@session.JoinLink" target="_blank" class="text-truncate d-inline-block" style="max-width: 150px;">Link</a></td>
<td>
<a href="/session/edit/@session.Id" class="btn btn-sm btn-outline-secondary">Edit</a>
</td>
</tr>
}
</tbody>
</table>
</div>
}
</div>
</div>
@code {
[Parameter] public Guid GroupId { get; set; }
private List<WebSession>? sessions;
protected override async Task OnInitializedAsync()
{
sessions = await SessionService.GetUpcomingSessionsAsync(GroupId);
}
private string GetStatusClass(string status) => status switch
{
SessionStatus.Confirmed => "bg-success",
SessionStatus.Cancelled => "bg-danger",
SessionStatus.ConfirmationSent => "bg-warning text-dark",
_ => "bg-secondary"
};
}