@dcrackel/hematournamentui 1.0.711 → 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.
Files changed (109) hide show
  1. package/.prettierignore +7 -7
  2. package/.storybook/main.js +3 -1
  3. package/README.md +182 -148
  4. package/dist/HemaTournamentUI-lib.es.js +4150 -4039
  5. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  6. package/dist/hematournamentui-tailwind.css +11 -0
  7. package/package.json +96 -96
  8. package/prettier.config.js +16 -16
  9. package/src/main.js +1 -1
  10. package/src/mocks/getDEBouts-TableOf128.js +37 -37
  11. package/src/mocks/getDEBouts-TableOf64.js +35 -35
  12. package/src/stories/Atoms/Input/BaseInput.test.js +97 -97
  13. package/src/stories/Atoms/Input/BaseInput.vue +149 -149
  14. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -23
  15. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -35
  16. package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -0
  17. package/src/stories/Molecules/Menus/HoverActions/HoverActions.test.js +138 -0
  18. package/src/stories/Molecules/Menus/HoverActions/HoverActions.vue +212 -0
  19. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +6 -0
  20. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -18
  21. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +1 -1
  22. package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +5 -1
  23. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +19 -10
  24. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +11 -7
  25. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -33
  26. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -45
  27. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -103
  28. package/src/stories/Organisms/Cards/Director/DirectorCard.test.js +82 -0
  29. package/src/stories/Organisms/Cards/Director/DirectorCard.vue +32 -51
  30. package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -51
  31. package/src/stories/Organisms/Cards/EventCard/EventCard.stories.js +41 -1
  32. package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -0
  33. package/src/stories/Organisms/Cards/EventCard/EventCard.vue +62 -30
  34. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -298
  35. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +77 -60
  36. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +144 -143
  37. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +289 -256
  38. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -69
  39. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -80
  40. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +153 -153
  41. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +4 -0
  42. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -31
  43. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -109
  44. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -192
  45. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +1 -0
  46. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +247 -204
  47. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +89 -15
  48. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -29
  49. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -46
  50. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -21
  51. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -39
  52. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -145
  53. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -64
  54. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +36 -57
  55. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +101 -65
  56. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +14 -1
  57. package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.vue +12 -5
  58. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +52 -48
  59. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +256 -227
  60. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -226
  61. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
  62. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +137 -133
  63. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.test.js +221 -191
  64. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue +372 -366
  65. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -172
  66. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -216
  67. package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -79
  68. package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -0
  69. package/src/stories/Organisms/Wizards/EditEventWizard/WeaponSelection.test.js +138 -0
  70. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +123 -12
  71. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +178 -166
  72. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +35 -7
  73. package/src/stories/Templates/EventManagement/Bracket/BracketDirectorAssignment.test.js +242 -0
  74. package/src/stories/Templates/EventManagement/Bracket/BracketMobile.test.js +131 -0
  75. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +42 -39
  76. package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +80 -0
  77. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.js +93 -0
  78. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.test.js +152 -0
  79. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -39
  80. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -79
  81. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -93
  82. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -91
  83. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +31 -1
  84. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -121
  85. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +9 -2
  86. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +5 -4
  87. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +81 -81
  88. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -39
  89. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +101 -101
  90. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -71
  91. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +86 -86
  92. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -86
  93. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +247 -247
  94. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -170
  95. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -156
  96. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -329
  97. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -329
  98. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -42
  99. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.stories.js +58 -45
  100. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -84
  101. package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +114 -114
  102. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -363
  103. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -116
  104. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +307 -307
  105. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -45
  106. package/src/stories/Util/Validations/PriceValidation.js +6 -6
  107. package/src/stories/Util/boutDirectorAssignment.js +46 -0
  108. package/src/stories/Util/registrationStatus.js +11 -0
  109. package/src/stories/Util/tabs.js +19 -47
@@ -0,0 +1,106 @@
1
+ import { afterEach, expect, it } from 'vitest';
2
+ import { shallowMount } from '@vue/test-utils';
3
+ import EventCard from './EventCard.vue';
4
+
5
+ const wrappers = [];
6
+ afterEach(() => wrappers.splice(0).forEach(wrapper => wrapper.unmount()));
7
+ const nativeFunction = 'function link() { [native code] }';
8
+
9
+ function rule(RuleName, RuleValue) {
10
+ return { Rules: { RuleName }, RuleValue };
11
+ }
12
+
13
+ function mountCard(overrides = {}) {
14
+ const wrapper = shallowMount(EventCard, {
15
+ props: {
16
+ event: {
17
+ EventName: 'Example Event',
18
+ Date: '2026-10-17',
19
+ StartTime: '12:00',
20
+ Present: '8',
21
+ Absent: '0',
22
+ EventRules: [],
23
+ ...overrides
24
+ }
25
+ },
26
+ global: { stubs: { BaseText: { props: ['text'], template: '<span>{{ text }}</span>' } } }
27
+ });
28
+ wrappers.push(wrapper);
29
+ return wrapper;
30
+ }
31
+
32
+ it.each([
33
+ [32, '32'],
34
+ ['32', '32'],
35
+ [0, 'Unlimited'],
36
+ ['0', 'Unlimited'],
37
+ [null, 'Unlimited'],
38
+ ['', 'Unlimited'],
39
+ [nativeFunction, 'Not set']
40
+ ])('renders the fencer limit %j as %s', (value, expected) => {
41
+ const wrapper = mountCard({ EventRules: [rule('CutOffNumber', value)] });
42
+ expect(wrapper.vm.getMaxFencers).toBe(expected);
43
+ expect(wrapper.text()).not.toContain('[native code]');
44
+ });
45
+
46
+ it('handles missing rules without crashing or inventing an elimination format', () => {
47
+ const wrapper = mountCard({ EventRules: undefined });
48
+ expect(wrapper.vm.getMaxFencers).toBe('Unlimited');
49
+ expect(wrapper.vm.eventSummary).toBe('Event settings not available');
50
+ expect(wrapper.vm.getCombinedNumberOfFencers).toBe('8 / Unlimited');
51
+ });
52
+
53
+ it('keeps the phase, time, point and fencer summaries for nested and flat API rules', () => {
54
+ const EventRules = [
55
+ rule('PoolTime', '90'),
56
+ rule('DETime', '180'),
57
+ rule('PoolMaxPoints', '7'),
58
+ rule('DEMaxPoints', '12'),
59
+ rule('DEType', 'single'),
60
+ rule('DE3rdFenceOff', true),
61
+ rule('CutOffNumber', '32')
62
+ ];
63
+ for (const event of [
64
+ { EventRules },
65
+ {
66
+ EventRules: undefined,
67
+ rules: EventRules.map(item => ({ RuleName: item.Rules.RuleName, RuleValue: item.RuleValue }))
68
+ }
69
+ ]) {
70
+ const wrapper = mountCard(event);
71
+ expect(wrapper.vm.eventSummary).toBe('Pools -> Single Elim -> 3rd Place Fence off');
72
+ expect(wrapper.vm.getMaxPoolPoints).toBe('7');
73
+ expect(wrapper.vm.getMaxDEPoints).toBe('12');
74
+ expect(wrapper.vm.getCombinedNumberOfFencers).toBe('8 / 32');
75
+ expect(wrapper.text().match(/1:30/g)).toHaveLength(2);
76
+ expect(wrapper.text().match(/3:00/g)).toHaveLength(2);
77
+ }
78
+ });
79
+
80
+ it('renders pass-based pools and double elimination correctly', () => {
81
+ const wrapper = mountCard({
82
+ EventRules: [
83
+ rule('PoolNumOfPasses', '5'),
84
+ rule('DENumOfPasses', '7'),
85
+ rule('DEType', 'double'),
86
+ rule('DE3rdFenceOff', 'false')
87
+ ]
88
+ });
89
+ expect(wrapper.vm.eventSummary).toBe('Pools -> Double Elim -> Tie for 3rd Place');
90
+ expect(wrapper.vm.getPoolBoutTimeOrPasses).toBe('5');
91
+ expect(wrapper.vm.getDEBoutTimeOrPasses).toBe('7');
92
+ });
93
+
94
+ it('marks corrupted stored values as unset instead of displaying function text or guessing a format', () => {
95
+ const wrapper = mountCard({
96
+ EventRules: ['CutOffNumber', 'PoolTime', 'PoolMaxPoints', 'DETime', 'DEMaxPoints', 'DEType'].map(name =>
97
+ rule(name, nativeFunction)
98
+ )
99
+ });
100
+ expect(wrapper.text()).not.toContain('[native code]');
101
+ expect(wrapper.text()).not.toContain('NaN');
102
+ expect(wrapper.vm.eventSummary).toContain('DE format not set');
103
+ expect(wrapper.vm.getMaxFencers).toBe('Not set');
104
+ expect(wrapper.vm.getPoolBoutTimeOrPasses).toBe('Not set');
105
+ expect(wrapper.vm.getMaxDEPoints).toBe('Not set');
106
+ });
@@ -6,7 +6,7 @@
6
6
  <div class="w-full flex flex-col md:flex-row justify-between">
7
7
  <section class="flex flex-row px-1 justify-start w-full">
8
8
  <div class="flex flex-col w-full">
9
- <div class="hidden md:flex mb-1">
9
+ <div class="hidden md:flex flex-wrap items-center gap-y-1 mb-1">
10
10
  <div class="flex">
11
11
  <BaseIcon icon-name="fa-calendar" size="xs" color="quaternary" class="mr-2" />
12
12
  <BaseText :text="event.Date" size="xs" color="quinary" class="text-left" />
@@ -19,6 +19,7 @@
19
19
  <BaseIcon icon-name="fa-users" size="xs" color="quaternary" class="mx-2" />
20
20
  <BaseText :text="getMaxFencers" size="xs" color="quinary" class="text-right" />
21
21
  </div>
22
+ <RegistrationStatus :status="event.RegistrationStatus ?? event.Status" class="ml-6" />
22
23
  </div>
23
24
 
24
25
  <BaseText
@@ -31,12 +32,17 @@
31
32
  <BaseText :text="eventSummary" size="xs" color="quaternary" class="text-left" />
32
33
 
33
34
  <div class="flex flex-col md:hidden pt-2">
34
- <div class="flex">
35
- <BaseIcon icon-name="fa-calendar" size="xs" color="quaternary" class="mr-2" />
36
- <BaseText :text="event.Date" size="xs" color="quinary" class="text-left" />
37
- <BaseText :text="formatStartTime(event.StartTime)" size="xs" color="quinary" class="text-left ml-1" />
38
- <BaseIcon icon-name="fa-users" size="xs" color="quaternary" class="mr-2 ml-4" />
39
- <BaseText :text="getCombinedNumberOfFencers" size="xs" color="quinary" class="text-right" />
35
+ <div class="flex flex-wrap items-center gap-x-4 gap-y-1">
36
+ <div class="flex items-center whitespace-nowrap">
37
+ <BaseIcon icon-name="fa-calendar" size="xs" color="quaternary" class="mr-2" />
38
+ <BaseText :text="event.Date" size="xs" color="quinary" class="text-left" />
39
+ <BaseText :text="formatStartTime(event.StartTime)" size="xs" color="quinary" class="text-left ml-1" />
40
+ </div>
41
+ <div class="flex items-center whitespace-nowrap">
42
+ <BaseIcon icon-name="fa-users" size="xs" color="quaternary" class="mr-2" />
43
+ <BaseText :text="getCombinedNumberOfFencers" size="xs" color="quinary" class="text-right" />
44
+ </div>
45
+ <RegistrationStatus :status="event.RegistrationStatus ?? event.Status" />
40
46
  </div>
41
47
 
42
48
  <div class="flex flex-row mt-3 w-full">
@@ -49,7 +55,7 @@
49
55
  </div>
50
56
  <div class="flex w-full">
51
57
  <BaseIcon icon-name="fa-clock-desk" size="xs" color="quaternary" class="mr-2" />
52
- <BaseText :text="getDEBoutTimeOrPasses" size="xs" color="quinary" class="text-right" />
58
+ <BaseText :text="getPoolBoutTimeOrPasses" size="xs" color="quinary" class="text-right" />
53
59
  </div>
54
60
  </div>
55
61
  </div>
@@ -151,10 +157,11 @@ import BaseButton from '../../../Molecules/Buttons/BaseButton/BaseButton.vue';
151
157
  import EventStatusBox from '../../../Molecules/Boxes/EventStatusBox/EventStatusBox.vue';
152
158
  import BaseIcon from '../../../Atoms/Icon/BaseIcon.vue';
153
159
  import BaseModal from '../../../Molecules/Modals/BaseModal/BaseModal.vue';
160
+ import RegistrationStatus from '../../../Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue';
154
161
 
155
162
  export default {
156
163
  name: 'EventCard',
157
- components: { BaseModal, BaseIcon, EventStatusBox, BaseButton, BaseText },
164
+ components: { RegistrationStatus, BaseModal, BaseIcon, EventStatusBox, BaseButton, BaseText },
158
165
  emits: ['manage', 'edit', 'delete'],
159
166
  props: {
160
167
  event: {
@@ -168,54 +175,72 @@ export default {
168
175
  };
169
176
  },
170
177
  computed: {
178
+ eventRules() {
179
+ const rules = this.event.EventRules ?? this.event.rules;
180
+ return Array.isArray(rules) ? rules.filter(rule => rule && typeof rule === 'object') : [];
181
+ },
171
182
  eventSummary() {
172
- const rules = this.event.EventRules;
183
+ const rules = this.eventRules;
184
+ if (rules.length === 0) return 'Event settings not available';
173
185
 
174
- const poolRule = rules.find(rule => rule.Rules.RuleName === 'PoolTime');
175
- const deType = rules.find(rule => rule.Rules.RuleName === 'DEType');
176
- const deThirdFenceOff = rules.find(rule => rule.Rules.RuleName === 'DE3rdFenceOff');
186
+ const poolRule = rules.find(rule =>
187
+ ['PoolTime', 'PoolNumOfPasses', 'PoolMaxPoints', 'PoolSeeding'].includes(rule.Rules?.RuleName ?? rule.RuleName)
188
+ );
189
+ const deType = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'DEType');
190
+ const deThirdFenceOff = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'DE3rdFenceOff');
177
191
 
178
192
  const poolSummary = poolRule ? 'Pools' : '';
179
- const deSummary = deType?.RuleValue === 'single' ? 'Single Elim' : 'Double Elim';
193
+ const deSummary =
194
+ new Map([
195
+ ['single', 'Single Elim'],
196
+ ['double', 'Double Elim']
197
+ ]).get(deType?.RuleValue) ?? 'DE format not set';
180
198
  const thirdPlaceSummary =
181
- deThirdFenceOff && deThirdFenceOff.RuleValue === 'true' ? '3rd Place Fence off' : 'Tie for 3rd Place';
199
+ new Map([
200
+ ['true', '3rd Place Fence off'],
201
+ ['false', 'Tie for 3rd Place']
202
+ ]).get(String(deThirdFenceOff?.RuleValue)) ?? '';
182
203
 
183
204
  return [poolSummary, deSummary, thirdPlaceSummary].filter(Boolean).join(' -> ');
184
205
  },
185
206
  getMaxFencers() {
186
- const maxRule = this.event.EventRules.find(rule => rule.Rules.RuleName === 'CutOffNumber');
187
- return maxRule.RuleValue === 0 ? 'Unlimited' : maxRule.RuleValue;
207
+ const maxRule = this.eventRules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'CutOffNumber');
208
+ const value = maxRule?.RuleValue;
209
+ if (value == null || value === '') return 'Unlimited';
210
+ const limit = Number(value);
211
+ if (!Number.isSafeInteger(limit) || limit < 0) return 'Not set';
212
+ return limit === 0 ? 'Unlimited' : String(limit);
188
213
  },
189
214
  getCombinedNumberOfFencers() {
190
- const combined = this.event.Present + this.event.Absent;
215
+ const combined = Number(this.event.Present ?? 0) + Number(this.event.Absent ?? 0);
191
216
  return combined + ' / ' + this.getMaxFencers;
192
217
  },
193
218
  getMaxPoolPoints() {
194
- const maxRule = this.event.EventRules.find(rule => rule.Rules.RuleName === 'PoolMaxPoints');
195
- return maxRule ? maxRule.RuleValue : '';
219
+ const maxRule = this.eventRules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'PoolMaxPoints');
220
+ return maxRule ? this.formatRuleNumber(maxRule.RuleValue) : '';
196
221
  },
197
222
  getMaxDEPoints() {
198
- const maxRule = this.event.EventRules.find(rule => rule.Rules.RuleName === 'DEMaxPoints');
199
- return maxRule ? maxRule.RuleValue : '';
223
+ const maxRule = this.eventRules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'DEMaxPoints');
224
+ return maxRule ? this.formatRuleNumber(maxRule.RuleValue) : '';
200
225
  },
201
226
  getPoolBoutTimeOrPasses() {
202
- const rules = this.event.EventRules;
227
+ const rules = this.eventRules;
203
228
 
204
- const poolTimeRule = rules.find(rule => rule.Rules.RuleName === 'PoolTime');
205
- const passesRule = rules.find(rule => rule.Rules.RuleName === 'PoolNumOfPasses');
229
+ const poolTimeRule = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'PoolTime');
230
+ const passesRule = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'PoolNumOfPasses');
206
231
 
207
232
  if (poolTimeRule) return this.formatTime(poolTimeRule.RuleValue);
208
- if (passesRule) return passesRule.RuleValue;
233
+ if (passesRule) return this.formatRuleNumber(passesRule.RuleValue);
209
234
  return '';
210
235
  },
211
236
  getDEBoutTimeOrPasses() {
212
- const rules = this.event.EventRules;
237
+ const rules = this.eventRules;
213
238
 
214
- const DETimeRule = rules.find(rule => rule.Rules.RuleName === 'DETime');
215
- const passesRule = rules.find(rule => rule.Rules.RuleName === 'DENumOfPasses');
239
+ const DETimeRule = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'DETime');
240
+ const passesRule = rules.find(rule => (rule.Rules?.RuleName ?? rule.RuleName) === 'DENumOfPasses');
216
241
 
217
242
  if (DETimeRule) return this.formatTime(DETimeRule.RuleValue);
218
- if (passesRule) return passesRule.RuleValue;
243
+ if (passesRule) return this.formatRuleNumber(passesRule.RuleValue);
219
244
  return '';
220
245
  }
221
246
  },
@@ -234,7 +259,14 @@ export default {
234
259
  handleSubmit() {
235
260
  this.$emit('delete', this.event);
236
261
  },
262
+ formatRuleNumber(value) {
263
+ if (value == null || value === '') return '';
264
+ const number = Number(value);
265
+ return Number.isFinite(number) && number >= 0 ? String(number) : 'Not set';
266
+ },
237
267
  formatTime(seconds) {
268
+ if (seconds == null || seconds === '' || !Number.isFinite(Number(seconds)) || Number(seconds) < 0)
269
+ return 'Not set';
238
270
  const minutes = Math.floor(seconds / 60);
239
271
  const remainingSeconds = seconds % 60;
240
272
  return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;