@dcrackel/hematournamentui 1.0.714 → 1.0.716
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/README.md +2 -0
- package/dist/HemaTournamentUI-lib.es.js +4367 -3850
- package/dist/HemaTournamentUI-lib.umd.js +27 -27
- package/dist/hematournamentui-tailwind.css +267 -9
- package/package.json +1 -1
- package/src/index.js +45 -43
- package/src/stories/Organisms/Cards/RingBoutCard/RingBoutCard.vue +158 -0
- package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +10 -0
- package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +140 -80
- package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.test.js +94 -0
- package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +180 -153
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +19 -3
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +59 -22
- package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +13 -0
- package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +26 -0
- package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +6 -2
- package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +35 -3
- package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.test.js +73 -0
- package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.vue +417 -403
- package/src/stories/Templates/EventManagement/RingBoard/README.md +29 -0
- package/src/stories/Templates/EventManagement/RingBoard/RingBoard.stories.js +118 -0
- package/src/stories/Templates/EventManagement/RingBoard/RingBoard.test.js +208 -0
- package/src/stories/Templates/EventManagement/RingBoard/RingBoard.vue +170 -0
- package/src/stories/Util/ringBoard.js +103 -0
- package/src/stories/Util/waitlistAttendance.js +8 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<article class="overflow-hidden rounded border" :class="cardClass" :aria-label="boutLabel">
|
|
3
|
+
<header class="grid grid-cols-[minmax(0,1fr)_auto] items-center gap-2 px-2 py-1.5" :class="headerClass">
|
|
4
|
+
<div class="flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs">
|
|
5
|
+
<span v-if="active || !ready" class="flex items-center gap-1.5 font-semibold text-primary">
|
|
6
|
+
<span v-if="active" class="h-1.5 w-1.5 rounded-full bg-orange2" aria-hidden="true"></span>
|
|
7
|
+
{{ stateLabel }}
|
|
8
|
+
</span>
|
|
9
|
+
<span class="text-primary/70">{{ boutReference }}</span>
|
|
10
|
+
</div>
|
|
11
|
+
<div v-if="active" class="flex flex-wrap items-baseline justify-end gap-x-2">
|
|
12
|
+
<span class="text-xs text-primary/70">{{ timerLabel }}</span>
|
|
13
|
+
<span class="font-mono text-2xl font-bold tabular-nums text-primary">{{ displayTime }}</span>
|
|
14
|
+
</div>
|
|
15
|
+
</header>
|
|
16
|
+
<div class="space-y-px p-1">
|
|
17
|
+
<TableauFencerCard
|
|
18
|
+
v-for="row in fencerRows"
|
|
19
|
+
:key="row.key"
|
|
20
|
+
:fencer="row.fencer"
|
|
21
|
+
:position="row.position"
|
|
22
|
+
:score="row.score"
|
|
23
|
+
:showScore="active"
|
|
24
|
+
:scoreColor="row.scoreColor"
|
|
25
|
+
:color="row.color"
|
|
26
|
+
:isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
|
|
27
|
+
large
|
|
28
|
+
nightMode
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
</article>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<script>
|
|
35
|
+
import TableauFencerCard from '../TableauFencerCard/TableauFencerCard.vue';
|
|
36
|
+
import { hasReadyFencer, isActiveBout, isReadyBout } from '../../../Util/ringBoard.js';
|
|
37
|
+
|
|
38
|
+
const normalizeTime = value => {
|
|
39
|
+
if (value == null || value === '') return null;
|
|
40
|
+
const seconds = Number(value);
|
|
41
|
+
return Number.isFinite(seconds) ? Math.max(0, Math.floor(seconds)) : null;
|
|
42
|
+
};
|
|
43
|
+
const formatTime = seconds =>
|
|
44
|
+
seconds == null ? '—' : `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`;
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
name: 'RingBoutCard',
|
|
48
|
+
components: { TableauFencerCard },
|
|
49
|
+
emits: [],
|
|
50
|
+
props: {
|
|
51
|
+
bout: { type: Object, required: true },
|
|
52
|
+
hostingClubColors: { type: Object, default: () => ({}) },
|
|
53
|
+
isCountingBackwardsMaxScore: { type: Number, default: 0 },
|
|
54
|
+
connectedToServer: { type: Boolean, default: true }
|
|
55
|
+
},
|
|
56
|
+
data() {
|
|
57
|
+
return { now: Date.now(), receivedAt: Date.now(), interval: null };
|
|
58
|
+
},
|
|
59
|
+
computed: {
|
|
60
|
+
active() {
|
|
61
|
+
return isActiveBout(this.bout);
|
|
62
|
+
},
|
|
63
|
+
ready() {
|
|
64
|
+
return isReadyBout(this.bout);
|
|
65
|
+
},
|
|
66
|
+
cardClass() {
|
|
67
|
+
return this.active ? 'border-neutral/60 bg-lightgray' : 'border-neutral/20 bg-lightgray';
|
|
68
|
+
},
|
|
69
|
+
headerClass() {
|
|
70
|
+
return this.active ? 'bg-black/10' : 'bg-black/5';
|
|
71
|
+
},
|
|
72
|
+
stateLabel() {
|
|
73
|
+
if (this.active) return 'In progress';
|
|
74
|
+
return this.ready ? 'Scheduled' : 'Awaiting fencers';
|
|
75
|
+
},
|
|
76
|
+
boutReference() {
|
|
77
|
+
const id = this.bout.DEBoutId ?? this.bout.BoutId;
|
|
78
|
+
return [this.bout.RoundLabel, id == null ? '' : `Bout ${id}`].filter(Boolean).join(' · ');
|
|
79
|
+
},
|
|
80
|
+
boutLabel() {
|
|
81
|
+
return `${this.stateLabel}: ${this.fencerRows.map(row => row.fencer.DisplayName).join(' versus ')}`;
|
|
82
|
+
},
|
|
83
|
+
running() {
|
|
84
|
+
return this.active && String(this.bout.TimerStatus || '').toLowerCase() === 'running';
|
|
85
|
+
},
|
|
86
|
+
remaining() {
|
|
87
|
+
const seconds = normalizeTime(this.bout.TimeLeft);
|
|
88
|
+
if (seconds == null || !this.running || !this.connectedToServer) return seconds;
|
|
89
|
+
return Math.max(0, seconds - Math.floor((this.now - this.receivedAt) / 1000));
|
|
90
|
+
},
|
|
91
|
+
displayTime() {
|
|
92
|
+
return formatTime(this.remaining);
|
|
93
|
+
},
|
|
94
|
+
timerLabel() {
|
|
95
|
+
if (!this.connectedToServer) return 'Last received';
|
|
96
|
+
if (this.remaining == null) return 'Time unavailable';
|
|
97
|
+
if (String(this.bout.TimerStatus || '').toLowerCase() === 'paused') return 'Paused';
|
|
98
|
+
return this.running ? 'Remaining' : 'Stopped';
|
|
99
|
+
},
|
|
100
|
+
fencerRows() {
|
|
101
|
+
return ['1', '2'].map(this.createFencerRow);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
watch: {
|
|
105
|
+
'bout.TimeLeft': 'resetTimer',
|
|
106
|
+
'bout.TimerStatus': 'resetTimer',
|
|
107
|
+
'bout.Status': 'resetTimer',
|
|
108
|
+
'bout.BoutId': 'resetTimer',
|
|
109
|
+
'bout.DEBoutId': 'resetTimer',
|
|
110
|
+
connectedToServer: 'resetTimer'
|
|
111
|
+
},
|
|
112
|
+
mounted() {
|
|
113
|
+
this.resetTimer();
|
|
114
|
+
},
|
|
115
|
+
beforeUnmount() {
|
|
116
|
+
this.stopTimer();
|
|
117
|
+
},
|
|
118
|
+
methods: {
|
|
119
|
+
getDisplayedScore(number) {
|
|
120
|
+
const score = Number(this.bout[`Score${number}`] ?? 0);
|
|
121
|
+
if (!Number.isFinite(score)) return null;
|
|
122
|
+
return this.isCountingBackwardsMaxScore > 0 ? this.isCountingBackwardsMaxScore - score : score;
|
|
123
|
+
},
|
|
124
|
+
isLeadingFencer(number) {
|
|
125
|
+
if (!this.active) return false;
|
|
126
|
+
const score = this.getDisplayedScore(number);
|
|
127
|
+
const opponentScore = this.getDisplayedScore(number === '1' ? '2' : '1');
|
|
128
|
+
return score !== null && opponentScore !== null && score > opponentScore;
|
|
129
|
+
},
|
|
130
|
+
createFencerRow(number) {
|
|
131
|
+
const person = this.bout[`Person${number}`];
|
|
132
|
+
return {
|
|
133
|
+
key: number,
|
|
134
|
+
fencer: hasReadyFencer(person) ? person : { DisplayName: 'Awaiting winner' },
|
|
135
|
+
position: person?.EventPersons?.[0]?.DEPosition ?? '',
|
|
136
|
+
score: String(this.bout[`Score${number}`] ?? 0),
|
|
137
|
+
scoreColor: this.isLeadingFencer(number) ? 'bg-orange2' : 'bg-neutral/50',
|
|
138
|
+
color: this.hostingClubColors[`Color${number}`] || ''
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
resetTimer() {
|
|
142
|
+
this.stopTimer();
|
|
143
|
+
this.receivedAt = Date.now();
|
|
144
|
+
this.now = this.receivedAt;
|
|
145
|
+
if (!this.running || !this.connectedToServer || !this.remaining) return;
|
|
146
|
+
this.interval = setInterval(this.tickTimer, 250);
|
|
147
|
+
},
|
|
148
|
+
tickTimer() {
|
|
149
|
+
this.now = Date.now();
|
|
150
|
+
if (this.remaining === 0) this.stopTimer();
|
|
151
|
+
},
|
|
152
|
+
stopTimer() {
|
|
153
|
+
if (this.interval) clearInterval(this.interval);
|
|
154
|
+
this.interval = null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
</script>
|
|
@@ -67,3 +67,13 @@ export const PendingFencer = {
|
|
|
67
67
|
showScore: false
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
|
+
|
|
71
|
+
export const Large = {
|
|
72
|
+
args: {
|
|
73
|
+
large: true,
|
|
74
|
+
fencer: { DisplayName: 'Alexandra Montgomery-Worthington' },
|
|
75
|
+
score: '6',
|
|
76
|
+
position: '12',
|
|
77
|
+
scoreColor: 'bg-dropdownSelect'
|
|
78
|
+
}
|
|
79
|
+
};
|
|
@@ -1,80 +1,140 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section class="flex
|
|
3
|
-
<div
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
},
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<section class="flex overflow-hidden border" :class="[cornerClass, presentation.layout, rowClass]">
|
|
3
|
+
<div
|
|
4
|
+
class="flex shrink-0 items-center justify-center px-1"
|
|
5
|
+
:class="[positionColorClass, presentation.positionWidth]"
|
|
6
|
+
>
|
|
7
|
+
<BaseText :text="position" :size="presentation.positionSize" :color="positionTextColor" />
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div class="flex min-w-0 flex-1 items-center px-2">
|
|
11
|
+
<BaseText
|
|
12
|
+
:text="name"
|
|
13
|
+
:size="presentation.nameSize"
|
|
14
|
+
:color="presentation.nameColor"
|
|
15
|
+
:class="presentation.nameClass"
|
|
16
|
+
/>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div
|
|
20
|
+
v-if="showScore || !large"
|
|
21
|
+
class="flex shrink-0 items-center justify-center border-l"
|
|
22
|
+
:class="[scoreColor, presentation.scoreWidth, scoreBorderClass]"
|
|
23
|
+
>
|
|
24
|
+
<BaseText
|
|
25
|
+
v-if="showScore"
|
|
26
|
+
:text="displayedScore"
|
|
27
|
+
:size="presentation.scoreSize"
|
|
28
|
+
:color="presentation.scoreTextColor"
|
|
29
|
+
class="tabular-nums"
|
|
30
|
+
:class="{ 'text-black': nightMode }"
|
|
31
|
+
:weight="nightMode ? 'bold' : 'normal'"
|
|
32
|
+
/>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script>
|
|
38
|
+
import BaseText from '../../../Atoms/Text/BaseText.vue';
|
|
39
|
+
|
|
40
|
+
const cornerClasses = {
|
|
41
|
+
none: '',
|
|
42
|
+
top: 'rounded-t',
|
|
43
|
+
bottom: 'rounded-b'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default {
|
|
47
|
+
name: 'TableauFencerCard',
|
|
48
|
+
components: { BaseText },
|
|
49
|
+
props: {
|
|
50
|
+
large: { type: Boolean, default: false },
|
|
51
|
+
nightMode: { type: Boolean, default: false },
|
|
52
|
+
fencer: {
|
|
53
|
+
type: Object,
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
score: {
|
|
57
|
+
type: String,
|
|
58
|
+
required: true
|
|
59
|
+
},
|
|
60
|
+
position: {
|
|
61
|
+
type: [String, Number],
|
|
62
|
+
required: true
|
|
63
|
+
},
|
|
64
|
+
color: {
|
|
65
|
+
type: String,
|
|
66
|
+
default: ''
|
|
67
|
+
},
|
|
68
|
+
scoreColor: {
|
|
69
|
+
type: String,
|
|
70
|
+
required: true
|
|
71
|
+
},
|
|
72
|
+
showScore: {
|
|
73
|
+
type: Boolean,
|
|
74
|
+
default: true
|
|
75
|
+
},
|
|
76
|
+
isCountingBackwardsMaxScore: {
|
|
77
|
+
type: Number,
|
|
78
|
+
default: 0
|
|
79
|
+
},
|
|
80
|
+
corners: {
|
|
81
|
+
type: String,
|
|
82
|
+
default: 'none',
|
|
83
|
+
validator: value => Object.hasOwn(cornerClasses, value)
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
computed: {
|
|
87
|
+
presentation() {
|
|
88
|
+
if (this.large)
|
|
89
|
+
return {
|
|
90
|
+
layout: this.nightMode ? 'min-h-12 w-full' : 'min-h-16 w-full',
|
|
91
|
+
positionWidth: 'w-8',
|
|
92
|
+
positionSize: 'sm',
|
|
93
|
+
nameSize: this.nightMode ? '2xl' : 'xl',
|
|
94
|
+
nameColor: 'primary',
|
|
95
|
+
nameClass: this.nightMode
|
|
96
|
+
? 'break-words py-2 font-semibold leading-tight'
|
|
97
|
+
: 'break-words py-3 font-semibold leading-tight',
|
|
98
|
+
scoreWidth: 'w-14',
|
|
99
|
+
scoreSize: '3xl',
|
|
100
|
+
scoreTextColor: this.nightMode ? 'none' : 'primary'
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
layout: 'h-7 w-40',
|
|
104
|
+
positionWidth: 'w-5',
|
|
105
|
+
positionSize: 'xs',
|
|
106
|
+
nameSize: 'xs',
|
|
107
|
+
nameColor: this.nightMode ? 'primary' : 'quaternary',
|
|
108
|
+
nameClass: 'truncate whitespace-nowrap',
|
|
109
|
+
scoreWidth: 'w-7',
|
|
110
|
+
scoreSize: 'xs',
|
|
111
|
+
scoreTextColor: this.nightMode ? 'none' : 'neutral'
|
|
112
|
+
};
|
|
113
|
+
},
|
|
114
|
+
scoreBorderClass() {
|
|
115
|
+
return this.nightMode ? 'border-transparent' : 'border-poolSetup';
|
|
116
|
+
},
|
|
117
|
+
rowClass() {
|
|
118
|
+
return this.nightMode ? 'border-black/10 bg-lightgray' : 'border-poolSetup bg-poolSetup';
|
|
119
|
+
},
|
|
120
|
+
positionTextColor() {
|
|
121
|
+
if (this.nightMode) return this.color ? 'neutral' : 'primaryHighlight';
|
|
122
|
+
return this.large && !this.color ? 'primaryHighlight' : 'neutral';
|
|
123
|
+
},
|
|
124
|
+
cornerClass() {
|
|
125
|
+
return cornerClasses[this.corners];
|
|
126
|
+
},
|
|
127
|
+
displayedScore() {
|
|
128
|
+
const score = Number(this.score || 0);
|
|
129
|
+
return this.isCountingBackwardsMaxScore > 0 ? this.isCountingBackwardsMaxScore - score : score;
|
|
130
|
+
},
|
|
131
|
+
name() {
|
|
132
|
+
return String(this.fencer?.DisplayName || '');
|
|
133
|
+
},
|
|
134
|
+
positionColorClass() {
|
|
135
|
+
if (this.color) return `bg-${this.color}`;
|
|
136
|
+
return this.nightMode ? 'bg-black/10' : 'bg-dropdownSelect';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
</script>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import WaitlistFencerCard from './WaitlistFencerCard.vue';
|
|
4
|
+
import ButtonBar from '../../../Molecules/Buttons/ButtonBar/ButtonBar.vue';
|
|
5
|
+
|
|
6
|
+
const createPerson = (fields = {}) => ({
|
|
7
|
+
PersonId: 42,
|
|
8
|
+
Status: 'Waitlist',
|
|
9
|
+
CheckedIn: false,
|
|
10
|
+
Person: {
|
|
11
|
+
DisplayName: 'Waiting Fencer',
|
|
12
|
+
Club: { Name: 'Sword Club' },
|
|
13
|
+
Weaver: { Required: true, Scope: 'Tournament', Signed: true }
|
|
14
|
+
},
|
|
15
|
+
...fields
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const findButton = (wrapper, label) => wrapper.findAll('button').find(button => button.text() === label);
|
|
19
|
+
const selectAttendance = (wrapper, label) =>
|
|
20
|
+
wrapper
|
|
21
|
+
.getComponent(ButtonBar)
|
|
22
|
+
.findAll('section')
|
|
23
|
+
.find(button => button.text() === label)
|
|
24
|
+
.trigger('click');
|
|
25
|
+
|
|
26
|
+
describe('Waitlist day-of attendance', () => {
|
|
27
|
+
it('marks present and absent without changing waitlist status or mutating incoming data', async () => {
|
|
28
|
+
const person = Object.freeze(createPerson());
|
|
29
|
+
const wrapper = mount(WaitlistFencerCard, { props: { person, queuePosition: 1 } });
|
|
30
|
+
|
|
31
|
+
await selectAttendance(wrapper, 'Present');
|
|
32
|
+
const present = wrapper.emitted('update:check-in')[0][0];
|
|
33
|
+
expect(present).toEqual({ ...person, CheckedIn: true });
|
|
34
|
+
expect(present.Person).not.toBe(person.Person);
|
|
35
|
+
expect(person.CheckedIn).toBe(false);
|
|
36
|
+
expect(wrapper.emitted('move:active')).toBeUndefined();
|
|
37
|
+
|
|
38
|
+
await wrapper.setProps({ person: present });
|
|
39
|
+
expect(wrapper.text()).toContain('Checked in');
|
|
40
|
+
await selectAttendance(wrapper, 'Absent');
|
|
41
|
+
expect(wrapper.emitted('update:check-in')[1][0]).toEqual({ ...person, CheckedIn: false });
|
|
42
|
+
wrapper.unmount();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('offers waiver signing before attendance and keeps promotion available', async () => {
|
|
46
|
+
const person = createPerson();
|
|
47
|
+
person.Person.Weaver.Signed = false;
|
|
48
|
+
const wrapper = mount(WaitlistFencerCard, { props: { person, queuePosition: 1 } });
|
|
49
|
+
|
|
50
|
+
expect(wrapper.findComponent(ButtonBar).exists()).toBe(false);
|
|
51
|
+
await findButton(wrapper, 'Sign Waiver').trigger('click');
|
|
52
|
+
const request = wrapper.emitted('request:weaver-sign')[0][0];
|
|
53
|
+
expect(request).toEqual(person);
|
|
54
|
+
expect(request.Person).not.toBe(person.Person);
|
|
55
|
+
expect(wrapper.emitted('update:check-in')).toBeUndefined();
|
|
56
|
+
await findButton(wrapper, 'Move to Active').trigger('click');
|
|
57
|
+
expect(wrapper.emitted('move:active')[0][0]).toEqual(person);
|
|
58
|
+
|
|
59
|
+
const signed = createPerson();
|
|
60
|
+
await wrapper.setProps({ person: signed });
|
|
61
|
+
expect(findButton(wrapper, 'Sign Waiver')).toBeUndefined();
|
|
62
|
+
expect(wrapper.getComponent(ButtonBar).props('selected')).toBe('Absent');
|
|
63
|
+
await selectAttendance(wrapper, 'Present');
|
|
64
|
+
expect(wrapper.emitted('update:check-in')[0][0].CheckedIn).toBe(true);
|
|
65
|
+
wrapper.unmount();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('reconciles parent attendance and promotes the checked-in waitlist person unchanged', async () => {
|
|
69
|
+
const person = createPerson();
|
|
70
|
+
const wrapper = mount(WaitlistFencerCard, { props: { person, queuePosition: 1, isNextCheckedIn: true } });
|
|
71
|
+
|
|
72
|
+
await wrapper.setProps({ person: { ...person, CheckedIn: true } });
|
|
73
|
+
expect(wrapper.getComponent(ButtonBar).props('selected')).toBe('Present');
|
|
74
|
+
expect(wrapper.text()).toContain('Next checked in');
|
|
75
|
+
await findButton(wrapper, 'Move to Active').trigger('click');
|
|
76
|
+
expect(wrapper.emitted('move:active')[0][0]).toEqual({ ...person, CheckedIn: true });
|
|
77
|
+
expect(wrapper.emitted('move:active')[0][0].Person).not.toBe(person.Person);
|
|
78
|
+
|
|
79
|
+
await wrapper.setProps({ person: { ...person, CheckedIn: false } });
|
|
80
|
+
expect(wrapper.getComponent(ButtonBar).props('selected')).toBe('Absent');
|
|
81
|
+
expect(wrapper.text()).not.toContain('Next checked in');
|
|
82
|
+
wrapper.unmount();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('shows attendance controls when no applicable waiver is required', () => {
|
|
86
|
+
const person = createPerson();
|
|
87
|
+
person.Person.Weaver = { Required: true, Scope: 'none', Signed: false };
|
|
88
|
+
const wrapper = mount(WaitlistFencerCard, { props: { person, queuePosition: 1 } });
|
|
89
|
+
|
|
90
|
+
expect(wrapper.findComponent(ButtonBar).exists()).toBe(true);
|
|
91
|
+
expect(findButton(wrapper, 'Sign Waiver')).toBeUndefined();
|
|
92
|
+
wrapper.unmount();
|
|
93
|
+
});
|
|
94
|
+
});
|