@dcrackel/hematournamentui 1.0.708 → 1.0.710

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +148 -106
  2. package/dist/HemaTournamentUI-lib.es.js +6678 -6196
  3. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  4. package/dist/hematournamentui-tailwind.css +16 -6
  5. package/package.json +1 -1
  6. package/src/stories/Atoms/Input/BaseInput.test.js +97 -85
  7. package/src/stories/Atoms/Input/BaseInput.vue +149 -150
  8. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -20
  9. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -0
  10. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -133
  11. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +48 -41
  12. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +227 -206
  13. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -225
  14. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
  15. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +133 -91
  16. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.test.js +191 -0
  17. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue +366 -309
  18. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -171
  19. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -215
  20. package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -0
  21. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -52
  22. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -75
  23. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +14 -0
  24. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -0
  25. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +13 -3
  26. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -172
  27. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -103
  28. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -0
  29. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -213
  30. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -0
  31. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -83
  32. package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +89 -0
  33. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -360
  34. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -113
  35. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +292 -280
  36. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -29
  37. package/src/stories/Util/Validations/PriceValidation.js +6 -0
  38. package/src/stories/Util/Validations/TournamentFieldsValidations.js +4 -0
@@ -0,0 +1,191 @@
1
+ import { afterEach, describe, expect, it } from 'vitest';
2
+ import { mount, shallowMount } from '@vue/test-utils';
3
+ import EditEventWizard from './EditEventWizard.vue';
4
+ import EditEventInfo from '../../../Templates/EventManagement/EditEventInfo/EditEventInfo.vue';
5
+ import EventCustomization from './EventCustomization/EventCustomization.vue';
6
+ import BasicEventInfo from './BasicEventInfo/BasicEventInfo.vue';
7
+
8
+ const wrappers = [];
9
+ afterEach(() => wrappers.splice(0).forEach(wrapper => wrapper.unmount()));
10
+
11
+ function createEvent() {
12
+ return {
13
+ EventId: 12,
14
+ EventName: 'Longsword Open',
15
+ Date: '2026-10-17',
16
+ StartTime: '12:00',
17
+ NumberOfRings: 2,
18
+ WeaponId: 1,
19
+ IncludeInRatings: false,
20
+ rules: [
21
+ { RuleId: 1, RuleValue: '90' },
22
+ { RuleId: 2, RuleValue: '7' },
23
+ { RuleId: 4, RuleValue: '12' },
24
+ { RuleId: 9, RuleValue: 'Win Percent,Indicator' },
25
+ { RuleId: 17, RuleValue: 'Count' },
26
+ { RuleId: 18, RuleValue: 'true' }
27
+ ]
28
+ };
29
+ }
30
+
31
+ function mountWizard(props = {}) {
32
+ const wrapper = shallowMount(EditEventWizard, { props: { event: createEvent(), weapons: [], ...props } });
33
+ wrappers.push(wrapper);
34
+ return wrapper;
35
+ }
36
+
37
+ describe('event editing with step saves', () => {
38
+ it('requests a save on each step and waits for the parent before advancing', async () => {
39
+ const wrapper = mountWizard({ editMode: true });
40
+ expect(wrapper.findComponent(BasicEventInfo).props('nextButtonLabel')).toBe('Save and Continue');
41
+ wrapper.vm.handleValueChange({ EventName: 'Changed Event' });
42
+ wrapper.vm.handleUpdate();
43
+ const request = wrapper.emitted('save:step')[0][0];
44
+ expect(request.step).toBe(1);
45
+ expect(request.event.EventName).toBe('Changed Event');
46
+ expect(wrapper.vm.currentStep).toBe(1);
47
+ expect(wrapper.emitted('update:submit')).toBeUndefined();
48
+ await wrapper.setProps({ event: request.event, step: 2, saving: false, saveNotice: 'Changes saved.' });
49
+ expect(wrapper.vm.currentStep).toBe(2);
50
+ expect(wrapper.findComponent(EventCustomization).props('nextButtonLabel')).toBe('Save and Continue');
51
+ expect(wrapper.text()).toContain('Changes saved.');
52
+ expect(wrapper.emitted('update:dirty').at(-1)).toEqual([false]);
53
+ });
54
+
55
+ it('blocks duplicate saves, navigation and edits while saving', async () => {
56
+ const wrapper = mountWizard({ editMode: true });
57
+ wrapper.vm.handleValueChange({ EventName: 'Changed Event' });
58
+ wrapper.vm.handleUpdate();
59
+ await wrapper.setProps({ saving: true });
60
+ expect(wrapper.find('fieldset').attributes()).toHaveProperty('disabled');
61
+ expect(wrapper.text()).toContain('Saving changes');
62
+ wrapper.vm.handleUpdate();
63
+ wrapper.vm.handleBack();
64
+ wrapper.vm.handleValueChange({ EventName: 'Blocked Change' });
65
+ expect(wrapper.emitted('save:step')).toHaveLength(1);
66
+ expect(wrapper.vm.eventConfig.EventName).toBe('Changed Event');
67
+ });
68
+
69
+ it('retains page two edits on failure and saves the latest toggle even with a stale Next payload', async () => {
70
+ const wrapper = mountWizard({ editMode: true, step: 2 });
71
+ const oldConfig = { ...wrapper.vm.eventConfig };
72
+ wrapper.vm.handleValueChange({ IncludeInRatings: true, CountDoubles: true });
73
+ wrapper.vm.handleUpdate(oldConfig);
74
+ expect(wrapper.emitted('save:step')[0][0].event.IncludeInRatings).toBe(true);
75
+ expect(wrapper.emitted('save:step')[0][0].event.rules.find(rule => rule.RuleId === 20).RuleValue).toBe('true');
76
+ await wrapper.setProps({ saving: false, saveError: 'Changes could not be saved.' });
77
+ expect(wrapper.vm.currentStep).toBe(2);
78
+ expect(wrapper.vm.eventConfig.IncludeInRatings).toBe(true);
79
+ expect(wrapper.find('[role="alert"]').text()).toBe('Changes could not be saved.');
80
+ wrapper.vm.handleUpdate();
81
+ expect(wrapper.emitted('save:step')).toHaveLength(2);
82
+ });
83
+
84
+ it('uses Save on the last step and stays on it until the app chooses otherwise', () => {
85
+ const wrapper = mountWizard({ editMode: true, step: 5 });
86
+ expect(wrapper.findComponent({ name: 'DirectElimination' }).props('nextButtonLabel')).toBe('Save');
87
+ wrapper.vm.handleSubmit();
88
+ expect(wrapper.emitted('save:step')[0][0].step).toBe(5);
89
+ expect(wrapper.vm.currentStep).toBe(5);
90
+ });
91
+
92
+ it('tracks reverted changes, requests Back navigation and never mutates the incoming event', () => {
93
+ const event = createEvent();
94
+ const original = structuredClone(event);
95
+ const wrapper = mountWizard({ editMode: true, step: 3, event });
96
+ wrapper.vm.handleValueChange({ EventName: 'Changed' });
97
+ expect(wrapper.emitted('update:dirty').at(-1)).toEqual([true]);
98
+ wrapper.vm.handleValueChange({ EventName: event.EventName });
99
+ expect(wrapper.emitted('update:dirty').at(-1)).toEqual([false]);
100
+ wrapper.vm.handleBack();
101
+ expect(wrapper.emitted('navigate:step')[0]).toEqual([2]);
102
+ expect(wrapper.vm.currentStep).toBe(3);
103
+ expect(event).toEqual(original);
104
+ });
105
+
106
+ it('loads ratings membership from the event column and mirrors a legacy rule only if present', () => {
107
+ const wrapper = mountWizard({ editMode: true, step: 2 });
108
+ expect(wrapper.vm.eventConfig.IncludeInRatings).toBe(false);
109
+ expect(wrapper.vm.buildEventPayload().rules.some(rule => rule.RuleId === 22)).toBe(false);
110
+ const event = createEvent();
111
+ event.rules.push({ RuleId: 22, RuleValue: 'true' });
112
+ const legacy = mountWizard({ editMode: true, event });
113
+ expect(legacy.vm.buildEventPayload().rules.find(rule => rule.RuleId === 22).RuleValue).toBe('false');
114
+ });
115
+
116
+ it('omits disabled max-point rules instead of reenabling them after saving', () => {
117
+ const wrapper = mountWizard({ editMode: true });
118
+ wrapper.vm.handleValueChange({ poolMax: false, DEMax: false });
119
+ const rules = wrapper.vm.buildEventPayload().rules;
120
+ expect(rules.some(rule => rule.RuleId === 2 || rule.RuleId === 4)).toBe(false);
121
+ });
122
+ });
123
+
124
+ it('keeps creation as a wizard with one submit at the end', () => {
125
+ const wrapper = mountWizard({ event: { ...createEvent(), EventId: 0 } });
126
+ expect(wrapper.findComponent(BasicEventInfo).props('nextButtonLabel')).toBe('Next');
127
+ for (let step = 1; step < 5; step += 1) wrapper.vm.handleUpdate();
128
+ expect(wrapper.vm.currentStep).toBe(5);
129
+ expect(wrapper.emitted('save:step')).toBeUndefined();
130
+ expect(wrapper.emitted('update:submit')).toBeUndefined();
131
+ wrapper.vm.handleSubmit();
132
+ expect(wrapper.emitted('update:submit')).toHaveLength(1);
133
+ });
134
+
135
+ it('forwards controlled edit state and save, dirty, navigation and cancel events through EditEventInfo', () => {
136
+ const wrapper = shallowMount(EditEventInfo, {
137
+ props: { event: createEvent(), rules: [], weapons: [], editMode: true, step: 2, saving: false, saveError: 'Retry' }
138
+ });
139
+ wrappers.push(wrapper);
140
+ const wizard = wrapper.findComponent(EditEventWizard);
141
+ expect(wizard.props('editMode')).toBe(true);
142
+ expect(wizard.props('step')).toBe(2);
143
+ expect(wizard.props('saveError')).toBe('Retry');
144
+ const request = { event: createEvent(), step: 2 };
145
+ wizard.vm.$emit('save:step', request);
146
+ wizard.vm.$emit('navigate:step', 1);
147
+ wizard.vm.$emit('update:dirty', true);
148
+ wizard.vm.$emit('update:cancel');
149
+ expect(wrapper.emitted('save:step')[0][0]).toEqual(request);
150
+ expect(wrapper.emitted('navigate:step')[0]).toEqual([1]);
151
+ expect(wrapper.emitted('update:dirty')[0]).toEqual([true]);
152
+ expect(wrapper.emitted('update:cancel')).toHaveLength(1);
153
+ });
154
+
155
+ it('renders a saved boolean Double Counter value as checked on page two', () => {
156
+ const wrapper = shallowMount(EventCustomization, {
157
+ props: { eventConfig: { CountDoubles: true }, steps: [], currentStep: 1 }
158
+ });
159
+ wrappers.push(wrapper);
160
+ const toggle = wrapper
161
+ .findAllComponents({ name: 'Toggle' })
162
+ .find(control => control.props('label') === 'Double Counter Added & Will Be Tracked');
163
+ expect(toggle.props('checked')).toBe(true);
164
+ });
165
+
166
+ it('renders page two controls and emits a save from the actual Save and Continue button', async () => {
167
+ const wrapper = mount(EditEventWizard, { props: { event: createEvent(), weapons: [], editMode: true, step: 2 } });
168
+ wrappers.push(wrapper);
169
+ await wrapper.vm.$nextTick();
170
+ expect(wrapper.emitted('update:dirty').at(-1)).toEqual([false]);
171
+ await wrapper.findAll('input[type="checkbox"]')[0].setValue(true);
172
+ const save = wrapper.findAll('button').find(button => button.text() === 'Save and Continue');
173
+ await save.trigger('click');
174
+ expect(wrapper.emitted('save:step')[0][0].event.IncludeInRatings).toBe(true);
175
+ expect(wrapper.vm.currentStep).toBe(2);
176
+ expect(wrapper.emitted('update:dirty').at(-1)).toEqual([true]);
177
+ });
178
+
179
+ it('reconciles authoritative saved data in final-step controls without leaving the editor', async () => {
180
+ const event = createEvent();
181
+ event.rules.push({ RuleId: 3, RuleValue: '180' });
182
+ const wrapper = mount(EditEventWizard, { props: { event, weapons: [], editMode: true, step: 5 } });
183
+ wrappers.push(wrapper);
184
+ await wrapper.vm.$nextTick();
185
+ expect(wrapper.findAll('input[type="checkbox"]')[0].element.checked).toBe(true);
186
+ await wrapper.setProps({ event: { ...event, rules: [] }, saveNotice: 'Changes saved.' });
187
+ expect(wrapper.vm.currentStep).toBe(5);
188
+ expect(wrapper.findAll('input[type="checkbox"]')[0].element.checked).toBe(false);
189
+ expect(wrapper.text()).toContain('Changes saved.');
190
+ expect(wrapper.findAll('button').some(button => button.text() === 'Save')).toBe(true);
191
+ });