volleyballsimtypes 0.0.462 → 0.0.464
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/cjs/src/api/index.d.ts +14 -1
- package/dist/cjs/src/data/init-models.js +17 -0
- package/dist/cjs/src/data/models/currency-transaction.d.ts +4 -2
- package/dist/cjs/src/data/models/currency-transaction.js +2 -0
- package/dist/cjs/src/data/models/index.d.ts +3 -0
- package/dist/cjs/src/data/models/index.js +3 -0
- package/dist/cjs/src/data/models/match-preset.d.ts +28 -0
- package/dist/cjs/src/data/models/match-preset.js +65 -0
- package/dist/cjs/src/data/models/match-set.d.ts +2 -0
- package/dist/cjs/src/data/models/match-set.js +5 -0
- package/dist/cjs/src/data/models/match.d.ts +5 -2
- package/dist/cjs/src/data/models/match.js +4 -0
- package/dist/cjs/src/data/models/physician.d.ts +25 -0
- package/dist/cjs/src/data/models/physician.js +57 -0
- package/dist/cjs/src/data/models/physiotherapist.d.ts +25 -0
- package/dist/cjs/src/data/models/physiotherapist.js +57 -0
- package/dist/cjs/src/data/models/player.d.ts +8 -0
- package/dist/cjs/src/data/models/player.js +17 -0
- package/dist/cjs/src/data/models/tactics.d.ts +9 -1
- package/dist/cjs/src/data/models/tactics.js +9 -0
- package/dist/cjs/src/data/transformers/division.js +1 -1
- package/dist/cjs/src/data/transformers/lineup-preset.js +7 -1
- package/dist/cjs/src/data/transformers/lineup-preset.test.js +15 -2
- package/dist/cjs/src/data/transformers/match-preset-override.test.d.ts +1 -0
- package/dist/cjs/src/data/transformers/match-preset-override.test.js +275 -0
- package/dist/cjs/src/data/transformers/match-set.js +2 -0
- package/dist/cjs/src/data/transformers/match.d.ts +2 -1
- package/dist/cjs/src/data/transformers/match.js +21 -5
- package/dist/cjs/src/data/transformers/player.js +15 -1
- package/dist/cjs/src/data/transformers/qualifier.js +1 -1
- package/dist/cjs/src/data/transformers/rally-event-injury.test.d.ts +1 -0
- package/dist/cjs/src/data/transformers/rally-event-injury.test.js +33 -0
- package/dist/cjs/src/data/transformers/rally-event.d.ts +1 -0
- package/dist/cjs/src/data/transformers/rally-event.js +4 -1
- package/dist/cjs/src/data/transformers/season.js +1 -1
- package/dist/cjs/src/data/transformers/tactics.d.ts +3 -2
- package/dist/cjs/src/data/transformers/tactics.js +106 -3
- package/dist/cjs/src/data/transformers/team.d.ts +7 -1
- package/dist/cjs/src/data/transformers/team.js +22 -4
- package/dist/cjs/src/data/transformers/tournament.js +1 -1
- package/dist/cjs/src/service/event/schemas/substitution.z.d.ts +1 -0
- package/dist/cjs/src/service/event/schemas/substitution.z.js +2 -1
- package/dist/cjs/src/service/event/substitution.d.ts +1 -0
- package/dist/cjs/src/service/event/substitution.js +4 -2
- package/dist/cjs/src/service/match/match-set-forfeit.test.d.ts +1 -0
- package/dist/cjs/src/service/match/match-set-forfeit.test.js +76 -0
- package/dist/cjs/src/service/match/match-set.d.ts +8 -0
- package/dist/cjs/src/service/match/match-set.js +23 -1
- package/dist/cjs/src/service/match/match.d.ts +2 -0
- package/dist/cjs/src/service/match/match.js +2 -1
- package/dist/cjs/src/service/match/schemas/match-set.z.d.ts +1 -0
- package/dist/cjs/src/service/match/schemas/match-set.z.js +30 -2
- package/dist/cjs/src/service/match/schemas/match.z.d.ts +4 -0
- package/dist/cjs/src/service/match/schemas/match.z.js +4 -1
- package/dist/cjs/src/service/player/index.d.ts +1 -0
- package/dist/cjs/src/service/player/index.js +1 -0
- package/dist/cjs/src/service/player/injury.d.ts +14 -0
- package/dist/cjs/src/service/player/injury.js +39 -0
- package/dist/cjs/src/service/player/injury.test.d.ts +1 -0
- package/dist/cjs/src/service/player/injury.test.js +78 -0
- package/dist/cjs/src/service/player/player-generator.js +3 -1
- package/dist/cjs/src/service/player/player.d.ts +3 -0
- package/dist/cjs/src/service/player/player.js +3 -1
- package/dist/cjs/src/service/player/schemas/player.z.d.ts +6 -0
- package/dist/cjs/src/service/player/schemas/player.z.js +7 -0
- package/dist/cjs/src/service/team/index.d.ts +1 -0
- package/dist/cjs/src/service/team/index.js +1 -0
- package/dist/cjs/src/service/team/reserve-priority.d.ts +6 -0
- package/dist/cjs/src/service/team/reserve-priority.js +2 -0
- package/dist/cjs/src/service/team/schemas/reserve-priority.z.test.d.ts +1 -0
- package/dist/cjs/src/service/team/schemas/reserve-priority.z.test.js +125 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +5 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.js +32 -1
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +5 -0
- package/dist/cjs/src/service/team/tactics.d.ts +5 -0
- package/dist/cjs/src/service/team/tactics.js +3 -1
- package/dist/esm/src/api/index.d.ts +14 -1
- package/dist/esm/src/data/init-models.js +18 -1
- package/dist/esm/src/data/models/currency-transaction.d.ts +4 -2
- package/dist/esm/src/data/models/currency-transaction.js +2 -0
- package/dist/esm/src/data/models/index.d.ts +3 -0
- package/dist/esm/src/data/models/index.js +3 -0
- package/dist/esm/src/data/models/match-preset.d.ts +28 -0
- package/dist/esm/src/data/models/match-preset.js +61 -0
- package/dist/esm/src/data/models/match-set.d.ts +2 -0
- package/dist/esm/src/data/models/match-set.js +5 -0
- package/dist/esm/src/data/models/match.d.ts +5 -2
- package/dist/esm/src/data/models/match.js +4 -0
- package/dist/esm/src/data/models/physician.d.ts +25 -0
- package/dist/esm/src/data/models/physician.js +53 -0
- package/dist/esm/src/data/models/physiotherapist.d.ts +25 -0
- package/dist/esm/src/data/models/physiotherapist.js +53 -0
- package/dist/esm/src/data/models/player.d.ts +8 -0
- package/dist/esm/src/data/models/player.js +17 -0
- package/dist/esm/src/data/models/tactics.d.ts +9 -1
- package/dist/esm/src/data/models/tactics.js +9 -0
- package/dist/esm/src/data/transformers/division.js +1 -1
- package/dist/esm/src/data/transformers/lineup-preset.js +7 -1
- package/dist/esm/src/data/transformers/lineup-preset.test.js +15 -2
- package/dist/esm/src/data/transformers/match-preset-override.test.d.ts +1 -0
- package/dist/esm/src/data/transformers/match-preset-override.test.js +273 -0
- package/dist/esm/src/data/transformers/match-set.js +2 -0
- package/dist/esm/src/data/transformers/match.d.ts +2 -1
- package/dist/esm/src/data/transformers/match.js +21 -5
- package/dist/esm/src/data/transformers/player.js +15 -1
- package/dist/esm/src/data/transformers/qualifier.js +1 -1
- package/dist/esm/src/data/transformers/rally-event-injury.test.d.ts +1 -0
- package/dist/esm/src/data/transformers/rally-event-injury.test.js +31 -0
- package/dist/esm/src/data/transformers/rally-event.d.ts +1 -0
- package/dist/esm/src/data/transformers/rally-event.js +4 -1
- package/dist/esm/src/data/transformers/season.js +1 -1
- package/dist/esm/src/data/transformers/tactics.d.ts +3 -2
- package/dist/esm/src/data/transformers/tactics.js +106 -4
- package/dist/esm/src/data/transformers/team.d.ts +7 -1
- package/dist/esm/src/data/transformers/team.js +20 -5
- package/dist/esm/src/data/transformers/tournament.js +1 -1
- package/dist/esm/src/service/event/schemas/substitution.z.d.ts +1 -0
- package/dist/esm/src/service/event/schemas/substitution.z.js +2 -1
- package/dist/esm/src/service/event/substitution.d.ts +1 -0
- package/dist/esm/src/service/event/substitution.js +4 -2
- package/dist/esm/src/service/match/match-set-forfeit.test.d.ts +1 -0
- package/dist/esm/src/service/match/match-set-forfeit.test.js +74 -0
- package/dist/esm/src/service/match/match-set.d.ts +8 -0
- package/dist/esm/src/service/match/match-set.js +23 -1
- package/dist/esm/src/service/match/match.d.ts +2 -0
- package/dist/esm/src/service/match/match.js +2 -1
- package/dist/esm/src/service/match/schemas/match-set.z.d.ts +1 -0
- package/dist/esm/src/service/match/schemas/match-set.z.js +30 -2
- package/dist/esm/src/service/match/schemas/match.z.d.ts +4 -0
- package/dist/esm/src/service/match/schemas/match.z.js +4 -1
- package/dist/esm/src/service/player/index.d.ts +1 -0
- package/dist/esm/src/service/player/index.js +1 -0
- package/dist/esm/src/service/player/injury.d.ts +14 -0
- package/dist/esm/src/service/player/injury.js +33 -0
- package/dist/esm/src/service/player/injury.test.d.ts +1 -0
- package/dist/esm/src/service/player/injury.test.js +76 -0
- package/dist/esm/src/service/player/player-generator.js +3 -1
- package/dist/esm/src/service/player/player.d.ts +3 -0
- package/dist/esm/src/service/player/player.js +3 -1
- package/dist/esm/src/service/player/schemas/player.z.d.ts +6 -0
- package/dist/esm/src/service/player/schemas/player.z.js +7 -0
- package/dist/esm/src/service/team/index.d.ts +1 -0
- package/dist/esm/src/service/team/index.js +1 -0
- package/dist/esm/src/service/team/reserve-priority.d.ts +6 -0
- package/dist/esm/src/service/team/reserve-priority.js +1 -0
- package/dist/esm/src/service/team/schemas/reserve-priority.z.test.d.ts +1 -0
- package/dist/esm/src/service/team/schemas/reserve-priority.z.test.js +123 -0
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +5 -0
- package/dist/esm/src/service/team/schemas/tactics.z.js +32 -1
- package/dist/esm/src/service/team/schemas/team.z.d.ts +5 -0
- package/dist/esm/src/service/team/tactics.d.ts +5 -0
- package/dist/esm/src/service/team/tactics.js +3 -1
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const globals_1 = require("@jest/globals");
|
|
4
4
|
const lineup_preset_1 = require("./lineup-preset");
|
|
5
5
|
(0, globals_1.describe)('transformToLineupPreset()', () => {
|
|
6
|
-
(0, globals_1.it)('maps preset columns to the API shape and
|
|
6
|
+
(0, globals_1.it)('maps preset columns to the API shape and heals a pre-injuries config to the effective default', () => {
|
|
7
7
|
const config = { lineup: { bench: [] }, substitutionBand: 'TIRED' };
|
|
8
8
|
const preset = (0, lineup_preset_1.transformToLineupPreset)({
|
|
9
9
|
preset_id: 'p1',
|
|
@@ -18,8 +18,21 @@ const lineup_preset_1 = require("./lineup-preset");
|
|
|
18
18
|
name: 'Starters',
|
|
19
19
|
isActive: true,
|
|
20
20
|
orderIndex: 2,
|
|
21
|
+
// A config stored before the injuries fields lacks replaceInjuredImmediately; the transform heals it
|
|
22
|
+
// to true (the effective hydration default) so the DTO's required field is honest.
|
|
23
|
+
config: { ...config, replaceInjuredImmediately: true }
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
(0, globals_1.it)('keeps an explicit replaceInjuredImmediately: false', () => {
|
|
27
|
+
const config = { lineup: { bench: [] }, substitutionBand: 'TIRED', replaceInjuredImmediately: false };
|
|
28
|
+
const preset = (0, lineup_preset_1.transformToLineupPreset)({
|
|
29
|
+
preset_id: 'p2',
|
|
30
|
+
team_id: 't1',
|
|
31
|
+
name: 'Risky',
|
|
32
|
+
is_active: false,
|
|
33
|
+
order_index: 3,
|
|
21
34
|
config
|
|
22
35
|
});
|
|
23
|
-
(0, globals_1.expect)(preset.config).toBe(
|
|
36
|
+
(0, globals_1.expect)(preset.config.replaceInjuredImmediately).toBe(false);
|
|
24
37
|
});
|
|
25
38
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const tactics_1 = require("./tactics");
|
|
6
|
+
const team_1 = require("./team");
|
|
7
|
+
const match_1 = require("./match");
|
|
8
|
+
const service_1 = require("../../service");
|
|
9
|
+
// ─── shared mocks (model-shaped plain objects, cast through unknown) ──────────
|
|
10
|
+
const countryFixture = {
|
|
11
|
+
country_id: (0, uuid_1.v4)(),
|
|
12
|
+
name: 'Test Country',
|
|
13
|
+
alpha_2: 'TC',
|
|
14
|
+
alpha_3: 'TCN',
|
|
15
|
+
country_code: '999',
|
|
16
|
+
iso_3166_2: 'TC-TEST',
|
|
17
|
+
region: '',
|
|
18
|
+
region_code: '',
|
|
19
|
+
sub_region: '',
|
|
20
|
+
sub_region_code: '',
|
|
21
|
+
intermediate_region: '',
|
|
22
|
+
intermediate_region_code: ''
|
|
23
|
+
};
|
|
24
|
+
function makePlayerModel(id) {
|
|
25
|
+
const stat = 50;
|
|
26
|
+
return {
|
|
27
|
+
player_id: id,
|
|
28
|
+
first_name: 'Test',
|
|
29
|
+
last_name: `P-${id.slice(0, 4)}`,
|
|
30
|
+
rarity: service_1.RarityEnum.COMMON,
|
|
31
|
+
roles: [service_1.RoleEnum.OUTSIDE_HITTER],
|
|
32
|
+
traits: [],
|
|
33
|
+
birth_age: 18,
|
|
34
|
+
birth_iteration: 1,
|
|
35
|
+
last_declined_iteration: null,
|
|
36
|
+
decline_profile: service_1.DeclineProfileEnum.STANDARD,
|
|
37
|
+
BoxScores: [],
|
|
38
|
+
BoxScoreTotals: null,
|
|
39
|
+
country: countryFixture,
|
|
40
|
+
PerformanceStat: {
|
|
41
|
+
setting: stat,
|
|
42
|
+
serve: stat,
|
|
43
|
+
spike: stat,
|
|
44
|
+
quick: stat,
|
|
45
|
+
power: stat,
|
|
46
|
+
awareness: stat,
|
|
47
|
+
attack: stat,
|
|
48
|
+
back_attack: stat,
|
|
49
|
+
reception: stat,
|
|
50
|
+
overhand: stat,
|
|
51
|
+
bump: stat,
|
|
52
|
+
block: stat,
|
|
53
|
+
read: stat,
|
|
54
|
+
commit: stat,
|
|
55
|
+
focus: stat,
|
|
56
|
+
defense: stat,
|
|
57
|
+
stamina: stat,
|
|
58
|
+
reflex: stat
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Six starters; index order is the row lineup's zone 1..6 order.
|
|
63
|
+
const ids = Array.from({ length: 6 }, () => (0, uuid_1.v4)());
|
|
64
|
+
const teamId = (0, uuid_1.v4)();
|
|
65
|
+
function rowLineup(order) {
|
|
66
|
+
return {
|
|
67
|
+
[service_1.CourtPosition.RIGHT_BACK]: order[0],
|
|
68
|
+
[service_1.CourtPosition.RIGHT_FRONT]: order[1],
|
|
69
|
+
[service_1.CourtPosition.MIDDLE_FRONT]: order[2],
|
|
70
|
+
[service_1.CourtPosition.LEFT_FRONT]: order[3],
|
|
71
|
+
[service_1.CourtPosition.LEFT_BACK]: order[4],
|
|
72
|
+
[service_1.CourtPosition.MIDDLE_BACK]: order[5],
|
|
73
|
+
bench: []
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function makeTacticsRow(order) {
|
|
77
|
+
return {
|
|
78
|
+
team_id: teamId,
|
|
79
|
+
lineup: rowLineup(order),
|
|
80
|
+
libero_replacements: [],
|
|
81
|
+
receive_rotation_offset: false,
|
|
82
|
+
designated_subs: [],
|
|
83
|
+
substitution_band: 'TIRED',
|
|
84
|
+
rotation_system: '6-0',
|
|
85
|
+
designated_setters: [],
|
|
86
|
+
offensive_preferences: []
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function makeTeamModel(activeOrder) {
|
|
90
|
+
return {
|
|
91
|
+
team_id: teamId,
|
|
92
|
+
name: 'Test Team',
|
|
93
|
+
short_name: 'TST',
|
|
94
|
+
rating: 0,
|
|
95
|
+
division_id: (0, uuid_1.v4)(),
|
|
96
|
+
active: true,
|
|
97
|
+
country: countryFixture,
|
|
98
|
+
tactics: makeTacticsRow(activeOrder),
|
|
99
|
+
PlayerTeams: ids.map(id => ({ player: makePlayerModel(id) }))
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// The API-DTO config for a preset whose starters are the REVERSE of the active order, so an applied
|
|
103
|
+
// override is visible through the hydrated lineup.
|
|
104
|
+
function makeConfig(order) {
|
|
105
|
+
return {
|
|
106
|
+
lineup: {
|
|
107
|
+
[service_1.CourtPosition.RIGHT_BACK]: order[0],
|
|
108
|
+
[service_1.CourtPosition.RIGHT_FRONT]: order[1],
|
|
109
|
+
[service_1.CourtPosition.MIDDLE_FRONT]: order[2],
|
|
110
|
+
[service_1.CourtPosition.LEFT_FRONT]: order[3],
|
|
111
|
+
[service_1.CourtPosition.LEFT_BACK]: order[4],
|
|
112
|
+
[service_1.CourtPosition.MIDDLE_BACK]: order[5],
|
|
113
|
+
bench: []
|
|
114
|
+
},
|
|
115
|
+
liberoReplacements: [],
|
|
116
|
+
receiveRotationOffset: false,
|
|
117
|
+
designatedSubs: [],
|
|
118
|
+
substitutionBand: 'TIRED',
|
|
119
|
+
rotationSystem: '6-0',
|
|
120
|
+
designatedSetters: [],
|
|
121
|
+
offensivePreferences: []
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const activeOrder = ids;
|
|
125
|
+
const presetOrder = [...ids].reverse();
|
|
126
|
+
function starterAt(tactics, zone) {
|
|
127
|
+
return tactics.lineup[zone]?.id;
|
|
128
|
+
}
|
|
129
|
+
// ─── apiTacticsConfigToTacticsAttributes ──────────────────────────────────────
|
|
130
|
+
(0, globals_1.describe)('apiTacticsConfigToTacticsAttributes()', () => {
|
|
131
|
+
(0, globals_1.it)('round-trips a fully-loaded Tactics row through tacticsColumnsToApiDto', () => {
|
|
132
|
+
const full = {
|
|
133
|
+
team_id: teamId,
|
|
134
|
+
lineup: { ...rowLineup(activeOrder), [service_1.CourtPosition.LIBERO_ZONE]: undefined },
|
|
135
|
+
libero_replacements: [ids[5]],
|
|
136
|
+
receive_rotation_offset: true,
|
|
137
|
+
designated_subs: [{ starterId: ids[0], benchId: undefined, mode: 'FATIGUE', fatigueBand: 'WORN', sets: [{ mode: 'NEVER' }] }],
|
|
138
|
+
substitution_band: 'TIRED',
|
|
139
|
+
second_libero: undefined,
|
|
140
|
+
libero_sub: undefined,
|
|
141
|
+
libero_replacement_sets: [[ids[0]], [ids[1]]],
|
|
142
|
+
rotation_system: '5-1',
|
|
143
|
+
designated_setters: [ids[0]],
|
|
144
|
+
offensive_preferences: [{ rotation: 1, order: [ids[1], ids[2]] }],
|
|
145
|
+
system_sets: [{ rotationSystem: '5-1', designatedSetters: [ids[0]] }],
|
|
146
|
+
offensive_preference_sets: [[{ rotation: 2, order: [ids[3]] }]]
|
|
147
|
+
};
|
|
148
|
+
const dto = (0, tactics_1.tacticsColumnsToApiDto)(full);
|
|
149
|
+
const back = (0, tactics_1.apiTacticsConfigToTacticsAttributes)(dto, teamId);
|
|
150
|
+
(0, globals_1.expect)(back.lineup).toEqual(full.lineup);
|
|
151
|
+
(0, globals_1.expect)(back.libero_replacements).toEqual(full.libero_replacements);
|
|
152
|
+
(0, globals_1.expect)(back.receive_rotation_offset).toBe(true);
|
|
153
|
+
(0, globals_1.expect)(back.designated_subs).toEqual(full.designated_subs);
|
|
154
|
+
(0, globals_1.expect)(back.libero_replacement_sets).toEqual(full.libero_replacement_sets);
|
|
155
|
+
(0, globals_1.expect)(back.rotation_system).toBe('5-1');
|
|
156
|
+
(0, globals_1.expect)(back.designated_setters).toEqual(full.designated_setters);
|
|
157
|
+
(0, globals_1.expect)(back.offensive_preferences).toEqual(full.offensive_preferences);
|
|
158
|
+
(0, globals_1.expect)(back.system_sets).toEqual(full.system_sets);
|
|
159
|
+
(0, globals_1.expect)(back.offensive_preference_sets).toEqual(full.offensive_preference_sets);
|
|
160
|
+
});
|
|
161
|
+
(0, globals_1.it)('hydrates through transformToTactics identically to the row path', () => {
|
|
162
|
+
const roster = ids.map(id => (0, team_1.transformToTeam)(makeTeamModel(activeOrder)).roster.find(p => p.id === id));
|
|
163
|
+
(0, globals_1.expect)(roster.every(p => p != null)).toBe(true);
|
|
164
|
+
const teamModel = makeTeamModel(activeOrder);
|
|
165
|
+
const players = (0, team_1.transformToTeam)(teamModel).roster;
|
|
166
|
+
const fromRow = (0, tactics_1.transformToTactics)(makeTacticsRow(activeOrder), players);
|
|
167
|
+
const fromConfig = (0, tactics_1.transformToTactics)((0, tactics_1.apiTacticsConfigToTacticsAttributes)(makeConfig(activeOrder), teamId), players);
|
|
168
|
+
for (const zone of [1, 2, 3, 4, 5, 6]) {
|
|
169
|
+
(0, globals_1.expect)(starterAt(fromConfig, zone)).toBe(starterAt(fromRow, zone));
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
(0, globals_1.it)('throws PLAYER_NOT_FOUND when the config references an off-roster player', () => {
|
|
173
|
+
const players = (0, team_1.transformToTeam)(makeTeamModel(activeOrder)).roster;
|
|
174
|
+
const drifted = makeConfig([(0, uuid_1.v4)(), ...ids.slice(1)]);
|
|
175
|
+
(0, globals_1.expect)(() => (0, tactics_1.transformToTactics)((0, tactics_1.apiTacticsConfigToTacticsAttributes)(drifted, teamId), players)).toThrow('PLAYER_NOT_FOUND');
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
// ─── transformToTeam override / fallback ──────────────────────────────────────
|
|
179
|
+
(0, globals_1.describe)('transformToTeam() tacticsOverride', () => {
|
|
180
|
+
(0, globals_1.it)('uses the override config when it hydrates', () => {
|
|
181
|
+
const team = (0, team_1.transformToTeam)(makeTeamModel(activeOrder), undefined, {
|
|
182
|
+
presetId: 'preset-1',
|
|
183
|
+
config: makeConfig(presetOrder)
|
|
184
|
+
});
|
|
185
|
+
(0, globals_1.expect)(starterAt(team.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(presetOrder[0]);
|
|
186
|
+
});
|
|
187
|
+
(0, globals_1.it)('falls back to the active tactics and notifies onFallback when the override fails', () => {
|
|
188
|
+
const onFallback = globals_1.jest.fn();
|
|
189
|
+
const drifted = makeConfig([(0, uuid_1.v4)(), ...ids.slice(1)]);
|
|
190
|
+
const team = (0, team_1.transformToTeam)(makeTeamModel(activeOrder), undefined, {
|
|
191
|
+
presetId: 'preset-1',
|
|
192
|
+
config: drifted,
|
|
193
|
+
onFallback
|
|
194
|
+
});
|
|
195
|
+
(0, globals_1.expect)(onFallback).toHaveBeenCalledTimes(1);
|
|
196
|
+
(0, globals_1.expect)(onFallback.mock.calls[0][0]).toBe('preset-1');
|
|
197
|
+
(0, globals_1.expect)(starterAt(team.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
198
|
+
});
|
|
199
|
+
(0, globals_1.it)('keeps the active tactics when no override is given', () => {
|
|
200
|
+
const team = (0, team_1.transformToTeam)(makeTeamModel(activeOrder));
|
|
201
|
+
(0, globals_1.expect)(starterAt(team.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
// ─── transformToMatch per-team override ───────────────────────────────────────
|
|
205
|
+
(0, globals_1.describe)('transformToMatch() MatchPresets', () => {
|
|
206
|
+
const awayIds = Array.from({ length: 6 }, () => (0, uuid_1.v4)());
|
|
207
|
+
const awayTeamId = (0, uuid_1.v4)();
|
|
208
|
+
function makeAwayModel() {
|
|
209
|
+
return {
|
|
210
|
+
team_id: awayTeamId,
|
|
211
|
+
name: 'Away Team',
|
|
212
|
+
short_name: 'AWY',
|
|
213
|
+
rating: 0,
|
|
214
|
+
division_id: (0, uuid_1.v4)(),
|
|
215
|
+
active: true,
|
|
216
|
+
country: countryFixture,
|
|
217
|
+
tactics: {
|
|
218
|
+
team_id: awayTeamId,
|
|
219
|
+
lineup: {
|
|
220
|
+
[service_1.CourtPosition.RIGHT_BACK]: awayIds[0],
|
|
221
|
+
[service_1.CourtPosition.RIGHT_FRONT]: awayIds[1],
|
|
222
|
+
[service_1.CourtPosition.MIDDLE_FRONT]: awayIds[2],
|
|
223
|
+
[service_1.CourtPosition.LEFT_FRONT]: awayIds[3],
|
|
224
|
+
[service_1.CourtPosition.LEFT_BACK]: awayIds[4],
|
|
225
|
+
[service_1.CourtPosition.MIDDLE_BACK]: awayIds[5],
|
|
226
|
+
bench: []
|
|
227
|
+
},
|
|
228
|
+
libero_replacements: [],
|
|
229
|
+
receive_rotation_offset: false,
|
|
230
|
+
designated_subs: [],
|
|
231
|
+
substitution_band: 'TIRED',
|
|
232
|
+
rotation_system: '6-0',
|
|
233
|
+
designated_setters: [],
|
|
234
|
+
offensive_preferences: []
|
|
235
|
+
},
|
|
236
|
+
PlayerTeams: awayIds.map(id => ({ player: makePlayerModel(id) }))
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
function makeMatchModel(withPreset) {
|
|
240
|
+
return {
|
|
241
|
+
match_id: (0, uuid_1.v4)(),
|
|
242
|
+
home_team: teamId,
|
|
243
|
+
away_team: awayTeamId,
|
|
244
|
+
scheduled_date: new Date(),
|
|
245
|
+
status: 'PENDING',
|
|
246
|
+
HomeTeam: makeTeamModel(activeOrder),
|
|
247
|
+
AwayTeam: makeAwayModel(),
|
|
248
|
+
MatchSets: [],
|
|
249
|
+
VPERs: [],
|
|
250
|
+
MatchPresets: withPreset
|
|
251
|
+
? [{ match_id: 'm', team_id: teamId, preset_id: 'preset-1', preset: { config: makeConfig(presetOrder) } }]
|
|
252
|
+
: undefined
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
(0, globals_1.it)('applies the home override and leaves the away team on active tactics', () => {
|
|
256
|
+
const match = (0, match_1.transformToMatch)(makeMatchModel(true));
|
|
257
|
+
(0, globals_1.expect)(starterAt(match.homeTeam.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(presetOrder[0]);
|
|
258
|
+
(0, globals_1.expect)(starterAt(match.awayTeam.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(awayIds[0]);
|
|
259
|
+
});
|
|
260
|
+
(0, globals_1.it)('behaves exactly as before when the association is absent', () => {
|
|
261
|
+
const match = (0, match_1.transformToMatch)(makeMatchModel(false));
|
|
262
|
+
(0, globals_1.expect)(starterAt(match.homeTeam.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
263
|
+
(0, globals_1.expect)(starterAt(match.awayTeam.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(awayIds[0]);
|
|
264
|
+
});
|
|
265
|
+
(0, globals_1.it)('routes the fallback callback with the failing team id', () => {
|
|
266
|
+
const onPresetFallback = globals_1.jest.fn();
|
|
267
|
+
const model = makeMatchModel(true);
|
|
268
|
+
model.MatchPresets[0].preset.config = makeConfig([(0, uuid_1.v4)(), ...ids.slice(1)]);
|
|
269
|
+
const match = (0, match_1.transformToMatch)(model, undefined, onPresetFallback);
|
|
270
|
+
(0, globals_1.expect)(onPresetFallback).toHaveBeenCalledTimes(1);
|
|
271
|
+
(0, globals_1.expect)(onPresetFallback.mock.calls[0][0]).toBe(teamId);
|
|
272
|
+
(0, globals_1.expect)(onPresetFallback.mock.calls[0][1]).toBe('preset-1');
|
|
273
|
+
(0, globals_1.expect)(starterAt(match.homeTeam.tactics, service_1.CourtPosition.RIGHT_BACK)).toBe(activeOrder[0]);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -27,6 +27,7 @@ function transformToAttributes(set, matchId) {
|
|
|
27
27
|
is_tie_break: set.isTieBreak,
|
|
28
28
|
home_score: set.homeScore,
|
|
29
29
|
away_score: set.awayScore,
|
|
30
|
+
forfeited: set.forfeited,
|
|
30
31
|
player_positions: [...homeSetPositions, ...awaySetPositions],
|
|
31
32
|
BoxScores: (set.boxScores ?? []).map(_1.transformFromBoxScore),
|
|
32
33
|
Rallies: (set.rallies ?? []).map((rally) => (0, _1.transformFromRally)(rally, set.id))
|
|
@@ -43,6 +44,7 @@ function transformToObject(model) {
|
|
|
43
44
|
homeScore: model.home_score,
|
|
44
45
|
awayScore: model.away_score,
|
|
45
46
|
isTieBreak: model.is_tie_break,
|
|
47
|
+
forfeited: model.forfeited ?? false,
|
|
46
48
|
boxScores: (model.BoxScores ?? []).map(_1.transformToBoxScore),
|
|
47
49
|
homePlayerPosition,
|
|
48
50
|
awayPlayerPosition,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MatchAttributes, MatchModel } from '../models';
|
|
2
2
|
import { Match } from '../../service';
|
|
3
|
+
export type PresetFallbackHandler = (teamId: string, presetId: string, err: unknown) => void;
|
|
3
4
|
declare function transformToAttributes(match: Match): MatchAttributes;
|
|
4
|
-
declare function transformToObject(model: MatchModel, currentIteration?: number): Match;
|
|
5
|
+
declare function transformToObject(model: MatchModel, currentIteration?: number, onPresetFallback?: PresetFallbackHandler): Match;
|
|
5
6
|
export { transformToObject as transformToMatch, transformToAttributes as transformFromMatch };
|
|
@@ -12,10 +12,25 @@ function transformToAttributes(match) {
|
|
|
12
12
|
away_team: match.awayTeam.id,
|
|
13
13
|
home_team: match.homeTeam.id,
|
|
14
14
|
scheduled_date: match.scheduledDate,
|
|
15
|
-
status: match.status
|
|
15
|
+
status: match.status,
|
|
16
|
+
// Both directions on purpose (the read is in transformToObject below). NOTE for the Sim: updateMatch's
|
|
17
|
+
// completion write uses an explicit column list and must include forfeit_type or the marker is lost.
|
|
18
|
+
forfeit_type: match.forfeitType ?? null
|
|
16
19
|
};
|
|
17
20
|
}
|
|
18
|
-
function transformToObject(model, currentIteration) {
|
|
21
|
+
function transformToObject(model, currentIteration, onPresetFallback) {
|
|
22
|
+
// The (match, team)-chosen preset override for one side, when the loader included MatchPresets with the
|
|
23
|
+
// preset loaded (only the simulator's match query does). Association absent = active tactics, as always.
|
|
24
|
+
const overrideFor = (teamId) => {
|
|
25
|
+
const row = (model.MatchPresets ?? []).find(mp => mp.team_id === teamId);
|
|
26
|
+
if (row?.preset == null)
|
|
27
|
+
return undefined;
|
|
28
|
+
return {
|
|
29
|
+
presetId: row.preset_id,
|
|
30
|
+
config: row.preset.config,
|
|
31
|
+
onFallback: (presetId, err) => onPresetFallback?.(teamId, presetId, err)
|
|
32
|
+
};
|
|
33
|
+
};
|
|
19
34
|
const sets = (model.MatchSets ?? []).map(match_set_1.transformToMatchSet)
|
|
20
35
|
.sort((s1, s2) => s1.order - s2.order);
|
|
21
36
|
let homeScore = 0;
|
|
@@ -47,14 +62,15 @@ function transformToObject(model, currentIteration) {
|
|
|
47
62
|
const VPERs = (model.VPERs ?? []).map(vper_1.transformToVPER);
|
|
48
63
|
return service_1.Match.create({
|
|
49
64
|
id: model.match_id,
|
|
50
|
-
homeTeam: (0, team_1.transformToTeam)(model.HomeTeam, currentIteration),
|
|
51
|
-
awayTeam: (0, team_1.transformToTeam)(model.AwayTeam, currentIteration),
|
|
65
|
+
homeTeam: (0, team_1.transformToTeam)(model.HomeTeam, currentIteration, overrideFor(model.home_team)),
|
|
66
|
+
awayTeam: (0, team_1.transformToTeam)(model.AwayTeam, currentIteration, overrideFor(model.away_team)),
|
|
52
67
|
scheduledDate: new Date(model.scheduled_date),
|
|
53
68
|
sets,
|
|
54
69
|
status: model.status,
|
|
55
70
|
VPERs,
|
|
56
71
|
homeScore,
|
|
57
72
|
awayScore,
|
|
58
|
-
winner: model.MatchResult?.Winner != null ? (0, team_1.transformToTeam)(model.MatchResult.Winner, currentIteration) : undefined
|
|
73
|
+
winner: model.MatchResult?.Winner != null ? (0, team_1.transformToTeam)(model.MatchResult.Winner, currentIteration) : undefined,
|
|
74
|
+
forfeitType: model.forfeit_type ?? undefined
|
|
59
75
|
});
|
|
60
76
|
}
|
|
@@ -25,7 +25,11 @@ function transformToAttributes(player) {
|
|
|
25
25
|
birth_age: player.birthAge,
|
|
26
26
|
birth_iteration: player.birthIteration,
|
|
27
27
|
last_declined_iteration: null,
|
|
28
|
-
decline_profile: player.declineProfile
|
|
28
|
+
decline_profile: player.declineProfile,
|
|
29
|
+
injury_prone_profile: player.injuryProneProfile,
|
|
30
|
+
injury_severity: player.injury?.severity ?? null,
|
|
31
|
+
injured_until: player.injury?.injuredUntil ?? null,
|
|
32
|
+
injury_debuff: player.injury?.debuff ?? null
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
function transformToBoxScoreTotals(model) {
|
|
@@ -91,6 +95,16 @@ function transformToObject(model, currentIteration) {
|
|
|
91
95
|
birthAge: model.birth_age,
|
|
92
96
|
birthIteration: model.birth_iteration,
|
|
93
97
|
declineProfile: model.decline_profile,
|
|
98
|
+
// DECIMAL comes back as a string from pg; schema-defaulted when the column is absent (pre-migration rows).
|
|
99
|
+
injuryProneProfile: model.injury_prone_profile != null ? Number(model.injury_prone_profile) : undefined,
|
|
100
|
+
// Severity defends against a partial write: an injured_until without severity reads as MINOR.
|
|
101
|
+
injury: model.injured_until != null
|
|
102
|
+
? {
|
|
103
|
+
severity: model.injury_severity != null ? Number(model.injury_severity) : 1,
|
|
104
|
+
injuredUntil: model.injured_until,
|
|
105
|
+
debuff: model.injury_debuff != null ? Number(model.injury_debuff) : undefined
|
|
106
|
+
}
|
|
107
|
+
: undefined,
|
|
94
108
|
boxScores: (model.BoxScores ?? []).map(_1.transformToBoxScore),
|
|
95
109
|
boxScoreTotals
|
|
96
110
|
});
|
|
@@ -44,7 +44,7 @@ function transformToObject(model) {
|
|
|
44
44
|
id: model.competition_id,
|
|
45
45
|
iteration: (0, _1.transformToIteration)(model.Iteration),
|
|
46
46
|
matches: (model.CompetitionMatches ?? []).map(_1.transformToQualifierMatch),
|
|
47
|
-
teams: (0, _1.sortTeamsByCompetitionIndex)(model.Teams ?? []).map(_1.transformToTeam),
|
|
47
|
+
teams: (0, _1.sortTeamsByCompetitionIndex)(model.Teams ?? []).map(team => (0, _1.transformToTeam)(team)),
|
|
48
48
|
status: model.status,
|
|
49
49
|
region: (0, _1.transformToRegion)(regionQualifier),
|
|
50
50
|
champion
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const globals_1 = require("@jest/globals");
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const service_1 = require("../../service");
|
|
6
|
+
const rally_event_1 = require("./rally-event");
|
|
7
|
+
(0, globals_1.describe)('rally-event compact round trip — injury substitution marker', () => {
|
|
8
|
+
const playerIn = (0, uuid_1.v4)();
|
|
9
|
+
const playerOut = (0, uuid_1.v4)();
|
|
10
|
+
const roster = [playerIn, playerOut];
|
|
11
|
+
const playerIndex = new Map(roster.map((id, i) => [id, i]));
|
|
12
|
+
(0, globals_1.it)('packs the injury flag as j=1 and round-trips it', () => {
|
|
13
|
+
const sub = service_1.Substitution.create({ playerId: playerIn, playerOut, injury: true });
|
|
14
|
+
const compact = (0, rally_event_1.transformToCompact)(sub, playerIndex);
|
|
15
|
+
(0, globals_1.expect)(compact.j).toBe(1);
|
|
16
|
+
const decoded = (0, rally_event_1.transformToSubstitution)(compact, roster);
|
|
17
|
+
(0, globals_1.expect)(decoded.injury).toBe(true);
|
|
18
|
+
(0, globals_1.expect)(decoded.playerId).toBe(playerIn);
|
|
19
|
+
(0, globals_1.expect)(decoded.playerOut).toBe(playerOut);
|
|
20
|
+
});
|
|
21
|
+
(0, globals_1.it)('omits j entirely for a normal substitution', () => {
|
|
22
|
+
const sub = service_1.Substitution.create({ playerId: playerIn, playerOut });
|
|
23
|
+
const compact = (0, rally_event_1.transformToCompact)(sub, playerIndex);
|
|
24
|
+
(0, globals_1.expect)('j' in compact).toBe(false);
|
|
25
|
+
const decoded = (0, rally_event_1.transformToSubstitution)(compact, roster);
|
|
26
|
+
(0, globals_1.expect)(decoded.injury).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
(0, globals_1.it)('decodes a legacy compact event (no j key) as a non-injury substitution', () => {
|
|
29
|
+
const legacy = { p: 0, e: service_1.EventTypeEnum.SUBSTITUTION, o: 1 };
|
|
30
|
+
const decoded = (0, rally_event_1.transformToSubstitution)(legacy, roster);
|
|
31
|
+
(0, globals_1.expect)(decoded.injury).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -9,6 +9,7 @@ export interface CompactEvent {
|
|
|
9
9
|
b?: number[];
|
|
10
10
|
l?: number;
|
|
11
11
|
o?: number;
|
|
12
|
+
j?: number;
|
|
12
13
|
}
|
|
13
14
|
export declare function transformToCompact(evt: RallyEvent, playerIndex: Map<string, number>): CompactEvent;
|
|
14
15
|
export declare function transformToBlock(event: CompactEvent, roster: string[]): Block;
|
|
@@ -29,6 +29,8 @@ function transformToCompact(evt, playerIndex) {
|
|
|
29
29
|
}
|
|
30
30
|
else if (evt instanceof service_1.Substitution) {
|
|
31
31
|
out.o = playerIndex.get(evt.playerOut);
|
|
32
|
+
if (evt.injury === true)
|
|
33
|
+
out.j = 1;
|
|
32
34
|
}
|
|
33
35
|
return out;
|
|
34
36
|
}
|
|
@@ -88,6 +90,7 @@ function transformToSpike(event, roster) {
|
|
|
88
90
|
function transformToSubstitution(event, roster) {
|
|
89
91
|
return service_1.Substitution.create({
|
|
90
92
|
playerId: roster[event.p],
|
|
91
|
-
playerOut: roster[event.o]
|
|
93
|
+
playerOut: roster[event.o],
|
|
94
|
+
injury: event.j === 1 ? true : undefined
|
|
92
95
|
});
|
|
93
96
|
}
|
|
@@ -33,7 +33,7 @@ function transformToAttributes(season) {
|
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
35
|
function transformToObject(model) {
|
|
36
|
-
const teams = (0, _1.sortTeamsByCompetitionIndex)(model.Teams ?? []).map(_1.transformToTeam);
|
|
36
|
+
const teams = (0, _1.sortTeamsByCompetitionIndex)(model.Teams ?? []).map(team => (0, _1.transformToTeam)(team));
|
|
37
37
|
// One standing per roster team: the persisted CompetitionStandings aggregate where it exists, a zero-stat
|
|
38
38
|
// row otherwise. CompetitionStandings only gains a row once a team plays (the DB trigger fires on match
|
|
39
39
|
// COMPLETE), so mapping it directly dropped un-played teams from the table for the first part of a season.
|
|
@@ -2,6 +2,7 @@ import { TacticsAttributes, TacticsModel } from '../models';
|
|
|
2
2
|
import { Player, Tactics } from '../../service';
|
|
3
3
|
import type { Tactics as ApiTactics } from '../../api';
|
|
4
4
|
declare function transformToAttributes(tactics: Tactics, teamId: string): TacticsAttributes;
|
|
5
|
-
declare function transformToObject(model:
|
|
5
|
+
declare function transformToObject(model: TacticsAttributes, roster: Player[]): Tactics;
|
|
6
6
|
declare function tacticsColumnsToApiDto(model: TacticsModel): ApiTactics;
|
|
7
|
-
|
|
7
|
+
declare function apiTacticsConfigToAttributes(config: ApiTactics, teamId: string): TacticsAttributes;
|
|
8
|
+
export { transformToObject as transformToTactics, transformToAttributes as transformFromTactics, tacticsColumnsToApiDto, apiTacticsConfigToAttributes as apiTacticsConfigToTacticsAttributes };
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.transformToTactics = transformToObject;
|
|
4
4
|
exports.transformFromTactics = transformToAttributes;
|
|
5
5
|
exports.tacticsColumnsToApiDto = tacticsColumnsToApiDto;
|
|
6
|
+
exports.apiTacticsConfigToTacticsAttributes = apiTacticsConfigToAttributes;
|
|
6
7
|
const service_1 = require("../../service");
|
|
7
8
|
// Back-compat: new rows persist `mode`; rows written before the mode change persist `isPinchServer` (+ a
|
|
8
9
|
// 'NEVER' fatigueBand for the old "never fatigue-sub" state). Derive the mode so existing data keeps working
|
|
@@ -86,9 +87,16 @@ function transformToAttributes(tactics, teamId) {
|
|
|
86
87
|
offensive_preference_sets: tactics.offensivePreferenceSets?.map(prefs => prefs.map(p => ({
|
|
87
88
|
rotation: p.rotation,
|
|
88
89
|
order: p.order.map((a) => a.id)
|
|
89
|
-
})))
|
|
90
|
+
}))),
|
|
91
|
+
replace_injured_immediately: tactics.replaceInjuredImmediately,
|
|
92
|
+
reserve_priorities: tactics.reservePriorities.map(rp => ({
|
|
93
|
+
position: rp.position,
|
|
94
|
+
reserves: rp.reserves.map((p) => p.id)
|
|
95
|
+
}))
|
|
90
96
|
};
|
|
91
97
|
}
|
|
98
|
+
// Accepts TacticsAttributes (not just TacticsModel) so a per-match preset's config, shaped as column data by
|
|
99
|
+
// apiTacticsConfigToTacticsAttributes below, can be hydrated through the exact same validation/healing path.
|
|
92
100
|
function transformToObject(model, roster) {
|
|
93
101
|
const lineup = transformToLineup(model.lineup, roster);
|
|
94
102
|
// A second libero is only valid as a RESERVE: it needs a starting libero (LIBERO_ZONE), the L2 player must be
|
|
@@ -161,7 +169,39 @@ function transformToObject(model, roster) {
|
|
|
161
169
|
offensivePreferenceSets: model.offensive_preference_sets?.map(prefs => prefs.map(p => ({
|
|
162
170
|
rotation: p.rotation,
|
|
163
171
|
order: p.order.map((id) => findPlayer(id, roster))
|
|
164
|
-
})))
|
|
172
|
+
}))),
|
|
173
|
+
replaceInjuredImmediately: model.replace_injured_immediately ?? true,
|
|
174
|
+
// LENIENT (roster.find, not findPlayer): a dismissed/retired reserve, one that has since moved INTO the
|
|
175
|
+
// lineup, one that doubles as the resolved second libero, or a duplicate (player in two lists, repeated
|
|
176
|
+
// position) is DROPPED rather than crashing the sim batch (reserves are depth, not structure); now-empty
|
|
177
|
+
// entries are pruned. First occurrence wins on duplicates; the next tactics save rewrites cleanly.
|
|
178
|
+
reservePriorities: (() => {
|
|
179
|
+
const seenIds = new Set();
|
|
180
|
+
const seenPositions = new Set();
|
|
181
|
+
return (model.reserve_priorities ?? [])
|
|
182
|
+
.filter(rp => {
|
|
183
|
+
// Court slots only (1-6): jsonb has no constraint, and an out-of-range position would pass the
|
|
184
|
+
// heal only to be rejected by the schema, crashing the very sim batch this heal protects.
|
|
185
|
+
if (!Number.isInteger(rp.position) || rp.position < 1 || rp.position > 6)
|
|
186
|
+
return false;
|
|
187
|
+
if (seenPositions.has(rp.position))
|
|
188
|
+
return false;
|
|
189
|
+
seenPositions.add(rp.position);
|
|
190
|
+
return true;
|
|
191
|
+
})
|
|
192
|
+
.map(rp => ({
|
|
193
|
+
position: rp.position,
|
|
194
|
+
reserves: rp.reserves
|
|
195
|
+
.map((id) => roster.find((p) => p.id === id))
|
|
196
|
+
.filter((p) => {
|
|
197
|
+
if (p == null || inLineupIds.has(p.id) || p.id === secondLibero?.id || seenIds.has(p.id))
|
|
198
|
+
return false;
|
|
199
|
+
seenIds.add(p.id);
|
|
200
|
+
return true;
|
|
201
|
+
})
|
|
202
|
+
}))
|
|
203
|
+
.filter(rp => rp.reserves.length > 0);
|
|
204
|
+
})()
|
|
165
205
|
});
|
|
166
206
|
}
|
|
167
207
|
// Serialize a Tactics ROW (db columns) straight to the API Tactics DTO (player references stay as ids; no
|
|
@@ -221,6 +261,69 @@ function tacticsColumnsToApiDto(model) {
|
|
|
221
261
|
designatedSetters: model.designated_setters,
|
|
222
262
|
offensivePreferences: model.offensive_preferences,
|
|
223
263
|
systemSets: model.system_sets,
|
|
224
|
-
offensivePreferenceSets: model.offensive_preference_sets
|
|
264
|
+
offensivePreferenceSets: model.offensive_preference_sets,
|
|
265
|
+
replaceInjuredImmediately: model.replace_injured_immediately ?? true,
|
|
266
|
+
// Same stale-data policy as validSecondLibero above: don't surface reserve entries the service load
|
|
267
|
+
// would drop (bad position, id now in the lineup, the L2, duplicates), or the UI reloads and re-emits
|
|
268
|
+
// them forever. Roster membership can't be checked here (ids only); the service heal covers that.
|
|
269
|
+
reservePriorities: (() => {
|
|
270
|
+
const seenIds = new Set();
|
|
271
|
+
const seenPositions = new Set();
|
|
272
|
+
const healed = (model.reserve_priorities ?? [])
|
|
273
|
+
.filter(rp => {
|
|
274
|
+
if (!Number.isInteger(rp.position) || rp.position < 1 || rp.position > 6)
|
|
275
|
+
return false;
|
|
276
|
+
if (seenPositions.has(rp.position))
|
|
277
|
+
return false;
|
|
278
|
+
seenPositions.add(rp.position);
|
|
279
|
+
return true;
|
|
280
|
+
})
|
|
281
|
+
.map(rp => ({
|
|
282
|
+
position: rp.position,
|
|
283
|
+
reserves: rp.reserves.filter(id => {
|
|
284
|
+
if (lineupIds.has(id) || id === validSecondLibero || seenIds.has(id))
|
|
285
|
+
return false;
|
|
286
|
+
seenIds.add(id);
|
|
287
|
+
return true;
|
|
288
|
+
})
|
|
289
|
+
}))
|
|
290
|
+
.filter(rp => rp.reserves.length > 0);
|
|
291
|
+
return healed.length > 0 ? healed : undefined;
|
|
292
|
+
})()
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
// Inverse of tacticsColumnsToApiDto: shape a preset's stored API config (camelCase, player ids) as
|
|
296
|
+
// TacticsAttributes-shaped column data so transformToTactics can hydrate it against a roster through the
|
|
297
|
+
// exact same validation/healing path as the Tactics row. Only the top-level keys need renaming -- the jsonb
|
|
298
|
+
// sub-objects (designated subs, libero sub, preferences, system sets) are stored camelCase in both shapes.
|
|
299
|
+
// Deliberately NOT mirroring writeTacticsRow's null-clears-column rule: nothing is being UPDATEd here, so
|
|
300
|
+
// absent optionals simply stay undefined.
|
|
301
|
+
function apiTacticsConfigToAttributes(config, teamId) {
|
|
302
|
+
return {
|
|
303
|
+
team_id: teamId,
|
|
304
|
+
lineup: {
|
|
305
|
+
[service_1.CourtPosition.LIBERO_ZONE]: config.lineup[service_1.CourtPosition.LIBERO_ZONE],
|
|
306
|
+
[service_1.CourtPosition.LEFT_BACK]: config.lineup[service_1.CourtPosition.LEFT_BACK],
|
|
307
|
+
[service_1.CourtPosition.LEFT_FRONT]: config.lineup[service_1.CourtPosition.LEFT_FRONT],
|
|
308
|
+
[service_1.CourtPosition.MIDDLE_BACK]: config.lineup[service_1.CourtPosition.MIDDLE_BACK],
|
|
309
|
+
[service_1.CourtPosition.MIDDLE_FRONT]: config.lineup[service_1.CourtPosition.MIDDLE_FRONT],
|
|
310
|
+
[service_1.CourtPosition.RIGHT_BACK]: config.lineup[service_1.CourtPosition.RIGHT_BACK],
|
|
311
|
+
[service_1.CourtPosition.RIGHT_FRONT]: config.lineup[service_1.CourtPosition.RIGHT_FRONT],
|
|
312
|
+
bench: config.lineup.bench
|
|
313
|
+
},
|
|
314
|
+
libero_replacements: config.liberoReplacements,
|
|
315
|
+
receive_rotation_offset: config.receiveRotationOffset,
|
|
316
|
+
designated_subs: config.designatedSubs,
|
|
317
|
+
substitution_band: config.substitutionBand,
|
|
318
|
+
second_libero: config.secondLibero,
|
|
319
|
+
libero_sub: config.liberoSub,
|
|
320
|
+
libero_replacement_sets: config.liberoReplacementSets,
|
|
321
|
+
rotation_system: config.rotationSystem,
|
|
322
|
+
designated_setters: config.designatedSetters,
|
|
323
|
+
offensive_preferences: config.offensivePreferences,
|
|
324
|
+
system_sets: config.systemSets,
|
|
325
|
+
offensive_preference_sets: config.offensivePreferenceSets,
|
|
326
|
+
replace_injured_immediately: config.replaceInjuredImmediately,
|
|
327
|
+
reserve_priorities: config.reservePriorities
|
|
225
328
|
};
|
|
226
329
|
}
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { Team } from '../../service';
|
|
2
2
|
import { TeamAttributes, TeamModel } from '../models';
|
|
3
|
+
import type { Tactics as ApiTactics } from '../../api';
|
|
4
|
+
export interface MatchTacticsOverride {
|
|
5
|
+
presetId: string;
|
|
6
|
+
config: ApiTactics;
|
|
7
|
+
onFallback?: (presetId: string, err: unknown) => void;
|
|
8
|
+
}
|
|
3
9
|
declare function transformToAttributes(team: Team): TeamAttributes;
|
|
4
|
-
declare function transformToObject(model: TeamModel, currentIteration?: number): Team;
|
|
10
|
+
declare function transformToObject(model: TeamModel, currentIteration?: number, tacticsOverride?: MatchTacticsOverride): Team;
|
|
5
11
|
export { transformToObject as transformToTeam, transformToAttributes as transformFromTeam };
|