Skip to content

Commit

Permalink
Bump version to v3.5.1
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
axunonb committed Feb 13, 2023
1 parent 46b32af commit eb1e12c
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 106 deletions.
2 changes: 1 addition & 1 deletion ClubSite/ClubSite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<Version>3.5.0</Version>
<Version>3.5.1</Version>
<Authors>axuno gGmbH</Authors>
<Description>The source code for https://www.volleyballclub.de/</Description>
<CurrentYear>$([System.DateTime]::Now.ToString(yyyy))</CurrentYear>
Expand Down
7 changes: 7 additions & 0 deletions ClubSite/Data/Poco/TournamentRegistration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ public class TournamentRegistration
[Column(TypeName = "datetime")] public DateTime? CreatedOn { get; set; }
[Column(TypeName = "datetime")] public DateTime? ModifiedOn { get; set; }

/// <summary>
/// <see langword="true"/> if registration was successful.
/// This field is not part of the database table.
/// </summary>
[NotMapped]
public bool Success { get; set; } = false;

public string GetTeamNameWithClub()
{
return string.Join(' ', (TeamName ?? string.Empty).Trim(),
Expand Down
148 changes: 75 additions & 73 deletions ClubSite/Pages/TournamentPage.cshtml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
@page
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject IAuthorizationService Auth
@model ClubSite.Pages.TournamentPageModel
@{
Expand Down Expand Up @@ -60,83 +61,84 @@
</header>
<div class="mx-3 pt-2">
<div class="mb-4">
<partial name="_RegistrationSuccessPartial" />
<table id="tournament-description" class="table table-responsive table-striped table-bordered rounded shadow">
<tbody>
<tr>
<th>
Datum:
</th>
<td>
<b>@GetTournamentDates()</b>
</td>
</tr>
<tr>
<th>
Uhrzeit:
</th>
<td>
@Html.Raw(definition.TimeFromTo.Value)
</td>
</tr>
<tr>
<th>
Besetzung:
</th>
<td>
@Html.Raw(definition.TeamComposition.Value)
</td>
</tr>
<tr>
<th>
Anz. Teams:
</th>
<td>
@Html.Raw(definition.NumberOfTeams.Value)
</td>
</tr>
<tr>
<th>
Startgeld:
</th>
<td>
@Html.Raw(definition.EntryFee.Value)
</td>
</tr>
<tr>
<th>
Adresse:
</th>
<td>
@Html.Raw(definition.Address.Value)
</td>
</tr>
<tr>
<th>
Infos:
</th>
<td>
@Html.Raw(definition.Infos.Value)
</td>
</tr>
<tr>
<th>
<div class="pe-3">Anmeldefrist:</div>
</th>
<td>
@if (definition.IsOver(DateTime.Now))
{
<text>Ist abgelaufen</text>
}
else if (definition.IsRegistrationPeriodSet())
<tr>
<th>
Datum:
</th>
<td>
<b>@GetTournamentDates()</b>
</td>
</tr>
<tr>
<th>
Uhrzeit:
</th>
<td>
@Html.Raw(definition.TimeFromTo.Value)
</td>
</tr>
<tr>
<th>
Besetzung:
</th>
<td>
@Html.Raw(definition.TeamComposition.Value)
</td>
</tr>
<tr>
<th>
Anz. Teams:
</th>
<td>
@Html.Raw(definition.NumberOfTeams.Value)
</td>
</tr>
<tr>
<th>
Startgeld:
</th>
<td>
@Html.Raw(definition.EntryFee.Value)
</td>
</tr>
<tr>
<th>
Adresse:
</th>
<td>
@Html.Raw(definition.Address.Value)
</td>
</tr>
<tr>
<th>
Infos:
</th>
<td>
@Html.Raw(definition.Infos.Value)
</td>
</tr>
<tr>
<th>
<div class="pe-3">Anmeldefrist:</div>
</th>
<td>
@if (definition.IsOver(DateTime.Now))
{
<text>Ist abgelaufen</text>
}
else if (definition.IsRegistrationPeriodSet())
{
<div>@Html.Raw(definition.RegistrationStart.Value?.ToShortDateString()) <text> bis </text> @Html.Raw(definition.RegistrationDeadline.Value?.ToShortDateString())</div>
if (definition.IsRegistrationAllowed(DateTime.Now))
{
<div>@Html.Raw(definition.RegistrationStart.Value?.ToShortDateString()) <text> bis </text> @Html.Raw(definition.RegistrationDeadline.Value?.ToShortDateString())</div>
if (definition.IsRegistrationAllowed(DateTime.Now))
{
<div class="my-2"><a asp-page="/TournamentRegistration" asp-route-dateticks="@definition.DateFrom.Value!.Value.Date.Ticks" class="btn btn-lg btn-primary">Jetzt anmelden</a></div>
}
<div class="my-2"><a asp-page="/TournamentRegistration" asp-route-dateticks="@definition.DateFrom.Value!.Value.Date.Ticks" class="btn btn-lg btn-primary">Jetzt anmelden</a></div>
}
</td>
</tr>
}
</td>
</tr>
</tbody>
</table>
</div>
Expand Down
63 changes: 31 additions & 32 deletions ClubSite/Pages/TournamentRegistration.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<TournamentPage?> 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<TournamentPage>())
.Unwrap().ConfigureAwait(false).GetAwaiter()
.GetResult().FirstOrDefault();
var tournaments = await api.Pages.GetAllAsync<TournamentPage>();
return tournaments.FirstOrDefault();
}

public async Task<TournamentPage?> GetTournamentPageAsync()
{
return (await _api.Pages.GetAllAsync<TournamentPage>().ConfigureAwait(false)).FirstOrDefault();
var tp = await _api.Pages.GetAllAsync<TournamentPage>();
return tp.FirstOrDefault();
}

[BindProperty]
Expand Down Expand Up @@ -98,20 +93,22 @@ public async Task<IActionResult> 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();
}
}
Expand All @@ -121,16 +118,16 @@ public async Task<IActionResult> 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)
{
Expand Down Expand Up @@ -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<TournamentRegistration>(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<TournamentRegistration>(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<TournamentPage>()
Expand All @@ -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?
Expand All @@ -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}
Expand Down
11 changes: 11 additions & 0 deletions ClubSite/Pages/_BootstrapAlertIconsPartial.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="check-circle-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z" />
</symbol>
<symbol id="info-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z" />
</symbol>
<symbol id="exclamation-triangle-fill" fill="currentColor" viewBox="0 0 16 16">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z" />
</symbol>
</svg>
27 changes: 27 additions & 0 deletions ClubSite/Pages/_RegistrationSuccessPartial.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@using ClubSite.Data.Poco
@{
// Check whether the tournament registration form put data to TempData
var registration = TempData.Get<TournamentRegistration>(nameof(TournamentRegistration));
//registration = new TournamentRegistration() { TeamName = "The club name", Success = true };
}
@{
// No data from registration form
if (registration != null)
{
if (registration.Success)
{
<div class="alert alert-success" role="alert">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Success:"><use xlink:href="#check-circle-fill" /></svg>
<strong>Die Anmeldung für das Team &quot;@(registration.TeamName)&quot; war erfolgreich. Vielen Dank!</strong>
</div>
}
else
{
<div class="alert alert-danger" role="alert">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Danger:"><use xlink:href="#exclamation-triangle-fill" /></svg>
Bei der Anmeldung ist leider ein Fehler aufgetreten. Bitte später noch einmal versuchen.
</div>
}
}
}
<partial name="_BootstrapAlertIconsPartial" />
Loading

0 comments on commit eb1e12c

Please sign in to comment.