@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
@@ -0,0 +1,138 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import BasicEventInfo from './BasicEventInfo/BasicEventInfo.vue';
4
+ import EditEventWizard from './EditEventWizard.vue';
5
+ import DropDownMenu from '../../ComplexInputs/DropDown/DropDownMenu.vue';
6
+
7
+ const wrappers = [];
8
+ const weapons = [
9
+ { text: 'Sword & Buckler', link: 8 },
10
+ { text: 'MG Longsword', link: 17 },
11
+ { text: 'Longsword', link: 1 }
12
+ ];
13
+
14
+ afterEach(() => wrappers.splice(0).forEach(wrapper => wrapper.unmount()));
15
+
16
+ function mountBasic(eventConfig, options = weapons) {
17
+ const wrapper = mount(BasicEventInfo, {
18
+ props: {
19
+ eventConfig,
20
+ weapons: options,
21
+ validation: { EventName: { invalid: false } },
22
+ steps: [],
23
+ currentStep: 0
24
+ },
25
+ global: { stubs: { DatePicker: true, StepIndicator: true } }
26
+ });
27
+ wrappers.push(wrapper);
28
+ return wrapper;
29
+ }
30
+
31
+ function selectedText(wrapper) {
32
+ return wrapper.findComponent(DropDownMenu).find('input').element.value;
33
+ }
34
+
35
+ describe('event weapon selection', () => {
36
+ it.each([1, '1', 8, '8', 17, '17'])('shows the actual saved weapon for ID %s without emitting a change', WeaponId => {
37
+ const wrapper = mountBasic({ WeaponId });
38
+ expect(selectedText(wrapper)).toBe(weapons.find(option => String(option.link) === String(WeaponId)).text);
39
+ expect(wrapper.emitted('update')).toBeUndefined();
40
+ });
41
+
42
+ it('accepts raw weapon records as well as dropdown options', () => {
43
+ const wrapper = mountBasic(
44
+ { WeaponId: '8' },
45
+ weapons.map(option => ({ Id: option.link, Name: option.text }))
46
+ );
47
+ expect(selectedText(wrapper)).toBe('Sword & Buckler');
48
+ expect(wrapper.findComponent(DropDownMenu).props('items')).toEqual(
49
+ weapons.map(option => ({ ...option, Id: option.link, Name: option.text }))
50
+ );
51
+ });
52
+
53
+ it.each([undefined, 99])('does not pretend an unavailable weapon %s is Longsword', WeaponId => {
54
+ const wrapper = mountBasic({ WeaponId });
55
+ expect(selectedText(wrapper)).toBe('Select a weapon');
56
+ expect(wrapper.emitted('update')).toBeUndefined();
57
+ });
58
+
59
+ it('reconciles a parent weapon change and restores that selection after closing the menu', async () => {
60
+ const wrapper = mountBasic({ WeaponId: 8 });
61
+ await wrapper.setProps({ eventConfig: { WeaponId: 1 } });
62
+ expect(selectedText(wrapper)).toBe('Longsword');
63
+ const dropdown = wrapper.findComponent(DropDownMenu);
64
+ dropdown.vm.clearInput();
65
+ dropdown.vm.restorePreviousValue();
66
+ await wrapper.vm.$nextTick();
67
+ expect(selectedText(wrapper)).toBe('Longsword');
68
+ expect(wrapper.emitted('update')).toBeUndefined();
69
+ });
70
+
71
+ it('shows the saved selection when the weapon options arrive later', async () => {
72
+ const wrapper = mountBasic({ WeaponId: 8 }, []);
73
+ await wrapper.setProps({ weapons });
74
+ expect(selectedText(wrapper)).toBe('Sword & Buckler');
75
+ expect(wrapper.emitted('update')).toBeUndefined();
76
+ });
77
+
78
+ it('emits the chosen option ID only after an explicit selection', async () => {
79
+ const wrapper = mountBasic({ WeaponId: 8 });
80
+ const dropdown = wrapper.findComponent(DropDownMenu);
81
+ dropdown.vm.handleDropDown();
82
+ await wrapper.vm.$nextTick();
83
+ await dropdown
84
+ .findAll('a')
85
+ .find(option => option.text() === 'Longsword')
86
+ .trigger('click');
87
+ expect(wrapper.emitted('update')).toEqual([[{ WeaponId: 1 }]]);
88
+ });
89
+
90
+ it('preserves the saved weapon when another field is edited and saves a deliberate weapon change', async () => {
91
+ const event = { EventId: 12, EventName: 'Example Event', EventPrice: 25, WeaponId: '8', rules: [] };
92
+ const wrapper = mount(EditEventWizard, {
93
+ props: { event, weapons, editMode: true },
94
+ global: { stubs: { DatePicker: true, StepIndicator: true } }
95
+ });
96
+ wrappers.push(wrapper);
97
+ await wrapper.vm.$nextTick();
98
+ expect(selectedText(wrapper)).toBe('Sword & Buckler');
99
+ wrapper.vm.handleValueChange({ EventName: 'Renamed Event' });
100
+ wrapper.vm.handleUpdate();
101
+ expect(wrapper.emitted('save:step')[0][0].event.WeaponId).toBe('8');
102
+
103
+ wrapper.findComponent(DropDownMenu).vm.handleClick(weapons.find(option => option.link === 17));
104
+ await wrapper.vm.$nextTick();
105
+ wrapper.vm.handleUpdate();
106
+ expect(wrapper.emitted('save:step')[1][0].event.WeaponId).toBe(17);
107
+ expect(event.WeaponId).toBe('8');
108
+ });
109
+ });
110
+
111
+ it.each([false, true])(
112
+ 'actual dropdown choices retain catalog IDs through submit with edit mode %s',
113
+ async editMode => {
114
+ const event = { EventId: 230, EventName: 'MG Longsword', EventPrice: 45, WeaponId: 17, rules: [] };
115
+ const wrapper = mount(EditEventWizard, {
116
+ props: { event, weapons, editMode },
117
+ global: { stubs: { DatePicker: true, StepIndicator: true } }
118
+ });
119
+ wrappers.push(wrapper);
120
+ await wrapper.vm.$nextTick();
121
+ expect(selectedText(wrapper)).toBe('MG Longsword');
122
+ for (const option of weapons) {
123
+ const dropdown = wrapper.findComponent(DropDownMenu);
124
+ dropdown.vm.handleDropDown();
125
+ await wrapper.vm.$nextTick();
126
+ await dropdown
127
+ .findAll('a')
128
+ .find(item => item.text() === option.text)
129
+ .trigger('click');
130
+ expect(selectedText(wrapper)).toBe(option.text);
131
+ wrapper.vm.handleSubmit();
132
+ const eventName = editMode ? 'save:step' : 'update:submit';
133
+ const submitted = wrapper.emitted(eventName).at(-1)[0];
134
+ expect((editMode ? submitted.event : submitted).WeaponId).toBe(option.link);
135
+ }
136
+ expect(event.WeaponId).toBe(17);
137
+ }
138
+ );
@@ -1,5 +1,6 @@
1
- import { ref, watch } from 'vue';
1
+ import { computed, nextTick, ref, watch } from 'vue';
2
2
  import Bracket from './Bracket.vue';
3
+ import { distributeStoryAssignments } from './BracketStoryAssignments.js';
3
4
  import {
4
5
  activateStoryBout,
5
6
  canStoryDirectorDirectBout,
@@ -8,11 +9,7 @@ import {
8
9
  getStoryDirectorId,
9
10
  replaceStoryBout
10
11
  } from './BracketStoryHarness.js';
11
- import {
12
- plannedBoutAssignments,
13
- plannedDirectingStations,
14
- plannedSuggestionViewerId
15
- } from './BracketPlannedSuggestions.js';
12
+ import { plannedDirectingStations, plannedSuggestionViewerId } from './BracketPlannedSuggestions.js';
16
13
  //import getDEWithBouts from '../../../../mocks/getDEWithBouts.js';
17
14
  import getDEWithBouts6Fencers from '../../../../mocks/getDeBouts-6fencers.js';
18
15
  import getDEWithBouts12Fencers from '../../../../mocks/getDeBouts-12fencers.js';
@@ -30,11 +27,54 @@ const renderInteractiveBracket = args => ({
30
27
  setup() {
31
28
  const storyBouts = ref(cloneStoryBouts(args.bouts));
32
29
  const storyStations = ref(cloneStations(args.plannedDirectingStations));
30
+ const storyAssignments = ref([]);
31
+ const canAddDirector = computed(() =>
32
+ plannedDirectingStations.some(
33
+ station => !storyStations.value.some(item => String(item.RingId) === String(station.RingId))
34
+ )
35
+ );
36
+ const redistributeAssignments = () => {
37
+ storyAssignments.value = distributeStoryAssignments({
38
+ bouts: storyBouts.value,
39
+ stations: storyStations.value,
40
+ previousAssignments: storyAssignments.value
41
+ });
42
+ };
43
+ const applyProvidedAssignments = assignments => {
44
+ redistributeAssignments();
45
+ const providedIds = new Set(assignments.map(assignment => String(assignment.DEBoutId)));
46
+ storyAssignments.value = [
47
+ ...storyAssignments.value.filter(assignment => !providedIds.has(String(assignment.DEBoutId))),
48
+ ...assignments.map(assignment => ({ ...assignment }))
49
+ ];
50
+ };
51
+ const assignmentBout = ref(null);
52
+ const directorPicker = ref(null);
53
+ const assignmentDialog = ref(null);
54
+
55
+ watch(() => args.plannedBoutAssignments, applyProvidedAssignments, { deep: true, immediate: true });
56
+
57
+ const requestBoutDirector = async bout => {
58
+ assignmentBout.value = bout;
59
+ await nextTick();
60
+ assignmentDialog.value?.showModal();
61
+ directorPicker.value?.focus();
62
+ };
63
+
64
+ const assignPlannedDirector = ringId => {
65
+ const bout = assignmentBout.value;
66
+ const station = storyStations.value.find(item => String(item.RingId) === String(ringId));
67
+ if (!bout || !station) return;
68
+ const remaining = storyAssignments.value.filter(item => String(item.DEBoutId) !== String(bout.DEBoutId));
69
+ storyAssignments.value = [...remaining, { DEBoutId: bout.DEBoutId, RingId: station.RingId }];
70
+ assignmentBout.value = null;
71
+ };
33
72
 
34
73
  watch(
35
74
  () => args.bouts,
36
75
  bouts => {
37
76
  storyBouts.value = cloneStoryBouts(bouts);
77
+ redistributeAssignments();
38
78
  },
39
79
  { deep: true }
40
80
  );
@@ -43,6 +83,7 @@ const renderInteractiveBracket = args => ({
43
83
  () => args.plannedDirectingStations,
44
84
  stations => {
45
85
  storyStations.value = cloneStations(stations);
86
+ redistributeAssignments();
46
87
  },
47
88
  { deep: true }
48
89
  );
@@ -59,6 +100,7 @@ const renderInteractiveBracket = args => ({
59
100
  const nextStation = plannedDirectingStations.find(station => !assignedRingIds.has(String(station.RingId)));
60
101
  if (!nextStation) return;
61
102
  storyStations.value = [...storyStations.value, ...cloneStations([nextStation])];
103
+ redistributeAssignments();
62
104
  };
63
105
 
64
106
  const handleDirectingBout = bout => {
@@ -75,6 +117,13 @@ const renderInteractiveBracket = args => ({
75
117
  args,
76
118
  storyBouts,
77
119
  storyStations,
120
+ storyAssignments,
121
+ canAddDirector,
122
+ assignmentBout,
123
+ directorPicker,
124
+ assignmentDialog,
125
+ requestBoutDirector,
126
+ assignPlannedDirector,
78
127
  handleAddPlannedDirector,
79
128
  handleCloseDirectingBout,
80
129
  handleDirectingBout,
@@ -82,6 +131,19 @@ const renderInteractiveBracket = args => ({
82
131
  };
83
132
  },
84
133
  template: `
134
+ <div>
135
+ <dialog v-if="assignmentBout" ref="assignmentDialog" aria-label="Assign director" class="fixed inset-0 m-auto rounded border border-dropdownSelect bg-white p-4 shadow-lg backdrop:bg-black/20" @cancel="assignmentBout = null">
136
+ <label class="text-sm text-primaryHighlight">
137
+ Assign director to bout {{ assignmentBout.DEBoutId }}
138
+ <select ref="directorPicker" class="ml-2 rounded border border-dropdownSelect p-2" @change="assignPlannedDirector($event.target.value)">
139
+ <option value="">Choose director</option>
140
+ <option v-for="station in storyStations" :key="station.RingId" :value="station.RingId">
141
+ {{ station.Director?.DisplayName || 'Director TBD' }} / {{ station.RingName }}
142
+ </option>
143
+ </select>
144
+ </label>
145
+ <button type="button" class="ml-4 text-sm underline" @click="assignmentBout = null">Cancel</button>
146
+ </dialog>
85
147
  <Bracket
86
148
  v-bind="args"
87
149
  :connectedToServer="args.connectedToServer"
@@ -89,6 +151,9 @@ const renderInteractiveBracket = args => ({
89
151
  :hostingClubColors="args.hostingClubColors"
90
152
  :bouts="storyBouts"
91
153
  :plannedDirectingStations="storyStations"
154
+ :plannedBoutAssignments="storyAssignments"
155
+ :showAddPlannedDirectorCard="args.showAddPlannedDirectorCard && canAddDirector"
156
+ @request:bout-director="requestBoutDirector"
92
157
  @close:directing-bout="handleCloseDirectingBout"
93
158
  @request:add-planned-director="handleAddPlannedDirector"
94
159
  @update:directing-bout="handleDirectingBout"
@@ -96,6 +161,7 @@ const renderInteractiveBracket = args => ({
96
161
  @update:bout="persistStoryBout"
97
162
  @submit:bout="persistStoryBout"
98
163
  />
164
+ </div>
99
165
  `
100
166
  });
101
167
 
@@ -110,13 +176,18 @@ export default {
110
176
  hostingClubColors: getDEWithBouts.hostingClubColors,
111
177
  connectedToServer: true,
112
178
  role: 'admin',
113
- plannedDirectingStations: [],
179
+ plannedDirectingStations: plannedDirectingStations.slice(0, 2),
114
180
  plannedBoutAssignments: [],
115
- viewerPersonId: null,
116
- showPlannedDirectorAssignments: false,
117
- showAddPlannedDirectorCard: true
181
+ viewerPersonId: plannedSuggestionViewerId,
182
+ showPlannedDirectorAssignments: true,
183
+ showAddPlannedDirectorCard: true,
184
+ showBoutDirectorAssignment: true
118
185
  },
119
186
  argTypes: {
187
+ showBoutDirectorAssignment: {
188
+ control: 'boolean',
189
+ description: 'Include Assign director in the pairing hover menu. The parent supplies staff selection and saving.'
190
+ },
120
191
  bouts: {
121
192
  control: 'object'
122
193
  },
@@ -220,10 +291,38 @@ export const PlannedDirectorSuggestions = {
220
291
  status: 'de',
221
292
  role: 'Admin',
222
293
  plannedDirectingStations: plannedDirectingStations.slice(0, 2),
223
- plannedBoutAssignments,
224
294
  viewerPersonId: plannedSuggestionViewerId,
225
295
  showPlannedDirectorAssignments: true,
226
- showAddPlannedDirectorCard: true
296
+ showAddPlannedDirectorCard: true,
297
+ showBoutDirectorAssignment: true
298
+ }
299
+ };
300
+
301
+ export const ThreeDirectors = {
302
+ args: { ...PlannedDirectorSuggestions.args, plannedDirectingStations: plannedDirectingStations.slice(0, 3) }
303
+ };
304
+
305
+ export const FourDirectors = {
306
+ args: { ...PlannedDirectorSuggestions.args, plannedDirectingStations: plannedDirectingStations.slice(0, 4) }
307
+ };
308
+
309
+ export const FiveDirectors = {
310
+ args: { ...PlannedDirectorSuggestions.args, plannedDirectingStations: plannedDirectingStations.slice(0, 5) }
311
+ };
312
+
313
+ export const SixDirectors = {
314
+ args: { ...PlannedDirectorSuggestions.args, plannedDirectingStations }
315
+ };
316
+
317
+ export const HoverBoutActions = {
318
+ args: { ...PlannedDirectorSuggestions.args },
319
+ parameters: {
320
+ docs: {
321
+ description: {
322
+ story:
323
+ 'Hover over a pairing for Open bout and Assign director. Clicking the pairing still opens it directly. The picker in this story simulates the consuming app; the story simulates balanced section coverage through the semifinals, while production assignment and persistence remain app-owned.'
324
+ }
325
+ }
227
326
  }
228
327
  };
229
328
 
@@ -246,3 +345,15 @@ export const noBoutsYet = {
246
345
  status: 'live'
247
346
  }
248
347
  };
348
+
349
+ export const PhoneDirector = {
350
+ args: { ...PlannedDirectorSuggestions.args, role: 'Director' },
351
+ parameters: {
352
+ docs: {
353
+ description: {
354
+ story:
355
+ 'Use a viewport narrower than 768px to see the phone directing view: a bout list, ring suggestions, connection status, and collapsed completed bouts. Organising controls stay on desktop, even when assignment options are enabled. Returning to desktop restores the selected List View or Tableau.'
356
+ }
357
+ }
358
+ }
359
+ };