- {{ period().value.start | amDateFormat: 'DD MMM YYYY' }} -
- {{ period().value.end | amDateFormat: 'DD MMM YYYY' }}
+
+ {{ filter.value.start | amDateFormat: 'DD MMM YYYY' }} -
+ {{ filter.value.end | amDateFormat: 'DD MMM YYYY' }}
verified_user
- {{ this.eventTournament().official ? 'Unmake' : 'Make' }} official
+ {{ this.eventTournament()?.official ? 'Unmake' : 'Make' }} official
sync
- Sync
+ {{ 'all.competition.menu.sync' | translate }}
+
+
+
+
+ sync
+ {{ 'all.button.re-sync' | translate }}
@@ -53,55 +61,60 @@
-
{{ this.eventTournament().name }}
+
{{ this.eventTournament()?.name }}
{{ 'all.tournament.enrollment.title' | translate }}:
{{
- (this.eventTournament().allowEnlisting ?? false
+ (this.eventTournament()?.allowEnlisting ?? false
? 'all.tournament.enrollment.open'
: 'all.tournament.enrollment.closed'
) | translate
}}
- @if (this.eventTournament().lastSync) {
+ @if (this.eventTournament()?.lastSync) {
{{ 'all.event.last-sync' | translate }}:
- {{ this.eventTournament().lastSync | amCalendar }}
+ {{ this.eventTournament()?.lastSync | amCalendar }}
}
-@if (this.subEvents()) {
-
- @for (type of this.subEvents(); track type) {
-
-
{{ 'all.game.types.long.' + type.eventType | translate }}
- @for (sub of type.subEvents; track sub) {
-
-
- {{ sub.name }} {{ sub.eventType }}
- @if ((sub?.rankingGroups?.length ?? 0) > 0) {
- *
+
+@if (loaded()) {
+ @if (this.subEvents()) {
+
+ @for (type of this.subEvents(); track type) {
+
+
{{ 'all.game.types.long.' + type.eventType | translate }}
+ @for (sub of type.subEvents; track sub) {
+
+
+ {{ sub.name }} {{ sub.eventType }}
+ @if ((sub?.rankingGroups?.length ?? 0) > 0) {
+ *
+ }
+
+
+
+
+ @for (draw of sub.drawTournaments; track draw) {
+
+
+ {{ draw.name }} ({{ draw.size }} entries)
+
+
}
-
-
-
-
- @for (draw of sub.drawTournaments; track draw) {
-
-
- {{ draw.name }} ({{ draw.size }} entries)
-
-
- }
-
-
- }
-
- }
-
+
+
+ }
+
+ }
+
+ }
+} @else {
+
}
diff --git a/libs/frontend/pages/tournament/src/pages/detail/detail.page.ts b/libs/frontend/pages/tournament/src/pages/detail/detail.page.ts
index 147a2fede1..07ae3d89db 100644
--- a/libs/frontend/pages/tournament/src/pages/detail/detail.page.ts
+++ b/libs/frontend/pages/tournament/src/pages/detail/detail.page.ts
@@ -1,6 +1,5 @@
import { CommonModule } from '@angular/common';
import { Component, computed, effect, inject } from '@angular/core';
-import { toSignal } from '@angular/core/rxjs-interop';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
@@ -12,7 +11,7 @@ import { MatInputModule } from '@angular/material/input';
import { MatMenuModule } from '@angular/material/menu';
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
import { MatTooltipModule } from '@angular/material/tooltip';
-import { ActivatedRoute, Router, RouterModule } from '@angular/router';
+import { Router, RouterModule } from '@angular/router';
import {
ConfirmDialogComponent,
ConfirmDialogModel,
@@ -20,16 +19,18 @@ import {
OpenCloseDateDialogComponent,
PageHeaderComponent,
} from '@badman/frontend-components';
-import { EventTournament, SubEventTournament } from '@badman/frontend-models';
+import { SubEventTournament } from '@badman/frontend-models';
import { JobsService } from '@badman/frontend-queue';
import { SeoService } from '@badman/frontend-seo';
import { sortSubEvents } from '@badman/utils';
import { TranslateModule } from '@ngx-translate/core';
-import { Apollo, gql } from 'apollo-angular';
import { MomentModule } from 'ngx-moment';
import { lastValueFrom } from 'rxjs';
import { BreadcrumbService } from 'xng-breadcrumb';
import { AssignRankingGroupsComponent } from '../../components';
+import { TournamentDetailService } from './detail.service';
+import { injectParams } from 'ngxtension/inject-params';
+import { MatProgressBarModule } from '@angular/material/progress-bar';
@Component({
selector: 'badman-tournament-detail',
@@ -55,28 +56,30 @@ import { AssignRankingGroupsComponent } from '../../components';
MatSnackBarModule,
PageHeaderComponent,
HasClaimComponent,
+ MatProgressBarModule,
],
})
export class DetailPageComponent {
- private readonly route = inject(ActivatedRoute);
private readonly breadcrumbService = inject(BreadcrumbService);
private readonly seoService = inject(SeoService);
private readonly router = inject(Router);
- private readonly apollo = inject(Apollo);
private readonly jobsService = inject(JobsService);
private readonly dialog = inject(MatDialog);
private readonly matSnackBar = inject(MatSnackBar);
- private routeData = toSignal(this.route.data);
+ private readonly detailService = inject(TournamentDetailService);
- eventTournament = computed(() => this.routeData()?.['eventTournament'] as EventTournament);
+ eventTournament = this.detailService.tournament;
+ loaded = this.detailService.loaded;
+ errors = this.detailService.error;
+ private readonly eventId = injectParams('id');
subEvents = computed(() => {
return this.eventTournament()
- .subEventTournaments?.sort(sortSubEvents)
+ ?.subEventTournaments?.sort(sortSubEvents)
?.reduce(
(acc, subEventTournament) => {
- const eventType = subEventTournament.eventType || 'Unknown';
+ const eventType = subEventTournament.eventType ?? 'Unknown';
const subEvents = acc.find((x) => x.eventType === eventType)?.subEvents;
if (subEvents) {
subEvents.push(subEventTournament);
@@ -91,7 +94,19 @@ export class DetailPageComponent {
constructor() {
effect(() => {
- const eventTournamentName = `${this.eventTournament().name}`;
+ const eventId = this.eventId();
+
+ if (!eventId) {
+ return;
+ }
+
+ this.detailService.filter.patchValue({
+ tournamentId: eventId,
+ });
+ });
+
+ effect(() => {
+ const eventTournamentName = `${this.eventTournament()?.name}`;
this.seoService.update({
title: eventTournamentName,
@@ -104,83 +119,53 @@ export class DetailPageComponent {
}
setOpenClose() {
+ if (!this.eventTournament()) {
+ return;
+ }
+
// open dialog
const ref = this.dialog.open(OpenCloseDateDialogComponent, {
data: {
- openDate: this.eventTournament().openDate,
- closeDate: this.eventTournament().closeDate,
+ openDate: this.eventTournament()?.openDate,
+ closeDate: this.eventTournament()?.closeDate,
},
width: '400px',
});
- ref.afterClosed().subscribe((result) => {
- if (result) {
- this.eventTournament().openDate = result.openDate;
- this.eventTournament().closeDate = result.closeDate;
-
- this.apollo
- .mutate({
- mutation: gql`
- mutation UpdateEventTournament($data: EventTournamentUpdateInput!) {
- updateEventTournament(data: $data) {
- id
- }
- }
- `,
- variables: {
- data: {
- id: this.eventTournament().id,
- openDate: this.eventTournament().openDate,
- closeDate: this.eventTournament().closeDate,
- },
- },
- })
- .subscribe(() => {
- this.matSnackBar.open(
- `Tournament ${this.eventTournament().name} open/close dates updated`,
- 'Close',
- {
- duration: 2000,
- },
- );
- });
- }
- });
- }
+ ref.afterClosed().subscribe(async (result) => {
+ if (result?.openDate || result?.closeDate) {
+ await this.detailService.state.setOpenCloseDates({
+ openDate: result?.openDate,
+ closeDate: result?.closeDate,
+ });
- makeOfficial() {
- this.eventTournament().official = !this.eventTournament().official;
- this.apollo
- .mutate({
- mutation: gql`
- mutation UpdateEventTournament($data: EventTournamentUpdateInput!) {
- updateEventTournament(data: $data) {
- id
- }
- }
- `,
- variables: {
- data: {
- id: this.eventTournament().id,
- official: this.eventTournament().official,
- },
- },
- })
- .subscribe(() => {
this.matSnackBar.open(
- `Tournament ${this.eventTournament().name} is ${this.eventTournament().official ? 'official' : 'unofficial'}`,
+ `Tournament ${this.eventTournament()?.name} open/close dates updated`,
'Close',
{
duration: 2000,
},
);
- });
+ }
+ });
+ }
+
+ async makeOfficial() {
+ await this.detailService.state.toggleOfficialStatus();
+
+ this.matSnackBar.open(
+ `Tournament ${this.eventTournament()?.name} is ${!this.eventTournament()?.official ? 'official' : 'unofficial'}`,
+ 'Close',
+ {
+ duration: 2000,
+ },
+ );
}
async syncEvent() {
- if (!this.eventTournament().visualCode) {
+ if (!this.eventTournament()?.visualCode) {
this.matSnackBar.open(
- `Tournament ${this.eventTournament().name} has no visual code, add it via the "add event" button in the overview page.`,
+ `Tournament ${this.eventTournament()?.name} has no visual code, add it via the "add event" button in the overview page.`,
'Close',
{
duration: 2000,
@@ -191,7 +176,7 @@ export class DetailPageComponent {
}
await lastValueFrom(
- this.jobsService.syncEventById({ id: this.eventTournament().visualCode as string }),
+ this.jobsService.syncEventById({ id: this.eventTournament()?.visualCode as string }),
);
}
@@ -221,30 +206,22 @@ export class DetailPageComponent {
data: dialogData,
});
- dialogRef.afterClosed().subscribe((dialogResult) => {
+ dialogRef.afterClosed().subscribe(async (dialogResult) => {
if (!dialogResult) {
return;
}
- this.apollo
- .mutate({
- mutation: gql`
- mutation RemoveTournament($id: ID!) {
- removeEventTournament(id: $id)
- }
- `,
- variables: {
- id: this.eventTournament().id,
- },
- refetchQueries: ['EventTournament'],
- })
- .subscribe(() => {
- this.matSnackBar.open('Deleted', undefined, {
- duration: 1000,
- panelClass: 'success',
- });
- this.router.navigate(['/tournament']);
- });
+ await this.detailService.state.removeTournament();
+
+ this.matSnackBar.open('Deleted', undefined, {
+ duration: 1000,
+ panelClass: 'success',
+ });
+ this.router.navigate(['/tournament']);
});
}
+
+ reCalculatePoints() {
+ this.detailService.state.reCalculatePoints();
+ }
}
diff --git a/libs/frontend/pages/tournament/src/pages/detail/detail.service.ts b/libs/frontend/pages/tournament/src/pages/detail/detail.service.ts
new file mode 100644
index 0000000000..d352f1a30e
--- /dev/null
+++ b/libs/frontend/pages/tournament/src/pages/detail/detail.service.ts
@@ -0,0 +1,240 @@
+import { Injectable, computed, inject } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { FetchPolicy } from '@apollo/client/core';
+import { EventTournament } from '@badman/frontend-models';
+import { Apollo, gql } from 'apollo-angular';
+import { signalSlice } from 'ngxtension/signal-slice';
+import { EMPTY, Observable, Subject, merge, of } from 'rxjs';
+import {
+ catchError,
+ delay,
+ distinctUntilChanged,
+ filter,
+ map,
+ startWith,
+ switchMap
+} from 'rxjs/operators';
+
+export const EVENT_QUERY = gql`
+ query EventTournament($id: ID!) {
+ eventTournament(id: $id) {
+ id
+ name
+ slug
+ openDate
+ closeDate
+ visualCode
+ lastSync
+ official
+ subEventTournaments {
+ id
+ name
+ eventType
+ level
+ rankingGroups {
+ id
+ name
+ }
+ drawTournaments {
+ id
+ name
+ size
+ }
+ }
+ }
+ }
+`;
+
+export interface TournamentDetailState {
+ tournament: EventTournament | null;
+ loaded: boolean;
+ error: string | null;
+}
+
+@Injectable({
+ providedIn: 'root',
+})
+export class TournamentDetailService {
+ private readonly apollo = inject(Apollo);
+
+ private initialState: TournamentDetailState = {
+ tournament: null,
+ loaded: false,
+ error: null,
+ };
+
+ filter = new FormGroup({
+ tournamentId: new FormControl
(''),
+ });
+
+ tournament = computed(() => this.state().tournament);
+ loaded = computed(() => this.state().loaded);
+ error = computed(() => this.state().error);
+
+ private filterChanged$ = this.filter.valueChanges.pipe(
+ startWith(this.filter.value),
+ filter((filter) => !!filter.tournamentId && filter.tournamentId.length > 0),
+ distinctUntilChanged(),
+ );
+
+ // sources
+ private error$ = new Subject();
+ private tournamentLoaded = this.filterChanged$.pipe(
+ // throttleTime(300),
+ switchMap((filter) =>
+ this.getTournament(filter.tournamentId).pipe(
+ map((tournament) => ({ tournament, loaded: true, error: null })),
+ startWith({ tournmaent: null, loaded: false, error: null }),
+ ),
+ ),
+ delay(100), // some delay to show the loading indicator
+ catchError((err) => {
+ this.error$.next(err);
+ return EMPTY;
+ }),
+ );
+
+ sources$ = merge(
+ this.tournamentLoaded,
+ this.error$.pipe(map((error) => ({ error }))),
+ this.filterChanged$.pipe(map(() => ({ loaded: false }))),
+ );
+
+ state = signalSlice({
+ initialState: this.initialState,
+ sources: [this.sources$],
+ selectors: (state) => ({
+ loadedAndError: () => {
+ return state().loaded && state().error;
+ },
+ }),
+ actionSources: {
+ toggleOfficialStatus: (_state, action$: Observable) =>
+ action$.pipe(
+ switchMap(() => this.toggleOfficialStatus(_state().tournament)),
+ switchMap(() =>
+ this.getTournament(_state().tournament?.id, 'no-cache').pipe(
+ map((tournament) => ({ tournament, loaded: true, error: null })),
+ startWith({ tournmaent: null, loaded: false, error: null }),
+ ),
+ ),
+ ),
+ setOpenCloseDates: (_state, action$: Observable<{ openDate: string; closeDate: string }>) =>
+ action$.pipe(
+ switchMap(({ openDate, closeDate }) =>
+ this.setOpenCloseDates(_state().tournament, openDate, closeDate),
+ ),
+ switchMap(() =>
+ this.getTournament(_state().tournament?.id, 'no-cache').pipe(
+ map((tournament) => ({ tournament, loaded: true, error: null })),
+ startWith({ tournmaent: null, loaded: false, error: null }),
+ ),
+ ),
+ ),
+ removeTournament: (_state, action$: Observable) =>
+ action$.pipe(
+ switchMap(() => this.removeTournament(_state().tournament)),
+ switchMap(() => of({ tournament: null, loaded: false, error: null })),
+ ),
+ reCalculatePoints: (_state, action$: Observable) =>
+ action$.pipe(
+ switchMap(() => this.reCalculatePoints(_state().tournament)),
+ map(() => _state()),
+ ),
+ },
+ });
+
+ private getTournament(
+ tournamentId?: string | null,
+ fetchPolicy?: FetchPolicy,
+ ): Observable {
+ if (!tournamentId) {
+ return of(null);
+ }
+
+ return this.apollo
+ .query<{ eventTournament: Partial }>({
+ fetchPolicy,
+ query: EVENT_QUERY,
+ variables: {
+ id: tournamentId,
+ },
+ })
+ .pipe(
+ map((result) => {
+ if (!result?.data.eventTournament) {
+ throw new Error('No tournament');
+ }
+
+ return new EventTournament(result.data.eventTournament);
+ }),
+ );
+ }
+
+ private toggleOfficialStatus(tournament: EventTournament | null) {
+ return this.apollo.mutate({
+ mutation: gql`
+ mutation UpdateEventTournament($data: EventTournamentUpdateInput!) {
+ updateEventTournament(data: $data) {
+ id
+ }
+ }
+ `,
+ variables: {
+ data: {
+ id: tournament?.id,
+ official: !tournament?.official,
+ },
+ },
+ });
+ }
+
+ private removeTournament(tournament: EventTournament | null) {
+ return this.apollo.mutate({
+ mutation: gql`
+ mutation RemoveTournament($id: ID!) {
+ removeEventTournament(id: $id)
+ }
+ `,
+ variables: {
+ id: tournament?.id,
+ },
+ });
+ }
+
+ private reCalculatePoints(tournament: EventTournament | null) {
+ return this.apollo.mutate({
+ mutation: gql`
+ mutation RecalculateEventTournamentRankingPoints($eventId: ID!) {
+ recalculateEventTournamentRankingPoints(eventId: $eventId)
+ }
+ `,
+ variables: {
+ id: tournament?.id,
+ },
+ });
+ }
+
+ private setOpenCloseDates(
+ tournament: EventTournament | null,
+ openDate: string,
+ closeDate: string,
+ ) {
+ return this.apollo.mutate({
+ mutation: gql`
+ mutation UpdateEventTournament($data: EventTournamentUpdateInput!) {
+ updateEventTournament(data: $data) {
+ id
+ }
+ }
+ `,
+ variables: {
+ data: {
+ id: tournament?.id,
+ openDate,
+ closeDate,
+ },
+ },
+ });
+ }
+}
diff --git a/libs/frontend/pages/tournament/src/resolver/event.resolver.ts b/libs/frontend/pages/tournament/src/resolver/event.resolver.ts
index a7e80f10ab..485babd644 100644
--- a/libs/frontend/pages/tournament/src/resolver/event.resolver.ts
+++ b/libs/frontend/pages/tournament/src/resolver/event.resolver.ts
@@ -3,7 +3,37 @@ import { ActivatedRouteSnapshot } from '@angular/router';
import { EventTournament } from '@badman/frontend-models';
import { transferState } from '@badman/frontend-utils';
import { Apollo, gql } from 'apollo-angular';
-import { first, map } from 'rxjs/operators';
+import { map } from 'rxjs/operators';
+
+export const EVENT_QUERY = gql`
+ query EventTournament($id: ID!) {
+ eventTournament(id: $id) {
+ id
+ name
+ slug
+ openDate
+ closeDate
+ visualCode
+ lastSync
+ official
+ subEventTournaments {
+ id
+ name
+ eventType
+ level
+ rankingGroups {
+ id
+ name
+ }
+ drawTournaments {
+ id
+ name
+ size
+ }
+ }
+ }
+ }
+`;
@Injectable()
export class EventResolver {
@@ -15,41 +45,13 @@ export class EventResolver {
const eventId = route.params['id'];
return this.apollo
- .query<{ eventTournament: Partial }>({
- query: gql`
- query EventTournament($id: ID!) {
- eventTournament(id: $id) {
- id
- name
- slug
- openDate
- closeDate
- visualCode
- lastSync
- official
- subEventTournaments {
- id
- name
- eventType
- level
- rankingGroups {
- id
- name
- }
- drawTournaments {
- id
- name
- size
- }
- }
- }
- }
- `,
+ .watchQuery<{ eventTournament: Partial }>({
+ query: EVENT_QUERY,
variables: {
id: eventId,
},
})
- .pipe(
+ .valueChanges.pipe(
transferState('eventKey-' + eventId, this.stateTransfer, this.platformId),
map((result) => {
if (!result?.data.eventTournament) {
@@ -57,8 +59,6 @@ export class EventResolver {
}
return new EventTournament(result.data.eventTournament);
}),
-
- first(),
);
}
}
diff --git a/libs/frontend/pages/tournament/src/tournament.module.ts b/libs/frontend/pages/tournament/src/tournament.module.ts
index da65c8c088..753da0fb92 100644
--- a/libs/frontend/pages/tournament/src/tournament.module.ts
+++ b/libs/frontend/pages/tournament/src/tournament.module.ts
@@ -11,6 +11,7 @@ const MODULE_ROUTES: Routes = [
},
{
path: ':id',
+ runGuardsAndResolvers: 'always',
resolve: {
eventTournament: EventResolver,
},
@@ -22,10 +23,12 @@ const MODULE_ROUTES: Routes = [
children: [
{
path: '',
+ runGuardsAndResolvers: 'always',
component: DetailPageComponent,
},
{
path: 'draw/:id',
+ runGuardsAndResolvers: 'always',
resolve: {
drawTournament: DrawResolver,
},
diff --git a/libs/frontend/pages/transfers/package.json b/libs/frontend/pages/transfers/package.json
index 26531ae49e..09214e0e63 100644
--- a/libs/frontend/pages/transfers/package.json
+++ b/libs/frontend/pages/transfers/package.json
@@ -1,6 +1,6 @@
{
"name": "@badman/frontend-transfers",
- "version": "6.166.3",
+ "version": "6.168.3",
"peerDependencies": {
"@angular/common": "18.1.0",
"@angular/core": "18.1.0",
diff --git a/libs/utils/package.json b/libs/utils/package.json
index 852c735c35..6de63eff0b 100644
--- a/libs/utils/package.json
+++ b/libs/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@badman/utils",
- "version": "6.166.3",
+ "version": "6.168.3",
"dependencies": {
"joi": "^17.12.1",
"moment": "^2.30.1",
diff --git a/libs/utils/src/lib/enums/index.ts b/libs/utils/src/lib/enums/index.ts
index 61887554b6..8024a419c8 100644
--- a/libs/utils/src/lib/enums/index.ts
+++ b/libs/utils/src/lib/enums/index.ts
@@ -1,4 +1,3 @@
-// start:ng42.barrel
export * from './changeEncounterAvailibilty.enum';
export * from './clubMembershipType.enum';
export * from './drawType.enum';
@@ -6,6 +5,7 @@ export * from './eventType.enum';
export * from './gameStatus.enum';
export * from './gameType.enum';
export * from './levelType.enum';
+export * from './loggingAction.enum';
export * from './notificationType.enum';
export * from './rankingsSystemsType.enum';
export * from './rankingTiming.enum';
@@ -15,5 +15,3 @@ export * from './subEventType.enum';
export * from './teamMembershipType.enum';
export * from './usedRankingTiming.enum';
export * from './useForTeams.enum';
-export * from './loggingAction.enum';
-// end:ng42.barrel
diff --git a/libs/utils/src/lib/game-label.ts b/libs/utils/src/lib/game-label.ts
index 363b5311c7..d3c5e85c27 100644
--- a/libs/utils/src/lib/game-label.ts
+++ b/libs/utils/src/lib/game-label.ts
@@ -1,5 +1,7 @@
+import { SubEventTypeEnum } from './enums';
+
export const gameLabel = (
- gameType: 'MX' | 'M' | 'F',
+ gameType: SubEventTypeEnum,
gameNumber: number,
): [
'all.game.types.short.male' | 'all.game.types.short.female' | 'all.game.types.short.mix',
diff --git a/libs/utils/src/lib/get-rankingpoint-result.ts b/libs/utils/src/lib/get-rankingpoint-result.ts
index e465106f2e..72076a7c12 100644
--- a/libs/utils/src/lib/get-rankingpoint-result.ts
+++ b/libs/utils/src/lib/get-rankingpoint-result.ts
@@ -51,6 +51,5 @@ export enum GameBreakdownType {
WON = 'WON',
LOST_UPGRADE = 'LOST_UPGRADE',
LOST_DOWNGRADE = 'LOST_DOWNGRADE',
- LOST_IGNORED = 'LOST_IGNORED',
- OUT_SCOPE = 'OUT_SCOPE',
+ LOST_IGNORED = 'LOST_IGNORED'
}
diff --git a/libs/utils/src/lib/i18n.generated.ts b/libs/utils/src/lib/i18n.generated.ts
index a45ac55ea9..1e211182d2 100644
--- a/libs/utils/src/lib/i18n.generated.ts
+++ b/libs/utils/src/lib/i18n.generated.ts
@@ -29,6 +29,7 @@ export type I18nTranslations = {
"none": string;
"open-in-new-tab": string;
"prev": string;
+ "re-sync": string;
"save": string;
"save-and-continue": string;
"set-primary": string;
@@ -688,6 +689,13 @@ export type I18nTranslations = {
"backup": string;
"base": string;
"claimed": string;
+ "ical": {
+ "click": string;
+ "copied": string;
+ "description": string;
+ "linked": string;
+ "team-season": string;
+ };
"menu": {
"add": string;
"claim": string;
@@ -696,6 +704,12 @@ export type I18nTranslations = {
};
"no-memberid": string;
"no-players": string;
+ "re-calculate": {
+ "calculate": string;
+ "from": string;
+ "title": string;
+ "to": string;
+ };
"regular": string;
"search": {
"backup-player": string;
@@ -725,11 +739,34 @@ export type I18nTranslations = {
"can-not-upgrade": string;
"can-upgrade": string;
"corrected": string;
+ "countsForDowngrade": string;
+ "countsForUpgrade": string;
"date": string;
"disclaimer": string;
- "downgrade": string;
+ "downgrade-average": string;
"drops-next-period": string;
"evolution": string;
+ "export": {
+ "avgDowngrade": string;
+ "avgUpgrade": string;
+ "countsFor": string;
+ "date": string;
+ "LOST_DOWNGRADE": string;
+ "LOST_IGNORED": string;
+ "LOST_UPGRADE": string;
+ "opponent1": string;
+ "opponent2": string;
+ "OUT_SCOPE": string;
+ "player1": string;
+ "player2": string;
+ "points": string;
+ "title": string;
+ "usedForDowngrade": string;
+ "usedForUpgrade": string;
+ "WON": string;
+ };
+ "games-downgrade": string;
+ "games-upgrade": string;
"hint": string;
"ignored": string;
"includeOutOfScope": string;
@@ -747,6 +784,9 @@ export type I18nTranslations = {
"one": string;
"other": string;
};
+ "outOfScopeDowngrade": string;
+ "outOfScopeUpgrade": string;
+ "outOfScopeWonGames": string;
"period": {
"last-point-update": string;
"last-ranking-update": string;
@@ -765,9 +805,9 @@ export type I18nTranslations = {
"point": string;
"ranking": string;
};
- "upgrade": string;
- "usedForDowngrade": string;
- "usedForUpgrade": string;
+ "upgrade-average": string;
+ "used-for-downgrade": string;
+ "used-for-upgrade": string;
};
"clone-points": string;
"double": string;
diff --git a/libs/utils/src/lib/index.ts b/libs/utils/src/lib/index.ts
index 710002994e..c8c41eaaf9 100644
--- a/libs/utils/src/lib/index.ts
+++ b/libs/utils/src/lib/index.ts
@@ -1,16 +1,16 @@
-export * from './sorts';
+export * from './comp';
export * from './enums';
export * from './events';
-
-export * from './comp';
export * from './game-label';
-export * from './i18n.generated';
-export * from './languages';
-export * from './is-uuid';
-export * from './get-team-values';
export * from './get-index';
export * from './get-letter-for-team';
-export * from './get-rankingpoint-result';
-export * from './get-ranking-protected';
export * from './get-ranking-periods';
+export * from './get-ranking-protected';
+export * from './get-rankingpoint-result';
+export * from './get-team-values';
+export * from './i18n.generated';
+export * from './is-uuid';
+export * from './languages';
export * from './run-parallel';
+export * from './sorts';
+export * from './types';
diff --git a/libs/utils/src/lib/types/event.type.ts b/libs/utils/src/lib/types/event.type.ts
new file mode 100644
index 0000000000..b1efb3f212
--- /dev/null
+++ b/libs/utils/src/lib/types/event.type.ts
@@ -0,0 +1 @@
+export type Event = 'S' | 'D' | 'MX';
diff --git a/libs/utils/src/lib/types/gender.type.ts b/libs/utils/src/lib/types/gender.type.ts
new file mode 100644
index 0000000000..8d1a83371f
--- /dev/null
+++ b/libs/utils/src/lib/types/gender.type.ts
@@ -0,0 +1 @@
+export type Gender = 'M' | 'F';
diff --git a/libs/utils/src/lib/types/index.ts b/libs/utils/src/lib/types/index.ts
new file mode 100644
index 0000000000..b61ae94c07
--- /dev/null
+++ b/libs/utils/src/lib/types/index.ts
@@ -0,0 +1,3 @@
+export * from './event.type';
+export * from './gender.type';
+export * from './ranking.type';
diff --git a/libs/utils/src/lib/types/ranking.type.ts b/libs/utils/src/lib/types/ranking.type.ts
new file mode 100644
index 0000000000..3ba32bd5a4
--- /dev/null
+++ b/libs/utils/src/lib/types/ranking.type.ts
@@ -0,0 +1 @@
+export type Ranking = 'single' | 'double' | 'mix';
diff --git a/package.json b/package.json
index dc654de9a4..17a9e60c7a 100644
--- a/package.json
+++ b/package.json
@@ -109,6 +109,7 @@
"geojson": "^0.5.0",
"graphql": "16.9.0",
"graphql-type-json": "^0.3.2",
+ "ical-generator": "^7.2.0",
"joi": "^17.13.3",
"juice": "^10.0.0",
"jwks-rsa": "^3.1.0",
@@ -150,7 +151,7 @@
"web-vitals": "^4.2.1",
"winston": "^3.13.1",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz",
- "xng-breadcrumb": "^11.0.0",
+ "xng-breadcrumb": "^12.0.0",
"zone.js": "0.14.7"
},
"devDependencies": {
diff --git a/sonar-project.properties b/sonar-project.properties
index 82570efe16..e2050c0297 100644
--- a/sonar-project.properties
+++ b/sonar-project.properties
@@ -11,4 +11,7 @@ sonar.sources=.
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
-sonar.javascript.lcov.reportPaths=coverage/lcov.info
\ No newline at end of file
+sonar.javascript.lcov.reportPaths=coverage/lcov.info
+
+# exclude spec files and database/migrations from analysis
+sonar.exclusions=**/*.spec.ts,**/migrations/**
\ No newline at end of file