From eb1e12c73c366e6ea63201efc1f087557350014d Mon Sep 17 00:00:00 2001 From: axunonb Date: Tue, 14 Feb 2023 00:40:24 +0100 Subject: [PATCH] Bump version to v3.5.1 * Fixes for tournament registration * Add user notification on registration success and failure * Add Bootstrap 5 alert icons as razor partial * Add TempDataExtensions for handing over data between requests --- ClubSite/ClubSite.csproj | 2 +- ClubSite/Data/Poco/TournamentRegistration.cs | 7 + ClubSite/Pages/TournamentPage.cshtml | 148 +++++++++--------- .../Pages/TournamentRegistration.cshtml.cs | 63 ++++---- .../Pages/_BootstrapAlertIconsPartial.cshtml | 11 ++ .../Pages/_RegistrationSuccessPartial.cshtml | 27 ++++ ClubSite/TempDataExtensions.cs | 37 +++++ 7 files changed, 189 insertions(+), 106 deletions(-) create mode 100644 ClubSite/Pages/_BootstrapAlertIconsPartial.cshtml create mode 100644 ClubSite/Pages/_RegistrationSuccessPartial.cshtml create mode 100644 ClubSite/TempDataExtensions.cs diff --git a/ClubSite/ClubSite.csproj b/ClubSite/ClubSite.csproj index a008589..9b4fefa 100644 --- a/ClubSite/ClubSite.csproj +++ b/ClubSite/ClubSite.csproj @@ -4,7 +4,7 @@ net6.0 enable latest - 3.5.0 + 3.5.1 axuno gGmbH The source code for https://www.volleyballclub.de/ $([System.DateTime]::Now.ToString(yyyy)) diff --git a/ClubSite/Data/Poco/TournamentRegistration.cs b/ClubSite/Data/Poco/TournamentRegistration.cs index 5bbc211..17a7ad4 100644 --- a/ClubSite/Data/Poco/TournamentRegistration.cs +++ b/ClubSite/Data/Poco/TournamentRegistration.cs @@ -79,6 +79,13 @@ public class TournamentRegistration [Column(TypeName = "datetime")] public DateTime? CreatedOn { get; set; } [Column(TypeName = "datetime")] public DateTime? ModifiedOn { get; set; } + /// + /// if registration was successful. + /// This field is not part of the database table. + /// + [NotMapped] + public bool Success { get; set; } = false; + public string GetTeamNameWithClub() { return string.Join(' ', (TeamName ?? string.Empty).Trim(), diff --git a/ClubSite/Pages/TournamentPage.cshtml b/ClubSite/Pages/TournamentPage.cshtml index a5eeecd..b5b875e 100644 --- a/ClubSite/Pages/TournamentPage.cshtml +++ b/ClubSite/Pages/TournamentPage.cshtml @@ -1,5 +1,6 @@ @page @using Microsoft.AspNetCore.Authorization +@using Microsoft.AspNetCore.Mvc.TagHelpers @inject IAuthorizationService Auth @model ClubSite.Pages.TournamentPageModel @{ @@ -60,83 +61,84 @@
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + } + +
- Datum: - - @GetTournamentDates() -
- Uhrzeit: - - @Html.Raw(definition.TimeFromTo.Value) -
- Besetzung: - - @Html.Raw(definition.TeamComposition.Value) -
- Anz. Teams: - - @Html.Raw(definition.NumberOfTeams.Value) -
- Startgeld: - - @Html.Raw(definition.EntryFee.Value) -
- Adresse: - - @Html.Raw(definition.Address.Value) -
- Infos: - - @Html.Raw(definition.Infos.Value) -
-
Anmeldefrist:
-
- @if (definition.IsOver(DateTime.Now)) - { - Ist abgelaufen - } - else if (definition.IsRegistrationPeriodSet()) +
+ Datum: + + @GetTournamentDates() +
+ Uhrzeit: + + @Html.Raw(definition.TimeFromTo.Value) +
+ Besetzung: + + @Html.Raw(definition.TeamComposition.Value) +
+ Anz. Teams: + + @Html.Raw(definition.NumberOfTeams.Value) +
+ Startgeld: + + @Html.Raw(definition.EntryFee.Value) +
+ Adresse: + + @Html.Raw(definition.Address.Value) +
+ Infos: + + @Html.Raw(definition.Infos.Value) +
+
Anmeldefrist:
+
+ @if (definition.IsOver(DateTime.Now)) + { + Ist abgelaufen + } + else if (definition.IsRegistrationPeriodSet()) + { +
@Html.Raw(definition.RegistrationStart.Value?.ToShortDateString()) bis @Html.Raw(definition.RegistrationDeadline.Value?.ToShortDateString())
+ if (definition.IsRegistrationAllowed(DateTime.Now)) { -
@Html.Raw(definition.RegistrationStart.Value?.ToShortDateString()) bis @Html.Raw(definition.RegistrationDeadline.Value?.ToShortDateString())
- if (definition.IsRegistrationAllowed(DateTime.Now)) - { - - } + } -
diff --git a/ClubSite/Pages/TournamentRegistration.cshtml.cs b/ClubSite/Pages/TournamentRegistration.cshtml.cs index 2081370..56a024f 100644 --- a/ClubSite/Pages/TournamentRegistration.cshtml.cs +++ b/ClubSite/Pages/TournamentRegistration.cshtml.cs @@ -54,21 +54,16 @@ public TournamentRegistrationModel(ClubDbContext context, IApi api, Services.IMa Registration = new TournamentRegistration(); } - public static TournamentPage? GetTournamentPage(IApi api) + public static async Task GetTournamentPage(IApi api) { - // Call async method from sync context - we need the permalink to the TournamentPage - return new TaskFactory(CancellationToken.None, - TaskCreationOptions.None, - TaskContinuationOptions.None, - TaskScheduler.Default) - .StartNew(() => api.Pages.GetAllAsync()) - .Unwrap().ConfigureAwait(false).GetAwaiter() - .GetResult().FirstOrDefault(); + var tournaments = await api.Pages.GetAllAsync(); + return tournaments.FirstOrDefault(); } public async Task GetTournamentPageAsync() { - return (await _api.Pages.GetAllAsync().ConfigureAwait(false)).FirstOrDefault(); + var tp = await _api.Pages.GetAllAsync(); + return tp.FirstOrDefault(); } [BindProperty] @@ -98,20 +93,22 @@ public async Task OnGetAsync(long dateTicks, Guid registrationId) var isAuthenticated = (await _authorizationService.AuthorizeAsync(User, Piranha.Manager.Permission.Pages)) .Succeeded; - // Redirect to the default TournamentPage - if (TournamentPage.TournamentDefinition.IsOver(DateTime.Now) && !isAuthenticated) - { - return Redirect((await GetTournamentPageAsync())?.Permalink ?? "/"); - } - try { await SetupModel(dateTicks, registrationId); + + // Redirect to the default TournamentPage + if (TournamentPage.TournamentDefinition.IsOver(DateTime.Now) && !isAuthenticated) + { + return Redirect((await GetTournamentPageAsync())?.Permalink ?? "/"); + } + + // Return entry form return Page(); } catch (Exception e) { - _logger.LogCritical("Error setting up the model for date {new DateOnly(dateTicks)}", e); + _logger.LogCritical(e, "Error setting up the model for date {DateTicks}", new DateTime(dateTicks)); return NotFound(); } } @@ -121,16 +118,16 @@ public async Task OnPostAsync(CancellationToken cancellationToken var isAuthenticated = (await _authorizationService.AuthorizeAsync(User, Piranha.Manager.Permission.Pages)) .Succeeded; - // Redirect to the default TournamentPage - if (TournamentPage.TournamentDefinition.IsOver(DateTime.Now) && !isAuthenticated) - { - return Redirect((await GetTournamentPageAsync())?.Permalink ?? "/"); - } - try { // Model must always be set up await SetupModel(TournamentDate, Registration.RegistrationId); + + // Redirect to the default TournamentPage + if (TournamentPage.TournamentDefinition.IsOver(DateTime.Now) && !isAuthenticated) + { + return Redirect((await GetTournamentPageAsync())?.Permalink ?? "/"); + } } catch (Exception e) { @@ -185,23 +182,25 @@ await _mailService.SendTournamentRegistrationEmailAsync(Registration.GetComplete $"Anmeldung zum Turnier am {TournamentPage.TournamentDefinition.DateFrom.Value!.Value.ToShortDateString()}", GetConfirmationMessage(), cancellationToken); } - - // Todo: Add notification for successful saving on TournamentPage + + Registration.Success = true; + TempData.Put(nameof(TournamentRegistration), Registration); } catch (Exception e) { - _logger.LogCritical("Error saving the tournament registration", e); - // Todo: Add notification for failure of saving on TournamentPage + _logger.LogCritical(e, "Error saving the tournament registration. {Registration}", GetConfirmationMessage()); + Registration.Success = false; + TempData.Put(nameof(TournamentRegistration), Registration); } // Redirect to the default TournamentPage return Redirect((await GetTournamentPageAsync())?.Permalink ?? "/"); } - private async Task SetupModel(long date, Guid? registrationId) + private async Task SetupModel(long dateTicks, Guid? registrationId) { // Create date from ticks - var tournamentDate = new DateTime(date).Date; + var tournamentDate = new DateTime(dateTicks).Date; // Get the TournamentPage containing the definition for the given date TournamentPage = (await _api.Pages.GetAllAsync() @@ -214,7 +213,7 @@ private async Task SetupModel(long date, Guid? registrationId) if (definition == null || definition.DateFrom == null || !definition.DateFrom.Value.HasValue || !definition.NumberOfTeams.Value.HasValue) { - throw new Exception($"Tournament definition has no '{nameof(definition.DateFrom)}' defined. {nameof(TournamentRegistrationModel)} cannot be created."); + throw new InvalidOperationException($"Tournament definition has no '{nameof(definition.DateFrom)}' defined. {nameof(TournamentRegistrationModel)} cannot be created."); } AllRegistrations = (await _clubDbContext.TournamentRegistration? @@ -235,13 +234,13 @@ private async Task SetupModel(long date, Guid? registrationId) private string GetConfirmationMessage() { - var definition = TournamentPage!.TournamentDefinition; + var definition = TournamentPage.TournamentDefinition; return $@"Eingangsbestätigung der Turnieranmeldung {(Registration.IsStandByRegistration ? "\n(als möglicher \"Nachrücker\")" : "")} {definition.Name.Value} {new string('=', definition.Name.Value.Length)} -Datum: {definition.DateFrom.Value!.Value.ToLongDateString()} +Datum: {definition.DateFrom.Value?.ToLongDateString()} Adresse: {definition.Address.Value} Startgebühr: {definition.EntryFee.Value} diff --git a/ClubSite/Pages/_BootstrapAlertIconsPartial.cshtml b/ClubSite/Pages/_BootstrapAlertIconsPartial.cshtml new file mode 100644 index 0000000..84cd671 --- /dev/null +++ b/ClubSite/Pages/_BootstrapAlertIconsPartial.cshtml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ClubSite/Pages/_RegistrationSuccessPartial.cshtml b/ClubSite/Pages/_RegistrationSuccessPartial.cshtml new file mode 100644 index 0000000..c9760c9 --- /dev/null +++ b/ClubSite/Pages/_RegistrationSuccessPartial.cshtml @@ -0,0 +1,27 @@ +@using ClubSite.Data.Poco +@{ + // Check whether the tournament registration form put data to TempData + var registration = TempData.Get(nameof(TournamentRegistration)); + //registration = new TournamentRegistration() { TeamName = "The club name", Success = true }; +} +@{ + // No data from registration form + if (registration != null) + { + if (registration.Success) + { + + } + else + { + + } + } +} + \ No newline at end of file diff --git a/ClubSite/TempDataExtensions.cs b/ClubSite/TempDataExtensions.cs new file mode 100644 index 0000000..5e96491 --- /dev/null +++ b/ClubSite/TempDataExtensions.cs @@ -0,0 +1,37 @@ +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using Newtonsoft.Json; + +namespace ClubSite; + +/// +/// Extension for that stores and restores values from it. +/// +public static class TempDataExtensions +{ + /// + /// Stores the instance of type to the . + /// The type must be serializable by . + /// + /// + /// + /// + /// + public static void Put(this ITempDataDictionary tempData, string key, T value) where T : class + { + tempData[key] = JsonConvert.SerializeObject(value); + } + + /// + /// Restore a new instance of type from the . + /// The type must be deserializable by . + /// + /// + /// + /// + /// Returns a new instance of type from the + public static T? Get(this ITempDataDictionary tempData, string key) where T : class + { + tempData.TryGetValue(key, out var obj); + return obj == null ? null : JsonConvert.DeserializeObject((string)obj); + } +}