Compare commits

..
2 Commits
Author SHA1 Message Date
poprhythm 87db67f979 Refactor MudPaper component styling across various features for consistency
Updated the MudPaper component styling in multiple files to use a consistent padding class of "pa-3 pa-md-6" instead of "pa-6". This change enhances the visual consistency of the UI across the Calendar, Events, Students, and Teams components, improving the overall user experience.
2026-01-05 14:01:46 -05:00
poprhythm 2aaefb2491 Implement enhanced static file caching and improve calendar event loading with detailed logging
This commit introduces a new static file caching strategy in Program.cs, optimizing cache headers for Blazor assets to improve performance and ensure fresh content after deployments. Additionally, the Calendar component in Index.razor has been updated to include comprehensive logging for event loading, handling null occurrences, and error management during calendar item creation. The CalendarEventItem model is also initialized to prevent null reference issues. These changes enhance the application's reliability and user experience.
2026-01-05 13:21:03 -05:00
25 changed files with 249 additions and 86 deletions
@@ -19,7 +19,7 @@
<MudGrid> <MudGrid>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Paste Event Occurrence Data</MudText> <MudText Typo="Typo.h5" Class="mb-4">Paste Event Occurrence Data</MudText>
<MudStack Spacing="3"> <MudStack Spacing="3">
<MudTextField <MudTextField
@@ -53,7 +53,7 @@
</MudItem> </MudItem>
<MudItem xs="12" md="6"> <MudItem xs="12" md="6">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Parsed Results</MudText> <MudText Typo="Typo.h5" Class="mb-4">Parsed Results</MudText>
@if (_isParsing) @if (_isParsing)
@@ -4,7 +4,9 @@
@using WebApp.Models @using WebApp.Models
@using WebApp.Services @using WebApp.Services
@using Heron.MudCalendar @using Heron.MudCalendar
@using Microsoft.Extensions.Logging
@inject IEventOccurrenceService EventOccurrenceService @inject IEventOccurrenceService EventOccurrenceService
@inject ILogger<Index> Logger
<PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" Icon="@AppIcons.EventCalendar"> <PageHeader Title="Event Calendar" Description="View competition schedules and event occurrences" Icon="@AppIcons.EventCalendar">
<ActionButtons> <ActionButtons>
@@ -12,7 +14,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
@if (_calendarItems == null) @if (_calendarItems == null)
{ {
<MudProgressLinear Indeterminate="true" /> <MudProgressLinear Indeterminate="true" />
@@ -23,7 +25,7 @@
<MudCalendar T="CalendarEventItem" <MudCalendar T="CalendarEventItem"
Items="_calendarItems" Items="_calendarItems"
View="CalendarView.Day" View="CalendarView.Day"
CurrentDay=_calendarDate CurrentDay="@_calendarDate"
/> />
} }
</MudPaper> </MudPaper>
@@ -39,29 +41,115 @@
private async Task LoadCalendarEvents() private async Task LoadCalendarEvents()
{ {
try
{
Logger.LogInformation("Loading calendar events");
var occurrences = await EventOccurrenceService.GetEventOccurrencesAsync(); var occurrences = await EventOccurrenceService.GetEventOccurrencesAsync();
_calendarItems = occurrences
.Select(occ => new CalendarEventItem(occ)) if (occurrences == null)
.ToList(); {
Logger.LogWarning("Service returned null occurrences");
_calendarItems = new List<CalendarEventItem>();
return;
}
Logger.LogDebug("Received {Count} occurrences from service", occurrences.Count());
var items = new List<CalendarEventItem>();
foreach (var occ in occurrences)
{
try
{
if (occ == null)
{
Logger.LogWarning("Null occurrence found, skipping");
continue;
}
if (string.IsNullOrEmpty(occ.Name))
{
Logger.LogWarning("Occurrence with Id={Id} has null or empty Name", occ.Id);
}
var calendarItem = new CalendarEventItem(occ, occ.EventDefinition);
items.Add(calendarItem);
}
catch (Exception ex)
{
Logger.LogError(ex, "Error creating CalendarEventItem for occurrence Id={Id}, Name={Name}",
occ?.Id, occ?.Name);
// Continue processing other items
}
}
_calendarItems = items;
Logger.LogInformation("Created {Count} calendar items from {OccurrenceCount} occurrences",
_calendarItems.Count, occurrences.Count());
// Find the next date with events // Find the next date with events
_calendarDate = GetNextDateWithEvents(); _calendarDate = GetNextDateWithEvents();
} }
catch (Exception ex)
{
Logger.LogError(ex, "Error loading calendar events");
_calendarItems = new List<CalendarEventItem>();
}
finally
{
StateHasChanged();
}
}
private DateTime GetNextDateWithEvents() private DateTime GetNextDateWithEvents()
{
try
{ {
if (_calendarItems == null || !_calendarItems.Any()) if (_calendarItems == null || !_calendarItems.Any())
{ {
Logger.LogDebug("No calendar items available, returning today's date");
return DateTime.Today; return DateTime.Today;
} }
var today = DateTime.Today; var today = DateTime.Today;
var nextEvent = _calendarItems var nextEvent = _calendarItems
.Where(item => item.Start.Date >= today) .Where(item =>
{
try
{
return item != null && item.Start.Date >= today;
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Error checking item date, skipping item");
return false;
}
})
.OrderBy(item => item.Start) .OrderBy(item => item.Start)
.FirstOrDefault(); .FirstOrDefault();
return nextEvent?.Start.Date ?? _calendarItems.OrderBy(item => item.Start).First().Start.Date; if (nextEvent != null)
{
return nextEvent.Start.Date;
}
// Fallback to first event if no future events
var firstEvent = _calendarItems
.Where(item => item != null)
.OrderBy(item => item.Start)
.FirstOrDefault();
if (firstEvent != null)
{
return firstEvent.Start.Date;
}
return DateTime.Today;
}
catch (Exception ex)
{
Logger.LogError(ex, "Error in GetNextDateWithEvents");
return DateTime.Today;
}
} }
} }
@@ -14,14 +14,14 @@
@if (_isLoading) @if (_isLoading)
{ {
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudProgressLinear Indeterminate="true" Color="Color.Primary"/> <MudProgressLinear Indeterminate="true" Color="Color.Primary"/>
<MudText Typo="Typo.body1" Class="mt-4">Loading career mapping data...</MudText> <MudText Typo="Typo.body1" Class="mt-4">Loading career mapping data...</MudText>
</MudPaper> </MudPaper>
} }
else if (_networkData == null || !_networkData.Nodes.Any()) else if (_networkData == null || !_networkData.Nodes.Any())
{ {
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h6" Class="mb-4">No Career Field Mappings Found</MudText> <MudText Typo="Typo.h6" Class="mb-4">No Career Field Mappings Found</MudText>
<MudText Typo="Typo.body1" Class="mud-text-secondary"> <MudText Typo="Typo.body1" Class="mud-text-secondary">
No events have related careers assigned that match any career fields. Edit events to add related careers. No events have related careers assigned that match any career fields. Edit events to add related careers.
@@ -30,7 +30,7 @@ else if (_networkData == null || !_networkData.Nodes.Any())
} }
else else
{ {
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h6" Class="mb-4">Event-Career Field Relationships</MudText> <MudText Typo="Typo.h6" Class="mb-4">Event-Career Field Relationships</MudText>
<MudText Typo="Typo.body2" Class="mb-4 mud-text-secondary"> <MudText Typo="Typo.body2" Class="mb-4 mud-text-secondary">
This diagram shows the connections between events and their related career fields. This diagram shows the connections between events and their related career fields.
@@ -25,7 +25,7 @@
<ValidationErrorDisplay Errors="_validationErrors" /> <ValidationErrorDisplay Errors="_validationErrors" />
<MudStack Spacing="4"> <MudStack Spacing="4">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6"> <MudItem xs="12" sm="6">
@@ -49,7 +49,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Event Details</MudText> <MudText Typo="Typo.h5" Class="mb-4">Event Details</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -67,7 +67,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Team Configuration</MudText> <MudText Typo="Typo.h5" Class="mb-4">Team Configuration</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -88,7 +88,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Competition Details</MudText> <MudText Typo="Typo.h5" Class="mb-4">Competition Details</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -27,7 +27,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6" md="4"> <MudItem xs="12" sm="6" md="4">
+4 -4
View File
@@ -32,7 +32,7 @@
<ValidationErrorDisplay Errors="_validationErrors" /> <ValidationErrorDisplay Errors="_validationErrors" />
<MudStack Spacing="4"> <MudStack Spacing="4">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Basic Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6"> <MudItem xs="12" sm="6">
@@ -56,7 +56,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Event Details</MudText> <MudText Typo="Typo.h5" Class="mb-4">Event Details</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -74,7 +74,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Team Configuration</MudText> <MudText Typo="Typo.h5" Class="mb-4">Team Configuration</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -95,7 +95,7 @@
</MudGrid> </MudGrid>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Competition Details</MudText> <MudText Typo="Typo.h5" Class="mb-4">Competition Details</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12"> <MudItem xs="12">
@@ -15,7 +15,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudDataGrid T="EventDefinition" <MudDataGrid T="EventDefinition"
ServerData="ServerReload" ServerData="ServerReload"
@ref="_dataGrid" @ref="_dataGrid"
@@ -11,9 +11,9 @@
<PageHeader Title="@($"{Configuration["ChapterSettings:Shortname"]} TSA Schedule {Configuration["ChapterSettings:CompetitionYear"]}")" /> <PageHeader Title="@($"{Configuration["ChapterSettings:Shortname"]} TSA Schedule {Configuration["ChapterSettings:CompetitionYear"]}")" />
<MudPaper Elevation="2" Class="pa-6 mt-4"> <MudPaper Elevation="2" Class="pa-3 pa-md-6 mt-4">
<MudGrid> <MudGrid>
<MudItem xs="7" sm="8" lg="9"> <MudItem xs="12" sm="8" lg="9">
<MudText Typo="Typo.h4">Time Slots</MudText> <MudText Typo="Typo.h4">Time Slots</MudText>
<MudPaper Class="pa-2 ma-2" Elevation="3"> <MudPaper Class="pa-2 ma-2" Elevation="3">
<MudGrid> <MudGrid>
@@ -81,7 +81,7 @@
</RowTemplate> </RowTemplate>
</MudTable> </MudTable>
</MudItem> </MudItem>
<MudItem xs="5" sm="4" lg="3"> <MudItem xs="12" sm="4" lg="3">
<MudStack> <MudStack>
<StudentTextBoxSelector Students="@_students" <StudentTextBoxSelector Students="@_students"
SelectedStudents="_absentStudents" SelectedStudents="_absentStudents"
@@ -22,7 +22,7 @@
<ValidationErrorDisplay Errors="_validationErrors" /> <ValidationErrorDisplay Errors="_validationErrors" />
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Student Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Student Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6"> <MudItem xs="12" sm="6">
@@ -26,7 +26,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Student Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Student Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6" md="4"> <MudItem xs="12" sm="6" md="4">
@@ -32,7 +32,7 @@
<MudGrid> <MudGrid>
<MudItem xs="12" sm="7"> <MudItem xs="12" sm="7">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudTextField T="string" Label="First Name" @bind-Value="Student.FirstName" For="@(() => Student.FirstName)"></MudTextField> <MudTextField T="string" Label="First Name" @bind-Value="Student.FirstName" For="@(() => Student.FirstName)"></MudTextField>
<MudTextField T="string" Label="Last Name" @bind-Value="Student.LastName" For="@(() => Student.LastName)"></MudTextField> <MudTextField T="string" Label="Last Name" @bind-Value="Student.LastName" For="@(() => Student.LastName)"></MudTextField>
<MudTextField T="string" Label="Email Adress" @bind-Value="Student.Email" For="@(() => Student.Email)"></MudTextField> <MudTextField T="string" Label="Email Adress" @bind-Value="Student.Email" For="@(() => Student.Email)"></MudTextField>
@@ -18,7 +18,7 @@
else else
{ {
<MudStack Spacing="4"> <MudStack Spacing="4">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Students by Rank</MudText> <MudText Typo="Typo.h5" Class="mb-4">Students by Rank</MudText>
<MudTable Items="_students" Hover="true" Striped="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info"> <MudTable Items="_students" Hover="true" Striped="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info">
<HeaderContent> <HeaderContent>
@@ -64,7 +64,7 @@ else
</MudTable> </MudTable>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Events by Student</MudText> <MudText Typo="Typo.h5" Class="mb-4">Events by Student</MudText>
<MudTable Items="_eventStudentRankings" Hover="true" Striped="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info"> <MudTable Items="_eventStudentRankings" Hover="true" Striped="true" Breakpoint="Breakpoint.Sm" LoadingProgressColor="Color.Info">
<HeaderContent> <HeaderContent>
@@ -15,7 +15,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudDataGrid T="Student" <MudDataGrid T="Student"
ServerData="ServerReload" ServerData="ServerReload"
@ref="_dataGrid" @ref="_dataGrid"
@@ -30,7 +30,7 @@
<PropertyColumn Property="@(e => e.LastName)" Title="Name" Sortable="true"> <PropertyColumn Property="@(e => e.LastName)" Title="Name" Sortable="true">
<CellTemplate> <CellTemplate>
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Spacing="1"> <MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Spacing="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1"> <div class="d-flex align-center flex-wrap" style="gap: 0.25rem;">
<MudLink Href="@($"/students/details?id={context.Item.Id}&returnUrl=/students")" <MudLink Href="@($"/students/details?id={context.Item.Id}&returnUrl=/students")"
Underline="Underline.Hover" Underline="Underline.Hover"
Color="Color.Primary"> Color="Color.Primary">
@@ -40,7 +40,7 @@
{ {
<MudChip T="string" Size="Size.Small" Icon="@(AppIcons.OfficerRoleIcon(context.Item.OfficerRole.Value))">@context.Item.OfficerRole</MudChip> <MudChip T="string" Size="Size.Small" Icon="@(AppIcons.OfficerRoleIcon(context.Item.OfficerRole.Value))">@context.Item.OfficerRole</MudChip>
} }
</MudStack> </div>
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1"> <MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<IconButtonWithTooltip Icon="@Icons.Material.Filled.Edit" <IconButtonWithTooltip Icon="@Icons.Material.Filled.Edit"
TooltipText="Edit" TooltipText="Edit"
@@ -19,7 +19,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.body2" Class="mb-2"> <MudText Typo="Typo.body2" Class="mb-2">
<MudIcon Icon="@AppIcons.Captain" Size="Size.Small" /> = Team Captain <MudIcon Icon="@AppIcons.Captain" Size="Size.Small" /> = Team Captain
</MudText> </MudText>
@@ -78,8 +78,9 @@
var teamsToDisplay = _showRegionalOnly var teamsToDisplay = _showRegionalOnly
? context.Item.Teams.Where(t => t?.Event is { RegionalEvent: true }).OrderBy(t => t.Event.Name) ? context.Item.Teams.Where(t => t?.Event is { RegionalEvent: true }).OrderBy(t => t.Event.Name)
: context.Item.Teams.Where(t => t?.Event != null).OrderBy(t => t.Event.Name); : context.Item.Teams.Where(t => t?.Event != null).OrderBy(t => t.Event.Name);
}
foreach (var team in teamsToDisplay) <div class="d-flex flex-wrap" style="gap: 0.25rem;">
@foreach (var team in teamsToDisplay)
{ {
var isCaptain = team.Captain != null && team.Captain.Equals(context.Item.Student); var isCaptain = team.Captain != null && team.Captain.Equals(context.Item.Student);
var teamMembers = string.Join(", ", team.Students.Select(s => s.FirstName)); var teamMembers = string.Join(", ", team.Students.Select(s => s.FirstName));
@@ -97,7 +98,7 @@
</MudChip> </MudChip>
</MudTooltip> </MudTooltip>
} }
} </div>
</CellTemplate> </CellTemplate>
</TemplateColumn> </TemplateColumn>
</Columns> </Columns>
@@ -21,7 +21,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6 mt-4"> <MudPaper Elevation="2" Class="pa-3 pa-md-6 mt-4">
<MudGrid> <MudGrid>
<MudItem Style="width:160px;"> <MudItem Style="width:160px;">
<MudNumericField @bind-Value="_parameters.TeamSizeLimit" <MudNumericField @bind-Value="_parameters.TeamSizeLimit"
@@ -117,7 +117,7 @@
<MudButton Class="ma-3" OnClick="Solve" Variant="Variant.Filled" Color="Color.Primary" Disabled="@_isSolving">Solve</MudButton> <MudButton Class="ma-3" OnClick="Solve" Variant="Variant.Filled" Color="Color.Primary" Disabled="@_isSolving">Solve</MudButton>
</MudPaper> </MudPaper>
<MudPaper Elevation="2" Class="pa-6 mt-4"> <MudPaper Elevation="2" Class="pa-3 pa-md-6 mt-4">
<MudGrid> <MudGrid>
<MudItem xs="12" lg="8"> <MudItem xs="12" lg="8">
<MudText Typo="Typo.h5" Class="mb-4">Students</MudText> <MudText Typo="Typo.h5" Class="mb-4">Students</MudText>
@@ -1,4 +1,5 @@
@using WebApp.Models @using WebApp.Models
<div class="d-flex flex-wrap" style="gap: 0.25rem;">
@foreach (var student in @foreach (var student in
Team.Students Team.Students
.OrderBy(e => .OrderBy(e =>
@@ -20,7 +21,7 @@
Class="mx-1 my-1"> Class="mx-1 my-1">
@if (eventRank.HasValue) @if (eventRank.HasValue)
{ {
<span style="@($"display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: {color}; margin-right: 6px;")")></span> <span style="@($"display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: {color}; margin-right: 6px;")")"></span>
} }
@student.FirstName @student.FirstName
@if (captain && Team.Event.EventFormat != EventFormat.Individual) @if (captain && Team.Event.EventFormat != EventFormat.Individual)
@@ -30,6 +31,7 @@
</MudChip> </MudChip>
</MudTooltip> </MudTooltip>
} }
</div>
@code { @code {
[Parameter] [Parameter]
@@ -30,7 +30,7 @@
<MudGrid> <MudGrid>
<MudItem xs="12" sm="7"> <MudItem xs="12" sm="7">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudSelect T="EventDefinition" Value="@Team.Event" ValueChanged="OnEventChanged" Label="Event"> <MudSelect T="EventDefinition" Value="@Team.Event" ValueChanged="OnEventChanged" Label="Event">
@foreach (var evt in _events) @foreach (var evt in _events)
@@ -29,7 +29,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudText Typo="Typo.h5" Class="mb-4">Team Information</MudText> <MudText Typo="Typo.h5" Class="mb-4">Team Information</MudText>
<MudGrid Spacing="3"> <MudGrid Spacing="3">
<MudItem xs="12" sm="6"> <MudItem xs="12" sm="6">
+1 -1
View File
@@ -29,7 +29,7 @@
<MudGrid> <MudGrid>
<MudItem xs="12" sm="7"> <MudItem xs="12" sm="7">
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<StudentToggleSelector Students="@_students" <StudentToggleSelector Students="@_students"
@bind-SelectedStudents="_selectedStudents" @bind-SelectedStudents="_selectedStudents"
Title="Students" Title="Students"
+1 -1
View File
@@ -16,7 +16,7 @@
} }
else else
{ {
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
@foreach (var student in _students) @foreach (var student in _students)
{ {
<MudContainer Class="pagebreak"> <MudContainer Class="pagebreak">
+1 -1
View File
@@ -21,7 +21,7 @@
</ActionButtons> </ActionButtons>
</PageHeader> </PageHeader>
<MudPaper Elevation="2" Class="pa-6"> <MudPaper Elevation="2" Class="pa-3 pa-md-6">
<MudDataGrid T="Team" <MudDataGrid T="Team"
ServerData="ServerReload" ServerData="ServerReload"
@ref="_dataGrid" @ref="_dataGrid"
@@ -9,7 +9,9 @@
<MudLayout> <MudLayout>
<MudAppBar Class="no-print"> <MudAppBar Class="no-print">
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" /> <MudIconButton Icon="@Icons.Material.Filled.Menu" Color="Color.Inherit" Edge="Edge.Start" OnClick="@((e) => DrawerToggle())" />
<MudText Typo="Typo.h6" Class="text-truncate appbar-title">
TSA Chapter Organizer - @Configuration["ChapterSettings:Name"] TSA Chapter Organizer - @Configuration["ChapterSettings:Name"]
</MudText>
<MudSpacer /> <MudSpacer />
<AuthorizeView> <AuthorizeView>
<form action="Auth/CookieLogout" method="post"> <form action="Auth/CookieLogout" method="post">
+4
View File
@@ -24,6 +24,10 @@ public class CalendarEventItem : CalendarItem
/// </summary> /// </summary>
public CalendarEventItem() public CalendarEventItem()
{ {
// Initialize base class properties to avoid null reference issues
Text = string.Empty;
Start = DateTime.MinValue;
End = DateTime.MinValue;
} }
public CalendarEventItem(Core.Entities.EventOccurrence occurrence, Core.Entities.EventDefinition? eventDefinition = null) public CalendarEventItem(Core.Entities.EventOccurrence occurrence, Core.Entities.EventDefinition? eventDefinition = null)
+26 -1
View File
@@ -1,5 +1,6 @@
using Data; using Data;
using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using MudBlazor.Services; using MudBlazor.Services;
using Serilog; using Serilog;
@@ -268,7 +269,31 @@ app.UseRouting();
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseStaticFiles(); // Configure static files with proper cache headers for Blazor assets
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = ctx =>
{
// Blazor framework files: Use ETags with short cache and revalidation
// This allows caching for performance but ensures fresh files after deployments
// Browser will check ETag on each request (304 Not Modified if unchanged)
if (ctx.File.Name.Contains("_framework") ||
ctx.File.Name.Contains("blazor") ||
ctx.File.Name.EndsWith(".dll") ||
ctx.File.Name.EndsWith(".wasm"))
{
// Cache for 12 hours, but must revalidate (check ETag) before using
// If file hasn't changed, browser gets 304 Not Modified (no download)
// If file changed, browser downloads new version
ctx.Context.Response.Headers.Append("Cache-Control", "public, max-age=43200, must-revalidate");
}
else
{
// Other static files (CSS, images, etc.) can be cached long-term
ctx.Context.Response.Headers.Append("Cache-Control", "public, max-age=31536000");
}
}
});
app.UseAntiforgery(); app.UseAntiforgery();
app.MapRazorComponents<App>() app.MapRazorComponents<App>()
+41
View File
@@ -56,6 +56,19 @@
display: flex; display: flex;
gap: 0.5rem; gap: 0.5rem;
align-items: center; align-items: center;
flex-wrap: wrap;
}
@media (max-width: 600px) {
.page-header-actions {
width: 100%;
margin-top: 0.5rem;
}
.page-header > div > div:first-of-type {
flex-direction: column;
align-items: flex-start !important;
}
} }
.form-actions { .form-actions {
@@ -66,6 +79,17 @@
margin-bottom: 1rem; margin-bottom: 1rem;
} }
@media (max-width: 600px) {
.form-actions {
flex-direction: column;
width: 100%;
}
.form-actions > * {
width: 100%;
}
}
.icon-button-hover-error:hover { .icon-button-hover-error:hover {
color: var(--mud-palette-error) !important; color: var(--mud-palette-error) !important;
} }
@@ -88,3 +112,20 @@
opacity: 1; opacity: 1;
} }
} }
/* Toolbar title responsive styling */
.appbar-title {
max-width: calc(100vw - 150px);
}
@media (max-width: 600px) {
.appbar-title {
font-size: 0.875rem;
max-width: calc(100vw - 120px);
}
/* Reduce base font size on mobile */
body {
font-size: 0.875rem;
}
}