@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.
- package/.prettierignore +7 -7
- package/.storybook/main.js +3 -1
- package/README.md +30 -0
- package/package.json +96 -96
- package/prettier.config.js +16 -16
- 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/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 +33 -33
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -45
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -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 +29 -29
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -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 +123 -12
- package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +178 -166
- package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +35 -7
- 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 +42 -39
- package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +80 -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/registrationStatus.js +11 -11
- package/src/stories/Util/tabs.js +19 -47
package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue
CHANGED
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<button
|
|
3
|
-
v-if="isAddCard"
|
|
4
|
-
type="button"
|
|
5
|
-
data-testid="add-bracket-director"
|
|
6
|
-
class="flex min-h-20 w-full items-center justify-center gap-3 rounded-lg border-2 border-dashed border-dropdownSelect bg-white px-4 py-3 text-left transition hover:border-bright hover:shadow-md focus-visible:border-bright focus-visible:outline-none"
|
|
7
|
-
@click="$emit('action:add')"
|
|
8
|
-
>
|
|
9
|
-
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-bannerBackground" aria-hidden="true">
|
|
10
|
-
<BaseIcon iconName="fa-plus" color="primaryHighlight" size="sm" />
|
|
11
|
-
</span>
|
|
12
|
-
<BaseText :text="addLabel" color="primaryHighlight" size="sm" weight="bold" />
|
|
13
|
-
</button>
|
|
14
|
-
|
|
15
|
-
<article
|
|
16
|
-
v-else-if="station"
|
|
17
|
-
data-testid="bracket-director-assignment"
|
|
18
|
-
class="flex min-h-16 w-full items-center gap-3 overflow-hidden rounded-md border border-poolSetup bg-white px-3 py-2 shadow-sm hover:border-bright"
|
|
19
|
-
:aria-label="assignmentLabel"
|
|
20
|
-
>
|
|
21
|
-
<div class="relative h-12 w-12 flex-none">
|
|
22
|
-
<img
|
|
23
|
-
v-if="portraitUrl"
|
|
24
|
-
:src="portraitUrl"
|
|
25
|
-
:alt="`${directorName} portrait`"
|
|
26
|
-
class="h-12 w-12 rounded-xl object-cover"
|
|
27
|
-
/>
|
|
28
|
-
<div
|
|
29
|
-
v-else
|
|
30
|
-
data-testid="director-initials"
|
|
31
|
-
class="flex h-12 w-12 items-center justify-center rounded-xl bg-dropdownSelect"
|
|
32
|
-
aria-hidden="true"
|
|
33
|
-
>
|
|
34
|
-
<BaseText :text="directorInitials" color="primaryHighlight" size="sm" weight="bold" />
|
|
35
|
-
</div>
|
|
36
|
-
<span
|
|
37
|
-
data-testid="director-color-dot"
|
|
38
|
-
class="absolute -bottom-1 -right-1 h-4 w-4 rounded-full ring-2 ring-white"
|
|
39
|
-
:style="{ backgroundColor: station.Color }"
|
|
40
|
-
aria-hidden="true"
|
|
41
|
-
></span>
|
|
42
|
-
</div>
|
|
43
|
-
|
|
44
|
-
<div class="min-w-0 flex-1">
|
|
45
|
-
<BaseText :text="ringLabel" color="quaternary" size="xs" />
|
|
46
|
-
<BaseText class="truncate" :text="directorName" color="primaryHighlight" size="sm" weight="bold" />
|
|
47
|
-
</div>
|
|
48
|
-
</article>
|
|
49
|
-
</template>
|
|
50
|
-
|
|
51
|
-
<script>
|
|
52
|
-
import BaseIcon from '../../../Atoms/Icon/BaseIcon.vue';
|
|
53
|
-
import BaseText from '../../../Atoms/Text/BaseText.vue';
|
|
54
|
-
|
|
55
|
-
const getInitials = displayName =>
|
|
56
|
-
String(displayName || '')
|
|
57
|
-
.split(/\s+/)
|
|
58
|
-
.filter(Boolean)
|
|
59
|
-
.slice(0, 2)
|
|
60
|
-
.map(name => name[0].toUpperCase())
|
|
61
|
-
.join('');
|
|
62
|
-
|
|
63
|
-
export default {
|
|
64
|
-
name: 'BracketDirectorAssignmentCard',
|
|
65
|
-
components: { BaseIcon, BaseText },
|
|
66
|
-
emits: ['action:add'],
|
|
67
|
-
props: {
|
|
68
|
-
station: {
|
|
69
|
-
type: Object,
|
|
70
|
-
default: null
|
|
71
|
-
},
|
|
72
|
-
variant: {
|
|
73
|
-
type: String,
|
|
74
|
-
default: 'assigned',
|
|
75
|
-
validator: value => ['assigned', 'add'].includes(value)
|
|
76
|
-
},
|
|
77
|
-
addLabel: {
|
|
78
|
-
type: String,
|
|
79
|
-
default: 'Click here to add director'
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
computed: {
|
|
83
|
-
assignmentLabel() {
|
|
84
|
-
return `${this.ringLabel}, Director ${this.directorName}`;
|
|
85
|
-
},
|
|
86
|
-
directorInitials() {
|
|
87
|
-
return getInitials(this.directorName) || '?';
|
|
88
|
-
},
|
|
89
|
-
directorName() {
|
|
90
|
-
return this.station?.Director?.DisplayName || 'Director TBD';
|
|
91
|
-
},
|
|
92
|
-
isAddCard() {
|
|
93
|
-
return this.variant === 'add';
|
|
94
|
-
},
|
|
95
|
-
portraitUrl() {
|
|
96
|
-
return this.station?.Director?.PortraitUrl || '';
|
|
97
|
-
},
|
|
98
|
-
ringLabel() {
|
|
99
|
-
return this.station?.RingName || `Ring ${this.station?.RingId ?? ''}`.trim();
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
</script>
|
|
1
|
+
<template>
|
|
2
|
+
<button
|
|
3
|
+
v-if="isAddCard"
|
|
4
|
+
type="button"
|
|
5
|
+
data-testid="add-bracket-director"
|
|
6
|
+
class="flex min-h-20 w-full items-center justify-center gap-3 rounded-lg border-2 border-dashed border-dropdownSelect bg-white px-4 py-3 text-left transition hover:border-bright hover:shadow-md focus-visible:border-bright focus-visible:outline-none"
|
|
7
|
+
@click="$emit('action:add')"
|
|
8
|
+
>
|
|
9
|
+
<span class="flex h-9 w-9 items-center justify-center rounded-full bg-bannerBackground" aria-hidden="true">
|
|
10
|
+
<BaseIcon iconName="fa-plus" color="primaryHighlight" size="sm" />
|
|
11
|
+
</span>
|
|
12
|
+
<BaseText :text="addLabel" color="primaryHighlight" size="sm" weight="bold" />
|
|
13
|
+
</button>
|
|
14
|
+
|
|
15
|
+
<article
|
|
16
|
+
v-else-if="station"
|
|
17
|
+
data-testid="bracket-director-assignment"
|
|
18
|
+
class="flex min-h-16 w-full items-center gap-3 overflow-hidden rounded-md border border-poolSetup bg-white px-3 py-2 shadow-sm hover:border-bright"
|
|
19
|
+
:aria-label="assignmentLabel"
|
|
20
|
+
>
|
|
21
|
+
<div class="relative h-12 w-12 flex-none">
|
|
22
|
+
<img
|
|
23
|
+
v-if="portraitUrl"
|
|
24
|
+
:src="portraitUrl"
|
|
25
|
+
:alt="`${directorName} portrait`"
|
|
26
|
+
class="h-12 w-12 rounded-xl object-cover"
|
|
27
|
+
/>
|
|
28
|
+
<div
|
|
29
|
+
v-else
|
|
30
|
+
data-testid="director-initials"
|
|
31
|
+
class="flex h-12 w-12 items-center justify-center rounded-xl bg-dropdownSelect"
|
|
32
|
+
aria-hidden="true"
|
|
33
|
+
>
|
|
34
|
+
<BaseText :text="directorInitials" color="primaryHighlight" size="sm" weight="bold" />
|
|
35
|
+
</div>
|
|
36
|
+
<span
|
|
37
|
+
data-testid="director-color-dot"
|
|
38
|
+
class="absolute -bottom-1 -right-1 h-4 w-4 rounded-full ring-2 ring-white"
|
|
39
|
+
:style="{ backgroundColor: station.Color }"
|
|
40
|
+
aria-hidden="true"
|
|
41
|
+
></span>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="min-w-0 flex-1">
|
|
45
|
+
<BaseText :text="ringLabel" color="quaternary" size="xs" />
|
|
46
|
+
<BaseText class="truncate" :text="directorName" color="primaryHighlight" size="sm" weight="bold" />
|
|
47
|
+
</div>
|
|
48
|
+
</article>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
<script>
|
|
52
|
+
import BaseIcon from '../../../Atoms/Icon/BaseIcon.vue';
|
|
53
|
+
import BaseText from '../../../Atoms/Text/BaseText.vue';
|
|
54
|
+
|
|
55
|
+
const getInitials = displayName =>
|
|
56
|
+
String(displayName || '')
|
|
57
|
+
.split(/\s+/)
|
|
58
|
+
.filter(Boolean)
|
|
59
|
+
.slice(0, 2)
|
|
60
|
+
.map(name => name[0].toUpperCase())
|
|
61
|
+
.join('');
|
|
62
|
+
|
|
63
|
+
export default {
|
|
64
|
+
name: 'BracketDirectorAssignmentCard',
|
|
65
|
+
components: { BaseIcon, BaseText },
|
|
66
|
+
emits: ['action:add'],
|
|
67
|
+
props: {
|
|
68
|
+
station: {
|
|
69
|
+
type: Object,
|
|
70
|
+
default: null
|
|
71
|
+
},
|
|
72
|
+
variant: {
|
|
73
|
+
type: String,
|
|
74
|
+
default: 'assigned',
|
|
75
|
+
validator: value => ['assigned', 'add'].includes(value)
|
|
76
|
+
},
|
|
77
|
+
addLabel: {
|
|
78
|
+
type: String,
|
|
79
|
+
default: 'Click here to add director'
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
computed: {
|
|
83
|
+
assignmentLabel() {
|
|
84
|
+
return `${this.ringLabel}, Director ${this.directorName}`;
|
|
85
|
+
},
|
|
86
|
+
directorInitials() {
|
|
87
|
+
return getInitials(this.directorName) || '?';
|
|
88
|
+
},
|
|
89
|
+
directorName() {
|
|
90
|
+
return this.station?.Director?.DisplayName || 'Director TBD';
|
|
91
|
+
},
|
|
92
|
+
isAddCard() {
|
|
93
|
+
return this.variant === 'add';
|
|
94
|
+
},
|
|
95
|
+
portraitUrl() {
|
|
96
|
+
return this.station?.Director?.PortraitUrl || '';
|
|
97
|
+
},
|
|
98
|
+
ringLabel() {
|
|
99
|
+
return this.station?.RingName || `Ring ${this.station?.RingId ?? ''}`.trim();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
</script>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { mount, shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import DirectorCard from './DirectorCard.vue';
|
|
4
|
+
import DirectorModal from '../../../Molecules/Modals/DirectorModal/DirectorModal.vue';
|
|
5
|
+
import ScoreBox from '../../../Molecules/Boxes/BoutBoxes/ScoreBox/ScoreBox.vue';
|
|
6
|
+
import eventRules from '../../../../mocks/eventRules.js';
|
|
7
|
+
import getPoolsWithBouts from '../../../../mocks/getPoolsWithBouts.js';
|
|
8
|
+
|
|
9
|
+
const createProps = rules => ({
|
|
10
|
+
bout: structuredClone(getPoolsWithBouts[0]),
|
|
11
|
+
eventRules: rules,
|
|
12
|
+
hostingClubColors: { Color1: 'red', Color2: 'blue' }
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const poolRules = [
|
|
16
|
+
{ RuleName: 'PoolTime', RuleValue: '120' },
|
|
17
|
+
{ RuleName: 'PoolMaxPoints', RuleValue: '5' },
|
|
18
|
+
{ RuleName: 'PoolNumOfPasses', RuleValue: '4' },
|
|
19
|
+
{ RuleName: 'PoolScoreDirection', RuleValue: 'false' },
|
|
20
|
+
{ RuleName: 'CountHandshots', RuleValue: 'true' },
|
|
21
|
+
{ RuleName: 'PenaltiesReset', RuleValue: 'true' }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
describe('DirectorCard event rules', () => {
|
|
25
|
+
it.each([
|
|
26
|
+
['flat', poolRules],
|
|
27
|
+
['nested', poolRules.map(({ RuleName, RuleValue }) => ({ Rules: { RuleName }, RuleValue }))],
|
|
28
|
+
['mixed with invalid entries', [null, undefined, {}, { Rules: null }, ...poolRules]]
|
|
29
|
+
])('applies %s rules without crashing', (name, rules) => {
|
|
30
|
+
const props = createProps(rules);
|
|
31
|
+
const originalBout = structuredClone(props.bout);
|
|
32
|
+
const wrapper = shallowMount(DirectorCard, { props });
|
|
33
|
+
expect(wrapper.vm.timeLeft).toBe(120);
|
|
34
|
+
expect(wrapper.vm.maxPoints).toBe(5);
|
|
35
|
+
expect(wrapper.vm.maxPasses).toBe(4);
|
|
36
|
+
expect(wrapper.vm.showPassesBox).toBe(true);
|
|
37
|
+
expect(wrapper.vm.showTimerBox).toBe(true);
|
|
38
|
+
expect(wrapper.vm.showHandShotCounterBox).toBe(true);
|
|
39
|
+
expect(wrapper.vm.penaltiesReset).toBe(true);
|
|
40
|
+
expect(wrapper.findComponent(ScoreBox).props('isCountingUp')).toBe(false);
|
|
41
|
+
expect(wrapper.emitted('update:bout')[0][0]).toMatchObject({
|
|
42
|
+
Status: 'Scheduled',
|
|
43
|
+
TimeLeft: 120,
|
|
44
|
+
IsCountingUp: false,
|
|
45
|
+
MaxPoints: 5
|
|
46
|
+
});
|
|
47
|
+
expect(props.bout).toEqual(originalBout);
|
|
48
|
+
wrapper.unmount();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it.each([undefined, null, [], [null, undefined, {}, { Rules: {} }]])(
|
|
52
|
+
'uses defaults for missing or invalid rules: %j',
|
|
53
|
+
rules => {
|
|
54
|
+
const wrapper = shallowMount(DirectorCard, { props: createProps(rules) });
|
|
55
|
+
expect(wrapper.vm.timeLeft).toBe(180);
|
|
56
|
+
expect(wrapper.vm.maxPoints).toBe(7);
|
|
57
|
+
expect(wrapper.vm.maxPasses).toBe(7);
|
|
58
|
+
expect(wrapper.vm.isCountingUp).toBe(true);
|
|
59
|
+
expect(wrapper.vm.showTimerBox).toBe(false);
|
|
60
|
+
expect(wrapper.vm.showPassesBox).toBe(false);
|
|
61
|
+
expect(wrapper.vm.showHandShotCounterBox).toBe(false);
|
|
62
|
+
expect(wrapper.vm.penaltiesReset).toBe(false);
|
|
63
|
+
wrapper.unmount();
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
it('opens the real modal with Storybook rules and preserves scores on close', async () => {
|
|
68
|
+
const props = createProps(eventRules);
|
|
69
|
+
const wrapper = mount(DirectorModal, { props: { ...props, show: false } });
|
|
70
|
+
await wrapper.setProps({ show: true });
|
|
71
|
+
expect(wrapper.findComponent(DirectorCard).exists()).toBe(true);
|
|
72
|
+
wrapper.findComponent(ScoreBox).vm.$emit('update:Score', 3);
|
|
73
|
+
await wrapper.vm.$nextTick();
|
|
74
|
+
expect(wrapper.findComponent(DirectorCard).vm.localBout.Score1).toBe(3);
|
|
75
|
+
expect(wrapper.emitted('update:bout').at(-1)[0]).toMatchObject({ Score1: 3, Status: 'Scheduled' });
|
|
76
|
+
await wrapper.get('[data-testid="director-modal-backdrop"]').trigger('click');
|
|
77
|
+
expect(wrapper.emitted('update:closeModal')).toEqual([[props.bout]]);
|
|
78
|
+
expect(wrapper.findComponent(DirectorCard).vm.localBout.Score1).toBe(3);
|
|
79
|
+
expect(props.bout.Score1).toBe(0);
|
|
80
|
+
wrapper.unmount();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -36,6 +36,14 @@
|
|
|
36
36
|
@update:HandShotValue="updateHandShot2"
|
|
37
37
|
/>
|
|
38
38
|
|
|
39
|
+
<BaseText
|
|
40
|
+
v-if="bout?.RefName"
|
|
41
|
+
:text="bout.RefName"
|
|
42
|
+
size="sm"
|
|
43
|
+
color="quaternary"
|
|
44
|
+
class="mt-3 text-center wrap-break-word"
|
|
45
|
+
/>
|
|
46
|
+
|
|
39
47
|
<div class="w-11/12 mt-5 mx-6">
|
|
40
48
|
<BaseButton label="Submit" type="grayAndBlue" :selected="showSubmit" @buttonClick="submitBout" />
|
|
41
49
|
</div>
|
|
@@ -59,6 +67,7 @@ import PassesBox from '../../../Molecules/Boxes/BoutBoxes/PassesBox/PassesBox.vu
|
|
|
59
67
|
import BaseButton from '../../../Molecules/Buttons/BaseButton/BaseButton.vue';
|
|
60
68
|
import PenaltyConfirmationBox from '../../../Molecules/Boxes/BoutBoxes/PenaltyConfirmationBox/PenaltyConfirmationBox.vue';
|
|
61
69
|
import PenaltyModal from '../../../Molecules/Modals/PenaltyModal/PenaltyModal.vue';
|
|
70
|
+
import BaseText from '../../../Atoms/Text/BaseText.vue';
|
|
62
71
|
|
|
63
72
|
const scoreReachedByDirection = {
|
|
64
73
|
true: (bout, maxPoints) => bout.Score1 === maxPoints || bout.Score2 === maxPoints,
|
|
@@ -102,7 +111,7 @@ const cloneBout = bout => ({
|
|
|
102
111
|
|
|
103
112
|
export default {
|
|
104
113
|
name: 'DirectorCard',
|
|
105
|
-
components: { PenaltyModal, PenaltyConfirmationBox, ScoreBox, TimerBox, PassesBox, BaseButton },
|
|
114
|
+
components: { PenaltyModal, PenaltyConfirmationBox, ScoreBox, TimerBox, PassesBox, BaseButton, BaseText },
|
|
106
115
|
emits: ['penalty:remove', 'penalty:add', 'penalty:update', 'submit:bout', 'update:bout'],
|
|
107
116
|
props: {
|
|
108
117
|
eventRules: Array,
|
|
@@ -116,7 +125,6 @@ export default {
|
|
|
116
125
|
showPenaltyConfirmationBox: false,
|
|
117
126
|
fencer1ForModal: true,
|
|
118
127
|
localBout: cloneBout(this.bout),
|
|
119
|
-
isCountingUp: true,
|
|
120
128
|
lastNote1: '',
|
|
121
129
|
lastNote2: '',
|
|
122
130
|
timeLeft: 180,
|
|
@@ -126,7 +134,6 @@ export default {
|
|
|
126
134
|
},
|
|
127
135
|
mounted() {
|
|
128
136
|
this.initTime();
|
|
129
|
-
this.determineCountingDirection();
|
|
130
137
|
this.emitBout();
|
|
131
138
|
},
|
|
132
139
|
beforeUnmount() {
|
|
@@ -136,77 +143,51 @@ export default {
|
|
|
136
143
|
RingName() {
|
|
137
144
|
return this.localBout.Pool?.RingName || 'Ring';
|
|
138
145
|
},
|
|
146
|
+
isCountingUp() {
|
|
147
|
+
const rule = this.findEventRule('PoolScoreDirection');
|
|
148
|
+
return rule ? rule.RuleValue === 'true' : true;
|
|
149
|
+
},
|
|
139
150
|
showPassesBox() {
|
|
140
151
|
return (
|
|
141
|
-
Array.isArray(this.eventRules) &&
|
|
142
152
|
!this.showPenaltyConfirmationBox &&
|
|
143
|
-
(this.
|
|
144
|
-
this.eventRules.some(n2 => n2.Rules.RuleName === 'DENumOfPasses'))
|
|
153
|
+
Boolean(this.findEventRule('PoolNumOfPasses') || this.findEventRule('DENumOfPasses'))
|
|
145
154
|
);
|
|
146
155
|
},
|
|
147
156
|
showTimerBox() {
|
|
148
157
|
return (
|
|
149
|
-
|
|
150
|
-
!this.showPenaltyConfirmationBox &&
|
|
151
|
-
(this.eventRules.some(n2 => n2.Rules.RuleName === 'PoolTime') ||
|
|
152
|
-
this.eventRules.some(n2 => n2.Rules.RuleName === 'DETime'))
|
|
158
|
+
!this.showPenaltyConfirmationBox && Boolean(this.findEventRule('PoolTime') || this.findEventRule('DETime'))
|
|
153
159
|
);
|
|
154
160
|
},
|
|
155
161
|
showHandShotCounterBox() {
|
|
156
|
-
return (
|
|
157
|
-
Array.isArray(this.eventRules) &&
|
|
158
|
-
this.eventRules.some(n2 => n2.Rules.RuleName === 'CountHandshots' && n2.RuleValue === 'true')
|
|
159
|
-
);
|
|
162
|
+
return this.findEventRule('CountHandshots')?.RuleValue === 'true';
|
|
160
163
|
},
|
|
161
164
|
penaltiesReset() {
|
|
162
|
-
return (
|
|
163
|
-
Array.isArray(this.eventRules) &&
|
|
164
|
-
this.eventRules.some(n2 => n2.Rules.RuleName === 'PenaltiesReset' && n2.RuleValue === 'true')
|
|
165
|
-
);
|
|
165
|
+
return this.findEventRule('PenaltiesReset')?.RuleValue === 'true';
|
|
166
166
|
},
|
|
167
167
|
maxPoints() {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const n2 = Array.isArray(this.eventRules)
|
|
174
|
-
? this.eventRules.find(e => e.Rules.RuleName === 'PoolMaxPoints')
|
|
175
|
-
: null;
|
|
176
|
-
return n2 ? parseInt(n2.RuleValue) : 7;
|
|
168
|
+
const ruleName = this.localBout.DEBoutId ? 'DEMaxPoints' : 'PoolMaxPoints';
|
|
169
|
+
const rule = this.findEventRule(ruleName);
|
|
170
|
+
return rule ? parseInt(rule.RuleValue) : 7;
|
|
177
171
|
},
|
|
178
172
|
maxPasses() {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const n2 = Array.isArray(this.eventRules)
|
|
185
|
-
? this.eventRules.find(e => e.Rules.RuleName === 'PoolNumOfPasses')
|
|
186
|
-
: null;
|
|
187
|
-
return n2 ? parseInt(n2.RuleValue) : 7;
|
|
173
|
+
const ruleName = this.localBout.DEBoutId ? 'DEMaxPasses' : 'PoolNumOfPasses';
|
|
174
|
+
const rule = this.findEventRule(ruleName);
|
|
175
|
+
return rule ? parseInt(rule.RuleValue) : 7;
|
|
188
176
|
}
|
|
189
177
|
},
|
|
190
178
|
methods: {
|
|
179
|
+
findEventRule(ruleName) {
|
|
180
|
+
if (!Array.isArray(this.eventRules)) return undefined;
|
|
181
|
+
return this.eventRules.find(rule => (rule?.Rules?.RuleName ?? rule?.RuleName) === ruleName);
|
|
182
|
+
},
|
|
191
183
|
initTime() {
|
|
192
184
|
const saved = Number(this.bout?.TimeLeft);
|
|
193
|
-
|
|
194
|
-
this.timeLeft = Number.isFinite(saved) && saved > 0 ? saved : this.startTime(); // your DE/Pool rule lookup
|
|
185
|
+
this.timeLeft = Number.isFinite(saved) && saved > 0 ? saved : this.startTime();
|
|
195
186
|
},
|
|
196
187
|
startTime() {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const n2 = Array.isArray(this.eventRules) ? this.eventRules.find(e => e.Rules.RuleName === 'PoolTime') : null;
|
|
203
|
-
return n2 ? parseInt(n2.RuleValue) : 180;
|
|
204
|
-
},
|
|
205
|
-
determineCountingDirection() {
|
|
206
|
-
if (Array.isArray(this.eventRules)) {
|
|
207
|
-
const rule = this.eventRules.find(rule => rule.Rules.RuleName === 'PoolScoreDirection');
|
|
208
|
-
this.isCountingUp = rule ? rule.RuleValue === 'true' : true;
|
|
209
|
-
}
|
|
188
|
+
const ruleName = this.localBout.DEBoutId ? 'DETime' : 'PoolTime';
|
|
189
|
+
const rule = this.findEventRule(ruleName);
|
|
190
|
+
return rule ? parseInt(rule.RuleValue) : 180;
|
|
210
191
|
},
|
|
211
192
|
toggleTimer() {
|
|
212
193
|
if (this.timerRunning) {
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import { shallowMount } from '@vue/test-utils';
|
|
2
|
-
import { describe, expect, it } from 'vitest';
|
|
3
|
-
import ToggleSwitch from '../../../Molecules/Inputs/Toggle/Toggle.vue';
|
|
4
|
-
import EditBout from './EditBout.vue';
|
|
5
|
-
|
|
6
|
-
const createBout = () => ({
|
|
7
|
-
BoutId: 42,
|
|
8
|
-
DEBoutId: null,
|
|
9
|
-
Status: 'Completed',
|
|
10
|
-
Score1: 4,
|
|
11
|
-
Score2: 3,
|
|
12
|
-
Person1: { PersonId: 1, DisplayName: 'Fencer One' },
|
|
13
|
-
Person2: { PersonId: 2, DisplayName: 'Fencer Two' }
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const mountEditBout = bout =>
|
|
17
|
-
shallowMount(EditBout, {
|
|
18
|
-
props: {
|
|
19
|
-
bout,
|
|
20
|
-
hostingClubColors: {},
|
|
21
|
-
eventRules: [{ Rules: { RuleName: 'PoolMaxPoints' }, RuleValue: '7' }]
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
describe('EditBout intent', () => {
|
|
26
|
-
it('submits edited values without changing lifecycle fields', () => {
|
|
27
|
-
const bout = createBout();
|
|
28
|
-
const wrapper = mountEditBout(bout);
|
|
29
|
-
|
|
30
|
-
wrapper.vm.plus(true);
|
|
31
|
-
wrapper.vm.submitBout();
|
|
32
|
-
|
|
33
|
-
const submittedBout = wrapper.emitted('submit:bout')[0][0];
|
|
34
|
-
expect(submittedBout).toMatchObject({ Status: 'Completed', Score1: 5, Score2: 3 });
|
|
35
|
-
expect(submittedBout.Ended).toBeUndefined();
|
|
36
|
-
expect(bout.Score1).toBe(4);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('emits make-active intent without setting Status', async () => {
|
|
40
|
-
const bout = createBout();
|
|
41
|
-
const wrapper = mountEditBout(bout);
|
|
42
|
-
|
|
43
|
-
wrapper.findComponent(ToggleSwitch).vm.$emit('update:checked', true);
|
|
44
|
-
await wrapper.vm.$nextTick();
|
|
45
|
-
wrapper.vm.submitBout();
|
|
46
|
-
|
|
47
|
-
expect(wrapper.emitted('submit:bout')).toBeUndefined();
|
|
48
|
-
expect(wrapper.emitted('request:bout-active')).toEqual([[bout]]);
|
|
49
|
-
expect(bout.Status).toBe('Completed');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
1
|
+
import { shallowMount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import ToggleSwitch from '../../../Molecules/Inputs/Toggle/Toggle.vue';
|
|
4
|
+
import EditBout from './EditBout.vue';
|
|
5
|
+
|
|
6
|
+
const createBout = () => ({
|
|
7
|
+
BoutId: 42,
|
|
8
|
+
DEBoutId: null,
|
|
9
|
+
Status: 'Completed',
|
|
10
|
+
Score1: 4,
|
|
11
|
+
Score2: 3,
|
|
12
|
+
Person1: { PersonId: 1, DisplayName: 'Fencer One' },
|
|
13
|
+
Person2: { PersonId: 2, DisplayName: 'Fencer Two' }
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const mountEditBout = bout =>
|
|
17
|
+
shallowMount(EditBout, {
|
|
18
|
+
props: {
|
|
19
|
+
bout,
|
|
20
|
+
hostingClubColors: {},
|
|
21
|
+
eventRules: [{ Rules: { RuleName: 'PoolMaxPoints' }, RuleValue: '7' }]
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('EditBout intent', () => {
|
|
26
|
+
it('submits edited values without changing lifecycle fields', () => {
|
|
27
|
+
const bout = createBout();
|
|
28
|
+
const wrapper = mountEditBout(bout);
|
|
29
|
+
|
|
30
|
+
wrapper.vm.plus(true);
|
|
31
|
+
wrapper.vm.submitBout();
|
|
32
|
+
|
|
33
|
+
const submittedBout = wrapper.emitted('submit:bout')[0][0];
|
|
34
|
+
expect(submittedBout).toMatchObject({ Status: 'Completed', Score1: 5, Score2: 3 });
|
|
35
|
+
expect(submittedBout.Ended).toBeUndefined();
|
|
36
|
+
expect(bout.Score1).toBe(4);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('emits make-active intent without setting Status', async () => {
|
|
40
|
+
const bout = createBout();
|
|
41
|
+
const wrapper = mountEditBout(bout);
|
|
42
|
+
|
|
43
|
+
wrapper.findComponent(ToggleSwitch).vm.$emit('update:checked', true);
|
|
44
|
+
await wrapper.vm.$nextTick();
|
|
45
|
+
wrapper.vm.submitBout();
|
|
46
|
+
|
|
47
|
+
expect(wrapper.emitted('submit:bout')).toBeUndefined();
|
|
48
|
+
expect(wrapper.emitted('request:bout-active')).toEqual([[bout]]);
|
|
49
|
+
expect(bout.Status).toBe('Completed');
|
|
50
|
+
});
|
|
51
|
+
});
|