@dcrackel/hematournamentui 1.0.710 → 1.0.712
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 +152 -148
- package/dist/HemaTournamentUI-lib.es.js +4172 -4046
- package/dist/HemaTournamentUI-lib.umd.js +27 -27
- package/dist/hematournamentui-tailwind.css +11 -0
- package/package.json +1 -1
- package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -0
- package/src/stories/Organisms/Cards/EventCard/EventCard.stories.js +41 -1
- package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -0
- package/src/stories/Organisms/Cards/EventCard/EventCard.vue +62 -30
- package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +1 -1
- package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.vue +12 -5
- package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +4 -0
- package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +249 -227
- package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +4 -0
- package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.test.js +221 -191
- package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue +372 -366
- package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -0
- package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +31 -1
- package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.stories.js +58 -45
- package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +27 -2
- package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +1 -1
- package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +17 -2
- package/src/stories/Util/registrationStatus.js +11 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { afterEach, expect, it } from 'vitest';
|
|
2
|
+
import { shallowMount } from '@vue/test-utils';
|
|
3
|
+
import EditEventWizard from './EditEventWizard.vue';
|
|
4
|
+
|
|
5
|
+
const wrappers = [];
|
|
6
|
+
afterEach(() => wrappers.splice(0).forEach(wrapper => wrapper.unmount()));
|
|
7
|
+
|
|
8
|
+
it.each([
|
|
9
|
+
['CutOffNumber', '32', 14, '32'],
|
|
10
|
+
['poolTime', '180', 1, '180'],
|
|
11
|
+
['DEType', 'single', 15, 'single'],
|
|
12
|
+
['DEType', 'double', 15, 'double'],
|
|
13
|
+
['DEPromoteBy', 'Percent', 17, 'Percent'],
|
|
14
|
+
['seedWith', 'HR - Hema Rating', 11, 'HR - Hema Rating'],
|
|
15
|
+
['CutOffNumber', 0, 14, '0'],
|
|
16
|
+
['thirdPlaceBout', false, 5, 'false'],
|
|
17
|
+
['DEPromotionOrder', ['Win Percent', 'Indicator'], 9, 'Win Percent,Indicator'],
|
|
18
|
+
['seedWith', { text: 'Manual', link: 'MN' }, 11, 'MN']
|
|
19
|
+
])('serializes %s value %j without accessing inherited string methods', (field, value, ruleId, expected) => {
|
|
20
|
+
const wrapper = shallowMount(EditEventWizard, {
|
|
21
|
+
props: { event: { EventId: 12, EventName: 'Example Event', EventPrice: 0, rules: [] }, weapons: [], editMode: true }
|
|
22
|
+
});
|
|
23
|
+
wrappers.push(wrapper);
|
|
24
|
+
wrapper.vm.handleValueChange({ poolTimer: true, [field]: value });
|
|
25
|
+
wrapper.vm.handleUpdate();
|
|
26
|
+
const payload = wrapper.emitted('save:step')[0][0].event;
|
|
27
|
+
expect(payload.rules.find(rule => rule.RuleId === ruleId).RuleValue).toBe(expected);
|
|
28
|
+
expect(payload.rules.some(rule => rule.RuleValue.includes('[native code]'))).toBe(false);
|
|
29
|
+
});
|
|
@@ -110,6 +110,36 @@ export const WithWaitlist = {
|
|
|
110
110
|
personsInTournament: [...activeRoster, ...waitlistedPersons],
|
|
111
111
|
personsAll: personsAllMock,
|
|
112
112
|
fencingClubs: clubsMock,
|
|
113
|
-
event:
|
|
113
|
+
event: {
|
|
114
|
+
...makeEventWithCapacity(eventPersonGetEventById.event, activeRoster.length),
|
|
115
|
+
RegistrationStatus: 'waitlist'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const RegistrationOpen = {
|
|
121
|
+
args: {
|
|
122
|
+
personsInTournament: eventPersonGetEventById.persons,
|
|
123
|
+
personsAll: personsAllMock,
|
|
124
|
+
fencingClubs: clubsMock,
|
|
125
|
+
event: { ...eventPersonGetEventById.event, RegistrationStatus: 'open' }
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const RegistrationFull = {
|
|
130
|
+
args: {
|
|
131
|
+
personsInTournament: eventPersonGetEventById.persons,
|
|
132
|
+
personsAll: personsAllMock,
|
|
133
|
+
fencingClubs: clubsMock,
|
|
134
|
+
event: { ...eventPersonGetEventById.event, RegistrationStatus: 'full' }
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
export const RegistrationClosed = {
|
|
139
|
+
args: {
|
|
140
|
+
personsInTournament: eventPersonGetEventById.persons,
|
|
141
|
+
personsAll: personsAllMock,
|
|
142
|
+
fencingClubs: clubsMock,
|
|
143
|
+
event: { ...eventPersonGetEventById.event, RegistrationStatus: 'closed' }
|
|
114
144
|
}
|
|
115
145
|
};
|
|
@@ -1,45 +1,58 @@
|
|
|
1
|
-
import ListEvents from './ListEvents.vue';
|
|
2
|
-
import eventMockData from '../../../../mocks/eventMock.js';
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
title: 'Templates/TournamentManagement/ListEvents',
|
|
6
|
-
component: ListEvents,
|
|
7
|
-
tags: ['autodocs'],
|
|
8
|
-
args: {
|
|
9
|
-
tournamentId: 8,
|
|
10
|
-
events: eventMockData
|
|
11
|
-
},
|
|
12
|
-
argTypes: {
|
|
13
|
-
tournamentId: { control: { type: 'number' } },
|
|
14
|
-
events: { control: { type: 'object' } },
|
|
15
|
-
'tab:menu-click': { action: 'tab:menu-click' },
|
|
16
|
-
manageEvent: { action: 'manageEvent' },
|
|
17
|
-
editEvent: { action: 'editEvent' },
|
|
18
|
-
addEvent: { action: 'addEvent' },
|
|
19
|
-
'delete:Event': { action: 'delete:Event' }
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const Default = {
|
|
24
|
-
args: {
|
|
25
|
-
tournamentId: 8,
|
|
26
|
-
events: eventMockData
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export const NoEvents = {
|
|
31
|
-
args: {
|
|
32
|
-
tournamentId: 8,
|
|
33
|
-
events: []
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
export const
|
|
38
|
-
args: {
|
|
39
|
-
tournamentId: 8,
|
|
40
|
-
events:
|
|
41
|
-
...
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
1
|
+
import ListEvents from './ListEvents.vue';
|
|
2
|
+
import eventMockData from '../../../../mocks/eventMock.js';
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Templates/TournamentManagement/ListEvents',
|
|
6
|
+
component: ListEvents,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
args: {
|
|
9
|
+
tournamentId: 8,
|
|
10
|
+
events: eventMockData
|
|
11
|
+
},
|
|
12
|
+
argTypes: {
|
|
13
|
+
tournamentId: { control: { type: 'number' } },
|
|
14
|
+
events: { control: { type: 'object' } },
|
|
15
|
+
'tab:menu-click': { action: 'tab:menu-click' },
|
|
16
|
+
manageEvent: { action: 'manageEvent' },
|
|
17
|
+
editEvent: { action: 'editEvent' },
|
|
18
|
+
addEvent: { action: 'addEvent' },
|
|
19
|
+
'delete:Event': { action: 'delete:Event' }
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const Default = {
|
|
24
|
+
args: {
|
|
25
|
+
tournamentId: 8,
|
|
26
|
+
events: eventMockData
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const NoEvents = {
|
|
31
|
+
args: {
|
|
32
|
+
tournamentId: 8,
|
|
33
|
+
events: []
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const RegistrationStatuses = {
|
|
38
|
+
args: {
|
|
39
|
+
tournamentId: 8,
|
|
40
|
+
events: ['open', 'waitlist', 'full', 'closed'].map((RegistrationStatus, index) => ({
|
|
41
|
+
...eventMockData[0],
|
|
42
|
+
EventId: index + 1,
|
|
43
|
+
EventName: ['Longsword Division 1', 'Rapier', 'Saber', 'Longsword Division 2'][index],
|
|
44
|
+
Status: 'registration',
|
|
45
|
+
RegistrationStatus
|
|
46
|
+
}))
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const CompletedEvents = {
|
|
51
|
+
args: {
|
|
52
|
+
tournamentId: 8,
|
|
53
|
+
events: eventMockData.map(event => ({
|
|
54
|
+
...event,
|
|
55
|
+
Status: 'completed'
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
};
|
|
@@ -34,6 +34,31 @@ function existingTournament(BasePrice) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
describe('tournament base price', () => {
|
|
37
|
+
it.each([
|
|
38
|
+
[45, '45.00'],
|
|
39
|
+
['45', '45.00'],
|
|
40
|
+
[45.5, '45.50'],
|
|
41
|
+
['45.5', '45.50']
|
|
42
|
+
])('displays a saved base price of %s as %s', async (value, display) => {
|
|
43
|
+
const wrapper = mountTournament(existingTournament(value));
|
|
44
|
+
await wrapper.vm.$nextTick();
|
|
45
|
+
expect(priceField(wrapper).find('input').element.value).toBe(display);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('formats an entered price on blur and preserves the numeric submit payload', async () => {
|
|
49
|
+
const wrapper = mountTournament();
|
|
50
|
+
await wrapper.vm.$nextTick();
|
|
51
|
+
wrapper.vm.updateTournament({ Name: 'Test Tournament', Address: { Name: 'Test Venue' } });
|
|
52
|
+
const input = priceField(wrapper).find('input');
|
|
53
|
+
await input.setValue('45.');
|
|
54
|
+
expect(input.element.value).toBe('45.');
|
|
55
|
+
await input.setValue('45');
|
|
56
|
+
await input.trigger('focusout');
|
|
57
|
+
expect(input.element.value).toBe('45.00');
|
|
58
|
+
wrapper.vm.handleSubmit();
|
|
59
|
+
expect(wrapper.emitted('submit:tournament')[0][0].BasePrice).toBe(45);
|
|
60
|
+
});
|
|
61
|
+
|
|
37
62
|
it('retains decimal typing and submits the base price from a new tournament', async () => {
|
|
38
63
|
const wrapper = mountTournament();
|
|
39
64
|
await wrapper.vm.$nextTick();
|
|
@@ -61,13 +86,13 @@ describe('tournament base price', () => {
|
|
|
61
86
|
expect(wrapper.emitted('submit:tournament')[0][0].BasePrice).toBe(80.75);
|
|
62
87
|
expect(tournament.BasePrice).toBe(50.25);
|
|
63
88
|
await wrapper.setProps({ tournament: existingTournament(0) });
|
|
64
|
-
expect(priceField(wrapper).find('input').element.value).toBe('0');
|
|
89
|
+
expect(priceField(wrapper).find('input').element.value).toBe('0.00');
|
|
65
90
|
});
|
|
66
91
|
|
|
67
92
|
it.each([undefined, null, 0])('allows free tournaments with an initial price of %s', async value => {
|
|
68
93
|
const wrapper = mountTournament(existingTournament(value));
|
|
69
94
|
await wrapper.vm.$nextTick();
|
|
70
|
-
expect(priceField(wrapper).find('input').element.value).toBe(value == null ? '' : '0');
|
|
95
|
+
expect(priceField(wrapper).find('input').element.value).toBe(value == null ? '' : '0.00');
|
|
71
96
|
expect(priceField(wrapper).find('input').attributes('placeholder')).toBe('0.00');
|
|
72
97
|
await priceField(wrapper).find('input').setValue('');
|
|
73
98
|
wrapper.vm.handleSubmit();
|
package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js
CHANGED
|
@@ -89,7 +89,7 @@ export const EmptyTournament = {
|
|
|
89
89
|
|
|
90
90
|
export const EditMode = {
|
|
91
91
|
args: {
|
|
92
|
-
tournament: { ...mockTournaments[0], BasePrice:
|
|
92
|
+
tournament: { ...mockTournaments[0], BasePrice: 45 },
|
|
93
93
|
locations: mockLocation,
|
|
94
94
|
persons: mockPersons,
|
|
95
95
|
validation: validTournamentFields,
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
:invalid="isFieldInvalid('BasePrice')"
|
|
52
52
|
:title="getValidationLabel('BasePrice', 'Base Price')"
|
|
53
53
|
placeholder="0.00"
|
|
54
|
-
@update:value="
|
|
54
|
+
@update:value="handleBasePriceUpdate"
|
|
55
|
+
@focusout="handleBasePriceBlur"
|
|
55
56
|
class="mb-4"
|
|
56
57
|
/>
|
|
57
58
|
|
|
@@ -223,6 +224,9 @@ export default {
|
|
|
223
224
|
TitledInput,
|
|
224
225
|
TextBoxEditor
|
|
225
226
|
},
|
|
227
|
+
data() {
|
|
228
|
+
return { basePriceDraft: null };
|
|
229
|
+
},
|
|
226
230
|
computed: {
|
|
227
231
|
tournamentImage() {
|
|
228
232
|
if (this.tournament.Images && this.tournament.Images.length > 0) {
|
|
@@ -234,7 +238,11 @@ export default {
|
|
|
234
238
|
return this.tournament?.Name || '';
|
|
235
239
|
},
|
|
236
240
|
basePrice() {
|
|
237
|
-
|
|
241
|
+
const price = this.tournament?.BasePrice ?? '';
|
|
242
|
+
if (price === '' || price === this.basePriceDraft) return price;
|
|
243
|
+
const amount = Number(price);
|
|
244
|
+
if (!Number.isFinite(amount)) return price;
|
|
245
|
+
return amount.toFixed(2);
|
|
238
246
|
},
|
|
239
247
|
addressName() {
|
|
240
248
|
return this.tournament?.Address?.Name || '';
|
|
@@ -257,6 +265,13 @@ export default {
|
|
|
257
265
|
}
|
|
258
266
|
},
|
|
259
267
|
methods: {
|
|
268
|
+
handleBasePriceUpdate(value) {
|
|
269
|
+
this.basePriceDraft = value;
|
|
270
|
+
this.emitUpdate('BasePrice', value);
|
|
271
|
+
},
|
|
272
|
+
handleBasePriceBlur() {
|
|
273
|
+
this.basePriceDraft = null;
|
|
274
|
+
},
|
|
260
275
|
handleNewImageUrl(url) {
|
|
261
276
|
this.$emit('update:image', { ImageLink: url });
|
|
262
277
|
},
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const statusLabels = new Map([
|
|
2
|
+
['open', 'Open'],
|
|
3
|
+
['waitlist', 'Waitlist'],
|
|
4
|
+
['full', 'Full'],
|
|
5
|
+
['closed', 'Closed']
|
|
6
|
+
]);
|
|
7
|
+
|
|
8
|
+
export function getRegistrationStatusLabel(status) {
|
|
9
|
+
const value = typeof status === 'string' ? status.trim().toLowerCase() : '';
|
|
10
|
+
return statusLabels.get(value) ?? '';
|
|
11
|
+
}
|