From ea4aabda99262adba2f9aa5f1af5ef937a58a265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 07:34:50 +0100 Subject: [PATCH 1/8] Image chooser style improvement --- .../image-chooser/image-chooser.component.html | 6 +++--- .../image-chooser/image-chooser.component.scss | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html index 698a7ac6..bf7eaff1 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html @@ -34,12 +34,12 @@ [icon]="'check-lg'" (buttonClick)="image.id !== currentImageId && modal.close({ type: 'ImageSelected', image: image })" /> } - {{ image.name }} + {{ image.name }} - {{ image.fileSize | fileSize: 'de' }} + {{ image.fileSize | fileSize: 'de' }}
diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.scss b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.scss index 38440487..50431e69 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.scss +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.scss @@ -13,11 +13,15 @@ .image-tile { &:hover, &.hover-override { - border-color: #333 !important; - .hover-buttons { - background: rgb(255 255 255 / 85%); display: flex !important; + z-index: 2; + } + + .image-wrapper { + img { + opacity: 0.1; + } } } From 29a9e5b9c610958d6b2fafa42143b3e02677b4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 07:34:56 +0100 Subject: [PATCH 2/8] Fix bug in chooser --- .../components/image-chooser/image-chooser.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html index bf7eaff1..16b291a1 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-chooser/image-chooser.component.html @@ -42,7 +42,7 @@ {{ image.fileSize | fileSize: 'de' }}
- +
} @empty { From 44e2e4471fb1125afb8083a184307e142234e79c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 07:36:02 +0100 Subject: [PATCH 3/8] Fix bug in image widget --- .../components/image-widget/image-widget.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-widget/image-widget.component.html b/src/Turnierplan.App/Client/src/app/portal/components/image-widget/image-widget.component.html index 66543005..fd27a90c 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-widget/image-widget.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-widget/image-widget.component.html @@ -1,8 +1,8 @@
@if (currentImage) {
-
- +
+
} @else { From 20adba271b7339ffb604030efd29ec487e82a299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 08:06:20 +0100 Subject: [PATCH 4/8] Changed error handling inside base renderer --- .../Endpoints/Documents/GetDocumentPdfEndpoint.cs | 10 +++------- .../Renderer/RendererTestBase.cs | 4 +--- .../Renderer/DocumentRendererBase.cs | 6 ++---- .../Renderer/IDocumentRenderer.cs | 2 +- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs b/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs index 7f13a035..7c3187fb 100644 --- a/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs +++ b/src/Turnierplan.App/Endpoints/Documents/GetDocumentPdfEndpoint.cs @@ -78,8 +78,6 @@ private static async Task Handle( // Wrap the code below in a transaction such that the generation count // is only incremented when the document is rendered successfully. - bool isSuccessful; - await using (var transaction = await repository.UnitOfWork.WrapTransactionAsync()) { document.IncreaseGenerationCount(); @@ -90,13 +88,11 @@ private static async Task Handle( document.Tournament.ShiftToTimezone(timeZoneInfo); document.Tournament.Compute(); - isSuccessful = renderer.Render(document.Tournament, configuration, localization, stream); + renderer.Render(document.Tournament, configuration, localization, stream); - transaction.ShouldCommit = isSuccessful; + transaction.ShouldCommit = true; } - return isSuccessful - ? Results.File(stream.ToArray(), "application/pdf") - : Results.InternalServerError(); + return Results.File(stream.ToArray(), "application/pdf"); } } diff --git a/src/Turnierplan.PdfRendering.Test.Unit/Renderer/RendererTestBase.cs b/src/Turnierplan.PdfRendering.Test.Unit/Renderer/RendererTestBase.cs index f9ed36e2..bae2eb06 100644 --- a/src/Turnierplan.PdfRendering.Test.Unit/Renderer/RendererTestBase.cs +++ b/src/Turnierplan.PdfRendering.Test.Unit/Renderer/RendererTestBase.cs @@ -58,9 +58,7 @@ protected void AssertRender(Tournament tournament, IDocumentConfiguration config __serviceProvider.GetRequiredService().TryGetLocalization(languageCode, out var localization).Should().BeTrue(); using var stream = new MemoryStream(); - var isSuccessful = GetRenderer().Render(tournament, configuration, new LocalizationWrapper(localization!), stream); - - isSuccessful.Should().BeTrue(); + GetRenderer().Render(tournament, configuration, new LocalizationWrapper(localization!), stream); var pdfData = stream.ToArray(); diff --git a/src/Turnierplan.PdfRendering/Renderer/DocumentRendererBase.cs b/src/Turnierplan.PdfRendering/Renderer/DocumentRendererBase.cs index d6b4b6e7..ad43f622 100644 --- a/src/Turnierplan.PdfRendering/Renderer/DocumentRendererBase.cs +++ b/src/Turnierplan.PdfRendering/Renderer/DocumentRendererBase.cs @@ -20,7 +20,7 @@ private protected DocumentRendererBase() protected Activity? CurrentActivity { get; private set; } - public bool Render(Tournament tournament, IDocumentConfiguration configuration, ILocalization localization, Stream destination) + public void Render(Tournament tournament, IDocumentConfiguration configuration, ILocalization localization, Stream destination) { lock (this) { @@ -53,7 +53,7 @@ public bool Render(Tournament tournament, IDocumentConfiguration configuration, CurrentActivity?.AddException(ex); CurrentActivity?.Stop(); - return false; + throw; } finally { @@ -66,8 +66,6 @@ public bool Render(Tournament tournament, IDocumentConfiguration configuration, // Ignored } } - - return true; } protected abstract Document Render(Tournament tournament, T configuration, ILocalization localization); diff --git a/src/Turnierplan.PdfRendering/Renderer/IDocumentRenderer.cs b/src/Turnierplan.PdfRendering/Renderer/IDocumentRenderer.cs index 9a2b9ded..af0c2c49 100644 --- a/src/Turnierplan.PdfRendering/Renderer/IDocumentRenderer.cs +++ b/src/Turnierplan.PdfRendering/Renderer/IDocumentRenderer.cs @@ -8,5 +8,5 @@ public interface IDocumentRenderer { Type DocumentConfigurationType { get; } - bool Render(Tournament tournament, IDocumentConfiguration configuration, ILocalization localization, Stream destination); + void Render(Tournament tournament, IDocumentConfiguration configuration, ILocalization localization, Stream destination); } From f2ec96c715f01abbeabcc18e8ad713b69ab36412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 08:26:54 +0100 Subject: [PATCH 5/8] Implement bugfix for rewnderer --- .../Extensions/QuestPdfContainerExtensions.cs | 45 ++++++++++++++++++- .../Renderer/MatchPlanRenderer.cs | 4 +- .../Renderer/ReceiptsRenderer.cs | 4 +- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs b/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs index 935aff7a..23ab231f 100644 --- a/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs +++ b/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs @@ -9,8 +9,51 @@ namespace Turnierplan.PdfRendering.Extensions; internal static class QuestPdfContainerExtensions { + /// + /// Displays an image at the current position with the specified size. This method overwrites properties of the specified + /// so you should always call this method with a fresh container. Most importantly, the + /// method is called to allow for the desired positioning. + /// + public static void LogoImage(this IContainer container, Image image, IImageStorage imageStorage, float size, Unit sizeUnit, bool alignRight = false) + { + if (alignRight) + { + float translateX; + + if (image.Height > image.Width) + { + var imageDisplayWidth = (float)image.Width / image.Height * size; + translateX = -imageDisplayWidth; + } + else + { + translateX = -size; + } + + container = container.AlignRight().TranslateX(translateX, sizeUnit); + } + + float translateY; + + if (image.Width > image.Height) + { + var imageDisplayHeight = (float)image.Height / image.Width * size; + translateY = 0.5f * (size - imageDisplayHeight); + } + else + { + translateY = 0.0f; + } + + container.TranslateY(translateY, sizeUnit).Unconstrained().Width(size, sizeUnit).Height(size, sizeUnit).Image(image, imageStorage); + } + extension(IContainer container) { + + /// + /// Pulls the specified image from the configured image storage and creates a QuestPDF image with the retrieved image data. + /// public void Image(Image image, IImageStorage imageStorage) { // Waiting for the task to complete is not ideal. However, attempting to use async @@ -26,7 +69,7 @@ public void Image(Image image, IImageStorage imageStorage) } /// - /// This code is taken from , last viewed on 2025-09-26 + /// This code is taken from , last viewed on 2026-03-01 /// public void SkiaSharpSvgCanvas(Action drawOnCanvas) { diff --git a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs index 94587fb3..8c486b33 100644 --- a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs +++ b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs @@ -50,13 +50,13 @@ string GetSectionHeader(string key) if (tournament.PrimaryLogo is not null) { using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.PrimaryLogo, nameof(tournament.PrimaryLogo)); - column.Item().Unconstrained().Width(3, Unit.Centimetre).Image(tournament.PrimaryLogo, imageStorage); + column.Item().LogoImage(tournament.PrimaryLogo, imageStorage, 3.0f, Unit.Centimetre); } if (tournament.SecondaryLogo is not null) { using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.SecondaryLogo, nameof(tournament.SecondaryLogo)); - column.Item().AlignRight().Unconstrained().TranslateX(-3, Unit.Centimetre).Width(3, Unit.Centimetre).Image(tournament.SecondaryLogo, imageStorage); + column.Item().LogoImage(tournament.SecondaryLogo, imageStorage, 3.0f, Unit.Centimetre, alignRight: true); } var organizerName = string.IsNullOrWhiteSpace(configuration.OrganizerNameOverride) diff --git a/src/Turnierplan.PdfRendering/Renderer/ReceiptsRenderer.cs b/src/Turnierplan.PdfRendering/Renderer/ReceiptsRenderer.cs index 7c64f055..4d5e2236 100644 --- a/src/Turnierplan.PdfRendering/Renderer/ReceiptsRenderer.cs +++ b/src/Turnierplan.PdfRendering/Renderer/ReceiptsRenderer.cs @@ -37,13 +37,13 @@ protected override Document Render(Tournament tournament, ReceiptsDocumentConfig if (configuration.ShowPrimaryLogo && tournament.PrimaryLogo is not null) { using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.PrimaryLogo, nameof(tournament.PrimaryLogo)); - column.Item().Unconstrained().Width(1.7f, Unit.Centimetre).Image(tournament.PrimaryLogo, imageStorage); + column.Item().LogoImage(tournament.PrimaryLogo, imageStorage, 1.7f, Unit.Centimetre); } if (configuration.ShowSecondaryLogo && tournament.SecondaryLogo is not null) { using var _ = DocumentRendererActivitySource.LoadRemoteImage(CurrentActivity, tournament.SecondaryLogo, nameof(tournament.SecondaryLogo)); - column.Item().AlignRight().Unconstrained().TranslateX(-1.7f, Unit.Centimetre).Width(1.7f, Unit.Centimetre).Image(tournament.SecondaryLogo, imageStorage); + column.Item().LogoImage(tournament.SecondaryLogo, imageStorage, 1.7f, Unit.Centimetre, alignRight: true); } column.Item().PaddingVertical(10).MinHeight(12, Unit.Millimetre).AlignMiddle().Column(headerColumn => From 7c3abd9eea4f02ad598c0cb8b7849d2b4667c3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 08:27:31 +0100 Subject: [PATCH 6/8] Remove extension block --- .../Extensions/QuestPdfContainerExtensions.cs | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs b/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs index 23ab231f..e50e5583 100644 --- a/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs +++ b/src/Turnierplan.PdfRendering/Extensions/QuestPdfContainerExtensions.cs @@ -48,43 +48,39 @@ public static void LogoImage(this IContainer container, Image image, IImageStora container.TranslateY(translateY, sizeUnit).Unconstrained().Width(size, sizeUnit).Height(size, sizeUnit).Image(image, imageStorage); } - extension(IContainer container) + /// + /// Pulls the specified image from the configured image storage and creates a QuestPDF image with the retrieved image data. + /// + public static void Image(this IContainer container, Image image, IImageStorage imageStorage) { + // Waiting for the task to complete is not ideal. However, attempting to use async + // inside the QuestPDF document structure is probably a much bigger nightmare... + var task = imageStorage.GetImageAsync(image); + task.Wait(); - /// - /// Pulls the specified image from the configured image storage and creates a QuestPDF image with the retrieved image data. - /// - public void Image(Image image, IImageStorage imageStorage) - { - // Waiting for the task to complete is not ideal. However, attempting to use async - // inside the QuestPDF document structure is probably a much bigger nightmare... - var task = imageStorage.GetImageAsync(image); - task.Wait(); - - var stream = task.Result; - container.Image(stream); + var stream = task.Result; + container.Image(stream); - // Dispose the stream (at this point, QuestPDF has read the stream content into an internal buffer) - stream.Dispose(); - } + // Dispose the stream (at this point, QuestPDF has read the stream content into an internal buffer) + stream.Dispose(); + } - /// - /// This code is taken from , last viewed on 2026-03-01 - /// - public void SkiaSharpSvgCanvas(Action drawOnCanvas) + /// + /// This code is taken from , last viewed on 2026-03-01 + /// + public static void SkiaSharpSvgCanvas(this IContainer container, Action drawOnCanvas) + { + container.Svg(size => { - container.Svg(size => - { - using var stream = new MemoryStream(); + using var stream = new MemoryStream(); - using (var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), stream)) - { - drawOnCanvas(canvas, size); - } + using (var canvas = SKSvgCanvas.Create(new SKRect(0, 0, size.Width, size.Height), stream)) + { + drawOnCanvas(canvas, size); + } - var svgData = stream.ToArray(); - return Encoding.UTF8.GetString(svgData); - }); - } + var svgData = stream.ToArray(); + return Encoding.UTF8.GetString(svgData); + }); } } From d5f0b0e5ff5e446e5f80ebae2935723cb81dcc3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 08:29:25 +0100 Subject: [PATCH 7/8] Remove extension blocks --- .../Extensions/EnumerableExtensions.cs | 14 +- .../Extensions/HttpContextExtensions.cs | 65 ++- .../Extensions/ConfigurationExtensions.cs | 15 +- .../Extensions/UnitOfWorkExtensions.cs | 9 +- .../Renderer/MatchPlanRenderer.cs | 414 +++++++++--------- 5 files changed, 248 insertions(+), 269 deletions(-) diff --git a/src/Turnierplan.App/Extensions/EnumerableExtensions.cs b/src/Turnierplan.App/Extensions/EnumerableExtensions.cs index 67fe1ed8..18671ff2 100644 --- a/src/Turnierplan.App/Extensions/EnumerableExtensions.cs +++ b/src/Turnierplan.App/Extensions/EnumerableExtensions.cs @@ -2,19 +2,13 @@ namespace Turnierplan.App.Extensions; internal static class EnumerableExtensions { - extension(IEnumerable enumerable) + public static bool None(this IEnumerable enumerable, Func predicate) { - public bool None(Func predicate) - { - return !enumerable.Any(predicate); - } + return !enumerable.Any(predicate); } - extension(IEnumerable enumerable) + public static IEnumerable WhereNotNull(this IEnumerable enumerable) { - public IEnumerable WhereNotNull() - { - return enumerable.Where(x => x is not null)!; - } + return enumerable.Where(x => x is not null)!; } } diff --git a/src/Turnierplan.App/Extensions/HttpContextExtensions.cs b/src/Turnierplan.App/Extensions/HttpContextExtensions.cs index d86b042d..912af102 100644 --- a/src/Turnierplan.App/Extensions/HttpContextExtensions.cs +++ b/src/Turnierplan.App/Extensions/HttpContextExtensions.cs @@ -5,51 +5,48 @@ namespace Turnierplan.App.Extensions; internal static class HttpContextExtensions { - extension(HttpContext context) + public static Guid GetCurrentUserIdOrThrow(this HttpContext context) { - public Guid GetCurrentUserIdOrThrow() - { - var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.UserId))?.Value; + var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.UserId))?.Value; - if (string.IsNullOrWhiteSpace(claimValue) || !Guid.TryParse(claimValue, out var userId)) - { - throw new InvalidOperationException("Could not get current user ID from HttpContext."); - } - - return userId; + if (string.IsNullOrWhiteSpace(claimValue) || !Guid.TryParse(claimValue, out var userId)) + { + throw new InvalidOperationException("Could not get current user ID from HttpContext."); } - public bool IsCurrentUserAdministrator() - { - var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.Administrator))?.Value; + return userId; + } - return !string.IsNullOrWhiteSpace(claimValue) && claimValue.Equals("true"); - } + public static bool IsCurrentUserAdministrator(this HttpContext context) + { + var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.Administrator))?.Value; - public Principal GetActivePrincipal() - { - PrincipalKind? kind = null; - Guid? principalId = null; + return !string.IsNullOrWhiteSpace(claimValue) && claimValue.Equals("true"); + } - foreach (var claim in context.User.Claims) - { - switch (claim.Type) - { - case ClaimTypes.PrincipalKind: - kind = Enum.Parse(claim.Value); - break; - case ClaimTypes.PrincipalId: - principalId = Guid.Parse(claim.Value); - break; - } - } + public static Principal GetActivePrincipal(this HttpContext context) + { + PrincipalKind? kind = null; + Guid? principalId = null; - if (kind.HasValue && principalId.HasValue) + foreach (var claim in context.User.Claims) + { + switch (claim.Type) { - return new Principal(kind.Value, principalId.Value); + case ClaimTypes.PrincipalKind: + kind = Enum.Parse(claim.Value); + break; + case ClaimTypes.PrincipalId: + principalId = Guid.Parse(claim.Value); + break; } + } - throw new InvalidOperationException("Could not determine active principal."); + if (kind.HasValue && principalId.HasValue) + { + return new Principal(kind.Value, principalId.Value); } + + throw new InvalidOperationException("Could not determine active principal."); } } diff --git a/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs b/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs index caba0ef9..1f255dd6 100644 --- a/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs +++ b/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs @@ -4,16 +4,13 @@ namespace Turnierplan.Dal.Extensions; public static class ConfigurationExtensions { - extension(IConfiguration configuration) + public static bool UseInMemoryDatabase(this IConfiguration configuration) { - public bool UseInMemoryDatabase() - { - return configuration.GetSection("Database").GetValue("InMemory", false); - } + return configuration.GetSection("Database").GetValue("InMemory", false); + } - public string? GetDatabaseConnectionString() - { - return configuration.GetSection("Database").GetValue("ConnectionString"); - } + public static string? GetDatabaseConnectionString(this IConfiguration configuration) + { + return configuration.GetSection("Database").GetValue("ConnectionString"); } } diff --git a/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs b/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs index 1b099674..9bd74dba 100644 --- a/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs +++ b/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs @@ -4,14 +4,11 @@ namespace Turnierplan.Dal.Extensions; public static class UnitOfWorkExtensions { - extension(IUnitOfWork unitOfWork) + public static async Task WrapTransactionAsync(this IUnitOfWork unitOfWork) { - public async Task WrapTransactionAsync() - { - await unitOfWork.BeginTransactionAsync(); + await unitOfWork.BeginTransactionAsync(); - return new TransactionWrapper(unitOfWork); - } + return new TransactionWrapper(unitOfWork); } public sealed class TransactionWrapper : IAsyncDisposable diff --git a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs index 8c486b33..405a6e14 100644 --- a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs +++ b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs @@ -331,274 +331,268 @@ file static class MatchPlanQuestPdfExtensions { private const string TableHeaderBackgroundColor = "aaaaaa"; - extension(ColumnDescriptor column) + public static void Groups(this ColumnDescriptor column, Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) { - public void Groups(Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) + var groupsArray = tournament.Groups.OrderBy(group => group.AlphabeticalId).ToArray(); + for (var i = 0; i < groupsArray.Length; i += 2) { - var groupsArray = tournament.Groups.OrderBy(group => group.AlphabeticalId).ToArray(); - for (var i = 0; i < groupsArray.Length; i += 2) - { - var groupLeft = groupsArray[i]; - var groupRight = i + 1 < groupsArray.Length ? groupsArray[i + 1] : null; + var groupLeft = groupsArray[i]; + var groupRight = i + 1 < groupsArray.Length ? groupsArray[i + 1] : null; - if (groupRight is not null) - { - var isLastRow = i + 2 >= groupsArray.Length; - column.Item().PaddingBottom(isLastRow ? 0 : 6, Unit.Millimetre).AlignCenter().Row(row => - { - row.Spacing(6, Unit.Millimetre); - row.RelativeItem().AlignRight().Group(groupLeft, localization, outcomes); - row.RelativeItem().Group(groupRight, localization, outcomes); - }); - } - else + if (groupRight is not null) + { + var isLastRow = i + 2 >= groupsArray.Length; + column.Item().PaddingBottom(isLastRow ? 0 : 6, Unit.Millimetre).AlignCenter().Row(row => { - column.Item().AlignCenter().Group(groupLeft, localization, outcomes); - } + row.Spacing(6, Unit.Millimetre); + row.RelativeItem().AlignRight().Group(groupLeft, localization, outcomes); + row.RelativeItem().Group(groupRight, localization, outcomes); + }); + } + else + { + column.Item().AlignCenter().Group(groupLeft, localization, outcomes); } } } - extension(IContainer container) + public static void MatchTimeSection(this IContainer container, DateTime? kickoff, TimeSpan? playTime, TimeSpan? pauseTime, ILocalization localization) { - public void MatchTimeSection(DateTime? kickoff, TimeSpan? playTime, TimeSpan? pauseTime, ILocalization localization) + container.Row(row => { - container.Row(row => + row.Spacing(5, Unit.Millimetre); + + if (kickoff.HasValue) { - row.Spacing(5, Unit.Millimetre); + row.AutoItem().Text(text => + { + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPre")); + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.Kickoff", kickoff)).Underline().Bold(); + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPost")); + }); + } - if (kickoff.HasValue) + if (playTime.HasValue) + { + row.AutoItem().Text(text => { - row.AutoItem().Text(text => - { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPre")); - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.Kickoff", kickoff)).Underline().Bold(); - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPost")); - }); - } + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTimePre")); + var minutes = (int)playTime.Value.TotalMinutes; + var seconds = (int)playTime.Value.TotalSeconds - minutes * 60; + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTime", minutes, seconds)).Underline().Bold(); + text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PlayTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); + }); + } - if (playTime.HasValue) + if (pauseTime.HasValue) + { + row.AutoItem().Text(text => { - row.AutoItem().Text(text => - { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTimePre")); - var minutes = (int)playTime.Value.TotalMinutes; - var seconds = (int)playTime.Value.TotalSeconds - minutes * 60; - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTime", minutes, seconds)).Underline().Bold(); - text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PlayTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); - }); + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTimePre")); + var minutes = (int)pauseTime.Value.TotalMinutes; + var seconds = (int)pauseTime.Value.TotalSeconds - minutes * 60; + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTime", minutes, seconds)).Underline().Bold(); + text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PauseTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); + }); + } + }); + } + + public static void GroupPhaseMatches(this IContainer container, Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + + container.Table(table => + { + table.ColumnsDefinition(columns => + { + // Sum of all column widths should equal the width of two group tables + the spacing in between them + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(9, Unit.Millimetre); // Group + columns.ConstantColumn(9, Unit.Millimetre); // Court + columns.ConstantColumn(12, Unit.Millimetre); // Kickoff + columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team A + columns.ConstantColumn(3, Unit.Millimetre); // Separator + columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team B + + if (showResultColumn) + { + // If this column is not shown, its width is evenly distributed amongst team A/B columns + columns.ConstantColumn(14, Unit.Millimetre); // Outcome } - if (pauseTime.HasValue) + columns.ConstantColumn(6, Unit.Millimetre); // Empty Column + }); + + table.Header(header => + { + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Group")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderVertical(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); + header.Cell().ColumnSpan(3).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Teams")).Bold(); + + if (showResultColumn) { - row.AutoItem().Text(text => - { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTimePre")); - var minutes = (int)pauseTime.Value.TotalMinutes; - var seconds = (int)pauseTime.Value.TotalSeconds - minutes * 60; - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTime", minutes, seconds)).Underline().Bold(); - text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PauseTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); - }); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); } + + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black); }); - } - public void GroupPhaseMatches(Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) - { - var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var matches = tournament.Matches.Where(x => x.IsGroupMatch).OrderBy(x => x.Index).ToArray(); - container.Table(table => + for (var i = 0; i < matches.Length; i++) { - table.ColumnsDefinition(columns => - { - // Sum of all column widths should equal the width of two group tables + the spacing in between them - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(9, Unit.Millimetre); // Group - columns.ConstantColumn(9, Unit.Millimetre); // Court - columns.ConstantColumn(12, Unit.Millimetre); // Kickoff - columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team A - columns.ConstantColumn(3, Unit.Millimetre); // Separator - columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team B - - if (showResultColumn) - { - // If this column is not shown, its width is evenly distributed amongst team A/B columns - columns.ConstantColumn(14, Unit.Millimetre); // Outcome - } + var match = matches[i]; + var isLast = i == matches.Length - 1; - columns.ConstantColumn(6, Unit.Millimetre); // Empty Column - }); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Index}"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Group!.AlphabeticalId.ToString()); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Court + 1}"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).Text(match.TeamA?.Name ?? string.Empty).ClampLines(1); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text("-"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).Text(match.TeamB?.Name ?? string.Empty).ClampLines(1); - table.Header(header => + if (showResultColumn) { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Group")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderVertical(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); - header.Cell().ColumnSpan(3).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Teams")).Bold(); + var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(text).Bold(); + } - if (showResultColumn) - { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); - } + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderVertical(2).BorderColor(Colors.Black); + } + }); + } - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black); - }); + public static void DecidingMatch(this IContainer container, Tournament tournament, Match match, string headerColor, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var matches = tournament.Matches.Where(x => x.IsGroupMatch).OrderBy(x => x.Index).ToArray(); + container.Table(table => + { + table.ColumnsDefinition(columns => + { + // Sum of all column widths should equal the width of two group tables + the spacing in between them + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(9, Unit.Millimetre); // Court + columns.ConstantColumn(12, Unit.Millimetre); // Kickoff + columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team A + columns.ConstantColumn(4, Unit.Millimetre); // Separator + columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team B - for (var i = 0; i < matches.Length; i++) + if (showResultColumn) { - var match = matches[i]; - var isLast = i == matches.Length - 1; - - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Index}"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Group!.AlphabeticalId.ToString()); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Court + 1}"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).Text(match.TeamA?.Name ?? string.Empty).ClampLines(1); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text("-"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).Text(match.TeamB?.Name ?? string.Empty).ClampLines(1); - - if (showResultColumn) - { - var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(text).Bold(); - } - - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderVertical(2).BorderColor(Colors.Black); + // If this column is not shown, its width is evenly distributed amongst team A/B columns + columns.ConstantColumn(14, Unit.Millimetre); // Outcome } - }); - } - public void DecidingMatch(Tournament tournament, Match match, string headerColor, ILocalization localization, MatchPlanOutcomes outcomes) - { - var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + columns.ConstantColumn(6, Unit.Millimetre); // Empty Column + }); - container.Table(table => + table.Header(header => { - table.ColumnsDefinition(columns => + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); + header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); + header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); + header.Cell().ColumnSpan(3).Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeMatchDisplayName(match)).Bold(); + + if (showResultColumn) { - // Sum of all column widths should equal the width of two group tables + the spacing in between them - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(9, Unit.Millimetre); // Court - columns.ConstantColumn(12, Unit.Millimetre); // Kickoff - columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team A - columns.ConstantColumn(4, Unit.Millimetre); // Separator - columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team B + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); + } - if (showResultColumn) - { - // If this column is not shown, its width is evenly distributed amongst team A/B columns - columns.ConstantColumn(14, Unit.Millimetre); // Outcome - } + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black); + }); - columns.ConstantColumn(6, Unit.Millimetre); // Empty Column - }); + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Index}"); + table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Court + 1}"); + table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(0.5f).BorderRight(2).Padding(2).AlignCenter().AlignMiddle().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); - table.Header(header => - { - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); - header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); - header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); - header.Cell().ColumnSpan(3).Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeMatchDisplayName(match)).Bold(); + if (insertOutcomes) + { + table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamA?.Name ?? string.Empty).AlignCenter().ClampLines(1); + table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); + table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamB?.Name ?? string.Empty).AlignCenter().ClampLines(1); + } + else + { + table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); + table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); + table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); + } - if (showResultColumn) - { - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); - } + if (showResultColumn) + { + var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text(text).Bold(); + } - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black); - }); + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle(); + table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorA, tournament)).FontSize(7); + table.Cell().BorderBottom(2); + table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorB, tournament)).FontSize(7); + }); + } - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Index}"); - table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Court + 1}"); - table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(0.5f).BorderRight(2).Padding(2).AlignCenter().AlignMiddle().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); + private static void Group(this IContainer container, Group group, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumns = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - if (insertOutcomes) + container.Table(table => + { + table.ColumnsDefinition(columns => + { + if (showResultColumns) { - table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamA?.Name ?? string.Empty).AlignCenter().ClampLines(1); - table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); - table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamB?.Name ?? string.Empty).AlignCenter().ClampLines(1); + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(48, Unit.Millimetre); // Team Name + columns.ConstantColumn(8, Unit.Millimetre); // Points + columns.ConstantColumn(11, Unit.Millimetre); // Goals + columns.ConstantColumn(8, Unit.Millimetre); // Goal Diff. } else { - table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); - table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); - table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(75, Unit.Millimetre); // Team Name } + }); - if (showResultColumn) + table.Header(header => + { + header.Cell().ColumnSpan(2).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeGroupName(group)).Bold(); + + if (showResultColumns) { - var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text(text).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Points")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Score")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.ScoreDiff")).Bold(); } - - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle(); - table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorA, tournament)).FontSize(7); - table.Cell().BorderBottom(2); - table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorB, tournament)).FontSize(7); }); - } - private void Group(Group group, ILocalization localization, MatchPlanOutcomes outcomes) - { - var showResultColumns = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var participants = insertOutcomes + ? group.Participants.OrderBy(x => x.Statistics.Position).ToList() + : group.Participants.ToList(); - container.Table(table => + for (var i = 0; i < participants.Count; i++) { - table.ColumnsDefinition(columns => - { - if (showResultColumns) - { - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(48, Unit.Millimetre); // Team Name - columns.ConstantColumn(8, Unit.Millimetre); // Points - columns.ConstantColumn(11, Unit.Millimetre); // Goals - columns.ConstantColumn(8, Unit.Millimetre); // Goal Diff. - } - else - { - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(75, Unit.Millimetre); // Team Name - } - }); - - table.Header(header => - { - header.Cell().ColumnSpan(2).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeGroupName(group)).Bold(); - - if (showResultColumns) - { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Points")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Score")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.ScoreDiff")).Bold(); - } - }); + var participant = participants[i]; + var isLast = i == participants.Count - 1; - var participants = insertOutcomes - ? group.Participants.OrderBy(x => x.Statistics.Position).ToList() - : group.Participants.ToList(); + table.Cell().BorderLeft(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(insertOutcomes ? $"{participant.Statistics.Position}." : $"{i + 1}."); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).Text(participant.Team.Name); - for (var i = 0; i < participants.Count; i++) + if (showResultColumns) { - var participant = participants[i]; - var isLast = i == participants.Count - 1; - - table.Cell().BorderLeft(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(insertOutcomes ? $"{participant.Statistics.Position}." : $"{i + 1}."); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).Text(participant.Team.Name); - - if (showResultColumns) - { - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.Points}" : string.Empty); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreFor} : {participant.Statistics.ScoreAgainst}" : ":"); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreDifference}" : string.Empty).FontColor(participant.Statistics.ScoreDifference < 0 ? Colors.Red.Medium : Colors.Black); - } + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.Points}" : string.Empty); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreFor} : {participant.Statistics.ScoreAgainst}" : ":"); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreDifference}" : string.Empty).FontColor(participant.Statistics.ScoreDifference < 0 ? Colors.Red.Medium : Colors.Black); } - }); - } + } + }); } } From 0c88bbea66f3251e0eda07a5d804819cdee946db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sun, 1 Mar 2026 08:36:47 +0100 Subject: [PATCH 8/8] Revert "Remove extension blocks" This reverts commit d5f0b0e5ff5e446e5f80ebae2935723cb81dcc3f. --- .../Extensions/EnumerableExtensions.cs | 14 +- .../Extensions/HttpContextExtensions.cs | 65 +-- .../Extensions/ConfigurationExtensions.cs | 15 +- .../Extensions/UnitOfWorkExtensions.cs | 9 +- .../Renderer/MatchPlanRenderer.cs | 414 +++++++++--------- 5 files changed, 269 insertions(+), 248 deletions(-) diff --git a/src/Turnierplan.App/Extensions/EnumerableExtensions.cs b/src/Turnierplan.App/Extensions/EnumerableExtensions.cs index 18671ff2..67fe1ed8 100644 --- a/src/Turnierplan.App/Extensions/EnumerableExtensions.cs +++ b/src/Turnierplan.App/Extensions/EnumerableExtensions.cs @@ -2,13 +2,19 @@ namespace Turnierplan.App.Extensions; internal static class EnumerableExtensions { - public static bool None(this IEnumerable enumerable, Func predicate) + extension(IEnumerable enumerable) { - return !enumerable.Any(predicate); + public bool None(Func predicate) + { + return !enumerable.Any(predicate); + } } - public static IEnumerable WhereNotNull(this IEnumerable enumerable) + extension(IEnumerable enumerable) { - return enumerable.Where(x => x is not null)!; + public IEnumerable WhereNotNull() + { + return enumerable.Where(x => x is not null)!; + } } } diff --git a/src/Turnierplan.App/Extensions/HttpContextExtensions.cs b/src/Turnierplan.App/Extensions/HttpContextExtensions.cs index 912af102..d86b042d 100644 --- a/src/Turnierplan.App/Extensions/HttpContextExtensions.cs +++ b/src/Turnierplan.App/Extensions/HttpContextExtensions.cs @@ -5,48 +5,51 @@ namespace Turnierplan.App.Extensions; internal static class HttpContextExtensions { - public static Guid GetCurrentUserIdOrThrow(this HttpContext context) + extension(HttpContext context) { - var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.UserId))?.Value; - - if (string.IsNullOrWhiteSpace(claimValue) || !Guid.TryParse(claimValue, out var userId)) + public Guid GetCurrentUserIdOrThrow() { - throw new InvalidOperationException("Could not get current user ID from HttpContext."); - } + var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.UserId))?.Value; - return userId; - } + if (string.IsNullOrWhiteSpace(claimValue) || !Guid.TryParse(claimValue, out var userId)) + { + throw new InvalidOperationException("Could not get current user ID from HttpContext."); + } - public static bool IsCurrentUserAdministrator(this HttpContext context) - { - var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.Administrator))?.Value; + return userId; + } - return !string.IsNullOrWhiteSpace(claimValue) && claimValue.Equals("true"); - } + public bool IsCurrentUserAdministrator() + { + var claimValue = context.User.Claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.Administrator))?.Value; - public static Principal GetActivePrincipal(this HttpContext context) - { - PrincipalKind? kind = null; - Guid? principalId = null; + return !string.IsNullOrWhiteSpace(claimValue) && claimValue.Equals("true"); + } - foreach (var claim in context.User.Claims) + public Principal GetActivePrincipal() { - switch (claim.Type) + PrincipalKind? kind = null; + Guid? principalId = null; + + foreach (var claim in context.User.Claims) { - case ClaimTypes.PrincipalKind: - kind = Enum.Parse(claim.Value); - break; - case ClaimTypes.PrincipalId: - principalId = Guid.Parse(claim.Value); - break; + switch (claim.Type) + { + case ClaimTypes.PrincipalKind: + kind = Enum.Parse(claim.Value); + break; + case ClaimTypes.PrincipalId: + principalId = Guid.Parse(claim.Value); + break; + } } - } - if (kind.HasValue && principalId.HasValue) - { - return new Principal(kind.Value, principalId.Value); - } + if (kind.HasValue && principalId.HasValue) + { + return new Principal(kind.Value, principalId.Value); + } - throw new InvalidOperationException("Could not determine active principal."); + throw new InvalidOperationException("Could not determine active principal."); + } } } diff --git a/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs b/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs index 1f255dd6..caba0ef9 100644 --- a/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs +++ b/src/Turnierplan.Dal/Extensions/ConfigurationExtensions.cs @@ -4,13 +4,16 @@ namespace Turnierplan.Dal.Extensions; public static class ConfigurationExtensions { - public static bool UseInMemoryDatabase(this IConfiguration configuration) + extension(IConfiguration configuration) { - return configuration.GetSection("Database").GetValue("InMemory", false); - } + public bool UseInMemoryDatabase() + { + return configuration.GetSection("Database").GetValue("InMemory", false); + } - public static string? GetDatabaseConnectionString(this IConfiguration configuration) - { - return configuration.GetSection("Database").GetValue("ConnectionString"); + public string? GetDatabaseConnectionString() + { + return configuration.GetSection("Database").GetValue("ConnectionString"); + } } } diff --git a/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs b/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs index 9bd74dba..1b099674 100644 --- a/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs +++ b/src/Turnierplan.Dal/Extensions/UnitOfWorkExtensions.cs @@ -4,11 +4,14 @@ namespace Turnierplan.Dal.Extensions; public static class UnitOfWorkExtensions { - public static async Task WrapTransactionAsync(this IUnitOfWork unitOfWork) + extension(IUnitOfWork unitOfWork) { - await unitOfWork.BeginTransactionAsync(); + public async Task WrapTransactionAsync() + { + await unitOfWork.BeginTransactionAsync(); - return new TransactionWrapper(unitOfWork); + return new TransactionWrapper(unitOfWork); + } } public sealed class TransactionWrapper : IAsyncDisposable diff --git a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs index 405a6e14..8c486b33 100644 --- a/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs +++ b/src/Turnierplan.PdfRendering/Renderer/MatchPlanRenderer.cs @@ -331,268 +331,274 @@ file static class MatchPlanQuestPdfExtensions { private const string TableHeaderBackgroundColor = "aaaaaa"; - public static void Groups(this ColumnDescriptor column, Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) + extension(ColumnDescriptor column) { - var groupsArray = tournament.Groups.OrderBy(group => group.AlphabeticalId).ToArray(); - for (var i = 0; i < groupsArray.Length; i += 2) + public void Groups(Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) { - var groupLeft = groupsArray[i]; - var groupRight = i + 1 < groupsArray.Length ? groupsArray[i + 1] : null; - - if (groupRight is not null) - { - var isLastRow = i + 2 >= groupsArray.Length; - column.Item().PaddingBottom(isLastRow ? 0 : 6, Unit.Millimetre).AlignCenter().Row(row => - { - row.Spacing(6, Unit.Millimetre); - row.RelativeItem().AlignRight().Group(groupLeft, localization, outcomes); - row.RelativeItem().Group(groupRight, localization, outcomes); - }); - } - else + var groupsArray = tournament.Groups.OrderBy(group => group.AlphabeticalId).ToArray(); + for (var i = 0; i < groupsArray.Length; i += 2) { - column.Item().AlignCenter().Group(groupLeft, localization, outcomes); - } - } - } + var groupLeft = groupsArray[i]; + var groupRight = i + 1 < groupsArray.Length ? groupsArray[i + 1] : null; - public static void MatchTimeSection(this IContainer container, DateTime? kickoff, TimeSpan? playTime, TimeSpan? pauseTime, ILocalization localization) - { - container.Row(row => - { - row.Spacing(5, Unit.Millimetre); - - if (kickoff.HasValue) - { - row.AutoItem().Text(text => + if (groupRight is not null) { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPre")); - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.Kickoff", kickoff)).Underline().Bold(); - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPost")); - }); - } - - if (playTime.HasValue) - { - row.AutoItem().Text(text => - { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTimePre")); - var minutes = (int)playTime.Value.TotalMinutes; - var seconds = (int)playTime.Value.TotalSeconds - minutes * 60; - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTime", minutes, seconds)).Underline().Bold(); - text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PlayTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); - }); - } - - if (pauseTime.HasValue) - { - row.AutoItem().Text(text => + var isLastRow = i + 2 >= groupsArray.Length; + column.Item().PaddingBottom(isLastRow ? 0 : 6, Unit.Millimetre).AlignCenter().Row(row => + { + row.Spacing(6, Unit.Millimetre); + row.RelativeItem().AlignRight().Group(groupLeft, localization, outcomes); + row.RelativeItem().Group(groupRight, localization, outcomes); + }); + } + else { - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTimePre")); - var minutes = (int)pauseTime.Value.TotalMinutes; - var seconds = (int)pauseTime.Value.TotalSeconds - minutes * 60; - text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTime", minutes, seconds)).Underline().Bold(); - text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PauseTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); - }); + column.Item().AlignCenter().Group(groupLeft, localization, outcomes); + } } - }); + } } - public static void GroupPhaseMatches(this IContainer container, Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) + extension(IContainer container) { - var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - - container.Table(table => + public void MatchTimeSection(DateTime? kickoff, TimeSpan? playTime, TimeSpan? pauseTime, ILocalization localization) { - table.ColumnsDefinition(columns => + container.Row(row => { - // Sum of all column widths should equal the width of two group tables + the spacing in between them - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(9, Unit.Millimetre); // Group - columns.ConstantColumn(9, Unit.Millimetre); // Court - columns.ConstantColumn(12, Unit.Millimetre); // Kickoff - columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team A - columns.ConstantColumn(3, Unit.Millimetre); // Separator - columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team B + row.Spacing(5, Unit.Millimetre); - if (showResultColumn) + if (kickoff.HasValue) { - // If this column is not shown, its width is evenly distributed amongst team A/B columns - columns.ConstantColumn(14, Unit.Millimetre); // Outcome + row.AutoItem().Text(text => + { + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPre")); + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.Kickoff", kickoff)).Underline().Bold(); + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.KickoffPost")); + }); } - columns.ConstantColumn(6, Unit.Millimetre); // Empty Column - }); - - table.Header(header => - { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Group")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderVertical(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); - header.Cell().ColumnSpan(3).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Teams")).Bold(); - - if (showResultColumn) + if (playTime.HasValue) { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); + row.AutoItem().Text(text => + { + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTimePre")); + var minutes = (int)playTime.Value.TotalMinutes; + var seconds = (int)playTime.Value.TotalSeconds - minutes * 60; + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PlayTime", minutes, seconds)).Underline().Bold(); + text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PlayTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); + }); } - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black); + if (pauseTime.HasValue) + { + row.AutoItem().Text(text => + { + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTimePre")); + var minutes = (int)pauseTime.Value.TotalMinutes; + var seconds = (int)pauseTime.Value.TotalSeconds - minutes * 60; + text.Span(localization.Get("Documents.MatchPlan.MatchTimes.PauseTime", minutes, seconds)).Underline().Bold(); + text.Span(localization.Get($"Documents.MatchPlan.MatchTimes.PauseTimePost.{(minutes == 1 && seconds == 0 ? "One" : "Many")}")); + }); + } }); + } - var matches = tournament.Matches.Where(x => x.IsGroupMatch).OrderBy(x => x.Index).ToArray(); + public void GroupPhaseMatches(Tournament tournament, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - for (var i = 0; i < matches.Length; i++) + container.Table(table => { - var match = matches[i]; - var isLast = i == matches.Length - 1; + table.ColumnsDefinition(columns => + { + // Sum of all column widths should equal the width of two group tables + the spacing in between them + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(9, Unit.Millimetre); // Group + columns.ConstantColumn(9, Unit.Millimetre); // Court + columns.ConstantColumn(12, Unit.Millimetre); // Kickoff + columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team A + columns.ConstantColumn(3, Unit.Millimetre); // Separator + columns.ConstantColumn(showResultColumn ? 55 : 62, Unit.Millimetre); // Team B + + if (showResultColumn) + { + // If this column is not shown, its width is evenly distributed amongst team A/B columns + columns.ConstantColumn(14, Unit.Millimetre); // Outcome + } - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Index}"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Group!.AlphabeticalId.ToString()); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Court + 1}"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).Text(match.TeamA?.Name ?? string.Empty).ClampLines(1); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text("-"); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).Text(match.TeamB?.Name ?? string.Empty).ClampLines(1); + columns.ConstantColumn(6, Unit.Millimetre); // Empty Column + }); - if (showResultColumn) + table.Header(header => { - var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(text).Bold(); - } + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Group")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderVertical(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); + header.Cell().ColumnSpan(3).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Teams")).Bold(); - table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderVertical(2).BorderColor(Colors.Black); - } - }); - } + if (showResultColumn) + { + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); + } - public static void DecidingMatch(this IContainer container, Tournament tournament, Match match, string headerColor, ILocalization localization, MatchPlanOutcomes outcomes) - { - var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black); + }); - container.Table(table => - { - table.ColumnsDefinition(columns => - { - // Sum of all column widths should equal the width of two group tables + the spacing in between them - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(9, Unit.Millimetre); // Court - columns.ConstantColumn(12, Unit.Millimetre); // Kickoff - columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team A - columns.ConstantColumn(4, Unit.Millimetre); // Separator - columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team B + var matches = tournament.Matches.Where(x => x.IsGroupMatch).OrderBy(x => x.Index).ToArray(); - if (showResultColumn) + for (var i = 0; i < matches.Length; i++) { - // If this column is not shown, its width is evenly distributed amongst team A/B columns - columns.ConstantColumn(14, Unit.Millimetre); // Outcome - } + var match = matches[i]; + var isLast = i == matches.Length - 1; + + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Index}"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Group!.AlphabeticalId.ToString()); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text($"{match.Court + 1}"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).Text(match.TeamA?.Name ?? string.Empty).ClampLines(1); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text("-"); + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).Text(match.TeamB?.Name ?? string.Empty).ClampLines(1); + + if (showResultColumn) + { + var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(text).Bold(); + } - columns.ConstantColumn(6, Unit.Millimetre); // Empty Column + table.Cell().BorderTop(0.5f).BorderBottom(isLast ? 2 : 0.5f).BorderVertical(2).BorderColor(Colors.Black); + } }); + } - table.Header(header => - { - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); - header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); - header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); - header.Cell().ColumnSpan(3).Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeMatchDisplayName(match)).Bold(); + public void DecidingMatch(Tournament tournament, Match match, string headerColor, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumn = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - if (showResultColumn) + container.Table(table => + { + table.ColumnsDefinition(columns => { - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); - } + // Sum of all column widths should equal the width of two group tables + the spacing in between them + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(9, Unit.Millimetre); // Court + columns.ConstantColumn(12, Unit.Millimetre); // Kickoff + columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team A + columns.ConstantColumn(4, Unit.Millimetre); // Separator + columns.ConstantColumn(showResultColumn ? 59 : 66, Unit.Millimetre); // Team B - header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black); - }); + if (showResultColumn) + { + // If this column is not shown, its width is evenly distributed amongst team A/B columns + columns.ConstantColumn(14, Unit.Millimetre); // Outcome + } - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Index}"); - table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Court + 1}"); - table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(0.5f).BorderRight(2).Padding(2).AlignCenter().AlignMiddle().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); + columns.ConstantColumn(6, Unit.Millimetre); // Empty Column + }); - if (insertOutcomes) - { - table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamA?.Name ?? string.Empty).AlignCenter().ClampLines(1); - table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); - table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamB?.Name ?? string.Empty).AlignCenter().ClampLines(1); - } - else - { - table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); - table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); - table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); - } + table.Header(header => + { + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Index")).Bold(); + header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(0.5f).BorderLeft(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Court")).Bold(); + header.Cell().Background(headerColor).BorderHorizontal(2).BorderRight(2).BorderLeft(0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Kickoff")).Bold(); + header.Cell().ColumnSpan(3).Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeMatchDisplayName(match)).Bold(); - if (showResultColumn) - { - var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text(text).Bold(); - } + if (showResultColumn) + { + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Outcome")).Bold(); + } - table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle(); - table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorA, tournament)).FontSize(7); - table.Cell().BorderBottom(2); - table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorB, tournament)).FontSize(7); - }); - } + header.Cell().Background(headerColor).Border(2).BorderColor(Colors.Black); + }); - private static void Group(this IContainer container, Group group, ILocalization localization, MatchPlanOutcomes outcomes) - { - var showResultColumns = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Index}"); + table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(2).BorderRight(0.5f).Padding(2).AlignCenter().AlignMiddle().Text($"{match.Court + 1}"); + table.Cell().RowSpan(2).BorderHorizontal(2).BorderLeft(0.5f).BorderRight(2).Padding(2).AlignCenter().AlignMiddle().Text(match.Kickoff is null ? string.Empty : localization.Get("Documents.MatchPlan.MatchKickoffTime", match.Kickoff)); - container.Table(table => - { - table.ColumnsDefinition(columns => - { - if (showResultColumns) + if (insertOutcomes) { - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(48, Unit.Millimetre); // Team Name - columns.ConstantColumn(8, Unit.Millimetre); // Points - columns.ConstantColumn(11, Unit.Millimetre); // Goals - columns.ConstantColumn(8, Unit.Millimetre); // Goal Diff. + table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamA?.Name ?? string.Empty).AlignCenter().ClampLines(1); + table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); + table.Cell().BorderBottom(0.5f).Padding(2).Text(match.TeamB?.Name ?? string.Empty).AlignCenter().ClampLines(1); } else { - columns.ConstantColumn(7, Unit.Millimetre); // Index - columns.ConstantColumn(75, Unit.Millimetre); // Team Name + table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); + table.Cell().BorderBottom(0.5f).Padding(2).Text("-").AlignCenter(); + table.Cell().BorderBottom(0.5f).Padding(2).Text(string.Empty); } - }); - - table.Header(header => - { - header.Cell().ColumnSpan(2).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeGroupName(group)).Bold(); - if (showResultColumns) + if (showResultColumn) { - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Points")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Score")).Bold(); - header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.ScoreDiff")).Bold(); + var text = insertOutcomes && match.IsFinished ? $"{match.ScoreA} : {match.ScoreB}" : ":"; + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle().Text(text).Bold(); } + + table.Cell().RowSpan(2).Border(2).Padding(2).AlignCenter().AlignMiddle(); + table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorA, tournament)).FontSize(7); + table.Cell().BorderBottom(2); + table.Cell().BorderBottom(2).PaddingBottom(1).AlignCenter().Text(localization.LocalizeTeamSelector(match.TeamSelectorB, tournament)).FontSize(7); }); + } - var participants = insertOutcomes - ? group.Participants.OrderBy(x => x.Statistics.Position).ToList() - : group.Participants.ToList(); + private void Group(Group group, ILocalization localization, MatchPlanOutcomes outcomes) + { + var showResultColumns = outcomes is MatchPlanOutcomes.ShowEmptyOutcomeStructures or MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; + var insertOutcomes = outcomes is MatchPlanOutcomes.ShowOutcomeStructuresWithOutcomes; - for (var i = 0; i < participants.Count; i++) + container.Table(table => { - var participant = participants[i]; - var isLast = i == participants.Count - 1; + table.ColumnsDefinition(columns => + { + if (showResultColumns) + { + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(48, Unit.Millimetre); // Team Name + columns.ConstantColumn(8, Unit.Millimetre); // Points + columns.ConstantColumn(11, Unit.Millimetre); // Goals + columns.ConstantColumn(8, Unit.Millimetre); // Goal Diff. + } + else + { + columns.ConstantColumn(7, Unit.Millimetre); // Index + columns.ConstantColumn(75, Unit.Millimetre); // Team Name + } + }); + + table.Header(header => + { + header.Cell().ColumnSpan(2).Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.LocalizeGroupName(group)).Bold(); + + if (showResultColumns) + { + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Points")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.Score")).Bold(); + header.Cell().Background(TableHeaderBackgroundColor).Border(2).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(localization.Get("Documents.MatchPlan.Headers.ScoreDiff")).Bold(); + } + }); - table.Cell().BorderLeft(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(insertOutcomes ? $"{participant.Statistics.Position}." : $"{i + 1}."); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).Text(participant.Team.Name); + var participants = insertOutcomes + ? group.Participants.OrderBy(x => x.Statistics.Position).ToList() + : group.Participants.ToList(); - if (showResultColumns) + for (var i = 0; i < participants.Count; i++) { - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.Points}" : string.Empty); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreFor} : {participant.Statistics.ScoreAgainst}" : ":"); - table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreDifference}" : string.Empty).FontColor(participant.Statistics.ScoreDifference < 0 ? Colors.Red.Medium : Colors.Black); + var participant = participants[i]; + var isLast = i == participants.Count - 1; + + table.Cell().BorderLeft(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).AlignCenter().Text(insertOutcomes ? $"{participant.Statistics.Position}." : $"{i + 1}."); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : showResultColumns ? 0.5f : 0).BorderColor(Colors.Black).Padding(2).Text(participant.Team.Name); + + if (showResultColumns) + { + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.Points}" : string.Empty); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreFor} : {participant.Statistics.ScoreAgainst}" : ":"); + table.Cell().BorderRight(2).BorderBottom(isLast ? 2 : 0.5f).BorderColor(Colors.Black).Padding(2).AlignCenter().AlignMiddle().Text(insertOutcomes ? $"{participant.Statistics.ScoreDifference}" : string.Empty).FontColor(participant.Statistics.ScoreDifference < 0 ? Colors.Red.Medium : Colors.Black); + } } - } - }); + }); + } } }