xpt-shared-types 1.24.0 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/inputs.d.ts +5 -0
- package/dist/generated/models.d.ts +5 -0
- package/dist/manual/tournamentTemplate.d.ts +50 -7
- package/dist/manual/tournamentTemplate.js +66 -11
- package/package.json +1 -1
- package/src/generated/inputs.ts +5 -0
- package/src/generated/models.ts +5 -0
- package/src/manual/tournamentTemplate.ts +92 -9
|
@@ -419,6 +419,11 @@ export interface TournamentTemplateInput {
|
|
|
419
419
|
hasThirdPlace?: boolean;
|
|
420
420
|
hasMatchLobby?: boolean;
|
|
421
421
|
rules?: string;
|
|
422
|
+
platform?: RelationInput;
|
|
423
|
+
gameAccount?: RelationInput;
|
|
424
|
+
region?: RelationInput;
|
|
425
|
+
imgMain?: MediaInput;
|
|
426
|
+
prizes?: unknown;
|
|
422
427
|
isDefault?: boolean;
|
|
423
428
|
useCount?: number;
|
|
424
429
|
lastUsedAt?: string;
|
|
@@ -419,6 +419,11 @@ export interface TournamentTemplate extends StrapiDocument {
|
|
|
419
419
|
hasThirdPlace?: boolean | null;
|
|
420
420
|
hasMatchLobby?: boolean | null;
|
|
421
421
|
rules?: string | null;
|
|
422
|
+
platform?: Platform | null;
|
|
423
|
+
gameAccount?: GameAccount | null;
|
|
424
|
+
region?: Region | null;
|
|
425
|
+
imgMain?: StrapiMedia | null;
|
|
426
|
+
prizes?: unknown | null;
|
|
422
427
|
isDefault?: boolean | null;
|
|
423
428
|
useCount?: number | null;
|
|
424
429
|
lastUsedAt?: string | null;
|
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
* last `LAST_RUN_WINDOW_DAYS` — so a host can repeat a night without ever
|
|
11
11
|
* having saved anything.
|
|
12
12
|
*
|
|
13
|
+
* What a setup carries is close to the whole publish checklist: the catalogue
|
|
14
|
+
* choice (platform and account network), the region, the banner and the prize
|
|
15
|
+
* amounts ride along with the shape and the rules. Only the date is left out,
|
|
16
|
+
* because it belongs to one event rather than to the setup.
|
|
17
|
+
*
|
|
13
18
|
* Routes (all scoped to the caller):
|
|
14
19
|
* - `GET /tournament-templates/setups` → `{ data: TournamentSetupsResponse }`
|
|
15
20
|
* - `POST /tournament-templates` with `SaveTemplateBody` → `{ data: TournamentTemplateSummary }`
|
|
16
21
|
* - `PUT /tournament-templates/:id/default` → `{ data: TournamentTemplateSummary }`
|
|
17
22
|
* - `DELETE /tournament-templates/:id`
|
|
18
|
-
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`
|
|
19
|
-
* `
|
|
23
|
+
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`, `region`,
|
|
24
|
+
* `platform`, `gameAccount`, `imgMain`, `prizes` and `template` (the
|
|
25
|
+
* documentId, for usage meta) alongside the usual fields.
|
|
20
26
|
*/
|
|
21
27
|
import type { StrapiMedia } from '../contracts';
|
|
22
28
|
import type { TournamentTeamSize } from '../generated/enums';
|
|
@@ -24,10 +30,29 @@ import type { TournamentTeamSize } from '../generated/enums';
|
|
|
24
30
|
export declare const LAST_RUN_WINDOW_DAYS = 30;
|
|
25
31
|
/** Templates a host may keep; the save route refuses the next one. */
|
|
26
32
|
export declare const MAX_TEMPLATES_PER_HOST = 20;
|
|
27
|
-
/**
|
|
28
|
-
|
|
33
|
+
/**
|
|
34
|
+
* The settings a host may leave out of a setup — the "Also copied" rows.
|
|
35
|
+
* `platform` covers the account network too: the two are chosen together and
|
|
36
|
+
* publish readiness counts them as one detail.
|
|
37
|
+
*/
|
|
38
|
+
export declare const TEMPLATE_COPY_KEYS: readonly ["entryFee", "prizes", "checkInTime", "platform", "region", "hasMatchLobby", "hasThirdPlace", "rules", "imgMain"];
|
|
29
39
|
export type TemplateCopyKey = (typeof TEMPLATE_COPY_KEYS)[number];
|
|
30
40
|
export type TemplateIncludeFlags = Record<TemplateCopyKey, boolean>;
|
|
41
|
+
/** A catalogue row as a setup names it. */
|
|
42
|
+
export interface SetupRef {
|
|
43
|
+
documentId: string;
|
|
44
|
+
name: string | null;
|
|
45
|
+
}
|
|
46
|
+
/** One placement's payout. The set of ranks is derived, never carried. */
|
|
47
|
+
export interface SetupPrize {
|
|
48
|
+
rank: string;
|
|
49
|
+
value: number | null;
|
|
50
|
+
}
|
|
51
|
+
/** The banner, as the media row a new tournament reuses. */
|
|
52
|
+
export interface SetupBanner {
|
|
53
|
+
id: number;
|
|
54
|
+
url: string | null;
|
|
55
|
+
}
|
|
31
56
|
export interface TournamentSetupFields {
|
|
32
57
|
participants: number;
|
|
33
58
|
teamSize: TournamentTeamSize;
|
|
@@ -38,6 +63,12 @@ export interface TournamentSetupFields {
|
|
|
38
63
|
hasThirdPlace: boolean | null;
|
|
39
64
|
hasMatchLobby: boolean | null;
|
|
40
65
|
rules: string | null;
|
|
66
|
+
platform: SetupRef | null;
|
|
67
|
+
/** Rides with `platform`; the create refuses it if the catalogue has moved. */
|
|
68
|
+
gameAccount: SetupRef | null;
|
|
69
|
+
region: SetupRef | null;
|
|
70
|
+
imgMain: SetupBanner | null;
|
|
71
|
+
prizes: SetupPrize[];
|
|
41
72
|
}
|
|
42
73
|
/** What a blank tournament starts from, and what a null setting falls back to. */
|
|
43
74
|
export declare const TOURNAMENT_SETUP_DEFAULTS: TournamentSetupFields;
|
|
@@ -93,9 +124,21 @@ export type SetupChoice = {
|
|
|
93
124
|
/** Every setting resolved: nulls replaced by the XPT default. */
|
|
94
125
|
export declare function resolveSetup(fields: Partial<TournamentSetupFields> | null | undefined): TournamentSetupFields;
|
|
95
126
|
/**
|
|
96
|
-
* The
|
|
97
|
-
*
|
|
98
|
-
*
|
|
127
|
+
* The copy keys this setup actually has something for. A setting the host
|
|
128
|
+
* unticked when saving, or never set on the tournament, carries nothing — so
|
|
129
|
+
* the "Also copied" list offers it no row rather than a tickable blank.
|
|
130
|
+
*/
|
|
131
|
+
export declare function copyableSetupKeys(fields: Partial<TournamentSetupFields> | null | undefined): TemplateCopyKey[];
|
|
132
|
+
/** The total a setup's prize amounts add up to. */
|
|
133
|
+
export declare const setupPrizePot: (prizes: SetupPrize[] | null | undefined) => number;
|
|
134
|
+
/** The tournament title rules' limit; a bumped name has to stay inside it. */
|
|
135
|
+
export declare const MAX_TOURNAMENT_NAME = 50;
|
|
136
|
+
/**
|
|
137
|
+
* The name a tournament started from a setup gets. A trailing `#N` is bumped
|
|
138
|
+
* (`Friday Night Ops #3` → `Friday Night Ops #4`); a name without one is
|
|
139
|
+
* treated as the first of its series and becomes `#2`. Either way the host
|
|
140
|
+
* sees a name they can keep or type over, and never one that silently
|
|
141
|
+
* duplicates the tournament it came from.
|
|
99
142
|
*/
|
|
100
143
|
export declare function nextTournamentName(name: string): string;
|
|
101
144
|
/** A template name from a tournament title: the trailing `#N`, if any, dropped. */
|
|
@@ -11,17 +11,24 @@
|
|
|
11
11
|
* last `LAST_RUN_WINDOW_DAYS` — so a host can repeat a night without ever
|
|
12
12
|
* having saved anything.
|
|
13
13
|
*
|
|
14
|
+
* What a setup carries is close to the whole publish checklist: the catalogue
|
|
15
|
+
* choice (platform and account network), the region, the banner and the prize
|
|
16
|
+
* amounts ride along with the shape and the rules. Only the date is left out,
|
|
17
|
+
* because it belongs to one event rather than to the setup.
|
|
18
|
+
*
|
|
14
19
|
* Routes (all scoped to the caller):
|
|
15
20
|
* - `GET /tournament-templates/setups` → `{ data: TournamentSetupsResponse }`
|
|
16
21
|
* - `POST /tournament-templates` with `SaveTemplateBody` → `{ data: TournamentTemplateSummary }`
|
|
17
22
|
* - `PUT /tournament-templates/:id/default` → `{ data: TournamentTemplateSummary }`
|
|
18
23
|
* - `DELETE /tournament-templates/:id`
|
|
19
|
-
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`
|
|
20
|
-
* `
|
|
24
|
+
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`, `region`,
|
|
25
|
+
* `platform`, `gameAccount`, `imgMain`, `prizes` and `template` (the
|
|
26
|
+
* documentId, for usage meta) alongside the usual fields.
|
|
21
27
|
*/
|
|
22
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.TOURNAMENT_SETUP_DEFAULTS = exports.TEMPLATE_COPY_KEYS = exports.MAX_TEMPLATES_PER_HOST = exports.LAST_RUN_WINDOW_DAYS = void 0;
|
|
29
|
+
exports.MAX_TOURNAMENT_NAME = exports.setupPrizePot = exports.TOURNAMENT_SETUP_DEFAULTS = exports.TEMPLATE_COPY_KEYS = exports.MAX_TEMPLATES_PER_HOST = exports.LAST_RUN_WINDOW_DAYS = void 0;
|
|
24
30
|
exports.resolveSetup = resolveSetup;
|
|
31
|
+
exports.copyableSetupKeys = copyableSetupKeys;
|
|
25
32
|
exports.nextTournamentName = nextTournamentName;
|
|
26
33
|
exports.templateNameFrom = templateNameFrom;
|
|
27
34
|
exports.setupsForGame = setupsForGame;
|
|
@@ -34,13 +41,21 @@ exports.describeSetup = describeSetup;
|
|
|
34
41
|
exports.LAST_RUN_WINDOW_DAYS = 30;
|
|
35
42
|
/** Templates a host may keep; the save route refuses the next one. */
|
|
36
43
|
exports.MAX_TEMPLATES_PER_HOST = 20;
|
|
37
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* The settings a host may leave out of a setup — the "Also copied" rows.
|
|
46
|
+
* `platform` covers the account network too: the two are chosen together and
|
|
47
|
+
* publish readiness counts them as one detail.
|
|
48
|
+
*/
|
|
38
49
|
exports.TEMPLATE_COPY_KEYS = [
|
|
39
50
|
'entryFee',
|
|
51
|
+
'prizes',
|
|
40
52
|
'checkInTime',
|
|
53
|
+
'platform',
|
|
54
|
+
'region',
|
|
41
55
|
'hasMatchLobby',
|
|
42
56
|
'hasThirdPlace',
|
|
43
57
|
'rules',
|
|
58
|
+
'imgMain',
|
|
44
59
|
];
|
|
45
60
|
/** What a blank tournament starts from, and what a null setting falls back to. */
|
|
46
61
|
exports.TOURNAMENT_SETUP_DEFAULTS = {
|
|
@@ -51,10 +66,15 @@ exports.TOURNAMENT_SETUP_DEFAULTS = {
|
|
|
51
66
|
hasThirdPlace: false,
|
|
52
67
|
hasMatchLobby: true,
|
|
53
68
|
rules: null,
|
|
69
|
+
platform: null,
|
|
70
|
+
gameAccount: null,
|
|
71
|
+
region: null,
|
|
72
|
+
imgMain: null,
|
|
73
|
+
prizes: [],
|
|
54
74
|
};
|
|
55
75
|
/** Every setting resolved: nulls replaced by the XPT default. */
|
|
56
76
|
function resolveSetup(fields) {
|
|
57
|
-
var _a, _b, _c, _d, _e, _f;
|
|
77
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
58
78
|
const source = fields !== null && fields !== void 0 ? fields : {};
|
|
59
79
|
return {
|
|
60
80
|
participants: source.participants || exports.TOURNAMENT_SETUP_DEFAULTS.participants,
|
|
@@ -64,20 +84,55 @@ function resolveSetup(fields) {
|
|
|
64
84
|
hasThirdPlace: (_d = source.hasThirdPlace) !== null && _d !== void 0 ? _d : exports.TOURNAMENT_SETUP_DEFAULTS.hasThirdPlace,
|
|
65
85
|
hasMatchLobby: (_e = source.hasMatchLobby) !== null && _e !== void 0 ? _e : exports.TOURNAMENT_SETUP_DEFAULTS.hasMatchLobby,
|
|
66
86
|
rules: (_f = source.rules) !== null && _f !== void 0 ? _f : exports.TOURNAMENT_SETUP_DEFAULTS.rules,
|
|
87
|
+
platform: (_g = source.platform) !== null && _g !== void 0 ? _g : null,
|
|
88
|
+
gameAccount: (_h = source.gameAccount) !== null && _h !== void 0 ? _h : null,
|
|
89
|
+
region: (_j = source.region) !== null && _j !== void 0 ? _j : null,
|
|
90
|
+
imgMain: (_k = source.imgMain) !== null && _k !== void 0 ? _k : null,
|
|
91
|
+
prizes: (_l = source.prizes) !== null && _l !== void 0 ? _l : [],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The copy keys this setup actually has something for. A setting the host
|
|
96
|
+
* unticked when saving, or never set on the tournament, carries nothing — so
|
|
97
|
+
* the "Also copied" list offers it no row rather than a tickable blank.
|
|
98
|
+
*/
|
|
99
|
+
function copyableSetupKeys(fields) {
|
|
100
|
+
var _a;
|
|
101
|
+
const setup = fields !== null && fields !== void 0 ? fields : {};
|
|
102
|
+
const has = {
|
|
103
|
+
entryFee: setup.entryFee != null,
|
|
104
|
+
prizes: ((_a = setup.prizes) !== null && _a !== void 0 ? _a : []).length > 0,
|
|
105
|
+
checkInTime: setup.checkInTime != null,
|
|
106
|
+
platform: setup.platform != null,
|
|
107
|
+
region: setup.region != null,
|
|
108
|
+
hasMatchLobby: setup.hasMatchLobby != null,
|
|
109
|
+
hasThirdPlace: setup.hasThirdPlace != null,
|
|
110
|
+
rules: !!setup.rules,
|
|
111
|
+
imgMain: setup.imgMain != null,
|
|
67
112
|
};
|
|
113
|
+
return exports.TEMPLATE_COPY_KEYS.filter((key) => has[key]);
|
|
68
114
|
}
|
|
115
|
+
/** The total a setup's prize amounts add up to. */
|
|
116
|
+
const setupPrizePot = (prizes) => (prizes !== null && prizes !== void 0 ? prizes : []).reduce((sum, prize) => sum + (Number(prize.value) || 0), 0);
|
|
117
|
+
exports.setupPrizePot = setupPrizePot;
|
|
69
118
|
const TRAILING_COUNTER = /\s*#(\d+)$/;
|
|
119
|
+
/** The tournament title rules' limit; a bumped name has to stay inside it. */
|
|
120
|
+
exports.MAX_TOURNAMENT_NAME = 50;
|
|
70
121
|
/**
|
|
71
|
-
* The name a tournament started from a setup gets
|
|
72
|
-
* (`Friday Night Ops #3` → `Friday Night Ops #4`);
|
|
73
|
-
*
|
|
122
|
+
* The name a tournament started from a setup gets. A trailing `#N` is bumped
|
|
123
|
+
* (`Friday Night Ops #3` → `Friday Night Ops #4`); a name without one is
|
|
124
|
+
* treated as the first of its series and becomes `#2`. Either way the host
|
|
125
|
+
* sees a name they can keep or type over, and never one that silently
|
|
126
|
+
* duplicates the tournament it came from.
|
|
74
127
|
*/
|
|
75
128
|
function nextTournamentName(name) {
|
|
76
129
|
const match = TRAILING_COUNTER.exec(name);
|
|
77
|
-
|
|
130
|
+
const base = (match ? name.slice(0, match.index) : name).trim();
|
|
131
|
+
if (!base)
|
|
78
132
|
return name;
|
|
79
|
-
const
|
|
80
|
-
|
|
133
|
+
const suffix = ` #${match ? Number(match[1]) + 1 : 2}`;
|
|
134
|
+
const room = exports.MAX_TOURNAMENT_NAME - suffix.length;
|
|
135
|
+
return `${base.length > room ? base.slice(0, room).trim() : base}${suffix}`;
|
|
81
136
|
}
|
|
82
137
|
/** A template name from a tournament title: the trailing `#N`, if any, dropped. */
|
|
83
138
|
function templateNameFrom(title) {
|
package/package.json
CHANGED
package/src/generated/inputs.ts
CHANGED
|
@@ -506,6 +506,11 @@ export interface TournamentTemplateInput {
|
|
|
506
506
|
hasThirdPlace?: boolean;
|
|
507
507
|
hasMatchLobby?: boolean;
|
|
508
508
|
rules?: string;
|
|
509
|
+
platform?: RelationInput;
|
|
510
|
+
gameAccount?: RelationInput;
|
|
511
|
+
region?: RelationInput;
|
|
512
|
+
imgMain?: MediaInput;
|
|
513
|
+
prizes?: unknown;
|
|
509
514
|
isDefault?: boolean;
|
|
510
515
|
useCount?: number;
|
|
511
516
|
lastUsedAt?: string;
|
package/src/generated/models.ts
CHANGED
|
@@ -507,6 +507,11 @@ export interface TournamentTemplate extends StrapiDocument {
|
|
|
507
507
|
hasThirdPlace?: boolean | null;
|
|
508
508
|
hasMatchLobby?: boolean | null;
|
|
509
509
|
rules?: string | null;
|
|
510
|
+
platform?: Platform | null;
|
|
511
|
+
gameAccount?: GameAccount | null;
|
|
512
|
+
region?: Region | null;
|
|
513
|
+
imgMain?: StrapiMedia | null;
|
|
514
|
+
prizes?: unknown | null;
|
|
510
515
|
isDefault?: boolean | null;
|
|
511
516
|
useCount?: number | null;
|
|
512
517
|
lastUsedAt?: string | null;
|
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
* last `LAST_RUN_WINDOW_DAYS` — so a host can repeat a night without ever
|
|
11
11
|
* having saved anything.
|
|
12
12
|
*
|
|
13
|
+
* What a setup carries is close to the whole publish checklist: the catalogue
|
|
14
|
+
* choice (platform and account network), the region, the banner and the prize
|
|
15
|
+
* amounts ride along with the shape and the rules. Only the date is left out,
|
|
16
|
+
* because it belongs to one event rather than to the setup.
|
|
17
|
+
*
|
|
13
18
|
* Routes (all scoped to the caller):
|
|
14
19
|
* - `GET /tournament-templates/setups` → `{ data: TournamentSetupsResponse }`
|
|
15
20
|
* - `POST /tournament-templates` with `SaveTemplateBody` → `{ data: TournamentTemplateSummary }`
|
|
16
21
|
* - `PUT /tournament-templates/:id/default` → `{ data: TournamentTemplateSummary }`
|
|
17
22
|
* - `DELETE /tournament-templates/:id`
|
|
18
|
-
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`
|
|
19
|
-
* `
|
|
23
|
+
* - `POST /tournaments` accepts `entryFee`, `checkInTime`, `rules`, `region`,
|
|
24
|
+
* `platform`, `gameAccount`, `imgMain`, `prizes` and `template` (the
|
|
25
|
+
* documentId, for usage meta) alongside the usual fields.
|
|
20
26
|
*/
|
|
21
27
|
|
|
22
28
|
import type { StrapiMedia } from '../contracts';
|
|
@@ -28,18 +34,44 @@ export const LAST_RUN_WINDOW_DAYS = 30;
|
|
|
28
34
|
/** Templates a host may keep; the save route refuses the next one. */
|
|
29
35
|
export const MAX_TEMPLATES_PER_HOST = 20;
|
|
30
36
|
|
|
31
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* The settings a host may leave out of a setup — the "Also copied" rows.
|
|
39
|
+
* `platform` covers the account network too: the two are chosen together and
|
|
40
|
+
* publish readiness counts them as one detail.
|
|
41
|
+
*/
|
|
32
42
|
export const TEMPLATE_COPY_KEYS = [
|
|
33
43
|
'entryFee',
|
|
44
|
+
'prizes',
|
|
34
45
|
'checkInTime',
|
|
46
|
+
'platform',
|
|
47
|
+
'region',
|
|
35
48
|
'hasMatchLobby',
|
|
36
49
|
'hasThirdPlace',
|
|
37
50
|
'rules',
|
|
51
|
+
'imgMain',
|
|
38
52
|
] as const;
|
|
39
53
|
|
|
40
54
|
export type TemplateCopyKey = (typeof TEMPLATE_COPY_KEYS)[number];
|
|
41
55
|
export type TemplateIncludeFlags = Record<TemplateCopyKey, boolean>;
|
|
42
56
|
|
|
57
|
+
/** A catalogue row as a setup names it. */
|
|
58
|
+
export interface SetupRef {
|
|
59
|
+
documentId: string;
|
|
60
|
+
name: string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** One placement's payout. The set of ranks is derived, never carried. */
|
|
64
|
+
export interface SetupPrize {
|
|
65
|
+
rank: string;
|
|
66
|
+
value: number | null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** The banner, as the media row a new tournament reuses. */
|
|
70
|
+
export interface SetupBanner {
|
|
71
|
+
id: number;
|
|
72
|
+
url: string | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
43
75
|
export interface TournamentSetupFields {
|
|
44
76
|
participants: number;
|
|
45
77
|
teamSize: TournamentTeamSize;
|
|
@@ -50,6 +82,12 @@ export interface TournamentSetupFields {
|
|
|
50
82
|
hasThirdPlace: boolean | null;
|
|
51
83
|
hasMatchLobby: boolean | null;
|
|
52
84
|
rules: string | null;
|
|
85
|
+
platform: SetupRef | null;
|
|
86
|
+
/** Rides with `platform`; the create refuses it if the catalogue has moved. */
|
|
87
|
+
gameAccount: SetupRef | null;
|
|
88
|
+
region: SetupRef | null;
|
|
89
|
+
imgMain: SetupBanner | null;
|
|
90
|
+
prizes: SetupPrize[];
|
|
53
91
|
}
|
|
54
92
|
|
|
55
93
|
/** What a blank tournament starts from, and what a null setting falls back to. */
|
|
@@ -61,6 +99,11 @@ export const TOURNAMENT_SETUP_DEFAULTS: TournamentSetupFields = {
|
|
|
61
99
|
hasThirdPlace: false,
|
|
62
100
|
hasMatchLobby: true,
|
|
63
101
|
rules: null,
|
|
102
|
+
platform: null,
|
|
103
|
+
gameAccount: null,
|
|
104
|
+
region: null,
|
|
105
|
+
imgMain: null,
|
|
106
|
+
prizes: [],
|
|
64
107
|
};
|
|
65
108
|
|
|
66
109
|
/** Enough of a game for the client to open the create dialog on it. */
|
|
@@ -126,21 +169,61 @@ export function resolveSetup(
|
|
|
126
169
|
hasThirdPlace: source.hasThirdPlace ?? TOURNAMENT_SETUP_DEFAULTS.hasThirdPlace,
|
|
127
170
|
hasMatchLobby: source.hasMatchLobby ?? TOURNAMENT_SETUP_DEFAULTS.hasMatchLobby,
|
|
128
171
|
rules: source.rules ?? TOURNAMENT_SETUP_DEFAULTS.rules,
|
|
172
|
+
platform: source.platform ?? null,
|
|
173
|
+
gameAccount: source.gameAccount ?? null,
|
|
174
|
+
region: source.region ?? null,
|
|
175
|
+
imgMain: source.imgMain ?? null,
|
|
176
|
+
prizes: source.prizes ?? [],
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The copy keys this setup actually has something for. A setting the host
|
|
182
|
+
* unticked when saving, or never set on the tournament, carries nothing — so
|
|
183
|
+
* the "Also copied" list offers it no row rather than a tickable blank.
|
|
184
|
+
*/
|
|
185
|
+
export function copyableSetupKeys(
|
|
186
|
+
fields: Partial<TournamentSetupFields> | null | undefined
|
|
187
|
+
): TemplateCopyKey[] {
|
|
188
|
+
const setup = fields ?? {};
|
|
189
|
+
const has: Record<TemplateCopyKey, boolean> = {
|
|
190
|
+
entryFee: setup.entryFee != null,
|
|
191
|
+
prizes: (setup.prizes ?? []).length > 0,
|
|
192
|
+
checkInTime: setup.checkInTime != null,
|
|
193
|
+
platform: setup.platform != null,
|
|
194
|
+
region: setup.region != null,
|
|
195
|
+
hasMatchLobby: setup.hasMatchLobby != null,
|
|
196
|
+
hasThirdPlace: setup.hasThirdPlace != null,
|
|
197
|
+
rules: !!setup.rules,
|
|
198
|
+
imgMain: setup.imgMain != null,
|
|
129
199
|
};
|
|
200
|
+
return TEMPLATE_COPY_KEYS.filter((key) => has[key]);
|
|
130
201
|
}
|
|
131
202
|
|
|
203
|
+
/** The total a setup's prize amounts add up to. */
|
|
204
|
+
export const setupPrizePot = (prizes: SetupPrize[] | null | undefined): number =>
|
|
205
|
+
(prizes ?? []).reduce((sum, prize) => sum + (Number(prize.value) || 0), 0);
|
|
206
|
+
|
|
132
207
|
const TRAILING_COUNTER = /\s*#(\d+)$/;
|
|
133
208
|
|
|
209
|
+
/** The tournament title rules' limit; a bumped name has to stay inside it. */
|
|
210
|
+
export const MAX_TOURNAMENT_NAME = 50;
|
|
211
|
+
|
|
134
212
|
/**
|
|
135
|
-
* The name a tournament started from a setup gets
|
|
136
|
-
* (`Friday Night Ops #3` → `Friday Night Ops #4`);
|
|
137
|
-
*
|
|
213
|
+
* The name a tournament started from a setup gets. A trailing `#N` is bumped
|
|
214
|
+
* (`Friday Night Ops #3` → `Friday Night Ops #4`); a name without one is
|
|
215
|
+
* treated as the first of its series and becomes `#2`. Either way the host
|
|
216
|
+
* sees a name they can keep or type over, and never one that silently
|
|
217
|
+
* duplicates the tournament it came from.
|
|
138
218
|
*/
|
|
139
219
|
export function nextTournamentName(name: string): string {
|
|
140
220
|
const match = TRAILING_COUNTER.exec(name);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
221
|
+
const base = (match ? name.slice(0, match.index) : name).trim();
|
|
222
|
+
if (!base) return name;
|
|
223
|
+
|
|
224
|
+
const suffix = ` #${match ? Number(match[1]) + 1 : 2}`;
|
|
225
|
+
const room = MAX_TOURNAMENT_NAME - suffix.length;
|
|
226
|
+
return `${base.length > room ? base.slice(0, room).trim() : base}${suffix}`;
|
|
144
227
|
}
|
|
145
228
|
|
|
146
229
|
/** A template name from a tournament title: the trailing `#N`, if any, dropped. */
|