@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
@@ -5,9 +5,9 @@
5
5
 
6
6
  <div v-for="bout in section.bouts" :key="bout.BoutId" class="mb-4">
7
7
  <div
8
- v-if="getPlannedStation(bout)"
8
+ v-if="getPlannedStation(bout) && getBoutDirectorLabel(bout)"
9
9
  data-testid="planned-bout-label"
10
- class="mb-1 flex items-center gap-2 rounded border-l-4 border-dropdownSelect bg-white px-2 py-1 shadow-sm"
10
+ class="mb-1 hidden md:flex items-center justify-center gap-2 rounded border-l-4 border-dropdownSelect bg-white px-2 py-1 shadow-sm"
11
11
  :style="{ borderLeftColor: getPlannedStation(bout).Color }"
12
12
  >
13
13
  <span
@@ -15,19 +15,51 @@
15
15
  :style="{ backgroundColor: getPlannedStation(bout).Color }"
16
16
  aria-hidden="true"
17
17
  ></span>
18
- <BaseText :text="getPlannedStationLabel(bout)" color="quaternary" size="xs" />
18
+ <BaseText :text="getBoutDirectorLabel(bout)" color="quaternary" size="xs" />
19
19
  </div>
20
+ <HoverActions
21
+ :actions="getBoutActions(bout)"
22
+ :focusable="!canOpenBracketBout(bout)"
23
+ @action="handleListBoutAction(bout, $event)"
24
+ >
25
+ <BoutCard
26
+ :bout="bout"
27
+ :hostingClubColors="hostingClubColors"
28
+ :editMode="canOpenBracketBout(bout)"
29
+ :reorderMode="false"
30
+ :timerStatus="bout.TimerStatus || 'stopped'"
31
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
32
+ @action:startBout="handleDirectingFromIds"
33
+ @action:resumeBout="handleDirectingFromIds"
34
+ @action:editBout="handleEditFromIds"
35
+ >
36
+ <template v-if="getBoutDirectorLabel(bout)" #mobile-details>
37
+ <BaseText
38
+ :text="getBoutDirectorLabel(bout)"
39
+ color="quaternary"
40
+ size="xs"
41
+ class="w-full min-w-0 break-words text-center"
42
+ />
43
+ </template>
44
+ </BoutCard>
45
+ </HoverActions>
46
+ </div>
47
+ </div>
48
+ <details v-if="completedListBouts.length" class="my-4 w-full md:w-3/4">
49
+ <summary class="cursor-pointer py-3 text-sm font-bold text-primaryHighlight">
50
+ Completed bouts ({{ completedListBouts.length }})
51
+ </summary>
52
+ <div v-for="bout in completedListBouts" :key="bout.BoutId" class="mb-4">
20
53
  <BoutCard
21
54
  :bout="bout"
22
55
  :hostingClubColors="hostingClubColors"
23
56
  :editMode="true"
24
57
  :reorderMode="false"
25
- @action:startBout="handleDirectingFromIds"
26
- @action:resumeBout="handleDirectingFromIds"
58
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
27
59
  @action:editBout="handleEditFromIds"
28
60
  />
29
61
  </div>
30
- </div>
62
+ </details>
31
63
  </section>
32
64
 
33
65
  <section
@@ -47,6 +79,9 @@
47
79
  :bracketSize="bracketSize"
48
80
  :layout="getRoundLayout(roundIndex)"
49
81
  :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
82
+ :showBoutDirectorAssignment="showBoutDirectorAssignment"
83
+ :plannedStationByBout="plannedStationByBout"
84
+ @request:bout-director="$emit('request:bout-director', $event)"
50
85
  @register-node-anchor="registerNodeAnchor"
51
86
  @action:editBout="handleEditBout"
52
87
  @action:directingBout="handleDirectorBout"
@@ -63,6 +98,9 @@
63
98
  :bracketSize="bracketSize"
64
99
  :layout="placementLayout"
65
100
  :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
101
+ :showBoutDirectorAssignment="showBoutDirectorAssignment"
102
+ :plannedStationByBout="plannedStationByBout"
103
+ @request:bout-director="$emit('request:bout-director', $event)"
66
104
  @register-node-anchor="registerNodeAnchor"
67
105
  @action:editBout="handleEditBout"
68
106
  @action:directingBout="handleDirectorBout"
@@ -81,6 +119,9 @@
81
119
  :bracketSize="bracketSize"
82
120
  :layout="getRoundLayout(roundIndex)"
83
121
  :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
122
+ :showBoutDirectorAssignment="showBoutDirectorAssignment"
123
+ :plannedStationByBout="plannedStationByBout"
124
+ @request:bout-director="$emit('request:bout-director', $event)"
84
125
  @register-node-anchor="registerNodeAnchor"
85
126
  @action:editBout="handleEditBout"
86
127
  @action:directingBout="handleDirectorBout"
@@ -95,6 +136,8 @@
95
136
 
96
137
  <script>
97
138
  import BaseText from '../../../Atoms/Text/BaseText.vue';
139
+ import { canAssignBoutDirector, canOpenBracketBout, createBoutActions } from '../../../Util/boutDirectorAssignment.js';
140
+ import HoverActions from '../../../Molecules/Menus/HoverActions/HoverActions.vue';
98
141
  import BoutCard from '../../../Organisms/Cards/BoutCard/BoutCard.vue';
99
142
  import TableauColumn from '../TableauColumn/TableauColumn.vue';
100
143
  import TableauSvgOverlay from './components/TableauSvgOverlay.vue';
@@ -149,12 +192,16 @@ const addBoutToRound = (rounds, bout) => {
149
192
 
150
193
  const groupBoutsByRound = bouts => (bouts || []).reduce(addBoutToRound, {});
151
194
 
152
- const isVisibleListBout = bout =>
153
- bout.Person1?.DisplayName !== 'BYE' && bout.Person2?.DisplayName !== 'BYE' && bout.Status !== 'Completed';
195
+ const isCompletedBout = bout => String(bout.Status || '').toLowerCase() === 'completed';
196
+
197
+ const isVisibleListBout = (bout, showBoutDirectorAssignment) => {
198
+ if (showBoutDirectorAssignment && canAssignBoutDirector(bout)) return true;
199
+ return canOpenBracketBout(bout) && !isCompletedBout(bout);
200
+ };
154
201
 
155
- const createVisibleRoundEntry = ([roundName, bouts]) => [
202
+ const createVisibleRoundEntry = ([roundName, bouts], showBoutDirectorAssignment) => [
156
203
  roundName,
157
- bouts.filter(isVisibleListBout).sort(sortBoutsByNodeId)
204
+ bouts.filter(bout => isVisibleListBout(bout, showBoutDirectorAssignment)).sort(sortBoutsByNodeId)
158
205
  ];
159
206
 
160
207
  const hasBouts = ([, bouts]) => bouts.length > 0;
@@ -176,9 +223,10 @@ const createRoundLayout = depth => {
176
223
 
177
224
  export default {
178
225
  name: 'Tableau',
179
- components: { BaseText, BoutCard, TableauColumn, TableauSvgOverlay },
180
- emits: ['action:editBout', 'action:directingBout'],
226
+ components: { BaseText, BoutCard, TableauColumn, TableauSvgOverlay, HoverActions },
227
+ emits: ['action:editBout', 'action:directingBout', 'request:bout-director'],
181
228
  props: {
229
+ showBoutDirectorAssignment: { type: Boolean, default: true },
182
230
  bouts: { type: Array, required: true },
183
231
  bracketSize: { type: String, required: true },
184
232
  hostingClubColors: { type: Object, required: true },
@@ -216,9 +264,15 @@ export default {
216
264
  loserRoundEntries() {
217
265
  return this.roundEntries.filter(([roundName]) => isConsolationRoundName(roundName)).sort(sortRoundEntries);
218
266
  },
267
+ completedListBouts() {
268
+ return this.bouts.filter(bout => isCompletedBout(bout) && canOpenBracketBout(bout)).sort(sortBoutsByNodeId);
269
+ },
219
270
  filteredBouts() {
220
271
  return Object.fromEntries(
221
- [...this.roundEntries].sort(sortRoundEntries).map(createVisibleRoundEntry).filter(hasBouts)
272
+ [...this.roundEntries]
273
+ .sort(sortRoundEntries)
274
+ .map(entry => createVisibleRoundEntry(entry, this.showBoutDirectorAssignment))
275
+ .filter(hasBouts)
222
276
  );
223
277
  },
224
278
  listSections() {
@@ -290,6 +344,22 @@ export default {
290
344
  }
291
345
  },
292
346
  methods: {
347
+ canOpenBracketBout,
348
+ getBoutActions(bout) {
349
+ const station = this.getPlannedStation(bout);
350
+ return createBoutActions(bout, this.showBoutDirectorAssignment, station?.Director?.DisplayName, station?.Color);
351
+ },
352
+ handleListBoutAction(bout, actionId) {
353
+ if (actionId === 'assign-director') {
354
+ if (!this.showBoutDirectorAssignment || !canAssignBoutDirector(bout)) return;
355
+ this.$emit('request:bout-director', JSON.parse(JSON.stringify(bout)));
356
+ return;
357
+ }
358
+ if (actionId !== 'open' || !canOpenBracketBout(bout)) return;
359
+ const event =
360
+ String(bout.Status || '').toLowerCase() === 'completed' ? 'action:editBout' : 'action:directingBout';
361
+ this.$emit(event, bout);
362
+ },
293
363
  findBoutByIds(idsOrBout) {
294
364
  if (!idsOrBout) return null;
295
365
  if (idsOrBout.BoutId && idsOrBout.Person1) return idsOrBout;
@@ -317,11 +387,15 @@ export default {
317
387
  getPlannedStation(bout) {
318
388
  return getPlannedStationForBout(this.plannedStationByBout, bout);
319
389
  },
320
- getPlannedStationLabel(bout) {
390
+ isActiveListBout(bout) {
391
+ return String(bout.Status || '').toLowerCase() === 'active';
392
+ },
393
+ getBoutDirectorLabel(bout) {
394
+ if (this.isActiveListBout(bout)) return [bout.RefName, bout.RingName].filter(Boolean).join(' / ');
321
395
  const station = this.getPlannedStation(bout);
322
396
  if (!station) return '';
323
397
  const directorName = station.Director?.DisplayName || 'Director TBD';
324
- return `Suggested: ${station.RingName} / ${directorName}`;
398
+ return [directorName, station.RingName].filter(Boolean).join(' / ');
325
399
  },
326
400
  registerNodeAnchor({ id, kind, el }) {
327
401
  const anchors = kind === 'input' ? this.inputAnchors : this.outputAnchors;
@@ -1,29 +1,29 @@
1
- import { shallowMount } from '@vue/test-utils';
2
- import { describe, expect, it } from 'vitest';
3
- import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
4
- import BracketDirectorAssignmentPanel from './BracketDirectorAssignmentPanel.vue';
5
-
6
- const stations = [
7
- { RingId: 1, RingName: 'Ring 1', Director: { PersonId: 10, DisplayName: 'Director One' } },
8
- { RingId: 2, RingName: 'Ring 2', Director: { PersonId: 20, DisplayName: 'Director Two' } }
9
- ];
10
-
11
- describe('BracketDirectorAssignmentPanel', () => {
12
- it('renders assigned directors followed by the add summary card', () => {
13
- const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
14
- const cards = wrapper.findAllComponents(BracketDirectorAssignmentCard);
15
-
16
- expect(cards).toHaveLength(3);
17
- expect(cards.slice(0, 2).map(card => card.props('station'))).toEqual(stations);
18
- expect(cards[2].props('variant')).toBe('add');
19
- });
20
-
21
- it('forwards add intent to its parent', async () => {
22
- const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations: [] } });
23
-
24
- wrapper.findComponent(BracketDirectorAssignmentCard).vm.$emit('action:add');
25
- await wrapper.vm.$nextTick();
26
-
27
- expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
28
- });
29
- });
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { describe, expect, it } from 'vitest';
3
+ import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
4
+ import BracketDirectorAssignmentPanel from './BracketDirectorAssignmentPanel.vue';
5
+
6
+ const stations = [
7
+ { RingId: 1, RingName: 'Ring 1', Director: { PersonId: 10, DisplayName: 'Director One' } },
8
+ { RingId: 2, RingName: 'Ring 2', Director: { PersonId: 20, DisplayName: 'Director Two' } }
9
+ ];
10
+
11
+ describe('BracketDirectorAssignmentPanel', () => {
12
+ it('renders assigned directors followed by the add summary card', () => {
13
+ const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
14
+ const cards = wrapper.findAllComponents(BracketDirectorAssignmentCard);
15
+
16
+ expect(cards).toHaveLength(3);
17
+ expect(cards.slice(0, 2).map(card => card.props('station'))).toEqual(stations);
18
+ expect(cards[2].props('variant')).toBe('add');
19
+ });
20
+
21
+ it('forwards add intent to its parent', async () => {
22
+ const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations: [] } });
23
+
24
+ wrapper.findComponent(BracketDirectorAssignmentCard).vm.$emit('action:add');
25
+ await wrapper.vm.$nextTick();
26
+
27
+ expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
28
+ });
29
+ });
@@ -1,46 +1,46 @@
1
- <template>
2
- <aside
3
- data-testid="bracket-director-assignment-panel"
4
- class="w-full rounded-xl border border-dropdownSelect bg-bannerBackground p-4 shadow-lg xl:sticky xl:top-4 xl:w-72"
5
- >
6
- <div class="mb-4">
7
- <BaseText color="primaryHighlight" size="md" text="Director Assignments" weight="bold" />
8
- <BaseText
9
- class="mt-1"
10
- color="quaternary"
11
- size="xs"
12
- text="Plan ring coverage. Scheduled bouts may still be moved between rings."
13
- />
14
- </div>
15
-
16
- <div class="space-y-3">
17
- <BracketDirectorAssignmentCard v-for="station in stations" :key="station.RingId" :station="station" />
18
- <BracketDirectorAssignmentCard
19
- v-if="showAddCard"
20
- variant="add"
21
- @action:add="$emit('request:add-planned-director')"
22
- />
23
- </div>
24
- </aside>
25
- </template>
26
-
27
- <script>
28
- import BaseText from '../../../../Atoms/Text/BaseText.vue';
29
- import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
30
-
31
- export default {
32
- name: 'BracketDirectorAssignmentPanel',
33
- components: { BaseText, BracketDirectorAssignmentCard },
34
- emits: ['request:add-planned-director'],
35
- props: {
36
- stations: {
37
- type: Array,
38
- default: () => []
39
- },
40
- showAddCard: {
41
- type: Boolean,
42
- default: true
43
- }
44
- }
45
- };
46
- </script>
1
+ <template>
2
+ <aside
3
+ data-testid="bracket-director-assignment-panel"
4
+ class="w-full rounded-xl border border-dropdownSelect bg-bannerBackground p-4 shadow-lg xl:sticky xl:top-4 xl:w-72"
5
+ >
6
+ <div class="mb-4">
7
+ <BaseText color="primaryHighlight" size="md" text="Director Assignments" weight="bold" />
8
+ <BaseText
9
+ class="mt-1"
10
+ color="quaternary"
11
+ size="xs"
12
+ text="Plan ring coverage. Scheduled bouts may still be moved between rings."
13
+ />
14
+ </div>
15
+
16
+ <div class="space-y-3">
17
+ <BracketDirectorAssignmentCard v-for="station in stations" :key="station.RingId" :station="station" />
18
+ <BracketDirectorAssignmentCard
19
+ v-if="showAddCard"
20
+ variant="add"
21
+ @action:add="$emit('request:add-planned-director')"
22
+ />
23
+ </div>
24
+ </aside>
25
+ </template>
26
+
27
+ <script>
28
+ import BaseText from '../../../../Atoms/Text/BaseText.vue';
29
+ import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
30
+
31
+ export default {
32
+ name: 'BracketDirectorAssignmentPanel',
33
+ components: { BaseText, BracketDirectorAssignmentCard },
34
+ emits: ['request:add-planned-director'],
35
+ props: {
36
+ stations: {
37
+ type: Array,
38
+ default: () => []
39
+ },
40
+ showAddCard: {
41
+ type: Boolean,
42
+ default: true
43
+ }
44
+ }
45
+ };
46
+ </script>
@@ -1,21 +1,21 @@
1
- import { shallowMount } from '@vue/test-utils';
2
- import { describe, expect, it } from 'vitest';
3
- import TableauSvgOverlay from './TableauSvgOverlay.vue';
4
-
5
- describe('TableauSvgOverlay', () => {
6
- it('uses the standard connector thickness for neutral and assigned paths', () => {
7
- const wrapper = shallowMount(TableauSvgOverlay, {
8
- props: {
9
- size: { width: 200, height: 100 },
10
- paths: [
11
- { key: 'neutral', path: 'M 0 0 L 10 0', stroke: null },
12
- { key: 'assigned', path: 'M 0 10 L 10 10', stroke: '#2563eb' }
13
- ]
14
- }
15
- });
16
- const paths = wrapper.findAll('path');
17
-
18
- expect(paths.map(path => path.attributes('stroke-width'))).toEqual(['1.5', '1.5']);
19
- expect(paths.map(path => path.attributes('stroke'))).toEqual(['currentColor', '#2563eb']);
20
- });
21
- });
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { describe, expect, it } from 'vitest';
3
+ import TableauSvgOverlay from './TableauSvgOverlay.vue';
4
+
5
+ describe('TableauSvgOverlay', () => {
6
+ it('uses the standard connector thickness for neutral and assigned paths', () => {
7
+ const wrapper = shallowMount(TableauSvgOverlay, {
8
+ props: {
9
+ size: { width: 200, height: 100 },
10
+ paths: [
11
+ { key: 'neutral', path: 'M 0 0 L 10 0', stroke: null },
12
+ { key: 'assigned', path: 'M 0 10 L 10 10', stroke: '#2563eb' }
13
+ ]
14
+ }
15
+ });
16
+ const paths = wrapper.findAll('path');
17
+
18
+ expect(paths.map(path => path.attributes('stroke-width'))).toEqual(['1.5', '1.5']);
19
+ expect(paths.map(path => path.attributes('stroke'))).toEqual(['currentColor', '#2563eb']);
20
+ });
21
+ });
@@ -1,39 +1,39 @@
1
- <template>
2
- <svg
3
- class="pointer-events-none absolute inset-0 z-0 text-dropdownSelect"
4
- :width="size.width"
5
- :height="size.height"
6
- :viewBox="`0 0 ${size.width} ${size.height}`"
7
- shape-rendering="crispEdges"
8
- overflow="visible"
9
- aria-hidden="true"
10
- >
11
- <path
12
- v-for="connector in paths"
13
- :key="connector.key"
14
- :d="connector.path"
15
- :data-segment-role="connector.segmentRole"
16
- fill="none"
17
- :stroke="connector.stroke || 'currentColor'"
18
- stroke-width="1.5"
19
- stroke-linecap="square"
20
- stroke-linejoin="miter"
21
- />
22
- </svg>
23
- </template>
24
-
25
- <script>
26
- export default {
27
- name: 'TableauSvgOverlay',
28
- props: {
29
- size: {
30
- type: Object,
31
- required: true
32
- },
33
- paths: {
34
- type: Array,
35
- required: true
36
- }
37
- }
38
- };
39
- </script>
1
+ <template>
2
+ <svg
3
+ class="pointer-events-none absolute inset-0 z-0 text-dropdownSelect"
4
+ :width="size.width"
5
+ :height="size.height"
6
+ :viewBox="`0 0 ${size.width} ${size.height}`"
7
+ shape-rendering="crispEdges"
8
+ overflow="visible"
9
+ aria-hidden="true"
10
+ >
11
+ <path
12
+ v-for="connector in paths"
13
+ :key="connector.key"
14
+ :d="connector.path"
15
+ :data-segment-role="connector.segmentRole"
16
+ fill="none"
17
+ :stroke="connector.stroke || 'currentColor'"
18
+ stroke-width="1.5"
19
+ stroke-linecap="square"
20
+ stroke-linejoin="miter"
21
+ />
22
+ </svg>
23
+ </template>
24
+
25
+ <script>
26
+ export default {
27
+ name: 'TableauSvgOverlay',
28
+ props: {
29
+ size: {
30
+ type: Object,
31
+ required: true
32
+ },
33
+ paths: {
34
+ type: Array,
35
+ required: true
36
+ }
37
+ }
38
+ };
39
+ </script>