Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
f95be25
Improve filter component
eliaspr Sep 1, 2025
17d1039
New group/team list without dnd
eliaspr Sep 1, 2025
74775e9
Remove dnd package
eliaspr Sep 1, 2025
68b5fc9
Revert "Remove dnd package"
eliaspr Sep 1, 2025
25dd527
Add swap teams button
eliaspr Sep 1, 2025
c1e5f6a
Disabled rounded corners on the cards
eliaspr Sep 1, 2025
1b0fdf7
Add TODO
eliaspr Sep 1, 2025
3d556da
Add tooltips for team swapping buttons
eliaspr Sep 1, 2025
e41fd81
Move team to other group
eliaspr Sep 2, 2025
45ac4aa
Fix wrongful delete confirmation
eliaspr Sep 2, 2025
8685118
Add rename function for groups
eliaspr Sep 2, 2025
d47792c
Rename team feature
eliaspr Sep 2, 2025
950e2ee
@else
eliaspr Sep 2, 2025
ed058ac
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 2, 2025
5adca07
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 4, 2025
140399a
del uznused import
eliaspr Sep 4, 2025
85736b0
Re-add button for adding team
eliaspr Sep 4, 2025
f05a042
fix
eliaspr Sep 5, 2025
392dcbe
Add separate dialog
eliaspr Sep 5, 2025
de59c79
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 5, 2025
04b5e56
Modal can now add new teams
eliaspr Sep 5, 2025
276e302
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 13, 2025
3b0f21d
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 13, 2025
3885ec3
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 13, 2025
6d10ae9
Merge branch 'main' into eliaspr/3-configure-tournament-ui
eliaspr Sep 13, 2025
774b709
Update TODOs
eliaspr Sep 13, 2025
0e6d74a
add placeholder
eliaspr Sep 14, 2025
10491af
Rm tooltip
eliaspr Sep 14, 2025
6b9d7bd
Add component for select application team
eliaspr Sep 14, 2025
6c61ac9
Implement data flow
eliaspr Sep 14, 2025
3f75bbd
WIP selector
eliaspr Sep 14, 2025
51cc722
linked teams query/dto
eliaspr Sep 14, 2025
5bde5d2
Add component for pagination + integrate into existing manage applica…
eliaspr Sep 14, 2025
a2ab012
revert endpoint change
eliaspr Sep 14, 2025
36a39b1
Display & select teams
eliaspr Sep 14, 2025
a0083fa
i18n
eliaspr Sep 14, 2025
0bd3d61
Implement multi-select and better confirm button
eliaspr Sep 14, 2025
70beef2
Fix request
eliaspr Sep 14, 2025
c610623
Some fixes/improvements
eliaspr Sep 14, 2025
08f0c91
update tooltip
eliaspr Sep 14, 2025
e6d05a8
disable pt. 1
eliaspr Sep 14, 2025
6978374
Delete unused repo
eliaspr Sep 14, 2025
82a2d4d
Display name of planningrealm / tournament class
eliaspr Sep 14, 2025
bcf9b5b
Display already selected teams
eliaspr Sep 14, 2025
adc9380
PR review
eliaspr Sep 14, 2025
322353d
SQ
eliaspr Sep 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// TODO: Add e2e test for configuring tournament (w/o planning realm)
64 changes: 55 additions & 9 deletions src/Turnierplan.App/Client/src/app/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ export const de = {
OpenInNewTab: 'In neuem Tab öffnen',
UnsavedChanges: 'Sie haben ungespeicherte Änderungen.',
ApplyChanges: 'Änderungen übernehmen',
CtrlEnter: 'Strg. + Enter zum Speichern'
CtrlEnter: 'Strg. + Enter zum Speichern',
PaginationRange: 'Zeige {{start}} - {{end}} von {{total}}'
},
UserInfoPopover: {
Text: 'Sie sind angemeldet als:\n<strong>{{userName}}</strong>',
Expand Down Expand Up @@ -651,15 +652,42 @@ export const de = {
Title: 'Teilnehmende Mannschaften',
GroupWithName: 'Gruppe {{alphabeticalId}}: {{displayName}}',
GroupWithoutName: 'Gruppe {{alphabeticalId}}',
DragDropPlaceholder: 'hier einfügen...',
TeamEntry: '{{index}}. {{name}}',
CannotDeleteGroupWithTeams: 'Entfernen Sie erst alle Mannschaften, um diese Gruppe zu löschen',
MoveToOtherGroup: 'Verschieben in:',
NoTeamsNotice: 'Keine Mannschaften vorhanden',
NewTeamNamePlaceholder: 'Neue Mannschaft',
AddTeamButton: 'Hinzufügen',
NoGroupsNotice: 'Es existieren aktuell keine Gruppen',
AddGroupButton: 'Neue Gruppe hinzufügen',
ShuffleGroupsButton: 'Gruppen würfeln',
GroupLimitReached: 'Die maximale Gruppenanzahl (26) ist erreicht.'
GroupLimitReached: 'Die maximale Gruppenanzahl (26) ist erreicht.',
GroupRename: {
Button: 'Gruppennamen ändern',
Title: 'Gruppennamen ändern',
EnterNewName: 'Geben Sie den neuen Namen für die Gruppe ein:',
EmptyAllowed: 'Sie können das Feld leerlassen, um zum Standardnamen ("Gruppe A-Z") zurückzukehren.'
},
TeamFromPlanningRealm: 'Diese Mannschaft stammt aus einen Turnierplaner: "{{planningRealm}}" ("{{tournamentClass}}")',
TeamRename: {
Button: 'Teamnamen ändern',
Title: 'Teamnamen ändern',
EnterNewName: 'Geben Sie den neuen Namen für die Mannschaft ein:'
},
AddTeam: {
Button: 'Mannschaft',
Title: 'Mannschaft hinzufügen',
NewTeam: {
Navigation: 'Neue Mannschaft',
Explanation: 'Erstellen Sie eine neue Mannschaft, welche nur in diesem Turnier mitspielt.',
TeamName: 'Mannschaftsname:',
TeamNamePlaceholder: 'Neuen Mannschaftsnamen eingeben',
TeamNameEmpty: 'Der Mannschaftsname darf nicht leer sein'
},
ImportTeam: {
Navigation: 'Angemeldete Mannschaft'
},
Confirm: 'Hinzufügen',
ConfirmWithSingle: '{{count}} Mannschaft hinzufügen',
ConfirmWithCount: '{{count}} Mannschaften hinzufügen'
}
},
GroupPhase: {
Title: 'Gruppenphase',
Expand Down Expand Up @@ -1004,8 +1032,7 @@ export const de = {
HiddenTeamsTooltip: 'Diese Anmeldung beinhaltet weitere Mannschaften, welche nicht den Filterkriterien entsprechen',
HiddenTeams: '{{count}} weitere Mannschaft(en) werden aufgrund Ihrer Filterkriterien nicht angezeigt.',
HiddenTeamsShowAll: 'alle anzeigen',
NoResults: 'Es gibt keine Anmeldungen, welche den angegebenen Filterkriterien entsprechen.',
PaginationRange: 'Zeige {{start}} - {{end}} von {{total}}'
NoResults: 'Es gibt keine Anmeldungen, welche den angegebenen Filterkriterien entsprechen.'
},
Settings: {
Rename: {
Expand Down Expand Up @@ -1107,7 +1134,9 @@ export const de = {
},
MultiSelectFilter: {
All: 'alle',
Reset: 'Filter zurücksetzen'
Reset: 'Filter zurücksetzen',
NoFilter: 'keine Filter verfügbar',
NoFurtherFilter: 'keine weiteren Filter verfügbar'
},
ErrorPage: {
Title: 'Fehler',
Expand All @@ -1116,6 +1145,23 @@ export const de = {
'Beim Verarbeiten der Anfrage ist ein unerwarteter Server-Fehler aufgetreten.\nLaden Sie die Seite neu und versuchen Sie es erneut.',
ErrorDescription: 'Fehlerbeschreibung:'
},
SelectApplicationTeam: {
Explanation: 'Wählen Sie eine angemeldete Mannschaft aus einem Turnierplaner:',
LoadingPlanningRealms: 'Turnierplaner werden geladen',
LoadingPlanningRealmsFailed: 'Fehler beim Laden der Turnierplaner',
NoPlanningRealms: 'In dieser Organisation gibt es keine Turnierplaner',
LoadingPlanningRealmDetail: 'Turnierplaner wird geladen',
LoadingPlanningRealmDetailFailed: 'Fehler beim Laden des Turnierplaners',
FilterExplanation: 'Suchen und wählen Sie die Mannschaften zum Hinzufügen:',
FilterTooltip: 'Dieser Suchfilter wird synchronisiert mit dem Filter auf der Turnierplaner-Seite',
LoadingApplications: 'Mannschaftsanmeldungen werden geladen',
LoadingApplicationsFailed: 'Fehler beim Laden der Mannschaftsanmeldungen. Eventuell fehlen die erforderlichen Rechte',
NoApplications:
'Es gibt keine Anmeldungen, die den Suchkriterien entsprechen, oder alle Anmeldungen sind bereits einem Turnier zugeordnet.',
TournamentClass: 'Turnierklasse',
TeamName: 'Mannschaft',
ApplicationDetails: 'Anmeldung'
},
RbacManagement: {
Title: 'Zugriff verwalten',
ButtonLabel: 'Verwalten',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
@if (icon) {
<i class="bi bi-{{ icon }}" aria-hidden="true"></i>
}
<span [translate]="title" [ngClass]="{ 'ms-2': icon }"></span>
<span [translate]="title" [translateParams]="titleParams" [ngClass]="{ 'ms-2': icon }"></span>
</button>
} @else if (mode === 'IconRightAndText') {
<button type="button" class="btn btn-sm btn-{{ type }} text-nowrap" [ngClass]="{ disabled: disabled }" (click)="buttonClick.emit()">
<span [translate]="title" [ngClass]="{ 'me-2': icon }"></span>
<span [translate]="title" [translateParams]="titleParams" [ngClass]="{ 'me-2': icon }"></span>
@if (icon) {
<i class="bi bi-{{ icon }}" aria-hidden="true"></i>
}
Expand All @@ -17,7 +17,7 @@
type="button"
class="btn btn-sm btn-{{ type }}"
[ngClass]="{ disabled: disabled }"
[attr.aria-label]="title | translate"
[attr.aria-label]="title | translate: titleParams"
(click)="buttonClick.emit()">
<i class="bi bi-{{ icon }}" aria-hidden="true"></i>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export class ActionButtonComponent {
@Input()
public title = '';

@Input()
public titleParams: { [key: string]: unknown } = {};

@Input()
public mode: 'IconLeftAndText' | 'IconRightAndText' | 'IconOnly' = 'IconLeftAndText';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<div class="modal-header">
<div class="modal-title" [translate]="'Portal.ConfigureTournament.Sections.Participants.AddTeam.Title'"></div>
<button type="button" class="btn-close" (click)="modal.dismiss()" [tabindex]="-1"></button>
</div>
<div class="modal-body d-flex flex-column">
<ul ngbNav #nav="ngbNav" [(activeId)]="currentMode" (activeIdChange)="currentModeChanged()" class="nav-tabs">
<li [ngbNavItem]="AddTeamMode.NewTeam">
<button ngbNavLink translate="Portal.ConfigureTournament.Sections.Participants.AddTeam.NewTeam.Navigation"></button>
<ng-template ngbNavContent>
<div translate="Portal.ConfigureTournament.Sections.Participants.AddTeam.NewTeam.Explanation"></div>

<div class="form-group">
<label
class="form-label"
for="content"
translate="Portal.ConfigureTournament.Sections.Participants.AddTeam.NewTeam.TeamName"></label>
<input
#addTeamNameInput
class="form-control"
type="text"
id="content"
autofocus
[(ngModel)]="addTeamName"
[placeholder]="'Portal.ConfigureTournament.Sections.Participants.AddTeam.NewTeam.TeamNamePlaceholder' | translate"
[ngClass]="{ 'is-invalid': confirmAttempted && addTeamNameInvalid }"
(keyup.enter)="confirm()" />

@if (addTeamNameInvalid) {
<div class="invalid-feedback" translate="Portal.ConfigureTournament.Sections.Participants.AddTeam.NewTeam.TeamNameEmpty"></div>
}
</div>
</ng-template>
</li>
<li [ngbNavItem]="AddTeamMode.ImportTeam">
<button ngbNavLink translate="Portal.ConfigureTournament.Sections.Participants.AddTeam.ImportTeam.Navigation"></button>
<ng-template ngbNavContent>
@if (organizationId) {
<tp-select-application-team
[organizationId]="organizationId"
[usedApplicationTeamIds]="usedApplicationTeamIds"
(teamSelected)="importTeamSelected = $event" />
}
</ng-template>
</li>
</ul>

<div [ngbNavOutlet]="nav" class="mt-4"></div>
</div>
<div class="modal-footer">
<tp-action-button [type]="'outline-dark'" [title]="'Portal.General.Cancel'" (buttonClick)="modal.dismiss()" />

@if (currentMode === AddTeamMode.ImportTeam) {
@let count = importTeamSelected ? importTeamSelected.length : 0;
<tp-action-button
[type]="'success'"
[title]="'Portal.ConfigureTournament.Sections.Participants.AddTeam.' + (count === 1 ? 'ConfirmWithSingle' : 'ConfirmWithCount')"
[titleParams]="{ count: count }"
[disabled]="count === 0"
(buttonClick)="confirm()" />
} @else {
<tp-action-button
[type]="'success'"
[title]="'Portal.ConfigureTournament.Sections.Participants.AddTeam.Confirm'"
(buttonClick)="confirm()" />
}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
import { ActionButtonComponent } from '../action-button/action-button.component';
import { FormsModule } from '@angular/forms';
import { TranslateDirective, TranslatePipe } from '@ngx-translate/core';
import { NgbActiveModal, NgbNav, NgbNavContent, NgbNavItem, NgbNavLinkButton, NgbNavOutlet } from '@ng-bootstrap/ng-bootstrap';
import { LocalStorageService } from '../../services/local-storage.service';
import { NgClass } from '@angular/common';
import { TemporaryTeam } from '../../pages/configure-tournament/configure-tournament.component';
import { SelectApplicationTeamComponent, SelectApplicationTeamResult } from '../select-application-team/select-application-team.component';
import { PublicId } from '../../../api';

enum AddTeamMode {
NewTeam = 'NewTeam',
ImportTeam = 'ImportTeam'
}

@Component({
imports: [
ActionButtonComponent,
FormsModule,
TranslateDirective,
NgbNav,
NgbNavItem,
NgbNavLinkButton,
NgbNavContent,
NgbNavOutlet,
NgClass,
TranslatePipe,
SelectApplicationTeamComponent
],
templateUrl: './configure-tournament-add-team.component.html'
})
export class ConfigureTournamentAddTeamComponent implements AfterViewInit {
protected readonly AddTeamMode = AddTeamMode;

@ViewChild('addTeamNameInput')
protected addTeamNameInput!: ElementRef<HTMLInputElement>;

protected organizationId?: PublicId;
protected usedApplicationTeamIds: number[] = [];
protected currentMode: AddTeamMode = AddTeamMode.NewTeam;
protected addTeamName: string = '';
protected importTeamSelected?: SelectApplicationTeamResult;
protected confirmAttempted = false;

constructor(
protected readonly modal: NgbActiveModal,
private readonly localStorageService: LocalStorageService
) {
const value = localStorageService.getAddTeamDialogMode();
this.currentMode = value === AddTeamMode.ImportTeam ? AddTeamMode.ImportTeam : AddTeamMode.NewTeam;
}

protected get addTeamNameInvalid(): boolean {
return this.addTeamName.trim().length === 0;
}

public ngAfterViewInit(): void {
if (this.currentMode === AddTeamMode.NewTeam) {
this.addTeamNameInput.nativeElement.focus();
setTimeout(() => this.addTeamNameInput.nativeElement.select(), 20);
}
}

public init(organizationId: PublicId, usedApplicationTeamIds: number[]): void {
this.organizationId = organizationId;
this.usedApplicationTeamIds = usedApplicationTeamIds;
}

protected currentModeChanged(): void {
this.localStorageService.setAddTeamDialogMode(this.currentMode);

if (this.currentMode === AddTeamMode.ImportTeam) {
this.importTeamSelected = undefined;
}
}

protected confirm(): void {
this.confirmAttempted = true;

switch (this.currentMode) {
case AddTeamMode.NewTeam: {
const trimmed = this.addTeamName.trim();
if (trimmed.length > 0) {
this.modal.close([
{
id: undefined,
name: this.addTeamName
}
] as TemporaryTeam[]);
}
break;
}
case AddTeamMode.ImportTeam: {
if (this.importTeamSelected) {
this.modal.close(
this.importTeamSelected.map(
(team) =>
({
id: undefined,
name: team.name,
teamLink: {
planningRealmId: team.planningRealmId,
planningRealmName: team.planningRealmName,
tournamentClassName: team.tournamentClassName,
applicationTeamId: team.applicationTeamId
}
}) as TemporaryTeam
)
);
}
break;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
container="body"
popoverClass="tp-popover-slim"
[tabindex]="popover.isOpen() ? 0 : -1"
(keydown)="popover.isOpen() && confirmed.emit()"
(keydown.enter)="popover.isOpen() && confirmed.emit()"
[ngClass]="{
'tp-cursor-pointer': reducedFootprint,
'btn btn-sm btn-outline-danger': !reducedFootprint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,21 +128,6 @@
</tbody>
</table>

<div class="mt-4 d-flex flex-column align-items-center gap-2">
<div
class="small"
translate="Portal.ViewPlanningRealm.Applications.PaginationRange"
[translateParams]="{
start: result.currentPage * result.itemsPerPage + 1,
end: result.currentPage * result.itemsPerPage + result.items.length,
total: result.totalItems
}"></div>
<ngb-pagination
[collectionSize]="result.totalItems"
[page]="currentPage"
[pageSize]="pageSize"
[maxSize]="5"
(pageChange)="switchPage($event)" />
</div>
<tp-pagination [pagination]="result" (pageChange)="switchPage($event)" />
}
}
Loading
Loading