@dcrackel/hematournamentui 1.0.699 → 1.0.702

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 (72) hide show
  1. package/AGENTS.md +3 -0
  2. package/dist/HemaTournamentUI-lib.es.js +10913 -10269
  3. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  4. package/dist/hematournamentui-tailwind.css +23 -0
  5. package/dist/hematournamentui.css +1 -1
  6. package/package.json +8 -8
  7. package/src/stories/Molecules/Boxes/BoutBoxes/TimerBox/TimerBox.vue +1 -1
  8. package/src/stories/Molecules/Buttons/BaseButton/BaseButton.vue +1 -1
  9. package/src/stories/Molecules/Buttons/ButtonBar/ButtonBar.vue +1 -1
  10. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +2 -2
  11. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +25 -22
  12. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +31 -29
  13. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardWaiting/BoutCardWaiting.vue +2 -4
  14. package/src/stories/Organisms/Cards/KioskCard/KioskCard.vue +54 -35
  15. package/src/stories/Organisms/Cards/PenaltyCard/PenaltyCard.stories.js +6 -6
  16. package/src/stories/Organisms/Cards/PenaltyCard/PenaltyCard.vue +64 -53
  17. package/src/stories/Organisms/Cards/PenaltyFencerCard/PenaltyFencerCard.stories.js +6 -7
  18. package/src/stories/Organisms/Cards/PenaltyFencerCard/PenaltyFencerCard.vue +18 -10
  19. package/src/stories/Organisms/Cards/StaffCard/StaffCard.vue +4 -4
  20. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +129 -0
  21. package/src/stories/Organisms/ComplexInputs/DatePicker/DatePicker.vue +64 -45
  22. package/src/stories/Organisms/Containers/KioskContainer/KioskContainer.vue +13 -9
  23. package/src/stories/Organisms/Containers/Pool/Pool.vue +1 -1
  24. package/src/stories/Organisms/Containers/PoolResults/PoolResultsTable.vue +35 -20
  25. package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.vue +45 -19
  26. package/src/stories/Templates/Assignment/Assignment.stories.js +17 -15
  27. package/src/stories/Templates/Assignment/Assignment.vue +71 -40
  28. package/src/stories/Templates/ClubManagement/ClubBasic/ClubBasic.stories.js +49 -30
  29. package/src/stories/Templates/ClubManagement/ClubBasic/ClubBasic.vue +29 -28
  30. package/src/stories/Templates/ClubManagement/{ClubMangement.stories.js → ClubManagement.stories.js} +74 -59
  31. package/src/stories/Templates/ClubManagement/ClubManagement.vue +104 -92
  32. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +92 -97
  33. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +37 -33
  34. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +11 -14
  35. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +49 -14
  36. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.vue +156 -62
  37. package/src/stories/Templates/EventManagement/EventFinalResults/EventFinalResults.stories.js +11 -10
  38. package/src/stories/Templates/EventManagement/EventFinalResults/EventFinalResults.vue +8 -8
  39. package/src/stories/Templates/EventManagement/KioskDisplay/KioskDisplay.stories.js +13 -16
  40. package/src/stories/Templates/EventManagement/KioskDisplay/KioskDisplay.vue +25 -25
  41. package/src/stories/Templates/EventManagement/Penalties/PenaltyReport.stories.js +25 -24
  42. package/src/stories/Templates/EventManagement/Penalties/PenaltyReport.vue +28 -34
  43. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.stories.js +54 -19
  44. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.vue +75 -68
  45. package/src/stories/Templates/EventManagement/PoolManagement/PoolManagement.stories.js +55 -45
  46. package/src/stories/Templates/EventManagement/PoolManagement/PoolManagement.vue +47 -55
  47. package/src/stories/Templates/EventManagement/PoolResults/PoolResults.stories.js +56 -55
  48. package/src/stories/Templates/EventManagement/PoolResults/PoolResults.vue +71 -73
  49. package/src/stories/Templates/EventManagement/StaffList/StaffList.stories.js +18 -16
  50. package/src/stories/Templates/EventManagement/StaffList/StaffList.vue +9 -6
  51. package/src/stories/Templates/Menus/Admin/AdminLeftMenu.stories.js +22 -19
  52. package/src/stories/Templates/Menus/Admin/AdminLeftMenu.vue +44 -36
  53. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +30 -23
  54. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +55 -58
  55. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +67 -0
  56. package/src/stories/Templates/PersonManagement/Admin/{Administration.vue → Administration/Administration.vue} +94 -87
  57. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -0
  58. package/src/stories/Templates/PersonManagement/Admin/{FencerAdmin.vue → FencerAdmin/FencerAdmin.vue} +90 -77
  59. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -0
  60. package/src/stories/Templates/PersonManagement/Admin/{MergeAccounts.vue → MergeAccounts/MergeAccounts.vue} +258 -242
  61. package/src/stories/Templates/PersonManagement/Attendance/Attendance.vue +28 -28
  62. package/src/stories/Templates/PersonManagement/PersonManagement.vue +1 -1
  63. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.stories.js +45 -28
  64. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.vue +22 -16
  65. package/src/stories/Templates/TournamentManagement/{TournamentMangement.stories.js → TournamentManagement.stories.js} +68 -39
  66. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +199 -153
  67. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +113 -0
  68. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +123 -106
  69. package/src/stories/Templates/TournamentManagement/TournamentPageTwo/TournamentPageTwo.stories.js +76 -31
  70. package/src/stories/Templates/TournamentManagement/TournamentPageTwo/{TouranmentPageTwo.vue → TournamentPageTwo.vue} +63 -44
  71. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin.stories.js +0 -48
  72. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TouranmentPageOne.stories.js +0 -63
@@ -160,17 +160,26 @@ export default {
160
160
  const portrait = images.find(image => image.URL);
161
161
  return portrait ? portrait.URL : '';
162
162
  },
163
- displayName() {
164
- return this.fencer1 ? this.bout.Person1.DisplayName : this.bout.Person2.DisplayName
165
- },
166
- clubName() {
167
- return this.fencer1 ? this.bout.Person1.Club.Name : this.bout.Person2.Club.Name
168
- },
169
- pastCards() {
170
- return this.fencer1 ? this.bout.Person1.Penalties : this.bout.Person2.Penalties
171
- },
172
- toggled() {
173
- return this.yellowCardToggle || this.redCardToggle || this.noteToggle || this.blackCardToggle || this.medicalToggle;
163
+ displayName() {
164
+ return this.selectedPerson?.DisplayName || '';
165
+ },
166
+ clubName() {
167
+ return this.selectedPerson?.Club?.Name || '';
168
+ },
169
+ pastCards() {
170
+ return this.selectedPersonPenalties;
171
+ },
172
+ selectedPerson() {
173
+ return this.fencer1 ? this.bout.Person1 : this.bout.Person2;
174
+ },
175
+ selectedPersonId() {
176
+ return this.fencer1 ? this.bout.Person1Id : this.bout.Person2Id;
177
+ },
178
+ selectedPersonPenalties() {
179
+ return Array.isArray(this.selectedPerson?.Penalties) ? this.selectedPerson.Penalties : [];
180
+ },
181
+ toggled() {
182
+ return this.yellowCardToggle || this.redCardToggle || this.noteToggle || this.blackCardToggle || this.medicalToggle;
174
183
  },
175
184
  },
176
185
  methods: {
@@ -193,16 +202,13 @@ export default {
193
202
  clearPenalty() {
194
203
  this.toggleAllOff();
195
204
  this.currentNote = '';
196
- },
197
- countCards() {
198
- if (this.fencer1) {
199
- this.yellowCardCount = this.bout.Person1?.Penalties?.filter((t3) => t3.PenaltyType === "Yellow" && t3.BoutId === this.bout.BoutId).length || 0;
200
- this.redCardCount = this.bout.Person1?.Penalties?.filter((t3) => t3.PenaltyType === "Red" && t3.BoutId === this.bout.BoutId).length || 0;
201
- }
202
- if (!this.fencer1) {
203
- this.yellowCardCount = this.bout.Person2?.Penalties?.filter((t3) => t3.PenaltyType === "Yellow" && t3.BoutId === this.bout.BoutId).length || 0;
204
- this.redCardCount = this.bout.Person2?.Penalties?.filter((t3) => t3.PenaltyType === "Red" && t3.BoutId === this.bout.BoutId).length || 0;
205
- }
205
+ },
206
+ countCards() {
207
+ const boutPenalties = this.selectedPersonPenalties.filter(penalty => penalty.BoutId === this.bout.BoutId);
208
+ this.yellowCardCount = boutPenalties.filter(penalty => penalty.PenaltyType === "Yellow").length;
209
+ this.redCardCount = boutPenalties.filter(penalty => penalty.PenaltyType === "Red").length;
210
+ this.blackCardCount = boutPenalties.filter(penalty => penalty.PenaltyType === "Black").length;
211
+ this.medicalCount = boutPenalties.filter(penalty => penalty.PenaltyType === "Medical").length;
206
212
  },
207
213
  toggleAllOff() {
208
214
  if (this.yellowCardToggle) this.yellowCardCount--;
@@ -251,31 +257,21 @@ export default {
251
257
  this.noteToggle = !this.noteToggle;
252
258
  this.currentNote = "";
253
259
  },
254
- addNewPenalty() {
255
- const newPenalty = {
256
- BoutId: this.bout.BoutId,
257
- FencerId: this.fencer1 ? this.bout.Person1Id : this.bout.Person2Id,
258
- PenaltyId: Date.now(),
259
- PenaltyType: this.getToggled(),
260
- Notes: this.currentNote
261
- };
262
-
263
- if (this.fencer1) {
264
- if (!Array.isArray(this.bout.Person1.Penalties)) {
265
- this.bout.Person1.Penalties = [];
266
- this.bout.Person1.EventPersons[0].LastNote = this.currentNote;
267
- }
268
- this.bout.Person1.Penalties.push(newPenalty);
269
- } else {
270
- if (!Array.isArray(this.bout.Person2.Penalties)) {
271
- this.bout.Person2.Penalties = [];
272
- this.bout.Person2.EventPersons[0].LastNote = this.currentNote;
273
- }
274
- this.bout.Person2.Penalties.push(newPenalty);
275
- }
276
-
277
- this.countCards();
278
- this.$emit("penalty:add", newPenalty);
260
+ addNewPenalty() {
261
+ const newPenalty = {
262
+ BoutId: this.bout.BoutId,
263
+ PersonId: this.selectedPersonId,
264
+ PenaltyId: Date.now(),
265
+ PenaltyType: this.getToggled(),
266
+ Notes: this.currentNote
267
+ };
268
+
269
+ this.ensureSelectedPersonPenaltyState();
270
+ this.selectedPerson.Penalties.push(newPenalty);
271
+ this.updateSelectedPersonLastNote(this.currentNote);
272
+
273
+ this.countCards();
274
+ this.$emit("penalty:add", newPenalty);
279
275
  this.currentNote = "";
280
276
  },
281
277
  updatePenalty() {
@@ -283,11 +279,13 @@ export default {
283
279
  const existingPenaltyIndex = penalties.findIndex(p => p.PenaltyId === this.selectPenaltyId);
284
280
 
285
281
  if (existingPenaltyIndex !== -1) {
286
- penalties[existingPenaltyIndex].PenaltyType = this.getToggled();
287
- penalties[existingPenaltyIndex].Notes = this.currentNote;
288
-
289
- this.$emit('penalty:update', penalties[existingPenaltyIndex]);
290
- }
282
+ penalties[existingPenaltyIndex].PenaltyType = this.getToggled();
283
+ penalties[existingPenaltyIndex].Notes = this.currentNote;
284
+ penalties[existingPenaltyIndex].PersonId = this.selectedPersonId;
285
+ this.updateSelectedPersonLastNote(this.currentNote);
286
+
287
+ this.$emit('penalty:update', penalties[existingPenaltyIndex]);
288
+ }
291
289
 
292
290
  this.countCards();
293
291
  this.selectPenaltyId = null;
@@ -339,6 +337,19 @@ export default {
339
337
  getFlagColor(flag) {
340
338
  return flagColors[flag] || 'gray';
341
339
  },
340
+ ensureSelectedPersonPenaltyState() {
341
+ if (!Array.isArray(this.selectedPerson.Penalties)) {
342
+ this.selectedPerson.Penalties = [];
343
+ }
344
+
345
+ if (!Array.isArray(this.selectedPerson.EventPersons)) {
346
+ this.selectedPerson.EventPersons = [{ LastNote: '' }];
347
+ }
348
+ },
349
+ updateSelectedPersonLastNote(note) {
350
+ this.ensureSelectedPersonPenaltyState();
351
+ this.selectedPerson.EventPersons[0].LastNote = note;
352
+ },
342
353
  },
343
- };
344
- </script>
354
+ };
355
+ </script>
@@ -8,13 +8,12 @@ export default {
8
8
  args: {
9
9
  personPenalties: getPenaltiesByEvent.Penalties[0],
10
10
  eventName: "Lynx Cup 2026"
11
- },
12
- argTypes: {
13
- person: { control: 'object' },
14
- penalties: { control: 'array' },
15
- eventName: { control: 'text' }
16
- }
17
- };
11
+ },
12
+ argTypes: {
13
+ personPenalties: { control: 'object' },
14
+ eventName: { control: 'text' }
15
+ }
16
+ };
18
17
 
19
18
  export const Default = {
20
19
  args: {
@@ -27,7 +27,7 @@
27
27
  <BaseIcon icon-name="fa-square" size="lg" color="black"/>
28
28
  </div>
29
29
  <div v-if="medicalCount > 0" class="flex items-center ml-2">
30
- <BaseIcon icon-name="fa-plus-circle" size="lg" color="blue"/>
30
+ <BaseIcon icon-name="fa-truck-medical" size="lg" color="orange"/>
31
31
  </div>
32
32
  </div>
33
33
 
@@ -38,8 +38,8 @@
38
38
  </div>
39
39
 
40
40
  <transition name="fade-in-down">
41
- <div v-if="localShowDetails" class="border-t border-dropdownSelect md:p-3">
42
- <div v-for="penalty in personPenalties.Penalties" :key="penalty.BoutId" class="flex flex-col p-2 border-b my-2">
41
+ <div v-if="localShowDetails" class="border-t border-dropdownSelect md:p-3">
42
+ <div v-for="penalty in penalties" :key="penaltyKey(penalty)" class="flex flex-col p-2 border-b border-dropdownSelect my-2">
43
43
  <div class="flex flex-row justify-between">
44
44
  <div class="flex flex-row">
45
45
  <BaseIcon :icon-name="penaltyIcons[penalty.PenaltyType]" size="md" :color="penaltyColors[penalty.PenaltyType]"/>
@@ -77,7 +77,7 @@ import BaseButton from "../../../Molecules/Buttons/BaseButton/BaseButton.vue";
77
77
  export default {
78
78
  name: 'PenaltyFencerCard',
79
79
  components: {BaseButton, BaseIcon, BaseText },
80
- emits: ['handleConfirmRemoval','penalty:removal'],
80
+ emits: ['penalty:removal'],
81
81
  props: {
82
82
  personPenalties: {
83
83
  type: Object,
@@ -115,17 +115,20 @@ export default {
115
115
  const portrait = images.find(image => image.Type === 'Portrait');
116
116
  return portrait ? portrait.URL : '';
117
117
  },
118
+ penalties() {
119
+ return Array.isArray(this.personPenalties?.Penalties) ? this.personPenalties.Penalties : [];
120
+ },
118
121
  yellowCardCount() {
119
- return this.personPenalties.Penalties.filter(p => p.PenaltyType === 'Yellow').length;
122
+ return this.penalties.filter(p => p.PenaltyType === 'Yellow').length;
120
123
  },
121
124
  redCardCount() {
122
- return this.personPenalties.Penalties.filter(p => p.PenaltyType === 'Red').length;
125
+ return this.penalties.filter(p => p.PenaltyType === 'Red').length;
123
126
  },
124
127
  blackCardCount() {
125
- return this.personPenalties.Penalties.filter(p => p.PenaltyType === 'Black').length;
128
+ return this.penalties.filter(p => p.PenaltyType === 'Black').length;
126
129
  },
127
130
  medicalCount() {
128
- return this.personPenalties.Penalties.filter(p => p.PenaltyType === 'Medical').length;
131
+ return this.penalties.filter(p => p.PenaltyType === 'Medical').length;
129
132
  },
130
133
  },
131
134
  methods: {
@@ -136,8 +139,13 @@ export default {
136
139
  this.localShowDetails = !this.localShowDetails;
137
140
  },
138
141
  handleConfirmRemoval(penalty){
139
- penalty.PersonId = this.personPenalties.PersonId
140
- this.$emit('penalty:removal', penalty)
142
+ this.$emit('penalty:removal', {
143
+ ...penalty,
144
+ PersonId: this.personPenalties.PersonId
145
+ });
146
+ },
147
+ penaltyKey(penalty) {
148
+ return penalty.PenaltyId || `${penalty.BoutId}-${penalty.PenaltyType}-${penalty.Notes || ''}`;
141
149
  }
142
150
  }
143
151
  };
@@ -73,10 +73,10 @@ export default {
73
73
  }
74
74
  },
75
75
  methods: {
76
- handleAssignment(selected) {
77
- const staff = { ...this.staff, Status: selected };
78
- this.$emit('update:assignment', staff);
79
- },
76
+ handleAssignment(selected) {
77
+ const staff = { ...this.staff, Role: selected };
78
+ this.$emit('update:assignment', staff);
79
+ },
80
80
  handlerRemoveFencer() {
81
81
  this.$emit('remove:staff', this.staff);
82
82
  }
@@ -0,0 +1,129 @@
1
+ <template>
2
+ <div v-if="person"
3
+ class="flex items-center border border-dropdownSelect rounded-lg shadow-lg bg-poolBox hover:border-bright">
4
+ <div class="hidden md:block mr-4 border-r border-dropdownSelect h-20 w-10 items-center text-center pt-7">
5
+ <BaseText :text="queueLabel" size="md" color="quaternary" weight="bold"/>
6
+ </div>
7
+ <div class="block md:hidden mr-4 border-r border-dropdownSelect h-16 w-5 items-center text-center pt-5">
8
+ <BaseText :text="queueLabel" size="sm" color="quaternary" weight="bold"/>
9
+ </div>
10
+
11
+ <div class="hidden md:block mr-4">
12
+ <div v-if="!portraitURL" class="w-14 h-14 rounded-xl bg-dropdownSelect flex items-center justify-center">
13
+ <BaseText text="" size="sm" color="neutral" weight="bold"/>
14
+ </div>
15
+ <img v-if="portraitURL" :src="portraitURL" alt="Portrait" class="w-14 h-14 rounded-xl">
16
+ </div>
17
+
18
+ <div class="flex flex-col w-3/4 md:w-4/12 justify-start">
19
+ <button class="flex justify-start overflow-hidden text-left" type="button" @click="handleForwardToProfile">
20
+ <BaseText :text="displayName" size="md" color="quaternary" weight="bold" class="text-sm md:text-xl truncate"/>
21
+ </button>
22
+ <span class="w-full flex justify-start">
23
+ <BaseText :text="clubName" size="md" color="quinary" weight="normal" class="hidden md:block"/>
24
+ <BaseText :text="clubShortName" size="xs" color="quinary" weight="normal" class="block md:hidden"/>
25
+ </span>
26
+
27
+ <div class="grid md:hidden grid-cols-2 gap-x-4 mr-10">
28
+ <div class="flex">
29
+ <BaseText text="M2: " size="xs" color="quaternary" weight="normal" class="mr-1"/>
30
+ <BaseText :text="M2Rating" size="xs" color="quinary" weight="bold" class="ml-1"/>
31
+ </div>
32
+ <div class="flex">
33
+ <BaseText text="HR: " size="xs" color="quaternary" weight="normal" class="mr-1"/>
34
+ <BaseText :text="HEMARating" size="xs" color="quinary" weight="bold" class="ml-1"/>
35
+ </div>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="hidden md:grid grid-cols-1 gap-x-4 mr-4 w-36">
40
+ <div class="flex">
41
+ <BaseText text="M2Rating:" size="sm" color="quaternary" weight="normal" class="mr-2"/>
42
+ <BaseText :text="M2Rating" size="sm" color="quinary" weight="bold" class="pt-0.5"/>
43
+ </div>
44
+ <div class="flex">
45
+ <BaseText text="HemaRating:" size="sm" color="quaternary" weight="normal" class="mr-2"/>
46
+ <BaseText :text="HEMARating" size="sm" color="quinary" weight="bold"/>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="mr-2 md:mr-4 md:ml-auto flex flex-row">
51
+ <div class="flex justify-center w-20">
52
+ <div class="hidden md:flex border border-dropdownSelect shadow-md rounded-lg bg-natural h-9 px-3 items-center">
53
+ <BaseText text="Unpaid" size="xs" color="quinary" weight="bold"/>
54
+ </div>
55
+ </div>
56
+ <div class="w-44 flex justify-end">
57
+ <BaseButton label="Move to Active" size="xs" type="primary" icon-name="fa-arrow-up-from-bracket"
58
+ class="border border-dropdownSelect shadow-md rounded-l-lg bg-natural w-40 m-0! p-0! h-10"
59
+ @buttonClick="handleMoveToActive"/>
60
+ </div>
61
+ </div>
62
+
63
+ <div
64
+ class="block md:ml-4 border-l border-dropdownSelect h-16 w-9 md:h-20 md:w-10 items-center text-center hover:bg-eventBoxRed rounded-tr-lg rounded-br-lg pt-5 md:pt-6">
65
+ <BaseIcon icon-name="fa-circle-xmark" size="" color="quinary" hover="alarm"
66
+ @click="handleRemoveFencer" class="text-md md:text-2xl"/>
67
+ </div>
68
+ </div>
69
+ </template>
70
+
71
+ <script>
72
+ import BaseIcon from "../../../Atoms/Icon/BaseIcon.vue";
73
+ import BaseText from "../../../Atoms/Text/BaseText.vue";
74
+ import BaseButton from "../../../Molecules/Buttons/BaseButton/BaseButton.vue";
75
+
76
+ export default {
77
+ name: 'WaitlistFencerCard',
78
+ components: {BaseButton, BaseIcon, BaseText},
79
+ emits: ['forward:toProfile', 'move:active', 'remove:fencer'],
80
+ props: {
81
+ person: {
82
+ type: Object,
83
+ required: true
84
+ },
85
+ queuePosition: {
86
+ type: Number,
87
+ required: true
88
+ }
89
+ },
90
+ computed: {
91
+ queueLabel() {
92
+ return `#${this.queuePosition}`;
93
+ },
94
+ displayName() {
95
+ return this.person?.Person?.DisplayName || 'Unknown fencer';
96
+ },
97
+ clubName() {
98
+ return this.person?.Person?.Club?.Name || 'No club listed';
99
+ },
100
+ clubShortName() {
101
+ return this.person?.Person?.Club?.ShortName || this.clubName;
102
+ },
103
+ portraitURL() {
104
+ const images = this.person?.Person?.Images || [];
105
+ const portrait = images.find(image => image.Type === 'Portrait');
106
+ return portrait ? portrait.URL : '';
107
+ },
108
+ HEMARating() {
109
+ const rating = this.person?.Person?.HEMARatings?.[0]?.Rating;
110
+ return rating || 0;
111
+ },
112
+ M2Rating() {
113
+ const rating = this.person?.Person?.M2Ratings?.[0]?.Rating;
114
+ return rating || 'U';
115
+ }
116
+ },
117
+ methods: {
118
+ handleMoveToActive() {
119
+ this.$emit('move:active', {...this.person});
120
+ },
121
+ handleRemoveFencer() {
122
+ this.$emit('remove:fencer', {...this.person});
123
+ },
124
+ handleForwardToProfile() {
125
+ this.$emit('forward:toProfile', this.person.PersonId);
126
+ }
127
+ }
128
+ };
129
+ </script>
@@ -1,14 +1,14 @@
1
1
  <template>
2
2
  <div>
3
- <VueDatePicker
4
- v-model="selectedDate"
5
- :format="dateFormat"
6
- :min-date="minDate"
7
- :mode="pickerMode"
8
- placeholder="Select Date or Time"
9
- :class="{'dp__input_invalid': invalid}"
10
- v-bind="datePickerAttrs"
11
- />
3
+ <VueDatePicker
4
+ v-model="selectedDate"
5
+ :formats="datePickerFormats"
6
+ :min-date="minDate"
7
+ :enable-time-picker="usesTimePicker"
8
+ :time-picker="pickerMode === 'time'"
9
+ placeholder="Select Date or Time"
10
+ :class="{'dp__input_invalid': invalid}"
11
+ />
12
12
  </div>
13
13
  </template>
14
14
 
@@ -50,43 +50,62 @@ export default {
50
50
  ? new Date('2018-01-01')
51
51
  : this.getThreeMonthsAgo();
52
52
  },
53
- datePickerAttrs() {
54
- return this.pickerMode === 'time' ? { 'time-picker': true } : {};
55
- },
56
- dateFormat() {
57
- if (this.pickerMode === 'time') return 'HH:mm';
58
- if (this.pickerMode === 'datetime') return 'MM/dd/yyyy HH:mm';
59
- return 'MM/dd/yyyy';
60
- }
61
- },
53
+ usesTimePicker() {
54
+ return this.pickerMode !== 'date';
55
+ },
56
+ datePickerFormats() {
57
+ const formatsByMode = {
58
+ date: { input: 'MM/dd/yyyy' },
59
+ datetime: { input: 'MM/dd/yyyy HH:mm' },
60
+ time: { input: 'HH:mm' },
61
+ };
62
+
63
+ return formatsByMode[this.pickerMode];
64
+ },
65
+ },
62
66
  watch: {
63
- selectedDate(newDate) {
64
- if (this.pickerMode === 'time' && newDate && typeof newDate === 'object') {
65
- const hours = String(newDate.hours).padStart(2, '0');
66
- const minutes = String(newDate.minutes).padStart(2, '0');
67
- this.$emit("update:selectedDate", `${hours}:${minutes}`);
68
- } else {
69
- this.$emit("update:selectedDate", newDate);
70
- }
71
- }
72
- },
73
- methods: {
74
- getThreeMonthsAgo() {
75
- return new Date(new Date().setMonth(new Date().getMonth() - 3));
76
- },
77
- parseDate(date) {
78
- if (typeof date === 'string') {
79
- if (this.pickerMode === 'time') {
80
- const [hours, minutes] = date.split(':').map(Number);
81
- return { hours, minutes };
82
- }
83
- const [year, month, day] = date.split('-').map(Number);
84
- return new Date(year, month - 1, day);
85
- }
86
- return date;
87
- }
88
- }
89
- }
67
+ selectedDate(newDate) {
68
+ if (this.pickerMode === 'time' && newDate && typeof newDate === 'object') {
69
+ const hours = String(newDate.hours).padStart(2, '0');
70
+ const minutes = String(newDate.minutes).padStart(2, '0');
71
+ this.$emit("update:selectedDate", `${hours}:${minutes}`);
72
+ return;
73
+ }
74
+
75
+ if (this.pickerMode === 'date') {
76
+ this.$emit("update:selectedDate", this.formatDateOnly(newDate));
77
+ return;
78
+ }
79
+
80
+ this.$emit("update:selectedDate", newDate);
81
+ }
82
+ },
83
+ methods: {
84
+ getThreeMonthsAgo() {
85
+ return new Date(new Date().setMonth(new Date().getMonth() - 3));
86
+ },
87
+ parseDate(date) {
88
+ if (typeof date === 'string') {
89
+ if (this.pickerMode === 'time') {
90
+ const [hours, minutes] = date.split(':').map(Number);
91
+ return { hours, minutes };
92
+ }
93
+ const [year, month, day] = date.slice(0, 10).split('-').map(Number);
94
+ return new Date(year, month - 1, day);
95
+ }
96
+ return date;
97
+ },
98
+ formatDateOnly(date) {
99
+ if (!date) return '';
100
+ if (typeof date === 'string') return date.slice(0, 10);
101
+
102
+ const year = date.getFullYear();
103
+ const month = String(date.getMonth() + 1).padStart(2, '0');
104
+ const day = String(date.getDate()).padStart(2, '0');
105
+ return `${year}-${month}-${day}`;
106
+ }
107
+ }
108
+ }
90
109
  </script>
91
110
 
92
111
  <style scoped>
@@ -13,9 +13,9 @@
13
13
  </div>
14
14
  </section>
15
15
  <section v-if="localDisplays.length > 0" class="flex flex-col">
16
- <div v-if="scoreboards.length > 0">
17
- <BaseText text="Scoreboards" size="lg" color="quinary" weight="bold" class="w-full text-left"/>
18
- <div class="border-b border-dropdownSelect mb-4"/>
16
+ <div v-if="scoreboards.length > 0">
17
+ <BaseText text="Scoreboards" size="lg" color="quinary" weight="bold" class="w-full text-left"/>
18
+ <div class="border-b border-dropdownSelect mb-4"/>
19
19
  <div v-for="scoreboard in scoreboards" :key="scoreboard.DisplayId + '-' + scoreboard.Type"
20
20
  class="flex flex-col py-2">
21
21
  <KioskCard :displayObject="scoreboard"
@@ -28,9 +28,10 @@
28
28
  @update:ringChange="handeRingChange"
29
29
  @remove:kiosk="handleRemove"
30
30
  @update:assignToMe="handleAssignToMe"
31
- @update:selectedType="handleChangeType"
32
- @update:editKiosk="handleEditKiosk"
33
- />
31
+ @update:selectedType="handleChangeType"
32
+ @update:editKiosk="handleEditKiosk"
33
+ @test:scoreboard="handleScoreboardTest"
34
+ />
34
35
  </div>
35
36
  </div>
36
37
 
@@ -103,7 +104,7 @@ export default {
103
104
  BaseText,
104
105
  emptyDesertIcon
105
106
  },
106
- emits: ['remove:kiosk', 'update:selectedStaff', 'update:selectedEvent', 'update:ringChange','update:selectedType', 'submit:kiosk', 'update:assignToMe'],
107
+ emits: ['remove:kiosk', 'test:scoreboard', 'update:selectedStaff', 'update:selectedEvent', 'update:ringChange','update:selectedType', 'submit:kiosk', 'update:assignToMe'],
107
108
  props: {
108
109
  displays: {
109
110
  type: Array,
@@ -158,8 +159,11 @@ export default {
158
159
  });
159
160
  }
160
161
  },
161
- methods: {
162
- transformAssignedTo(display) {
162
+ methods: {
163
+ handleScoreboardTest(display) {
164
+ this.$emit('test:scoreboard', display);
165
+ },
166
+ transformAssignedTo(display) {
163
167
  if (display.Person) {
164
168
  if (display.Person.DisplayName === 'BYE') {
165
169
  return {text: 'Unassigned', link: 'Unassigned'};
@@ -64,7 +64,7 @@ export default {
64
64
  BaseText,
65
65
  PoolFencerCard
66
66
  },
67
- emits: ['remove:fencer', 'select:pool', 'add:fencerToPool'],
67
+ emits: ['add:fencerToPool', 'remove:fencer', 'select:pool', 'update:setDirector', 'update:swapFencers'],
68
68
  props: {
69
69
  poolId: {
70
70
  type: [String, Number],
@@ -11,7 +11,7 @@
11
11
  </div>
12
12
  </section>
13
13
 
14
- <div v-if="shouldShowResults" v-for="(person, index) in results" :key="person.personId" class="flex flex-col py-1">
14
+ <div v-if="shouldShowResults" v-for="(person, index) in results" :key="person.PersonId || person.personId || index" class="flex flex-col py-1">
15
15
  <FencerPoolResultsCard
16
16
  :isLast="index === results.length - 1"
17
17
  :person="person"
@@ -39,18 +39,16 @@
39
39
  </template>
40
40
 
41
41
  <script>
42
- import FencerPoolResultsCard from "../../Cards/FencerPoolResultsCard/FencerPoolResultsCard.vue";
43
- import BaseIcon from "../../../Atoms/Icon/BaseIcon.vue";
44
- import BaseText from "../../../Atoms/Text/BaseText.vue";
45
- import emptyDesertIcon from '../../../../assets/empty_desert_icon.png';
46
- import FencerFinalResultCard from "../../Cards/FencerFinalResultCard/FencerFinalResultCard.vue";
42
+ import FencerPoolResultsCard from "../../Cards/FencerPoolResultsCard/FencerPoolResultsCard.vue";
43
+ import BaseIcon from "../../../Atoms/Icon/BaseIcon.vue";
44
+ import BaseText from "../../../Atoms/Text/BaseText.vue";
45
+ import emptyDesertIcon from '../../../../assets/empty_desert_icon.png';
47
46
 
48
47
  export default {
49
48
  name: "PoolResultsTable",
50
49
  emits: ['update:handleDecreaseCount', 'update:handleIncreaseCount', 'update:moveFencerDESeedDown', 'update:moveFencerDESeedUp', 'update:withdraw'],
51
50
  components: {
52
- FencerFinalResultCard,
53
- BaseText,
51
+ BaseText,
54
52
  BaseIcon,
55
53
  FencerPoolResultsCard,
56
54
  emptyDesertIcon
@@ -84,16 +82,30 @@ export default {
84
82
  },
85
83
  data() {
86
84
  return {
87
- results: this.poolResults,
88
- cutoffIndex: this.initialCutoffIndex,
85
+ results: this.clonePoolResults(this.poolResults),
86
+ cutoffIndex: this.initialCutoffIndex,
89
87
  emptyDesertIcon
90
88
  }
91
89
  },
92
- watch: {
93
- initialCutoffIndex(newValue) {
94
- this.cutoffIndex = newValue - 1;
95
- this.updatePromotions();
96
- }
90
+ watch: {
91
+ poolResults: {
92
+ deep: true,
93
+ immediate: true,
94
+ handler(newResults) {
95
+ this.results = this.clonePoolResults(newResults);
96
+ this.updatePromotions();
97
+ }
98
+ },
99
+ initialCutoffIndex(newValue) {
100
+ this.cutoffIndex = newValue - 1;
101
+ this.updatePromotions();
102
+ },
103
+ numPromoted() {
104
+ this.updatePromotions();
105
+ },
106
+ promotedBy() {
107
+ this.updatePromotions();
108
+ },
97
109
  },
98
110
  computed: {
99
111
  shouldShowResults() {
@@ -156,11 +168,14 @@ export default {
156
168
  moveFencerDESeedUp(person) {
157
169
  this.$emit('update:moveFencerDESeedUp', person);
158
170
  },
159
- moveFencerDESeedDown(person) {
160
- this.$emit('update:moveFencerDESeedDown', person);
161
- }
162
- }
163
- }
171
+ moveFencerDESeedDown(person) {
172
+ this.$emit('update:moveFencerDESeedDown', person);
173
+ },
174
+ clonePoolResults(poolResults) {
175
+ return Array.isArray(poolResults) ? poolResults.map(person => ({ ...person })) : [];
176
+ }
177
+ }
178
+ }
164
179
  </script>
165
180
 
166
181