@dcrackel/hematournamentui 1.0.713 → 1.0.715
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/README.md +14 -0
- package/dist/HemaTournamentUI-lib.es.js +6917 -6255
- package/dist/HemaTournamentUI-lib.umd.js +27 -27
- package/dist/hematournamentui-tailwind.css +109 -18
- package/package.json +1 -1
- package/src/index.js +43 -42
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.stories.js +49 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.test.js +129 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.vue +165 -0
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +10 -1
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +31 -0
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +20 -1
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +38 -0
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +63 -9
- package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +46 -4
- package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +244 -178
- package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +48 -3
- package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +8 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +121 -0
- package/src/stories/Util/bracketDirectors.js +86 -0
|
@@ -1,178 +1,244 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import DirectorModal from '../../../Molecules/Modals/DirectorModal/DirectorModal.vue';
|
|
4
|
-
import EditBoutModal from '../../../Molecules/Modals/EditBoutModal/EditBoutModal.vue';
|
|
5
|
-
import Tableau from '../../../Organisms/Containers/Tableau/Tableau.vue';
|
|
6
|
-
import BracketDirectorAssignmentPanel from '../../../Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue';
|
|
7
|
-
import Bracket from './Bracket.vue';
|
|
8
|
-
|
|
9
|
-
const createBout = (overrides = {}) => ({
|
|
10
|
-
BoutId: 42,
|
|
11
|
-
DEBoutId: 1,
|
|
12
|
-
RoundLabel: 'Final',
|
|
13
|
-
Status: 'Scheduled',
|
|
14
|
-
Score1: 4,
|
|
15
|
-
Score2: 3,
|
|
16
|
-
Person1: { PersonId: 1, DisplayName: 'Fencer One' },
|
|
17
|
-
Person2: { PersonId: 2, DisplayName: 'Fencer Two' },
|
|
18
|
-
...overrides
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
const mountBracket = (bouts = [createBout()], optionalProps = {}) =>
|
|
22
|
-
shallowMount(Bracket, {
|
|
23
|
-
props: {
|
|
24
|
-
bouts,
|
|
25
|
-
eventRules: [],
|
|
26
|
-
hostingClubColors: {},
|
|
27
|
-
connectedToServer: true,
|
|
28
|
-
...optionalProps
|
|
29
|
-
}
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('Bracket bout interaction', () => {
|
|
33
|
-
it('emits directing intent for a scheduled bout and waits for Active props', async () => {
|
|
34
|
-
const bout = createBout();
|
|
35
|
-
const wrapper = mountBracket([bout]);
|
|
36
|
-
|
|
37
|
-
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
38
|
-
await wrapper.vm.$nextTick();
|
|
39
|
-
|
|
40
|
-
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
41
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
42
|
-
|
|
43
|
-
await wrapper.setProps({ bouts: [{ ...bout, Status: 'Active' }] });
|
|
44
|
-
|
|
45
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(true);
|
|
46
|
-
expect(wrapper.findComponent(DirectorModal).props('bout').Status).toBe('Active');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('resumes an active bout through the directing path', async () => {
|
|
50
|
-
const bout = createBout({ Status: 'Active' });
|
|
51
|
-
const wrapper = mountBracket([bout]);
|
|
52
|
-
|
|
53
|
-
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
54
|
-
await wrapper.vm.$nextTick();
|
|
55
|
-
|
|
56
|
-
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
57
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('opens completed bouts for editing instead of directing', async () => {
|
|
61
|
-
const bout = createBout({ Status: 'Completed' });
|
|
62
|
-
const wrapper = mountBracket([bout]);
|
|
63
|
-
|
|
64
|
-
wrapper.findComponent(Tableau).vm.$emit('action:editBout', bout);
|
|
65
|
-
await wrapper.vm.$nextTick();
|
|
66
|
-
|
|
67
|
-
expect(wrapper.findComponent(EditBoutModal).props('show')).toBe(true);
|
|
68
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
69
|
-
expect(wrapper.emitted('update:directing-bout')).toBeUndefined();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('does not direct a BYE bout', async () => {
|
|
73
|
-
const bout = createBout({
|
|
74
|
-
Person2: { PersonId: 0, DisplayName: 'BYE' }
|
|
75
|
-
});
|
|
76
|
-
const wrapper = mountBracket([bout]);
|
|
77
|
-
|
|
78
|
-
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
79
|
-
await wrapper.vm.$nextTick();
|
|
80
|
-
|
|
81
|
-
expect(wrapper.emitted('update:directing-bout')).toBeUndefined();
|
|
82
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
it('closes the scoring modal without changing status or scores', async () => {
|
|
86
|
-
const bout = createBout({ Status: 'Active', Score1: 6, Score2: 5 });
|
|
87
|
-
const wrapper = mountBracket([bout]);
|
|
88
|
-
|
|
89
|
-
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
90
|
-
await wrapper.vm.$nextTick();
|
|
91
|
-
wrapper.findComponent(DirectorModal).vm.$emit('update:closeModal', bout);
|
|
92
|
-
await wrapper.vm.$nextTick();
|
|
93
|
-
|
|
94
|
-
expect(wrapper.vm.boutsData[0]).toMatchObject({ Status: 'Active', Score1: 6, Score2: 5 });
|
|
95
|
-
expect(wrapper.emitted('update:bout')).toBeUndefined();
|
|
96
|
-
expect(wrapper.emitted('close:directing-bout')).toEqual([[bout]]);
|
|
97
|
-
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('emits submitted bout data without assigning completion fields', async () => {
|
|
101
|
-
const bout = createBout({ Status: 'Active' });
|
|
102
|
-
const submittedBout = { ...bout, Score1: 7 };
|
|
103
|
-
const wrapper = mountBracket([bout]);
|
|
104
|
-
|
|
105
|
-
wrapper.findComponent(DirectorModal).vm.$emit('submit:bout', submittedBout);
|
|
106
|
-
await wrapper.vm.$nextTick();
|
|
107
|
-
|
|
108
|
-
const emittedBout = wrapper.emitted('submit:bout')[0][0];
|
|
109
|
-
expect(emittedBout).toEqual(submittedBout);
|
|
110
|
-
expect(emittedBout.Status).toBe('Active');
|
|
111
|
-
expect(emittedBout.Ended).toBeUndefined();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it('does not render a director-selection modal', () => {
|
|
115
|
-
const wrapper = mountBracket();
|
|
116
|
-
|
|
117
|
-
expect(wrapper.findComponent({ name: 'AssignDirectorModal' }).exists()).toBe(false);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('renders optional planned suggestions without changing the directing contract', async () => {
|
|
121
|
-
const bout = createBout();
|
|
122
|
-
const plannedDirectingStations = [
|
|
123
|
-
{
|
|
124
|
-
RingId: 1,
|
|
125
|
-
RingName: 'Ring 1',
|
|
126
|
-
Color: '#2563eb',
|
|
127
|
-
Director: { PersonId: 1001, DisplayName: 'Alex Morgan' }
|
|
128
|
-
}
|
|
129
|
-
];
|
|
130
|
-
const plannedBoutAssignments = [{ DEBoutId: bout.DEBoutId, RingId: 1 }];
|
|
131
|
-
const wrapper = mountBracket([bout], {
|
|
132
|
-
plannedDirectingStations,
|
|
133
|
-
plannedBoutAssignments,
|
|
134
|
-
viewerPersonId: 1001
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).props('stations')).toEqual(plannedDirectingStations);
|
|
138
|
-
expect(wrapper.findComponent(Tableau).props()).toMatchObject({
|
|
139
|
-
plannedDirectingStations,
|
|
140
|
-
plannedBoutAssignments,
|
|
141
|
-
viewerPersonId: 1001
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
145
|
-
await wrapper.vm.$nextTick();
|
|
146
|
-
|
|
147
|
-
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
148
|
-
expect(wrapper.emitted('assign:director')).toBeUndefined();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('emits add-planned-director intent from the assignment panel', async () => {
|
|
152
|
-
const wrapper = mountBracket([], { showPlannedDirectorAssignments: true });
|
|
153
|
-
|
|
154
|
-
wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit('request:add-planned-director');
|
|
155
|
-
await wrapper.vm.$nextTick();
|
|
156
|
-
|
|
157
|
-
expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('does not render planned directing controls when explicitly disabled', () => {
|
|
161
|
-
const wrapper = mountBracket(undefined, {
|
|
162
|
-
showPlannedDirectorAssignments: false,
|
|
163
|
-
showBoutDirectorAssignment: false
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).exists()).toBe(false);
|
|
167
|
-
expect(wrapper.findComponent(Tableau).props('plannedDirectingStations')).toEqual([]);
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
describe('Bracket assignment defaults', () => {
|
|
172
|
-
it('shows assignment actions and the director panel without opt-in props', () => {
|
|
173
|
-
const wrapper = mountBracket();
|
|
174
|
-
expect(wrapper.findComponent(Tableau).props('showBoutDirectorAssignment')).toBe(true);
|
|
175
|
-
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).exists()).toBe(true);
|
|
176
|
-
wrapper.unmount();
|
|
177
|
-
});
|
|
178
|
-
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import DirectorModal from '../../../Molecules/Modals/DirectorModal/DirectorModal.vue';
|
|
4
|
+
import EditBoutModal from '../../../Molecules/Modals/EditBoutModal/EditBoutModal.vue';
|
|
5
|
+
import Tableau from '../../../Organisms/Containers/Tableau/Tableau.vue';
|
|
6
|
+
import BracketDirectorAssignmentPanel from '../../../Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue';
|
|
7
|
+
import Bracket from './Bracket.vue';
|
|
8
|
+
|
|
9
|
+
const createBout = (overrides = {}) => ({
|
|
10
|
+
BoutId: 42,
|
|
11
|
+
DEBoutId: 1,
|
|
12
|
+
RoundLabel: 'Final',
|
|
13
|
+
Status: 'Scheduled',
|
|
14
|
+
Score1: 4,
|
|
15
|
+
Score2: 3,
|
|
16
|
+
Person1: { PersonId: 1, DisplayName: 'Fencer One' },
|
|
17
|
+
Person2: { PersonId: 2, DisplayName: 'Fencer Two' },
|
|
18
|
+
...overrides
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const mountBracket = (bouts = [createBout()], optionalProps = {}) =>
|
|
22
|
+
shallowMount(Bracket, {
|
|
23
|
+
props: {
|
|
24
|
+
bouts,
|
|
25
|
+
eventRules: [],
|
|
26
|
+
hostingClubColors: {},
|
|
27
|
+
connectedToServer: true,
|
|
28
|
+
...optionalProps
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('Bracket bout interaction', () => {
|
|
33
|
+
it('emits directing intent for a scheduled bout and waits for Active props', async () => {
|
|
34
|
+
const bout = createBout();
|
|
35
|
+
const wrapper = mountBracket([bout]);
|
|
36
|
+
|
|
37
|
+
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
38
|
+
await wrapper.vm.$nextTick();
|
|
39
|
+
|
|
40
|
+
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
41
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
42
|
+
|
|
43
|
+
await wrapper.setProps({ bouts: [{ ...bout, Status: 'Active' }] });
|
|
44
|
+
|
|
45
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(true);
|
|
46
|
+
expect(wrapper.findComponent(DirectorModal).props('bout').Status).toBe('Active');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('resumes an active bout through the directing path', async () => {
|
|
50
|
+
const bout = createBout({ Status: 'Active' });
|
|
51
|
+
const wrapper = mountBracket([bout]);
|
|
52
|
+
|
|
53
|
+
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
54
|
+
await wrapper.vm.$nextTick();
|
|
55
|
+
|
|
56
|
+
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
57
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('opens completed bouts for editing instead of directing', async () => {
|
|
61
|
+
const bout = createBout({ Status: 'Completed' });
|
|
62
|
+
const wrapper = mountBracket([bout]);
|
|
63
|
+
|
|
64
|
+
wrapper.findComponent(Tableau).vm.$emit('action:editBout', bout);
|
|
65
|
+
await wrapper.vm.$nextTick();
|
|
66
|
+
|
|
67
|
+
expect(wrapper.findComponent(EditBoutModal).props('show')).toBe(true);
|
|
68
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
69
|
+
expect(wrapper.emitted('update:directing-bout')).toBeUndefined();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('does not direct a BYE bout', async () => {
|
|
73
|
+
const bout = createBout({
|
|
74
|
+
Person2: { PersonId: 0, DisplayName: 'BYE' }
|
|
75
|
+
});
|
|
76
|
+
const wrapper = mountBracket([bout]);
|
|
77
|
+
|
|
78
|
+
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
79
|
+
await wrapper.vm.$nextTick();
|
|
80
|
+
|
|
81
|
+
expect(wrapper.emitted('update:directing-bout')).toBeUndefined();
|
|
82
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('closes the scoring modal without changing status or scores', async () => {
|
|
86
|
+
const bout = createBout({ Status: 'Active', Score1: 6, Score2: 5 });
|
|
87
|
+
const wrapper = mountBracket([bout]);
|
|
88
|
+
|
|
89
|
+
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
90
|
+
await wrapper.vm.$nextTick();
|
|
91
|
+
wrapper.findComponent(DirectorModal).vm.$emit('update:closeModal', bout);
|
|
92
|
+
await wrapper.vm.$nextTick();
|
|
93
|
+
|
|
94
|
+
expect(wrapper.vm.boutsData[0]).toMatchObject({ Status: 'Active', Score1: 6, Score2: 5 });
|
|
95
|
+
expect(wrapper.emitted('update:bout')).toBeUndefined();
|
|
96
|
+
expect(wrapper.emitted('close:directing-bout')).toEqual([[bout]]);
|
|
97
|
+
expect(wrapper.findComponent(DirectorModal).props('show')).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('emits submitted bout data without assigning completion fields', async () => {
|
|
101
|
+
const bout = createBout({ Status: 'Active' });
|
|
102
|
+
const submittedBout = { ...bout, Score1: 7 };
|
|
103
|
+
const wrapper = mountBracket([bout]);
|
|
104
|
+
|
|
105
|
+
wrapper.findComponent(DirectorModal).vm.$emit('submit:bout', submittedBout);
|
|
106
|
+
await wrapper.vm.$nextTick();
|
|
107
|
+
|
|
108
|
+
const emittedBout = wrapper.emitted('submit:bout')[0][0];
|
|
109
|
+
expect(emittedBout).toEqual(submittedBout);
|
|
110
|
+
expect(emittedBout.Status).toBe('Active');
|
|
111
|
+
expect(emittedBout.Ended).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('does not render a director-selection modal', () => {
|
|
115
|
+
const wrapper = mountBracket();
|
|
116
|
+
|
|
117
|
+
expect(wrapper.findComponent({ name: 'AssignDirectorModal' }).exists()).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('renders optional planned suggestions without changing the directing contract', async () => {
|
|
121
|
+
const bout = createBout();
|
|
122
|
+
const plannedDirectingStations = [
|
|
123
|
+
{
|
|
124
|
+
RingId: 1,
|
|
125
|
+
RingName: 'Ring 1',
|
|
126
|
+
Color: '#2563eb',
|
|
127
|
+
Director: { PersonId: 1001, DisplayName: 'Alex Morgan' }
|
|
128
|
+
}
|
|
129
|
+
];
|
|
130
|
+
const plannedBoutAssignments = [{ DEBoutId: bout.DEBoutId, RingId: 1 }];
|
|
131
|
+
const wrapper = mountBracket([bout], {
|
|
132
|
+
plannedDirectingStations,
|
|
133
|
+
plannedBoutAssignments,
|
|
134
|
+
viewerPersonId: 1001
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).props('stations')).toEqual(plannedDirectingStations);
|
|
138
|
+
expect(wrapper.findComponent(Tableau).props()).toMatchObject({
|
|
139
|
+
plannedDirectingStations,
|
|
140
|
+
plannedBoutAssignments,
|
|
141
|
+
viewerPersonId: 1001
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
wrapper.findComponent(Tableau).vm.$emit('action:directingBout', bout);
|
|
145
|
+
await wrapper.vm.$nextTick();
|
|
146
|
+
|
|
147
|
+
expect(wrapper.emitted('update:directing-bout')).toEqual([[bout]]);
|
|
148
|
+
expect(wrapper.emitted('assign:director')).toBeUndefined();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('emits add-planned-director intent from the assignment panel', async () => {
|
|
152
|
+
const wrapper = mountBracket([], { showPlannedDirectorAssignments: true });
|
|
153
|
+
|
|
154
|
+
wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit('request:add-planned-director');
|
|
155
|
+
await wrapper.vm.$nextTick();
|
|
156
|
+
|
|
157
|
+
expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('does not render planned directing controls when explicitly disabled', () => {
|
|
161
|
+
const wrapper = mountBracket(undefined, {
|
|
162
|
+
showPlannedDirectorAssignments: false,
|
|
163
|
+
showBoutDirectorAssignment: false
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).exists()).toBe(false);
|
|
167
|
+
expect(wrapper.findComponent(Tableau).props('plannedDirectingStations')).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
describe('Bracket assignment defaults', () => {
|
|
172
|
+
it('shows assignment actions and the director panel without opt-in props', () => {
|
|
173
|
+
const wrapper = mountBracket();
|
|
174
|
+
expect(wrapper.findComponent(Tableau).props('showBoutDirectorAssignment')).toBe(true);
|
|
175
|
+
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).exists()).toBe(true);
|
|
176
|
+
wrapper.unmount();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('forwards removal of a right-column director without changing authoritative bout fields', async () => {
|
|
180
|
+
const station = { RingId: 1, RingName: 'Ring 1', Director: { PersonId: 99, DisplayName: 'Dave Smith' } };
|
|
181
|
+
const bout = createBout({ Status: 'Active', RefereeId: 99, RefName: 'Dave Smith' });
|
|
182
|
+
const wrapper = mountBracket([bout], { plannedDirectingStations: [station] });
|
|
183
|
+
wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit('request:remove-planned-director', station);
|
|
184
|
+
expect(wrapper.emitted('request:remove-planned-director')).toEqual([[station]]);
|
|
185
|
+
expect(wrapper.emitted('update:bout')).toBeUndefined();
|
|
186
|
+
expect(wrapper.vm.boutsData).toEqual([bout]);
|
|
187
|
+
expect(wrapper.props('plannedDirectingStations')).toEqual([station]);
|
|
188
|
+
await wrapper.setProps({ plannedDirectingStations: [] });
|
|
189
|
+
expect(wrapper.findComponent(BracketDirectorAssignmentPanel).props('stations')).toEqual([]);
|
|
190
|
+
wrapper.unmount();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('opens the staff-fed modal and forwards detached roster save intent without changing bout or station props', async () => {
|
|
194
|
+
const selected = {
|
|
195
|
+
RingId: 1,
|
|
196
|
+
RingName: 'Ring 1',
|
|
197
|
+
Director: { PersonId: 99, DisplayName: 'Dave Smith' },
|
|
198
|
+
Color: '#2563eb'
|
|
199
|
+
};
|
|
200
|
+
const wrapper = mountBracket(undefined, {
|
|
201
|
+
availableDirectors: [selected.Director],
|
|
202
|
+
rings: [{ RingId: 1, RingName: 'Ring 1' }],
|
|
203
|
+
plannedDirectingStations: [selected]
|
|
204
|
+
});
|
|
205
|
+
wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit('request:add-planned-director');
|
|
206
|
+
await wrapper.vm.$nextTick();
|
|
207
|
+
const modal = wrapper.findComponent({ name: 'BracketDirectorsModal' });
|
|
208
|
+
expect(modal.props('show')).toBe(true);
|
|
209
|
+
expect(wrapper.emitted('request:add-planned-director')).toBeUndefined();
|
|
210
|
+
modal.vm.$emit('submit:stations', []);
|
|
211
|
+
await wrapper.vm.$nextTick();
|
|
212
|
+
expect(wrapper.emitted('update:planned-directing-stations')).toEqual([[[]]]);
|
|
213
|
+
expect(wrapper.props('plannedDirectingStations')).toEqual([selected]);
|
|
214
|
+
expect(wrapper.emitted('update:bout')).toBeUndefined();
|
|
215
|
+
expect(modal.props('show')).toBe(false);
|
|
216
|
+
wrapper.unmount();
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe('Bracket assignment toolbar', () => {
|
|
221
|
+
it.each(['request:redistribute-scheduled-bouts', 'request:refresh-assignments'])(
|
|
222
|
+
'forwards %s without changing bouts or planned coverage',
|
|
223
|
+
async event => {
|
|
224
|
+
const bouts = [createBout(), createBout({ BoutId: 43, Status: 'Active', RefereeId: 99 })];
|
|
225
|
+
const stations = [{ RingId: 1, Director: { PersonId: 99, DisplayName: 'Dave Smith' } }];
|
|
226
|
+
const assignments = [{ DEBoutId: 1, RingId: 1 }];
|
|
227
|
+
const wrapper = mountBracket(bouts, {
|
|
228
|
+
availableDirectors: [stations[0].Director],
|
|
229
|
+
rings: [{ RingId: 1 }],
|
|
230
|
+
plannedDirectingStations: stations,
|
|
231
|
+
plannedBoutAssignments: assignments
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit(event);
|
|
235
|
+
await wrapper.vm.$nextTick();
|
|
236
|
+
|
|
237
|
+
expect(wrapper.emitted()).toEqual({ [event]: [[]] });
|
|
238
|
+
expect(wrapper.vm.boutsData).toEqual(bouts);
|
|
239
|
+
expect(wrapper.props('plannedDirectingStations')).toEqual(stations);
|
|
240
|
+
expect(wrapper.props('plannedBoutAssignments')).toEqual(assignments);
|
|
241
|
+
wrapper.unmount();
|
|
242
|
+
}
|
|
243
|
+
);
|
|
244
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section>
|
|
2
|
+
<section class="flex min-h-full flex-col xl:min-h-[calc(100dvh-2rem)]">
|
|
3
3
|
<EditEventsTopMenu :currentTab="'Bracket'" :tabs="tabs(role, isMobile)" @tabMenuClick="handleTabMenuClick" />
|
|
4
4
|
<ToggleHeader
|
|
5
5
|
:currentSelection="viewName"
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
:statusText="connectedToServer ? 'Connected' : 'Disconnected'"
|
|
18
18
|
/>
|
|
19
19
|
|
|
20
|
-
<div class="flex flex-col gap-4 xl:flex-row">
|
|
20
|
+
<div class="flex flex-1 flex-col gap-4 xl:flex-row">
|
|
21
21
|
<div class="min-w-0 flex-1">
|
|
22
22
|
<Tableau
|
|
23
23
|
:bouts="sortedBouts"
|
|
@@ -40,7 +40,12 @@
|
|
|
40
40
|
class="order-first flex-none xl:order-last"
|
|
41
41
|
:stations="plannedDirectingStations"
|
|
42
42
|
:showAddCard="showAddPlannedDirectorCard"
|
|
43
|
-
|
|
43
|
+
:canManageDirectors="canManagePlannedDirectors"
|
|
44
|
+
@request:manage-planned-directors="handleManagePlannedDirectors"
|
|
45
|
+
@request:redistribute-scheduled-bouts="$emit('request:redistribute-scheduled-bouts')"
|
|
46
|
+
@request:refresh-assignments="$emit('request:refresh-assignments')"
|
|
47
|
+
@request:add-planned-director="handleAddPlannedDirector"
|
|
48
|
+
@request:remove-planned-director="$emit('request:remove-planned-director', $event)"
|
|
44
49
|
/>
|
|
45
50
|
</div>
|
|
46
51
|
|
|
@@ -64,6 +69,15 @@
|
|
|
64
69
|
</div>
|
|
65
70
|
</div>
|
|
66
71
|
|
|
72
|
+
<BracketDirectorsModal
|
|
73
|
+
v-if="canManagePlannedDirectors"
|
|
74
|
+
v-model:show="showPlannedDirectorsModal"
|
|
75
|
+
:availableDirectors="availableDirectors"
|
|
76
|
+
:rings="rings"
|
|
77
|
+
:stations="plannedDirectingStations"
|
|
78
|
+
@submit:stations="handlePlannedDirectorsSubmit"
|
|
79
|
+
/>
|
|
80
|
+
|
|
67
81
|
<DirectorModal
|
|
68
82
|
:bout="selectedBout"
|
|
69
83
|
:eventRules="eventRules"
|
|
@@ -96,6 +110,8 @@ import ToggleHeader from '../../../Organisms/Headers/ToggleHeader/ToggleHeader.v
|
|
|
96
110
|
import ServerConnected from '../../../Molecules/Indicators/ServerConnected/ServerConnected.vue';
|
|
97
111
|
import BaseText from '../../../Atoms/Text/BaseText.vue';
|
|
98
112
|
import DirectorModal from '../../../Molecules/Modals/DirectorModal/DirectorModal.vue';
|
|
113
|
+
import BracketDirectorsModal from '../../../Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.vue';
|
|
114
|
+
import { cloneDirectorData } from '../../../Util/bracketDirectors.js';
|
|
99
115
|
import EditBoutModal from '../../../Molecules/Modals/EditBoutModal/EditBoutModal.vue';
|
|
100
116
|
import Tableau from '../../../Organisms/Containers/Tableau/Tableau.vue';
|
|
101
117
|
import BracketDirectorAssignmentPanel from '../../../Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue';
|
|
@@ -121,6 +137,10 @@ export default {
|
|
|
121
137
|
'penalty:remove',
|
|
122
138
|
'penalty:update',
|
|
123
139
|
'request:add-planned-director',
|
|
140
|
+
'request:remove-planned-director',
|
|
141
|
+
'request:redistribute-scheduled-bouts',
|
|
142
|
+
'request:refresh-assignments',
|
|
143
|
+
'update:planned-directing-stations',
|
|
124
144
|
'request:bout-active',
|
|
125
145
|
'request:bout-director',
|
|
126
146
|
'submit:bout',
|
|
@@ -133,12 +153,15 @@ export default {
|
|
|
133
153
|
EditBoutModal,
|
|
134
154
|
DirectorModal,
|
|
135
155
|
BracketDirectorAssignmentPanel,
|
|
156
|
+
BracketDirectorsModal,
|
|
136
157
|
BaseText,
|
|
137
158
|
ToggleHeader,
|
|
138
159
|
ServerConnected,
|
|
139
160
|
EditEventsTopMenu
|
|
140
161
|
},
|
|
141
162
|
props: {
|
|
163
|
+
availableDirectors: { type: Array, default: undefined },
|
|
164
|
+
rings: { type: Array, default: undefined },
|
|
142
165
|
showBoutDirectorAssignment: { type: Boolean, default: true },
|
|
143
166
|
bouts: {
|
|
144
167
|
type: Array,
|
|
@@ -189,6 +212,7 @@ export default {
|
|
|
189
212
|
return {
|
|
190
213
|
emptyDesertIcon,
|
|
191
214
|
showDirectorModal: false,
|
|
215
|
+
showPlannedDirectorsModal: false,
|
|
192
216
|
showEditBoutModal: false,
|
|
193
217
|
selectedBout: {},
|
|
194
218
|
tabs,
|
|
@@ -198,6 +222,9 @@ export default {
|
|
|
198
222
|
};
|
|
199
223
|
},
|
|
200
224
|
computed: {
|
|
225
|
+
canManagePlannedDirectors() {
|
|
226
|
+
return !this.isMobile && Array.isArray(this.availableDirectors) && Array.isArray(this.rings);
|
|
227
|
+
},
|
|
201
228
|
isMobile() {
|
|
202
229
|
return this.windowWidth < 768;
|
|
203
230
|
},
|
|
@@ -236,6 +263,9 @@ export default {
|
|
|
236
263
|
}
|
|
237
264
|
},
|
|
238
265
|
watch: {
|
|
266
|
+
isMobile(value) {
|
|
267
|
+
if (value) this.showPlannedDirectorsModal = false;
|
|
268
|
+
},
|
|
239
269
|
bouts: {
|
|
240
270
|
handler(newBouts) {
|
|
241
271
|
this.boutsData = this.cloneBouts(newBouts);
|
|
@@ -253,6 +283,21 @@ export default {
|
|
|
253
283
|
window.removeEventListener('resize', this.updateViewBasedOnScreenSize);
|
|
254
284
|
},
|
|
255
285
|
methods: {
|
|
286
|
+
handleAddPlannedDirector() {
|
|
287
|
+
if (this.canManagePlannedDirectors) {
|
|
288
|
+
this.handleManagePlannedDirectors();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
this.$emit('request:add-planned-director');
|
|
292
|
+
},
|
|
293
|
+
handleManagePlannedDirectors() {
|
|
294
|
+
if (!this.canManagePlannedDirectors) return;
|
|
295
|
+
this.showPlannedDirectorsModal = true;
|
|
296
|
+
},
|
|
297
|
+
handlePlannedDirectorsSubmit(stations) {
|
|
298
|
+
this.$emit('update:planned-directing-stations', cloneDirectorData(stations));
|
|
299
|
+
this.showPlannedDirectorsModal = false;
|
|
300
|
+
},
|
|
256
301
|
handleBoutDirectorRequest(bout) {
|
|
257
302
|
if (!this.canShowBoutDirectorAssignment) return;
|
|
258
303
|
const currentBout = this.boutsData.find(item => String(item.BoutId) === String(bout?.BoutId));
|
|
@@ -40,3 +40,11 @@ export const plannedDirectingStations = [
|
|
|
40
40
|
];
|
|
41
41
|
|
|
42
42
|
export const plannedSuggestionViewerId = plannedDirectingStations[0].Director.PersonId;
|
|
43
|
+
|
|
44
|
+
export const availableDirectors = plannedDirectingStations.map((station, index) => ({
|
|
45
|
+
...station.Director,
|
|
46
|
+
Color: station.Color,
|
|
47
|
+
DefaultRingId: index < 2 ? station.RingId : null
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
export const availableRings = plannedDirectingStations.map(({ RingId, RingName }) => ({ RingId, RingName }));
|