@dcrackel/hematournamentui 1.0.712 → 1.0.714
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +7 -7
- package/.storybook/main.js +3 -1
- package/README.md +42 -0
- package/dist/HemaTournamentUI-lib.es.js +6856 -6243
- package/dist/HemaTournamentUI-lib.umd.js +27 -27
- package/dist/hematournamentui-tailwind.css +79 -9
- package/package.json +96 -96
- package/prettier.config.js +16 -16
- package/src/index.js +43 -42
- package/src/main.js +1 -1
- package/src/mocks/getDEBouts-TableOf128.js +37 -37
- package/src/mocks/getDEBouts-TableOf64.js +35 -35
- package/src/stories/Atoms/Input/BaseInput.test.js +97 -97
- package/src/stories/Atoms/Input/BaseInput.vue +149 -149
- package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -23
- package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -35
- package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -30
- package/src/stories/Molecules/Menus/HoverActions/HoverActions.test.js +138 -0
- package/src/stories/Molecules/Menus/HoverActions/HoverActions.vue +212 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.stories.js +49 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.test.js +129 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.vue +165 -0
- package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +6 -0
- package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -18
- package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +1 -1
- package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +5 -1
- package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +19 -10
- package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +11 -7
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +42 -33
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +76 -45
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +122 -103
- package/src/stories/Organisms/Cards/Director/DirectorCard.test.js +82 -0
- package/src/stories/Organisms/Cards/Director/DirectorCard.vue +32 -51
- package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -51
- package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -106
- package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -298
- package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +77 -60
- package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +144 -143
- package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +289 -256
- package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -69
- package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -80
- package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +153 -153
- package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +4 -0
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -31
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -109
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -192
- package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +1 -0
- package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +247 -204
- package/src/stories/Organisms/Containers/Tableau/Tableau.vue +89 -15
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +51 -29
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +63 -46
- package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -21
- package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -39
- package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -145
- package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -64
- package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +36 -57
- package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +101 -65
- package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +14 -1
- package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +52 -52
- package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +10 -3
- package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -226
- package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
- package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +137 -137
- package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -172
- package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -216
- package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -79
- package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -29
- package/src/stories/Organisms/Wizards/EditEventWizard/WeaponSelection.test.js +138 -0
- package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +149 -9
- package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +54 -2
- package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +77 -8
- package/src/stories/Templates/EventManagement/Bracket/BracketDirectorAssignment.test.js +242 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketMobile.test.js +131 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +50 -39
- package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +201 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.js +93 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.test.js +152 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -39
- package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -79
- package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -93
- package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -91
- package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -121
- package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +9 -2
- package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +5 -4
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +81 -81
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -39
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +101 -101
- package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -71
- package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +86 -86
- package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -86
- package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +247 -247
- package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -170
- package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -156
- package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -329
- package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -329
- package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -42
- package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -84
- package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +114 -114
- package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -363
- package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -116
- package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +307 -307
- package/src/stories/Util/Validations/PersonManagementValidations.js +45 -45
- package/src/stories/Util/Validations/PriceValidation.js +6 -6
- package/src/stories/Util/boutDirectorAssignment.js +46 -0
- package/src/stories/Util/bracketDirectors.js +86 -0
- package/src/stories/Util/registrationStatus.js +11 -11
- 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="
|
|
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
|
-
|
|
26
|
-
@action:resumeBout="handleDirectingFromIds"
|
|
58
|
+
:isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
|
|
27
59
|
@action:editBout="handleEditFromIds"
|
|
28
60
|
/>
|
|
29
61
|
</div>
|
|
30
|
-
</
|
|
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
|
|
153
|
-
|
|
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]
|
|
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
|
-
|
|
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
|
|
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;
|
package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js
CHANGED
|
@@ -1,29 +1,51 @@
|
|
|
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
|
+
|
|
30
|
+
it('forwards remove intent and reconciles the parent station list', async () => {
|
|
31
|
+
const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
|
|
32
|
+
wrapper.findComponent(BracketDirectorAssignmentCard).vm.$emit('action:remove', stations[0]);
|
|
33
|
+
expect(wrapper.emitted('request:remove-planned-director')).toEqual([[stations[0]]]);
|
|
34
|
+
expect(wrapper.props('stations')).toHaveLength(2);
|
|
35
|
+
await wrapper.setProps({ stations: stations.slice(1) });
|
|
36
|
+
expect(
|
|
37
|
+
wrapper
|
|
38
|
+
.findAllComponents(BracketDirectorAssignmentCard)
|
|
39
|
+
.filter(card => card.props('variant') !== 'add')
|
|
40
|
+
.map(card => card.props('station'))
|
|
41
|
+
).toEqual([stations[1]]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('opens director management', async () => {
|
|
45
|
+
const wrapper = shallowMount(BracketDirectorAssignmentPanel, {
|
|
46
|
+
props: { stations, canManageDirectors: true }
|
|
47
|
+
});
|
|
48
|
+
await wrapper.get('[data-testid="manage-bracket-directors"]').trigger('click');
|
|
49
|
+
expect(wrapper.emitted('request:manage-planned-directors')).toEqual([[]]);
|
|
50
|
+
});
|
|
51
|
+
});
|
package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue
CHANGED
|
@@ -1,46 +1,63 @@
|
|
|
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
|
-
<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
</
|
|
26
|
-
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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 flex items-start justify-between gap-3">
|
|
7
|
+
<div>
|
|
8
|
+
<BaseText color="primaryHighlight" size="md" text="Director Assignments" weight="bold" />
|
|
9
|
+
<BaseText
|
|
10
|
+
class="mt-1"
|
|
11
|
+
color="quaternary"
|
|
12
|
+
size="xs"
|
|
13
|
+
text="Plan ring coverage. Scheduled bouts may still be moved between rings."
|
|
14
|
+
/>
|
|
15
|
+
</div>
|
|
16
|
+
<button
|
|
17
|
+
v-if="canManageDirectors"
|
|
18
|
+
type="button"
|
|
19
|
+
data-testid="manage-bracket-directors"
|
|
20
|
+
class="rounded border border-dropdownSelect bg-white px-2 py-1 text-xs text-primaryHighlight hover:bg-dropdownSelect focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
|
|
21
|
+
@click="$emit('request:manage-planned-directors')"
|
|
22
|
+
>
|
|
23
|
+
Manage
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div class="space-y-3">
|
|
28
|
+
<BracketDirectorAssignmentCard
|
|
29
|
+
v-for="station in stations"
|
|
30
|
+
:key="station.RingId"
|
|
31
|
+
:station="station"
|
|
32
|
+
@action:remove="$emit('request:remove-planned-director', $event)"
|
|
33
|
+
/>
|
|
34
|
+
<BracketDirectorAssignmentCard
|
|
35
|
+
v-if="showAddCard"
|
|
36
|
+
variant="add"
|
|
37
|
+
@action:add="$emit('request:add-planned-director')"
|
|
38
|
+
/>
|
|
39
|
+
</div>
|
|
40
|
+
</aside>
|
|
41
|
+
</template>
|
|
42
|
+
|
|
43
|
+
<script>
|
|
44
|
+
import BaseText from '../../../../Atoms/Text/BaseText.vue';
|
|
45
|
+
import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
|
|
46
|
+
|
|
47
|
+
export default {
|
|
48
|
+
name: 'BracketDirectorAssignmentPanel',
|
|
49
|
+
components: { BaseText, BracketDirectorAssignmentCard },
|
|
50
|
+
emits: ['request:add-planned-director', 'request:remove-planned-director', 'request:manage-planned-directors'],
|
|
51
|
+
props: {
|
|
52
|
+
canManageDirectors: { type: Boolean, default: false },
|
|
53
|
+
stations: {
|
|
54
|
+
type: Array,
|
|
55
|
+
default: () => []
|
|
56
|
+
},
|
|
57
|
+
showAddCard: {
|
|
58
|
+
type: Boolean,
|
|
59
|
+
default: true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
</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>
|