From f5ad1faf0e66bcee574a67d4557d9382b95116ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 10:26:08 +0100 Subject: [PATCH 1/9] Add delete modal component --- .../delete-button.component.html | 4 +-- .../delete-button/delete-button.component.ts | 34 +++++++++++++++++-- .../delete-modal/delete-modal.component.html | 19 +++++++++++ .../delete-modal/delete-modal.component.ts | 34 +++++++++++++++++++ .../delete-widget.component.html | 24 +------------ .../delete-widget/delete-widget.component.ts | 24 ++++++------- 6 files changed, 100 insertions(+), 39 deletions(-) create mode 100644 src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html create mode 100644 src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.ts diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html b/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html index d7b03374..81fdffb0 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html @@ -5,7 +5,7 @@ container="body" popoverClass="tp-popover-slim" [tabindex]="popover.isOpen() ? 0 : -1" - (keydown.enter)="popover.isOpen() && confirmed.emit()" + (keydown.enter)="popover.isOpen() && deleteClicked()" [ngClass]="{ 'tp-cursor-pointer': reducedFootprint, 'btn btn-sm btn-outline-danger': !reducedFootprint, @@ -23,7 +23,7 @@ @if (!disabled) { } (); + + private readonly modalService = inject(NgbModal); + + protected deleteClicked(): void { + if (this.modalConfirmation) { + if (!this.translationKey || !this.targetObjectName) { + throw new Error('The translation key and target object name must be specified if modal confirmation is enabled.'); + } + + showDeleteModal(this.modalService, this.translationKey, this.targetObjectName).subscribe({ + next: (result): void => { + if (result) { + this.confirmed.emit(); + } + } + }); + } else { + this.confirmed.emit(); + } + } } diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html new file mode 100644 index 00000000..80217338 --- /dev/null +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html @@ -0,0 +1,19 @@ + + + diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.ts b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.ts new file mode 100644 index 00000000..354d48a0 --- /dev/null +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.ts @@ -0,0 +1,34 @@ +import { Component, inject } from '@angular/core'; +import { ActionButtonComponent } from '../action-button/action-button.component'; +import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'; +import { E2eDirective } from '../../../core/directives/e2e.directive'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { map, Observable } from 'rxjs'; +import { filter } from 'rxjs/operators'; + +export const showDeleteModal = (ngbModal: NgbModal, translationKey: string, targetObjectName: string): Observable => { + const ref = ngbModal.open(DeleteModalComponent, { + size: 'md', + fullscreen: 'md', + centered: true + }); + + ref.componentInstance.translationKey = translationKey; + ref.componentInstance.targetObjectName = targetObjectName; + + return ref.closed.pipe( + map((x) => x as boolean), + filter((x) => x) + ); +}; + +@Component({ + imports: [ActionButtonComponent, TranslatePipe, TranslateDirective, E2eDirective], + templateUrl: './delete-modal.component.html' +}) +export class DeleteModalComponent { + public translationKey: string = ''; + public targetObjectName: string = ''; + + protected readonly modal = inject(NgbActiveModal); +} diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.html b/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.html index 81fada33..f5b18a1a 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.html @@ -25,29 +25,7 @@ [icon]="'trash'" [disabled]="!allowDeletion" [title]="'Portal.DeleteWidget.Delete'" - (buttonClick)="deleteClicked(confirmModal)" /> + (buttonClick)="deleteClicked()" /> } - - - - - - diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.ts b/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.ts index 4effd227..ca814369 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-widget/delete-widget.component.ts @@ -4,7 +4,8 @@ import { NgClass } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { ActionButtonComponent } from '../action-button/action-button.component'; import { E2eDirective } from '../../../core/directives/e2e.directive'; -import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { showDeleteModal } from '../delete-modal/delete-modal.component'; @Component({ selector: 'tp-delete-widget', @@ -20,6 +21,7 @@ export class DeleteWidgetComponent { @Input() public set targetObjectName(value: string) { + this._targetObjectName = value; this.confirmationText = value.replaceAll(/[^A-Za-z0-9.\-_|ÄÖÜäöüß ]+/g, '').trim(); } @@ -29,7 +31,8 @@ export class DeleteWidgetComponent { protected confirmationText?: string; protected confirmationTextInput: string = ''; protected allowDeletion = false; - protected openModal?: NgbModalRef; + + private _targetObjectName: string = ''; constructor(private readonly modalService: NgbModal) {} @@ -37,18 +40,15 @@ export class DeleteWidgetComponent { this.allowDeletion = this.confirmationText !== undefined && this.confirmationText === this.confirmationTextInput.trim(); } - protected deleteClicked(template: TemplateRef): void { + protected deleteClicked(): void { if (this.allowDeletion) { - this.openModal = this.modalService.open(template, { - size: 'md', - fullscreen: 'md', - centered: true + showDeleteModal(this.modalService, this.translationKey, this._targetObjectName).subscribe({ + next: (result): void => { + if (result) { + this.deleteClick.emit(); + } + } }); } } - - protected confirmDeleteClicked(): void { - this.openModal?.dismiss(); - this.deleteClick.emit(); - } } From 3674ea676ee1e1cf9feb8ab1a282655b3656a62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 10:31:42 +0100 Subject: [PATCH 2/9] Adjust behavior --- .../delete-button.component.html | 3 +- .../delete-button/delete-button.component.ts | 31 ++++++++++++------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html b/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html index 81fdffb0..324536d5 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-button/delete-button.component.html @@ -1,11 +1,12 @@
+ } @@ -93,7 +93,11 @@ @if (writeAllowed) { - + } 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 0ed0cfa9..2b57a301 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 @@ -450,8 +450,8 @@ export class ViewTournamentComponent implements OnInit, OnDestroy { next: () => { this.notificationService.showNotification( 'info', - 'Portal.ViewTournament.Documents.DeleteToast.Title', - 'Portal.ViewTournament.Documents.DeleteToast.Message' + 'Portal.ViewTournament.Documents.Delete.SuccessToast.Title', + 'Portal.ViewTournament.Documents.Delete.SuccessToast.Message' ); }, error: (error) => { From 13eb98f17609d7c534b320254cdb90f3ec5bcc01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 10:37:52 +0100 Subject: [PATCH 5/9] Implement for image manager --- src/Turnierplan.App/Client/src/app/i18n/de.ts | 9 ++++++--- .../image-manager/image-manager.component.html | 7 ++++++- .../components/image-manager/image-manager.component.ts | 4 ++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/i18n/de.ts b/src/Turnierplan.App/Client/src/app/i18n/de.ts index be55eec0..f0756497 100644 --- a/src/Turnierplan.App/Client/src/app/i18n/de.ts +++ b/src/Turnierplan.App/Client/src/app/i18n/de.ts @@ -230,9 +230,12 @@ export const de = { Title: 'Bild umbenennen', EnterNewName: 'Geben Sie den neuen Namen für das Bild ein:' }, - DeleteToast: { - Title: 'Bild wurde gelöscht', - Message: 'Das Bild wurde gelöscht.' + Delete: { + Title: 'Bild löschen', + SuccessToast: { + Title: 'Bild wurde gelöscht', + Message: 'Das Bild wurde gelöscht.' + } } }, ApiKeys: { diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.html b/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.html index 8069ec88..017051b4 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.html @@ -58,7 +58,12 @@ @if (writeAllowed) { - + + } diff --git a/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.ts b/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.ts index aca5694a..2cd31bad 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.ts +++ b/src/Turnierplan.App/Client/src/app/portal/components/image-manager/image-manager.component.ts @@ -87,8 +87,8 @@ export class ImageManagerComponent { this.notificationService.showNotification( 'info', - 'Portal.ViewOrganization.Images.DeleteToast.Title', - 'Portal.ViewOrganization.Images.DeleteToast.Message' + 'Portal.ViewOrganization.Images.Delete.SuccessToast.Title', + 'Portal.ViewOrganization.Images.Delete.SuccessToast.Message' ); }, error: (error) => { From 7877f4cf8c0537de83ffdb3b0b1994c32513cefd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 10:38:04 +0100 Subject: [PATCH 6/9] Add TODOs for 2 other places --- .../manage-applications/manage-applications.component.html | 1 + .../src/app/portal/components/ranking/ranking.component.html | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html b/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html index 8542fb31..89c84b51 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html @@ -245,6 +245,7 @@ [margin]="false" [tooltipText]="'Portal.ViewPlanningRealm.Applications.CannotDeleteTeamWhileLinked'" /> } @else { + } diff --git a/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html b/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html index 38391802..f9a7e5a4 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html @@ -48,6 +48,7 @@ @if (ranking.rankingOverwriteId !== undefined) {
+
} From bd4d349485ba23294bf7bd03637327300198f4a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 18:55:25 +0100 Subject: [PATCH 7/9] Implement other two places --- src/Turnierplan.App/Client/src/app/i18n/de.ts | 9 +++++++-- .../manage-applications.component.html | 8 ++++++-- .../portal/components/ranking/ranking.component.html | 12 ++++++++---- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/i18n/de.ts b/src/Turnierplan.App/Client/src/app/i18n/de.ts index f0756497..70bd5bc0 100644 --- a/src/Turnierplan.App/Client/src/app/i18n/de.ts +++ b/src/Turnierplan.App/Client/src/app/i18n/de.ts @@ -423,10 +423,12 @@ export const de = { HideRanking: 'Platzierung ausblenden', HideRankingTooltip: 'Wenn die Platzierung ausgeblendet wird, ist sie nicht mehr auf den öffentlichen Turnierseiten sichtbar' }, - DeleteOverwrite: { + Delete: { Header: 'Zurücksetzen', Tooltip: - 'Wenn zuvorige Änderungen gelöscht werden, wird an der jeweiligen Stelle wieder die Standard-Platzierung anzeigt, welche im Turnier berechnet werden.' + 'Wenn zuvorige Änderungen gelöscht werden, wird an der jeweiligen Stelle wieder die Standard-Platzierung anzeigt, welche im Turnier berechnet werden.', + Title: 'Platzierung zurücksetzen', + ObjectDisplayName: 'Zuweisung für {{value}}. Platz' } }, Documents: { @@ -1172,6 +1174,9 @@ export const de = { NoLabels: 'keine Labels', NoLinkedTournament: 'kein Turnier verknüpft', CannotDeleteTeamWhileLinked: 'Die Mannschaft kann nicht gelöscht werden, solange sie an einem Turnier teilnimmt.', + DeleteTeam: { + Title: 'Mannschaft löschen' + }, RenameTeam: { Title: 'Mannschaft umbenennen', EnterNewName: diff --git a/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html b/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html index 89c84b51..29a5ba0e 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/manage-applications/manage-applications.component.html @@ -245,8 +245,12 @@ [margin]="false" [tooltipText]="'Portal.ViewPlanningRealm.Applications.CannotDeleteTeamWhileLinked'" /> } @else { - - + } diff --git a/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html b/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html index f9a7e5a4..c120aed5 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/ranking/ranking.component.html @@ -10,8 +10,8 @@ @if (showDeleteOverwriteColumn) { - - + + } @@ -48,8 +48,12 @@ @if (ranking.rankingOverwriteId !== undefined) {
- - +
} From 7e7634c4ab4870635512bd80dc320d5bd030ff45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Elias=20H=C3=B6rner?= Date: Sat, 21 Feb 2026 19:03:12 +0100 Subject: [PATCH 8/9] AdditionalModalText --- src/Turnierplan.App/Client/src/app/i18n/de.ts | 8 ++++++-- .../components/delete-modal/delete-modal.component.html | 7 +++++++ .../components/image-manager/image-manager.component.html | 1 - 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/Turnierplan.App/Client/src/app/i18n/de.ts b/src/Turnierplan.App/Client/src/app/i18n/de.ts index 70bd5bc0..277b7cd5 100644 --- a/src/Turnierplan.App/Client/src/app/i18n/de.ts +++ b/src/Turnierplan.App/Client/src/app/i18n/de.ts @@ -232,6 +232,7 @@ export const de = { }, Delete: { Title: 'Bild löschen', + AdditionalModalText: 'Nach dem Löschen wird das Bild bei allen bisher verknüpften Turnieren nicht mehr sichtbar sein!', SuccessToast: { Title: 'Bild wurde gelöscht', Message: 'Das Bild wurde gelöscht.' @@ -428,7 +429,9 @@ export const de = { Tooltip: 'Wenn zuvorige Änderungen gelöscht werden, wird an der jeweiligen Stelle wieder die Standard-Platzierung anzeigt, welche im Turnier berechnet werden.', Title: 'Platzierung zurücksetzen', - ObjectDisplayName: 'Zuweisung für {{value}}. Platz' + ObjectDisplayName: 'Zuweisung für {{value}}. Platz', + AdditionalModalText: + 'Nach dem Löschen wird an der jeweiligen Stelle wieder die Standard-Platzierung anzeigt, welche im Turnier berechnet werden.' } }, Documents: { @@ -1175,7 +1178,8 @@ export const de = { NoLinkedTournament: 'kein Turnier verknüpft', CannotDeleteTeamWhileLinked: 'Die Mannschaft kann nicht gelöscht werden, solange sie an einem Turnier teilnimmt.', DeleteTeam: { - Title: 'Mannschaft löschen' + Title: 'Mannschaft aus Anmeldung löschen', + AdditionalModalText: 'Die anderen Mannschaften in dieser Anmeldung sind hiervon nicht betroffen.' }, RenameTeam: { Title: 'Mannschaft umbenennen', diff --git a/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html index 80217338..56fa78b5 100644 --- a/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html +++ b/src/Turnierplan.App/Client/src/app/portal/components/delete-modal/delete-modal.component.html @@ -3,6 +3,13 @@