@dcrackel/hematournamentui 1.0.714 → 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.
@@ -681,6 +681,9 @@
681
681
  .min-h-5 {
682
682
  min-height: calc(var(--spacing) * 5);
683
683
  }
684
+ .min-h-9 {
685
+ min-height: calc(var(--spacing) * 9);
686
+ }
684
687
  .min-h-11 {
685
688
  min-height: calc(var(--spacing) * 11);
686
689
  }
@@ -699,6 +702,9 @@
699
702
  .min-h-\[320px\] {
700
703
  min-height: 320px;
701
704
  }
705
+ .min-h-full {
706
+ min-height: 100%;
707
+ }
702
708
  .min-h-screen {
703
709
  min-height: 100vh;
704
710
  }
@@ -894,6 +900,9 @@
894
900
  .flex-1 {
895
901
  flex: 1;
896
902
  }
903
+ .flex-auto {
904
+ flex: auto;
905
+ }
897
906
  .flex-none {
898
907
  flex: none;
899
908
  }
@@ -2641,19 +2650,14 @@
2641
2650
  width: calc(var(--spacing) * 12);
2642
2651
  }
2643
2652
  }
2644
- .xl\:sticky {
2645
- @media (width >= 80rem) {
2646
- position: sticky;
2647
- }
2648
- }
2649
- .xl\:top-4 {
2653
+ .xl\:order-last {
2650
2654
  @media (width >= 80rem) {
2651
- top: calc(var(--spacing) * 4);
2655
+ order: 9999;
2652
2656
  }
2653
2657
  }
2654
- .xl\:order-last {
2658
+ .xl\:min-h-\[calc\(100dvh-2rem\)\] {
2655
2659
  @media (width >= 80rem) {
2656
- order: 9999;
2660
+ min-height: calc(100dvh - 2rem);
2657
2661
  }
2658
2662
  }
2659
2663
  .xl\:w-72 {
@@ -2666,6 +2670,23 @@
2666
2670
  flex-direction: row;
2667
2671
  }
2668
2672
  }
2673
+ .xl\:border-b-0 {
2674
+ @media (width >= 80rem) {
2675
+ border-bottom-style: var(--tw-border-style);
2676
+ border-bottom-width: 0px;
2677
+ }
2678
+ }
2679
+ .xl\:border-l {
2680
+ @media (width >= 80rem) {
2681
+ border-left-style: var(--tw-border-style);
2682
+ border-left-width: 1px;
2683
+ }
2684
+ }
2685
+ .xl\:pl-4 {
2686
+ @media (width >= 80rem) {
2687
+ padding-left: calc(var(--spacing) * 4);
2688
+ }
2689
+ }
2669
2690
  }
2670
2691
  @font-face {
2671
2692
  font-family: 'Inter';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dcrackel/hematournamentui",
3
3
  "private": false,
4
- "version": "1.0.714",
4
+ "version": "1.0.715",
5
5
  "type": "module",
6
6
  "main": "dist/HemaTournamentUI-lib.umd.js",
7
7
  "module": "dist/HemaTournamentUI-lib.es.js",
@@ -41,11 +41,27 @@ describe('BracketDirectorAssignmentPanel', () => {
41
41
  ).toEqual([stations[1]]);
42
42
  });
43
43
 
44
- it('opens director management', async () => {
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) => {
45
49
  const wrapper = shallowMount(BracketDirectorAssignmentPanel, {
46
50
  props: { stations, canManageDirectors: true }
47
51
  });
48
- await wrapper.get('[data-testid="manage-bracket-directors"]').trigger('click');
49
- expect(wrapper.emitted('request:manage-planned-directors')).toEqual([[]]);
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();
50
66
  });
51
67
  });
@@ -1,29 +1,33 @@
1
1
  <template>
2
2
  <aside
3
3
  data-testid="bracket-director-assignment-panel"
4
- class="w-full rounded-xl border border-dropdownSelect bg-bannerBackground p-4 shadow-lg xl:sticky xl:top-4 xl:w-72"
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
- <div class="mb-4 flex items-start justify-between gap-3">
7
- <div>
8
- <BaseText color="primaryHighlight" size="md" text="Director Assignments" weight="bold" />
9
- <BaseText
10
- class="mt-1"
11
- color="quaternary"
12
- size="xs"
13
- text="Plan ring coverage. Scheduled bouts may still be moved between rings."
14
- />
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>
15
23
  </div>
16
- <button
17
- v-if="canManageDirectors"
18
- type="button"
19
- data-testid="manage-bracket-directors"
20
- class="rounded border border-dropdownSelect bg-white px-2 py-1 text-xs text-primaryHighlight hover:bg-dropdownSelect focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-bright"
21
- @click="$emit('request:manage-planned-directors')"
22
- >
23
- Manage
24
- </button>
25
- </div>
26
24
 
25
+ <BaseText
26
+ color="quaternary"
27
+ size="xs"
28
+ text="Plan ring coverage. Scheduled bouts may still be moved between rings."
29
+ />
30
+ </header>
27
31
  <div class="space-y-3">
28
32
  <BracketDirectorAssignmentCard
29
33
  v-for="station in stations"
@@ -42,12 +46,45 @@
42
46
 
43
47
  <script>
44
48
  import BaseText from '../../../../Atoms/Text/BaseText.vue';
49
+ import BaseIcon from '../../../../Atoms/Icon/BaseIcon.vue';
45
50
  import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
46
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
+ ];
47
75
  export default {
48
76
  name: 'BracketDirectorAssignmentPanel',
49
- components: { BaseText, BracketDirectorAssignmentCard },
50
- emits: ['request:add-planned-director', 'request:remove-planned-director', 'request:manage-planned-directors'],
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
+ },
51
88
  props: {
52
89
  canManageDirectors: { type: Boolean, default: false },
53
90
  stations: {
@@ -135,6 +135,11 @@ const renderInteractiveBracket = args => ({
135
135
  requestBoutDirector,
136
136
  assignPlannedDirector,
137
137
  handleAddPlannedDirector,
138
+ redistributeAssignments,
139
+ refreshAssignments() {
140
+ storyStations.value = cloneStations(args.plannedDirectingStations);
141
+ applyProvidedAssignments(args.plannedBoutAssignments);
142
+ },
138
143
  handleRemovePlannedDirector(station) {
139
144
  const remainingStations = storyStations.value.filter(item => String(item.RingId) !== String(station?.RingId));
140
145
  if (remainingStations.length === storyStations.value.length) return;
@@ -174,6 +179,8 @@ const renderInteractiveBracket = args => ({
174
179
  :plannedBoutAssignments="storyAssignments"
175
180
  :showAddPlannedDirectorCard="args.showAddPlannedDirectorCard && canAddDirector"
176
181
  @request:bout-director="requestBoutDirector"
182
+ @request:redistribute-scheduled-bouts="redistributeAssignments"
183
+ @request:refresh-assignments="refreshAssignments"
177
184
  @close:directing-bout="handleCloseDirectingBout"
178
185
  @request:add-planned-director="handleAddPlannedDirector"
179
186
  @request:remove-planned-director="handleRemovePlannedDirector"
@@ -208,6 +215,12 @@ export default {
208
215
  showBoutDirectorAssignment: true
209
216
  },
210
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
+ },
211
224
  availableDirectors: {
212
225
  control: 'object',
213
226
  description: 'Eligible staff, including DE-only directors, supplied by the parent.'
@@ -216,3 +216,29 @@ describe('Bracket assignment defaults', () => {
216
216
  wrapper.unmount();
217
217
  });
218
218
  });
219
+
220
+ describe('Bracket assignment toolbar', () => {
221
+ it.each(['request:redistribute-scheduled-bouts', 'request:refresh-assignments'])(
222
+ 'forwards %s without changing bouts or planned coverage',
223
+ async event => {
224
+ const bouts = [createBout(), createBout({ BoutId: 43, Status: 'Active', RefereeId: 99 })];
225
+ const stations = [{ RingId: 1, Director: { PersonId: 99, DisplayName: 'Dave Smith' } }];
226
+ const assignments = [{ DEBoutId: 1, RingId: 1 }];
227
+ const wrapper = mountBracket(bouts, {
228
+ availableDirectors: [stations[0].Director],
229
+ rings: [{ RingId: 1 }],
230
+ plannedDirectingStations: stations,
231
+ plannedBoutAssignments: assignments
232
+ });
233
+
234
+ wrapper.findComponent(BracketDirectorAssignmentPanel).vm.$emit(event);
235
+ await wrapper.vm.$nextTick();
236
+
237
+ expect(wrapper.emitted()).toEqual({ [event]: [[]] });
238
+ expect(wrapper.vm.boutsData).toEqual(bouts);
239
+ expect(wrapper.props('plannedDirectingStations')).toEqual(stations);
240
+ expect(wrapper.props('plannedBoutAssignments')).toEqual(assignments);
241
+ wrapper.unmount();
242
+ }
243
+ );
244
+ });
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <section>
2
+ <section class="flex min-h-full flex-col xl:min-h-[calc(100dvh-2rem)]">
3
3
  <EditEventsTopMenu :currentTab="'Bracket'" :tabs="tabs(role, isMobile)" @tabMenuClick="handleTabMenuClick" />
4
4
  <ToggleHeader
5
5
  :currentSelection="viewName"
@@ -17,7 +17,7 @@
17
17
  :statusText="connectedToServer ? 'Connected' : 'Disconnected'"
18
18
  />
19
19
 
20
- <div class="flex flex-col gap-4 xl:flex-row">
20
+ <div class="flex flex-1 flex-col gap-4 xl:flex-row">
21
21
  <div class="min-w-0 flex-1">
22
22
  <Tableau
23
23
  :bouts="sortedBouts"
@@ -42,6 +42,8 @@
42
42
  :showAddCard="showAddPlannedDirectorCard"
43
43
  :canManageDirectors="canManagePlannedDirectors"
44
44
  @request:manage-planned-directors="handleManagePlannedDirectors"
45
+ @request:redistribute-scheduled-bouts="$emit('request:redistribute-scheduled-bouts')"
46
+ @request:refresh-assignments="$emit('request:refresh-assignments')"
45
47
  @request:add-planned-director="handleAddPlannedDirector"
46
48
  @request:remove-planned-director="$emit('request:remove-planned-director', $event)"
47
49
  />
@@ -136,6 +138,8 @@ export default {
136
138
  'penalty:update',
137
139
  'request:add-planned-director',
138
140
  'request:remove-planned-director',
141
+ 'request:redistribute-scheduled-bouts',
142
+ 'request:refresh-assignments',
139
143
  'update:planned-directing-stations',
140
144
  'request:bout-active',
141
145
  'request:bout-director',