@dcrackel/hematournamentui 1.0.711 → 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 (109) hide show
  1. package/.prettierignore +7 -7
  2. package/.storybook/main.js +3 -1
  3. package/README.md +182 -148
  4. package/dist/HemaTournamentUI-lib.es.js +4150 -4039
  5. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  6. package/dist/hematournamentui-tailwind.css +11 -0
  7. package/package.json +96 -96
  8. package/prettier.config.js +16 -16
  9. package/src/main.js +1 -1
  10. package/src/mocks/getDEBouts-TableOf128.js +37 -37
  11. package/src/mocks/getDEBouts-TableOf64.js +35 -35
  12. package/src/stories/Atoms/Input/BaseInput.test.js +97 -97
  13. package/src/stories/Atoms/Input/BaseInput.vue +149 -149
  14. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -23
  15. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -35
  16. package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -0
  17. package/src/stories/Molecules/Menus/HoverActions/HoverActions.test.js +138 -0
  18. package/src/stories/Molecules/Menus/HoverActions/HoverActions.vue +212 -0
  19. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +6 -0
  20. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -18
  21. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +1 -1
  22. package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +5 -1
  23. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +19 -10
  24. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +11 -7
  25. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -33
  26. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -45
  27. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -103
  28. package/src/stories/Organisms/Cards/Director/DirectorCard.test.js +82 -0
  29. package/src/stories/Organisms/Cards/Director/DirectorCard.vue +32 -51
  30. package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -51
  31. package/src/stories/Organisms/Cards/EventCard/EventCard.stories.js +41 -1
  32. package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -0
  33. package/src/stories/Organisms/Cards/EventCard/EventCard.vue +62 -30
  34. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -298
  35. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +77 -60
  36. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +144 -143
  37. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +289 -256
  38. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -69
  39. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -80
  40. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +153 -153
  41. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +4 -0
  42. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -31
  43. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -109
  44. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -192
  45. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +1 -0
  46. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +247 -204
  47. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +89 -15
  48. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -29
  49. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -46
  50. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -21
  51. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -39
  52. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -145
  53. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -64
  54. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +36 -57
  55. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +101 -65
  56. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +14 -1
  57. package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.vue +12 -5
  58. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +52 -48
  59. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +256 -227
  60. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -226
  61. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
  62. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +137 -133
  63. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.test.js +221 -191
  64. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue +372 -366
  65. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -172
  66. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -216
  67. package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -79
  68. package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -0
  69. package/src/stories/Organisms/Wizards/EditEventWizard/WeaponSelection.test.js +138 -0
  70. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +123 -12
  71. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +178 -166
  72. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +35 -7
  73. package/src/stories/Templates/EventManagement/Bracket/BracketDirectorAssignment.test.js +242 -0
  74. package/src/stories/Templates/EventManagement/Bracket/BracketMobile.test.js +131 -0
  75. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +42 -39
  76. package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +80 -0
  77. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.js +93 -0
  78. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.test.js +152 -0
  79. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -39
  80. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -79
  81. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -93
  82. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -91
  83. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +31 -1
  84. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -121
  85. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +9 -2
  86. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +5 -4
  87. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +81 -81
  88. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -39
  89. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +101 -101
  90. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -71
  91. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +86 -86
  92. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -86
  93. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +247 -247
  94. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -170
  95. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -156
  96. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -329
  97. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -329
  98. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -42
  99. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.stories.js +58 -45
  100. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -84
  101. package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +114 -114
  102. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -363
  103. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -116
  104. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +307 -307
  105. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -45
  106. package/src/stories/Util/Validations/PriceValidation.js +6 -6
  107. package/src/stories/Util/boutDirectorAssignment.js +46 -0
  108. package/src/stories/Util/registrationStatus.js +11 -0
  109. 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);