diff --git a/src/api/tabletennis/types.ts b/src/api/tabletennis/types.ts
index db9a6ee4..f27dc282 100644
--- a/src/api/tabletennis/types.ts
+++ b/src/api/tabletennis/types.ts
@@ -167,6 +167,26 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/my-games": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ /**
+ * Get My Games
+ * @description Returns every game of the current player, in the same format as /get-games-by-id.
+ */
+ get: operations["table_tennis_get_my_games"];
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/ping": {
parameters: {
query?: never;
@@ -221,7 +241,8 @@ export interface paths {
put?: never;
/**
* Set Status
- * @description Sets a player's status by innohassle_id.
+ * @description Sets a player's status by innohassle_id. Works in both directions regardless of the rating;
+ * the automatic promotion at ADVANCED_RATING only happens when the rating crosses it.
*/
post: operations["table_tennis_set_status"];
delete?: never;
@@ -241,8 +262,8 @@ export interface paths {
put?: never;
/**
* Set Rating
- * @description Admin endpoint to assign a player's starting RTTF rating, as the organizer does for a newcomer:
- * a multiple of 25 and at least 100. The change is added to the player's rating history.
+ * @description Admin endpoint to set a player's rating by hand to any value from 0. The change is added to
+ * the player's rating history; reaching ADVANCED_RATING from below promotes a beginner.
*/
post: operations["table_tennis_set_rating"];
delete?: never;
@@ -251,6 +272,49 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/guests": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Create Guest
+ * @description Admin endpoint to create a guest for someone who has no account yet. A guest can be added to any
+ * tournament and plays like a registered player; /merge-guest later hands everything over to the account.
+ */
+ post: operations["table_tennis_create_guest"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/merge-guest": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Merge Guest
+ * @description Admin endpoint to hand a guest over to the registered player it stood for. The player replaces the
+ * guest in every tournament and game, and the guest is deleted. A player who has not played yet takes
+ * the guest's rating, status and history; otherwise the rating changes of the guest's games are added.
+ */
+ post: operations["table_tennis_merge_guest"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/reg-tour": {
parameters: {
query?: never;
@@ -264,7 +328,7 @@ export interface paths {
* Register Tour
* @description Creates a new tournament. Players can be specified either by innohassle_id (player_ids)
* or by email (emails) - both lists are optional and can be freely combined.
- * Requires at least 2 valid, registered (/reg) players to succeed.
+ * Nothing is created unless every given player is found; the 400 response lists the missing ones.
*/
post: operations["table_tennis_register_tour"];
delete?: never;
@@ -286,7 +350,7 @@ export interface paths {
* Add Player
* @description Adds multiple players to an ACTIVE tournament, specified either by innohassle_id (player_ids)
* or by email (emails) - both lists are optional and can be freely combined.
- * Processes valid players and returns a report of skipped/failed ones without crashing.
+ * Nobody is added unless every given player can be added; the 400 response lists the failed ones.
*/
post: operations["table_tennis_add_player"];
delete?: never;
@@ -465,6 +529,48 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/delete-tour": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Delete Tournament
+ * @description Admin endpoint to delete a tournament created by mistake. Only possible while no game in it
+ * is finished, so ratings are never affected; unfinished games are deleted with it.
+ */
+ post: operations["table_tennis_delete_tournament"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
+ "/reopen-tour": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get?: never;
+ put?: never;
+ /**
+ * Reopen Tournament
+ * @description Admin endpoint to make an archived tournament active again, so its games and standings can be corrected.
+ * Bonuses are still granted at most once.
+ */
+ post: operations["table_tennis_reopen_tournament"];
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/reg-game": {
parameters: {
query?: never;
@@ -622,6 +728,11 @@ export interface components {
* @default Beginner
*/
status: string;
+ /**
+ * Placeholder
+ * @default false
+ */
+ placeholder: boolean;
};
/** @example 5eb7cf5a86d9755df3a6c593 */
PydanticObjectId: string;
@@ -933,6 +1044,35 @@ export interface operations {
};
};
};
+ table_tennis_get_my_games: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Unable to verify credentials OR Credentials not provided */
+ 401: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ };
+ };
table_tennis_ping: {
parameters: {
query?: never;
@@ -1058,7 +1198,7 @@ export interface operations {
};
};
};
- /** @description Starting rating must be a multiple of {RTTF_START_RATING_STEP} and at least {RTTF_MIN_START_RATING} */
+ /** @description Rating cannot be negative */
400: {
headers: {
[name: string]: unknown;
@@ -1090,6 +1230,110 @@ export interface operations {
};
};
};
+ table_tennis_create_guest: {
+ parameters: {
+ query: {
+ nickname: string;
+ rating?: number;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Rating cannot be negative OR Nickname must be 2 to 20 characters long */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description You are not a Table Tennis administrator/coach! */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ table_tennis_merge_guest: {
+ parameters: {
+ query: {
+ guest_id: string;
+ player_id?: string | null;
+ email?: string | null;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description f"The guest and the player are in the same tournament: {', '.join((t.name for t in shared))}" */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description You are not a Table Tennis administrator/coach! */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Registered player not found OR Guest or player not found OR Guest not found */
+ 404: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
table_tennis_register_tour: {
parameters: {
query: {
@@ -1117,7 +1361,7 @@ export interface operations {
};
};
};
- /** @description Bad Request */
+ /** @description Tournament name must be 1 to 100 characters long OR A tournament needs at least 2 players */
400: {
headers: {
[name: string]: unknown;
@@ -1293,13 +1537,6 @@ export interface operations {
};
content?: never;
};
- /** @description Tournament not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
/** @description Validation Error */
422: {
headers: {
@@ -1346,13 +1583,6 @@ export interface operations {
};
content?: never;
};
- /** @description Tournament not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
/** @description Validation Error */
422: {
headers: {
@@ -1446,7 +1676,7 @@ export interface operations {
};
};
};
- /** @description Every group needs at least 2 players: {small_groups} OR Create groups first */
+ /** @description Create groups first OR Every group needs at least 2 players: {small_groups} */
400: {
headers: {
[name: string]: unknown;
@@ -1460,8 +1690,48 @@ export interface operations {
};
content?: never;
};
- /** @description Tournament not found */
- 404: {
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ table_tennis_unlock_groups: {
+ parameters: {
+ query: {
+ tour_id: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description Cannot unlock groups: validation games have already started */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ /** @description You are not a Table Tennis administrator/coach! */
+ 403: {
headers: {
[name: string]: unknown;
};
@@ -1478,7 +1748,7 @@ export interface operations {
};
};
};
- table_tennis_unlock_groups: {
+ table_tennis_set_qual_seeding: {
parameters: {
query: {
tour_id: string;
@@ -1487,7 +1757,11 @@ export interface operations {
path?: never;
cookie?: never;
};
- requestBody?: never;
+ requestBody: {
+ content: {
+ "application/json": string[];
+ };
+ };
responses: {
/** @description Successful Response */
200: {
@@ -1500,7 +1774,7 @@ export interface operations {
};
};
};
- /** @description Cannot unlock groups: validation games have already started */
+ /** @description Lock the groups first OR At least 2 players are needed for qualification OR Qualification games have already started OR Seeding must contain every participant exactly once OR Finish or cancel the group games in progress first */
400: {
headers: {
[name: string]: unknown;
@@ -1514,6 +1788,46 @@ export interface operations {
};
content?: never;
};
+ /** @description Validation Error */
+ 422: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["HTTPValidationError"];
+ };
+ };
+ };
+ };
+ table_tennis_finish_tournament: {
+ parameters: {
+ query: {
+ tour_id: string;
+ };
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successful Response */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ [key: string]: unknown;
+ };
+ };
+ };
+ /** @description You are not a Table Tennis administrator/coach! */
+ 403: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
/** @description Tournament not found */
404: {
headers: {
@@ -1532,7 +1846,7 @@ export interface operations {
};
};
};
- table_tennis_set_qual_seeding: {
+ table_tennis_delete_tournament: {
parameters: {
query: {
tour_id: string;
@@ -1541,11 +1855,7 @@ export interface operations {
path?: never;
cookie?: never;
};
- requestBody: {
- content: {
- "application/json": string[];
- };
- };
+ requestBody?: never;
responses: {
/** @description Successful Response */
200: {
@@ -1558,7 +1868,7 @@ export interface operations {
};
};
};
- /** @description Seeding must contain every participant exactly once OR Qualification games have already started OR Lock the groups first OR At least 2 players are needed for qualification */
+ /** @description A tournament with finished games cannot be deleted */
400: {
headers: {
[name: string]: unknown;
@@ -1590,7 +1900,7 @@ export interface operations {
};
};
};
- table_tennis_finish_tournament: {
+ table_tennis_reopen_tournament: {
parameters: {
query: {
tour_id: string;
@@ -1612,6 +1922,13 @@ export interface operations {
};
};
};
+ /** @description Tournament is already active */
+ 400: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
/** @description You are not a Table Tennis administrator/coach! */
403: {
headers: {
@@ -1664,7 +1981,7 @@ export interface operations {
};
};
};
- /** @description Qualification has already started OR Lock the groups before starting validation games OR A player cannot play a match against themselves! OR Validation games are played only inside one group OR One or both players are not in this tournament's player list */
+ /** @description A player cannot play a match against themselves! OR Qualification has already started OR One or both players are not in this tournament's player list OR Validation games are played only inside one group OR Lock the groups before starting validation games */
400: {
headers: {
[name: string]: unknown;
@@ -1678,7 +1995,7 @@ export interface operations {
};
content?: never;
};
- /** @description Tournament not found OR One or both players are not registered in the system (/reg) */
+ /** @description One or both players are not registered in the system (/reg) */
404: {
headers: {
[name: string]: unknown;
@@ -1728,7 +2045,7 @@ export interface operations {
};
};
};
- /** @description This game has already been finished. Recalculating rating is not allowed. */
+ /** @description Group games cannot be finished after qualification started OR This game has already been finished. Recalculating rating is not allowed. */
400: {
headers: {
[name: string]: unknown;
@@ -1742,7 +2059,7 @@ export interface operations {
};
content?: never;
};
- /** @description One or both players from this game are not registered (/reg) OR Tournament not found OR Game not found in this tournament */
+ /** @description Game not found in this tournament OR One or both players from this game are not registered (/reg) */
404: {
headers: {
[name: string]: unknown;
@@ -1785,7 +2102,7 @@ export interface operations {
};
};
};
- /** @description This game was finished before score corrections were supported OR Group games cannot be corrected after qualification started OR This game is not finished yet OR Cannot correct games of an inactive/archived tournament! */
+ /** @description The winner cannot change: the next qualification match of these players has already started OR This game is not finished yet OR This game was finished before score corrections were supported OR Group games cannot be corrected after qualification started OR Cannot correct games of an inactive/archived tournament! */
400: {
headers: {
[name: string]: unknown;
@@ -1799,7 +2116,7 @@ export interface operations {
};
content?: never;
};
- /** @description One or both players from this game are not registered (/reg) OR Tournament not found OR Game not found in this tournament */
+ /** @description Game not found in this tournament OR One or both players from this game are not registered (/reg) OR Tournament not found */
404: {
headers: {
[name: string]: unknown;
@@ -1861,7 +2178,7 @@ export interface operations {
};
content?: never;
};
- /** @description Tournament not found OR Game not found in this tournament */
+ /** @description Game not found in this tournament */
404: {
headers: {
[name: string]: unknown;
@@ -1883,7 +2200,6 @@ export interface operations {
export enum PathsSetStatusPostParametersQueryStatus {
beginner = "beginner",
advanced = "advanced",
- admin = "admin",
}
export enum PathsRegGamePostParametersQueryTip {
val = "val",
diff --git a/src/app/route-tree.gen.ts b/src/app/route-tree.gen.ts
index abf6d162..e96ae759 100644
--- a/src/app/route-tree.gen.ts
+++ b/src/app/route-tree.gen.ts
@@ -56,7 +56,6 @@ import { Route as With_menuWhen2meetNewRouteImport } from "./routes/_with_menu/w
import { Route as With_menuTabletennisTournamentsRouteImport } from "./routes/_with_menu/tabletennis/tournaments";
import { Route as With_menuTabletennisPlayersRouteImport } from "./routes/_with_menu/tabletennis/players";
import { Route as With_menuTabletennisMatchesRouteImport } from "./routes/_with_menu/tabletennis/matches";
-import { Route as With_menuTabletennisEventsRouteImport } from "./routes/_with_menu/tabletennis/events";
import { Route as With_menuStudentAffairsSignOutRouteImport } from "./routes/_with_menu/student-affairs/sign-out";
import { Route as With_menuStudentAffairsSignInRouteImport } from "./routes/_with_menu/student-affairs/sign-in";
import { Route as With_menuSportTrainerRouteImport } from "./routes/_with_menu/sport/trainer";
@@ -350,12 +349,6 @@ const With_menuTabletennisMatchesRoute =
path: "/tabletennis/matches",
getParentRoute: () => With_menuRouteRoute,
} as any);
-const With_menuTabletennisEventsRoute =
- With_menuTabletennisEventsRouteImport.update({
- id: "/tabletennis/events",
- path: "/tabletennis/events",
- getParentRoute: () => With_menuRouteRoute,
- } as any);
const With_menuStudentAffairsSignOutRoute =
With_menuStudentAffairsSignOutRouteImport.update({
id: "/student-affairs/sign-out",
@@ -657,7 +650,6 @@ export interface FileRoutesByFullPath {
"/sport/trainer": typeof With_menuSportTrainerRoute;
"/student-affairs/sign-in": typeof With_menuStudentAffairsSignInRoute;
"/student-affairs/sign-out": typeof With_menuStudentAffairsSignOutRoute;
- "/tabletennis/events": typeof With_menuTabletennisEventsRoute;
"/tabletennis/matches": typeof With_menuTabletennisMatchesRoute;
"/tabletennis/players": typeof With_menuTabletennisPlayersRoute;
"/tabletennis/tournaments": typeof With_menuTabletennisTournamentsRoute;
@@ -748,7 +740,6 @@ export interface FileRoutesByTo {
"/sport/trainer": typeof With_menuSportTrainerRoute;
"/student-affairs/sign-in": typeof With_menuStudentAffairsSignInRoute;
"/student-affairs/sign-out": typeof With_menuStudentAffairsSignOutRoute;
- "/tabletennis/events": typeof With_menuTabletennisEventsRoute;
"/tabletennis/matches": typeof With_menuTabletennisMatchesRoute;
"/tabletennis/players": typeof With_menuTabletennisPlayersRoute;
"/tabletennis/tournaments": typeof With_menuTabletennisTournamentsRoute;
@@ -844,7 +835,6 @@ export interface FileRoutesById {
"/_with_menu/sport/trainer": typeof With_menuSportTrainerRoute;
"/_with_menu/student-affairs/sign-in": typeof With_menuStudentAffairsSignInRoute;
"/_with_menu/student-affairs/sign-out": typeof With_menuStudentAffairsSignOutRoute;
- "/_with_menu/tabletennis/events": typeof With_menuTabletennisEventsRoute;
"/_with_menu/tabletennis/matches": typeof With_menuTabletennisMatchesRoute;
"/_with_menu/tabletennis/players": typeof With_menuTabletennisPlayersRoute;
"/_with_menu/tabletennis/tournaments": typeof With_menuTabletennisTournamentsRoute;
@@ -940,7 +930,6 @@ export interface FileRouteTypes {
| "/sport/trainer"
| "/student-affairs/sign-in"
| "/student-affairs/sign-out"
- | "/tabletennis/events"
| "/tabletennis/matches"
| "/tabletennis/players"
| "/tabletennis/tournaments"
@@ -1031,7 +1020,6 @@ export interface FileRouteTypes {
| "/sport/trainer"
| "/student-affairs/sign-in"
| "/student-affairs/sign-out"
- | "/tabletennis/events"
| "/tabletennis/matches"
| "/tabletennis/players"
| "/tabletennis/tournaments"
@@ -1126,7 +1114,6 @@ export interface FileRouteTypes {
| "/_with_menu/sport/trainer"
| "/_with_menu/student-affairs/sign-in"
| "/_with_menu/student-affairs/sign-out"
- | "/_with_menu/tabletennis/events"
| "/_with_menu/tabletennis/matches"
| "/_with_menu/tabletennis/players"
| "/_with_menu/tabletennis/tournaments"
@@ -1512,13 +1499,6 @@ declare module "@tanstack/react-router" {
preLoaderRoute: typeof With_menuTabletennisMatchesRouteImport;
parentRoute: typeof With_menuRouteRoute;
};
- "/_with_menu/tabletennis/events": {
- id: "/_with_menu/tabletennis/events";
- path: "/tabletennis/events";
- fullPath: "/tabletennis/events";
- preLoaderRoute: typeof With_menuTabletennisEventsRouteImport;
- parentRoute: typeof With_menuRouteRoute;
- };
"/_with_menu/student-affairs/sign-out": {
id: "/_with_menu/student-affairs/sign-out";
path: "/student-affairs/sign-out";
@@ -1894,7 +1874,6 @@ interface With_menuRouteRouteChildren {
With_menuSportTrainerRoute: typeof With_menuSportTrainerRoute;
With_menuStudentAffairsSignInRoute: typeof With_menuStudentAffairsSignInRoute;
With_menuStudentAffairsSignOutRoute: typeof With_menuStudentAffairsSignOutRoute;
- With_menuTabletennisEventsRoute: typeof With_menuTabletennisEventsRoute;
With_menuTabletennisMatchesRoute: typeof With_menuTabletennisMatchesRoute;
With_menuTabletennisPlayersRoute: typeof With_menuTabletennisPlayersRoute;
With_menuTabletennisTournamentsRoute: typeof With_menuTabletennisTournamentsRoute;
@@ -1972,7 +1951,6 @@ const With_menuRouteRouteChildren: With_menuRouteRouteChildren = {
With_menuSportTrainerRoute: With_menuSportTrainerRoute,
With_menuStudentAffairsSignInRoute: With_menuStudentAffairsSignInRoute,
With_menuStudentAffairsSignOutRoute: With_menuStudentAffairsSignOutRoute,
- With_menuTabletennisEventsRoute: With_menuTabletennisEventsRoute,
With_menuTabletennisMatchesRoute: With_menuTabletennisMatchesRoute,
With_menuTabletennisPlayersRoute: With_menuTabletennisPlayersRoute,
With_menuTabletennisTournamentsRoute: With_menuTabletennisTournamentsRoute,
diff --git a/src/app/routes/_with_menu/tabletennis/events.tsx b/src/app/routes/_with_menu/tabletennis/events.tsx
deleted file mode 100644
index be768b23..00000000
--- a/src/app/routes/_with_menu/tabletennis/events.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createFileRoute } from "@tanstack/react-router";
-import { Helmet } from "@dr.pogodin/react-helmet";
-import { TabletennisTabs } from "@/components/tabletennis/TabletennisTabs";
-import { Topbar } from "@/components/layout/Topbar.tsx";
-
-export const Route = createFileRoute("/_with_menu/tabletennis/events")({
- component: RouteComponent,
-});
-
-function RouteComponent() {
- return (
- <>
-
- Club events, open trainings and friendly matches will appear here. - Stay tuned! -
-+ {formatDay(tour.date)} · {tour.players.length} players +
+ )} ++ {empty} +
+ ); + } + return ( ++ {tour.date && `${formatDay(tour.date)} · `} + {tour.players.length} players +
++ Are you sure you want to finish {tour.name}? It can + be reopened from the archive later. +
+ {!qualProgress && ( ++ Qualification has not started, no final standings will be saved. +
+ )} + {qualProgress && !qualProgress.bracket.complete && ( ++ Qualification is not finished: {qualProgress.left} match + {qualProgress.left === 1 ? "" : "es"} left. Only the places decided + so far ({qualProgress.bracket.places.length} of {seeding.length}) + will be saved, and no bonuses are granted. +
+ )} +- RTTF: the organiser assigns a newcomer's starting rating, a - multiple of 25 and at least 100. Current rating of{" "} - {player.nickname}: {player.rating}. -
-