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.
@@ -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` and
19
- * `template` (the documentId, for usage meta) alongside the usual fields.
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
- /** The settings a host may leave out of a template — the "Also copied" rows. */
28
- export declare const TEMPLATE_COPY_KEYS: readonly ["entryFee", "checkInTime", "hasMatchLobby", "hasThirdPlace", "rules"];
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 name a tournament started from a setup gets: a trailing `#N` is bumped
97
- * (`Friday Night Ops #3` `Friday Night Ops #4`); anything else is copied
98
- * verbatim.
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` and
20
- * `template` (the documentId, for usage meta) alongside the usual fields.
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
- /** The settings a host may leave out of a template — the "Also copied" rows. */
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: a trailing `#N` is bumped
72
- * (`Friday Night Ops #3` → `Friday Night Ops #4`); anything else is copied
73
- * verbatim.
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
- if (!match)
130
+ const base = (match ? name.slice(0, match.index) : name).trim();
131
+ if (!base)
78
132
  return name;
79
- const next = Number(match[1]) + 1;
80
- return `${name.slice(0, match.index).trimEnd()} #${next}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpt-shared-types",
3
- "version": "1.24.0",
3
+ "version": "1.25.0",
4
4
  "description": "Shared types and data for XPT projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -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;
@@ -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` and
19
- * `template` (the documentId, for usage meta) alongside the usual fields.
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
- /** The settings a host may leave out of a template — the "Also copied" rows. */
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: a trailing `#N` is bumped
136
- * (`Friday Night Ops #3` → `Friday Night Ops #4`); anything else is copied
137
- * verbatim.
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
- if (!match) return name;
142
- const next = Number(match[1]) + 1;
143
- return `${name.slice(0, match.index).trimEnd()} #${next}`;
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. */