Compare commits
54 Commits
ce497ee7d8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 87d9291bfb | |||
| c646ae15ee | |||
|
|
8128ba4744 | ||
|
|
19e4372006 | ||
|
|
7fd84005f9 | ||
|
|
a56317fecd | ||
|
|
ecbb16776c | ||
|
|
3008f45dfa | ||
|
|
5baf1228f7 | ||
|
|
aacf06e203 | ||
| 368aa53b9d | |||
| 335577fa4d | |||
| 1f55acecd0 | |||
| c1fedd728b | |||
| 0e9e8d1e0f | |||
|
|
0e1e1932a1 | ||
| fb36ee1a05 | |||
| 9cb3568770 | |||
| d387452044 | |||
| 2cdccc4dd8 | |||
| 8083e7fc5f | |||
| 0fd693aa42 | |||
| edaffc82e4 | |||
| 1dbc2f1ca9 | |||
| 21fa706b45 | |||
| f2ee2b467d | |||
| cdf27f1948 | |||
| 2c33c9a68d | |||
| 181aebf16a | |||
| 6a9ea43e23 | |||
| 02b884bbcf | |||
| 5c846a6351 | |||
| 929fd6b581 | |||
| 33bfde27d4 | |||
| 564340fec4 | |||
|
|
d1338bf524 | ||
|
|
f858020330 | ||
|
|
cf86a449e2 | ||
|
|
3dd4746304 | ||
|
|
1e45a12392 | ||
|
|
8082a72d4d | ||
|
|
27e90ceb17 | ||
| d6de1c43fb | |||
| 5a5c134002 | |||
| 71e5b5c912 | |||
| 76acf809bd | |||
| 9bc0d5f362 | |||
| 348a2ab2c5 | |||
| 1b2f123683 | |||
| 1da6246d9a | |||
| 26c0c1a2da | |||
| f88cd94316 | |||
| af96ecda32 | |||
| f20d9dfc92 |
4
TODO.txt
4
TODO.txt
@@ -32,11 +32,11 @@
|
|||||||
Player-registrations: oude toernooien verbergen via knop
|
Player-registrations: oude toernooien verbergen via knop
|
||||||
Onderdeel afsluiten
|
Onderdeel afsluiten
|
||||||
Wedstrijd opgave
|
Wedstrijd opgave
|
||||||
Blessures
|
Invallers
|
||||||
Titels pagina's
|
Titels pagina's
|
||||||
Authenticatie
|
Authenticatie
|
||||||
Progress indicator tijdens communicatie backend
|
Progress indicator tijdens communicatie backend
|
||||||
|
Bij tellers rekening houden met invallers
|
||||||
https://blog.shhdharmen.me/browser-storage-in-angular-ssr
|
https://blog.shhdharmen.me/browser-storage-in-angular-ssr
|
||||||
|
|
||||||
Won't do / later:
|
Won't do / later:
|
||||||
|
|||||||
2634
package-lock.json
generated
2634
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -11,23 +11,24 @@
|
|||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/animations": "^20.1.6",
|
"@angular/animations": "^20.3.2",
|
||||||
"@angular/cdk": "^20.1.5",
|
"@angular/cdk": "^20.2.5",
|
||||||
"@angular/common": "^20.1.6",
|
"@angular/common": "^20.3.2",
|
||||||
"@angular/compiler": "^20.1.6",
|
"@angular/compiler": "^20.3.2",
|
||||||
"@angular/core": "^20.1.6",
|
"@angular/core": "^20.3.2",
|
||||||
"@angular/forms": "^20.1.6",
|
"@angular/forms": "^20.3.2",
|
||||||
"@angular/material": "^20.1.5",
|
"@angular/material": "^20.2.5",
|
||||||
"@angular/material-moment-adapter": "^20.1.5",
|
"@angular/material-moment-adapter": "^20.2.5",
|
||||||
"@angular/platform-browser": "^20.1.6",
|
"@angular/platform-browser": "^20.3.2",
|
||||||
"@angular/platform-browser-dynamic": "^20.1.6",
|
"@angular/platform-browser-dynamic": "^20.3.2",
|
||||||
"@angular/platform-server": "^20.1.6",
|
"@angular/platform-server": "^20.3.2",
|
||||||
"@angular/router": "^20.1.6",
|
"@angular/router": "^20.3.2",
|
||||||
"@angular/ssr": "^20.1.5",
|
"@angular/ssr": "^20.3.3",
|
||||||
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
"@ng-bootstrap/ng-bootstrap": "^19.0.1",
|
||||||
"@popperjs/core": "^2.11.8",
|
"@popperjs/core": "^2.11.8",
|
||||||
"bootstrap": "^5.3.7",
|
"bootstrap": "^5.3.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
|
"jwt-decode": "^4.0.0",
|
||||||
"moment": "2.30.1",
|
"moment": "2.30.1",
|
||||||
"ngx-cookie-service-ssr": "^20.1.0",
|
"ngx-cookie-service-ssr": "^20.1.0",
|
||||||
"ngx-mask": "^20.0.3",
|
"ngx-mask": "^20.0.3",
|
||||||
@@ -37,10 +38,10 @@
|
|||||||
"zone.js": "~0.15.1"
|
"zone.js": "~0.15.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@angular/build": "^20.1.5",
|
"@angular/build": "^20.3.3",
|
||||||
"@angular/cli": "^20.1.5",
|
"@angular/cli": "^20.3.3",
|
||||||
"@angular/compiler-cli": "^20.1.6",
|
"@angular/compiler-cli": "^20.3.2",
|
||||||
"@angular/localize": "^20.1.6",
|
"@angular/localize": "^20.3.2",
|
||||||
"@types/express": "^5.0.3",
|
"@types/express": "^5.0.3",
|
||||||
"@types/jasmine": "~5.1.0",
|
"@types/jasmine": "~5.1.0",
|
||||||
"@types/node": "^24.2.1",
|
"@types/node": "^24.2.1",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<mat-icon class="menu-button-icon" style="color: white;">menu</mat-icon>
|
<mat-icon class="menu-button-icon" style="color: white;">menu</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
-->
|
-->
|
||||||
<h5 class="m-3">{{ title }}</h5>
|
<h5 class="m-3">{{ header }}</h5>
|
||||||
<span class="spacer"></span>
|
<span class="spacer"></span>
|
||||||
<a routerLink="/tournaments" mat-button>
|
<a routerLink="/tournaments" mat-button>
|
||||||
<mat-icon>list</mat-icon>
|
<mat-icon>list</mat-icon>
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
<mat-icon>group</mat-icon>
|
<mat-icon>group</mat-icon>
|
||||||
Spelers
|
Spelers
|
||||||
</a>
|
</a>
|
||||||
@if (this.userService.isLoggedIn()) {
|
@if (this.authService.isLoggedIn()) {
|
||||||
<button mat-flat-button [matMenuTriggerFor]="accountMenu">
|
<button mat-flat-button [matMenuTriggerFor]="accountMenu">
|
||||||
<mat-icon>person</mat-icon>
|
<mat-icon>person</mat-icon>
|
||||||
{{ user }}
|
{{ this.authService.getUsername() }}
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #accountMenu="matMenu">
|
<mat-menu #accountMenu="matMenu">
|
||||||
<button mat-menu-item (click)="logOut()">
|
<button mat-menu-item (click)="logOut()">
|
||||||
|
|||||||
@@ -1,50 +1,63 @@
|
|||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router';
|
import {ActivatedRoute, NavigationEnd, Router, RouterLink, RouterOutlet} from '@angular/router';
|
||||||
import {CommonModule, NgOptimizedImage} from "@angular/common";
|
import {CommonModule, NgOptimizedImage} from "@angular/common";
|
||||||
import {MatAnchor, MatButton} from "@angular/material/button";
|
import {MatAnchor, MatButton} from "@angular/material/button";
|
||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {MatToolbar} from "@angular/material/toolbar";
|
import {MatToolbar} from "@angular/material/toolbar";
|
||||||
import {TitleService} from "./service/title.service";
|
import {filter, map, Subscription} from "rxjs";
|
||||||
import {Subscription} from "rxjs";
|
|
||||||
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
||||||
import {UserService} from "./authentication/user.service";
|
// import {UserService} from "./authentication/user.service";
|
||||||
import {TournamentService} from "./service/tournament.service";
|
import {TournamentService} from "./service/tournament.service";
|
||||||
|
import {HeaderService} from "./service/header.service";
|
||||||
|
import {AuthService} from "./auth/auth.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
imports: [RouterOutlet, CommonModule, RouterLink, MatAnchor, MatIcon, MatButton, MatToolbar, NgOptimizedImage, MatMenuTrigger, MatMenu, MatMenuItem],
|
imports: [RouterOutlet, CommonModule, RouterLink, MatAnchor, MatIcon, MatButton, MatToolbar, NgOptimizedImage, MatMenuTrigger, MatMenu, MatMenuItem],
|
||||||
providers: [TitleService],
|
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrl: './app.component.scss'
|
styleUrl: './app.component.scss'
|
||||||
})
|
})
|
||||||
export class AppComponent implements OnInit, OnDestroy {
|
export class AppComponent implements OnInit {
|
||||||
|
|
||||||
title: string;
|
header: string;
|
||||||
titleSubscription: Subscription;
|
// user: string;
|
||||||
user: string;
|
// userSubscription: Subscription;
|
||||||
userSubscription: Subscription;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected userService: UserService,
|
protected authService: AuthService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
protected activatedRoute: ActivatedRoute,
|
protected activatedRoute: ActivatedRoute,
|
||||||
private titleService: TitleService,
|
private tournamentService: TournamentService,
|
||||||
private tournamentService: TournamentService
|
private headerService: HeaderService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleSubscription = this.titleService.currentTitle.subscribe(newTitle => this.title = newTitle);
|
// this.userSubscription = this.userService.currentUser.subscribe(newUser => this.user = newUser);
|
||||||
this.userSubscription = this.userService.currentUser.subscribe(newUser => this.user = newUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
this.router.events.pipe(
|
||||||
this.titleSubscription.unsubscribe();
|
filter(event => event instanceof NavigationEnd),
|
||||||
|
map(() => {
|
||||||
|
let currentRoute = this.activatedRoute;
|
||||||
|
while (currentRoute.firstChild) {
|
||||||
|
currentRoute = currentRoute.firstChild;
|
||||||
|
}
|
||||||
|
return currentRoute.snapshot.data['header'] || '';
|
||||||
|
})
|
||||||
|
).subscribe(header => {
|
||||||
|
this.header = header;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.headerService.header$.subscribe(override => {
|
||||||
|
if (override) {
|
||||||
|
this.header = override;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
logOut() {
|
logOut() {
|
||||||
this.userService.removeUser();
|
this.authService.logout();
|
||||||
this.router.navigate(['/auth/login']);
|
this.router.navigate(['/login']);
|
||||||
}
|
}
|
||||||
|
|
||||||
addTestData() {
|
addTestData() {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {ApplicationConfig, provideZoneChangeDetection} from '@angular/core';
|
import {ApplicationConfig, inject, provideAppInitializer, provideZoneChangeDetection} from '@angular/core';
|
||||||
import {provideRouter} from '@angular/router';
|
import {provideRouter} from '@angular/router';
|
||||||
|
|
||||||
import {routes} from './app.routes';
|
import {routes} from './app.routes';
|
||||||
@@ -7,10 +7,9 @@ import {HTTP_INTERCEPTORS, provideHttpClient, withFetch, withInterceptorsFromDi}
|
|||||||
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
|
import {provideAnimationsAsync} from '@angular/platform-browser/animations/async';
|
||||||
import {provideMomentDateAdapter} from "@angular/material-moment-adapter";
|
import {provideMomentDateAdapter} from "@angular/material-moment-adapter";
|
||||||
import {MAT_SNACK_BAR_DEFAULT_OPTIONS} from "@angular/material/snack-bar";
|
import {MAT_SNACK_BAR_DEFAULT_OPTIONS} from "@angular/material/snack-bar";
|
||||||
import {AuthGuard} from "./authentication/authguard";
|
import {provideNgxMask} from "ngx-mask";
|
||||||
import {TokenInterceptor} from "./authentication/tokenInterceptor";
|
import {provideAnimations} from "@angular/platform-browser/animations";
|
||||||
import {ErrorInterceptor} from "./authentication/errorInterceptor";
|
import {AuthInterceptor} from "./auth/auth.interceptor";
|
||||||
import {provideEnvironmentNgxMask, provideNgxMask} from "ngx-mask";
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -18,12 +17,11 @@ export const appConfig: ApplicationConfig = {
|
|||||||
provideRouter(routes),
|
provideRouter(routes),
|
||||||
provideClientHydration(),
|
provideClientHydration(),
|
||||||
provideHttpClient(withFetch(), withInterceptorsFromDi()),
|
provideHttpClient(withFetch(), withInterceptorsFromDi()),
|
||||||
provideAnimationsAsync(),
|
provideAnimations(),
|
||||||
provideMomentDateAdapter(undefined, {useUtc: false}),
|
provideMomentDateAdapter(undefined, {useUtc: false}),
|
||||||
{ provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 2500}},
|
{ provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { duration: 2500}},
|
||||||
AuthGuard,
|
{ provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true },
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true },
|
|
||||||
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
|
|
||||||
provideNgxMask(),
|
provideNgxMask(),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,31 @@
|
|||||||
import { Routes } from '@angular/router';
|
import {Routes} from '@angular/router';
|
||||||
import { PlayerListComponent} from "./components/player-list/player-list.component";
|
import {PlayerListComponent} from "./components/player-list/player-list.component";
|
||||||
import {PlayerEditComponent} from "./components/player-edit/player-edit.component";
|
import {PlayerEditComponent} from "./components/player-edit/player-edit.component";
|
||||||
import {TournamentListComponent} from "./components/tournament-list/tournament-list.component";
|
import {TournamentListComponent} from "./components/tournament-list/tournament-list.component";
|
||||||
import {TournamentEditComponent} from "./components/tournament-edit/tournament-edit.component";
|
import {TournamentEditComponent} from "./components/tournament-edit/tournament-edit.component";
|
||||||
import {PlayerRegistrationsComponent} from "./components/player-registrations/player-registrations.component";
|
import {PlayerRegistrationsComponent} from "./components/player-registrations/player-registrations.component";
|
||||||
import {TournamentRegistrationsComponent} from "./components/tournament-registrations/tournament-registrations.component";
|
|
||||||
import {TournamentValidateComponent} from "./components/tournament-validate/tournament-validate.component";
|
|
||||||
import {TournamentDivideComponent} from "./components/tournament-divide/tournament-divide.component";
|
|
||||||
import {TournamentDrawComponent} from "./components/tournament-draw/tournament-draw.component";
|
import {TournamentDrawComponent} from "./components/tournament-draw/tournament-draw.component";
|
||||||
import {TournamentManageComponent} from "./components/tournament-manage/tournament-manage.component";
|
import {TournamentManageComponent} from "./components/tournament-manage/tournament-manage.component";
|
||||||
import {MatchSheetsComponent} from "./components/match-sheets/match-sheets.component";
|
import {MatchSheetsComponent} from "./components/match-sheets/match-sheets.component";
|
||||||
import {RoundOverviewComponent} from "./components/round-overview/round-overview.component";
|
import {RoundOverviewComponent} from "./components/round-overview/round-overview.component";
|
||||||
import {AuthGuard} from "./authentication/authguard";
|
|
||||||
import {LoginComponent} from "./components/login/login.component";
|
import {LoginComponent} from "./components/login/login.component";
|
||||||
|
import {TournamentPlayersComponent} from "./components/tournament-players/tournament-players.component";
|
||||||
|
import {AuthGuard} from "./auth/auth-guard.service";
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: '', component: TournamentListComponent, canActivate: [AuthGuard], title: 'Toernooien' },
|
{ path: '', component: TournamentListComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Toernooien' }},
|
||||||
{ path: 'tournaments', component: TournamentListComponent, canActivate: [AuthGuard], title: 'Toernooien' },
|
{ path: 'tournaments', component: TournamentListComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Toernooien' }},
|
||||||
{ path: 'tournaments/add', component: TournamentEditComponent, canActivate: [AuthGuard], title: 'Nieuw Toernooi' },
|
{ path: 'tournaments/add', component: TournamentEditComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Nieuw Toernooi' }},
|
||||||
{ path: 'tournaments/:id/edit', component: TournamentEditComponent, canActivate: [AuthGuard], title: 'Bewerk Toernooi' },
|
{ path: 'tournaments/:id/edit', component: TournamentEditComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Bewerk Toernooi' }},
|
||||||
{ path: 'tournaments/:id/registrations', component: TournamentRegistrationsComponent, canActivate: [AuthGuard], title: 'Inschrijvingen' },
|
{ path: 'tournaments/:id/registrations', component: TournamentPlayersComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Inschrijvingen' }},
|
||||||
{ path: 'tournaments/:id/validate', component: TournamentValidateComponent, canActivate: [AuthGuard], title: 'Toernooi' },
|
{ path: 'tournaments/:id/draw', component: TournamentDrawComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Toernooi loten' }},
|
||||||
{ path: 'tournaments/:id/divide', component: TournamentDivideComponent, canActivate: [AuthGuard], title: 'Toernooi valideren' },
|
{ path: 'tournaments/:id/manage', component: TournamentManageComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER' }},
|
||||||
{ path: 'tournaments/:id/draw', component: TournamentDrawComponent, canActivate: [AuthGuard], title: 'Toernooi loten' },
|
{ path: 'tournaments/:id/manage/:tab', component: TournamentManageComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Toernooien' }},
|
||||||
{ path: 'tournaments/:id/manage', component: TournamentManageComponent, canActivate: [AuthGuard] },
|
{ path: 'players', component: PlayerListComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Spelers' }},
|
||||||
{ path: 'tournaments/:id/manage/:tab', component: TournamentManageComponent, canActivate: [AuthGuard], title: 'Toernooien' },
|
{ path: 'players/add', component: PlayerEditComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Nieuwe Speler' }},
|
||||||
{ path: 'players', component: PlayerListComponent, canActivate: [AuthGuard], title: 'Spelers' },
|
{ path: 'players/:id/edit', component: PlayerEditComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Bewerk Speler' }},
|
||||||
{ path: 'players/add', component: PlayerEditComponent, canActivate: [AuthGuard], title: 'Nieuwe Speler' },
|
{ path: 'players/:id/registrations', component: PlayerRegistrationsComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER' }},
|
||||||
{ path: 'players/edit/:id', component: PlayerEditComponent, canActivate: [AuthGuard], title: 'Bewerk Speler' },
|
{ path: 'tournaments/:id/rounds/:roundId/matchsheets', component: MatchSheetsComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER' }},
|
||||||
{ path: 'players/:id/registrations', component: PlayerRegistrationsComponent, canActivate: [AuthGuard], title: 'Inschrijvingen' },
|
{ path: 'tournaments/:id/rounds/:roundId/overview', component: RoundOverviewComponent, canActivate: [AuthGuard], data: { role: 'ROLE_USER', header: 'Rondeoverzicht' }},
|
||||||
{ path: 'tournaments/:id/rounds/:roundId/matchsheets', component: MatchSheetsComponent, canActivate: [AuthGuard], title: 'Wedstrijdbriefjes' },
|
{ path: 'login', component: LoginComponent, data: { header: 'Inloggen'}}
|
||||||
{ path: 'tournaments/:id/rounds/:roundId/overview', component: RoundOverviewComponent, canActivate: [AuthGuard], title: 'Rondeoverzicht' },
|
|
||||||
{ path: 'auth/login', component: LoginComponent }
|
|
||||||
];
|
];
|
||||||
|
|||||||
22
src/app/auth/auth-guard.service.ts
Normal file
22
src/app/auth/auth-guard.service.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from '@angular/router';
|
||||||
|
import {AuthService} from './auth.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class AuthGuard implements CanActivate {
|
||||||
|
|
||||||
|
constructor(private authService: AuthService, private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
||||||
|
if (this.authService.isLoggedIn() && this.authService.isUserInRole(next.routeConfig?.data?.['role'])) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// this.router.navigateByUrl("/login");
|
||||||
|
this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/app/auth/auth.interceptor.ts
Normal file
35
src/app/auth/auth.interceptor.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
|
||||||
|
import {catchError, Observable, throwError} from 'rxjs';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthInterceptor implements HttpInterceptor {
|
||||||
|
|
||||||
|
constructor(private router: Router) {
|
||||||
|
}
|
||||||
|
|
||||||
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
let token = localStorage.getItem("app.token");
|
||||||
|
if (token) {
|
||||||
|
request = request.clone({
|
||||||
|
setHeaders: {
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return next.handle(request).pipe(
|
||||||
|
catchError((error: HttpErrorResponse) => this.handleErrorRes(error))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleErrorRes(error: HttpErrorResponse): Observable<never> {
|
||||||
|
if (error.status === 401) {
|
||||||
|
this.router.navigateByUrl("/login", {replaceUrl: true});
|
||||||
|
}
|
||||||
|
return throwError(() => error);
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/app/auth/auth.service.ts
Normal file
85
src/app/auth/auth.service.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import {Inject, Injectable, PLATFORM_ID} from '@angular/core';
|
||||||
|
import {HttpClient} from '@angular/common/http';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
import {environment} from "../../environments/environment";
|
||||||
|
import {isPlatformBrowser} from "@angular/common";
|
||||||
|
import {jwtDecode, JwtPayload} from "jwt-decode";
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class AuthService {
|
||||||
|
|
||||||
|
private readonly authUrl: string
|
||||||
|
|
||||||
|
constructor(private http: HttpClient,
|
||||||
|
@Inject(PLATFORM_ID) private platformId: Object) {
|
||||||
|
this.authUrl = `${environment.backendUrl}/api/auth`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isBrowser(): boolean {
|
||||||
|
return isPlatformBrowser(this.platformId);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoggedIn(): boolean {
|
||||||
|
if (!this.isBrowser) return false;
|
||||||
|
return localStorage.getItem("app.token") != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
login(username: string, password: string): Observable<string> {
|
||||||
|
if (!this.isBrowser) {
|
||||||
|
throw new Error('Login can only be performed in browser');
|
||||||
|
}
|
||||||
|
|
||||||
|
const credentials = btoa(`${username}:${password}`);
|
||||||
|
|
||||||
|
const httpOptions = {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Basic ${credentials}`,
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
responseType: 'text' as 'text',
|
||||||
|
};
|
||||||
|
return this.http.post(this.authUrl, null, httpOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
logout() {
|
||||||
|
if (!this.isBrowser) return;
|
||||||
|
localStorage.removeItem("app.token");
|
||||||
|
localStorage.removeItem("app.roles");
|
||||||
|
}
|
||||||
|
|
||||||
|
isUserInRole(roleFromRoute: string) {
|
||||||
|
if (!this.isBrowser) return false;
|
||||||
|
|
||||||
|
const roles = localStorage.getItem("app.roles");
|
||||||
|
|
||||||
|
if (roles!.includes(",")) {
|
||||||
|
if (roles === roleFromRoute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const roleArray = roles!.split(",");
|
||||||
|
for (let role of roleArray) {
|
||||||
|
if (role === roleFromRoute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUsername(): string | null {
|
||||||
|
if (!this.isBrowser) return null;
|
||||||
|
|
||||||
|
const token = localStorage.getItem("app.token");
|
||||||
|
if (!token) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decodedToken = jwtDecode<JwtPayload>(token);
|
||||||
|
return decodedToken.sub || null; // 'sub' is the standard JWT claim for subject/username
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import {environment} from "../../environments/environment";
|
|
||||||
import {LoginCredentials} from "./loginCredentials";
|
|
||||||
import {TokenModel} from "./tokenModel";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class AuthenticationService {
|
|
||||||
|
|
||||||
private readonly authUrl: string
|
|
||||||
|
|
||||||
constructor(private http: HttpClient) {
|
|
||||||
this.authUrl = `${environment.backendUrl}`
|
|
||||||
}
|
|
||||||
|
|
||||||
public login(loginCredentials: LoginCredentials) {
|
|
||||||
return this.http.post<any>(`${this.authUrl}/authenticate`, loginCredentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
public logout(tokenModel: TokenModel) {
|
|
||||||
return this.http.post<string>(
|
|
||||||
`${this.authUrl}/logout`,
|
|
||||||
tokenModel
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public logoutEverywhere() {
|
|
||||||
return this.http.post<string>(
|
|
||||||
`${this.authUrl}/logout-everywhere`,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import {Injectable} from "@angular/core";
|
|
||||||
import {ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot} from "@angular/router";
|
|
||||||
import {UserService} from "./user.service";
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class AuthGuard implements CanActivate {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private router: Router,
|
|
||||||
private userService: UserService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
|
||||||
if (this.userService.getUser()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.router.navigate(['/auth/login'], {
|
|
||||||
queryParams: { returnUrl: state.url },
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import {Injectable} from "@angular/core";
|
|
||||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
|
|
||||||
import {catchError, Observable, throwError} from "rxjs";
|
|
||||||
import {UserService} from "./user.service";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class ErrorInterceptor implements HttpInterceptor {
|
|
||||||
|
|
||||||
constructor(private userService: UserService) { }
|
|
||||||
|
|
||||||
intercept(
|
|
||||||
request: HttpRequest<any>,
|
|
||||||
next: HttpHandler
|
|
||||||
): Observable<HttpEvent<any>> {
|
|
||||||
|
|
||||||
return next.handle(request).pipe(catchError(error=>{
|
|
||||||
if((error.status == 401 || error.status == 403) && !this.isLoginPage(request)){
|
|
||||||
this.userService.removeUser();
|
|
||||||
}
|
|
||||||
// const errMsg = error.error.message || error.statusText;
|
|
||||||
return throwError(() => error);
|
|
||||||
// return throwError(()=> errMsg);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private isLoginPage(request: HttpRequest<any>){
|
|
||||||
return request.url.includes("/authenticate")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class IpService {
|
|
||||||
|
|
||||||
constructor(private http: HttpClient) { }
|
|
||||||
|
|
||||||
public getIPAddress() {
|
|
||||||
return this.http.get("http://api.ipify.org/?format=json");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export class LoginCredentials {
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
// ipAddress: string;
|
|
||||||
// recaptcha: string;
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import {Injectable} from "@angular/core";
|
|
||||||
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from "@angular/common/http";
|
|
||||||
import {Observable} from "rxjs";
|
|
||||||
import {UserService} from "./user.service";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class TokenInterceptor implements HttpInterceptor {
|
|
||||||
|
|
||||||
constructor(private userService: UserService) { }
|
|
||||||
|
|
||||||
intercept(
|
|
||||||
request: HttpRequest<any>,
|
|
||||||
next: HttpHandler
|
|
||||||
): Observable<HttpEvent<any>> {
|
|
||||||
if (this.userService.isLoggedIn()) {
|
|
||||||
let newRequest = request.clone({
|
|
||||||
setHeaders: {
|
|
||||||
Authorization: `Bearer ${this.userService.getUser()?.accessToken}`,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return next.handle(newRequest);
|
|
||||||
}
|
|
||||||
return next.handle(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export class TokenModel {
|
|
||||||
constructor(token: string, refreshToken: string, ipAddress: string) {
|
|
||||||
this.token = token;
|
|
||||||
this.refreshToken = refreshToken;
|
|
||||||
this.ipAddress = ipAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
token: string;
|
|
||||||
refreshToken: string;
|
|
||||||
ipAddress: string;
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import {EventEmitter, Injectable, Output} from "@angular/core";
|
|
||||||
import {SsrCookieService} from "ngx-cookie-service-ssr";
|
|
||||||
import {Router} from "@angular/router";
|
|
||||||
import {User} from "./user";
|
|
||||||
import {BehaviorSubject} from "rxjs";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class UserService {
|
|
||||||
user?: User;
|
|
||||||
|
|
||||||
private userEmitter = new BehaviorSubject('');
|
|
||||||
currentUser = this.userEmitter.asObservable();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private cookieService: SsrCookieService,
|
|
||||||
private router: Router,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public getUser(): User | undefined {
|
|
||||||
const user = this.cookieService.get('swissuser');
|
|
||||||
|
|
||||||
if (user) {
|
|
||||||
this.user = JSON.parse(user);
|
|
||||||
this.userEmitter.next(JSON.parse(user).username);
|
|
||||||
} else {
|
|
||||||
this.user = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isLoggedIn(): boolean {
|
|
||||||
const user = this.cookieService.get('swissuser');
|
|
||||||
|
|
||||||
return !(user == undefined || false || user == "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public setUser(user: User) {
|
|
||||||
this.cookieService.set('swissuser', JSON.stringify(user));
|
|
||||||
|
|
||||||
this.user = user;
|
|
||||||
|
|
||||||
this.userEmitter.next(this.user.username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeUser() {
|
|
||||||
this.cookieService.delete('swissuser');
|
|
||||||
this.user = undefined;
|
|
||||||
this.router.navigate(['/tournaments']);
|
|
||||||
this.userEmitter.next('');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
export class User {
|
|
||||||
accessToken: string;
|
|
||||||
username: string;
|
|
||||||
// passwordHash: string;
|
|
||||||
// email: string;
|
|
||||||
// token: string;
|
|
||||||
// refreshToken: string;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<mat-dialog-content>
|
||||||
|
<h3>Kies een teller:</h3>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Teller</mat-label>
|
||||||
|
<mat-select [(ngModel)]="counter">
|
||||||
|
<mat-option>Geen</mat-option>
|
||||||
|
@for (player of data.availableCounters; track player.playerId) {
|
||||||
|
<mat-option [value]="player">
|
||||||
|
{{ player.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
@if (counter) {
|
||||||
|
<button mat-button [mat-dialog-close]="{counter: counter}">Opslaan</button>
|
||||||
|
}
|
||||||
|
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: all !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import {Component, inject, Inject} from '@angular/core';
|
||||||
|
import {
|
||||||
|
MAT_DIALOG_DATA,
|
||||||
|
MatDialogActions,
|
||||||
|
MatDialogClose,
|
||||||
|
MatDialogContent,
|
||||||
|
MatDialogRef
|
||||||
|
} from "@angular/material/dialog";
|
||||||
|
import {Match} from "../../model/match";
|
||||||
|
import {MatButton} from "@angular/material/button";
|
||||||
|
import {TournamentPlayer} from "../../model/tournamentPlayer";
|
||||||
|
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
|
import {MatOption, MatSelect} from "@angular/material/select";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-counter-selection',
|
||||||
|
imports: [
|
||||||
|
MatDialogContent,
|
||||||
|
MatButton,
|
||||||
|
MatDialogClose,
|
||||||
|
MatDialogActions,
|
||||||
|
MatFormField,
|
||||||
|
MatLabel,
|
||||||
|
MatOption,
|
||||||
|
MatSelect,
|
||||||
|
FormsModule,
|
||||||
|
|
||||||
|
],
|
||||||
|
templateUrl: './counter-selection.component.html',
|
||||||
|
standalone: true,
|
||||||
|
styleUrl: './counter-selection.component.scss'
|
||||||
|
})
|
||||||
|
export class CounterSelectionComponent {
|
||||||
|
|
||||||
|
counter: TournamentPlayer;
|
||||||
|
|
||||||
|
readonly dialogRef = inject(MatDialogRef<CounterSelectionComponent>);
|
||||||
|
|
||||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: {
|
||||||
|
match: Match,
|
||||||
|
availableCounters: TournamentPlayer[]
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
onAnnulerenClick() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,12 +1,32 @@
|
|||||||
<h2 mat-dialog-title>Kies een baan:</h2>
|
|
||||||
<mat-dialog-content>
|
<mat-dialog-content>
|
||||||
<button type="button" class="btn {{ data.availableCourts.indexOf(i + 1) < 0 ? 'btn-secondary' : 'btn-primary' }} btn-lg m-3"
|
<h3>Kies een baan:</h3>
|
||||||
*ngFor="let item of [].constructor(data.totalCourts); let i = index"
|
@for (item of [].constructor(data.totalCourts); track $index) {
|
||||||
[disabled]="data.availableCourts.indexOf(i + 1) < 0" [mat-dialog-close]="i + 1">
|
<button type="button"
|
||||||
{{ i + 1 }}
|
class="btn {{ this.court == ($index + 1) ? 'btn-primary' : (data.availableCourts.indexOf($index + 1) < 0 ? 'btn-outline-secondary' : 'btn-outline-primary') }} btn-lg m-3"
|
||||||
|
[disabled]="data.availableCourts.indexOf($index + 1) < 0"
|
||||||
|
(click)="selectCourt($index + 1)">
|
||||||
|
{{ $index + 1 }}
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
<br>
|
<br>
|
||||||
|
<h3>Kies een teller:</h3>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Teller</mat-label>
|
||||||
|
<mat-select [(ngModel)]="counter">
|
||||||
|
<mat-option>Geen</mat-option>
|
||||||
|
@for (player of data.availableCounters; track player.playerId) {
|
||||||
|
<mat-option [value]="player">
|
||||||
|
{{ player.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
|
@if (court && counter) {
|
||||||
|
<button mat-button [mat-dialog-close]="{court: court, counter: counter}">Wedstrijd starten</button>
|
||||||
|
}
|
||||||
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
button:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
pointer-events: all !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,39 +3,53 @@ import {
|
|||||||
MAT_DIALOG_DATA,
|
MAT_DIALOG_DATA,
|
||||||
MatDialogActions,
|
MatDialogActions,
|
||||||
MatDialogClose,
|
MatDialogClose,
|
||||||
MatDialogContent, MatDialogRef,
|
MatDialogContent,
|
||||||
MatDialogTitle
|
MatDialogRef
|
||||||
} from "@angular/material/dialog";
|
} from "@angular/material/dialog";
|
||||||
import {Match} from "../../model/match";
|
import {Match} from "../../model/match";
|
||||||
import {NgForOf} from "@angular/common";
|
|
||||||
import {MatButton} from "@angular/material/button";
|
import {MatButton} from "@angular/material/button";
|
||||||
|
import {TournamentPlayer} from "../../model/tournamentPlayer";
|
||||||
|
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
|
import {MatOption, MatSelect} from "@angular/material/select";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-court-selection',
|
selector: 'app-court-selection',
|
||||||
imports: [
|
imports: [
|
||||||
MatDialogTitle,
|
|
||||||
MatDialogContent,
|
MatDialogContent,
|
||||||
NgForOf,
|
|
||||||
MatButton,
|
MatButton,
|
||||||
MatDialogClose,
|
MatDialogClose,
|
||||||
MatDialogActions
|
MatDialogActions,
|
||||||
|
MatFormField,
|
||||||
|
MatLabel,
|
||||||
|
MatOption,
|
||||||
|
MatSelect,
|
||||||
|
FormsModule,
|
||||||
|
|
||||||
],
|
],
|
||||||
templateUrl: './court-selection.component.html',
|
templateUrl: './court-selection.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './court-selection.component.scss'
|
styleUrl: './court-selection.component.scss'
|
||||||
})
|
})
|
||||||
export class CourtSelectionComponent {
|
export class CourtSelectionComponent {
|
||||||
|
|
||||||
court: number;
|
court: number;
|
||||||
|
counter: TournamentPlayer;
|
||||||
|
|
||||||
readonly dialogRef = inject(MatDialogRef<CourtSelectionComponent>);
|
readonly dialogRef = inject(MatDialogRef<CourtSelectionComponent>);
|
||||||
|
|
||||||
constructor(@Inject(MAT_DIALOG_DATA) public data: {
|
constructor(@Inject(MAT_DIALOG_DATA) public data: {
|
||||||
match: Match,
|
match: Match,
|
||||||
availableCourts: number[],
|
availableCourts: number[],
|
||||||
totalCourts: number
|
totalCourts: number,
|
||||||
|
availableCounters: TournamentPlayer[]
|
||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
onAnnulerenClick() {
|
onAnnulerenClick() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
selectCourt(selectedCourt: number) {
|
||||||
|
this.court = selectedCourt;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
<mat-card>
|
<mat-card>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<form class="form-horizontal" [formGroup]="form">
|
<form class="form-horizontal" (ngSubmit)="login()">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Gebruikersnaam</mat-label>
|
<mat-label>Gebruikersnaam</mat-label>
|
||||||
<input matInput placeholder="Gebruikersnaam" formControlName="username" required>
|
<input matInput name="username" placeholder="Gebruikersnaam" required [(ngModel)]="username">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
<mat-label>Wachtwoord</mat-label>
|
<mat-label>Wachtwoord</mat-label>
|
||||||
<input matInput placeholder="Wachtwoord" type="password" formControlName="password" required>
|
<input matInput name="password" placeholder="Wachtwoord" type="password" required [(ngModel)]="password">
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<button class="w-100 mt-2" mat-button mat-flat-button color="primary"
|
<button class="w-100 mt-2" mat-button mat-flat-button color="primary"
|
||||||
(click)="login()" [disabled]="form.invalid">
|
(click)="login()">
|
||||||
Inloggen
|
Inloggen
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
<p>{{message}}</p>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
import {FormBuilder, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
import {FormGroup, FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {MatCard, MatCardContent, MatCardHeader, MatCardTitle} from "@angular/material/card";
|
import {MatCard, MatCardContent} from "@angular/material/card";
|
||||||
import {MatError, MatFormField, MatLabel} from "@angular/material/form-field";
|
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
import {MatButton} from "@angular/material/button";
|
import {MatButton} from "@angular/material/button";
|
||||||
import {MatInput} from "@angular/material/input";
|
import {MatInput} from "@angular/material/input";
|
||||||
import {NgIf} from "@angular/common";
|
import {jwtDecode, JwtPayload} from "jwt-decode";
|
||||||
import {AuthenticationService} from "../../authentication/authentication.service";
|
import {AuthService} from "../../auth/auth.service";
|
||||||
import {UserService} from "../../authentication/user.service";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
import {LoginCredentials} from "../../authentication/loginCredentials";
|
|
||||||
import {User} from "../../authentication/user";
|
|
||||||
import {TitleService} from "../../service/title.service";
|
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -24,7 +21,9 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatInput,
|
MatInput,
|
||||||
MatLabel,
|
MatLabel,
|
||||||
MatCard,
|
MatCard,
|
||||||
|
FormsModule,
|
||||||
],
|
],
|
||||||
|
standalone: true,
|
||||||
styleUrls: ['./login.component.scss']
|
styleUrls: ['./login.component.scss']
|
||||||
})
|
})
|
||||||
export class LoginComponent implements OnInit {
|
export class LoginComponent implements OnInit {
|
||||||
@@ -32,50 +31,42 @@ export class LoginComponent implements OnInit {
|
|||||||
private returnUrl: string;
|
private returnUrl: string;
|
||||||
private ipAddress: string;
|
private ipAddress: string;
|
||||||
|
|
||||||
constructor(
|
username: string = "";
|
||||||
|
password: string = "";
|
||||||
|
message: string = "";
|
||||||
|
|
||||||
|
constructor(private authService: AuthService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private fb: FormBuilder,
|
|
||||||
private authenticationService: AuthenticationService,
|
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private userPersistenceService: UserService,
|
private snackBar: MatSnackBar) {
|
||||||
private titleService: TitleService,
|
|
||||||
) {
|
|
||||||
this.initializeForm();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeForm() {
|
|
||||||
this.form = this.fb.group({
|
|
||||||
username: ['', [Validators.required]],
|
|
||||||
password: ['', [Validators.required]],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
|
||||||
this.titleService.setTitle("Inloggen");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
login() {
|
public login(): void {
|
||||||
if (this.form.invalid) {
|
localStorage.removeItem("app.token");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let loginCredentials = new LoginCredentials();
|
this.authService.login(this.username, this.password)
|
||||||
loginCredentials = {
|
.subscribe({
|
||||||
...loginCredentials,
|
next: (token) => {
|
||||||
...this.form.value,
|
localStorage.setItem("app.token", token);
|
||||||
};
|
|
||||||
|
|
||||||
this.authenticationService
|
const decodedToken = jwtDecode<JwtPayload>(token);
|
||||||
.login(loginCredentials)
|
// @ts-ignore
|
||||||
.subscribe(
|
localStorage.setItem("app.roles", decodedToken.scope);
|
||||||
{
|
|
||||||
next: (user: User) => {
|
// this.router.navigateByUrl("/persons");
|
||||||
this.userPersistenceService.setUser(user);
|
|
||||||
this.router.navigate([this.returnUrl]);
|
// this.router.navigate([this.returnUrl]);
|
||||||
|
this.router.navigate([this.route.snapshot.queryParams['returnUrl'] || '/']);
|
||||||
|
},
|
||||||
|
error: (error) => {
|
||||||
|
this.snackBar.open(`Login failed: ${error.status}`, "OK")
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,76 +7,76 @@
|
|||||||
<b>{{ data.group.name }} {{ data.round.name }}</b>
|
<b>{{ data.group.name }} {{ data.round.name }}</b>
|
||||||
</div>
|
</div>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
<mat-grid-tile>
|
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(1, 1)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(1, 2)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(1, 3)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
|
|
||||||
|
@for (gameNum of [1, 2, 3]; track gameNum) {
|
||||||
|
<mat-grid-tile>
|
||||||
|
<button type="button" class="btn btn-primary btn-lg" (click)="set21(1, gameNum)">21</button>
|
||||||
|
</mat-grid-tile>
|
||||||
|
}
|
||||||
<mat-grid-tile colspan="2">
|
<mat-grid-tile colspan="2">
|
||||||
<div class="w-100" [ngClass]="{'winner': validateResult() == 1}">
|
<div class="w-100" [ngClass]="{'winner': isValidResult == 1}">
|
||||||
{{ data.match.team1 | teamText }}
|
<app-team-display
|
||||||
|
[team]="data.match.team1"
|
||||||
|
[event]="data.event"
|
||||||
|
[tournament]="data.tournament"
|
||||||
|
[inline]="false">
|
||||||
|
</app-team-display>
|
||||||
</div>
|
</div>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
|
|
||||||
|
@for (game of result.games; track $index; let i = $index) {
|
||||||
<mat-grid-tile>
|
<mat-grid-tile>
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
<mat-form-field appearance="outline">
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[0].score1">
|
<input matInput
|
||||||
</mat-form-field>
|
type="number"
|
||||||
</mat-grid-tile>
|
min="0"
|
||||||
<mat-grid-tile>
|
max="30"
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
[(ngModel)]="game.score1"
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[1].score1">
|
(blur)="complementScores()"
|
||||||
</mat-form-field>
|
(ngModelChange)="validateResult()"
|
||||||
</mat-grid-tile>
|
[tabindex]="i * 2 + 1">
|
||||||
<mat-grid-tile>
|
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[2].score1">
|
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
|
}
|
||||||
|
|
||||||
<mat-grid-tile colspan="2">
|
<mat-grid-tile colspan="2">
|
||||||
<div class="w-100" [ngClass]="{'winner': validateResult() == -1}">
|
<div class="w-100" [ngClass]="{'winner': isValidResult == -1}">
|
||||||
{{ data.match.team2 | teamText }}
|
<app-team-display
|
||||||
|
[team]="data.match.team2"
|
||||||
|
[event]="data.event"
|
||||||
|
[tournament]="data.tournament"
|
||||||
|
[inline]="false">
|
||||||
|
</app-team-display>
|
||||||
</div>
|
</div>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
<mat-grid-tile>
|
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[0].score2">
|
|
||||||
</mat-form-field>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[1].score2">
|
|
||||||
</mat-form-field>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<mat-form-field appearance="outline" (change)="validateResult()">
|
|
||||||
<input matInput type="number" min="0" max="30" [(ngModel)]="result.games[2].score2">
|
|
||||||
</mat-form-field>
|
|
||||||
</mat-grid-tile>
|
|
||||||
|
|
||||||
|
@for (game of result.games; track $index; let i = $index) {
|
||||||
<mat-grid-tile>
|
<mat-grid-tile>
|
||||||
|
<mat-form-field appearance="outline">
|
||||||
|
<input matInput
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="30"
|
||||||
|
[(ngModel)]="game.score2"
|
||||||
|
(blur)="complementScores()"
|
||||||
|
(ngModelChange)="validateResult()"
|
||||||
|
[tabindex]="i * 2 + 2">
|
||||||
|
</mat-form-field>
|
||||||
|
</mat-grid-tile>
|
||||||
|
}
|
||||||
|
<mat-grid-tile></mat-grid-tile>
|
||||||
|
<mat-grid-tile></mat-grid-tile>
|
||||||
|
|
||||||
</mat-grid-tile>
|
@for (gameNum of [1, 2, 3]; track gameNum) {
|
||||||
<mat-grid-tile>
|
<mat-grid-tile>
|
||||||
|
<button type="button" class="btn btn-primary btn-lg" (click)="set21(2, gameNum)">21</button>
|
||||||
</mat-grid-tile>
|
</mat-grid-tile>
|
||||||
<mat-grid-tile>
|
}
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(2, 1)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(2, 2)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
<mat-grid-tile>
|
|
||||||
<button type="button" class="btn btn-primary btn-lg" (click)="set21(2, 3)">21</button>
|
|
||||||
</mat-grid-tile>
|
|
||||||
</mat-grid-list>
|
</mat-grid-list>
|
||||||
</mat-dialog-content>
|
</mat-dialog-content>
|
||||||
|
|
||||||
<mat-dialog-actions>
|
<mat-dialog-actions>
|
||||||
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
||||||
<button mat-button [disabled]="validateResult() == 0" [mat-dialog-close]="result">Opslaan</button>
|
<button mat-button [disabled]="isValidResult == 0" [mat-dialog-close]="result">Opslaan</button>
|
||||||
</mat-dialog-actions>
|
</mat-dialog-actions>
|
||||||
|
|||||||
@@ -3,22 +3,25 @@ import {
|
|||||||
MAT_DIALOG_DATA,
|
MAT_DIALOG_DATA,
|
||||||
MatDialogActions,
|
MatDialogActions,
|
||||||
MatDialogClose,
|
MatDialogClose,
|
||||||
MatDialogContent, MatDialogRef,
|
MatDialogContent,
|
||||||
|
MatDialogRef,
|
||||||
MatDialogTitle
|
MatDialogTitle
|
||||||
} from "@angular/material/dialog";
|
} from "@angular/material/dialog";
|
||||||
import {MatButton, MatIconButton} from "@angular/material/button";
|
import {MatButton} from "@angular/material/button";
|
||||||
import {DatePipe, NgClass, NgForOf} from "@angular/common";
|
import {NgClass} from "@angular/common";
|
||||||
import {MatIcon} from "@angular/material/icon";
|
|
||||||
import {TeamPipe} from "../../pipes/team-pipe";
|
import {TeamPipe} from "../../pipes/team-pipe";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {Match} from "../../model/match";
|
import {Match} from "../../model/match";
|
||||||
import {MatFormField, MatInput} from "@angular/material/input";
|
import {MatFormField, MatInput} from "@angular/material/input";
|
||||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {Result} from "../../model/result";
|
import {Result} from "../../model/result";
|
||||||
import {MatGridList, MatGridTile, MatGridTileText} from "@angular/material/grid-list";
|
import {MatGridList, MatGridTile} from "@angular/material/grid-list";
|
||||||
import {Round} from "../../model/round";
|
import {Round} from "../../model/round";
|
||||||
import {Group} from "../../model/group";
|
import {Group} from "../../model/group";
|
||||||
import {Game} from "../../model/game";
|
import {Game} from "../../model/game";
|
||||||
|
import {TeamDisplayComponent} from "../team-display/team-display.component";
|
||||||
|
import {Event} from "../../model/event";
|
||||||
|
import {Tournament} from "../../model/tournament";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-match-result',
|
selector: 'app-match-result',
|
||||||
@@ -28,27 +31,29 @@ import {Game} from "../../model/game";
|
|||||||
MatButton,
|
MatButton,
|
||||||
MatDialogClose,
|
MatDialogClose,
|
||||||
MatDialogTitle,
|
MatDialogTitle,
|
||||||
TeamPipe,
|
|
||||||
MatInput,
|
MatInput,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatGridList,
|
MatGridList,
|
||||||
MatGridTile,
|
MatGridTile,
|
||||||
NgClass
|
NgClass,
|
||||||
|
TeamDisplayComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
FullNamePipe,
|
FullNamePipe,
|
||||||
TeamPipe
|
TeamPipe
|
||||||
],
|
],
|
||||||
templateUrl: './match-result.component.html',
|
templateUrl: './match-result.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './match-result.component.scss'
|
styleUrl: './match-result.component.scss'
|
||||||
})
|
})
|
||||||
export class MatchResultComponent {
|
export class MatchResultComponent {
|
||||||
|
|
||||||
result: Result = new Result();
|
result: Result = new Result();
|
||||||
|
isValidResult: number = 0;
|
||||||
|
|
||||||
constructor(@Inject(MAT_DIALOG_DATA) public data: {match: Match, group: Group, round: Round}) {
|
constructor(@Inject(MAT_DIALOG_DATA) public data: {match: Match, tournament: Tournament, event: Event, group: Group, round: Round}) {
|
||||||
this.result.matchId = this.data.match.id;
|
this.result.matchId = this.data.match.id;
|
||||||
|
|
||||||
if (data.match.games.length == 0) {
|
if (data.match.games.length == 0) {
|
||||||
@@ -63,6 +68,8 @@ export class MatchResultComponent {
|
|||||||
this.result.games.push(new Game());
|
this.result.games.push(new Game());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.validateResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly dialogRef = inject(MatDialogRef<MatchResultComponent>);
|
readonly dialogRef = inject(MatDialogRef<MatchResultComponent>);
|
||||||
@@ -75,13 +82,18 @@ export class MatchResultComponent {
|
|||||||
this.result.games[game - 1].score2 = 21;
|
this.result.games[game - 1].score2 = 21;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.validateResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
onAnnulerenClick() {
|
onAnnulerenClick() {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
validateResult(): number {
|
complementScores() {
|
||||||
|
// console.log("in complementScores");
|
||||||
|
}
|
||||||
|
|
||||||
|
validateResult(): void {
|
||||||
let valid : boolean = true;
|
let valid : boolean = true;
|
||||||
valid &&= this.gameValid(this.result.games[0].score1, this.result.games[0].score2);
|
valid &&= this.gameValid(this.result.games[0].score1, this.result.games[0].score2);
|
||||||
valid &&= this.gameValid(this.result.games[1].score1, this.result.games[1].score2);
|
valid &&= this.gameValid(this.result.games[1].score1, this.result.games[1].score2);
|
||||||
@@ -90,7 +102,7 @@ export class MatchResultComponent {
|
|||||||
valid &&= this.gameValid(this.result.games[2].score1, this.result.games[2].score2);
|
valid &&= this.gameValid(this.result.games[2].score1, this.result.games[2].score2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid ? this.matchResult(this.result) : 0;
|
this.isValidResult = valid ? this.matchResult(this.result) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
gameValid(score1: number, score2: number): boolean {
|
gameValid(score1: number, score2: number): boolean {
|
||||||
@@ -113,29 +125,31 @@ export class MatchResultComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
matchResult(result: Result): number {
|
matchResult(result: Result): number {
|
||||||
let gameBalance = 0;
|
let team1Wins = 0;
|
||||||
if (result.games[0].score1 < result.games[0].score2) {
|
let team2Wins = 0;
|
||||||
gameBalance--;
|
|
||||||
} else {
|
// Count wins for games 1 and 2
|
||||||
gameBalance++;
|
if (result.games[0].score1 > result.games[0].score2) team1Wins++;
|
||||||
|
else team2Wins++;
|
||||||
|
|
||||||
|
if (result.games[1].score1 > result.games[1].score2) team1Wins++;
|
||||||
|
else team2Wins++;
|
||||||
|
|
||||||
|
// If match is already decided (2-0), game 3 shouldn't have scores
|
||||||
|
if (Math.max(team1Wins, team2Wins) == 2) {
|
||||||
|
if (result.games[2].score1 != undefined || result.games[2].score2 != undefined) {
|
||||||
|
return 0; // Invalid
|
||||||
}
|
}
|
||||||
if (result.games[1].score1 < result.games[1].score2) {
|
return team1Wins > team2Wins ? 1 : -1;
|
||||||
gameBalance--;
|
|
||||||
} else {
|
|
||||||
gameBalance++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Math.abs(gameBalance) == 2 && (result.games[2].score1 != undefined || result.games[2].score2 != undefined)) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Match is 1-1, check game 3
|
||||||
if (result.games[2].score1 != undefined && result.games[2].score2 != undefined) {
|
if (result.games[2].score1 != undefined && result.games[2].score2 != undefined) {
|
||||||
if (result.games[2].score1 < result.games[2].score2) {
|
if (result.games[2].score1 > result.games[2].score2) team1Wins++;
|
||||||
gameBalance--;
|
else team2Wins++;
|
||||||
} else {
|
return team1Wins > team2Wins ? 1 : -1;
|
||||||
gameBalance++;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return Math.sign(gameBalance);
|
return 0; // Incomplete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,28 @@
|
|||||||
@if (round) {
|
@if (round) {
|
||||||
<ng-container *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
<div class="nobreak">
|
<div class="nobreak">
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<h6>{{ tournament.name }}</h6>
|
|
||||||
<br>
|
|
||||||
<br>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<b>{{ match.team1 | teamText }}</b>
|
<h5>{{ tournament.name }}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 text-end">
|
||||||
|
{{ group.name }} {{ round.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
<div class="row align-middle">
|
||||||
|
<div class="col-6">
|
||||||
|
<b>
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team1"
|
||||||
|
[event]="this.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
@@ -28,7 +42,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<b>{{ match.team2 | teamText }}</b>
|
<b>
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team2"
|
||||||
|
[event]="this.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</b>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<mat-form-field appearance="outline">
|
<mat-form-field appearance="outline">
|
||||||
@@ -50,14 +71,10 @@
|
|||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<u>Graag de winnaar omcirkelen</u>
|
<u>Graag de winnaar omcirkelen</u>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 text-end">
|
|
||||||
{{ group.name }} {{ round.name }}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<br>
|
||||||
</ng-container>
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {MatCard, MatCardContent} from "@angular/material/card";
|
import {MatCard, MatCardContent} from "@angular/material/card";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
@@ -7,33 +7,35 @@ import {Round} from "../../model/round";
|
|||||||
import {TeamPipe} from "../../pipes/team-pipe";
|
import {TeamPipe} from "../../pipes/team-pipe";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {Group} from "../../model/group";
|
import {Group} from "../../model/group";
|
||||||
import {NgForOf} from "@angular/common";
|
|
||||||
import {MatFormField} from "@angular/material/form-field";
|
import {MatFormField} from "@angular/material/form-field";
|
||||||
import {MatInput} from "@angular/material/input";
|
import {MatInput} from "@angular/material/input";
|
||||||
import {ReactiveFormsModule} from "@angular/forms";
|
import {ReactiveFormsModule} from "@angular/forms";
|
||||||
import {TitleService} from "../../service/title.service";
|
import {HeaderService} from "../../service/header.service";
|
||||||
|
import {TeamDisplayComponent} from "../team-display/team-display.component";
|
||||||
|
import {Event} from "../../model/event";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-match-sheets',
|
selector: 'app-match-sheets',
|
||||||
imports: [
|
imports: [
|
||||||
MatCard,
|
MatCard,
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
TeamPipe,
|
|
||||||
NgForOf,
|
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatInput,
|
MatInput,
|
||||||
ReactiveFormsModule
|
ReactiveFormsModule,
|
||||||
|
TeamDisplayComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TeamPipe,
|
TeamPipe,
|
||||||
FullNamePipe
|
FullNamePipe
|
||||||
],
|
],
|
||||||
templateUrl: './match-sheets.component.html',
|
templateUrl: './match-sheets.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './match-sheets.component.scss'
|
styleUrl: './match-sheets.component.scss'
|
||||||
})
|
})
|
||||||
export class MatchSheetsComponent implements OnInit {
|
export class MatchSheetsComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
tournament: Tournament;
|
tournament: Tournament;
|
||||||
|
event: Event;
|
||||||
group: Group;
|
group: Group;
|
||||||
round: Round;
|
round: Round;
|
||||||
|
|
||||||
@@ -41,7 +43,7 @@ export class MatchSheetsComponent implements OnInit {
|
|||||||
private tournamentService: TournamentService,
|
private tournamentService: TournamentService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService
|
private headerService: HeaderService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,9 +57,10 @@ export class MatchSheetsComponent implements OnInit {
|
|||||||
for (let group of event.groups) {
|
for (let group of event.groups) {
|
||||||
for (let round of group.rounds) {
|
for (let round of group.rounds) {
|
||||||
if (round.id == roundId) {
|
if (round.id == roundId) {
|
||||||
|
this.event = event;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.round = round;
|
this.round = round;
|
||||||
this.titleService.setTitle(`Wedstrijdbriefjes ${this.group.name} ${this.round.name}`);
|
this.headerService.setTitle(`Wedstrijdbriefjes ${this.group.name} ${this.round.name}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,4 +69,8 @@ export class MatchSheetsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.headerService.clearTitle()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.has-substitute {
|
||||||
|
text-decoration-line: underline;
|
||||||
|
text-decoration-style: dotted;
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {Player} from '../../model/player';
|
||||||
|
import {Event} from '../../model/event';
|
||||||
|
import {Tournament} from '../../model/tournament';
|
||||||
|
import {FullNamePipe} from '../../pipes/fullname-pipe';
|
||||||
|
import {MatTooltip} from '@angular/material/tooltip';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-player-display',
|
||||||
|
standalone: true,
|
||||||
|
imports: [FullNamePipe, MatTooltip],
|
||||||
|
styleUrls: ['./player-display.component.scss'],
|
||||||
|
template: `
|
||||||
|
@let substitute = getSubstituteForEvent(player, event);
|
||||||
|
@if (exlicitSubstitute) {
|
||||||
|
@if (substitute) {
|
||||||
|
{{ substitute }} (valt in voor {{ player | fullName }})
|
||||||
|
} @else {
|
||||||
|
{{ player | fullName }}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
<span [class.has-substitute]="substitute"
|
||||||
|
[matTooltip]="substitute ? 'Valt in voor ' + (player | fullName) : ''"
|
||||||
|
matTooltipPosition="below">{{ substitute || (player | fullName) }}</span>
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class PlayerDisplayComponent {
|
||||||
|
@Input({ required: true }) player!: Player;
|
||||||
|
@Input({ required: true }) event!: Event;
|
||||||
|
@Input({ required: true }) tournament!: Tournament;
|
||||||
|
@Input({ required: false }) exlicitSubstitute: boolean = false;
|
||||||
|
|
||||||
|
getSubstituteForEvent(player: Player, event: Event): string | undefined {
|
||||||
|
const tournamentPlayer = this.tournament.tournamentPlayers.find(
|
||||||
|
tp => tp.playerId === player.id
|
||||||
|
);
|
||||||
|
if (!tournamentPlayer) return undefined;
|
||||||
|
|
||||||
|
const substitution = tournamentPlayer.substitutions.find(
|
||||||
|
s => s.event === event.type
|
||||||
|
);
|
||||||
|
if (!substitution) return undefined;
|
||||||
|
|
||||||
|
return this.tournament.tournamentPlayers.find(
|
||||||
|
p => p.id === substitution.substitute
|
||||||
|
)?.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,9 +60,11 @@
|
|||||||
<mat-form-field appearance="fill">
|
<mat-form-field appearance="fill">
|
||||||
<mat-label>Speelsterkte</mat-label>
|
<mat-label>Speelsterkte</mat-label>
|
||||||
<mat-select [(ngModel)]="player.strength" name="strength" required>
|
<mat-select [(ngModel)]="player.strength" name="strength" required>
|
||||||
<mat-option *ngFor="let strengthOption of Strength | keyvalue" [value]="strengthOption.key">
|
@for (strengthOption of Strength | keyvalue; track strengthOption) {
|
||||||
|
<mat-option [value]="strengthOption.key">
|
||||||
{{ strengthOption.value }}
|
{{ strengthOption.value }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
|
}
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,9 +9,8 @@ import {MatIcon} from "@angular/material/icon";
|
|||||||
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
||||||
import {MatCard, MatCardActions, MatCardContent} from "@angular/material/card";
|
import {MatCard, MatCardActions, MatCardContent} from "@angular/material/card";
|
||||||
import {MatOption, MatSelect} from "@angular/material/select";
|
import {MatOption, MatSelect} from "@angular/material/select";
|
||||||
import {KeyValuePipe, NgForOf} from "@angular/common";
|
import {KeyValuePipe} from "@angular/common";
|
||||||
import {MatAnchor, MatButton} from "@angular/material/button";
|
import {MatAnchor, MatButton} from "@angular/material/button";
|
||||||
import {TitleService} from "../../service/title.service";
|
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
import {NgxMaskDirective} from "ngx-mask";
|
import {NgxMaskDirective} from "ngx-mask";
|
||||||
|
|
||||||
@@ -33,25 +32,23 @@ import {NgxMaskDirective} from "ngx-mask";
|
|||||||
MatSelect,
|
MatSelect,
|
||||||
MatOption,
|
MatOption,
|
||||||
KeyValuePipe,
|
KeyValuePipe,
|
||||||
NgForOf,
|
|
||||||
MatButton,
|
MatButton,
|
||||||
MatAnchor,
|
MatAnchor,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
NgxMaskDirective
|
NgxMaskDirective
|
||||||
],
|
],
|
||||||
templateUrl: './player-edit.component.html',
|
templateUrl: './player-edit.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './player-edit.component.scss'
|
styleUrl: './player-edit.component.scss'
|
||||||
})
|
})
|
||||||
export class PlayerEditComponent implements OnInit {
|
export class PlayerEditComponent implements OnInit {
|
||||||
player: Player;
|
player: Player;
|
||||||
isEditMode: boolean = false;
|
isEditMode: boolean = false;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private playerService: PlayerService,
|
private playerService: PlayerService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService,
|
|
||||||
private _snackBar: MatSnackBar,
|
private _snackBar: MatSnackBar,
|
||||||
) {
|
) {
|
||||||
this.player = new Player();
|
this.player = new Player();
|
||||||
@@ -60,13 +57,11 @@ export class PlayerEditComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const id = this.route.snapshot.paramMap.get('id');
|
const id = this.route.snapshot.paramMap.get('id');
|
||||||
if (id) {
|
if (id) {
|
||||||
this.titleService.setTitle("Bewerk Speler");
|
|
||||||
this.isEditMode = true;
|
this.isEditMode = true;
|
||||||
this.playerService.getById(Number(id)).subscribe(data => {
|
this.playerService.getById(Number(id)).subscribe(data => {
|
||||||
this.player = data;
|
this.player = data;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.titleService.setTitle("Nieuwe Speler");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {MatAnchor} from "@angular/material/button";
|
// import {MatAnchor} from "@angular/material/button";
|
||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'player-link',
|
selector: 'player-link',
|
||||||
imports: [
|
imports: [
|
||||||
MatAnchor,
|
|
||||||
RouterLink
|
RouterLink
|
||||||
],
|
],
|
||||||
templateUrl: './player-link.component.html',
|
templateUrl: './player-link.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './player-link.component.scss'
|
styleUrl: './player-link.component.scss'
|
||||||
})
|
})
|
||||||
export class PlayerLinkComponent implements OnInit {
|
export class PlayerLinkComponent implements OnInit {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
<ng-container matColumnDef="action">
|
<ng-container matColumnDef="action">
|
||||||
<th mat-header-cell *matHeaderCellDef></th>
|
<th mat-header-cell *matHeaderCellDef></th>
|
||||||
<td mat-cell *matCellDef="let player">
|
<td mat-cell *matCellDef="let player">
|
||||||
<a mat-button [routerLink]="['/players/edit', player.id]">
|
<a mat-button [routerLink]="['/players', player.id, 'edit']">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
Bewerk
|
Bewerk
|
||||||
</a>
|
</a>
|
||||||
@@ -39,7 +39,8 @@
|
|||||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||||
</table>
|
</table>
|
||||||
<mat-paginator [pageSizeOptions]="[10, 20, 50]"
|
<mat-paginator [pageSizeOptions]="[10, 25, 100]"
|
||||||
|
[pageSize]="100"
|
||||||
showFirstLastButtons
|
showFirstLastButtons
|
||||||
aria-label="Select page of periodic elements">
|
aria-label="Select page of periodic elements">
|
||||||
</mat-paginator>
|
</mat-paginator>
|
||||||
|
|||||||
@@ -5,3 +5,7 @@ a {
|
|||||||
td, th {
|
td, th {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mat-mdc-row:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.075);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {MatAnchor} from "@angular/material/button";
|
|||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {MatCard, MatCardContent} from "@angular/material/card";
|
import {MatCard, MatCardContent} from "@angular/material/card";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {TitleService} from "../../service/title.service";
|
|
||||||
import {
|
import {
|
||||||
MatCell,
|
MatCell,
|
||||||
MatCellDef,
|
MatCellDef,
|
||||||
@@ -30,6 +29,7 @@ import {MatSort, MatSortHeader} from "@angular/material/sort";
|
|||||||
imports: [RouterLink, MatAnchor, MatIcon, MatCard, MatCardContent, FullNamePipe, MatTable, MatColumnDef, MatHeaderCell, MatHeaderCellDef, MatCell, MatCellDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef, MatFormField, MatInput, MatFormFieldModule, MatPaginator, MatSortHeader, MatSort],
|
imports: [RouterLink, MatAnchor, MatIcon, MatCard, MatCardContent, FullNamePipe, MatTable, MatColumnDef, MatHeaderCell, MatHeaderCellDef, MatCell, MatCellDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef, MatFormField, MatInput, MatFormFieldModule, MatPaginator, MatSortHeader, MatSort],
|
||||||
providers: [FullNamePipe],
|
providers: [FullNamePipe],
|
||||||
templateUrl: './player-list.component.html',
|
templateUrl: './player-list.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './player-list.component.scss'
|
styleUrl: './player-list.component.scss'
|
||||||
})
|
})
|
||||||
export class PlayerListComponent implements AfterViewInit {
|
export class PlayerListComponent implements AfterViewInit {
|
||||||
@@ -42,13 +42,11 @@ export class PlayerListComponent implements AfterViewInit {
|
|||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private titleService: TitleService,
|
|
||||||
private playerService: PlayerService,
|
private playerService: PlayerService,
|
||||||
private fullNamePipe: FullNamePipe) {
|
private fullNamePipe: FullNamePipe) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {
|
ngAfterViewInit() {
|
||||||
this.titleService.setTitle("Spelers");
|
|
||||||
this.playerService.getAll().subscribe(data => {
|
this.playerService.getAll().subscribe(data => {
|
||||||
this.players = data;
|
this.players = data;
|
||||||
this.dataSource = new MatTableDataSource(this.players);
|
this.dataSource = new MatTableDataSource(this.players);
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
@if (player && tournamentRegistrations && allPlayers) {
|
@if (player && tournamentRegistrations && allPlayers) {
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-card *ngFor="let tournamentRegistration of getTournamentRegistrations()" appearance="outlined" class="mb-3">
|
@for (tournamentRegistration of getTournamentRegistrations(); track tournamentRegistration.id) {
|
||||||
|
<mat-card appearance="outlined" class="mb-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h6>{{ tournamentRegistration.name }}</h6>
|
<h6>{{ tournamentRegistration.name }}</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<ng-container *ngFor="let eventRegistration of tournamentRegistration.events">
|
@for (eventRegistration of tournamentRegistration.events; track eventRegistration.id) {
|
||||||
<div class="row event-row">
|
<div class="row event-row">
|
||||||
<div class="col-md-2">
|
<div class="col-md-2">
|
||||||
<mat-checkbox [disabled]="!tournamentRegistration.editable" [(ngModel)]="eventRegistration.registered" (change)="updateModelWhenEventChecked(eventRegistration, $event)" name="registered">
|
<mat-checkbox [disabled]="!tournamentRegistration.editable" [(ngModel)]="eventRegistration.registered" (change)="updateModelWhenEventChecked(eventRegistration, $event)" name="registered">
|
||||||
@@ -14,23 +15,35 @@
|
|||||||
</mat-checkbox>
|
</mat-checkbox>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<ng-container *ngIf="eventRegistration.doublesEvent">
|
@if (eventRegistration.doublesEvent) {
|
||||||
<mat-form-field appearance="fill">
|
<mat-form-field appearance="fill">
|
||||||
<mat-label>Partner</mat-label>
|
<mat-label>Partner</mat-label>
|
||||||
<mat-select [value]="eventRegistration.partner" [disabled]="!tournamentRegistration.editable || !eventRegistration.registered" [(ngModel)]="eventRegistration.partner">
|
<input type="text"
|
||||||
<mat-option>Geen</mat-option>
|
matInput
|
||||||
<mat-option *ngFor="let player of getRelevantPlayers(eventRegistration.type)" [value]="player.id">
|
[matAutocomplete]="auto"
|
||||||
|
[disabled]="!tournamentRegistration.editable || !eventRegistration.registered"
|
||||||
|
[(ngModel)]="eventRegistration.partner"
|
||||||
|
(input)="onPartnerSearch($any($event.target).value, eventRegistration)"
|
||||||
|
[name]="'partner-' + eventRegistration.id">
|
||||||
|
<mat-autocomplete #auto="matAutocomplete"
|
||||||
|
[displayWith]="displayPartnerName.bind(this)"
|
||||||
|
(optionSelected)="onPartnerSelected($event, eventRegistration)">
|
||||||
|
<mat-option [value]="null">Geen</mat-option>
|
||||||
|
@for (player of getFilteredPlayers(eventRegistration); track player.id) {
|
||||||
|
<mat-option [value]="player.id">
|
||||||
{{ player | fullName }}
|
{{ player | fullName }}
|
||||||
</mat-option>
|
</mat-option>
|
||||||
</mat-select>
|
}
|
||||||
|
</mat-autocomplete>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</ng-container>
|
}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6"></div>
|
<div class="col-6"></div>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
}
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
<mat-card-actions *ngIf="tournamentRegistration.editable">
|
@if (tournamentRegistration.editable) {
|
||||||
|
<mat-card-actions>
|
||||||
<button mat-button (click)="saveRegistration(tournamentRegistration, $event)" [disabled]="waitingForBackend">
|
<button mat-button (click)="saveRegistration(tournamentRegistration, $event)" [disabled]="waitingForBackend">
|
||||||
<mat-icon>save</mat-icon>
|
<mat-icon>save</mat-icon>
|
||||||
Opslaan
|
Opslaan
|
||||||
@@ -40,11 +53,15 @@
|
|||||||
Annuleren
|
Annuleren
|
||||||
</a>
|
</a>
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
|
}
|
||||||
</mat-card>
|
</mat-card>
|
||||||
<button mat-button (click)="this.showAll = true" *ngIf="!this.showAll">
|
}
|
||||||
<mat-icon>search</mat-icon>
|
@if (!this.showAll) {
|
||||||
Toon oude toernooien
|
<button mat-button (click)="this.showAll = true">
|
||||||
|
<mat-icon>expand</mat-icon>
|
||||||
|
Toon inactieve toernooien
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ import {MatCard, MatCardActions, MatCardContent, MatCardHeader} from "@angular/m
|
|||||||
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
import {RegistrationService} from "../../service/registration.service";
|
import {RegistrationService} from "../../service/registration.service";
|
||||||
import {NgFor, NgIf} from "@angular/common";
|
|
||||||
import {MatCheckbox, MatCheckboxChange} from "@angular/material/checkbox";
|
import {MatCheckbox, MatCheckboxChange} from "@angular/material/checkbox";
|
||||||
import {EventRegistration, TournamentRegistration} from "../../model/tournamentRegistration";
|
import {EventRegistration, TournamentRegistration} from "../../model/tournamentRegistration";
|
||||||
import {MatOption} from "@angular/material/core";
|
import {MatOption} from "@angular/material/core";
|
||||||
import {MatSelect} from "@angular/material/select";
|
|
||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {MatAnchor, MatButton} from "@angular/material/button";
|
import {MatAnchor, MatButton} from "@angular/material/button";
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {TitleService} from "../../service/title.service";
|
import {HeaderService} from "../../service/header.service";
|
||||||
|
import {MatAutocomplete, MatAutocompleteTrigger} from "@angular/material/autocomplete";
|
||||||
|
import {MatInput} from "@angular/material/input";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-player-registrations',
|
selector: 'app-player-registrations',
|
||||||
@@ -25,24 +25,26 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatCardHeader,
|
MatCardHeader,
|
||||||
MatFormField,
|
MatFormField,
|
||||||
MatLabel,
|
MatLabel,
|
||||||
NgFor,
|
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
MatCheckbox,
|
MatCheckbox,
|
||||||
NgIf,
|
|
||||||
MatCardActions,
|
MatCardActions,
|
||||||
RouterLink,
|
RouterLink,
|
||||||
MatOption,
|
MatOption,
|
||||||
MatSelect,
|
// MatSelect,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
MatButton,
|
MatButton,
|
||||||
MatAnchor,
|
MatAnchor,
|
||||||
FullNamePipe
|
FullNamePipe,
|
||||||
|
MatAutocomplete,
|
||||||
|
MatAutocompleteTrigger,
|
||||||
|
MatInput,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
FullNamePipe
|
FullNamePipe
|
||||||
],
|
],
|
||||||
templateUrl: './player-registrations.component.html',
|
templateUrl: './player-registrations.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './player-registrations.component.scss'
|
styleUrl: './player-registrations.component.scss'
|
||||||
})
|
})
|
||||||
export class PlayerRegistrationsComponent implements OnInit {
|
export class PlayerRegistrationsComponent implements OnInit {
|
||||||
@@ -54,21 +56,23 @@ export class PlayerRegistrationsComponent implements OnInit {
|
|||||||
|
|
||||||
waitingForBackend: boolean = false;
|
waitingForBackend: boolean = false;
|
||||||
|
|
||||||
|
private partnerSearchTerms: Map<number, string> = new Map();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _snackBar: MatSnackBar,
|
private _snackBar: MatSnackBar,
|
||||||
private playerService: PlayerService,
|
private playerService: PlayerService,
|
||||||
private registrationService: RegistrationService,
|
private registrationService: RegistrationService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService,
|
private fullNamePipe: FullNamePipe,
|
||||||
private fullNamePipe: FullNamePipe
|
private headerService: HeaderService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const id = this.route.snapshot.paramMap.get('id');
|
const id = this.route.snapshot.paramMap.get('id');
|
||||||
this.playerService.getById(Number(id)).subscribe(data => {
|
this.playerService.getById(Number(id)).subscribe(data => {
|
||||||
this.player = data;
|
this.player = data;
|
||||||
this.titleService.setTitle(`Inschrijvingen van ${this.fullNamePipe.transform(this.player)}`);
|
this.headerService.setTitle(`Inschrijvingen van ${this.fullNamePipe.transform(this.player)}`);
|
||||||
});
|
});
|
||||||
this.registrationService.getTournamentRegistrationsByPlayerId(Number(id)).subscribe(data => {
|
this.registrationService.getTournamentRegistrationsByPlayerId(Number(id)).subscribe(data => {
|
||||||
this.tournamentRegistrations = data;
|
this.tournamentRegistrations = data;
|
||||||
@@ -78,6 +82,40 @@ export class PlayerRegistrationsComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onPartnerSearch(searchTerm: any, eventRegistration: EventRegistration) {
|
||||||
|
// Only treat as search if it's a string (typed input)
|
||||||
|
if (typeof searchTerm === 'string') {
|
||||||
|
this.partnerSearchTerms.set(eventRegistration.id, searchTerm?.toLowerCase() || '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilteredPlayers(eventRegistration: EventRegistration): Player[] {
|
||||||
|
const allRelevant = this.getRelevantPlayers(eventRegistration.type);
|
||||||
|
const searchTerm = this.partnerSearchTerms.get(eventRegistration.id);
|
||||||
|
|
||||||
|
if (!searchTerm) {
|
||||||
|
return allRelevant;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRelevant.filter(player => {
|
||||||
|
const fullName = this.fullNamePipe.transform(player).toLowerCase();
|
||||||
|
return fullName.includes(searchTerm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
displayPartnerName(playerId: number | null): string {
|
||||||
|
if (!playerId || !this.allPlayers) return '';
|
||||||
|
const player = this.allPlayers.find(p => p.id === playerId);
|
||||||
|
return player ? this.fullNamePipe.transform(player) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
onPartnerSelected(event: any, eventRegistration: EventRegistration) {
|
||||||
|
eventRegistration.partner = event.option.value;
|
||||||
|
// Clear the search term when a partner is selected
|
||||||
|
this.partnerSearchTerms.delete(eventRegistration.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
saveRegistration(tournamentRegistration: TournamentRegistration, event: MouseEvent) {
|
saveRegistration(tournamentRegistration: TournamentRegistration, event: MouseEvent) {
|
||||||
this.waitingForBackend = true;
|
this.waitingForBackend = true;
|
||||||
this.registrationService.saveTournamentRegistrations(tournamentRegistration, this.player.id).subscribe(data => {
|
this.registrationService.saveTournamentRegistrations(tournamentRegistration, this.player.id).subscribe(data => {
|
||||||
@@ -115,7 +153,7 @@ export class PlayerRegistrationsComponent implements OnInit {
|
|||||||
if (this.showAll) {
|
if (this.showAll) {
|
||||||
return this.tournamentRegistrations;
|
return this.tournamentRegistrations;
|
||||||
} else {
|
} else {
|
||||||
return this.tournamentRegistrations.filter(t => (t.status == 'UPCOMING' || t.status == 'ONGOING'));
|
return this.tournamentRegistrations.filter(t => t.active);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,28 @@
|
|||||||
@if (round.status != 'FINISHED') {
|
@if (round.status != 'FINISHED') {
|
||||||
<table class="table table-sm m-4 wide w-100">
|
<table class="table table-sm m-4 wide w-100">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
<td class="align-middle" style="width: 45%;">{{ match.team1 | teamText }}</td>
|
<tr>
|
||||||
|
<td class="align-middle" style="width: 45%;">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team1"
|
||||||
|
[event]="this.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-sep">-</td>
|
<td class="align-middle w-sep">-</td>
|
||||||
<td class="align-middle" style="width: 45%;">{{ match.team2 | teamText }}</td>
|
<td class="align-middle" style="width: 45%;">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team2"
|
||||||
|
[event]="this.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-sep"></td>
|
<td class="align-middle w-sep"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
@if (round.drawnOut) {
|
@if (round.drawnOut) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
||||||
@@ -19,30 +35,36 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<table class="table table-sm m-4 wide w-100">
|
<table class="table table-sm m-4 wide w-100">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle" style="width: 30%;">
|
<td class="align-middle" style="width: 30%;">
|
||||||
@if (event.doublesEvent) {
|
<app-team-display
|
||||||
{{ match.team1.player1 | fullName }} /<br>{{ match.team1.player2 | fullName }}
|
[team]="match.team1"
|
||||||
} @else {
|
[event]="this.event"
|
||||||
{{ match.team1.player1 | fullName }}
|
[tournament]="this.tournament"
|
||||||
}
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
<td class="align-middle w-sep">-</td>
|
<td class="align-middle w-sep">-</td>
|
||||||
<td class="align-middle" style="width: 30%;">
|
<td class="align-middle" style="width: 30%;">
|
||||||
@if (event.doublesEvent) {
|
<app-team-display
|
||||||
{{ match.team2.player1 | fullName }} /<br>{{ match.team2.player2 | fullName }}
|
[team]="match.team2"
|
||||||
} @else {
|
[event]="this.event"
|
||||||
{{ match.team2.player1 | fullName }}
|
[tournament]="this.tournament"
|
||||||
}
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle" style="width: 35%;">
|
<td class="align-middle" style="width: 35%;">
|
||||||
<div class="row result align-items-center">
|
<div class="row result align-items-center">
|
||||||
<span *ngFor="let game of match.games" class="col-3">{{ game.score1 }}-{{ game.score2 }}</span>
|
@for (game of match.games; track game.id) {
|
||||||
|
<span class="col-3">{{ game.score1 }}-{{ game.score2 }}</span>
|
||||||
|
}
|
||||||
@if (match.games.length == 2) {
|
@if (match.games.length == 2) {
|
||||||
<span class="col-3"></span>
|
<span class="col-3"></span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
@if (round.drawnOut) {
|
@if (round.drawnOut) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
||||||
@@ -78,14 +100,24 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
<tr *ngFor="let entry of round.standings.entries">
|
@for (entry of round.standings.entries; track entry.position) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ entry.position }}</td>
|
<td class="align-middle">{{ entry.position }}</td>
|
||||||
<td class="align-middle">{{ entry.team | teamText }}</td>
|
<td class="align-middle">
|
||||||
|
<app-team-display
|
||||||
|
[team]="entry.team"
|
||||||
|
[event]="this.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true"
|
||||||
|
[explicitSubstitute]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle">{{ entry.played }}</td>
|
<td class="align-middle">{{ entry.played }}</td>
|
||||||
<td class="align-middle">{{ entry.points / entry.played | number: '1.0-2' }}</td>
|
<td class="align-middle">{{ entry.points / entry.played | number: '1.0-2' }}</td>
|
||||||
<td class="align-middle">{{ (entry.gamesWon - entry.gamesLost) / entry.played | number: '1.0-2' }}</td>
|
<td class="align-middle">{{ (entry.gamesWon - entry.gamesLost) / entry.played | number: '1.0-2' }}</td>
|
||||||
<td class="align-middle">{{ (entry.pointsWon - entry.pointsLost) / entry.played | number: '1.0-2' }}</td>
|
<td class="align-middle">{{ (entry.pointsWon - entry.pointsLost) / entry.played | number: '1.0-2' }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,24 +5,24 @@ import {Group} from "../../model/group";
|
|||||||
import {Round} from "../../model/round";
|
import {Round} from "../../model/round";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
import {DecimalPipe, NgForOf} from "@angular/common";
|
import {DecimalPipe} from "@angular/common";
|
||||||
import {TeamPipe} from "../../pipes/team-pipe";
|
import {TeamPipe} from "../../pipes/team-pipe";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {TitleService} from "../../service/title.service";
|
import {TeamDisplayComponent} from "../team-display/team-display.component";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-round-overview',
|
selector: 'app-round-overview',
|
||||||
imports: [
|
imports: [
|
||||||
NgForOf,
|
|
||||||
TeamPipe,
|
TeamPipe,
|
||||||
DecimalPipe,
|
DecimalPipe,
|
||||||
FullNamePipe
|
TeamDisplayComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
TeamPipe,
|
TeamPipe,
|
||||||
FullNamePipe
|
FullNamePipe
|
||||||
],
|
],
|
||||||
templateUrl: './round-overview.component.html',
|
templateUrl: './round-overview.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './round-overview.component.scss'
|
styleUrl: './round-overview.component.scss'
|
||||||
})
|
})
|
||||||
export class RoundOverviewComponent implements OnInit {
|
export class RoundOverviewComponent implements OnInit {
|
||||||
@@ -37,12 +37,10 @@ export class RoundOverviewComponent implements OnInit {
|
|||||||
private tournamentService: TournamentService,
|
private tournamentService: TournamentService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle("Rondeoverzicht");
|
|
||||||
const tournamentId = this.route.snapshot.paramMap.get('id');
|
const tournamentId = this.route.snapshot.paramMap.get('id');
|
||||||
let roundId = Number(this.route.snapshot.paramMap.get('roundId'));
|
let roundId = Number(this.route.snapshot.paramMap.get('roundId'));
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<h2 mat-dialog-title>Kies een invaller voor {{ data.player.name }}:</h2>
|
||||||
|
<mat-dialog-content>
|
||||||
|
@for (substitution of data.player.substitutions; track $index) {
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<h5>{{ Event.getType(substitution.event) }}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<mat-form-field appearance="fill">
|
||||||
|
<mat-label>Invaller</mat-label>
|
||||||
|
<mat-select [(ngModel)]="substitution.substitute">
|
||||||
|
<mat-option [value]="-1">Geen</mat-option>
|
||||||
|
@for (player of data.availablePlayers; track player.id) {
|
||||||
|
<mat-option [value]="player.id">
|
||||||
|
{{ player.name }}
|
||||||
|
</mat-option>
|
||||||
|
}
|
||||||
|
</mat-select>
|
||||||
|
</mat-form-field>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</mat-dialog-content>
|
||||||
|
<mat-dialog-actions>
|
||||||
|
<!-- @if (substitute) {-->
|
||||||
|
<button mat-button [mat-dialog-close]="{substitutions: data.player.substitutions}">Opslaan</button>
|
||||||
|
<!-- }-->
|
||||||
|
<button mat-button (click)="onAnnulerenClick()">Annuleren</button>
|
||||||
|
</mat-dialog-actions>
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import {Component, inject, Inject} from '@angular/core';
|
||||||
|
import {MatButton} from "@angular/material/button";
|
||||||
|
import {
|
||||||
|
MAT_DIALOG_DATA,
|
||||||
|
MatDialogActions,
|
||||||
|
MatDialogClose,
|
||||||
|
MatDialogContent,
|
||||||
|
MatDialogRef,
|
||||||
|
MatDialogTitle
|
||||||
|
} from "@angular/material/dialog";
|
||||||
|
import {MatFormField, MatLabel} from "@angular/material/form-field";
|
||||||
|
import {MatOption, MatSelect} from "@angular/material/select";
|
||||||
|
import {TournamentPlayer} from "../../model/tournamentPlayer";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
import {Event} from "../../model/event";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-substitute-selection',
|
||||||
|
imports: [
|
||||||
|
MatButton,
|
||||||
|
MatDialogActions,
|
||||||
|
MatDialogTitle,
|
||||||
|
MatDialogContent,
|
||||||
|
MatFormField,
|
||||||
|
MatLabel,
|
||||||
|
MatOption,
|
||||||
|
MatSelect,
|
||||||
|
FormsModule,
|
||||||
|
MatDialogClose,
|
||||||
|
],
|
||||||
|
templateUrl: './substitute-selection.component.html',
|
||||||
|
styleUrl: './substitute-selection.component.scss',
|
||||||
|
standalone: true
|
||||||
|
})
|
||||||
|
export class SubstituteSelectionComponent {
|
||||||
|
|
||||||
|
substitute: TournamentPlayer[];
|
||||||
|
|
||||||
|
readonly dialogRef = inject(MatDialogRef<SubstituteSelectionComponent>);
|
||||||
|
|
||||||
|
constructor(@Inject(MAT_DIALOG_DATA) public data: {
|
||||||
|
player: TournamentPlayer,
|
||||||
|
availablePlayers: TournamentPlayer[]
|
||||||
|
}) {}
|
||||||
|
|
||||||
|
onAnnulerenClick() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected readonly Event = Event;
|
||||||
|
}
|
||||||
39
src/app/components/team-display/team-display.component.ts
Normal file
39
src/app/components/team-display/team-display.component.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import {Component, Input} from '@angular/core';
|
||||||
|
import {Event} from '../../model/event';
|
||||||
|
import {Tournament} from '../../model/tournament';
|
||||||
|
import {PlayerDisplayComponent} from '../player-display/player-display.component';
|
||||||
|
import {Team} from "../../model/team";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-team-display',
|
||||||
|
standalone: true,
|
||||||
|
imports: [PlayerDisplayComponent],
|
||||||
|
template: `
|
||||||
|
<app-player-display
|
||||||
|
[player]="team.player1"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament"
|
||||||
|
[exlicitSubstitute]="explicitSubstitute">
|
||||||
|
</app-player-display>
|
||||||
|
|
||||||
|
@if (event.doublesEvent && team.player2) {
|
||||||
|
@if (this.inline) {
|
||||||
|
/
|
||||||
|
} @else {
|
||||||
|
<br />
|
||||||
|
}
|
||||||
|
<app-player-display
|
||||||
|
[player]="team.player2"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-player-display>
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class TeamDisplayComponent {
|
||||||
|
@Input({ required: true }) team!: Team;
|
||||||
|
@Input({ required: true }) event!: Event;
|
||||||
|
@Input({ required: true }) tournament!: Tournament;
|
||||||
|
@Input({ required: false }) inline: boolean = true;
|
||||||
|
@Input({ required: false }) explicitSubstitute: boolean = false;
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<mat-card appearance="outlined" *ngIf="tournament">
|
|
||||||
<mat-card-header>
|
|
||||||
<h5>Indeling voor {{ tournament.name }}</h5>
|
|
||||||
</mat-card-header>
|
|
||||||
<mat-card-content>
|
|
||||||
<mat-card *ngFor="let event of tournament.events" appearance="outlined" class="m-3">
|
|
||||||
<mat-card-header>
|
|
||||||
<h6>Indeling {{ TournamentEvent.getType(event.type) }}</h6>
|
|
||||||
</mat-card-header>
|
|
||||||
<mat-card-content>
|
|
||||||
<mat-accordion multi="true">
|
|
||||||
<mat-expansion-panel *ngFor="let group of event.groups">
|
|
||||||
<mat-expansion-panel-header>
|
|
||||||
<mat-panel-title>
|
|
||||||
{{ group.name }} <span class="badge text-bg-success">{{ group.teams.length }}</span>
|
|
||||||
</mat-panel-title>
|
|
||||||
</mat-expansion-panel-header>
|
|
||||||
<table class="table {{ event.doublesEvent ? 'w-100' : 'w-50' }}">
|
|
||||||
<thead class="thead-dark">
|
|
||||||
<tr>
|
|
||||||
<th scope="col" class="w-20">Naam</th>
|
|
||||||
<th scope="col" class="w-20">Club</th>
|
|
||||||
<th scope="col" class="w-10">Speelsterkte</th>
|
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-20">Partner</th>
|
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-20">Club</th>
|
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-10">Speelsterkte</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let team of group.teams">
|
|
||||||
<td class="align-middle">{{ team.player1 | fullName }}</td>
|
|
||||||
<td class="align-middle">{{ team.player1.club }}</td>
|
|
||||||
<td class="align-middle">{{ getStrength(team.player1.strength.valueOf()) }}</td>
|
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ team.player2 | fullName }}</td>
|
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ team.player2.club }}</td>
|
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ getStrength(team.player2.strength.valueOf()) }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</mat-expansion-panel>
|
|
||||||
</mat-accordion>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
</mat-card-content>
|
|
||||||
</mat-card>
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
|
||||||
import {Tournament} from "../../model/tournament";
|
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
|
||||||
import {NgForOf, NgIf} from "@angular/common";
|
|
||||||
import {
|
|
||||||
MatAccordion,
|
|
||||||
MatExpansionPanel,
|
|
||||||
MatExpansionPanelHeader,
|
|
||||||
MatExpansionPanelTitle
|
|
||||||
} from "@angular/material/expansion";
|
|
||||||
import {Event} from "../../model/event";
|
|
||||||
import {Strength} from "../../model/player";
|
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-tournament-divide',
|
|
||||||
imports: [
|
|
||||||
MatCard,
|
|
||||||
MatCardHeader,
|
|
||||||
NgIf,
|
|
||||||
MatCardContent,
|
|
||||||
MatExpansionPanel,
|
|
||||||
MatExpansionPanelTitle,
|
|
||||||
MatExpansionPanelHeader,
|
|
||||||
NgForOf,
|
|
||||||
FullNamePipe,
|
|
||||||
MatAccordion
|
|
||||||
],
|
|
||||||
templateUrl: './tournament-divide.component.html',
|
|
||||||
styleUrl: './tournament-divide.component.scss'
|
|
||||||
})
|
|
||||||
export class TournamentDivideComponent implements OnInit {
|
|
||||||
tournament?: Tournament;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private tournamentService: TournamentService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
private router: Router
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
const id = this.route.snapshot.paramMap.get('id');
|
|
||||||
this.tournamentService.getById(Number(id)).subscribe(data => {
|
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
// this.tournamentService.getDivision(Number(id)).subscribe(data => {
|
|
||||||
// this.tournamentDivision = data;
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly TournamentEvent = Event;
|
|
||||||
|
|
||||||
getStrength(strength: string) {
|
|
||||||
for (let [key, value] of Object.entries(Strength)) {
|
|
||||||
if (key == strength) return value;
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
<mat-card appearance="outlined" *ngIf="tournament">
|
@if (tournament) {
|
||||||
|
<mat-card appearance="outlined">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h5>Loting voor {{ tournament.name }}</h5>
|
<h5>Loting voor {{ tournament.name }}</h5>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-card *ngFor="let event of tournament.events" appearance="outlined" class="m-3">
|
@for (event of tournament.events; track event.id) {
|
||||||
|
<mat-card appearance="outlined" class="m-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h6>Loting {{ TournamentEvent.getType(event.type) }}</h6>
|
<h6>Loting {{ TournamentEvent.getType(event.type) }}</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-accordion multi="true">
|
<mat-accordion multi="true">
|
||||||
<mat-expansion-panel *ngFor="let group of event.groups">
|
@for (group of event.groups; track group.id) {
|
||||||
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
{{ group.name }} <span class="badge text-bg-success">{{ group.teams.length }}</span>
|
{{ group.name }} <span class="badge text-bg-success">{{ group.teams.length }}</span>
|
||||||
@@ -24,17 +27,21 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of group.rounds[0].matches">
|
@for (match of group.rounds[0].matches; track match.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ match.team1 | teamText }}</td>
|
<td class="align-middle">{{ match.team1 | teamText }}</td>
|
||||||
<td class="align-middle">-</td>
|
<td class="align-middle">-</td>
|
||||||
<td class="align-middle">{{ match.team2 | teamText }}</td>
|
<td class="align-middle">{{ match.team2 | teamText }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
}
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
}
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import {Tournament} from "../../model/tournament";
|
|||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
||||||
import {NgForOf, NgIf} from "@angular/common";
|
|
||||||
import {
|
import {
|
||||||
MatAccordion,
|
MatAccordion,
|
||||||
MatExpansionPanel,
|
MatExpansionPanel,
|
||||||
@@ -18,13 +17,11 @@ import {FullNamePipe} from "../../pipes/fullname-pipe";
|
|||||||
selector: 'app-tournament-draw',
|
selector: 'app-tournament-draw',
|
||||||
imports: [
|
imports: [
|
||||||
MatCard,
|
MatCard,
|
||||||
NgIf,
|
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
MatCardHeader,
|
MatCardHeader,
|
||||||
MatExpansionPanel,
|
MatExpansionPanel,
|
||||||
MatExpansionPanelHeader,
|
MatExpansionPanelHeader,
|
||||||
MatExpansionPanelTitle,
|
MatExpansionPanelTitle,
|
||||||
NgForOf,
|
|
||||||
TeamPipe,
|
TeamPipe,
|
||||||
MatAccordion
|
MatAccordion
|
||||||
],
|
],
|
||||||
@@ -33,6 +30,7 @@ import {FullNamePipe} from "../../pipes/fullname-pipe";
|
|||||||
TeamPipe
|
TeamPipe
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-draw.component.html',
|
templateUrl: './tournament-draw.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-draw.component.scss'
|
styleUrl: './tournament-draw.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentDrawComponent implements OnInit {
|
export class TournamentDrawComponent implements OnInit {
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
@if (tournament) {
|
@if (tournament) {
|
||||||
<form (ngSubmit)="saveTournament()">
|
<form (ngSubmit)="saveTournament()">
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined">
|
||||||
<!--
|
|
||||||
<mat-card-header>
|
|
||||||
<h5>{{ isEditMode ? 'Bewerk toernooi' : 'Toevoegen toernooi' }}</h5>
|
|
||||||
</mat-card-header>
|
|
||||||
-->
|
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
@@ -26,6 +21,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-9"></div>
|
<div class="col-md-9"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<mat-checkbox [(ngModel)]="tournament.active" name="active">
|
||||||
|
Actief
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-9"></div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label id="max-events-label">Max. aantal onderdelen per inschrijving</label>
|
<label id="max-events-label">Max. aantal onderdelen per inschrijving</label>
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import {MatInput} from "@angular/material/input";
|
|||||||
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
import {MatRadioButton, MatRadioGroup} from "@angular/material/radio";
|
||||||
import {CurrencyPipe, registerLocaleData} from "@angular/common";
|
import {CurrencyPipe, registerLocaleData} from "@angular/common";
|
||||||
import nl from "@angular/common/locales/nl";
|
import nl from "@angular/common/locales/nl";
|
||||||
import {TitleService} from "../../service/title.service";
|
|
||||||
import {NgxMaskDirective} from "ngx-mask";
|
import {NgxMaskDirective} from "ngx-mask";
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {MatCheckbox} from "@angular/material/checkbox";
|
||||||
|
|
||||||
registerLocaleData(nl);
|
registerLocaleData(nl);
|
||||||
|
|
||||||
@@ -34,12 +34,14 @@ registerLocaleData(nl);
|
|||||||
MatRadioButton,
|
MatRadioButton,
|
||||||
MatRadioGroup,
|
MatRadioGroup,
|
||||||
MatHint,
|
MatHint,
|
||||||
NgxMaskDirective
|
NgxMaskDirective,
|
||||||
|
MatCheckbox
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
CurrencyPipe
|
CurrencyPipe
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-edit.component.html',
|
templateUrl: './tournament-edit.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-edit.component.scss'
|
styleUrl: './tournament-edit.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentEditComponent implements OnInit {
|
export class TournamentEditComponent implements OnInit {
|
||||||
@@ -52,7 +54,6 @@ export class TournamentEditComponent implements OnInit {
|
|||||||
private currencyPipe: CurrencyPipe,
|
private currencyPipe: CurrencyPipe,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService,
|
|
||||||
private _snackBar: MatSnackBar,
|
private _snackBar: MatSnackBar,
|
||||||
) {
|
) {
|
||||||
this.tournament = new Tournament();
|
this.tournament = new Tournament();
|
||||||
@@ -61,13 +62,10 @@ export class TournamentEditComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const id = this.route.snapshot.paramMap.get('id');
|
const id = this.route.snapshot.paramMap.get('id');
|
||||||
if (id) {
|
if (id) {
|
||||||
this.titleService.setTitle("Bewerk Toernooi");
|
|
||||||
this.isEditMode = true;
|
this.isEditMode = true;
|
||||||
this.tournamentService.getById(Number(id)).subscribe(data => {
|
this.tournamentService.getById(Number(id)).subscribe(data => {
|
||||||
this.tournament = data;
|
this.tournament = data;
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
this.titleService.setTitle("Nieuw Toernooi");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,4 +90,5 @@ export class TournamentEditComponent implements OnInit {
|
|||||||
this.tournament.costsPerEvent[number] = Number(value);
|
this.tournament.costsPerEvent[number] = Number(value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined">
|
||||||
<!--
|
|
||||||
<mat-card-header>
|
|
||||||
<h5>Toernooien</h5>
|
|
||||||
</mat-card-header>
|
|
||||||
-->
|
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<thead class="thead-dark">
|
<thead class="thead-dark">
|
||||||
@@ -16,28 +11,68 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let tournament of tournaments">
|
@for (tournament of getActiveTournaments(); track tournament.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ tournament.id }}</td>
|
<td class="align-middle">{{ tournament.id }}</td>
|
||||||
<td class="align-middle"><a [routerLink]="['/tournaments', tournament.id, 'manage']">{{ tournament.name }}</a></td>
|
<td class="align-middle"><a [routerLink]="['/tournaments', tournament.id, 'manage']">{{ tournament.name }}</a></td>
|
||||||
<td class="align-middle">{{ tournament.date }}</td>
|
<td class="align-middle">{{ tournament.date }}</td>
|
||||||
<td class="align-middle">{{ Tournament.getStatus(tournament) }}</td>
|
<td class="align-middle">{{ Tournament.getStatus(tournament) }}</td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
<a mat-button [routerLink]="['/tournaments', tournament.id, 'edit']" *ngIf="Tournament.getStatus(tournament) != 'Afgerond'">
|
@if (Tournament.getStatus(tournament) != 'Afgerond') {
|
||||||
|
<a mat-button [routerLink]="['/tournaments', tournament.id, 'edit']">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>edit</mat-icon>
|
||||||
Bewerk
|
Bewerk
|
||||||
</a>
|
</a>
|
||||||
|
}
|
||||||
<a mat-button [routerLink]="['/tournaments', tournament.id, 'registrations']">
|
<a mat-button [routerLink]="['/tournaments', tournament.id, 'registrations']">
|
||||||
<mat-icon>group</mat-icon>
|
<mat-icon>group</mat-icon>
|
||||||
Inschrijvingen
|
Inschrijvingen
|
||||||
</a>
|
</a>
|
||||||
<a mat-button (click)="clearDraw(tournament)" *ngIf="Tournament.getStatus(tournament) == 'Geloot'">
|
@if (Tournament.getStatus(tournament) == 'Geloot') {
|
||||||
|
<a mat-button (click)="clearDraw(tournament)">
|
||||||
<mat-icon>safety_divider</mat-icon>
|
<mat-icon>safety_divider</mat-icon>
|
||||||
Loting wissen
|
Loting wissen
|
||||||
</a>
|
</a>
|
||||||
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
|
@if (showInactive) {
|
||||||
|
@for (tournament of getInactiveTournaments(); track tournament.id) {
|
||||||
|
<tr>
|
||||||
|
<td class="align-middle">{{ tournament.id }}</td>
|
||||||
|
<td class="align-middle"><a [routerLink]="['/tournaments', tournament.id, 'manage']">{{ tournament.name }}</a></td>
|
||||||
|
<td class="align-middle">{{ tournament.date }}</td>
|
||||||
|
<td class="align-middle">{{ Tournament.getStatus(tournament) }}</td>
|
||||||
|
<td class="align-middle">
|
||||||
|
@if (Tournament.getStatus(tournament) != 'Afgerond') {
|
||||||
|
<a mat-button [routerLink]="['/tournaments', tournament.id, 'edit']">
|
||||||
|
<mat-icon>edit</mat-icon>
|
||||||
|
Bewerk
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
<a mat-button [routerLink]="['/tournaments', tournament.id, 'registrations']">
|
||||||
|
<mat-icon>group</mat-icon>
|
||||||
|
Inschrijvingen
|
||||||
|
</a>
|
||||||
|
@if (Tournament.getStatus(tournament) == 'Geloot') {
|
||||||
|
<a mat-button (click)="clearDraw(tournament)">
|
||||||
|
<mat-icon>safety_divider</mat-icon>
|
||||||
|
Loting wissen
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@if (!showInactive) {
|
||||||
|
<a mat-button (click)="showInactive = true">
|
||||||
|
<mat-icon>expand</mat-icon>
|
||||||
|
Toon inactief
|
||||||
|
</a>
|
||||||
|
}
|
||||||
<a mat-button routerLink="/tournaments/add">
|
<a mat-button routerLink="/tournaments/add">
|
||||||
<mat-icon>add</mat-icon>
|
<mat-icon>add</mat-icon>
|
||||||
Nieuw toernooi
|
Nieuw toernooi
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import {AfterContentChecked, Component, OnInit} from '@angular/core';
|
import {AfterContentChecked, Component, OnInit} from '@angular/core';
|
||||||
import {NgFor, NgIf} from "@angular/common";
|
|
||||||
import {RouterLink} from "@angular/router";
|
import {RouterLink} from "@angular/router";
|
||||||
import {Tournament} from "../../model/tournament";
|
import {Tournament} from "../../model/tournament";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
@@ -7,23 +6,24 @@ import {MatAnchor, MatButton} from "@angular/material/button";
|
|||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {MatCard, MatCardContent} from "@angular/material/card";
|
import {MatCard, MatCardContent} from "@angular/material/card";
|
||||||
import {MatTableModule} from "@angular/material/table";
|
import {MatTableModule} from "@angular/material/table";
|
||||||
import {TitleService} from "../../service/title.service";
|
import {HeaderService} from "../../service/header.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tournament-list',
|
selector: 'app-tournament-list',
|
||||||
imports: [
|
imports: [
|
||||||
NgFor, RouterLink, NgIf, MatAnchor, MatIcon, MatCard, MatCardContent, MatButton, MatTableModule
|
RouterLink, MatAnchor, MatIcon, MatCard, MatCardContent, MatButton, MatTableModule
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-list.component.html',
|
templateUrl: './tournament-list.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-list.component.scss'
|
styleUrl: './tournament-list.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentListComponent implements OnInit, AfterContentChecked {
|
export class TournamentListComponent implements OnInit {
|
||||||
|
|
||||||
tournaments: Tournament[];
|
tournaments: Tournament[];
|
||||||
|
showInactive: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private tournamentService: TournamentService,
|
private tournamentService: TournamentService
|
||||||
private titleService: TitleService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@@ -32,10 +32,6 @@ export class TournamentListComponent implements OnInit, AfterContentChecked {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
|
||||||
this.titleService.setTitle("Toernooien");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected readonly Tournament = Tournament;
|
protected readonly Tournament = Tournament;
|
||||||
|
|
||||||
clearDraw(tournament: Tournament) {
|
clearDraw(tournament: Tournament) {
|
||||||
@@ -46,4 +42,14 @@ export class TournamentListComponent implements OnInit, AfterContentChecked {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getActiveTournaments(): Tournament[] {
|
||||||
|
if (!this.tournaments) return [];
|
||||||
|
return this.tournaments.filter(tournament => tournament.active);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInactiveTournaments(): Tournament[] {
|
||||||
|
if (!this.tournaments) return [];
|
||||||
|
return this.tournaments.filter(tournament => !tournament.active);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
@if (tournament) {
|
@if (tournament) {
|
||||||
<mat-card appearance="outlined">
|
<mat-card appearance="outlined">
|
||||||
<!--
|
|
||||||
<mat-card-header>
|
|
||||||
<h5>{{ tournament.name }}</h5>
|
|
||||||
</mat-card-header>
|
|
||||||
-->
|
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
||||||
|
|
||||||
@@ -25,6 +20,13 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
<app-tournament-validate></app-tournament-validate>
|
<app-tournament-validate></app-tournament-validate>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<mat-icon>settings</mat-icon>
|
||||||
|
Beheer
|
||||||
|
</ng-template>
|
||||||
|
<app-tournament-players [tournament]="tournament"></app-tournament-players>
|
||||||
|
</mat-tab>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (tournament.status == 'DIVIDED') {
|
@if (tournament.status == 'DIVIDED') {
|
||||||
@@ -51,7 +53,7 @@
|
|||||||
<h6>Totaal: {{ getTournamentMatchCount(tournament)}} wedstrijden</h6>
|
<h6>Totaal: {{ getTournamentMatchCount(tournament)}} wedstrijden</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
@for (event of tournament.events; track event) {
|
@for (event of tournament.events; track event.id) {
|
||||||
@if (event.groups.length > 0) {
|
@if (event.groups.length > 0) {
|
||||||
<mat-card appearance="outlined" class="m-3">
|
<mat-card appearance="outlined" class="m-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
@@ -59,7 +61,8 @@
|
|||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-accordion multi="true">
|
<mat-accordion multi="true">
|
||||||
<mat-expansion-panel *ngFor="let group of event.groups">
|
@for (group of event.groups; track group.id) {
|
||||||
|
<mat-expansion-panel>
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
{{ group.name }} <span class="badge text-bg-success">{{ group.teams.length }}</span>
|
{{ group.name }} <span class="badge text-bg-success">{{ group.teams.length }}</span>
|
||||||
@@ -71,88 +74,109 @@
|
|||||||
<th scope="col" class="w-20">Naam</th>
|
<th scope="col" class="w-20">Naam</th>
|
||||||
<th scope="col" class="w-20">Club</th>
|
<th scope="col" class="w-20">Club</th>
|
||||||
<th scope="col" class="w-10">Speelsterkte</th>
|
<th scope="col" class="w-10">Speelsterkte</th>
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-20">Partner</th>
|
@if (event.doublesEvent) {
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-20">Club</th>
|
<th scope="col" class="w-20">Partner</th>
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-10">Speelsterkte</th>
|
<th scope="col" class="w-20">Club</th>
|
||||||
|
<th scope="col" class="w-10">Speelsterkte</th>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let team of group.teams">
|
@for (team of group.teams; track team.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ team.player1 | fullName }}</td>
|
<td class="align-middle">{{ team.player1 | fullName }}</td>
|
||||||
<td class="align-middle">{{ team.player1.club }}</td>
|
<td class="align-middle">{{ team.player1.club }}</td>
|
||||||
<td class="align-middle">{{ getStrength(team.player1.strength.valueOf()) }}</td>
|
<td class="align-middle">{{ getStrength(team.player1.strength.valueOf()) }}</td>
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ team.player2 | fullName }}</td>
|
@if (event.doublesEvent && team.player2) {
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ team.player2?.club }}</td>
|
<td class="align-middle">{{ team.player2 | fullName }}</td>
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ getStrength(team.player2?.strength?.valueOf()) }}</td>
|
<td class="align-middle">{{ team.player2.club }}</td>
|
||||||
|
<td class="align-middle">{{ getStrength(team.player2.strength.valueOf()) }}</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
|
}
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<mat-icon>settings</mat-icon>
|
||||||
|
Beheer
|
||||||
|
</ng-template>
|
||||||
|
<app-tournament-players [tournament]="tournament"></app-tournament-players>
|
||||||
|
</mat-tab>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (tournament.status == 'ONGOING') {
|
@if (tournament.status == 'ONGOING') {
|
||||||
<mat-tab>
|
<mat-tab>
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<mat-icon>play_arrow</mat-icon>
|
<mat-icon>play_arrow</mat-icon>
|
||||||
Actieve wedstrijden
|
Actieve wedstrijden
|
||||||
|
|
||||||
@if (this.activeMatches().length > 0) {
|
@if (this.activeMatches().length > 0) {
|
||||||
<span class="badge text-bg-success">{{ this.activeMatches().length }}</span>
|
<span class="badge text-bg-success">{{ this.activeMatches().length }}</span>
|
||||||
}
|
}
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
<div class="tab-content-wrapper">
|
||||||
@if (this.activeMatches().length > 0) {
|
@if (this.activeMatches().length > 0) {
|
||||||
<table class="table table-hover w-100 m-4">
|
<h6 class="mt-3"></h6>
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th colspan="3">Wedstrijd</th>
|
|
||||||
<th>Onderdeel/Ronde</th>
|
|
||||||
<th>Start</th>
|
|
||||||
<th>Baan</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@for (activeMatch of this.activeMatches(); track activeMatch.match.id) {
|
@for (activeMatch of this.activeMatches(); track activeMatch.match.id) {
|
||||||
<tr>
|
<mat-expansion-panel>
|
||||||
<td class="align-middle">{{ activeMatch.match.team1 | teamText }}</td>
|
<mat-expansion-panel-header>
|
||||||
<td class="align-middle">-</td>
|
<div class="col-md-2 d-flex align-items-center">Baan {{ activeMatch.match.court }}</div>
|
||||||
<td class="align-middle">{{ activeMatch.match.team2 | teamText }}</td>
|
<div class="col-md-3">
|
||||||
<td class="align-middle">{{ activeMatch.group.name }} {{ activeMatch.round.name }}</td>
|
<app-team-display
|
||||||
<td class="align-middle">{{ activeMatch.match.startTime | date: 'HH:mm' }}</td>
|
[team]="activeMatch.match.team1"
|
||||||
<td class="align-middle">{{ activeMatch.match.court }}</td>
|
[event]="activeMatch.event"
|
||||||
<td nowrap class="align-middle">
|
[tournament]="this.tournament"
|
||||||
<button class="align-baseline" mat-button (click)="editResult(activeMatch.match, activeMatch.group, activeMatch.round)">
|
[inline]="false">
|
||||||
<mat-icon>edit</mat-icon>
|
</app-team-display>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-1 d-flex align-items-center">-</div>
|
||||||
|
<div class="col-md-3">
|
||||||
|
<app-team-display
|
||||||
|
[team]="activeMatch.match.team2"
|
||||||
|
[event]="activeMatch.event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="false">
|
||||||
|
</app-team-display>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 d-flex align-items-center">{{ activeMatch.group.name }} {{ activeMatch.round.name }}</div>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<div class="row">
|
||||||
|
<hr/>
|
||||||
|
<div class="col-md-3">Teller: {{ activeMatch.match.counter | fullName }}</div>
|
||||||
|
<div class="col-md-5"></div>
|
||||||
|
<div class="col-md-2">Starttijd: {{ activeMatch.match.startTime | date: 'HH:mm' }}</div>
|
||||||
|
<div class="col-md-2">Duur: {{ getDuration(activeMatch.match.startTime) | date: 'mm:ss' }}</div>
|
||||||
|
</div>
|
||||||
|
<mat-action-row>
|
||||||
|
<button class="align-baseline" mat-button (click)="editResult(activeMatch.match, activeMatch.event, activeMatch.group, activeMatch.round)">
|
||||||
|
<mat-icon>leaderboard</mat-icon>
|
||||||
Uitslag invoeren
|
Uitslag invoeren
|
||||||
</button>
|
</button>
|
||||||
<button mat-icon-button [matMenuTriggerFor]="activeMatchMenu" [matMenuTriggerData]="{ match: activeMatch.match }" class="menu-button">
|
<button mat-button (click)="changeCounter(activeMatch.match)">
|
||||||
<mat-icon>more_vert</mat-icon>
|
<mat-icon>person</mat-icon>
|
||||||
|
Teller wijzigen
|
||||||
</button>
|
</button>
|
||||||
</td>
|
<button mat-button (click)="stopMatch(activeMatch.match)">
|
||||||
</tr>
|
|
||||||
}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<mat-menu #activeMatchMenu="matMenu">
|
|
||||||
<ng-template matMenuContent let-match="match">
|
|
||||||
<button mat-button (click)="stopMatch(match)">
|
|
||||||
<mat-icon>stop</mat-icon>
|
<mat-icon>stop</mat-icon>
|
||||||
Wedstrijd stoppen
|
Wedstrijd stoppen
|
||||||
</button>
|
</button>
|
||||||
</ng-template>
|
</mat-action-row>
|
||||||
</mat-menu>
|
</mat-expansion-panel>
|
||||||
|
<br>
|
||||||
|
}
|
||||||
} @else {
|
} @else {
|
||||||
<h6 class="mt-3">Geen actieve wedstrijden</h6>
|
<h6 class="mt-3">Geen actieve wedstrijden</h6>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
}
|
}
|
||||||
@if (tournament.status == 'ONGOING' || tournament.status == 'DRAWN') {
|
@if (tournament.status == 'ONGOING' || tournament.status == 'DRAWN') {
|
||||||
@@ -163,12 +187,13 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
||||||
<ng-container *ngFor="let event of tournament.events">
|
@for (event of tournament.events; track event.id) {
|
||||||
<ng-container *ngFor="let group of event.groups">
|
@for (group of event.groups; track group.id) {
|
||||||
<mat-tab label="{{group.id}}">
|
<mat-tab label="{{group.id}}">
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<!--<mat-icon>list</mat-icon> -->
|
@if (group.status == "FINISHED") {
|
||||||
{{ group.name }}
|
<mat-icon>check</mat-icon>
|
||||||
|
}{{ group.name }}
|
||||||
|
|
||||||
@if (getActiveMatchCountForGroup(group) > 0) {
|
@if (getActiveMatchCountForGroup(group) > 0) {
|
||||||
<span class="badge text-bg-success">{{ getActiveMatchCountForGroup(group) }}</span>
|
<span class="badge text-bg-success">{{ getActiveMatchCountForGroup(group) }}</span>
|
||||||
@@ -201,7 +226,7 @@
|
|||||||
disableRipple="true"
|
disableRipple="true"
|
||||||
[(selectedIndex)]="activeRoundTab"
|
[(selectedIndex)]="activeRoundTab"
|
||||||
(selectedTabChange)="onRoundTabChange($event)">
|
(selectedTabChange)="onRoundTabChange($event)">
|
||||||
<ng-container *ngFor="let round of group.rounds; index as roundIndex">
|
@for (round of group.rounds; track round.id; let roundIndex = $index) {
|
||||||
<mat-tab label="{{round.id}}">
|
<mat-tab label="{{round.id}}">
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<mat-icon>{{ getRoundIcon(round.status) }}</mat-icon>
|
<mat-icon>{{ getRoundIcon(round.status) }}</mat-icon>
|
||||||
@@ -216,22 +241,24 @@
|
|||||||
<mat-icon>play_arrow</mat-icon>
|
<mat-icon>play_arrow</mat-icon>
|
||||||
Ronde starten
|
Ronde starten
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
<button mat-menu-item (click)="printMatchSheets(round)">
|
<button mat-menu-item (click)="printMatchSheets(round)">
|
||||||
<mat-icon>print</mat-icon>
|
<mat-icon>print</mat-icon>
|
||||||
Wedstrijdbriefjes printen
|
Wedstrijdbriefjes printen
|
||||||
</button>
|
</button>
|
||||||
}
|
@if (!round.isFinalsRound) {
|
||||||
<button mat-menu-item (click)="printRoundOverview(round)">
|
<button mat-menu-item (click)="printRoundOverview(round)">
|
||||||
<mat-icon>print</mat-icon>
|
<mat-icon>print</mat-icon>
|
||||||
Rondeoverzicht printen
|
Rondeoverzicht printen
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
@if (round.status == 'IN_PROGRESS' && checkRoundComplete(round)) {
|
@if (round.status == 'IN_PROGRESS' && checkRoundComplete(round)) {
|
||||||
<button mat-menu-item (click)="finishRound(round)">
|
<button mat-menu-item (click)="finishRound(round)">
|
||||||
<mat-icon>check</mat-icon>
|
<mat-icon>check</mat-icon>
|
||||||
Ronde afsluiten
|
Ronde afsluiten
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
@if (group.status != 'FINISHED' && round.status == 'FINISHED' && (roundIndex + 1) == group.rounds.length) {
|
@if (group.status != 'FINISHED' && round.status == 'FINISHED' && (roundIndex + 1) == group.rounds.length && !round.isFinalsRound) {
|
||||||
<button mat-menu-item (click)="newRound(group)">
|
<button mat-menu-item (click)="newRound(group)">
|
||||||
<mat-icon>playlist_add</mat-icon>
|
<mat-icon>playlist_add</mat-icon>
|
||||||
Nieuwe ronde
|
Nieuwe ronde
|
||||||
@@ -243,46 +270,90 @@
|
|||||||
<h6 class="mt-3">Wedstrijden</h6>
|
<h6 class="mt-3">Wedstrijden</h6>
|
||||||
|
|
||||||
@if (round.status == 'NOT_STARTED') {
|
@if (round.status == 'NOT_STARTED') {
|
||||||
<table class="table table-hover m-4 wide w-100">
|
<table class="table table-hover m-4 wide w-95">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
<td class="align-middle w-team">{{ match.team1 | teamText }}</td>
|
<tr>
|
||||||
|
<td class="align-middle w-team">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team1"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-sep">-</td>
|
<td class="align-middle w-sep">-</td>
|
||||||
<td class="align-middle w-team">{{ match.team2 | teamText }}</td>
|
<td class="align-middle w-team">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team2"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-fill"></td>
|
<td class="align-middle w-fill"></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
@if (round.drawnOut) {
|
@if (round.drawnOut) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
<td class="align-middle w-100" colspan="4">
|
||||||
|
<b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
} @else if (round.status == 'IN_PROGRESS') {
|
} @else if (round.status == 'IN_PROGRESS') {
|
||||||
<table class="table table-hover m-4 wide w-100">
|
<table class="table table-hover m-4 wide w-95">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 1}">{{ match.team1 | teamText }}</td>
|
<tr>
|
||||||
|
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 1}">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team1"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-sep">-</td>
|
<td class="align-middle w-sep">-</td>
|
||||||
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 2}">{{ match.team2 | teamText }}</td>
|
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 2}">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team2"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-fill">
|
<td class="align-middle w-fill">
|
||||||
@if (match.status == 'NOT_STARTED') {
|
@if (match.status == 'NOT_STARTED') {
|
||||||
|
@if (match.canStart) {
|
||||||
<button mat-button (click)="startMatch(match)">
|
<button mat-button (click)="startMatch(match)">
|
||||||
<mat-icon>play_arrow</mat-icon>
|
<mat-icon>play_arrow</mat-icon>
|
||||||
Wedstrijd starten
|
Wedstrijd starten
|
||||||
</button>
|
</button>
|
||||||
|
} @else {
|
||||||
|
<span [matTooltip]="match.cantStartReason" style="cursor: not-allowed;">
|
||||||
|
<button mat-button disabled>
|
||||||
|
<mat-icon>play_arrow</mat-icon>
|
||||||
|
Wedstrijd starten
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
} @else if (match.status == 'IN_PROGRESS') {
|
} @else if (match.status == 'IN_PROGRESS') {
|
||||||
<button mat-button (click)="editResult(match, group, round)">
|
<button mat-button (click)="editResult(match, event, group, round)">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>leaderboard</mat-icon>
|
||||||
Uitslag invoeren
|
Uitslag
|
||||||
|
</button>
|
||||||
|
<button mat-button (click)="changeCounter(match)">
|
||||||
|
<mat-icon>person</mat-icon>
|
||||||
|
Teller
|
||||||
</button>
|
</button>
|
||||||
<button mat-button (click)="stopMatch(match)">
|
<button mat-button (click)="stopMatch(match)">
|
||||||
<mat-icon>stop</mat-icon>
|
<mat-icon>stop</mat-icon>
|
||||||
Wedstrijd stoppen
|
Stoppen
|
||||||
</button>
|
</button>
|
||||||
} @else if (match.status == 'FINISHED') {
|
} @else if (match.status == 'FINISHED') {
|
||||||
<div class="row result align-items-center">
|
<div class="row result align-items-center">
|
||||||
<span *ngFor="let game of match.games" class="col-2">{{ game.score1 }}-{{ game.score2 }}</span>
|
@for (game of match.games; track game.id) {
|
||||||
|
<span class="col-2">{{ game.score1 }}-{{ game.score2 }}</span>
|
||||||
|
}
|
||||||
@if (match.games.length == 2) {
|
@if (match.games.length == 2) {
|
||||||
<span class="col-2"></span>
|
<span class="col-2"></span>
|
||||||
}
|
}
|
||||||
@@ -291,8 +362,8 @@
|
|||||||
<mat-icon>more_vert</mat-icon>
|
<mat-icon>more_vert</mat-icon>
|
||||||
</button>
|
</button>
|
||||||
<mat-menu #finishedMatchMenu="matMenu">
|
<mat-menu #finishedMatchMenu="matMenu">
|
||||||
<button mat-menu-item (click)="editResult(match, group, round)">
|
<button mat-menu-item (click)="editResult(match, event, group, round)">
|
||||||
<mat-icon>edit</mat-icon>
|
<mat-icon>leaderboard</mat-icon>
|
||||||
Uitslag bewerken
|
Uitslag bewerken
|
||||||
</button>
|
</button>
|
||||||
</mat-menu>
|
</mat-menu>
|
||||||
@@ -300,6 +371,7 @@
|
|||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
@if (round.drawnOut) {
|
@if (round.drawnOut) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
||||||
@@ -308,21 +380,37 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
} @else if (round.status == 'FINISHED') {
|
} @else if (round.status == 'FINISHED') {
|
||||||
<table class="table table-hover m-4 wide {{ this.groupIsDoublesType(group) ? 'w-100' : 'w-100' }}">
|
<table class="table table-hover m-4 wide {{ this.groupIsDoublesType(group) ? 'w-95' : 'w-95' }}">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let match of round.matches">
|
@for (match of round.matches; track match.id) {
|
||||||
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 1}">{{ match.team1 | teamText }}</td>
|
<tr>
|
||||||
|
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 1}">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team1"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-sep">-</td>
|
<td class="align-middle w-sep">-</td>
|
||||||
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 2}">{{ match.team2 | teamText }}</td>
|
<td class="align-middle w-team" [ngClass]="{'winner': checkWinner(match) == 2}">
|
||||||
|
<app-team-display
|
||||||
|
[team]="match.team2"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="tournament">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle w-fill">
|
<td class="align-middle w-fill">
|
||||||
<div class="row result align-items-center">
|
<div class="row result align-items-center">
|
||||||
<span *ngFor="let game of match.games" class="col-2">{{ game.score1 }}-{{ game.score2 }}</span>
|
@for (game of match.games; track game.id) {
|
||||||
|
<span class="col-2">{{ game.score1 }}-{{ game.score2 }}</span>
|
||||||
|
}
|
||||||
@if (match.games.length == 2) {
|
@if (match.games.length == 2) {
|
||||||
<span class="col-2"></span>
|
<span class="col-2"></span>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
@if (round.drawnOut) {
|
@if (round.drawnOut) {
|
||||||
<tr>
|
<tr>
|
||||||
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
<td class="align-middle w-100" colspan="4"><b>Deze ronde uitgeloot:</b> {{ round.drawnOut | teamText }}</td>
|
||||||
@@ -332,6 +420,7 @@
|
|||||||
</table>
|
</table>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@if (!round.isFinalsRound) {
|
||||||
<h6 class="mt-3">Stand</h6>
|
<h6 class="mt-3">Stand</h6>
|
||||||
|
|
||||||
<table class="table w-75 m-4">
|
<table class="table w-75 m-4">
|
||||||
@@ -353,9 +442,17 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="table-group-divider">
|
<tbody class="table-group-divider">
|
||||||
<tr *ngFor="let entry of getStandingsForRound(round, group).entries">
|
@for (entry of getStandingsForRound(round, group).entries; track entry.position) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ entry.position }}</td>
|
<td class="align-middle">{{ entry.position }}</td>
|
||||||
<td class="align-middle">{{ entry.team | teamText }}</td>
|
<td class="align-middle">
|
||||||
|
<app-team-display
|
||||||
|
[team]="entry.team"
|
||||||
|
[event]="event"
|
||||||
|
[tournament]="this.tournament"
|
||||||
|
[inline]="true">
|
||||||
|
</app-team-display>
|
||||||
|
</td>
|
||||||
<td class="align-middle">{{ entry.played }}</td>
|
<td class="align-middle">{{ entry.played }}</td>
|
||||||
<td class="align-middle">
|
<td class="align-middle">
|
||||||
@if (entry.played > 0 ) {
|
@if (entry.played > 0 ) {
|
||||||
@@ -373,74 +470,44 @@
|
|||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
} @else if (round.isFinalsRound && round.status == 'FINISHED') {
|
||||||
|
<h6 class="mt-3">Uitslag</h6>
|
||||||
|
|
||||||
|
<table class="table w-50 m-4">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>1e Plaats</td>
|
||||||
|
<td>{{ (checkWinner(round.matches[0]) == 1 ? round.matches[0].team1 : round.matches[0].team2) | teamText }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2e Plaats</td>
|
||||||
|
<td>{{ (checkWinner(round.matches[0]) == 1 ? round.matches[0].team2 : round.matches[0].team1) | teamText }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3e Plaats</td>
|
||||||
|
<td>{{ (checkWinner(round.matches[1]) == 1 ? round.matches[1].team1 : round.matches[1].team2) | teamText }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
</ng-container>
|
}
|
||||||
</mat-tab-group>
|
|
||||||
</mat-tab>
|
|
||||||
</ng-container>
|
|
||||||
</ng-container>
|
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
}
|
}
|
||||||
@if (tournament.status == 'ONGOING' || tournament.status == 'DRAWN') {
|
}
|
||||||
|
</mat-tab-group>
|
||||||
|
</mat-tab>
|
||||||
|
|
||||||
<mat-tab>
|
<mat-tab>
|
||||||
<ng-template mat-tab-label>
|
<ng-template mat-tab-label>
|
||||||
<mat-icon>settings</mat-icon>
|
<mat-icon>settings</mat-icon>
|
||||||
Beheer
|
Beheer
|
||||||
</ng-template>
|
</ng-template>
|
||||||
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
<app-tournament-players [tournament]="tournament"></app-tournament-players>
|
||||||
<mat-tab>
|
|
||||||
<ng-template mat-tab-label>
|
|
||||||
<mat-icon>group</mat-icon>
|
|
||||||
Spelerslijst
|
|
||||||
</ng-template>
|
|
||||||
<table class="table table-hover w-75 m-4">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Naam</th>
|
|
||||||
<th>Onderdelen</th>
|
|
||||||
<th>Kosten</th>
|
|
||||||
<th>Betaald</th>
|
|
||||||
<th>Aanwezig</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr *ngFor="let tournamentPlayer of tournament.tournamentPlayers">
|
|
||||||
<td>{{ tournamentPlayer.name }}</td>
|
|
||||||
<td>
|
|
||||||
<ng-container *ngFor="let event of tournamentPlayer.events">
|
|
||||||
{{ event }}
|
|
||||||
</ng-container>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{{ tournament.costsPerEvent[tournamentPlayer.events.length - 1] | currency:'EUR':'symbol':'1.2-2':'nl' }}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<mat-slide-toggle [(ngModel)]="tournamentPlayer.paid" (change)="playerPaid($event, tournamentPlayer.playerId)">
|
|
||||||
@if (tournamentPlayer.paid) {
|
|
||||||
Betaald
|
|
||||||
} @else {
|
|
||||||
Nog niet betaald
|
|
||||||
}
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<mat-slide-toggle [(ngModel)]="tournamentPlayer.present" (change)="playerPresent($event, tournamentPlayer.playerId)">
|
|
||||||
@if (tournamentPlayer.present) {
|
|
||||||
Aanwezig
|
|
||||||
} @else {
|
|
||||||
Nog niet aanwezig
|
|
||||||
}
|
|
||||||
</mat-slide-toggle>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
</mat-tab>
|
|
||||||
</mat-tab-group>
|
|
||||||
</mat-tab>
|
</mat-tab>
|
||||||
}
|
}
|
||||||
</mat-tab-group>
|
</mat-tab-group>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
td {
|
td {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
td, th {
|
td, th {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
@@ -8,6 +9,7 @@ td, th {
|
|||||||
table.wide td, table.wide th {
|
table.wide td, table.wide th {
|
||||||
height: 4em;
|
height: 4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.winner {
|
.winner {
|
||||||
color: green;
|
color: green;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
@@ -24,3 +26,19 @@ td.w-sep {
|
|||||||
td.w-fill {
|
td.w-fill {
|
||||||
width: 35%;
|
width: 35%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-90 {
|
||||||
|
width: 90% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-95 {
|
||||||
|
width: 95% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-menu-panel {
|
||||||
|
z-index: 1000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-content-wrapper {
|
||||||
|
margin: 0 4px 0 4px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import {Component, inject, Input, OnInit} from '@angular/core';
|
import {Component, inject, OnDestroy, OnInit} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
MatAccordion,
|
MatAccordion,
|
||||||
MatExpansionPanel,
|
MatExpansionPanel,
|
||||||
|
MatExpansionPanelActionRow,
|
||||||
MatExpansionPanelHeader,
|
MatExpansionPanelHeader,
|
||||||
MatExpansionPanelTitle
|
MatExpansionPanelTitle
|
||||||
} from "@angular/material/expansion";
|
} from "@angular/material/expansion";
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
||||||
import {CurrencyPipe, DatePipe, DecimalPipe, NgClass, NgForOf, NgIf} from "@angular/common";
|
import {DatePipe, DecimalPipe, NgClass} from "@angular/common";
|
||||||
import {TeamPipe} from "../../pipes/team-pipe";
|
import {TeamPipe} from "../../pipes/team-pipe";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
@@ -16,7 +17,7 @@ import {MatButton, MatIconButton} from "@angular/material/button";
|
|||||||
import {MatIcon} from "@angular/material/icon";
|
import {MatIcon} from "@angular/material/icon";
|
||||||
import {Group} from "../../model/group";
|
import {Group} from "../../model/group";
|
||||||
import {Round} from "../../model/round";
|
import {Round} from "../../model/round";
|
||||||
import {MatMenu, MatMenuContent, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
||||||
import {Match} from "../../model/match";
|
import {Match} from "../../model/match";
|
||||||
import {FormsModule} from "@angular/forms";
|
import {FormsModule} from "@angular/forms";
|
||||||
import {MatTab, MatTabChangeEvent, MatTabGroup, MatTabLabel} from "@angular/material/tabs";
|
import {MatTab, MatTabChangeEvent, MatTabGroup, MatTabLabel} from "@angular/material/tabs";
|
||||||
@@ -25,13 +26,17 @@ import {MatDialog} from "@angular/material/dialog";
|
|||||||
import {MatchResultPipe} from "../../pipes/match-result-pipe";
|
import {MatchResultPipe} from "../../pipes/match-result-pipe";
|
||||||
import {Event} from "../../model/event";
|
import {Event} from "../../model/event";
|
||||||
import {TournamentValidateComponent} from "../tournament-validate/tournament-validate.component";
|
import {TournamentValidateComponent} from "../tournament-validate/tournament-validate.component";
|
||||||
import {Strength} from "../../model/player";
|
import {Player, Strength} from "../../model/player";
|
||||||
import {MatSlideToggle, MatSlideToggleChange} from "@angular/material/slide-toggle";
|
|
||||||
import {MatSnackBar} from "@angular/material/snack-bar";
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
import {CourtSelectionComponent} from "../court-selection/court-selection.component";
|
import {CourtSelectionComponent} from "../court-selection/court-selection.component";
|
||||||
import {Standings} from "../../model/standings";
|
import {Standings} from "../../model/standings";
|
||||||
import {Title} from '@angular/platform-browser';
|
import {HeaderService} from "../../service/header.service";
|
||||||
import {TitleService} from "../../service/title.service";
|
import {TournamentPlayersComponent} from "../tournament-players/tournament-players.component";
|
||||||
|
import {TournamentPlayer} from "../../model/tournamentPlayer";
|
||||||
|
import {TeamDisplayComponent} from "../team-display/team-display.component";
|
||||||
|
import {CounterSelectionComponent} from "../counter-selection/counter-selection.component";
|
||||||
|
import {Observable, Subscription, tap} from "rxjs";
|
||||||
|
import {MatTooltip} from '@angular/material/tooltip';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tournament-manage',
|
selector: 'app-tournament-manage',
|
||||||
@@ -44,8 +49,6 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatExpansionPanel,
|
MatExpansionPanel,
|
||||||
MatExpansionPanelHeader,
|
MatExpansionPanelHeader,
|
||||||
MatExpansionPanelTitle,
|
MatExpansionPanelTitle,
|
||||||
NgForOf,
|
|
||||||
NgIf,
|
|
||||||
TeamPipe,
|
TeamPipe,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
NgClass,
|
NgClass,
|
||||||
@@ -61,9 +64,10 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatIconButton,
|
MatIconButton,
|
||||||
DecimalPipe,
|
DecimalPipe,
|
||||||
TournamentValidateComponent,
|
TournamentValidateComponent,
|
||||||
MatSlideToggle,
|
TournamentPlayersComponent,
|
||||||
CurrencyPipe,
|
MatExpansionPanelActionRow,
|
||||||
MatMenuContent
|
TeamDisplayComponent,
|
||||||
|
MatTooltip,
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
FullNamePipe,
|
FullNamePipe,
|
||||||
@@ -71,11 +75,12 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatchResultPipe
|
MatchResultPipe
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-manage.component.html',
|
templateUrl: './tournament-manage.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-manage.component.scss'
|
styleUrl: './tournament-manage.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentManageComponent implements OnInit {
|
export class TournamentManageComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
@Input() tournament: Tournament;
|
tournament: Tournament;
|
||||||
|
|
||||||
activeRoundTab: number = 0;
|
activeRoundTab: number = 0;
|
||||||
|
|
||||||
@@ -84,8 +89,7 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
private _snackBar: MatSnackBar,
|
private _snackBar: MatSnackBar,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService,
|
private headerService: HeaderService,
|
||||||
private title: Title
|
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,11 +100,24 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
this.activeRoundTab = params['tab'];
|
this.activeRoundTab = params['tab'];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.tournamentService.getById(Number(id)).subscribe(data => {
|
this.loadTournament(
|
||||||
this.tournament = data;
|
this.tournamentService.getById(Number(id)),
|
||||||
this.titleService.setTitle(this.tournament.name);
|
(tournament) => this.headerService.setTitle(tournament.name)
|
||||||
this.title.setTitle(this.tournament.name);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.headerService.clearTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
getDuration(startTime: string | Date): Date {
|
||||||
|
const start = new Date(startTime);
|
||||||
|
const now = new Date();
|
||||||
|
const diffInMs = now.getTime() - start.getTime();
|
||||||
|
|
||||||
|
// Convert milliseconds to a Date object starting from epoch
|
||||||
|
// This allows the date pipe to format it as mm:ss
|
||||||
|
return new Date(diffInMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRoundTabChange(event: MatTabChangeEvent) {
|
onRoundTabChange(event: MatTabChangeEvent) {
|
||||||
@@ -112,7 +129,7 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getRoundIcon(status: String) {
|
getRoundIcon(status: string) {
|
||||||
if (status == "FINISHED") {
|
if (status == "FINISHED") {
|
||||||
return "check";
|
return "check";
|
||||||
} else if (status == "IN_PROGRESS") {
|
} else if (status == "IN_PROGRESS") {
|
||||||
@@ -137,87 +154,120 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
startRound(round: Round) {
|
startRound(round: Round) {
|
||||||
this.tournamentService.startRound(this.tournament.id, round.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.startRound(this.tournament.id, round.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finishRound(round: Round) {
|
finishRound(round: Round) {
|
||||||
this.tournamentService.finishRound(this.tournament.id, round.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.finishRound(this.tournament.id, round.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
finishGroup(group: Group) {
|
finishGroup(group: Group) {
|
||||||
this.tournamentService.finishGroup(this.tournament.id, group.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.finishGroup(this.tournament.id, group.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reopenGroup(group: Group) {
|
reopenGroup(group: Group) {
|
||||||
this.tournamentService.reopenGroup(this.tournament.id, group.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.reopenGroup(this.tournament.id, group.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
divideTournament() {
|
divideTournament() {
|
||||||
this.tournamentService.divide(this.tournament.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.divide(this.tournament.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
clearDivision() {
|
clearDivision() {
|
||||||
this.tournamentService.clearDivision(this.tournament.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.clearDivision(this.tournament.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTournament() {
|
drawTournament() {
|
||||||
this.tournamentService.draw(this.tournament.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.draw(this.tournament.id));
|
||||||
this.tournament = data;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
startMatch(match: Match) {
|
startMatch(match: Match) {
|
||||||
const availableCourts = this.getAvailableCourts();
|
const availableCourts = this.getAvailableCourts();
|
||||||
|
|
||||||
if (availableCourts.length == 0) {
|
if (availableCourts.length == 0) {
|
||||||
alert('Geen banen beschikbaar!');
|
this._snackBar.open('Geen banen beschikbaar.')
|
||||||
} else if (this.matchContainsPlayersThatArePlaying(match)) {
|
} else if (this.matchContainsPlayersThatArePlaying(match)) {
|
||||||
alert('Deze wedstrijd bevat spelers die al aan het spelen zijn!');
|
this._snackBar.open('Deze wedstrijd bevat spelers die al aan het spelen zijn.')
|
||||||
|
} else if (this.matchContainsPlayersThatAreCounting(match)) {
|
||||||
|
this._snackBar.open('Deze wedstrijd bevat spelers die aan het tellen zijn.')
|
||||||
} else {
|
} else {
|
||||||
this.courtSelectionDialog.open(CourtSelectionComponent, {
|
this.courtSelectionDialog.open(CourtSelectionComponent, {
|
||||||
data: {
|
data: {
|
||||||
match: match,
|
match: match,
|
||||||
availableCourts: this.getAvailableCourts(),
|
availableCourts: this.getAvailableCourts(),
|
||||||
totalCourts: this.tournament.courts
|
totalCourts: this.tournament.courts,
|
||||||
|
availableCounters: this.getAvailableCounters(match)
|
||||||
},
|
},
|
||||||
minWidth: '800px',
|
minWidth: '800px',
|
||||||
minHeight: '250px'
|
minHeight: '250px'
|
||||||
}).afterClosed().subscribe(result => {
|
}).afterClosed().subscribe(result => {
|
||||||
if (result != undefined) {
|
if (result != undefined) {
|
||||||
this.tournamentService.startMatch(this.tournament.id, match.id, result).subscribe(data => {
|
this.loadTournament(this.tournamentService.startMatch(this.tournament.id, match.id, result.court, result.counter.playerId));
|
||||||
this.tournament = data;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
matchContainsPlayersThatArePlaying(match: Match): boolean {
|
matchContainsPlayersThatArePlaying(match: Match): boolean {
|
||||||
let activePlayers: number[] = [];
|
|
||||||
for (let activeMatch of this.activeMatches()) {
|
|
||||||
activePlayers.push(activeMatch.match.team1.player1.id);
|
|
||||||
if (activeMatch.match.team1.player2) activePlayers.push(activeMatch.match.team1.player2.id);
|
|
||||||
activePlayers.push(activeMatch.match.team2.player1.id);
|
|
||||||
if (activeMatch.match.team2.player2) activePlayers.push(activeMatch.match.team2.player2.id);
|
|
||||||
}
|
|
||||||
let matchPlayers: number[] = [];
|
let matchPlayers: number[] = [];
|
||||||
matchPlayers.push(match.team1.player1.id);
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player1, match.type).id);
|
||||||
if (match.team1.player2) matchPlayers.push(match.team1.player2.id);
|
if (match.team1.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player2, match.type).id);
|
||||||
matchPlayers.push(match.team2.player1.id);
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player1, match.type).id);
|
||||||
if (match.team2.player2) matchPlayers.push(match.team2.player2.id);
|
if (match.team2.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player2, match.type).id);
|
||||||
let playersThatArePlaying = activePlayers.filter(Set.prototype.has, new Set(matchPlayers));
|
|
||||||
return playersThatArePlaying.length > 0;
|
let matchPlayersThatArePlaying = this.tournament.playersPlaying.filter(Set.prototype.has, new Set(matchPlayers));
|
||||||
|
return matchPlayersThatArePlaying.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
matchPlayersThatArePlaying(match: Match): number[] {
|
||||||
|
let matchPlayers: number[] = [];
|
||||||
|
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player1, match.type).id);
|
||||||
|
if (match.team1.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player2, match.type).id);
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player1, match.type).id);
|
||||||
|
if (match.team2.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player2, match.type).id);
|
||||||
|
|
||||||
|
return this.tournament.playersPlaying.filter(Set.prototype.has, new Set(matchPlayers));
|
||||||
|
}
|
||||||
|
|
||||||
|
matchContainsPlayersThatAreCounting(match: Match): boolean {
|
||||||
|
let matchPlayers: number[] = [];
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player1, match.type).id);
|
||||||
|
if (match.team1.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player2, match.type).id);
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player1, match.type).id);
|
||||||
|
if (match.team2.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player2, match.type).id);
|
||||||
|
|
||||||
|
let matchPlayersThatAreCounting = this.tournament.playersCounting.filter(Set.prototype.has, new Set(matchPlayers));
|
||||||
|
return matchPlayersThatAreCounting.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
matchPlayersThatAreCounting(match: Match): number[] {
|
||||||
|
let matchPlayers: number[] = [];
|
||||||
|
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player1, match.type).id);
|
||||||
|
if (match.team1.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player2, match.type).id);
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player1, match.type).id);
|
||||||
|
if (match.team2.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player2, match.type).id);
|
||||||
|
|
||||||
|
return this.tournament.playersCounting.filter(Set.prototype.has, new Set(matchPlayers));
|
||||||
|
}
|
||||||
|
|
||||||
|
getTournamentPlayerFromPlayer(player: Player): TournamentPlayer {
|
||||||
|
for (let tournamentPlayer of this.tournament.tournamentPlayers) {
|
||||||
|
if (tournamentPlayer.playerId == player.id) {
|
||||||
|
return tournamentPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null!;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTournamentPlayerFromId(tournamentPlayerId: number): TournamentPlayer {
|
||||||
|
for (let tournamentPlayer of this.tournament.tournamentPlayers) {
|
||||||
|
if (tournamentPlayer.id == tournamentPlayerId) {
|
||||||
|
return tournamentPlayer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null!;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAvailableCourts(): number[] {
|
getAvailableCourts(): number[] {
|
||||||
@@ -230,28 +280,42 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
return courts.filter(court => activeCourts.indexOf(court) < 0);
|
return courts.filter(court => activeCourts.indexOf(court) < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAvailableCounters(match: Match): TournamentPlayer[] {
|
||||||
|
const matchPlayers: number[] = [];
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player1, match.type).id);
|
||||||
|
if (match.team1.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team1.player2, match.type).id);
|
||||||
|
matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player1, match.type).id);
|
||||||
|
if (match.team2.player2) matchPlayers.push(this.getPlayerOrSubstitute(match.team2.player2, match.type).id);
|
||||||
|
|
||||||
|
const counterIds = this.tournament.playersAvailable.filter(player => !matchPlayers.includes(player));
|
||||||
|
|
||||||
|
return counterIds.map(id => this.getTournamentPlayerFromId(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlayerOrSubstitute(player: Player, type: String): TournamentPlayer {
|
||||||
|
return this.getSubstituteForEvent(player, type) || this.getTournamentPlayerFromPlayer(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSubstituteForEvent(player: Player, type: String): TournamentPlayer | undefined {
|
||||||
|
const tournamentPlayer = this.tournament.tournamentPlayers.find(
|
||||||
|
tp => tp.playerId === player.id
|
||||||
|
);
|
||||||
|
if (!tournamentPlayer) return undefined;
|
||||||
|
|
||||||
|
const substitution = tournamentPlayer.substitutions.find(
|
||||||
|
s => s.event === type
|
||||||
|
);
|
||||||
|
if (!substitution) return undefined;
|
||||||
|
|
||||||
|
return this.getTournamentPlayerFromId(substitution.substitute);
|
||||||
|
}
|
||||||
|
|
||||||
stopMatch(match: Match) {
|
stopMatch(match: Match) {
|
||||||
this.tournamentService.stopMatch(this.tournament.id, match.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.stopMatch(this.tournament.id, match.id));
|
||||||
this.tournament = data;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newRound(group: Group) {
|
newRound(group: Group) {
|
||||||
this.tournamentService.newRound(this.tournament.id, group.id).subscribe(data => {
|
this.loadTournament(this.tournamentService.newRound(this.tournament.id, group.id));
|
||||||
this.tournament = data;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
playerPaid($event: MatSlideToggleChange, playerId: number) {
|
|
||||||
this.tournamentService.playerPaid(this.tournament.id, playerId, $event.checked).subscribe(() => {
|
|
||||||
this._snackBar.open('Opgeslagen.');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
playerPresent($event: MatSlideToggleChange, playerId: number) {
|
|
||||||
this.tournamentService.playerPresent(this.tournament.id, playerId, $event.checked).subscribe(() => {
|
|
||||||
this._snackBar.open('Opgeslagen.');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getStrength(strength: string | undefined) {
|
getStrength(strength: string | undefined) {
|
||||||
@@ -277,7 +341,7 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
for (const round of group.rounds) {
|
for (const round of group.rounds) {
|
||||||
for (const match of round.matches) {
|
for (const match of round.matches) {
|
||||||
if (match.status == 'IN_PROGRESS') {
|
if (match.status == 'IN_PROGRESS') {
|
||||||
matches.push(new ActiveMatch(match, round, group));
|
matches.push(new ActiveMatch(match, round, group, event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -308,16 +372,15 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
|
|
||||||
matchResultDialog = inject(MatDialog);
|
matchResultDialog = inject(MatDialog);
|
||||||
courtSelectionDialog = inject(MatDialog);
|
courtSelectionDialog = inject(MatDialog);
|
||||||
|
counterSelectionDialog = inject(MatDialog);
|
||||||
|
|
||||||
editResult(match: Match, group: Group, round: Round) {
|
editResult(match: Match, event: Event, group: Group, round: Round) {
|
||||||
this.matchResultDialog.open(MatchResultComponent, {
|
this.matchResultDialog.open(MatchResultComponent, {
|
||||||
data: {match: match, group: group, round: round},
|
data: {match: match, tournament: this.tournament, event: event, group: group, round: round},
|
||||||
minWidth: '800px'
|
minWidth: '800px'
|
||||||
}).afterClosed().subscribe(result => {
|
}).afterClosed().subscribe(result => {
|
||||||
if (result != undefined) {
|
if (result != undefined) {
|
||||||
this.tournamentService.saveResult(this.tournament.id, result.matchId, result).subscribe(data => {
|
this.loadTournament(this.tournamentService.saveResult(this.tournament.id, result.matchId, result));
|
||||||
this.tournament = data;
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -367,16 +430,97 @@ export class TournamentManageComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
changeCounter(match: Match) {
|
||||||
|
this.counterSelectionDialog.open(CounterSelectionComponent, {
|
||||||
|
data: {
|
||||||
|
match: match,
|
||||||
|
availableCounters: this.getAvailableCounters(match)
|
||||||
|
},
|
||||||
|
minWidth: '800px',
|
||||||
|
minHeight: '250px'
|
||||||
|
}).afterClosed().subscribe(result => {
|
||||||
|
if (result != undefined) {
|
||||||
|
this.loadTournament(this.tournamentService.updateCounter(this.tournament.id, match.id, result.counter.playerId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadTournament(
|
||||||
|
observable: Observable<Tournament>,
|
||||||
|
afterLoad?: (tournament: Tournament) => void
|
||||||
|
): Subscription {
|
||||||
|
return observable.subscribe(data => {
|
||||||
|
this.tournament = data;
|
||||||
|
this.updateMatchAvailability();
|
||||||
|
afterLoad?.(data); // Optional chaining
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateMatchAvailability() {
|
||||||
|
for (const event of this.tournament.events) {
|
||||||
|
for (const group of event.groups) {
|
||||||
|
for (const round of group.rounds) {
|
||||||
|
for (const match of round.matches) {
|
||||||
|
let canStart = true;
|
||||||
|
let matchPlayersThatArePlaying = this.matchPlayersThatArePlaying(match);
|
||||||
|
let matchPlayersThatAreCounting = this.matchPlayersThatAreCounting(match);
|
||||||
|
|
||||||
|
let cantStartReason = "";
|
||||||
|
|
||||||
|
if (matchPlayersThatArePlaying.length > 0) {
|
||||||
|
canStart = false;
|
||||||
|
cantStartReason = this.joinWithEn(matchPlayersThatArePlaying.map(m => this.getTournamentPlayerFromId(m).name));
|
||||||
|
if (matchPlayersThatArePlaying.length == 1) {
|
||||||
|
cantStartReason += " is al aan het spelen";
|
||||||
|
} else {
|
||||||
|
cantStartReason += " zijn al aan het spelen";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchPlayersThatAreCounting.length > 0) {
|
||||||
|
canStart = false;
|
||||||
|
if (cantStartReason.length > 0) {
|
||||||
|
cantStartReason += " en ";
|
||||||
|
}
|
||||||
|
cantStartReason += this.joinWithEn(matchPlayersThatAreCounting.map(m => this.getTournamentPlayerFromId(m).name));
|
||||||
|
if (matchPlayersThatAreCounting.length == 1) {
|
||||||
|
cantStartReason += " is aan het tellen";
|
||||||
|
} else {
|
||||||
|
cantStartReason += " zijn aan het tellen";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match.canStart = canStart;
|
||||||
|
match.cantStartReason = cantStartReason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
joinWithEn(items: string[], separator = ", "): string {
|
||||||
|
const len = items.length;
|
||||||
|
|
||||||
|
if (len === 0) return "";
|
||||||
|
if (len === 1) return items[0];
|
||||||
|
if (len === 2) return items.join(" en ");
|
||||||
|
|
||||||
|
return items.slice(0, -1).join(separator) + `${separator}and ${items[len - 1]}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActiveMatch {
|
class ActiveMatch {
|
||||||
constructor(match: Match, round: Round, group: Group) {
|
constructor(match: Match, round: Round, group: Group, event: Event) {
|
||||||
this.match = match;
|
this.match = match;
|
||||||
this.round = round;
|
this.round = round;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
|
this.event = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
match: Match;
|
match: Match;
|
||||||
round: Round;
|
round: Round;
|
||||||
group: Group;
|
group: Group;
|
||||||
|
event: Event;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
@if (tournament) {
|
||||||
|
<mat-tab-group animationDuration="0ms" disableRipple="true">
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<mat-icon>euro</mat-icon>
|
||||||
|
Administratie
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<table class="table table-hover w-75 m-4">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Naam</th>
|
||||||
|
<th>Onderdelen</th>
|
||||||
|
<th>Kosten</th>
|
||||||
|
<th>Betaald</th>
|
||||||
|
@if (tournament.status == 'ONGOING') {
|
||||||
|
<th>Aanwezig</th>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (tournamentPlayer of tournament.tournamentPlayers; track tournamentPlayer.playerId) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ tournamentPlayer.name }}</td>
|
||||||
|
<td>
|
||||||
|
@for (event of tournamentPlayer.events; track event) {
|
||||||
|
{{ event }}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{ tournament.costsPerEvent[tournamentPlayer.events.length - 1] | currency:'EUR':'symbol':'1.2-2':'nl' }}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<mat-slide-toggle [(ngModel)]="tournamentPlayer.paid" (change)="playerPaid($event, tournamentPlayer.playerId)">
|
||||||
|
@if (tournamentPlayer.paid) {
|
||||||
|
Betaald
|
||||||
|
} @else {
|
||||||
|
Nog niet betaald
|
||||||
|
}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</td>
|
||||||
|
@if (tournament.status == 'ONGOING') {
|
||||||
|
<td>
|
||||||
|
<mat-slide-toggle [(ngModel)]="tournamentPlayer.present" (change)="playerPresent($event, tournamentPlayer.playerId)">
|
||||||
|
@if (tournamentPlayer.present) {
|
||||||
|
Aanwezig
|
||||||
|
} @else {
|
||||||
|
Nog niet aanwezig
|
||||||
|
}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</mat-tab>
|
||||||
|
@if (tournament.status == 'ONGOING') {
|
||||||
|
<mat-tab>
|
||||||
|
<ng-template mat-tab-label>
|
||||||
|
<mat-icon>group</mat-icon>
|
||||||
|
Tellers en invallers
|
||||||
|
</ng-template>
|
||||||
|
|
||||||
|
<table class="table table-hover w-75 m-4">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Naam</th>
|
||||||
|
<th>Wedstrijden geteld</th>
|
||||||
|
<th>Onderdelen</th>
|
||||||
|
<th>Invallers</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (tournamentPlayer of tournament.tournamentPlayers; track tournamentPlayer.playerId) {
|
||||||
|
<tr>
|
||||||
|
<td>{{ tournamentPlayer.name }}</td>
|
||||||
|
<td>{{ tournamentPlayer.counts }}</td>
|
||||||
|
<td>
|
||||||
|
@for (event of tournamentPlayer.events; track event) {
|
||||||
|
{{ event }}
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td>{{ hasSubstitutes(tournamentPlayer) ? 'Ja' : 'Nee' }}</td>
|
||||||
|
<td>
|
||||||
|
<button mat-icon-button [matMenuTriggerFor]="dividedTournamentMenu" class="menu-button">
|
||||||
|
<mat-icon>more_vert</mat-icon>
|
||||||
|
</button>
|
||||||
|
<mat-menu #dividedTournamentMenu="matMenu">
|
||||||
|
<button mat-menu-item (click)="findSubstitute(tournamentPlayer)">
|
||||||
|
<mat-icon>autorenew</mat-icon>
|
||||||
|
Invallers
|
||||||
|
</button>
|
||||||
|
<button mat-menu-item> <!--(click)="drawTournament()"-->
|
||||||
|
<mat-icon>do_not_disturb_on</mat-icon>
|
||||||
|
Deelname stoppen
|
||||||
|
</button>
|
||||||
|
</mat-menu>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</mat-tab>
|
||||||
|
}
|
||||||
|
</mat-tab-group>
|
||||||
|
}
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
td, th {
|
td, th {
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import {Component, inject, Input, OnInit} from '@angular/core';
|
||||||
|
import {CurrencyPipe} from "@angular/common";
|
||||||
|
import {MatSlideToggle, MatSlideToggleChange} from "@angular/material/slide-toggle";
|
||||||
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
import {Tournament} from "../../model/tournament";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
import {MatSnackBar} from "@angular/material/snack-bar";
|
||||||
|
import {MatIcon} from "@angular/material/icon";
|
||||||
|
import {MatIconButton} from "@angular/material/button";
|
||||||
|
import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu";
|
||||||
|
import {MatDialog} from "@angular/material/dialog";
|
||||||
|
import {SubstituteSelectionComponent} from "../substitute-selection/substitute-selection.component";
|
||||||
|
import {TournamentPlayer} from "../../model/tournamentPlayer";
|
||||||
|
import {MatTab, MatTabGroup, MatTabLabel} from "@angular/material/tabs";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-tournament-players',
|
||||||
|
imports: [
|
||||||
|
CurrencyPipe,
|
||||||
|
MatSlideToggle,
|
||||||
|
FormsModule,
|
||||||
|
MatIcon,
|
||||||
|
MatIconButton,
|
||||||
|
MatMenu,
|
||||||
|
MatMenuItem,
|
||||||
|
MatMenuTrigger,
|
||||||
|
MatTab,
|
||||||
|
MatTabGroup,
|
||||||
|
MatTabLabel
|
||||||
|
],
|
||||||
|
templateUrl: './tournament-players.component.html',
|
||||||
|
standalone: true,
|
||||||
|
styleUrl: './tournament-players.component.scss'
|
||||||
|
})
|
||||||
|
export class TournamentPlayersComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() tournament: Tournament;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private tournamentService: TournamentService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private _snackBar: MatSnackBar,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!this.tournament) {
|
||||||
|
const id = Number(this.route.snapshot.paramMap.get('id'));
|
||||||
|
if (id) {
|
||||||
|
this.tournamentService.getById(Number(id)).subscribe(data => {
|
||||||
|
this.tournament = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playerPaid($event: MatSlideToggleChange, playerId: number) {
|
||||||
|
this.tournamentService.playerPaid(this.tournament.id, playerId, $event.checked).subscribe(() => {
|
||||||
|
this._snackBar.open('Opgeslagen.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
playerPresent($event: MatSlideToggleChange, playerId: number) {
|
||||||
|
this.tournamentService.playerPresent(this.tournament.id, playerId, $event.checked).subscribe(() => {
|
||||||
|
this._snackBar.open('Opgeslagen.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
substituteSelectionDialog = inject(MatDialog);
|
||||||
|
|
||||||
|
findSubstitute(player: TournamentPlayer) {
|
||||||
|
this.substituteSelectionDialog.open(SubstituteSelectionComponent, {
|
||||||
|
data: {
|
||||||
|
player: player,
|
||||||
|
availablePlayers: this.tournament.tournamentPlayers
|
||||||
|
},
|
||||||
|
minWidth: '800px',
|
||||||
|
minHeight: '250px'
|
||||||
|
}).afterClosed().subscribe(result => {
|
||||||
|
if (result != undefined) {
|
||||||
|
this.tournamentService.playerSubstitute(this.tournament.id, player.playerId, result.substitutions).subscribe(data => {
|
||||||
|
this.tournament = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
hasSubstitutes(tournamentPlayer: TournamentPlayer) {
|
||||||
|
return tournamentPlayer.substitutions.filter(s => s.substitute != null && s.substitute >= 0).length > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
<mat-card appearance="outlined" *ngIf="tournament">
|
@if (tournament) {
|
||||||
|
<mat-card appearance="outlined">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h5>Inschrijvingen voor {{ tournament.name }}</h5>
|
<h5>Inschrijvingen voor {{ tournament.name }}</h5>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-card *ngFor="let event of tournament.events" appearance="outlined" class="m-3">
|
@for (event of tournament.events; track event.id) {
|
||||||
|
<mat-card appearance="outlined" class="m-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h6>{{ TournamentEvent.getType(event.type) }} ({{ event.registrations.length}} inschrijvingen)</h6>
|
<h6>{{ TournamentEvent.getType(event.type) }} ({{ event.registrations.length}} inschrijvingen)</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
@@ -13,20 +15,28 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="w-25">Naam</th>
|
<th scope="col" class="w-25">Naam</th>
|
||||||
<th scope="col" class="w-25">Club</th>
|
<th scope="col" class="w-25">Club</th>
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-25">Partner</th>
|
@if (event.doublesEvent) {
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-25">Club</th>
|
<th scope="col" class="w-25">Partner</th>
|
||||||
|
<th scope="col" class="w-25">Club</th>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let registration of event.registrations">
|
@for (registration of event.registrations; track registration.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ registration.player | fullName }}</td>
|
<td class="align-middle">{{ registration.player | fullName }}</td>
|
||||||
<td class="align-middle">{{ registration.player.club }}</td>
|
<td class="align-middle">{{ registration.player.club }}</td>
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ registration.partner | fullName }}</td>
|
@if (event.doublesEvent) {
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ registration.partner?.club }}</td>
|
<td class="align-middle">{{ registration.partner?.club }}</td>
|
||||||
|
<td class="align-middle">{{ registration.partner | fullName }}</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
}
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
|||||||
import {Tournament} from "../../model/tournament";
|
import {Tournament} from "../../model/tournament";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
import {NgForOf, NgIf} from "@angular/common";
|
|
||||||
import {Event} from "../../model/event";
|
import {Event} from "../../model/event";
|
||||||
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
import {FullNamePipe} from "../../pipes/fullname-pipe";
|
||||||
import {TitleService} from "../../service/title.service";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tournament-registrations',
|
selector: 'app-tournament-registrations',
|
||||||
@@ -14,11 +12,10 @@ import {TitleService} from "../../service/title.service";
|
|||||||
MatCard,
|
MatCard,
|
||||||
MatCardHeader,
|
MatCardHeader,
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
NgForOf,
|
|
||||||
NgIf,
|
|
||||||
FullNamePipe
|
FullNamePipe
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-registrations.component.html',
|
templateUrl: './tournament-registrations.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-registrations.component.scss'
|
styleUrl: './tournament-registrations.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentRegistrationsComponent implements OnInit {
|
export class TournamentRegistrationsComponent implements OnInit {
|
||||||
@@ -29,11 +26,9 @@ export class TournamentRegistrationsComponent implements OnInit {
|
|||||||
private tournamentService: TournamentService,
|
private tournamentService: TournamentService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private titleService: TitleService
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.titleService.setTitle("Inschrijvingen");
|
|
||||||
const id = this.route.snapshot.paramMap.get('id');
|
const id = this.route.snapshot.paramMap.get('id');
|
||||||
this.tournamentService.getById(Number(id)).subscribe(data => {
|
this.tournamentService.getById(Number(id)).subscribe(data => {
|
||||||
this.tournament = data;
|
this.tournament = data;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@if (tournamentValidation && tournament) {
|
@if (tournamentValidation && tournament) {
|
||||||
<mat-card appearance="outlined" class="m-3">
|
<mat-card appearance="outlined" class="m-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h6>Toernooi</h6>
|
<h6>Toernooi ({{ getTournamentMatchCount(tournament)}} wedstrijden)</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-expansion-panel [disabled]="tournamentValidation.validations.length == 0">
|
<mat-expansion-panel [disabled]="tournamentValidation.validations.length == 0">
|
||||||
@@ -11,17 +11,20 @@
|
|||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let validation of tournamentValidation.validations">
|
@for (validation of tournamentValidation.validations; track validation) {
|
||||||
|
<li>
|
||||||
<mat-icon class="text-{{ getColorForSeverity(validation.severity) }}">{{ getIconForSeverity(validation.severity) }}</mat-icon>
|
<mat-icon class="text-{{ getColorForSeverity(validation.severity) }}">{{ getIconForSeverity(validation.severity) }}</mat-icon>
|
||||||
{{ validation.message }}
|
{{ validation.message }}
|
||||||
</li>
|
</li>
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
<mat-card *ngFor="let event of tournament.events" appearance="outlined" class="m-3">
|
@for (event of tournament.events; track event.id) {
|
||||||
|
<mat-card appearance="outlined" class="m-3">
|
||||||
<mat-card-header>
|
<mat-card-header>
|
||||||
<h6>{{ TournamentEvent.getType(event.type) }}</h6>
|
<h6>{{ TournamentEvent.getType(event.type) }} ({{ getEventMatchCount(event)}} wedstrijden)</h6>
|
||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-accordion multi="true">
|
<mat-accordion multi="true">
|
||||||
@@ -36,17 +39,23 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th scope="col" class="w-25">Naam</th>
|
<th scope="col" class="w-25">Naam</th>
|
||||||
<th scope="col" class="w-25">Club</th>
|
<th scope="col" class="w-25">Club</th>
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-25">Partner</th>
|
@if (event.doublesEvent) {
|
||||||
<th *ngIf="event.doublesEvent" scope="col" class="w-25">Club</th>
|
<th scope="col" class="w-25">Partner</th>
|
||||||
|
<th scope="col" class="w-25">Club</th>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr *ngFor="let registration of event.registrations">
|
@for (registration of event.registrations; track registration.id) {
|
||||||
|
<tr>
|
||||||
<td class="align-middle">{{ registration.player | fullName }}</td>
|
<td class="align-middle">{{ registration.player | fullName }}</td>
|
||||||
<td class="align-middle">{{ registration.player.club }}</td>
|
<td class="align-middle">{{ registration.player.club }}</td>
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ registration.partner | fullName }}</td>
|
@if (event.doublesEvent) {
|
||||||
<td *ngIf="event.doublesEvent" class="align-middle">{{ registration.partner?.club }}</td>
|
<td class="align-middle">{{ registration.partner | fullName }}</td>
|
||||||
|
<td class="align-middle">{{ registration.partner?.club }}</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
@@ -57,18 +66,16 @@
|
|||||||
</mat-panel-title>
|
</mat-panel-title>
|
||||||
</mat-expansion-panel-header>
|
</mat-expansion-panel-header>
|
||||||
<ul>
|
<ul>
|
||||||
<li *ngFor="let validation of getEventValidation(event.id)?.validations">
|
@for (validation of getEventValidation(event.id)?.validations; track validation) {
|
||||||
|
<li>
|
||||||
<mat-icon class="text-{{ getColorForSeverity(validation.severity) }}">{{ getIconForSeverity(validation.severity) }}</mat-icon>
|
<mat-icon class="text-{{ getColorForSeverity(validation.severity) }}">{{ getIconForSeverity(validation.severity) }}</mat-icon>
|
||||||
<player-link [validationMessage]="validation.message"></player-link>
|
<player-link [validationMessage]="validation.message"></player-link>
|
||||||
<!--
|
|
||||||
<div *ngIf="validation.message">
|
|
||||||
<app-player-link [validationMessage]="'validation.message'"></app-player-link>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</li>
|
</li>
|
||||||
|
}
|
||||||
</ul>
|
</ul>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
</mat-accordion>
|
</mat-accordion>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {Component, OnInit} from '@angular/core';
|
||||||
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
import {MatCard, MatCardContent, MatCardHeader} from "@angular/material/card";
|
||||||
import {Tournament} from "../../model/tournament";
|
import {Tournament} from "../../model/tournament";
|
||||||
import {NgForOf, NgIf} from "@angular/common";
|
|
||||||
import {Event} from "../../model/event";
|
import {Event} from "../../model/event";
|
||||||
import {TournamentService} from "../../service/tournament.service";
|
import {TournamentService} from "../../service/tournament.service";
|
||||||
import {ActivatedRoute, Router} from "@angular/router";
|
import {ActivatedRoute, Router} from "@angular/router";
|
||||||
@@ -24,17 +23,16 @@ import {PlayerLinkComponent} from "../player-link/player-link.component";
|
|||||||
MatCard,
|
MatCard,
|
||||||
MatCardHeader,
|
MatCardHeader,
|
||||||
MatCardContent,
|
MatCardContent,
|
||||||
NgForOf,
|
|
||||||
MatExpansionPanel,
|
MatExpansionPanel,
|
||||||
MatExpansionPanelTitle,
|
MatExpansionPanelTitle,
|
||||||
MatExpansionPanelHeader,
|
MatExpansionPanelHeader,
|
||||||
NgIf,
|
|
||||||
MatAccordion,
|
MatAccordion,
|
||||||
MatIcon,
|
MatIcon,
|
||||||
FullNamePipe,
|
FullNamePipe,
|
||||||
PlayerLinkComponent
|
PlayerLinkComponent
|
||||||
],
|
],
|
||||||
templateUrl: './tournament-validate.component.html',
|
templateUrl: './tournament-validate.component.html',
|
||||||
|
standalone: true,
|
||||||
styleUrl: './tournament-validate.component.scss'
|
styleUrl: './tournament-validate.component.scss'
|
||||||
})
|
})
|
||||||
export class TournamentValidateComponent implements OnInit {
|
export class TournamentValidateComponent implements OnInit {
|
||||||
@@ -101,4 +99,24 @@ export class TournamentValidateComponent implements OnInit {
|
|||||||
return hasErrors;
|
return hasErrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEventMatchCount(event: Event) {
|
||||||
|
let numTeams = 0;
|
||||||
|
if (event.doublesEvent) {
|
||||||
|
numTeams = event.registrations.length / 2;
|
||||||
|
} else {
|
||||||
|
numTeams = event.registrations.length;
|
||||||
|
}
|
||||||
|
let rounds = numTeams <= 4 ? 3 : 4;
|
||||||
|
let matchesPerRound = Math.trunc(numTeams / 2);
|
||||||
|
|
||||||
|
return rounds * matchesPerRound;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTournamentMatchCount(tournament: Tournament) {
|
||||||
|
let count = 0;
|
||||||
|
for (let event of tournament.events) {
|
||||||
|
count += this.getEventMatchCount(event);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export class Game {
|
export class Game {
|
||||||
|
id: number;
|
||||||
score1: number;
|
score1: number;
|
||||||
score2: number;
|
score2: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Team } from "./team";
|
import { Team } from "./team";
|
||||||
import {Game} from "./game";
|
import {Game} from "./game";
|
||||||
|
import {Player} from "./player";
|
||||||
|
|
||||||
export class Match {
|
export class Match {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -11,4 +12,7 @@ export class Match {
|
|||||||
endTime: Date;
|
endTime: Date;
|
||||||
games: Game[];
|
games: Game[];
|
||||||
court: number;
|
court: number;
|
||||||
|
counter: Player;
|
||||||
|
canStart?: boolean;
|
||||||
|
cantStartReason?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ export class Round {
|
|||||||
quit: Team[];
|
quit: Team[];
|
||||||
drawnOut: Team;
|
drawnOut: Team;
|
||||||
standings: Standings;
|
standings: Standings;
|
||||||
|
isFinalsRound: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {Player} from "./player";
|
|||||||
import {FullNamePipe} from "../pipes/fullname-pipe";
|
import {FullNamePipe} from "../pipes/fullname-pipe";
|
||||||
|
|
||||||
export class Team {
|
export class Team {
|
||||||
|
id: number;
|
||||||
player1: Player;
|
player1: Player;
|
||||||
player2: Player;
|
player2: Player | null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ export class Tournament {
|
|||||||
status: string;
|
status: string;
|
||||||
events: Event[];
|
events: Event[];
|
||||||
tournamentPlayers: TournamentPlayer[];
|
tournamentPlayers: TournamentPlayer[];
|
||||||
maxEvents: number;
|
maxEvents: number = 2;
|
||||||
costsPerEvent: number[] = [0, 0, 0];
|
costsPerEvent: number[] = [10, 20, 0];
|
||||||
courts: number;
|
courts: number;
|
||||||
|
active: boolean;
|
||||||
|
playersPlaying: number[];
|
||||||
|
playersCounting: number[];
|
||||||
|
playersAvailable: number[];
|
||||||
|
|
||||||
static getStatus(tournament: Tournament): string {
|
static getStatus(tournament: Tournament): string {
|
||||||
if (tournament.status == "CLOSED") return "Afgerond";
|
if (tournament.status == "CLOSED") return "Afgerond";
|
||||||
|
|||||||
@@ -1,8 +1,18 @@
|
|||||||
|
|
||||||
export class TournamentPlayer {
|
export class TournamentPlayer {
|
||||||
|
id: number;
|
||||||
playerId: number;
|
playerId: number;
|
||||||
name: string;
|
name: string;
|
||||||
events: string[];
|
events: string[];
|
||||||
paid: boolean;
|
paid: boolean;
|
||||||
present: boolean;
|
present: boolean;
|
||||||
|
counting: boolean;
|
||||||
|
counts: number;
|
||||||
|
substitutions: TournamentPlayerSubstitution[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TournamentPlayerSubstitution {
|
||||||
|
substitutionId: number;
|
||||||
|
event: string;
|
||||||
|
substitute: number = -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export class TournamentRegistration {
|
|||||||
editable: boolean;
|
editable: boolean;
|
||||||
date: string;
|
date: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
active: boolean;
|
||||||
events: EventRegistration[];
|
events: EventRegistration[];
|
||||||
|
|
||||||
static getStatus(tournamentRegistration: TournamentRegistration): string {
|
static getStatus(tournamentRegistration: TournamentRegistration): string {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import {FullNamePipe} from "../pipes/fullname-pipe";
|
import {FullNamePipe} from "./fullname-pipe";
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'teamText',
|
name: 'teamText',
|
||||||
standalone: true
|
standalone: true
|
||||||
@@ -10,10 +11,8 @@ export class TeamPipe implements PipeTransform {
|
|||||||
|
|
||||||
transform(team: any, args?: any): any {
|
transform(team: any, args?: any): any {
|
||||||
if (team.player2 != null) {
|
if (team.player2 != null) {
|
||||||
// return this.player1.getFullName() + " / " + this.player2.getFullName();
|
|
||||||
return this.fullNamePipe.transform(team.player1) + " / " + this.fullNamePipe.transform(team.player2);
|
return this.fullNamePipe.transform(team.player1) + " / " + this.fullNamePipe.transform(team.player2);
|
||||||
}
|
}
|
||||||
// return this.player1.getFullName();
|
|
||||||
return this.fullNamePipe.transform(team.player1);
|
return this.fullNamePipe.transform(team.player1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/app/service/header.service.ts
Normal file
16
src/app/service/header.service.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class HeaderService {
|
||||||
|
private headerSource = new BehaviorSubject<string | null>(null);
|
||||||
|
header$ = this.headerSource.asObservable();
|
||||||
|
|
||||||
|
setTitle(header: string) {
|
||||||
|
this.headerSource.next(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
clearTitle() {
|
||||||
|
this.headerSource.next(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { BehaviorSubject } from 'rxjs';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TitleService {
|
|
||||||
|
|
||||||
private title = new BehaviorSubject('');
|
|
||||||
currentTitle = this.title.asObservable();
|
|
||||||
|
|
||||||
constructor() { }
|
|
||||||
|
|
||||||
setTitle(message: string) {
|
|
||||||
this.title.next(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import {Tournament} from "../model/tournament";
|
|||||||
import {TournamentValidation} from "../model/tournamentValidation";
|
import {TournamentValidation} from "../model/tournamentValidation";
|
||||||
import {Result} from "../model/result";
|
import {Result} from "../model/result";
|
||||||
import { environment } from "../../environments/environment"
|
import { environment } from "../../environments/environment"
|
||||||
|
import {TournamentPlayerSubstitution} from "../model/tournamentPlayer";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -72,8 +73,8 @@ export class TournamentService {
|
|||||||
return this.http.post<Tournament>(`${this.tournamentsUrl}/${tournamentId}/groups/${groupId}/new`, null);
|
return this.http.post<Tournament>(`${this.tournamentsUrl}/${tournamentId}/groups/${groupId}/new`, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public startMatch(tournamentId: number, matchId: number, court: number): Observable<Tournament> {
|
public startMatch(tournamentId: number, matchId: number, court: number, counter: number): Observable<Tournament> {
|
||||||
return this.http.post<Tournament>(`${this.tournamentsUrl}/${tournamentId}/matches/${matchId}/start/${court}`, null);
|
return this.http.post<Tournament>(`${this.tournamentsUrl}/${tournamentId}/matches/${matchId}/start?court=${court}&counter=${counter}`, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopMatch(tournamentId: number, matchId: number): Observable<Tournament> {
|
public stopMatch(tournamentId: number, matchId: number): Observable<Tournament> {
|
||||||
@@ -92,6 +93,14 @@ export class TournamentService {
|
|||||||
return this.http.patch<void>(`${this.tournamentsUrl}/${tournamentId}/players/${playerId}/present/${paid}`, null);
|
return this.http.patch<void>(`${this.tournamentsUrl}/${tournamentId}/players/${playerId}/present/${paid}`, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public playerSubstitute(tournamentId: number, playerId: number, substitutions: TournamentPlayerSubstitution[]): Observable<Tournament> {
|
||||||
|
return this.http.post<Tournament>(`${this.tournamentsUrl}/${tournamentId}/players/${playerId}/substitutions`, substitutions)
|
||||||
|
}
|
||||||
|
|
||||||
|
public updateCounter(tournamentId: number, matchId: number, counter: number): Observable<Tournament> {
|
||||||
|
return this.http.patch<Tournament>(`${this.tournamentsUrl}/${tournamentId}/matches/${matchId}/update?counter=${counter}`, null);
|
||||||
|
}
|
||||||
|
|
||||||
public addTestData(): Observable<void> {
|
public addTestData(): Observable<void> {
|
||||||
return this.http.get<void>(`${environment.backendUrl}/testdata`);
|
return this.http.get<void>(`${environment.backendUrl}/testdata`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { bootstrapApplication } from '@angular/platform-browser';
|
import { bootstrapApplication, BootstrapContext } from '@angular/platform-browser';
|
||||||
import { AppComponent } from './app/app.component';
|
import { AppComponent } from './app/app.component';
|
||||||
import { config } from './app/app.config.server';
|
import { config } from './app/app.config.server';
|
||||||
|
|
||||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
const bootstrap = (context: BootstrapContext) => bootstrapApplication(AppComponent, config, context);
|
||||||
|
|
||||||
export default bootstrap;
|
export default bootstrap;
|
||||||
|
|||||||
@@ -2,3 +2,11 @@
|
|||||||
|
|
||||||
html, body { height: 100%; }
|
html, body { height: 100%; }
|
||||||
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
|
||||||
|
/*
|
||||||
|
.cdk-overlay-container {
|
||||||
|
z-index: 2000 !important;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.mat-tooltip {
|
||||||
|
white-space: pre-line !important;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user