From 60bd9e85aee7d95f39b24b79fc1f815b41c5494a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20G=C3=B6ttfert?= Date: Sun, 28 Dec 2025 18:40:30 +0100 Subject: [PATCH 1/3] Fix #304: Sort documents deterministically by name and lastModifiedAt Documents on the view-tournament page are now sorted alphabetically by name with lastModifiedAt as a tiebreaker, ensuring consistent ordering across all scenarios. Previously, documents were sorted by GUID-based IDs which resulted in non-deterministic ordering. --- .../view-tournament.component.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts index b74b4f72..17078536 100644 --- a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts @@ -204,7 +204,7 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { private readonly router: Router, private readonly localStorageService: LocalStorageService, private readonly translateService: TranslateService - ) {} + ) { } protected get isTournamentTreeVisible(): boolean { return this.canShowTournamentTree && this.currentPage === ViewTournamentComponent.MatchPlanPageId && this.showTournamentTree; @@ -255,7 +255,13 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { this.turnierplanApi.invoke(getDocuments, { tournamentId: this.tournament.id }).subscribe({ next: (documents) => { this.documents = documents ?? []; - this.documents.sort((a, b) => a.id.localeCompare(b.id)); + this.documents.sort((a, b) => { + const nameComparison = a.name.localeCompare(b.name); + if (nameComparison !== 0) { + return nameComparison; + } + return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); + }); this.isLoadingDocuments = false; }, error: (error) => { @@ -888,7 +894,13 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { return this.turnierplanApi.invoke(getDocuments, { tournamentId: this.tournament.id }).pipe( tap((result) => { this.documents = result; - this.documents.sort((a, b) => a.id.localeCompare(b.id)); + this.documents.sort((a, b) => { + const nameComparison = a.name.localeCompare(b.name); + if (nameComparison !== 0) { + return nameComparison; + } + return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); + }); }) ); } @@ -1055,7 +1067,7 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { const rankingOverwriteId = ranking.reason === RankingReason.ManuallyChanged ? this.tournament?.rankingOverwrites.find((o) => o.placementRank === ranking.placementRank && o.assignTeamId === ranking.teamId) - ?.id + ?.id : undefined; return { From a7ce2f081e06254bc999aa73797b79e287404d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20G=C3=B6ttfert?= Date: Sun, 28 Dec 2025 22:37:50 +0100 Subject: [PATCH 2/3] run formatter --- .../portal/pages/view-tournament/view-tournament.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts index 17078536..8f85b4c4 100644 --- a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts @@ -204,7 +204,7 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { private readonly router: Router, private readonly localStorageService: LocalStorageService, private readonly translateService: TranslateService - ) { } + ) {} protected get isTournamentTreeVisible(): boolean { return this.canShowTournamentTree && this.currentPage === ViewTournamentComponent.MatchPlanPageId && this.showTournamentTree; @@ -1067,7 +1067,7 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { const rankingOverwriteId = ranking.reason === RankingReason.ManuallyChanged ? this.tournament?.rankingOverwrites.find((o) => o.placementRank === ranking.placementRank && o.assignTeamId === ranking.teamId) - ?.id + ?.id : undefined; return { From 6b4dc3f339f9fb937e5c3641334089928c536a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christopher=20G=C3=B6ttfert?= Date: Sun, 11 Jan 2026 00:15:06 +0100 Subject: [PATCH 3/3] document sorting after renaming --- .../document-manager.component.ts | 11 +++++++ .../view-tournament.component.ts | 29 ++++++++++--------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/document-manager/document-manager.component.ts b/src/Turnierplan.App/Client/src/app/portal/components/document-manager/document-manager.component.ts index c8b44897..ffed0b04 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/document-manager/document-manager.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/components/document-manager/document-manager.component.ts @@ -188,6 +188,7 @@ export class DocumentManagerComponent { const document = this.documents.find((x) => x.id === id); if (document) { document.name = name; + this.sortDocuments(); } this.currentlyUpdatingName = undefined; }, @@ -197,6 +198,16 @@ export class DocumentManagerComponent { }); } + private sortDocuments(): void { + this.documents = [...this.documents].sort((a, b) => { + const nameComparison = a.name.localeCompare(b.name); + if (nameComparison !== 0) { + return nameComparison; + } + return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); + }); + } + protected deleteDocument(id: string): void { if (id === this.currentlyViewedDocumentId) { this.displayPdfViewer = false; diff --git a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts index 8f85b4c4..135f4d39 100644 --- a/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/pages/view-tournament/view-tournament.component.ts @@ -255,13 +255,7 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { this.turnierplanApi.invoke(getDocuments, { tournamentId: this.tournament.id }).subscribe({ next: (documents) => { this.documents = documents ?? []; - this.documents.sort((a, b) => { - const nameComparison = a.name.localeCompare(b.name); - if (nameComparison !== 0) { - return nameComparison; - } - return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); - }); + this.sortDocuments(); this.isLoadingDocuments = false; }, error: (error) => { @@ -894,17 +888,24 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { return this.turnierplanApi.invoke(getDocuments, { tournamentId: this.tournament.id }).pipe( tap((result) => { this.documents = result; - this.documents.sort((a, b) => { - const nameComparison = a.name.localeCompare(b.name); - if (nameComparison !== 0) { - return nameComparison; - } - return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); - }); + this.sortDocuments(); }) ); } + private sortDocuments(): void { + if (!this.documents) { + return; + } + this.documents = [...this.documents].sort((a, b) => { + const nameComparison = a.name.localeCompare(b.name); + if (nameComparison !== 0) { + return nameComparison; + } + return new Date(a.lastModifiedAt).getTime() - new Date(b.lastModifiedAt).getTime(); + }); + } + private setTournament(tournament: TournamentDto | undefined): void { this.tournament = tournament; this.titleService.setTitleFrom(tournament);