Files
chapter-organizer/WebApp/Components/Features/Students/EventRankingEdit.razor
T
poprhythm b4c11cd0a6 Enhance UI with MudTooltip for action buttons across various components
This commit adds MudTooltip components to action buttons in the Calendar, Events, Students, and Teams features, improving user experience by providing contextual information on button actions. The changes ensure that users receive helpful hints when hovering over buttons, enhancing accessibility and usability throughout the application.
2026-01-17 10:41:46 -05:00

200 lines
7.3 KiB
Plaintext

@page "/students/event-ranking-edit/{StudentId:int}"
@attribute [Authorize]
@using Microsoft.EntityFrameworkCore
@using BlazorSortableList
@using WebApp.Models
@using WebApp.Components.Shared.Components
@inject AppDbContext Context
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
@inject ILogger<EventRankingEdit> Logger
@if (_student == null)
{
<p><em>Loading...</em></p>
}
else
{
<PageHeader
Title="Student Event Ranks"
Subtitle="@_student.Name"
Icon="@AppIcons.EventRank"
ShowBackButton="true"
BackButtonUrl="/students/event-ranking">
<ActionButtons>
<MudTooltip Text="Save Rankings">
<MudButton StartIcon="@Icons.Material.Filled.Save"
OnClick="Save"
Color="Color.Primary"
Variant="Variant.Filled">
Save Rankings
</MudButton>
</MudTooltip>
</ActionButtons>
</PageHeader>
@* https://github.com/AlexNek/BlazorSortableList *@
<MudGrid Class="mt-2">
<MudItem xs="12" md="6" xl="4">
<MudPaper Class="pa-3 mb-3" Elevation="2">
<div class="d-flex align-center mb-1">
<MudIcon Icon="@Icons.Material.Filled.FormatListNumbered" Class="mr-2" Color="Color.Primary" />
<MudText Typo="Typo.h6" Color="Color.Primary">Ranked Events</MudText>
</div>
<MudText Typo="Typo.caption" Color="Color.Secondary">Drag events here in order of preference</MudText>
</MudPaper>
<SortableList
Group="GroupId" Id="ListId1" Context="item"
Items="_rankedEvents" OnRemove="RankedEventsRemove" OnUpdate="Update">
<SortableItemTemplate>
<MudCard Outlined="true" Class="mb-2">
<MudCardContent Class="pa-2">
<div class="d-flex align-center">
<MudBadge Content="@(_rankedEvents.IndexOf(item) + 1)" Color="Color.Primary" Overlap="true" Class="mr-3">
<MudIcon Icon="@Icons.Material.Filled.DragIndicator" />
</MudBadge>
<div class="flex-grow-1">
<MudText Typo="Typo.body2"><strong>@item.Name</strong></MudText>
<MudText Typo="Typo.caption">
@AppIcons.EventAttributes(item) @AppIcons.EventEffort(item)
</MudText>
</div>
</div>
</MudCardContent>
</MudCard>
</SortableItemTemplate>
</SortableList>
</MudItem>
<MudItem xs="12" md="6" xl="4">
<MudPaper Class="pa-3 mb-3" Elevation="2">
<div class="d-flex align-center mb-1">
<MudIcon Icon="@AppIcons.Events" Class="mr-2" />
<MudText Typo="Typo.h6">Available Events</MudText>
</div>
<MudText Typo="Typo.caption" Color="Color.Secondary">Drag events to rank them</MudText>
</MudPaper>
<SortableList
Group="GroupId" Id="ListId2" Context="item"
Items="_availableEvents" OnRemove="AvailableEventsRemove" Sort="false">
<SortableItemTemplate>
<MudCard Outlined="true" Class="mb-2">
<MudCardContent Class="pa-2">
<MudText Typo="Typo.body2"><strong>@item.Name</strong></MudText>
<MudText Typo="Typo.caption">
@AppIcons.EventAttributes(item) @AppIcons.EventEffort(item)
</MudText>
</MudCardContent>
</MudCard>
</SortableItemTemplate>
</SortableList>
</MudItem>
</MudGrid>
}
@code {
private const string ListId1 = "SharedListId1";
private const string ListId2 = "SharedListId2";
private const string GroupId = "CommonGroup";
[Parameter] public int? StudentId { get; set; }
private Student? _student;
private List<EventDefinition>? _events;
public List<EventDefinition> _rankedEvents = [];
public List<EventDefinition> _availableEvents = [];
private void RankedEventsRemove((int oldIndex, int newIndex) indices)
{
// get the item at the old index in list 1
var item = _rankedEvents[indices.oldIndex];
// add it to the new index in list 2
_availableEvents.Insert(indices.newIndex, item);
// remove the item from the old index in list 1
_rankedEvents.Remove(_rankedEvents[indices.oldIndex]);
}
private void AvailableEventsRemove((int oldIndex, int newIndex) indices)
{
// get the item at the old index in list 2
var item = _availableEvents[indices.oldIndex];
// add it to the new index in list 1
_rankedEvents.Insert(indices.newIndex, item);
// remove the item from the old index in list 2
_availableEvents.Remove(_availableEvents[indices.oldIndex]);
}
protected override async Task OnInitializedAsync()
{
_student =
await Context.Students
.Include(e => e.EventRankings)
.Where(e => e.Id == StudentId).FirstAsync();
_events =
await Context.Events
.OrderBy(e => e.Name)
.ToListAsync();
_rankedEvents = _student.EventRankings.OrderBy(e => e.Rank).Select(e => e.EventDefinition).ToList();
_availableEvents = _events.Where(e => !_rankedEvents.Contains(e)).ToList();
}
private void Update((int oldIndex, int newIndex) indices)
{
var (oldIndex, newIndex) = indices;
var items = _rankedEvents;
var itemToMove = items[oldIndex];
items.RemoveAt(oldIndex);
if (newIndex < items.Count)
{
items.Insert(newIndex, itemToMove);
}
else
{
items.Add(itemToMove);
}
StateHasChanged();
}
async Task Save()
{
if (_student == null)
return;
try
{
_student.EventRankings.Clear();
for (var index = 0; index < _rankedEvents.Count; index++)
{
var evt = _rankedEvents[index];
_student.EventRankings.Add(new StudentEventRanking
{
EventDefinition = evt,
Student = _student,
Rank = index + 1
});
}
Context.Students.Update(_student);
await Context.SaveChangesAsync();
Snackbar.Add($"Event rankings saved for {_student.Name}", Severity.Success);
NavigationManager.NavigateTo("/students/event-ranking");
}
catch (Exception e)
{
Snackbar.Add("Error saving rankings. Please try again.", Severity.Error);
Logger.LogError(e, "Error saving event rankings for student {StudentId}", StudentId);
}
}
}