Add FormValidationService and EventDefinitionService to dependency injection
Enhanced the application's service layer by adding FormValidationService and EventDefinitionService to the dependency injection container in Program.cs. Updated Create, Edit, and other relevant components to utilize these services for improved form validation and event processing functionality.
This commit is contained in:
@@ -3,8 +3,13 @@
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using Core.Utility
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using MudBlazor
|
||||
@using WebApp.Services
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
@inject EventDefinitionService EventDefinitionService
|
||||
|
||||
<PageHeader
|
||||
Title="Create"
|
||||
@@ -12,10 +17,12 @@
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="@(ReturnUrl ?? "/events")" />
|
||||
|
||||
<EditForm Model="EventDefinition" OnValidSubmit="OnValidSubmit" Enhance>
|
||||
<EditForm EditContext="@_editContext" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudStack Spacing="4">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@@ -111,67 +118,52 @@
|
||||
private EventDefinition EventDefinition { get; set; } = new();
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_editContext = new EditContext(EventDefinition);
|
||||
}
|
||||
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, EventDefinition, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
|
||||
// Normalize and process related careers
|
||||
await ProcessRelatedCareersAsync(EventDefinition);
|
||||
|
||||
context.Events.Add(EventDefinition);
|
||||
await context.SaveChangesAsync();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/events");
|
||||
}
|
||||
|
||||
private async Task ProcessRelatedCareersAsync(EventDefinition eventDefinition)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventDefinition.RelatedCareersText))
|
||||
{
|
||||
eventDefinition.RelatedCareers.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedNames = CareerNormalizer.NormalizeCareerNames(eventDefinition.RelatedCareersText).ToList();
|
||||
_validationErrors.Clear();
|
||||
|
||||
if (!normalizedNames.Any())
|
||||
try
|
||||
{
|
||||
eventDefinition.RelatedCareers.Clear();
|
||||
return;
|
||||
}
|
||||
// Normalize and process related careers
|
||||
await EventDefinitionService.ProcessRelatedCareersAsync(EventDefinition);
|
||||
|
||||
// Get all existing careers from database (case-insensitive lookup)
|
||||
var existingCareers = await context.Careers.ToListAsync();
|
||||
var careerLookup = existingCareers.ToDictionary(
|
||||
c => CareerNormalizer.GetNormalizedKey(c.Name),
|
||||
c => c,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var careersToAdd = new List<Career>();
|
||||
|
||||
foreach (var normalizedName in normalizedNames)
|
||||
{
|
||||
var normalizedKey = CareerNormalizer.GetNormalizedKey(normalizedName);
|
||||
context.Events.Add(EventDefinition);
|
||||
await context.SaveChangesAsync();
|
||||
|
||||
if (careerLookup.TryGetValue(normalizedKey, out var existingCareer))
|
||||
{
|
||||
// Use existing career (preserve original capitalization)
|
||||
careersToAdd.Add(existingCareer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new career with the normalized name (preserving capitalization from input)
|
||||
var newCareer = new Career { Name = normalizedName };
|
||||
context.Careers.Add(newCareer);
|
||||
careersToAdd.Add(newCareer);
|
||||
careerLookup[normalizedKey] = newCareer;
|
||||
}
|
||||
Snackbar.Add($"Event '{EventDefinition.Name}' created successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/events");
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while creating the event.",
|
||||
"event",
|
||||
"creating",
|
||||
"name");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
|
||||
// Replace the collection
|
||||
eventDefinition.RelatedCareers = careersToAdd;
|
||||
}
|
||||
|
||||
|
||||
private void HandleCancel()
|
||||
{
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
|
||||
@@ -3,10 +3,16 @@
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using Core.Utility
|
||||
@using MudBlazor
|
||||
@using System.ComponentModel.DataAnnotations
|
||||
@using WebApp.Services
|
||||
@inject AppDbContext context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
@inject EventDefinitionService EventDefinitionService
|
||||
|
||||
@if (EventDefinition is null)
|
||||
@if (EventDefinition is null || _editContext is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
return;
|
||||
@@ -18,10 +24,12 @@
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="@(ReturnUrl ?? "/events")" />
|
||||
|
||||
<EditForm Model="EventDefinition" OnValidSubmit="OnValidSubmit" Enhance>
|
||||
<EditForm EditContext="@_editContext" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudStack Spacing="4">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@@ -120,6 +128,8 @@
|
||||
private EventDefinition? EventDefinition { get; set; }
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -135,96 +145,74 @@
|
||||
{
|
||||
// Populate RelatedCareersText from RelatedCareers collection
|
||||
EventDefinition.RelatedCareersText = string.Join("\n", EventDefinition.RelatedCareers.Select(c => c.Name));
|
||||
|
||||
// Create EditContext for validation
|
||||
_editContext = new EditContext(EventDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, EventDefinition!, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to.
|
||||
// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
// Get the tracked entity from the database
|
||||
var trackedEntity = await context.Events
|
||||
.Include(e => e.RelatedCareers)
|
||||
.FirstOrDefaultAsync(e => e.Id == EventDefinition!.Id);
|
||||
|
||||
if (trackedEntity == null)
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update scalar properties from the form-bound entity
|
||||
context.Entry(trackedEntity).CurrentValues.SetValues(EventDefinition!);
|
||||
|
||||
// Normalize and process related careers
|
||||
await ProcessRelatedCareersAsync(trackedEntity);
|
||||
|
||||
try
|
||||
{
|
||||
// Get the tracked entity from the database
|
||||
var trackedEntity = await context.Events
|
||||
.Include(e => e.RelatedCareers)
|
||||
.FirstOrDefaultAsync(e => e.Id == EventDefinition!.Id);
|
||||
|
||||
if (trackedEntity == null)
|
||||
{
|
||||
Snackbar.Add("Event not found. It may have been deleted.", Severity.Error);
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update scalar properties from the form-bound entity
|
||||
context.Entry(trackedEntity).CurrentValues.SetValues(EventDefinition!);
|
||||
|
||||
// Normalize and process related careers
|
||||
await EventDefinitionService.ProcessRelatedCareersAsync(trackedEntity);
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
_validationErrors.Clear();
|
||||
Snackbar.Add($"Event '{EventDefinition!.Name}' saved successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/events");
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
if (!EventDefinitionExists(EventDefinition!.Id))
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
if (!ValidationService.HandleDbUpdateConcurrencyException(
|
||||
ex,
|
||||
() => EventDefinitionExists(EventDefinition!.Id),
|
||||
"event",
|
||||
() => NavigationManager.NavigateTo("notfound")))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while saving the event.",
|
||||
"event",
|
||||
"saving",
|
||||
"name");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ProcessRelatedCareersAsync(EventDefinition eventDefinition)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(eventDefinition.RelatedCareersText))
|
||||
{
|
||||
eventDefinition.RelatedCareers.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
var normalizedNames = CareerNormalizer.NormalizeCareerNames(eventDefinition.RelatedCareersText).ToList();
|
||||
|
||||
if (!normalizedNames.Any())
|
||||
{
|
||||
eventDefinition.RelatedCareers.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get all existing careers from database (case-insensitive lookup)
|
||||
var existingCareers = await context.Careers.ToListAsync();
|
||||
var careerLookup = existingCareers.ToDictionary(
|
||||
c => CareerNormalizer.GetNormalizedKey(c.Name),
|
||||
c => c,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var careersToAdd = new List<Career>();
|
||||
|
||||
foreach (var normalizedName in normalizedNames)
|
||||
{
|
||||
var normalizedKey = CareerNormalizer.GetNormalizedKey(normalizedName);
|
||||
|
||||
if (careerLookup.TryGetValue(normalizedKey, out var existingCareer))
|
||||
{
|
||||
// Use existing career (preserve original capitalization)
|
||||
careersToAdd.Add(existingCareer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new career with the normalized name (preserving capitalization from input)
|
||||
var newCareer = new Career { Name = normalizedName };
|
||||
context.Careers.Add(newCareer);
|
||||
careersToAdd.Add(newCareer);
|
||||
careerLookup[normalizedKey] = newCareer;
|
||||
}
|
||||
}
|
||||
|
||||
// Replace the collection
|
||||
eventDefinition.RelatedCareers = careersToAdd;
|
||||
}
|
||||
|
||||
private async Task HandleCancel()
|
||||
{
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
@page "/students/create"
|
||||
@attribute [Authorize]
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
@using WebApp.Services
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
|
||||
<PageHeader
|
||||
Title="Create"
|
||||
@@ -10,10 +15,12 @@
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="@(ReturnUrl ?? "/students")" />
|
||||
|
||||
<EditForm Model="Student" OnValidSubmit="OnValidSubmit" Enhance>
|
||||
<EditForm EditContext="@_editContext" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
<MudText Typo="Typo.h5" Class="mb-4">Student Information</MudText>
|
||||
@@ -53,14 +60,45 @@
|
||||
private Student Student { get; set; } = new() { TsaYear = 1 };
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
|
||||
private void OnValidSubmit(EditContext context)
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
_editContext = new EditContext(Student);
|
||||
}
|
||||
|
||||
Context.Students.Add(Student);
|
||||
Context.SaveChanges();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/students");
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, Student, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task OnValidSubmit()
|
||||
{
|
||||
_validationErrors.Clear();
|
||||
|
||||
try
|
||||
{
|
||||
Context.Students.Add(Student);
|
||||
await Context.SaveChangesAsync();
|
||||
|
||||
Snackbar.Add($"Student '{Student.FirstNameLastName}' created successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/students");
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while creating the student.",
|
||||
"student",
|
||||
"creating");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCancel()
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
@attribute [Authorize]
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
@using WebApp.Services
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
|
||||
@if (Student is null)
|
||||
@if (Student is null || _editContext is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
return;
|
||||
@@ -19,10 +23,13 @@
|
||||
|
||||
@* https://www.mudblazor.com/components/form *@
|
||||
@* https://medium.com/@husainalbar/applying-mudblazor-for-crud-operations-in-our-blazor-project-a343037a52ef *@
|
||||
<EditForm method="post" Model="Student" OnValidSubmit="UpdateStudent" FormName="edit" Enhance>
|
||||
<EditForm method="post" EditContext="@_editContext" OnInvalidSubmit="OnInvalidSubmit" FormName="edit" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator/>
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="7">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@@ -64,6 +71,8 @@
|
||||
private Student? Student { get; set; }
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -73,12 +82,36 @@
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
{
|
||||
_editContext = new EditContext(Student);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, Student!, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to.
|
||||
// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
|
||||
private async Task UpdateStudent()
|
||||
{
|
||||
// Validate before processing
|
||||
if (_editContext != null)
|
||||
{
|
||||
var errors = ValidationService.CollectValidationErrors(_editContext, Student!);
|
||||
if (errors.Any())
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(_editContext, Student!, err => _validationErrors = err);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_validationErrors.Clear();
|
||||
|
||||
if (Student?.OfficerRole == 0)
|
||||
Student.OfficerRole = null;
|
||||
|
||||
@@ -87,20 +120,33 @@
|
||||
try
|
||||
{
|
||||
await Context.SaveChangesAsync();
|
||||
Snackbar.Add($"Student '{Student!.FirstNameLastName}' saved successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/students");
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
if (!StudentExists(Student!.Id))
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
if (!ValidationService.HandleDbUpdateConcurrencyException(
|
||||
ex,
|
||||
() => StudentExists(Student!.Id),
|
||||
"student",
|
||||
() => NavigationManager.NavigateTo("notfound")))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while saving the student.",
|
||||
"student",
|
||||
"saving");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleCancel()
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
@attribute [Authorize]
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
@using WebApp.Services
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
|
||||
@if (_events is null)
|
||||
@if (_events is null || _editContext is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
return;
|
||||
@@ -17,10 +21,13 @@
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="@(ReturnUrl ?? "/teams")" />
|
||||
|
||||
<EditForm method="post" Model="Team" OnValidSubmit="AddTeam" FormName="create" Enhance>
|
||||
<EditForm method="post" EditContext="@_editContext" OnInvalidSubmit="OnInvalidSubmit" FormName="create" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator />
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="7">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@@ -79,6 +86,8 @@
|
||||
private Team Team { get; set; } = new();
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
private List<EventDefinition>? _events;
|
||||
private List<Student> _students = [];
|
||||
private IEnumerable<Student> _selectedStudents = [];
|
||||
@@ -93,6 +102,14 @@
|
||||
.ToListAsync();
|
||||
|
||||
_students = await Context.Students.ToListAsync();
|
||||
|
||||
_editContext = new EditContext(Team);
|
||||
}
|
||||
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, Team, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task OnEventChanged(EventDefinition selectedEvent)
|
||||
@@ -111,61 +128,92 @@
|
||||
|
||||
private async Task AddTeam()
|
||||
{
|
||||
// Validate before processing
|
||||
if (_editContext != null)
|
||||
{
|
||||
var errors = ValidationService.CollectValidationErrors(_editContext, Team);
|
||||
if (errors.Any())
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(_editContext, Team, err => _validationErrors = err);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_validationErrors.Clear();
|
||||
|
||||
// Clear previous error message
|
||||
_errorMessage = null;
|
||||
|
||||
// Get current count of teams for this event
|
||||
var existingTeamCount = await Context.Teams
|
||||
.CountAsync(t => t.Event.Id == Team.Event.Id);
|
||||
|
||||
// Prohibit creation of third team
|
||||
if (existingTeamCount >= 2)
|
||||
try
|
||||
{
|
||||
_errorMessage = $"Cannot create a third team for {Team.Event.Name}. Maximum of 2 teams allowed.";
|
||||
return;
|
||||
}
|
||||
// Get current count of teams for this event
|
||||
var existingTeamCount = await Context.Teams
|
||||
.CountAsync(t => t.Event.Id == Team.Event.Id);
|
||||
|
||||
// Handle automatic numbering based on event format
|
||||
if (Team.Event.EventFormat == EventFormat.Individual && _selectedStudents.Count() == 1)
|
||||
{
|
||||
// For individual events, use student's first name as identifier
|
||||
var student = _selectedStudents.First();
|
||||
Team.Identifier = student.FirstName;
|
||||
Team.Captain = student;
|
||||
}
|
||||
else if (existingTeamCount == 1)
|
||||
{
|
||||
// This is the second team - assign numbers
|
||||
var existingTeam = await Context.Teams
|
||||
.FirstOrDefaultAsync(t => t.Event.Id == Team.Event.Id);
|
||||
|
||||
if (existingTeam != null)
|
||||
// Prohibit creation of third team
|
||||
if (existingTeamCount >= 2)
|
||||
{
|
||||
// Update existing team to number 1
|
||||
existingTeam.Identifier = "1";
|
||||
Context.Teams.Update(existingTeam);
|
||||
_errorMessage = $"Cannot create a third team for {Team.Event.Name}. Maximum of 2 teams allowed.";
|
||||
Snackbar.Add(_errorMessage, Severity.Error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set new team to number 2
|
||||
Team.Identifier = "2";
|
||||
// Handle automatic numbering based on event format
|
||||
if (Team.Event.EventFormat == EventFormat.Individual && _selectedStudents.Count() == 1)
|
||||
{
|
||||
// For individual events, use student's first name as identifier
|
||||
var student = _selectedStudents.First();
|
||||
Team.Identifier = student.FirstName;
|
||||
Team.Captain = student;
|
||||
}
|
||||
else if (existingTeamCount == 1)
|
||||
{
|
||||
// This is the second team - assign numbers
|
||||
var existingTeam = await Context.Teams
|
||||
.FirstOrDefaultAsync(t => t.Event.Id == Team.Event.Id);
|
||||
|
||||
if (existingTeam != null)
|
||||
{
|
||||
// Update existing team to number 1
|
||||
existingTeam.Identifier = "1";
|
||||
Context.Teams.Update(existingTeam);
|
||||
}
|
||||
|
||||
// Set new team to number 2
|
||||
Team.Identifier = "2";
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the first team - no number
|
||||
Team.Identifier = null;
|
||||
}
|
||||
|
||||
// Add selected students to the team
|
||||
foreach (var student in _selectedStudents)
|
||||
{
|
||||
Team.Students.Add(student);
|
||||
}
|
||||
|
||||
Context.Teams.Add(Team);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
Snackbar.Add($"Team '{Team}' created successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/teams");
|
||||
}
|
||||
else
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
// This is the first team - no number
|
||||
Team.Identifier = null;
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while creating the team.",
|
||||
"team",
|
||||
"creating");
|
||||
}
|
||||
|
||||
// Add selected students to the team
|
||||
foreach (var student in _selectedStudents)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Team.Students.Add(student);
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
|
||||
Context.Teams.Add(Team);
|
||||
|
||||
await Context.SaveChangesAsync();
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/teams");
|
||||
}
|
||||
|
||||
private void HandleCancel()
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
@attribute [Authorize]
|
||||
@using Microsoft.EntityFrameworkCore
|
||||
@using WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
@using WebApp.Services
|
||||
@inject AppDbContext Context
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject ISnackbar Snackbar
|
||||
@inject FormValidationService ValidationService
|
||||
|
||||
@if (Team is null)
|
||||
@if (Team is null || _editContext is null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
return;
|
||||
@@ -16,10 +20,13 @@
|
||||
ShowBackButton="true"
|
||||
BackButtonUrl="@(ReturnUrl ?? "/teams")" />
|
||||
|
||||
<EditForm method="post" Model="Team" OnValidSubmit="UpdateTeam" FormName="edit" Enhance>
|
||||
<EditForm method="post" EditContext="@_editContext" OnInvalidSubmit="OnInvalidSubmit" FormName="edit" Enhance>
|
||||
<FormChangeTracker @ref="_formChangeTracker" />
|
||||
<AntiforgeryToken />
|
||||
<DataAnnotationsValidator/>
|
||||
|
||||
<ValidationErrorDisplay Errors="_validationErrors" />
|
||||
|
||||
<MudGrid>
|
||||
<MudItem xs="12" sm="7">
|
||||
<MudPaper Elevation="2" Class="pa-6">
|
||||
@@ -53,6 +60,8 @@
|
||||
private Team? Team { get; set; }
|
||||
|
||||
private FormChangeTracker? _formChangeTracker;
|
||||
private EditContext? _editContext;
|
||||
private List<string> _validationErrors = new();
|
||||
private IEnumerable<Student>? _selectedStudents = [];
|
||||
private List<Student> _students = [];
|
||||
|
||||
@@ -69,24 +78,48 @@
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
|
||||
switch (Team!.Event.EventFormat)
|
||||
else
|
||||
{
|
||||
case EventFormat.Individual when Team.Students.Count == 1:
|
||||
Team.Captain ??= Team.Students[0];
|
||||
Team.Identifier ??= Team.Captain.FirstName;
|
||||
break;
|
||||
case EventFormat.Team:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
_editContext = new EditContext(Team);
|
||||
|
||||
switch (Team.Event.EventFormat)
|
||||
{
|
||||
case EventFormat.Individual when Team.Students.Count == 1:
|
||||
Team.Captain ??= Team.Students[0];
|
||||
Team.Identifier ??= Team.Captain.FirstName;
|
||||
break;
|
||||
case EventFormat.Team:
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnInvalidSubmit(EditContext editContext)
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(editContext, Team!, errors => _validationErrors = errors);
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
// To protect from overposting attacks, enable the specific properties you want to bind to.
|
||||
// For more information, see https://learn.microsoft.com/aspnet/core/blazor/forms/#mitigate-overposting-attacks.
|
||||
private async Task UpdateTeam()
|
||||
{
|
||||
// Validate before processing
|
||||
if (_editContext != null)
|
||||
{
|
||||
var errors = ValidationService.CollectValidationErrors(_editContext, Team!);
|
||||
if (errors.Any())
|
||||
{
|
||||
_validationErrors = ValidationService.HandleInvalidSubmit(_editContext, Team!, err => _validationErrors = err);
|
||||
StateHasChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_validationErrors.Clear();
|
||||
|
||||
//Context.Attach(Team!).Entity = EntityState.Modified;
|
||||
Team?.Students.Clear();
|
||||
if (_selectedStudents != null)
|
||||
@@ -105,20 +138,33 @@
|
||||
try
|
||||
{
|
||||
await Context.SaveChangesAsync();
|
||||
Snackbar.Add($"Team '{Team}' saved successfully.", Severity.Success);
|
||||
_formChangeTracker?.AllowNavigation();
|
||||
NavigationManager.NavigateTo(ReturnUrl ?? "/teams");
|
||||
}
|
||||
catch (DbUpdateConcurrencyException)
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
if (!TeamExists(Team!.Id))
|
||||
{
|
||||
NavigationManager.NavigateTo("notfound");
|
||||
}
|
||||
else
|
||||
if (!ValidationService.HandleDbUpdateConcurrencyException(
|
||||
ex,
|
||||
() => TeamExists(Team!.Id),
|
||||
"team",
|
||||
() => NavigationManager.NavigateTo("notfound")))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (DbUpdateException ex)
|
||||
{
|
||||
ValidationService.HandleDbUpdateException(
|
||||
ex,
|
||||
"An error occurred while saving the team.",
|
||||
"team",
|
||||
"saving");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ValidationService.HandleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task HandleCancel()
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
@namespace WebApp.Components.Shared.Components
|
||||
@using MudBlazor
|
||||
|
||||
@if (Errors != null && Errors.Any())
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Class="mb-4">
|
||||
<MudText Typo="Typo.h6" Class="mb-2">Please fix the following validation errors:</MudText>
|
||||
<ul style="margin: 0; padding-left: 20px;">
|
||||
@foreach (var error in Errors)
|
||||
{
|
||||
<li>@error</li>
|
||||
}
|
||||
</ul>
|
||||
</MudAlert>
|
||||
}
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// List of validation error messages to display
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public List<string>? Errors { get; set; }
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<MudThemeProvider Theme="CustomThemes.Ceruleantheme" />
|
||||
<MudPopoverProvider />
|
||||
<MudDialogProvider />
|
||||
<MudSnackbarProvider />
|
||||
|
||||
<MudLayout>
|
||||
<MudAppBar Class="no-print">
|
||||
|
||||
Reference in New Issue
Block a user