@dcrackel/hematournamentui 1.0.712 → 1.0.713

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.
Files changed (99) hide show
  1. package/.prettierignore +7 -7
  2. package/.storybook/main.js +3 -1
  3. package/README.md +30 -0
  4. package/package.json +96 -96
  5. package/prettier.config.js +16 -16
  6. package/src/main.js +1 -1
  7. package/src/mocks/getDEBouts-TableOf128.js +37 -37
  8. package/src/mocks/getDEBouts-TableOf64.js +35 -35
  9. package/src/stories/Atoms/Input/BaseInput.test.js +97 -97
  10. package/src/stories/Atoms/Input/BaseInput.vue +149 -149
  11. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -23
  12. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -35
  13. package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -30
  14. package/src/stories/Molecules/Menus/HoverActions/HoverActions.test.js +138 -0
  15. package/src/stories/Molecules/Menus/HoverActions/HoverActions.vue +212 -0
  16. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +6 -0
  17. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -18
  18. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +1 -1
  19. package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +5 -1
  20. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +19 -10
  21. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +11 -7
  22. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -33
  23. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -45
  24. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -103
  25. package/src/stories/Organisms/Cards/Director/DirectorCard.test.js +82 -0
  26. package/src/stories/Organisms/Cards/Director/DirectorCard.vue +32 -51
  27. package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -51
  28. package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -106
  29. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -298
  30. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +77 -60
  31. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +144 -143
  32. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +289 -256
  33. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -69
  34. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -80
  35. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +153 -153
  36. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +4 -0
  37. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -31
  38. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -109
  39. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -192
  40. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +1 -0
  41. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +247 -204
  42. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +89 -15
  43. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -29
  44. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -46
  45. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -21
  46. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -39
  47. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -145
  48. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -64
  49. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +36 -57
  50. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +101 -65
  51. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +14 -1
  52. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +52 -52
  53. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +10 -3
  54. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -226
  55. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
  56. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +137 -137
  57. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -172
  58. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -216
  59. package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -79
  60. package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -29
  61. package/src/stories/Organisms/Wizards/EditEventWizard/WeaponSelection.test.js +138 -0
  62. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +123 -12
  63. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +178 -166
  64. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +35 -7
  65. package/src/stories/Templates/EventManagement/Bracket/BracketDirectorAssignment.test.js +242 -0
  66. package/src/stories/Templates/EventManagement/Bracket/BracketMobile.test.js +131 -0
  67. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +42 -39
  68. package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +80 -0
  69. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.js +93 -0
  70. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.test.js +152 -0
  71. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -39
  72. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -79
  73. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -93
  74. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -91
  75. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -121
  76. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +9 -2
  77. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +5 -4
  78. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +81 -81
  79. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -39
  80. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +101 -101
  81. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -71
  82. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +86 -86
  83. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -86
  84. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +247 -247
  85. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -170
  86. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -156
  87. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -329
  88. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -329
  89. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -42
  90. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -84
  91. package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +114 -114
  92. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -363
  93. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -116
  94. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +307 -307
  95. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -45
  96. package/src/stories/Util/Validations/PriceValidation.js +6 -6
  97. package/src/stories/Util/boutDirectorAssignment.js +46 -0
  98. package/src/stories/Util/registrationStatus.js +11 -11
  99. package/src/stories/Util/tabs.js +19 -47
@@ -1,166 +1,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 optional behavior is disabled', () => {
161
- const wrapper = mountBracket();
162
-
163
- expect(wrapper.findComponent(BracketDirectorAssignmentPanel).exists()).toBe(false);
164
- expect(wrapper.findComponent(Tableau).props('plannedDirectingStations')).toEqual([]);
165
- });
166
- });
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,6 +1,6 @@
1
1
  <template>
2
2
  <section>
3
- <EditEventsTopMenu :currentTab="'Bracket'" :tabs="tabs(role, isListView)" @tabMenuClick="handleTabMenuClick" />
3
+ <EditEventsTopMenu :currentTab="'Bracket'" :tabs="tabs(role, isMobile)" @tabMenuClick="handleTabMenuClick" />
4
4
  <ToggleHeader
5
5
  :currentSelection="viewName"
6
6
  :key="viewName"
@@ -11,6 +11,12 @@
11
11
  @update:selection="handleViewChange"
12
12
  />
13
13
 
14
+ <ServerConnected
15
+ class="mb-2 md:hidden"
16
+ :connectedToServer="connectedToServer"
17
+ :statusText="connectedToServer ? 'Connected' : 'Disconnected'"
18
+ />
19
+
14
20
  <div class="flex flex-col gap-4 xl:flex-row">
15
21
  <div class="min-w-0 flex-1">
16
22
  <Tableau
@@ -22,6 +28,8 @@
22
28
  :plannedDirectingStations="plannedDirectingStations"
23
29
  :plannedBoutAssignments="plannedBoutAssignments"
24
30
  :viewerPersonId="viewerPersonId"
31
+ :showBoutDirectorAssignment="canShowBoutDirectorAssignment"
32
+ @request:bout-director="handleBoutDirectorRequest"
25
33
  @action:editBout="editBout"
26
34
  @action:directingBout="directBout"
27
35
  />
@@ -38,7 +46,7 @@
38
46
 
39
47
  <div v-if="bouts.length < 1" class="w-full flex justify-center items-center">
40
48
  <div class="text-center mb-10">
41
- <img alt="Small desert scene with wind blowing." class="w-96 mx-auto" :src="emptyDesertIcon" />
49
+ <img alt="Small desert scene with wind blowing." class="hidden md:block w-96 mx-auto" :src="emptyDesertIcon" />
42
50
  <BaseText text="Nothing to Show Yet!" size="lg" color="quinary" weight="bold" class="text-center" />
43
51
  <BaseText
44
52
  text="Check Back when this event has progress to Direct Elimination"
@@ -47,6 +55,7 @@
47
55
  weight="normal"
48
56
  />
49
57
  <BaseText
58
+ class="hidden md:block"
50
59
  text="The Generate bracket button will appear on the bottom of the pools screen"
51
60
  size="sm"
52
61
  color="quinary"
@@ -84,6 +93,7 @@
84
93
  <script>
85
94
  import EditEventsTopMenu from '../../Menus/EditEventsTopMenu/EditEventsTopMenu.vue';
86
95
  import ToggleHeader from '../../../Organisms/Headers/ToggleHeader/ToggleHeader.vue';
96
+ import ServerConnected from '../../../Molecules/Indicators/ServerConnected/ServerConnected.vue';
87
97
  import BaseText from '../../../Atoms/Text/BaseText.vue';
88
98
  import DirectorModal from '../../../Molecules/Modals/DirectorModal/DirectorModal.vue';
89
99
  import EditBoutModal from '../../../Molecules/Modals/EditBoutModal/EditBoutModal.vue';
@@ -91,6 +101,7 @@ import Tableau from '../../../Organisms/Containers/Tableau/Tableau.vue';
91
101
  import BracketDirectorAssignmentPanel from '../../../Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue';
92
102
  import emptyDesertIcon from '../../../../assets/empty_desert_icon.png';
93
103
  import { tabs } from '../../../Util/tabs.js';
104
+ import { canAssignBoutDirector } from '../../../Util/boutDirectorAssignment.js';
94
105
 
95
106
  const isByeFencer = person => {
96
107
  if (!person?.PersonId || Number(person.PersonId) === 0) return true;
@@ -111,6 +122,7 @@ export default {
111
122
  'penalty:update',
112
123
  'request:add-planned-director',
113
124
  'request:bout-active',
125
+ 'request:bout-director',
114
126
  'submit:bout',
115
127
  'tab:menu-click',
116
128
  'update:bout',
@@ -123,9 +135,11 @@ export default {
123
135
  BracketDirectorAssignmentPanel,
124
136
  BaseText,
125
137
  ToggleHeader,
138
+ ServerConnected,
126
139
  EditEventsTopMenu
127
140
  },
128
141
  props: {
142
+ showBoutDirectorAssignment: { type: Boolean, default: true },
129
143
  bouts: {
130
144
  type: Array,
131
145
  required: true
@@ -164,7 +178,7 @@ export default {
164
178
  },
165
179
  showPlannedDirectorAssignments: {
166
180
  type: Boolean,
167
- default: false
181
+ default: true
168
182
  },
169
183
  showAddPlannedDirectorCard: {
170
184
  type: Boolean,
@@ -180,12 +194,21 @@ export default {
180
194
  tabs,
181
195
  viewName: 'Tableau',
182
196
  boutsData: this.cloneBouts(this.bouts),
183
- isListView: false
197
+ windowWidth: typeof window === 'undefined' ? 1024 : window.innerWidth
184
198
  };
185
199
  },
186
200
  computed: {
201
+ isMobile() {
202
+ return this.windowWidth < 768;
203
+ },
204
+ isListView() {
205
+ return this.isMobile || this.viewName === 'List View';
206
+ },
207
+ canShowBoutDirectorAssignment() {
208
+ return !this.isMobile && this.showBoutDirectorAssignment;
209
+ },
187
210
  showPlannedAssignmentPanel() {
188
- return this.showPlannedDirectorAssignments || this.plannedDirectingStations.length > 0;
211
+ return !this.isMobile && (this.showPlannedDirectorAssignments || this.plannedDirectingStations.length > 0);
189
212
  },
190
213
  isCountingBackwardsMaxScore() {
191
214
  // 1) see if PoolScoreDirection exists and is explicitly "false"
@@ -230,6 +253,12 @@ export default {
230
253
  window.removeEventListener('resize', this.updateViewBasedOnScreenSize);
231
254
  },
232
255
  methods: {
256
+ handleBoutDirectorRequest(bout) {
257
+ if (!this.canShowBoutDirectorAssignment) return;
258
+ const currentBout = this.boutsData.find(item => String(item.BoutId) === String(bout?.BoutId));
259
+ if (!canAssignBoutDirector(currentBout)) return;
260
+ this.$emit('request:bout-director', JSON.parse(JSON.stringify(currentBout)));
261
+ },
233
262
  handleCloseModal() {
234
263
  const closedDirectingBout = this.showDirectorModal && this.selectedBout?.BoutId ? { ...this.selectedBout } : null;
235
264
  this.showDirectorModal = false;
@@ -242,7 +271,6 @@ export default {
242
271
  },
243
272
  handleViewChange(value) {
244
273
  this.viewName = value;
245
- this.isListView = value === 'List View';
246
274
  },
247
275
  handleUpdateBout(bout) {
248
276
  if (!bout?.BoutId) return;
@@ -279,7 +307,7 @@ export default {
279
307
  this.selectedBout = updatedBout;
280
308
  },
281
309
  updateViewBasedOnScreenSize() {
282
- this.isListView = window.innerWidth <= 768;
310
+ this.windowWidth = window.innerWidth;
283
311
  },
284
312
  handleUpdatePenalty(penalty) {
285
313
  this.$emit('penalty:update', penalty);