@dcrackel/hematournamentui 1.0.688 → 1.0.690

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.
@@ -6,4 +6,4 @@
6
6
  * Released under the MIT license
7
7
  *
8
8
  * Date: 2024-04-21T07:43:02.731Z
9
- */.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-wrap-box,.cropper-canvas,.cropper-drag-box,.cropper-crop-box,.cropper-modal{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-wrap-box,.cropper-canvas{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:calc(100% / 3);left:0;top:calc(100% / 3);width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:calc(100% / 3);top:0;width:calc(100% / 3)}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:before,.cropper-center:after{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media(min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media(min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media(min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}[data-v-a1dce3ee] .ProseMirror>*+*{margin-top:.75em}[data-v-a1dce3ee] .ProseMirror ol{padding:0 1rem;list-style:decimal!important}[data-v-a1dce3ee] .ProseMirror ul{padding:0 1rem;list-style:disc!important}[data-v-c53fc36c]::-webkit-scrollbar{width:15px;height:15px}[data-v-c53fc36c]::-webkit-scrollbar-track{background:#f6fafd}[data-v-c53fc36c]::-webkit-scrollbar-thumb{background:#d5e4ee;border-radius:5px}[data-v-c53fc36c]::-webkit-scrollbar-thumb:hover{background:#c3cce0}.fade-in-down-enter-active[data-v-507c44cb]{animation:fadeInDown-507c44cb .3s ease-out}.fade-in-down-leave-active[data-v-507c44cb]{animation:fadeOutUp-507c44cb .3s ease-in;max-height:100px;overflow:hidden}.fade-in-down-leave-to[data-v-507c44cb]{opacity:0;transform:translateY(-10px);max-height:0;overflow:hidden}@keyframes fadeInDown-507c44cb{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeOutUp-507c44cb{0%{opacity:1;transform:translateY(0);max-height:100px}to{opacity:0;transform:translateY(-10px);max-height:0}}
9
+ */.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;touch-action:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.cropper-container img{backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-wrap-box,.cropper-canvas,.cropper-drag-box,.cropper-crop-box,.cropper-modal{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-wrap-box,.cropper-canvas{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:calc(100% / 3);left:0;top:calc(100% / 3);width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:calc(100% / 3);top:0;width:calc(100% / 3)}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:before,.cropper-center:after{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media(min-width:768px){.cropper-point.point-se{height:15px;width:15px}}@media(min-width:992px){.cropper-point.point-se{height:10px;width:10px}}@media(min-width:1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}[data-v-a1dce3ee] .ProseMirror>*+*{margin-top:.75em}[data-v-a1dce3ee] .ProseMirror ol{padding:0 1rem;list-style:decimal!important}[data-v-a1dce3ee] .ProseMirror ul{padding:0 1rem;list-style:disc!important}.fade-in-down-enter-active[data-v-507c44cb]{animation:fadeInDown-507c44cb .3s ease-out}.fade-in-down-leave-active[data-v-507c44cb]{animation:fadeOutUp-507c44cb .3s ease-in;max-height:100px;overflow:hidden}.fade-in-down-leave-to[data-v-507c44cb]{opacity:0;transform:translateY(-10px);max-height:0;overflow:hidden}@keyframes fadeInDown-507c44cb{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeOutUp-507c44cb{0%{opacity:1;transform:translateY(0);max-height:100px}to{opacity:0;transform:translateY(-10px);max-height:0}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dcrackel/hematournamentui",
3
3
  "private": false,
4
- "version": "1.0.688",
4
+ "version": "1.0.690",
5
5
  "type": "module",
6
6
  "main": "dist/HemaTournamentUI-lib.umd.js",
7
7
  "module": "dist/HemaTournamentUI-lib.es.js",
@@ -108,7 +108,7 @@ export default {
108
108
  if (!fromDragId || !toDragId || String(fromDragId) === String(toDragId)) return;
109
109
  this.$emit('reorder:swap', { fromBoutId: fromDragId, toBoutId: toDragId });
110
110
  },
111
- onAssign(ids) { this.$emit('action:assignDirector', ids); },
111
+ onAssign(ids) { this.$emit('action:assignBout', ids); },
112
112
  },
113
113
  };
114
114
  </script>
@@ -1,44 +1,90 @@
1
1
  <template>
2
2
  <section v-if="listView" class="flex flex-col items-center">
3
- <div v-for="(bouts, roundName) in filteredBouts" :key="bouts[0].BoutId" class="w-full flex-col md:w-3/4 my-4">
4
- <BaseText class="my-5" color="primaryHighlight" size="sm" :text="roundName" weight="bold"/>
5
- <div v-for="bout in bouts" :key="bout.BoutId" class="mb-4 ">
6
- <section @click="handleBoutClick(bout)">
7
- <BoutCard :bout="bout"
8
- :hostingClubColors="hostingClubColors"
9
- :editMode="false"
10
- :reorderMode="false"
3
+
4
+ <!-- Assigned Bouts -->
5
+ <div v-if="hasAssignedBouts" class="w-full flex-col md:w-3/4 my-4">
6
+ <div class="flex flex-row justify-between">
7
+ <BaseText class="my-5" color="primaryHighlight" size="sm" text="Assigned Bouts" weight="bold"/>
8
+ <div class="flex mr-1">
9
+ <BaseText class="my-5 ml-5" color="primaryHighlight" size="sm" text="Assigned:"/>
10
+ <BaseText
11
+ :text="assignedBoutsToMe.length"
12
+ class="my-5 ml-2"
13
+ color="primaryHighlight"
14
+ size="sm"
15
+ weight="bold"
11
16
  />
12
- </section>
17
+ </div>
18
+ </div>
19
+ <div class="border-b border-dropdownSelect mb-5"></div>
20
+
21
+ <div v-for="bout in assignedBoutsToMe" :key="bout.BoutId" class="mb-4">
22
+ <BoutCard
23
+ :bout="bout"
24
+ :hostingClubColors="hostingClubColors"
25
+ :editMode="false"
26
+ :reorderMode="false"
27
+ :isAdmin="isAdmin"
28
+ @action:startBout="handleDirectingFromIds"
29
+ @action:resumeBout="handleDirectingFromIds"
30
+ @action:editBout="handleEditFromIds"
31
+ @action:assignBout="handleAssignBout"
32
+ />
33
+ </div>
34
+ </div>
35
+
36
+ <!-- Existing rounds -->
37
+ <div
38
+ v-for="(bouts, roundName) in filteredBouts"
39
+ :key="bouts[0].BoutId"
40
+ class="w-full flex-col md:w-3/4 my-4"
41
+ >
42
+ <BaseText class="my-5" color="primaryHighlight" size="sm" :text="roundName" weight="bold"/>
43
+
44
+ <div v-for="bout in bouts" :key="bout.BoutId" class="mb-4">
45
+ <BoutCard
46
+ :bout="bout"
47
+ :hostingClubColors="hostingClubColors"
48
+ :editMode="false"
49
+ :reorderMode="false"
50
+ :isAdmin="isAdmin"
51
+ @action:startBout="handleDirectingFromIds"
52
+ @action:resumeBout="handleDirectingFromIds"
53
+ @action:editBout="handleEditFromIds"
54
+ @action:assignBout="handleAssignBout"
55
+ />
13
56
  </div>
14
57
  </div>
15
58
  </section>
16
59
 
17
- <section v-if="!listView" class="flex w-full overflow-x-auto mt-4 scrollbar-thin">
60
+ <section v-else class="flex w-full overflow-x-auto mt-4 scrollbar-thin">
18
61
  <div class="min-w-full flex">
19
62
  <div v-for="(bouts, roundName) in groupedBouts.winnerBouts" :key="roundName">
20
- <TableauColumn :bouts="bouts"
21
- :hostingClubColors="hostingClubColors"
22
- :roundName="roundName"
23
- :bracketSize="bracketSize"
24
- :largeCards="largeCards"
25
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
26
- @action:editBout="handleEditBout"
27
- @action:directingBout="handleDirectorBout"
63
+ <TableauColumn
64
+ :bouts="bouts"
65
+ :hostingClubColors="hostingClubColors"
66
+ :roundName="roundName"
67
+ :bracketSize="bracketSize"
68
+ :largeCards="largeCards"
69
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
70
+ @action:editBout="handleEditBout"
71
+ @action:directingBout="handleDirectorBout"
28
72
  />
29
73
  </div>
30
74
  </div>
31
- <!-- loser bracket-->
75
+
76
+ <!-- Loser bracket -->
32
77
  <div class="min-w-full flex">
33
78
  <div v-for="(bouts, roundName) in groupedBouts.loserBouts" :key="roundName">
34
- <TableauColumn :bouts="bouts"
35
- :hostingClubColors="hostingClubColors"
36
- :roundName="roundName"
37
- :bracketSize="bracketSize"
38
- :largeCards="largeCards"
39
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
40
- @action:editBout="handleEditBout"
41
- @action:directingBout="handleDirectorBout"
79
+ <TableauColumn
80
+ :bouts="bouts"
81
+ :hostingClubColors="hostingClubColors"
82
+ :roundName="roundName"
83
+ :bracketSize="bracketSize"
84
+ :largeCards="largeCards"
85
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
86
+ @action:editBout="handleEditBout"
87
+ @action:directingBout="handleDirectorBout"
42
88
  />
43
89
  </div>
44
90
  </div>
@@ -52,56 +98,68 @@ import BoutCard from "../../../Organisms/Cards/BoutCard/BoutCard.vue";
52
98
  import BaseText from "../../../Atoms/Text/BaseText.vue";
53
99
 
54
100
  export default {
55
- name: 'Tableau',
56
- components: {BaseText, BoutCard, TableauBoutCard, TableauColumn},
101
+ name: "Tableau",
102
+ components: { BaseText, BoutCard, TableauBoutCard, TableauColumn },
103
+ emits: ["action:editBout", "action:directingBout", "action:assignBout"],
57
104
  props: {
58
- bouts: {
59
- type: Array,
60
- required: true
61
- },
62
- bracketSize: {
63
- type: String,
64
- required: true
65
- },
66
- hostingClubColors: {
67
- type: Object,
68
- required: true
105
+ bouts: { type: Array, required: true },
106
+ bracketSize: { type: String, required: true },
107
+ hostingClubColors: { type: Object, required: true },
108
+ largeCards: { type: Boolean, required: true },
109
+ listView: { type: Boolean, required: false },
110
+ isCountingBackwardsMaxScore: { type: Number, default: 0 },
111
+
112
+ // use role (already present upstream) instead of passing isAdmin
113
+ role: { type: String, default: "Admin" },
114
+
115
+ // needed for "Assigned Bouts" section
116
+ currentUserId: { type: [Number, String], default: null },
117
+ },
118
+ computed: {
119
+ isAdmin() {
120
+ return String(this.role || "").toLowerCase() === "admin";
69
121
  },
70
- largeCards: {
71
- type: Boolean,
72
- required: true
122
+
123
+ assignedBoutsToMe() {
124
+ const me = Number(this.currentUserId);
125
+ if (!me) return [];
126
+ return (this.bouts || [])
127
+ .filter(
128
+ (b) =>
129
+ Number(b.RefereeId) === me &&
130
+ String(b.Status || "").toLowerCase() !== "completed"
131
+ )
132
+ .slice()
133
+ .sort((a, b) => Number(a.DEBoutId ?? 0) - Number(b.DEBoutId ?? 0));
73
134
  },
74
- listView: {
75
- type: Boolean,
76
- required: false
135
+
136
+ hasAssignedBouts() {
137
+ return this.assignedBoutsToMe.length > 0;
77
138
  },
78
- isCountingBackwardsMaxScore: {
79
- type: Number,
80
- default: 0
81
- }
82
- },
83
- emits: ['action:editBout', 'action:directingBout'],
84
- computed: {
139
+
85
140
  groupedBouts() {
86
141
  const grouped = this.groupBoutsByRound(this.bouts);
87
142
 
88
- // Separate bouts into winnerBouts and loserBouts based on RoundLabel
89
- const {winnerBouts, loserBouts} = Object.entries(grouped).reduce(
143
+ const { winnerBouts, loserBouts } = Object.entries(grouped).reduce(
90
144
  (acc, [roundName, bouts]) => {
91
- if (roundName.includes('Consolation')) {
145
+ if (roundName.includes("Consolation")) {
92
146
  acc.loserBouts[roundName] = bouts;
93
147
  } else {
94
148
  acc.winnerBouts[roundName] = bouts;
95
149
  }
96
150
  return acc;
97
151
  },
98
- {winnerBouts: {}, loserBouts: {}}
152
+ { winnerBouts: {}, loserBouts: {} }
99
153
  );
100
154
 
101
- return {winnerBouts, loserBouts};
155
+ return { winnerBouts, loserBouts };
102
156
  },
157
+
103
158
  combinedBouts() {
104
- const combined = {...this.groupedBouts.winnerBouts, ...this.groupedBouts.loserBouts};
159
+ const combined = {
160
+ ...this.groupedBouts.winnerBouts,
161
+ ...this.groupedBouts.loserBouts,
162
+ };
105
163
  return Object.keys(combined)
106
164
  .sort()
107
165
  .reduce((acc, roundName) => {
@@ -109,21 +167,20 @@ export default {
109
167
  return acc;
110
168
  }, {});
111
169
  },
170
+
112
171
  filteredBouts() {
113
172
  return Object.keys(this.combinedBouts)
114
173
  .sort((a, b) => {
115
174
  const priority = {
116
175
  Final: 1,
117
176
  "Third Place Match": 2,
118
- Semifinal: 3
177
+ Semifinal: 3,
119
178
  };
120
179
 
121
180
  const priorityA = priority[a] || 0;
122
181
  const priorityB = priority[b] || 0;
123
182
 
124
- if (priorityA || priorityB) {
125
- return priorityA - priorityB;
126
- }
183
+ if (priorityA || priorityB) return priorityA - priorityB;
127
184
 
128
185
  const numA = parseInt(a.match(/\d+/)?.[0] || 0, 10);
129
186
  const numB = parseInt(b.match(/\d+/)?.[0] || 0, 10);
@@ -131,69 +188,72 @@ export default {
131
188
  return numB - numA;
132
189
  })
133
190
  .reduce((acc, roundName) => {
134
- const filteredBouts = this.combinedBouts[roundName]
191
+ const filtered = this.combinedBouts[roundName]
135
192
  .filter(
136
- bout =>
137
- bout.Person1?.DisplayName !== 'BYE' &&
138
- bout.Person2?.DisplayName !== 'BYE' &&
139
- bout.Status !== 'Completed'
193
+ (bout) =>
194
+ bout.Person1?.DisplayName !== "BYE" &&
195
+ bout.Person2?.DisplayName !== "BYE" &&
196
+ bout.Status !== "Completed"
140
197
  )
141
198
  .sort((a, b) => a.DEBoutId - b.DEBoutId);
142
199
 
143
- if (filteredBouts.length) {
144
- acc[roundName] = filteredBouts;
145
- }
200
+ if (filtered.length) acc[roundName] = filtered;
146
201
  return acc;
147
202
  }, {});
148
203
  },
149
204
  },
205
+
150
206
  methods: {
151
- handleBoutClick(bout) {
152
- if (bout.Status === 'Completed') {
153
- this.$emit('action:editBout', bout);
154
- } else {
155
- this.$emit('action:directingBout', bout);
156
- }
207
+ // --- helpers ---
208
+ _findBoutByIds(idsOrBout) {
209
+ // BoutCard emits ids for start/resume/edit; assign emits ids too.
210
+ // normalize to actual bout object
211
+ if (!idsOrBout) return null;
212
+ if (idsOrBout.BoutId && idsOrBout.Person1) return idsOrBout; // already a bout-ish object
213
+
214
+ const boutId = idsOrBout?.BoutId ?? idsOrBout?.boutId;
215
+ if (!boutId) return null;
216
+ return (this.bouts || []).find((b) => String(b.BoutId) === String(boutId)) || null;
157
217
  },
218
+
219
+ // Assign button from BoutCard (inactive scheduled variant)
220
+ handleAssignBout(ids) {
221
+ const bout = this._findBoutByIds(ids);
222
+ if (!bout) return;
223
+ this.$emit("action:assignBout", bout);
224
+ },
225
+
226
+ // Start/Resume from BoutCard
227
+ handleDirectingFromIds(ids) {
228
+ const bout = this._findBoutByIds(ids);
229
+ if (!bout) return;
230
+ this.$emit("action:directingBout", bout);
231
+ },
232
+
233
+ // Edit from BoutCard
234
+ handleEditFromIds(ids) {
235
+ const bout = this._findBoutByIds(ids);
236
+ if (!bout) return;
237
+ this.$emit("action:editBout", bout);
238
+ },
239
+
240
+ // TableauColumn / non-list view already emits bout objects
158
241
  handleEditBout(bout) {
159
- this.$emit('action:editBout', bout)
242
+ this.$emit("action:editBout", bout);
160
243
  },
161
244
  handleDirectorBout(bout) {
162
- this.$emit('action:directingBout', bout)
245
+ this.$emit("action:directingBout", bout);
163
246
  },
247
+
164
248
  groupBoutsByRound(bouts) {
165
- return bouts.reduce((acc, bout) => {
166
- if (!acc[bout.RoundLabel]) {
167
- acc[bout.RoundLabel] = [];
168
- }
169
- acc[bout.RoundLabel].push(bout);
249
+ return (bouts || []).reduce((acc, bout) => {
250
+ const key = bout.RoundLabel || "Unknown Round";
251
+ if (!acc[key]) acc[key] = [];
252
+ acc[key].push(bout);
170
253
  return acc;
171
254
  }, {});
172
255
  },
173
- }
256
+ },
174
257
  };
175
258
  </script>
176
259
 
177
- <style scoped>
178
- /* width */
179
- ::-webkit-scrollbar {
180
- width: 15px;
181
- height: 15px;
182
- }
183
-
184
- /* Track */
185
- ::-webkit-scrollbar-track {
186
- background: #f6fafd;
187
- }
188
-
189
- /* Handle */
190
- ::-webkit-scrollbar-thumb {
191
- background: #d5e4ee;
192
- border-radius: 5px;
193
- }
194
-
195
- /* Handle on hover */
196
- ::-webkit-scrollbar-thumb:hover {
197
- background: #c3cce0;
198
- }
199
- </style>
@@ -1,4 +1,5 @@
1
1
  import Bracket from './Bracket.vue';
2
+ import getDirectors from '../../../../mocks/getDirectors.js';
2
3
  //import getDEWithBouts from '../../../../mocks/getDEWithBouts.js';
3
4
  import getDEWithBouts6Fencers from '../../../../mocks/getDeBouts-6fencers.js';
4
5
  import getDEWithBouts12Fencers from '../../../../mocks/getDeBouts-12fencers.js';
@@ -17,7 +18,9 @@ export default {
17
18
  eventRules: getDEWithBouts.eventRules,
18
19
  hostingClubColors: getDEWithBouts.hostingClubColors,
19
20
  connectedToServer: true,
20
- role: 'admin'
21
+ role: 'admin',
22
+ directors: getDirectors,
23
+ currentUserId: 79, // optional but useful if you're showing "Assigned Bouts"
21
24
  },
22
25
  argTypes: {
23
26
  bouts: {
@@ -34,7 +37,9 @@ export default {
34
37
  },
35
38
  role: {
36
39
  control: String
37
- }
40
+ },
41
+ directors: { control: 'object' },
42
+ currentUserId: { control: 'number' },
38
43
  },
39
44
  };
40
45
 
@@ -45,7 +50,9 @@ export const Default = {
45
50
  hostingClubColors: getDEWithBouts12Fencers.hostingClubColors,
46
51
  connectedToServer: true,
47
52
  status: 'de',
48
- role: 'Admin'
53
+ role: 'Admin',
54
+ directors: getDirectors,
55
+ currentUserId: 79,
49
56
  }
50
57
  };
51
58
 
@@ -55,7 +62,9 @@ export const TableOfEight = {
55
62
  eventRules: getDEWithBouts6Fencers.eventRules,
56
63
  hostingClubColors: getDEWithBouts6Fencers.hostingClubColors,
57
64
  connectedToServer: true,
58
- status: 'de'
65
+ status: 'de',
66
+ directors: getDirectors,
67
+ currentUserId: 79,
59
68
  }
60
69
  };
61
70
 
@@ -65,7 +74,9 @@ export const TableWithTwentyTwo = {
65
74
  eventRules: getDEWithBouts22Fencers.eventRules,
66
75
  hostingClubColors: getDEWithBouts22Fencers.hostingClubColors,
67
76
  connectedToServer: true,
68
- status: 'de'
77
+ status: 'de',
78
+ directors: getDirectors,
79
+ currentUserId: 79,
69
80
  }
70
81
  };
71
82
 
@@ -75,7 +86,9 @@ export const tableOfThirtyTwo = {
75
86
  eventRules: getDEWithBouts.eventRules,
76
87
  hostingClubColors: getDEWithBouts.hostingClubColors,
77
88
  connectedToServer: true,
78
- status: 'de'
89
+ status: 'de',
90
+ directors: getDirectors,
91
+ currentUserId: 79,
79
92
  }
80
93
  };
81
94
 
@@ -85,7 +98,9 @@ export const tableOfFourtyTwo = {
85
98
  eventRules: getDEWithBout42Fencers.eventRules,
86
99
  hostingClubColors: getDEWithBout42Fencers.hostingClubColors,
87
100
  connectedToServer: true,
88
- status: 'de'
101
+ status: 'de',
102
+ directors: getDirectors,
103
+ currentUserId: 79,
89
104
  }
90
105
  };
91
106
 
@@ -95,7 +110,9 @@ export const tableOfSixtyFour = {
95
110
  eventRules: getDEWithBouts67Fencers.eventRules,
96
111
  hostingClubColors: getDEWithBouts67Fencers.hostingClubColors,
97
112
  connectedToServer: true,
98
- status: 'de'
113
+ status: 'de',
114
+ directors: getDirectors,
115
+ currentUserId: 79,
99
116
  }
100
117
  };
101
118
 
@@ -105,7 +122,9 @@ export const tableOfOneHundredTwentyEight = {
105
122
  eventRules: getDEWithBouts128Fencers.eventRules,
106
123
  hostingClubColors: getDEWithBouts128Fencers.hostingClubColors,
107
124
  connectedToServer: true,
108
- status: 'de'
125
+ status: 'de',
126
+ directors: getDirectors,
127
+ currentUserId: 79,
109
128
  }
110
129
  };
111
130
 
@@ -115,6 +134,8 @@ export const noBoutsYet = {
115
134
  eventRules: getDEWithBouts22Fencers.eventRules,
116
135
  hostingClubColors: getDEWithBouts22Fencers.hostingClubColors,
117
136
  connectedToServer: true,
118
- status: 'live'
137
+ status: 'live',
138
+ directors: getDirectors,
139
+ currentUserId: 79,
119
140
  }
120
141
  };
@@ -11,14 +11,20 @@
11
11
  :connectedToServer="connectedToServer"
12
12
  @update:selection="handleViewChange"
13
13
  @update:cardSize="handleCardSizeToggle"/>
14
- <Tableau :bouts="sortedBouts"
15
- :hostingClubColors="hostingClubColors"
16
- :bracketSize="bracketSize"
17
- :largeCards="largeCards"
18
- :listView="isListView"
19
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
20
- @action:editBout="editBout"
21
- @action:directingBout="directBout" />
14
+
15
+ <Tableau
16
+ :bouts="sortedBouts"
17
+ :hostingClubColors="hostingClubColors"
18
+ :bracketSize="bracketSize"
19
+ :largeCards="largeCards"
20
+ :listView="isListView"
21
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
22
+ :role="role"
23
+ :currentUserId="currentUserId"
24
+ @action:editBout="editBout"
25
+ @action:directingBout="directBout"
26
+ @action:assignBout="openAssignDirector"
27
+ />
22
28
 
23
29
  <div v-if="bouts.length < 1" class="w-full flex justify-center items-center">
24
30
  <div class="text-center mb-10">
@@ -49,6 +55,14 @@
49
55
  @update:closeModal="handleCloseModal"
50
56
  @submit:bout="handleSubmitBout"/>
51
57
 
58
+ <AssignDirectorModal
59
+ :show="showAssignDirectorModal"
60
+ :bout="assigningBout || {}"
61
+ :directors="directors"
62
+ @update:closeModal="closeAssignDirectorModal"
63
+ @assign="handleAssignDirector"
64
+ />
65
+
52
66
  </section>
53
67
  </template>
54
68
 
@@ -64,9 +78,11 @@ import TableauColumn from "../../../Organisms/Containers/TableauColumn/TableauCo
64
78
  import ServerConnected from "../../../Molecules/Indicators/ServerConnected/ServerConnected.vue";
65
79
  import emptyDesertIcon from "../../../../assets/empty_desert_icon.png";
66
80
  import { tabs } from '../../../Util/tabs.js';
81
+ import AssignDirectorModal from "../../../Molecules/Modals/AssignDirectorModal/AssignDirectorModal.vue";
67
82
 
68
83
  export default {
69
84
  components: {
85
+ AssignDirectorModal,
70
86
  ServerConnected,
71
87
  TableauColumn,
72
88
  Tableau,
@@ -101,9 +117,36 @@ export default {
101
117
  role: {
102
118
  type: String,
103
119
  default: 'Admin'
104
- }
120
+ },
121
+ directors: {
122
+ type: Array,
123
+ default: () => []
124
+ },
125
+ currentUserId: {
126
+ type: [Number, String],
127
+ default: null
128
+ },
129
+
130
+ },
131
+ data() {
132
+ return {
133
+ emptyDesertIcon,
134
+ showDirectorModal: false,
135
+ showEditBoutModal: false,
136
+ selectedBout: {},
137
+ tabs,
138
+ viewName: "Tableau",
139
+ largeCards: true,
140
+ boutsData: [...this.bouts],
141
+ isListView: false,
142
+ showAssignDirectorModal: false,
143
+ assigningBout: null,
144
+ };
105
145
  },
106
146
  computed: {
147
+ isAdmin() {
148
+ return String(this.role || '').toLowerCase() === 'admin';
149
+ },
107
150
  isCountingBackwardsMaxScore() {
108
151
  // 1) see if PoolScoreDirection exists and is explicitly "false"
109
152
  const dirRule = this.eventRules.find(r => r.Rules?.RuleName === 'PoolScoreDirection');
@@ -138,19 +181,6 @@ export default {
138
181
  });
139
182
  }
140
183
  },
141
- data() {
142
- return {
143
- emptyDesertIcon,
144
- showDirectorModal: false,
145
- showEditBoutModal: false,
146
- selectedBout: {},
147
- tabs,
148
- viewName: "Tableau",
149
- largeCards: true,
150
- boutsData: [...this.bouts],
151
- isListView: false
152
- };
153
- },
154
184
  watch: {
155
185
  bouts: {
156
186
  handler(newBouts) {
@@ -168,6 +198,33 @@ export default {
168
198
  window.removeEventListener('resize', this.updateViewBasedOnScreenSize);
169
199
  },
170
200
  methods: {
201
+ handleAssignDirector({ bout, director }) {
202
+ const boutId = bout?.BoutId;
203
+ if (!boutId) return;
204
+
205
+ const patched = {
206
+ ...bout,
207
+ RefereeId: director.PersonId,
208
+ RefName: director.DisplayName,
209
+ };
210
+
211
+ this.boutsData = this.boutsData.map(b =>
212
+ b.BoutId === boutId ? { ...b, ...patched } : b
213
+ );
214
+
215
+ // bubble up so the real app persists it
216
+ this.$emit('update:bout', patched);
217
+
218
+ this.closeAssignDirectorModal();
219
+ },
220
+ openAssignDirector(bout) {
221
+ this.assigningBout = bout;
222
+ this.showAssignDirectorModal = true;
223
+ },
224
+ closeAssignDirectorModal() {
225
+ this.showAssignDirectorModal = false;
226
+ this.assigningBout = null;
227
+ },
171
228
  handleCloseModal(bout) {
172
229
  this.showDirectorModal = false;
173
230
  this.showEditBoutModal = false;
@@ -34,8 +34,6 @@ export default {
34
34
  hostingClubColors: getPoolsWithBoutsByPoolId.hostingClubColors,
35
35
  connectedToServer: true,
36
36
  editMode: true,
37
-
38
- // IMPORTANT: these drive Admin + Assigned logic
39
37
  role: 'admin',
40
38
  currentUserId: 79, // Tom Testerlou
41
39
  directors: getDirectors,