@dcrackel/hematournamentui 1.0.713 → 1.0.715
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.
- package/README.md +14 -0
- package/dist/HemaTournamentUI-lib.es.js +6917 -6255
- package/dist/HemaTournamentUI-lib.umd.js +27 -27
- package/dist/hematournamentui-tailwind.css +109 -18
- package/package.json +1 -1
- package/src/index.js +43 -42
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.stories.js +49 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.test.js +129 -0
- package/src/stories/Molecules/Modals/BracketDirectorsModal/BracketDirectorsModal.vue +165 -0
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +10 -1
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +31 -0
- package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +20 -1
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +38 -0
- package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +63 -9
- package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +46 -4
- package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +244 -178
- package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +48 -3
- package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +8 -0
- package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +121 -0
- package/src/stories/Util/bracketDirectors.js +86 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<dialog
|
|
4
|
+
ref="dialog"
|
|
5
|
+
aria-label="DE directors"
|
|
6
|
+
data-testid="bracket-directors-modal"
|
|
7
|
+
class="fixed inset-0 m-auto max-h-[calc(100dvh-2rem)] w-[calc(100%-2rem)] max-w-xl overflow-y-auto rounded-xl border border-dropdownSelect bg-neutral p-0 text-primaryHighlight shadow-xl backdrop:bg-secondary/60"
|
|
8
|
+
@cancel.prevent="close"
|
|
9
|
+
@click="close"
|
|
10
|
+
>
|
|
11
|
+
<form class="p-5" @submit.prevent="submit" @click.stop>
|
|
12
|
+
<div class="mb-4 flex items-start justify-between gap-3">
|
|
13
|
+
<div>
|
|
14
|
+
<h2 class="text-xl font-bold">Directors</h2>
|
|
15
|
+
<p class="mt-1 text-sm text-quaternary">Select DE directors and choose their rings.</p>
|
|
16
|
+
</div>
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
aria-label="Close director assignments"
|
|
20
|
+
class="flex h-8 w-8 flex-none items-center justify-center rounded text-xl text-quaternary hover:bg-dropdownSelect focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
|
|
21
|
+
@click="close"
|
|
22
|
+
>
|
|
23
|
+
×
|
|
24
|
+
</button>
|
|
25
|
+
</div>
|
|
26
|
+
<p v-if="!draft.length" class="rounded border border-dropdownSelect p-4 text-sm text-quaternary">
|
|
27
|
+
No directors available.
|
|
28
|
+
</p>
|
|
29
|
+
<div class="space-y-3">
|
|
30
|
+
<div
|
|
31
|
+
v-for="row in draft"
|
|
32
|
+
:key="row.Director.PersonId"
|
|
33
|
+
class="flex items-center gap-3 rounded-lg border border-dropdownSelect px-3 py-3"
|
|
34
|
+
>
|
|
35
|
+
<label class="flex min-w-0 flex-1 cursor-pointer items-center gap-3">
|
|
36
|
+
<input
|
|
37
|
+
v-model="row.Selected"
|
|
38
|
+
type="checkbox"
|
|
39
|
+
:data-director-id="row.Director.PersonId"
|
|
40
|
+
:aria-label="'Include ' + row.Director.DisplayName + ' in DE'"
|
|
41
|
+
class="h-5 w-5 flex-none accent-bright"
|
|
42
|
+
/>
|
|
43
|
+
<span
|
|
44
|
+
v-if="row.Color"
|
|
45
|
+
class="h-2.5 w-2.5 flex-none rounded-full"
|
|
46
|
+
:style="{ backgroundColor: row.Color }"
|
|
47
|
+
aria-hidden="true"
|
|
48
|
+
></span>
|
|
49
|
+
<span class="min-w-0 wrap-break-word text-sm font-semibold">{{ row.Director.DisplayName }}</span>
|
|
50
|
+
</label>
|
|
51
|
+
<select
|
|
52
|
+
v-model="row.RingId"
|
|
53
|
+
:disabled="!row.Selected"
|
|
54
|
+
:data-ring-director-id="row.Director.PersonId"
|
|
55
|
+
:aria-label="'Ring for ' + row.Director.DisplayName"
|
|
56
|
+
class="w-28 flex-none rounded border border-dropdownSelect bg-white px-2 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright disabled:opacity-50"
|
|
57
|
+
>
|
|
58
|
+
<option value="">Choose ring</option>
|
|
59
|
+
<option v-for="ring in ringOptions" :key="ring.RingId" :value="ring.RingId" :disabled="ring.Disabled">
|
|
60
|
+
{{ ring.RingName }}
|
|
61
|
+
</option>
|
|
62
|
+
</select>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
<p v-if="error" role="alert" class="mt-3 text-sm text-alarm">{{ error }}</p>
|
|
66
|
+
<div class="mt-5 flex justify-end gap-2">
|
|
67
|
+
<button
|
|
68
|
+
type="button"
|
|
69
|
+
class="rounded border border-dropdownSelect px-4 py-2 text-sm hover:bg-dropdownSelect focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
|
|
70
|
+
@click="close"
|
|
71
|
+
>
|
|
72
|
+
Cancel
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
type="submit"
|
|
76
|
+
:disabled="Boolean(error)"
|
|
77
|
+
class="rounded bg-primary px-4 py-2 text-sm font-semibold text-neutral hover:bg-primaryHighlight focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright disabled:cursor-not-allowed disabled:opacity-50"
|
|
78
|
+
>
|
|
79
|
+
Save assignments
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</form>
|
|
83
|
+
</dialog>
|
|
84
|
+
</Teleport>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<script>
|
|
88
|
+
import {
|
|
89
|
+
createDirectorDraft,
|
|
90
|
+
reconcileDirectorDraft,
|
|
91
|
+
createDirectorStations,
|
|
92
|
+
getDirectorDraftError,
|
|
93
|
+
getRingOptions
|
|
94
|
+
} from '../../../Util/bracketDirectors.js';
|
|
95
|
+
|
|
96
|
+
export default {
|
|
97
|
+
name: 'BracketDirectorsModal',
|
|
98
|
+
emits: ['update:show', 'submit:stations'],
|
|
99
|
+
props: {
|
|
100
|
+
show: { type: Boolean, default: false },
|
|
101
|
+
availableDirectors: { type: Array, default: () => [] },
|
|
102
|
+
rings: { type: Array, default: () => [] },
|
|
103
|
+
stations: { type: Array, default: () => [] }
|
|
104
|
+
},
|
|
105
|
+
data() {
|
|
106
|
+
return { draft: [] };
|
|
107
|
+
},
|
|
108
|
+
computed: {
|
|
109
|
+
ringOptions() {
|
|
110
|
+
return getRingOptions(this.rings);
|
|
111
|
+
},
|
|
112
|
+
error() {
|
|
113
|
+
return getDirectorDraftError(this.draft, this.rings);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
watch: {
|
|
117
|
+
show: {
|
|
118
|
+
immediate: true,
|
|
119
|
+
handler(value) {
|
|
120
|
+
if (value) this.resetDraft();
|
|
121
|
+
this.$nextTick(this.syncDialog);
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
stations: {
|
|
125
|
+
deep: true,
|
|
126
|
+
handler() {
|
|
127
|
+
if (this.show) this.resetDraft();
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
availableDirectors: {
|
|
131
|
+
deep: true,
|
|
132
|
+
handler() {
|
|
133
|
+
if (!this.show) return;
|
|
134
|
+
this.draft = reconcileDirectorDraft(this.availableDirectors, this.stations, this.draft);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
mounted() {
|
|
139
|
+
this.syncDialog();
|
|
140
|
+
},
|
|
141
|
+
methods: {
|
|
142
|
+
resetDraft() {
|
|
143
|
+
this.draft = createDirectorDraft(this.availableDirectors, this.stations);
|
|
144
|
+
},
|
|
145
|
+
syncDialog() {
|
|
146
|
+
const dialog = this.$refs.dialog;
|
|
147
|
+
if (!dialog) return;
|
|
148
|
+
if (this.show && !dialog.open) {
|
|
149
|
+
dialog.showModal();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!this.show && dialog.open) dialog.close();
|
|
153
|
+
},
|
|
154
|
+
close() {
|
|
155
|
+
this.$emit('update:show', false);
|
|
156
|
+
},
|
|
157
|
+
submit() {
|
|
158
|
+
const stations = createDirectorStations(this.draft, this.rings);
|
|
159
|
+
if (!stations) return;
|
|
160
|
+
this.$emit('submit:stations', stations);
|
|
161
|
+
this.close();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
</script>
|
|
@@ -23,7 +23,16 @@ export default {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
export const Assigned = {
|
|
26
|
+
export const Assigned = {
|
|
27
|
+
parameters: {
|
|
28
|
+
docs: {
|
|
29
|
+
description: {
|
|
30
|
+
story:
|
|
31
|
+
'The remove control emits action:remove with a detached station payload. The parent updates planned coverage.'
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
27
36
|
|
|
28
37
|
export const AddDirector = {
|
|
29
38
|
args: {
|
|
@@ -42,4 +42,35 @@ describe('BracketDirectorAssignmentCard', () => {
|
|
|
42
42
|
expect(wrapper.emitted('action:add')).toEqual([[]]);
|
|
43
43
|
expect(wrapper.find('base-text-stub').attributes('text')).toBe('Click here to add director');
|
|
44
44
|
});
|
|
45
|
+
|
|
46
|
+
it('emits a detached station when removing a director and waits for updated props', async () => {
|
|
47
|
+
const selectedStation = structuredClone(station);
|
|
48
|
+
const wrapper = shallowMount(BracketDirectorAssignmentCard, { props: { station: selectedStation } });
|
|
49
|
+
const removeButton = wrapper.get('[data-testid="remove-bracket-director"]');
|
|
50
|
+
expect(removeButton.attributes('aria-label')).toBe('Remove Sam Rivera from Ring 3');
|
|
51
|
+
await removeButton.trigger('click');
|
|
52
|
+
const payload = wrapper.emitted('action:remove')[0][0];
|
|
53
|
+
expect(payload).toEqual(selectedStation);
|
|
54
|
+
expect(payload).not.toBe(selectedStation);
|
|
55
|
+
payload.Director.DisplayName = 'Changed';
|
|
56
|
+
expect(selectedStation.Director.DisplayName).toBe('Sam Rivera');
|
|
57
|
+
expect(wrapper.find('[data-testid="bracket-director-assignment"]').exists()).toBe(true);
|
|
58
|
+
await wrapper.setProps({ station: null });
|
|
59
|
+
expect(wrapper.find('[data-testid="bracket-director-assignment"]').exists()).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('has no remove control on the add card or an empty card', () => {
|
|
63
|
+
const addCard = shallowMount(BracketDirectorAssignmentCard, { props: { variant: 'add' } });
|
|
64
|
+
const emptyCard = shallowMount(BracketDirectorAssignmentCard);
|
|
65
|
+
expect(addCard.find('[data-testid="remove-bracket-director"]').exists()).toBe(false);
|
|
66
|
+
expect(emptyCard.find('[data-testid="remove-bracket-director"]').exists()).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('displays the ring as text and reflects parent updates', async () => {
|
|
70
|
+
const wrapper = shallowMount(BracketDirectorAssignmentCard, { props: { station } });
|
|
71
|
+
expect(wrapper.find('select').exists()).toBe(false);
|
|
72
|
+
expect(wrapper.findAll('base-text-stub').map(text => text.attributes('text'))).toContain('Ring 3');
|
|
73
|
+
await wrapper.setProps({ station: { ...station, RingId: 1, RingName: 'Ring 1' } });
|
|
74
|
+
expect(wrapper.findAll('base-text-stub').map(text => text.attributes('text'))).toContain('Ring 1');
|
|
75
|
+
});
|
|
45
76
|
});
|
package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue
CHANGED
|
@@ -45,6 +45,16 @@
|
|
|
45
45
|
<BaseText :text="ringLabel" color="quaternary" size="xs" />
|
|
46
46
|
<BaseText class="truncate" :text="directorName" color="primaryHighlight" size="sm" weight="bold" />
|
|
47
47
|
</div>
|
|
48
|
+
<button
|
|
49
|
+
type="button"
|
|
50
|
+
data-testid="remove-bracket-director"
|
|
51
|
+
:aria-label="removeLabel"
|
|
52
|
+
:title="removeLabel"
|
|
53
|
+
class="flex h-8 w-8 flex-none items-center justify-center rounded text-quaternary transition hover:bg-dropdownSelect hover:text-alarm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
|
|
54
|
+
@click.stop="removeDirector"
|
|
55
|
+
>
|
|
56
|
+
<BaseIcon iconName="fa-xmark" color="none" size="sm" />
|
|
57
|
+
</button>
|
|
48
58
|
</article>
|
|
49
59
|
</template>
|
|
50
60
|
|
|
@@ -63,7 +73,7 @@ const getInitials = displayName =>
|
|
|
63
73
|
export default {
|
|
64
74
|
name: 'BracketDirectorAssignmentCard',
|
|
65
75
|
components: { BaseIcon, BaseText },
|
|
66
|
-
emits: ['action:add'],
|
|
76
|
+
emits: ['action:add', 'action:remove'],
|
|
67
77
|
props: {
|
|
68
78
|
station: {
|
|
69
79
|
type: Object,
|
|
@@ -79,7 +89,16 @@ export default {
|
|
|
79
89
|
default: 'Click here to add director'
|
|
80
90
|
}
|
|
81
91
|
},
|
|
92
|
+
methods: {
|
|
93
|
+
removeDirector() {
|
|
94
|
+
if (!this.station) return;
|
|
95
|
+
this.$emit('action:remove', JSON.parse(JSON.stringify(this.station)));
|
|
96
|
+
}
|
|
97
|
+
},
|
|
82
98
|
computed: {
|
|
99
|
+
removeLabel() {
|
|
100
|
+
return 'Remove ' + this.directorName + ' from ' + this.ringLabel;
|
|
101
|
+
},
|
|
83
102
|
assignmentLabel() {
|
|
84
103
|
return `${this.ringLabel}, Director ${this.directorName}`;
|
|
85
104
|
},
|
package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js
CHANGED
|
@@ -26,4 +26,42 @@ describe('BracketDirectorAssignmentPanel', () => {
|
|
|
26
26
|
|
|
27
27
|
expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
|
|
28
28
|
});
|
|
29
|
+
|
|
30
|
+
it('forwards remove intent and reconciles the parent station list', async () => {
|
|
31
|
+
const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
|
|
32
|
+
wrapper.findComponent(BracketDirectorAssignmentCard).vm.$emit('action:remove', stations[0]);
|
|
33
|
+
expect(wrapper.emitted('request:remove-planned-director')).toEqual([[stations[0]]]);
|
|
34
|
+
expect(wrapper.props('stations')).toHaveLength(2);
|
|
35
|
+
await wrapper.setProps({ stations: stations.slice(1) });
|
|
36
|
+
expect(
|
|
37
|
+
wrapper
|
|
38
|
+
.findAllComponents(BracketDirectorAssignmentCard)
|
|
39
|
+
.filter(card => card.props('variant') !== 'add')
|
|
40
|
+
.map(card => card.props('station'))
|
|
41
|
+
).toEqual([stations[1]]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it.each([
|
|
45
|
+
['Manage directors', 'request:manage-planned-directors'],
|
|
46
|
+
['Redistribute scheduled bouts', 'request:redistribute-scheduled-bouts'],
|
|
47
|
+
['Refresh assignments', 'request:refresh-assignments']
|
|
48
|
+
])('emits %s intent without changing the station list', async (label, event) => {
|
|
49
|
+
const wrapper = shallowMount(BracketDirectorAssignmentPanel, {
|
|
50
|
+
props: { stations, canManageDirectors: true }
|
|
51
|
+
});
|
|
52
|
+
const button = wrapper.get('button[aria-label="' + label + '"]');
|
|
53
|
+
expect(button.attributes('title')).toBe(label);
|
|
54
|
+
await button.trigger('click');
|
|
55
|
+
|
|
56
|
+
expect(wrapper.emitted(event)).toEqual([[]]);
|
|
57
|
+
expect(Object.keys(wrapper.emitted()).filter(name => name.startsWith('request:'))).toEqual([event]);
|
|
58
|
+
expect(wrapper.props('stations')).toEqual(stations);
|
|
59
|
+
wrapper.unmount();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('hides the toolbar when management is unavailable', () => {
|
|
63
|
+
const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
|
|
64
|
+
expect(wrapper.find('[role="group"]').exists()).toBe(false);
|
|
65
|
+
wrapper.unmount();
|
|
66
|
+
});
|
|
29
67
|
});
|
package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<aside
|
|
3
3
|
data-testid="bracket-director-assignment-panel"
|
|
4
|
-
|
|
4
|
+
aria-label="Director assignments"
|
|
5
|
+
class="w-full border-b border-dropdownSelect py-4 xl:w-72 xl:border-b-0 xl:border-l xl:pl-4"
|
|
5
6
|
>
|
|
6
|
-
<
|
|
7
|
-
<
|
|
7
|
+
<header class="mb-4 space-y-3 border-b border-dropdownSelect pb-4">
|
|
8
|
+
<h2 class="text-md font-bold text-primaryHighlight">Director Assignments</h2>
|
|
9
|
+
<div v-if="canManageDirectors" class="flex flex-wrap gap-1" role="group" aria-label="Director assignment actions">
|
|
10
|
+
<button
|
|
11
|
+
v-for="action in directorActions"
|
|
12
|
+
:key="action.event"
|
|
13
|
+
type="button"
|
|
14
|
+
:data-testid="action.testId"
|
|
15
|
+
:aria-label="action.description"
|
|
16
|
+
:title="action.description"
|
|
17
|
+
class="flex min-h-9 flex-auto items-center justify-center gap-1 whitespace-nowrap rounded border border-dropdownSelect bg-white px-1 py-2 text-xs text-primaryHighlight transition hover:bg-dropdownSelect focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
|
|
18
|
+
@click="$emit(action.event)"
|
|
19
|
+
>
|
|
20
|
+
<BaseIcon :iconName="action.icon" iconStyle="fa" color="primaryHighlight" size="xs" aria-hidden="true" />
|
|
21
|
+
<span>{{ action.label }}</span>
|
|
22
|
+
</button>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
8
25
|
<BaseText
|
|
9
|
-
class="mt-1"
|
|
10
26
|
color="quaternary"
|
|
11
27
|
size="xs"
|
|
12
28
|
text="Plan ring coverage. Scheduled bouts may still be moved between rings."
|
|
13
29
|
/>
|
|
14
|
-
</
|
|
15
|
-
|
|
30
|
+
</header>
|
|
16
31
|
<div class="space-y-3">
|
|
17
|
-
<BracketDirectorAssignmentCard
|
|
32
|
+
<BracketDirectorAssignmentCard
|
|
33
|
+
v-for="station in stations"
|
|
34
|
+
:key="station.RingId"
|
|
35
|
+
:station="station"
|
|
36
|
+
@action:remove="$emit('request:remove-planned-director', $event)"
|
|
37
|
+
/>
|
|
18
38
|
<BracketDirectorAssignmentCard
|
|
19
39
|
v-if="showAddCard"
|
|
20
40
|
variant="add"
|
|
@@ -26,13 +46,47 @@
|
|
|
26
46
|
|
|
27
47
|
<script>
|
|
28
48
|
import BaseText from '../../../../Atoms/Text/BaseText.vue';
|
|
49
|
+
import BaseIcon from '../../../../Atoms/Icon/BaseIcon.vue';
|
|
29
50
|
import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
|
|
30
51
|
|
|
52
|
+
const directorActions = [
|
|
53
|
+
{
|
|
54
|
+
label: 'Manage',
|
|
55
|
+
description: 'Manage directors',
|
|
56
|
+
icon: 'fa-users',
|
|
57
|
+
event: 'request:manage-planned-directors',
|
|
58
|
+
testId: 'manage-bracket-directors'
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: 'Redistribute',
|
|
62
|
+
description: 'Redistribute scheduled bouts',
|
|
63
|
+
icon: 'fa-random',
|
|
64
|
+
event: 'request:redistribute-scheduled-bouts',
|
|
65
|
+
testId: 'redistribute-scheduled-bouts'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Refresh',
|
|
69
|
+
description: 'Refresh assignments',
|
|
70
|
+
icon: 'fa-refresh',
|
|
71
|
+
event: 'request:refresh-assignments',
|
|
72
|
+
testId: 'refresh-bracket-assignments'
|
|
73
|
+
}
|
|
74
|
+
];
|
|
31
75
|
export default {
|
|
32
76
|
name: 'BracketDirectorAssignmentPanel',
|
|
33
|
-
components: { BaseText, BracketDirectorAssignmentCard },
|
|
34
|
-
emits: [
|
|
77
|
+
components: { BaseIcon, BaseText, BracketDirectorAssignmentCard },
|
|
78
|
+
emits: [
|
|
79
|
+
'request:add-planned-director',
|
|
80
|
+
'request:remove-planned-director',
|
|
81
|
+
'request:manage-planned-directors',
|
|
82
|
+
'request:redistribute-scheduled-bouts',
|
|
83
|
+
'request:refresh-assignments'
|
|
84
|
+
],
|
|
85
|
+
data() {
|
|
86
|
+
return { directorActions };
|
|
87
|
+
},
|
|
35
88
|
props: {
|
|
89
|
+
canManageDirectors: { type: Boolean, default: false },
|
|
36
90
|
stations: {
|
|
37
91
|
type: Array,
|
|
38
92
|
default: () => []
|
|
@@ -9,7 +9,12 @@ import {
|
|
|
9
9
|
getStoryDirectorId,
|
|
10
10
|
replaceStoryBout
|
|
11
11
|
} from './BracketStoryHarness.js';
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
availableDirectors,
|
|
14
|
+
availableRings,
|
|
15
|
+
plannedDirectingStations,
|
|
16
|
+
plannedSuggestionViewerId
|
|
17
|
+
} from './BracketPlannedSuggestions.js';
|
|
13
18
|
//import getDEWithBouts from '../../../../mocks/getDEWithBouts.js';
|
|
14
19
|
import getDEWithBouts6Fencers from '../../../../mocks/getDeBouts-6fencers.js';
|
|
15
20
|
import getDEWithBouts12Fencers from '../../../../mocks/getDeBouts-12fencers.js';
|
|
@@ -29,8 +34,9 @@ const renderInteractiveBracket = args => ({
|
|
|
29
34
|
const storyStations = ref(cloneStations(args.plannedDirectingStations));
|
|
30
35
|
const storyAssignments = ref([]);
|
|
31
36
|
const canAddDirector = computed(() =>
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
(args.availableDirectors || availableDirectors).some(
|
|
38
|
+
director =>
|
|
39
|
+
!storyStations.value.some(station => String(station.Director?.PersonId) === String(director.PersonId))
|
|
34
40
|
)
|
|
35
41
|
);
|
|
36
42
|
const redistributeAssignments = () => {
|
|
@@ -93,7 +99,11 @@ const renderInteractiveBracket = args => ({
|
|
|
93
99
|
};
|
|
94
100
|
|
|
95
101
|
const getViewerStation = () =>
|
|
96
|
-
|
|
102
|
+
[
|
|
103
|
+
...storyStations.value,
|
|
104
|
+
...plannedDirectingStations,
|
|
105
|
+
...(args.availableDirectors || []).map(Director => ({ Director }))
|
|
106
|
+
].find(station => String(station.Director?.PersonId) === String(args.viewerPersonId));
|
|
97
107
|
|
|
98
108
|
const handleAddPlannedDirector = () => {
|
|
99
109
|
const assignedRingIds = new Set(storyStations.value.map(station => String(station.RingId)));
|
|
@@ -125,6 +135,21 @@ const renderInteractiveBracket = args => ({
|
|
|
125
135
|
requestBoutDirector,
|
|
126
136
|
assignPlannedDirector,
|
|
127
137
|
handleAddPlannedDirector,
|
|
138
|
+
redistributeAssignments,
|
|
139
|
+
refreshAssignments() {
|
|
140
|
+
storyStations.value = cloneStations(args.plannedDirectingStations);
|
|
141
|
+
applyProvidedAssignments(args.plannedBoutAssignments);
|
|
142
|
+
},
|
|
143
|
+
handleRemovePlannedDirector(station) {
|
|
144
|
+
const remainingStations = storyStations.value.filter(item => String(item.RingId) !== String(station?.RingId));
|
|
145
|
+
if (remainingStations.length === storyStations.value.length) return;
|
|
146
|
+
storyStations.value = remainingStations;
|
|
147
|
+
redistributeAssignments();
|
|
148
|
+
},
|
|
149
|
+
handlePlannedDirectorsUpdate(stations) {
|
|
150
|
+
storyStations.value = cloneStations(stations);
|
|
151
|
+
redistributeAssignments();
|
|
152
|
+
},
|
|
128
153
|
handleCloseDirectingBout,
|
|
129
154
|
handleDirectingBout,
|
|
130
155
|
persistStoryBout
|
|
@@ -154,8 +179,12 @@ const renderInteractiveBracket = args => ({
|
|
|
154
179
|
:plannedBoutAssignments="storyAssignments"
|
|
155
180
|
:showAddPlannedDirectorCard="args.showAddPlannedDirectorCard && canAddDirector"
|
|
156
181
|
@request:bout-director="requestBoutDirector"
|
|
182
|
+
@request:redistribute-scheduled-bouts="redistributeAssignments"
|
|
183
|
+
@request:refresh-assignments="refreshAssignments"
|
|
157
184
|
@close:directing-bout="handleCloseDirectingBout"
|
|
158
185
|
@request:add-planned-director="handleAddPlannedDirector"
|
|
186
|
+
@request:remove-planned-director="handleRemovePlannedDirector"
|
|
187
|
+
@update:planned-directing-stations="handlePlannedDirectorsUpdate"
|
|
159
188
|
@update:directing-bout="handleDirectingBout"
|
|
160
189
|
@request:bout-active="handleDirectingBout"
|
|
161
190
|
@update:bout="persistStoryBout"
|
|
@@ -171,6 +200,8 @@ export default {
|
|
|
171
200
|
tags: ['Bracket'],
|
|
172
201
|
render: renderInteractiveBracket,
|
|
173
202
|
args: {
|
|
203
|
+
availableDirectors,
|
|
204
|
+
rings: availableRings,
|
|
174
205
|
bouts: getDEWithBouts.pools[0].Bouts,
|
|
175
206
|
eventRules: getDEWithBouts.eventRules,
|
|
176
207
|
hostingClubColors: getDEWithBouts.hostingClubColors,
|
|
@@ -184,6 +215,17 @@ export default {
|
|
|
184
215
|
showBoutDirectorAssignment: true
|
|
185
216
|
},
|
|
186
217
|
argTypes: {
|
|
218
|
+
'request:redistribute-scheduled-bouts': {
|
|
219
|
+
description: 'Parent intent to redistribute scheduled coverage without changing active or completed bouts.'
|
|
220
|
+
},
|
|
221
|
+
'request:refresh-assignments': {
|
|
222
|
+
description: 'Parent intent to reload stations and assignments. This story reloads the supplied args fixtures.'
|
|
223
|
+
},
|
|
224
|
+
availableDirectors: {
|
|
225
|
+
control: 'object',
|
|
226
|
+
description: 'Eligible staff, including DE-only directors, supplied by the parent.'
|
|
227
|
+
},
|
|
228
|
+
rings: { control: 'object', description: 'Available rings supplied by the parent.' },
|
|
187
229
|
showBoutDirectorAssignment: {
|
|
188
230
|
control: 'boolean',
|
|
189
231
|
description: 'Include Assign director in the pairing hover menu. The parent supplies staff selection and saving.'
|