@dcrackel/hematournamentui 1.0.686 → 1.0.688

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.
@@ -60,6 +60,40 @@
60
60
  </div>
61
61
  </section>
62
62
 
63
+ <!-- Assigned Bouts -->
64
+ <section v-if="hasAssignedBout && !fencerList && !reorderMode">
65
+ <div class="flex flex-row justify-between">
66
+ <BaseText class="mt-5" color="primaryHighlight" size="md" text="Assigned Bouts" weight="bold"/>
67
+ <div class="flex mr-1">
68
+ <BaseText class="mt-5 ml-5" color="primaryHighlight" size="md" text="Assigned:"/>
69
+ <BaseText :text="assignedBoutsToMe.length" class="mt-5 ml-2" color="primaryHighlight" size="md" weight="bold"/>
70
+ </div>
71
+ </div>
72
+ <div class="border-b border-bright mb-5"></div>
73
+
74
+ <div class="w-full flex flex-col items-center">
75
+ <div
76
+ v-for="bout in assignedBoutsToMe"
77
+ :key="bout.BoutId"
78
+ class="w-full md:w-3/4 my-4 flex items-stretch"
79
+ >
80
+ <div class="flex-1">
81
+ <BoutCard
82
+ :bout="bout"
83
+ :hostingClubColors="hostingClubColors"
84
+ :editMode="editMode"
85
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
86
+ :reorderMode="false"
87
+ :isAdmin="isAdmin"
88
+ @action:startBout="directBout"
89
+ @action:resumeBout="directBout"
90
+ @action:assignDirector="handleAssignClick"
91
+ />
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </section>
96
+
63
97
  <!-- Upcoming -->
64
98
  <section v-if="remainingBoutsCount > 0 && !fencerList && !reorderMode">
65
99
  <div class="flex flex-row justify-between">
@@ -71,14 +105,20 @@
71
105
  </div>
72
106
  <div class="border-b border-dropdownSelect mb-5"></div>
73
107
  <div class="w-full flex flex-col items-center">
74
- <div v-for="bout in markedUpcomingBouts" :key="bout.BoutId" class="w-full md:w-3/4 my-4">
75
- <BoutCard :bout="bout"
76
- :hostingClubColors="hostingClubColors"
77
- :editMode="editMode"
78
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
79
- :reorderMode="false"
80
- @action:startBout="directBout"
81
- />
108
+ <div v-for="bout in markedUpcomingBouts" :key="bout.BoutId" class="w-full md:w-3/4 my-4 flex items-stretch">
109
+ <div class="flex-1">
110
+ <BoutCard
111
+ :bout="bout"
112
+ :hostingClubColors="hostingClubColors"
113
+ :editMode="editMode"
114
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
115
+ :reorderMode="false"
116
+ :isAdmin="isAdmin"
117
+ @action:startBout="directBout"
118
+ @action:assignDirector="handleAssignClick"
119
+ />
120
+ </div>
121
+
82
122
  </div>
83
123
  </div>
84
124
  </section>
@@ -127,9 +167,11 @@
127
167
  >
128
168
  <BaseText :text="`#${row.index+1}`" size="xs" color="quaternary" weight="" class="mr-4 w-4"/>
129
169
  <BaseText :text="`${row.pair[0]} vs ${row.pair[1]}`" size="xs" color="quaternary" class="w-9"/>
130
- <BaseIcon v-if="row.bout && row.index === row.boutIndex" icon-name="fa-square-check" size="xs" color="green" hover="green" class="ml-2" />
131
- <BaseIcon v-else-if="row.bout" icon-name="fa-square-xmark" size="xs" color="orange" hover="orange" class="ml-2" />
132
- <BaseIcon v-else icon-name="fa-square-minus" size="xs" color="red" hover="red" class="ml-2" />
170
+ <BaseIcon v-if="row.bout && row.index === row.boutIndex" icon-name="fa-square-check" size="xs"
171
+ color="green" hover="green" class="ml-2"/>
172
+ <BaseIcon v-else-if="row.bout" icon-name="fa-square-xmark" size="xs" color="orange" hover="orange"
173
+ class="ml-2"/>
174
+ <BaseIcon v-else icon-name="fa-square-minus" size="xs" color="red" hover="red" class="ml-2"/>
133
175
  </li>
134
176
  </ol>
135
177
  </div>
@@ -191,6 +233,14 @@
191
233
  @submit:bout="handleEditBout"
192
234
  :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"/>
193
235
 
236
+ <AssignDirectorModal
237
+ :show="showAssignDirectorModal"
238
+ :bout="assigningBout || {}"
239
+ :directors="directors"
240
+ @update:closeModal="closeAssignDirectorModal"
241
+ @assign="handleAssignDirector"
242
+ />
243
+
194
244
  </section>
195
245
  </template>
196
246
 
@@ -210,10 +260,12 @@ import BaseButton from "../../../Molecules/Buttons/BaseButton/BaseButton.vue";
210
260
  import FencerFinalResultCard from "../../../Organisms/Cards/FencerFinalResultCard/FencerFinalResultCard.vue";
211
261
  import ReorderableShell from "../../../Molecules/Reorder/ReorderableShell.vue";
212
262
  import {getPoolOrder} from '../../../Util/seeding.js';
263
+ import AssignDirectorModal from "../../../Molecules/Modals/AssignDirectorModal/AssignDirectorModal.vue";
213
264
 
214
265
  export default {
215
266
  name: "PoolLive",
216
267
  components: {
268
+ AssignDirectorModal,
217
269
  ReorderableShell,
218
270
  FencerFinalResultCard,
219
271
  BaseButton,
@@ -253,7 +305,16 @@ export default {
253
305
  role: {
254
306
  type: String,
255
307
  default: 'admin'
256
- }
308
+ },
309
+ directors: {
310
+ type: Array,
311
+ required: true
312
+ },
313
+ currentUserId: {
314
+ type: [Number, String],
315
+ required: true
316
+ },
317
+
257
318
  },
258
319
  data() {
259
320
  return {
@@ -267,6 +328,8 @@ export default {
267
328
  tabs,
268
329
  windowWidth: window.innerWidth,
269
330
  selectedReorderId: null,
331
+ showAssignDirectorModal: false,
332
+ assigningBout: null
270
333
  };
271
334
  },
272
335
  watch: {
@@ -286,6 +349,19 @@ export default {
286
349
  window.removeEventListener('resize', this.handleResize);
287
350
  },
288
351
  computed: {
352
+ isAdmin() {
353
+ return String(this.role || '').toLowerCase() === 'admin';
354
+ },
355
+ assignedBoutsToMe() {
356
+ const me = Number(this.currentUserId);
357
+ return this.localBouts
358
+ .filter(b => Number(b.RefereeId) === me && (b.Status || '').toLowerCase() !== 'completed')
359
+ .slice()
360
+ .sort((a, b) => Number(a.OrderIndex ?? 0) - Number(b.OrderIndex ?? 0));
361
+ },
362
+ hasAssignedBout() {
363
+ return this.assignedBoutsToMe.length > 0;
364
+ },
289
365
  currentSelectedBout() {
290
366
  return this.localBouts.find(b => b.BoutId === this.selectedBout.BoutId) || this.selectedBout;
291
367
  },
@@ -293,7 +369,7 @@ export default {
293
369
  return this.windowWidth <= 748;
294
370
  },
295
371
  eventRules() {
296
- return this.event?.EventRules || {};
372
+ return Array.isArray(this.event?.EventRules) ? this.event.EventRules : [];
297
373
  },
298
374
  upcomingBouts() {
299
375
  return this.localBouts
@@ -531,6 +607,31 @@ export default {
531
607
  });
532
608
  this.localBouts = [...this.localBouts];
533
609
  },
610
+ handleAssignClick(ids) {
611
+ const bout = this.localBouts.find(b => b.BoutId === ids.BoutId);
612
+ if (!bout) return;
613
+ this.openAssignDirector(bout);
614
+ },
615
+ openAssignDirector(bout) {
616
+ this.assigningBout = bout;
617
+ this.showAssignDirectorModal = true;
618
+ },
619
+ closeAssignDirectorModal() {
620
+ this.showAssignDirectorModal = false;
621
+ this.assigningBout = null;
622
+ },
623
+ handleAssignDirector({bout, director}) {
624
+ const updatedBout = this.localBouts.find(b => b.BoutId === bout.BoutId) || bout;
625
+
626
+ // only change these two fields
627
+ updatedBout.RefereeId = director.PersonId;
628
+ updatedBout.RefName = director.DisplayName;
629
+
630
+ // emit to parent, parent will call socket emit/save
631
+ this.$emit('update:bout', updatedBout);
632
+
633
+ this.closeAssignDirectorModal();
634
+ },
534
635
  // --- helpers ---
535
636
  _idOf(bout) {
536
637
  return bout?.BoutId ?? bout?.boutId;
@@ -546,7 +647,9 @@ export default {
546
647
  .filter(b => this._normalizeStatus(b.Status) === 'scheduled')
547
648
  .slice()
548
649
  .sort((a, b) => Number(a.OrderIndex ?? Infinity) - Number(b.OrderIndex ?? Infinity));
549
- scheduled.forEach((b, i) => { b.OrderIndex = i; });
650
+ scheduled.forEach((b, i) => {
651
+ b.OrderIndex = i;
652
+ });
550
653
  },
551
654
  _swapOrderBetweenIds(idA, idB) {
552
655
  if (!idA || !idB || String(idA) === String(idB)) return;
@@ -568,7 +671,7 @@ export default {
568
671
  },
569
672
 
570
673
  // --- DnD drop from <ReorderableShell> ---
571
- onReorderDrop({ fromDragId, toDragId }) {
674
+ onReorderDrop({fromDragId, toDragId}) {
572
675
  this._swapOrderBetweenIds(fromDragId, toDragId);
573
676
  },
574
677
 
@@ -0,0 +1,87 @@
1
+ <template>
2
+ <section class="mt-2">
3
+ <div class="flex flex-col mb-4">
4
+ <TitledInput
5
+ :inputValue="personId"
6
+ :inputType="'number'"
7
+ placeholder="Meyer Squared ID"
8
+ title="Meyer Squared ID"
9
+ :disabled="true"
10
+ aria-disabled="true"
11
+ />
12
+ </div>
13
+
14
+ <div class="flex flex-col mb-4">
15
+ <TitledInput
16
+ :inputValue="hemaRatingId"
17
+ :inputType="'number'"
18
+ placeholder="HEMA Rating ID"
19
+ title="HEMA Rating ID"
20
+ @update:value="emitUpdate('HemaRatingsId', $event)"
21
+ />
22
+ </div>
23
+
24
+ <div class="flex flex-col mb-4">
25
+ <BaseText text="Access Level" size="sm" color="quaternary"/>
26
+ <DropDownMenu
27
+ :label="getAccessLevelLabel"
28
+ :items="accessLevels"
29
+ class="pb-2"
30
+ :selectedItem="getSelectedAccessLevel"
31
+ @update:selectedItem="emitUpdate('AccessLevel', $event.text)"
32
+ width="w-full"
33
+ dropDownWidth="w-full"
34
+ />
35
+ </div>
36
+ </section>
37
+ </template>
38
+
39
+ <script>
40
+ import TitledInput from "../../../Molecules/CombinationInputs/TitledInput/TitledInput.vue";
41
+ import BaseText from "../../../Atoms/Text/BaseText.vue";
42
+ import DropDownMenu from "../../../Organisms/ComplexInputs/DropDown/DropDownMenu.vue";
43
+
44
+ export default {
45
+ name: "Administration",
46
+ emits: ["update:person"],
47
+ components: { TitledInput, BaseText, DropDownMenu },
48
+ props: {
49
+ person: { type: Object, required: true },
50
+ userLevel: { type: String, required: true },
51
+ },
52
+ computed: {
53
+ personId() {
54
+ return this.person?.PersonId ?? "";
55
+ },
56
+ hemaRatingId() {
57
+ return this.person?.HEMARatings?.[0]?.HemaRatingId ?? "";
58
+ },
59
+ accessLevels() {
60
+ if (this.userLevel === "Admin") {
61
+ return [
62
+ { text: "User", link: "User" },
63
+ { text: "Director", link: "Director" },
64
+ { text: "Club", link: "Club" },
65
+ { text: "Admin", link: "Admin" },
66
+ ];
67
+ }
68
+ return [
69
+ { text: "User", link: "User" },
70
+ { text: "Director", link: "Director" },
71
+ { text: "Club", link: "Club" },
72
+ ];
73
+ },
74
+ getSelectedAccessLevel() {
75
+ return { text: this.person.AccessLevel, link: this.person.AccessLevel };
76
+ },
77
+ getAccessLevelLabel() {
78
+ return this.person.AccessLevel ? "" : "Select Access Level";
79
+ },
80
+ },
81
+ methods: {
82
+ emitUpdate(field, value) {
83
+ this.$emit("update:person", { field, value });
84
+ },
85
+ },
86
+ };
87
+ </script>
@@ -1,94 +1,77 @@
1
- `<template>
1
+ <template>
2
2
  <section class="mt-2 min-h-80">
3
- <div>
4
- <div class="flex flex-col mb-4">
5
- <TitledInput
6
- :inputValue="personId"
7
- :inputType="'number'"
8
- placeholder="Meyer Squared ID"
9
- title="Meyer Squared ID"
10
- :disabled="true"
11
- aria-disabled="true"
3
+ <!-- Administration section -->
4
+ <div class="pb-6">
5
+ <BaseText text="Administration" size="lg" color="primary" />
6
+ <div class="mt-3">
7
+ <Administration
8
+ :person="person"
9
+ :userLevel="userLevel"
10
+ @update:person="$emit('update:person', $event)"
12
11
  />
13
12
  </div>
14
- <div class="flex flex-col mb-4">
15
- <TitledInput
16
- :inputValue="hemaRatingId"
17
- :inputType="'number'"
18
- placeholder="HEMA Rating ID"
19
- title="HEMA Rating ID"
20
- @update:value="emitUpdate('HemaRatingsId', $event)"
21
- />
22
- </div>
23
- <div class="flex flex-col mb-4">
24
- <BaseText text="Access Level" size="sm" color="quaternary"/>
25
- <DropDownMenu
26
- :label="getAccessLevelLabel"
27
- :items="accessLevels"
28
- class="pb-2"
29
- :selectedItem="getSelectedAccessLevel"
30
- @update:selectedItem="emitUpdate('AccessLevel', $event.text)"
31
- width="w-full"
32
- dropDownWidth="w-full"
33
- />
13
+ </div>
14
+
15
+ <!-- Merge Accounts section (collapsible) -->
16
+ <div v-if="userLevel === 'Admin'" class="pt-6 border-t border-dropdownSelect">
17
+ <button
18
+ type="button"
19
+ class="w-full flex items-center justify-between text-left"
20
+ @click="toggleMergeOpen"
21
+ >
22
+ <div>
23
+ <BaseText text="Merge Accounts" size="lg" color="primary" />
24
+ <BaseText
25
+ class="mt-1"
26
+ text="Merge other fencer accounts into this one. This will move all references and delete the duplicate accounts."
27
+ size="sm"
28
+ color="quaternary"
29
+ />
30
+ </div>
31
+
32
+ <!-- Optional caret (swap for your icon system if preferred) -->
33
+ <span
34
+ class="ml-4 text-quaternary transition-transform duration-200"
35
+ :class="mergeOpen ? 'rotate-180' : 'rotate-0'"
36
+ aria-hidden="true"
37
+ >
38
+
39
+ </span>
40
+ </button>
41
+
42
+ <div v-if="mergeOpen" class="mt-4">
43
+ <MergeAccounts :person="person" :people="people" @merge:merge="doMerge" />
34
44
  </div>
35
45
  </div>
36
46
  </section>
37
47
  </template>
38
48
 
39
49
  <script>
40
- import TitledInput from "../../../Molecules/CombinationInputs/TitledInput/TitledInput.vue";
41
50
  import BaseText from "../../../Atoms/Text/BaseText.vue";
42
- import DropDownMenu from "../../../Organisms/ComplexInputs/DropDown/DropDownMenu.vue";
51
+ import Administration from "./Administration.vue";
52
+ import MergeAccounts from "./MergeAccounts.vue";
43
53
 
44
54
  export default {
45
- name: 'FencerAdmin',
46
- emits: ['update:person'],
47
- components: { TitledInput, BaseText, DropDownMenu },
55
+ name: "FencerAdmin",
56
+ emits: ["update:person", "merge:merge"],
57
+ components: { BaseText, Administration, MergeAccounts },
48
58
  props: {
49
- person: {
50
- type: Object,
51
- required: true
52
- },
53
- userLevel: {
54
- type: String,
55
- required: true
56
- }
59
+ person: { type: Object, required: true },
60
+ people: { type: Array, required: true },
61
+ userLevel: { type: String, required: true },
57
62
  },
58
- computed: {
59
- personId() {
60
- return this.person?.PersonId ?? '';
61
- },
62
- hemaRatingId() {
63
- return this.person?.HEMARatings?.[0]?.HemaRatingId ?? '';
64
- },
65
- accessLevels() {
66
- if (this.userLevel === 'Admin') {
67
- return [
68
- { text: 'User', link: 'User' },
69
- { text: 'Director', link: 'Director' },
70
- { text: 'Club', link: 'Club' },
71
- { text: 'Admin', link: 'Admin' },
72
- ];
73
- }
74
- return [
75
- { text: 'User', link: 'User' },
76
- { text: 'Director', link: 'Director' },
77
- { text: 'Club', link: 'Club' }
78
- ];
79
- },
80
- getSelectedAccessLevel() {
81
- return { text: this.person.AccessLevel, link: this.person.AccessLevel };
82
- },
83
- getAccessLevelLabel() {
84
- return this.person.AccessLevel ? '' : 'Select Access Level';
85
- }
63
+ data() {
64
+ return {
65
+ mergeOpen: false, // collapsed by default
66
+ };
86
67
  },
87
68
  methods: {
88
- emitUpdate(field, value) {
89
- this.$emit('update:person', { field, value });
69
+ toggleMergeOpen() {
70
+ this.mergeOpen = !this.mergeOpen;
71
+ },
72
+ doMerge(data) {
73
+ this.$emit("merge:merge", data);
90
74
  }
91
- }
75
+ },
92
76
  };
93
77
  </script>
94
- `