Files
chapter-organizer/WebApp/Components/Features/Events/Index.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

211 lines
7.6 KiB
Plaintext

@page "/events"
@attribute [Authorize]
@implements IAsyncDisposable
@using Microsoft.EntityFrameworkCore
@using WebApp.Models
@using WebApp.Components.Shared.Components
@inject AppDbContext Context
@inject IDialogService DialogService
@inject ISnackbar Snackbar
<PageHeader Title="Events">
<ActionButtons>
<MudTooltip Text="Create New">
<MudButton StartIcon="@Icons.Material.Filled.Create" Href="events/create" Variant="Variant.Filled" Color="Color.Primary">Create New</MudButton>
</MudTooltip>
<MudTooltip Text="Printable Descriptions">
<MudButton StartIcon="@Icons.Material.Filled.Print" Href="events/printout" Variant="Variant.Outlined">Printable Descriptions</MudButton>
</MudTooltip>
<MudTooltip Text="Career Mapping">
<MudButton StartIcon="@Icons.Material.Filled.AccountTree" Href="events/career-mapping" Variant="Variant.Outlined">Career Mapping</MudButton>
</MudTooltip>
</ActionButtons>
</PageHeader>
<MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudDataGrid T="EventDefinition"
ServerData="ServerReload"
@ref="_dataGrid"
Filterable="true"
RowsPerPage="50"
Dense="true"
Striped="true"
Hover="true"
Loading="@_isLoading"
LoadingProgressColor="Color.Primary">
<Columns>
<PropertyColumn Property="@(e => e.Name)" Title="Event Name" Sortable="true">
<CellTemplate>
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Spacing="1">
<MudLink Href="@($"/events/details?id={context.Item.Id}&returnUrl=/events")"
Underline="Underline.Hover"
Color="Color.Primary">
@context.Item.Name
</MudLink>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<IconButtonWithTooltip Icon="@Icons.Material.Filled.Edit"
TooltipText="Edit"
Href="@($"/events/edit?id={context.Item.Id}&returnUrl=/events")" />
<IconButtonWithTooltip Icon="@Icons.Material.Outlined.Delete"
TooltipText="Delete"
HoverColor="Color.Error"
OnClick="() => DeleteEventDefinition(context.Item!)" />
</MudStack>
</MudStack>
</CellTemplate>
</PropertyColumn>
<TemplateColumn Title="Attributes" Sortable="false">
<CellTemplate>
<EventAttributes EventDefinition="context.Item"></EventAttributes>
</CellTemplate>
</TemplateColumn>
<TemplateColumn Title="Team Size" CellStyle="white-space:nowrap">
<CellTemplate>
<MudTooltip Text="@context.Item.Eligibility">
[@context.Item.MinTeamSize&nbsp;-&nbsp;@context.Item.MaxTeamSize]
</MudTooltip>
</CellTemplate>
</TemplateColumn>
<PropertyColumn Property="@(e => e.LevelOfEffort)" Title="Level of Effort" />
<PropertyColumn Property="@(e => e.EventFormat)" Title="Event Format" />
</Columns>
<PagerContent>
<MudDataGridPager T="EventDefinition"></MudDataGridPager>
</PagerContent>
</MudDataGrid>
</MudPaper>
@code {
MudDataGrid<EventDefinition> _dataGrid = null!;
private bool _isLoading = true;
private CancellationTokenSource? _cancellationTokenSource;
private bool _isDisposed = false;
protected override void OnInitialized()
{
_cancellationTokenSource = new CancellationTokenSource();
}
private async Task<GridData<EventDefinition>> ServerReload(GridState<EventDefinition> state)
{
if (_isDisposed)
{
return new GridData<EventDefinition> { TotalItems = 0, Items = [] };
}
_isLoading = true;
try
{
var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None;
var query = Context.Events
.AsNoTracking()
.OrderBy(e => e.Name).Where(state.FilterDefinitions).OrderBy(state.SortDefinitions);
var totalItems = await query.CountAsync(cancellationToken);
var pagedData = await query.Skip(state.Page * state.PageSize).Take(state.PageSize).ToArrayAsync(cancellationToken);
return new GridData<EventDefinition>
{
TotalItems = totalItems,
Items = pagedData
};
}
catch (TaskCanceledException)
{
return new GridData<EventDefinition> { TotalItems = 0, Items = [] };
}
catch (JSDisconnectedException)
{
return new GridData<EventDefinition> { TotalItems = 0, Items = [] };
}
finally
{
if (!_isDisposed)
{
_isLoading = false;
}
}
}
private async Task DeleteEventDefinition(EventDefinition evt)
{
if (_isDisposed) return;
try
{
var cancellationToken = _cancellationTokenSource?.Token ?? CancellationToken.None;
var result = await DialogService
.ShowMessageBox("Delete Event",
(MarkupString)$"Are you sure want to delete <b>{evt.Name}</b>? This cannot be undone.",
yesText:"Yes",
noText:"Cancel");
if (_isDisposed) return;
if (result == true)
{
// Load the event fresh from database with tracking to avoid tracking conflicts
var eventToDelete = await Context.Events
.FirstOrDefaultAsync(e => e.Id == evt.Id, cancellationToken);
if (_isDisposed) return;
if (eventToDelete == null)
{
if (!_isDisposed)
{
Snackbar.Add("Event not found or already deleted", Severity.Warning);
}
return;
}
Context.Events.Remove(eventToDelete);
await Context.SaveChangesAsync(cancellationToken);
if (!_isDisposed)
{
Snackbar.Add($"Event {eventToDelete.Name} deleted", Severity.Info);
}
}
if (!_isDisposed)
{
StateHasChanged();
await _dataGrid.ReloadServerData();
}
}
catch (TaskCanceledException)
{
// Component was disposed, ignore
}
catch (JSDisconnectedException)
{
// JS connection lost, ignore
}
catch (Exception ex)
{
if (!_isDisposed)
{
Snackbar.Add($"Error deleting event: {ex.Message}", Severity.Error);
}
}
}
public async ValueTask DisposeAsync()
{
if (!_isDisposed)
{
_isDisposed = true;
_cancellationTokenSource?.Cancel();
_cancellationTokenSource?.Dispose();
_cancellationTokenSource = null;
}
await ValueTask.CompletedTask;
}
}