@dcrackel/hematournamentui 1.0.706 → 1.0.708

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 (350) hide show
  1. package/.prettierignore +7 -1
  2. package/.storybook/main.js +5 -12
  3. package/.storybook/preview-head.html +10 -3
  4. package/.storybook/preview.js +5 -13
  5. package/AGENTS.md +118 -88
  6. package/README.md +29 -22
  7. package/dist/HemaTournamentUI-lib.es.js +8394 -8807
  8. package/dist/HemaTournamentUI-lib.umd.js +27 -27
  9. package/dist/hematournamentui-tailwind.css +183 -428
  10. package/dist/hematournamentui.css +1 -1
  11. package/eslint.config.js +49 -23
  12. package/package.json +15 -11
  13. package/postcss.config.js +3 -3
  14. package/prettier.config.js +16 -10
  15. package/src/config/boutScreenConfigurations.js +52 -50
  16. package/src/index.js +2 -0
  17. package/src/main.js +1 -1
  18. package/src/mocks/EventQuestionsMock.js +43 -55
  19. package/src/mocks/assignments.js +180 -181
  20. package/src/mocks/clubGetAllMock.js +8143 -8143
  21. package/src/mocks/eventMock.js +205 -205
  22. package/src/mocks/eventPersonGetInitalPoolAssignmentsLivePools.js +541 -542
  23. package/src/mocks/eventPersonGetInitalPoolAssignmentsPreparing.js +522 -523
  24. package/src/mocks/eventPersonGetInitialPoolAssignments.js +386 -387
  25. package/src/mocks/eventPersonGetInitialPoolAssignments2.js +2267 -2268
  26. package/src/mocks/eventPersonGetInitialStaff.js +94 -95
  27. package/src/mocks/eventPersonGetPoolAssignmentEvent.js +161 -162
  28. package/src/mocks/eventRules.js +87 -86
  29. package/src/mocks/eventpersonGetEventById.js +1737 -1737
  30. package/src/mocks/getAllClubs.js +8143 -8143
  31. package/src/mocks/getAllStaffByTournamentId.js +74 -75
  32. package/src/mocks/getAllWeapons.js +81 -82
  33. package/src/mocks/getAttendance.js +43 -44
  34. package/src/mocks/getClub.js +43 -44
  35. package/src/mocks/getClubDisplayByClubId.js +104 -105
  36. package/src/mocks/getDEBouts-128Fencers.js +3247 -3248
  37. package/src/mocks/getDEBouts-42Fencers.js +3736 -3737
  38. package/src/mocks/getDEBouts-67Fencers.js +6684 -6690
  39. package/src/mocks/getDEBouts-DoubleElim.js +4198 -4199
  40. package/src/mocks/getDEBouts-TableOf128.js +37 -0
  41. package/src/mocks/getDEBouts-TableOf64.js +35 -0
  42. package/src/mocks/getDEWithBouts.js +628 -629
  43. package/src/mocks/getDeBouts-12fencers.js +1255 -1256
  44. package/src/mocks/getDeBouts-15fencers.js +925 -927
  45. package/src/mocks/getDeBouts-22fencers.js +1683 -1685
  46. package/src/mocks/getDeBouts-6fencers.js +818 -818
  47. package/src/mocks/getEventByTournamentId.js +47 -48
  48. package/src/mocks/getFinalEventResult.js +304 -305
  49. package/src/mocks/getPenaltiesByEvent.js +655 -656
  50. package/src/mocks/getPoolResults.js +1533 -1534
  51. package/src/mocks/getPoolsWithBouts.js +662 -663
  52. package/src/mocks/getPoolsWithBouts6PoolId139.js +885 -886
  53. package/src/mocks/getPoolsWithBoutsByPoolId.js +841 -841
  54. package/src/mocks/getPoolsWithBoutsClubColors.js +4 -4
  55. package/src/mocks/getPoolsWithBoutsComplete.js +919 -921
  56. package/src/mocks/locationMock.js +3213 -3214
  57. package/src/mocks/mockRoutes.js +3 -3
  58. package/src/mocks/personGetAllMock.js +2536 -2537
  59. package/src/mocks/personsMock.js +17569 -17570
  60. package/src/mocks/singleEventMock.js +249 -250
  61. package/src/mocks/tournamentMock.js +1988 -1927
  62. package/src/stories/Atoms/CheckBox/BaseCheckbox.stories.js +25 -23
  63. package/src/stories/Atoms/CheckBox/BaseCheckbox.vue +30 -24
  64. package/src/stories/Atoms/Icon/BaseIcon.stories.js +63 -64
  65. package/src/stories/Atoms/Icon/BaseIcon.test.js +67 -69
  66. package/src/stories/Atoms/Icon/BaseIcon.vue +2 -3
  67. package/src/stories/Atoms/Input/BaseInput.stories.js +18 -18
  68. package/src/stories/Atoms/Input/BaseInput.test.js +65 -67
  69. package/src/stories/Atoms/Input/BaseInput.vue +36 -31
  70. package/src/stories/Atoms/RadioGroup/BaseRadioGroup.stories.js +21 -21
  71. package/src/stories/Atoms/RadioGroup/BaseRadioGroup.test.js +31 -31
  72. package/src/stories/Atoms/RadioGroup/BaseRadioGroup.vue +23 -18
  73. package/src/stories/Atoms/Tag/BaseTag.stories.js +21 -21
  74. package/src/stories/Atoms/Tag/BaseTag.test.js +26 -30
  75. package/src/stories/Atoms/Tag/BaseTag.vue +18 -19
  76. package/src/stories/Atoms/Text/BaseText.stories.js +54 -62
  77. package/src/stories/Atoms/Text/BaseText.test.js +152 -154
  78. package/src/stories/Atoms/Text/BaseText.vue +103 -81
  79. package/src/stories/Configure.mdx +5 -3
  80. package/src/stories/Molecules/Boxes/BoutBoxes/HandshotCounterBox/HandshotCounterBox.stories.js +24 -24
  81. package/src/stories/Molecules/Boxes/BoutBoxes/HandshotCounterBox/HandshotCounterBox.vue +33 -15
  82. package/src/stories/Molecules/Boxes/BoutBoxes/PassesBox/PassesBox.stories.js +46 -48
  83. package/src/stories/Molecules/Boxes/BoutBoxes/PassesBox/PassesBox.vue +38 -25
  84. package/src/stories/Molecules/Boxes/BoutBoxes/PenaltyConfirmationBox/PenaltyConfirmationBox.stories.js +4 -4
  85. package/src/stories/Molecules/Boxes/BoutBoxes/PenaltyConfirmationBox/PenaltyConfirmationBox.vue +9 -4
  86. package/src/stories/Molecules/Boxes/BoutBoxes/ScoreBox/ScoreBox.stories.js +90 -90
  87. package/src/stories/Molecules/Boxes/BoutBoxes/ScoreBox/ScoreBox.vue +51 -32
  88. package/src/stories/Molecules/Boxes/BoutBoxes/TimerBox/TimerBox.stories.js +25 -25
  89. package/src/stories/Molecules/Boxes/BoutBoxes/TimerBox/TimerBox.vue +47 -12
  90. package/src/stories/Molecules/Boxes/CounterBox/CounterBox.stories.js +19 -19
  91. package/src/stories/Molecules/Boxes/CounterBox/CounterBox.vue +25 -19
  92. package/src/stories/Molecules/Boxes/EventStatusBox/EventStatusBox.stories.js +25 -28
  93. package/src/stories/Molecules/Boxes/EventStatusBox/EventStatusBox.vue +7 -7
  94. package/src/stories/Molecules/Breadcrumb/Admin/Breadcrumb.stories.js +28 -29
  95. package/src/stories/Molecules/Breadcrumb/Admin/Breadcrumb.test.js +58 -58
  96. package/src/stories/Molecules/Breadcrumb/Admin/Breadcrumb.vue +15 -15
  97. package/src/stories/Molecules/Buttons/BaseButton/BaseButton.stories.js +86 -86
  98. package/src/stories/Molecules/Buttons/BaseButton/BaseButton.test.js +101 -103
  99. package/src/stories/Molecules/Buttons/BaseButton/BaseButton.vue +87 -24
  100. package/src/stories/Molecules/Buttons/ButtonBar/ButtonBar.stories.js +31 -31
  101. package/src/stories/Molecules/Buttons/ButtonBar/ButtonBar.vue +19 -21
  102. package/src/stories/Molecules/CombinationInputs/TitledInput/TitledInput.stories.js +57 -57
  103. package/src/stories/Molecules/CombinationInputs/TitledInput/TitledInput.test.js +64 -64
  104. package/src/stories/Molecules/CombinationInputs/TitledInput/TitledInput.vue +17 -16
  105. package/src/stories/Molecules/Filters/FilterAndSortBar/FilterAndSortBar.stories.js +19 -19
  106. package/src/stories/Molecules/Filters/FilterAndSortBar/FilterAndSortBar.vue +18 -7
  107. package/src/stories/Molecules/Filters/FilterUpcomingPast/FilterUpcomingPast.stories.js +16 -18
  108. package/src/stories/Molecules/Filters/FilterUpcomingPast/FilterUpcomingPast.vue +31 -13
  109. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -0
  110. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -0
  111. package/src/stories/Molecules/Indicators/ServerConnected/ServerConnected.stories.js +27 -27
  112. package/src/stories/Molecules/Indicators/ServerConnected/ServerConnected.vue +6 -5
  113. package/src/stories/Molecules/Inputs/MultiSelect/MultiSelect.stories.js +35 -35
  114. package/src/stories/Molecules/Inputs/MultiSelect/MultiSelect.vue +13 -8
  115. package/src/stories/Molecules/Inputs/Toggle/Toggle.stories.js +27 -27
  116. package/src/stories/Molecules/Inputs/Toggle/Toggle.vue +8 -5
  117. package/src/stories/Molecules/Menus/TabBar/TabBar.stories.js +18 -18
  118. package/src/stories/Molecules/Menus/TabBar/TabBar.vue +1 -2
  119. package/src/stories/Molecules/Modals/AddDisplayModal/AddDisplay.stories.js +36 -27
  120. package/src/stories/Molecules/Modals/AddDisplayModal/AddDisplayModal.vue +25 -15
  121. package/src/stories/Molecules/Modals/BaseModal/BaseModal.stories.js +66 -65
  122. package/src/stories/Molecules/Modals/BaseModal/BaseModal.vue +47 -9
  123. package/src/stories/Molecules/Modals/ConfirmModal/ConfirmModal.stories.js +23 -23
  124. package/src/stories/Molecules/Modals/ConfirmModal/ConfirmModal.vue +59 -11
  125. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +37 -37
  126. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -0
  127. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +29 -23
  128. package/src/stories/Molecules/Modals/EditBoutModal/EditBoutModal.stories.js +41 -41
  129. package/src/stories/Molecules/Modals/EditBoutModal/EditBoutModal.vue +28 -16
  130. package/src/stories/Molecules/Modals/EventWizardModal/EventWizardModal.stories.js +29 -30
  131. package/src/stories/Molecules/Modals/EventWizardModal/EventWizardModal.vue +18 -16
  132. package/src/stories/Molecules/Modals/LoadingModal/LoadingModal.stories.js +21 -21
  133. package/src/stories/Molecules/Modals/LoadingModal/LoadingModal.vue +7 -7
  134. package/src/stories/Molecules/Modals/PenaltyModal/PenaltyModal.stories.js +24 -24
  135. package/src/stories/Molecules/Modals/PenaltyModal/PenaltyModal.vue +30 -25
  136. package/src/stories/Molecules/Reorder/ReorderableShell.stories.js +81 -77
  137. package/src/stories/Molecules/Reorder/ReorderableShell.vue +46 -43
  138. package/src/stories/Molecules/StepIndicator/BaseStepIndicator/StepIndicator.stories.js +40 -40
  139. package/src/stories/Molecules/StepIndicator/BaseStepIndicator/StepIndicator.vue +23 -25
  140. package/src/stories/Organisms/Cards/BoutCard/BoutCard.stories.js +34 -29
  141. package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +69 -66
  142. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.stories.js +24 -22
  143. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +53 -41
  144. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.stories.js +24 -37
  145. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +88 -103
  146. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardReorder/BoutCardReorder.vue +13 -14
  147. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardWaiting/BoutCardWaiting.stories.js +5 -5
  148. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardWaiting/BoutCardWaiting.vue +22 -21
  149. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -0
  150. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -0
  151. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -0
  152. package/src/stories/Organisms/Cards/ClubCard/ClubCard.stories.js +14 -17
  153. package/src/stories/Organisms/Cards/ClubCard/ClubCard.vue +25 -18
  154. package/src/stories/Organisms/Cards/Director/DirectorCard.stories.js +23 -23
  155. package/src/stories/Organisms/Cards/Director/DirectorCard.vue +159 -103
  156. package/src/stories/Organisms/Cards/EditBout/EditBout.stories.js +47 -47
  157. package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -0
  158. package/src/stories/Organisms/Cards/EditBout/EditBout.vue +82 -54
  159. package/src/stories/Organisms/Cards/EventCard/EventCard.stories.js +9 -10
  160. package/src/stories/Organisms/Cards/EventCard/EventCard.vue +83 -63
  161. package/src/stories/Organisms/Cards/FencerCard/FencerCard.stories.js +105 -105
  162. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -194
  163. package/src/stories/Organisms/Cards/FencerFinalResultCard/FencerFinalResultCard.stories.js +14 -14
  164. package/src/stories/Organisms/Cards/FencerFinalResultCard/FencerFinalResultCard.vue +37 -26
  165. package/src/stories/Organisms/Cards/FencerPoolResultsCard/FencerPoolResults.stories.js +24 -24
  166. package/src/stories/Organisms/Cards/FencerPoolResultsCard/FencerPoolResultsCard.vue +95 -28
  167. package/src/stories/Organisms/Cards/ImportFencerCard/ImportFencerCard.stories.js +59 -60
  168. package/src/stories/Organisms/Cards/ImportFencerCard/ImportFencerCard.vue +13 -7
  169. package/src/stories/Organisms/Cards/KioskCard/KioskCard.stories.js +34 -34
  170. package/src/stories/Organisms/Cards/KioskCard/KioskCard.vue +149 -104
  171. package/src/stories/Organisms/Cards/PenaltyCard/PenaltyCard.stories.js +19 -20
  172. package/src/stories/Organisms/Cards/PenaltyCard/PenaltyCard.vue +207 -163
  173. package/src/stories/Organisms/Cards/PenaltyFencerCard/PenaltyFencerCard.stories.js +17 -17
  174. package/src/stories/Organisms/Cards/PenaltyFencerCard/PenaltyFencerCard.vue +88 -46
  175. package/src/stories/Organisms/Cards/PoolBoutMatchCard/PoolBoutMatchCard.stories.js +182 -182
  176. package/src/stories/Organisms/Cards/PoolBoutMatchCard/PoolBoutMatchCard.vue +21 -12
  177. package/src/stories/Organisms/Cards/PoolFencerCard/PoolFencerCard.stories.js +25 -28
  178. package/src/stories/Organisms/Cards/PoolFencerCard/PoolFencerCard.vue +28 -22
  179. package/src/stories/Organisms/Cards/StaffCard/StaffCard.stories.js +62 -62
  180. package/src/stories/Organisms/Cards/StaffCard/StaffCard.vue +27 -22
  181. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +60 -98
  182. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +143 -0
  183. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +256 -202
  184. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -93
  185. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -111
  186. package/src/stories/Organisms/Cards/TournamentAdminCard/TournamentAdmin.stories.js +20 -20
  187. package/src/stories/Organisms/Cards/TournamentAdminCard/TournamentAdminCard.vue +62 -33
  188. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +56 -32
  189. package/src/stories/Organisms/ComplexInputs/AddressAutocomplete/AddressAutocomplete.stories.js +6 -7
  190. package/src/stories/Organisms/ComplexInputs/AddressAutocomplete/AddressAutocomplete.vue +7 -8
  191. package/src/stories/Organisms/ComplexInputs/ClubColorPicker/ClubColorPicker.stories.js +31 -31
  192. package/src/stories/Organisms/ComplexInputs/ClubColorPicker/ClubColorPicker.vue +30 -35
  193. package/src/stories/Organisms/ComplexInputs/DatePicker/DatePicker.stories.js +13 -16
  194. package/src/stories/Organisms/ComplexInputs/DatePicker/DatePicker.vue +68 -70
  195. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.stories.js +32 -34
  196. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +48 -25
  197. package/src/stories/Organisms/ComplexInputs/FindLocation/FindLocation.stories.js +21 -21
  198. package/src/stories/Organisms/ComplexInputs/FindLocation/FindLocation.vue +65 -38
  199. package/src/stories/Organisms/ComplexInputs/FindOrAddPerson/FindOrAddPerson.stories.js +19 -20
  200. package/src/stories/Organisms/ComplexInputs/FindOrAddPerson/FindOrAddPerson.vue +46 -21
  201. package/src/stories/Organisms/ComplexInputs/FindPerson/FindPerson.stories.js +22 -22
  202. package/src/stories/Organisms/ComplexInputs/FindPerson/FindPerson.vue +68 -23
  203. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +13 -13
  204. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +25 -24
  205. package/src/stories/Organisms/ComplexInputs/TextBoxEditor/TextBoxEditor.stories.js +6 -10
  206. package/src/stories/Organisms/ComplexInputs/TextBoxEditor/TextBoxEditor.vue +112 -48
  207. package/src/stories/Organisms/Containers/BoutsSection/BoutsSection.stories.js +43 -44
  208. package/src/stories/Organisms/Containers/BoutsSection/BoutsSection.vue +30 -23
  209. package/src/stories/Organisms/Containers/FinalResults/FinalResultsTable.stories.js +20 -20
  210. package/src/stories/Organisms/Containers/FinalResults/FinalResultsTable.vue +12 -12
  211. package/src/stories/Organisms/Containers/KioskContainer/KioskContainer.stories.js +24 -25
  212. package/src/stories/Organisms/Containers/KioskContainer/KioskContainer.vue +112 -87
  213. package/src/stories/Organisms/Containers/Pool/Pool.stories.js +54 -54
  214. package/src/stories/Organisms/Containers/Pool/Pool.vue +64 -41
  215. package/src/stories/Organisms/Containers/PoolGrid/PoolGrid.stories.js +17 -19
  216. package/src/stories/Organisms/Containers/PoolGrid/PoolGrid.vue +189 -135
  217. package/src/stories/Organisms/Containers/PoolResults/PoolResultsTable.stories.js +53 -54
  218. package/src/stories/Organisms/Containers/PoolResults/PoolResultsTable.vue +107 -73
  219. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +23 -24
  220. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +177 -10
  221. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +319 -198
  222. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -0
  223. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -0
  224. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -0
  225. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -0
  226. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -0
  227. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -0
  228. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +57 -0
  229. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +65 -0
  230. package/src/stories/Organisms/Containers/TableauColumn/TabeauColumn.stories.js +26 -27
  231. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +31 -26
  232. package/src/stories/Organisms/Form/AddDisplay/AddDisplay.stories.js +26 -26
  233. package/src/stories/Organisms/Form/AddDisplay/AddDisplay.vue +130 -118
  234. package/src/stories/Organisms/Form/AddNewPerson/AddNewPerson.stories.js +14 -15
  235. package/src/stories/Organisms/Form/AddNewPerson/AddNewPerson.vue +94 -88
  236. package/src/stories/Organisms/Grids/GridContainer.stories.js +101 -101
  237. package/src/stories/Organisms/Grids/GridContainer.vue +160 -136
  238. package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.stories.js +47 -47
  239. package/src/stories/Organisms/Headers/AttendanceSummary/AttendanceSummary.vue +65 -54
  240. package/src/stories/Organisms/Headers/EventFinalResultsHeader/EventFinalResultsHeader.stories.js +27 -27
  241. package/src/stories/Organisms/Headers/EventFinalResultsHeader/EventFinalResultsHeader.vue +38 -22
  242. package/src/stories/Organisms/Headers/PenaltySummary/PenaltySummary.stories.js +80 -80
  243. package/src/stories/Organisms/Headers/PenaltySummary/PenaltySummary.vue +14 -6
  244. package/src/stories/Organisms/Headers/PoolResultsHeader/PoolResultsHeader.stories.js +34 -34
  245. package/src/stories/Organisms/Headers/PoolResultsHeader/PoolResultsHeader.vue +36 -36
  246. package/src/stories/Organisms/Headers/PoolSummary/PoolSummary.stories.js +21 -21
  247. package/src/stories/Organisms/Headers/PoolSummary/PoolSummary.vue +56 -29
  248. package/src/stories/Organisms/Headers/ToggleHeader/ToggleHeader.stories.js +38 -38
  249. package/src/stories/Organisms/Headers/ToggleHeader/ToggleHeader.vue +19 -20
  250. package/src/stories/Organisms/RankCalculator/ByLetter/RankCalculator.stories.js +12 -13
  251. package/src/stories/Organisms/RankCalculator/ByLetter/RankCalculator.vue +47 -41
  252. package/src/stories/Organisms/RankCalculator/ByPoints/RankCalculatorByPoints.stories.js +12 -13
  253. package/src/stories/Organisms/RankCalculator/ByPoints/RankCalculatorByPoints.vue +66 -37
  254. package/src/stories/Organisms/Skeletons/ClubManagement/ClubManagement.stories.js +6 -10
  255. package/src/stories/Organisms/Skeletons/ClubManagement/ClubMangementSkeleton.vue +2 -2
  256. package/src/stories/Organisms/Skeletons/EventCard/EventCardSkeleton.stories.js +6 -10
  257. package/src/stories/Organisms/Skeletons/EventCard/EventCardSkeleton.vue +2 -3
  258. package/src/stories/Organisms/Skeletons/FencerCard/FencerCardSkeleton.stories.js +6 -10
  259. package/src/stories/Organisms/Skeletons/FencerCard/FencerCardSkeleton.vue +1 -1
  260. package/src/stories/Organisms/Skeletons/PersonManagement/PersonManagementSkeleton.stories.js +6 -9
  261. package/src/stories/Organisms/Skeletons/PersonManagement/PersonManagementSkeleton.vue +2 -2
  262. package/src/stories/Organisms/Skeletons/PoolSkeleton/PoolSkeleton.stories.js +6 -10
  263. package/src/stories/Organisms/Skeletons/PoolSkeleton/PoolSkeleton.vue +10 -4
  264. package/src/stories/Organisms/Skeletons/TournamentCard/TournamentCardSkeleton.stories.js +6 -10
  265. package/src/stories/Organisms/Skeletons/TournamentCard/TournamentCardSkeleton.vue +2 -2
  266. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +34 -34
  267. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +106 -56
  268. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.stories.js +95 -95
  269. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +129 -69
  270. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.stories.js +87 -88
  271. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +105 -60
  272. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +82 -82
  273. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.vue +150 -177
  274. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.stories.js +28 -28
  275. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +96 -36
  276. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.stories.js +99 -99
  277. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +115 -67
  278. package/src/stories/Templates/Assignment/Assignment.stories.js +22 -23
  279. package/src/stories/Templates/Assignment/Assignment.vue +210 -161
  280. package/src/stories/Templates/ClubManagement/ClubBasic/ClubBasic.stories.js +90 -90
  281. package/src/stories/Templates/ClubManagement/ClubBasic/ClubBasic.vue +68 -55
  282. package/src/stories/Templates/ClubManagement/ClubManagement.stories.js +87 -87
  283. package/src/stories/Templates/ClubManagement/ClubManagement.vue +167 -147
  284. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +216 -109
  285. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +138 -43
  286. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +198 -195
  287. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +39 -0
  288. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -0
  289. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -0
  290. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +49 -50
  291. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +21 -21
  292. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.stories.js +105 -103
  293. package/src/stories/Templates/EventManagement/EventAttendance/EventAttendance.vue +289 -226
  294. package/src/stories/Templates/EventManagement/EventFinalResults/EventFinalResults.stories.js +30 -30
  295. package/src/stories/Templates/EventManagement/EventFinalResults/EventFinalResults.vue +27 -22
  296. package/src/stories/Templates/EventManagement/KioskDisplay/KioskDisplay.stories.js +44 -45
  297. package/src/stories/Templates/EventManagement/KioskDisplay/KioskDisplay.vue +52 -43
  298. package/src/stories/Templates/EventManagement/Penalties/PenaltyReport.stories.js +28 -29
  299. package/src/stories/Templates/EventManagement/Penalties/PenaltyReport.vue +54 -39
  300. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.stories.js +80 -111
  301. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -0
  302. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.vue +290 -255
  303. package/src/stories/Templates/EventManagement/PoolManagement/PoolManagement.stories.js +109 -107
  304. package/src/stories/Templates/EventManagement/PoolManagement/PoolManagement.vue +144 -87
  305. package/src/stories/Templates/EventManagement/PoolResults/PoolResults.stories.js +56 -57
  306. package/src/stories/Templates/EventManagement/PoolResults/PoolResults.vue +132 -123
  307. package/src/stories/Templates/EventManagement/StaffList/StaffList.stories.js +56 -57
  308. package/src/stories/Templates/EventManagement/StaffList/StaffList.vue +84 -47
  309. package/src/stories/Templates/Menus/Admin/AdminLeftMenu.stories.js +29 -29
  310. package/src/stories/Templates/Menus/Admin/AdminLeftMenu.vue +71 -67
  311. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +39 -40
  312. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +79 -71
  313. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +47 -47
  314. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +39 -42
  315. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +54 -54
  316. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +27 -31
  317. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +69 -69
  318. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +78 -89
  319. package/src/stories/Templates/PersonManagement/Attendance/Attendance.stories.js +22 -22
  320. package/src/stories/Templates/PersonManagement/Attendance/Attendance.vue +61 -33
  321. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.stories.js +29 -29
  322. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +80 -53
  323. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +84 -86
  324. package/src/stories/Templates/PersonManagement/PersonManagement.vue +107 -82
  325. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.stories.js +31 -31
  326. package/src/stories/Templates/TournamentManagement/ListEvents/ListEvents.vue +70 -51
  327. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +79 -79
  328. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +275 -230
  329. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +113 -113
  330. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +175 -146
  331. package/src/stories/Templates/TournamentManagement/TournamentPageTwo/TournamentPageTwo.stories.js +77 -77
  332. package/src/stories/Templates/TournamentManagement/TournamentPageTwo/TournamentPageTwo.vue +208 -185
  333. package/src/stories/Util/Validations/AddNewPersonModalValidation.js +23 -19
  334. package/src/stories/Util/Validations/ClubManagementValidations.js +17 -21
  335. package/src/stories/Util/Validations/PersonManagementValidations.js +26 -22
  336. package/src/stories/Util/Validations/TournamentFieldsValidations.js +20 -18
  337. package/src/stories/Util/seeding.js +131 -131
  338. package/src/stories/Util/tabs.js +75 -75
  339. package/tailwind/tailwind.css +5 -7
  340. package/tailwind.config.js +12 -15
  341. package/vite.config.js +3 -3
  342. package/src/mocks/getDirectors.js +0 -18
  343. package/src/stories/Molecules/Modals/AssignDirectorModal/AssignDirectorModal.stories.js +0 -45
  344. package/src/stories/Molecules/Modals/AssignDirectorModal/AssignDirectorModal.vue +0 -111
  345. package/src/stories/Organisms/Cards/DirectorAssignmentCard/DirectorAssignmentCard.stories.js +0 -45
  346. package/src/stories/Organisms/Cards/DirectorAssignmentCard/DirectorAssignmentCard.vue +0 -83
  347. package/src/stories/Organisms/Cards/TableauBoutCard/SpacingConfig.js +0 -111
  348. package/src/stories/Organisms/Containers/TableauLines/SpacingConfig.js +0 -112
  349. package/src/stories/Organisms/Containers/TableauLines/TableauLines.stories.js +0 -23
  350. package/src/stories/Organisms/Containers/TableauLines/TableauLines.vue +0 -79
@@ -1,259 +1,380 @@
1
1
  <template>
2
2
  <section v-if="listView" class="flex flex-col items-center">
3
+ <div v-for="section in listSections" :key="section.key" class="my-4 w-full flex-col md:w-3/4">
4
+ <BaseText class="my-5" color="primaryHighlight" size="sm" :text="section.label" weight="bold" />
3
5
 
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"
16
- />
6
+ <div v-for="bout in section.bouts" :key="bout.BoutId" class="mb-4">
7
+ <div
8
+ v-if="getPlannedStation(bout)"
9
+ data-testid="planned-bout-label"
10
+ class="mb-1 flex items-center gap-2 rounded border-l-4 border-dropdownSelect bg-white px-2 py-1 shadow-sm"
11
+ :style="{ borderLeftColor: getPlannedStation(bout).Color }"
12
+ >
13
+ <span
14
+ class="h-2.5 w-2.5 flex-none rounded-full"
15
+ :style="{ backgroundColor: getPlannedStation(bout).Color }"
16
+ aria-hidden="true"
17
+ ></span>
18
+ <BaseText :text="getPlannedStationLabel(bout)" color="quaternary" size="xs" />
17
19
  </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="true"
26
- :reorderMode="false"
27
- :isAdmin="isAdmin"
28
- @action:startBout="handleDirectingFromIds"
29
- @action:resumeBout="handleDirectingFromIds"
30
- @action:editBout="handleEditFromIds"
31
- @action:assignBout="handleAssignBout"
20
+ <BoutCard
21
+ :bout="bout"
22
+ :hostingClubColors="hostingClubColors"
23
+ :editMode="true"
24
+ :reorderMode="false"
25
+ @action:startBout="handleDirectingFromIds"
26
+ @action:resumeBout="handleDirectingFromIds"
27
+ @action:editBout="handleEditFromIds"
32
28
  />
33
29
  </div>
34
30
  </div>
31
+ </section>
35
32
 
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="true"
49
- :reorderMode="false"
50
- :isAdmin="isAdmin"
51
- @action:startBout="handleDirectingFromIds"
52
- @action:resumeBout="handleDirectingFromIds"
53
- @action:editBout="handleEditFromIds"
54
- @action:assignBout="handleAssignBout"
33
+ <section
34
+ v-else
35
+ ref="scrollElement"
36
+ class="mt-4 w-full overflow-x-auto scrollbar-thin"
37
+ @scroll.passive="scheduleConnectorUpdate"
38
+ >
39
+ <div ref="tableauRoot" class="relative min-w-max pb-6">
40
+ <div class="inline-flex items-start">
41
+ <TableauColumn
42
+ v-for="([roundName, roundBouts], roundIndex) in winnerRoundEntries"
43
+ :key="`winner-${roundName}`"
44
+ :bouts="roundBouts"
45
+ :hostingClubColors="hostingClubColors"
46
+ :roundName="roundName"
47
+ :bracketSize="bracketSize"
48
+ :layout="getRoundLayout(roundIndex)"
49
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
50
+ @register-node-anchor="registerNodeAnchor"
51
+ @action:editBout="handleEditBout"
52
+ @action:directingBout="handleDirectorBout"
55
53
  />
56
54
  </div>
57
- </div>
58
- </section>
59
55
 
60
- <section v-else class="flex w-full overflow-x-auto mt-4 scrollbar-thin">
61
- <div class="min-w-full flex">
62
- <div v-for="(bouts, roundName) in groupedBouts.winnerBouts" :key="roundName">
56
+ <div v-if="placementRoundEntries.length" class="mt-10 flex justify-end pr-10">
63
57
  <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"
58
+ v-for="[roundName, roundBouts] in placementRoundEntries"
59
+ :key="`placement-${roundName}`"
60
+ :bouts="roundBouts"
61
+ :hostingClubColors="hostingClubColors"
62
+ :roundName="roundName"
63
+ :bracketSize="bracketSize"
64
+ :layout="placementLayout"
65
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
66
+ @register-node-anchor="registerNodeAnchor"
67
+ @action:editBout="handleEditBout"
68
+ @action:directingBout="handleDirectorBout"
72
69
  />
73
70
  </div>
74
- </div>
75
71
 
76
- <!-- Loser bracket -->
77
- <div class="min-w-full flex">
78
- <div v-for="(bouts, roundName) in groupedBouts.loserBouts" :key="roundName">
79
- <TableauColumn
80
- :bouts="bouts"
72
+ <div v-if="loserRoundEntries.length" class="mt-12 border-t border-dropdownSelect pt-6">
73
+ <BaseText class="mb-4" color="quaternary" size="sm" text="Consolation" weight="bold" />
74
+ <div class="inline-flex items-start">
75
+ <TableauColumn
76
+ v-for="([roundName, roundBouts], roundIndex) in loserRoundEntries"
77
+ :key="`loser-${roundName}`"
78
+ :bouts="roundBouts"
81
79
  :hostingClubColors="hostingClubColors"
82
80
  :roundName="roundName"
83
81
  :bracketSize="bracketSize"
84
- :largeCards="largeCards"
82
+ :layout="getRoundLayout(roundIndex)"
85
83
  :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
84
+ @register-node-anchor="registerNodeAnchor"
86
85
  @action:editBout="handleEditBout"
87
86
  @action:directingBout="handleDirectorBout"
88
- />
87
+ />
88
+ </div>
89
89
  </div>
90
+
91
+ <TableauSvgOverlay :size="svgSize" :paths="connectorPaths" />
90
92
  </div>
91
93
  </section>
92
94
  </template>
93
95
 
94
96
  <script>
95
- import TableauColumn from "../TableauColumn/TableauColumn.vue";
96
- import TableauBoutCard from "../../Cards/TableauBoutCard/TableauBoutCard.vue";
97
- import BoutCard from "../../../Organisms/Cards/BoutCard/BoutCard.vue";
98
- import BaseText from "../../../Atoms/Text/BaseText.vue";
97
+ import BaseText from '../../../Atoms/Text/BaseText.vue';
98
+ import BoutCard from '../../../Organisms/Cards/BoutCard/BoutCard.vue';
99
+ import TableauColumn from '../TableauColumn/TableauColumn.vue';
100
+ import TableauSvgOverlay from './components/TableauSvgOverlay.vue';
101
+ import { buildBracketPaths, createDomAnchorLayout } from './layout/TableauLayoutEngine';
102
+ import {
103
+ addPlannedStrokesToConnectors,
104
+ createPlannedStationByBout,
105
+ getPlannedStationForBout,
106
+ isBoutSuggestedForViewer
107
+ } from './plannedAssignments';
99
108
 
100
- export default {
101
- name: "Tableau",
102
- components: { BaseText, BoutCard, TableauBoutCard, TableauColumn },
103
- emits: ["action:editBout", "action:directingBout", "action:assignBout"],
104
- props: {
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 },
109
+ const MATCH_HEIGHT_PX = 57;
110
+ const BASE_GAP_PX = 16;
111
+ const BASE_PITCH_PX = MATCH_HEIGHT_PX + BASE_GAP_PX;
112
+ const PLACEMENT_ROUND_NAMES = new Set(['Third Place', 'Third Place Match']);
113
+ const ROUND_ORDER = [
114
+ 'Table of 256',
115
+ 'Table of 128',
116
+ 'Table of 64',
117
+ 'Table of 32',
118
+ 'Table of 16',
119
+ 'Table of 8',
120
+ 'Semifinal',
121
+ 'Final',
122
+ 'Grand Final'
123
+ ];
111
124
 
112
- // use role (already present upstream) instead of passing isAdmin
113
- role: { type: String, default: "Admin" },
125
+ const roundAliases = {
126
+ Semifinals: 'Semifinal'
127
+ };
114
128
 
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";
121
- },
129
+ const normalizeRoundName = roundName => roundAliases[roundName] || roundName;
130
+ const isConsolationRoundName = roundName => roundName.includes('Consolation');
131
+ const isPlacementRoundName = roundName => PLACEMENT_ROUND_NAMES.has(roundName);
132
+ const getRoundNumber = roundName => Number.parseInt(roundName.match(/\d+/)?.[0] || '0', 10);
133
+ const getBoutNodeId = bout => bout.DEBoutId ?? bout.BoutId;
122
134
 
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));
134
- },
135
+ const getRoundOrder = roundName => {
136
+ const mainRoundIndex = ROUND_ORDER.indexOf(normalizeRoundName(roundName));
137
+ if (mainRoundIndex >= 0) return mainRoundIndex;
138
+ if (isConsolationRoundName(roundName)) return ROUND_ORDER.length + getRoundNumber(roundName);
139
+ return ROUND_ORDER.length * 2;
140
+ };
135
141
 
136
- hasAssignedBouts() {
137
- return this.assignedBoutsToMe.length > 0;
138
- },
142
+ const sortRoundEntries = ([roundNameA], [roundNameB]) => getRoundOrder(roundNameA) - getRoundOrder(roundNameB);
143
+ const sortBoutsByNodeId = (boutA, boutB) => Number(getBoutNodeId(boutA)) - Number(getBoutNodeId(boutB));
139
144
 
140
- groupedBouts() {
141
- const grouped = this.groupBoutsByRound(this.bouts);
145
+ const addBoutToRound = (rounds, bout) => {
146
+ const roundName = bout.RoundLabel || 'Unknown Round';
147
+ return { ...rounds, [roundName]: [...(rounds[roundName] || []), bout] };
148
+ };
142
149
 
143
- const { winnerBouts, loserBouts } = Object.entries(grouped).reduce(
144
- (acc, [roundName, bouts]) => {
145
- if (roundName.includes("Consolation")) {
146
- acc.loserBouts[roundName] = bouts;
147
- } else {
148
- acc.winnerBouts[roundName] = bouts;
149
- }
150
- return acc;
151
- },
152
- { winnerBouts: {}, loserBouts: {} }
153
- );
150
+ const groupBoutsByRound = bouts => (bouts || []).reduce(addBoutToRound, {});
154
151
 
155
- return { winnerBouts, loserBouts };
156
- },
152
+ const isVisibleListBout = bout =>
153
+ bout.Person1?.DisplayName !== 'BYE' && bout.Person2?.DisplayName !== 'BYE' && bout.Status !== 'Completed';
157
154
 
158
- combinedBouts() {
159
- const combined = {
160
- ...this.groupedBouts.winnerBouts,
161
- ...this.groupedBouts.loserBouts,
162
- };
163
- return Object.keys(combined)
164
- .sort()
165
- .reduce((acc, roundName) => {
166
- acc[roundName] = combined[roundName];
167
- return acc;
168
- }, {});
169
- },
155
+ const createVisibleRoundEntry = ([roundName, bouts]) => [
156
+ roundName,
157
+ bouts.filter(isVisibleListBout).sort(sortBoutsByNodeId)
158
+ ];
170
159
 
171
- filteredBouts() {
172
- return Object.keys(this.combinedBouts)
173
- .sort((a, b) => {
174
- const priority = {
175
- Final: 1,
176
- "Third Place Match": 2,
177
- Semifinal: 3,
178
- };
160
+ const hasBouts = ([, bouts]) => bouts.length > 0;
179
161
 
180
- const priorityA = priority[a] || 0;
181
- const priorityB = priority[b] || 0;
162
+ const createListSection = ([label, bouts]) => ({ key: label, label, bouts });
182
163
 
183
- if (priorityA || priorityB) return priorityA - priorityB;
164
+ const removeSuggestedBouts = (entries, suggestedBoutIds) =>
165
+ entries
166
+ .map(([roundName, bouts]) => [roundName, bouts.filter(bout => !suggestedBoutIds.has(String(bout.BoutId)))])
167
+ .filter(hasBouts);
184
168
 
185
- const numA = parseInt(a.match(/\d+/)?.[0] || 0, 10);
186
- const numB = parseInt(b.match(/\d+/)?.[0] || 0, 10);
169
+ const createRoundLayout = depth => {
170
+ const roundMultiplier = 2 ** Math.max(depth, 0);
171
+ return {
172
+ gapPx: BASE_PITCH_PX * roundMultiplier - MATCH_HEIGHT_PX,
173
+ padTopPx: (BASE_PITCH_PX * (roundMultiplier - 1)) / 2
174
+ };
175
+ };
187
176
 
188
- return numB - numA;
177
+ export default {
178
+ name: 'Tableau',
179
+ components: { BaseText, BoutCard, TableauColumn, TableauSvgOverlay },
180
+ emits: ['action:editBout', 'action:directingBout'],
181
+ props: {
182
+ bouts: { type: Array, required: true },
183
+ bracketSize: { type: String, required: true },
184
+ hostingClubColors: { type: Object, required: true },
185
+ listView: { type: Boolean, default: false },
186
+ isCountingBackwardsMaxScore: { type: Number, default: 0 },
187
+ plannedDirectingStations: { type: Array, default: () => [] },
188
+ plannedBoutAssignments: { type: Array, default: () => [] },
189
+ viewerPersonId: { type: [Number, String], default: null }
190
+ },
191
+ data() {
192
+ return {
193
+ connectorPaths: [],
194
+ inputAnchors: new Map(),
195
+ outputAnchors: new Map(),
196
+ resizeObserver: null,
197
+ scheduledFrame: null,
198
+ svgSize: { width: 0, height: 0 }
199
+ };
200
+ },
201
+ computed: {
202
+ roundEntries() {
203
+ return Object.entries(groupBoutsByRound(this.bouts)).map(([roundName, bouts]) => [
204
+ roundName,
205
+ [...bouts].sort(sortBoutsByNodeId)
206
+ ]);
207
+ },
208
+ winnerRoundEntries() {
209
+ return this.roundEntries
210
+ .filter(([roundName]) => !isConsolationRoundName(roundName) && !isPlacementRoundName(roundName))
211
+ .sort(sortRoundEntries);
212
+ },
213
+ placementRoundEntries() {
214
+ return this.roundEntries.filter(([roundName]) => isPlacementRoundName(roundName)).sort(sortRoundEntries);
215
+ },
216
+ loserRoundEntries() {
217
+ return this.roundEntries.filter(([roundName]) => isConsolationRoundName(roundName)).sort(sortRoundEntries);
218
+ },
219
+ filteredBouts() {
220
+ return Object.fromEntries(
221
+ [...this.roundEntries].sort(sortRoundEntries).map(createVisibleRoundEntry).filter(hasBouts)
222
+ );
223
+ },
224
+ listSections() {
225
+ const entries = Object.entries(this.filteredBouts);
226
+ const suggestedBouts = entries
227
+ .flatMap(([, bouts]) => bouts)
228
+ .filter(bout =>
229
+ isBoutSuggestedForViewer({
230
+ bout,
231
+ plannedStationByBout: this.plannedStationByBout,
232
+ viewerPersonId: this.viewerPersonId
189
233
  })
190
- .reduce((acc, roundName) => {
191
- const filtered = this.combinedBouts[roundName]
192
- .filter(
193
- (bout) =>
194
- bout.Person1?.DisplayName !== "BYE" &&
195
- bout.Person2?.DisplayName !== "BYE" &&
196
- bout.Status !== "Completed"
197
- )
198
- .sort((a, b) => a.DEBoutId - b.DEBoutId);
234
+ );
199
235
 
200
- if (filtered.length) acc[roundName] = filtered;
201
- return acc;
202
- }, {});
236
+ if (!suggestedBouts.length) return entries.map(createListSection);
237
+
238
+ const suggestedBoutIds = new Set(suggestedBouts.map(bout => String(bout.BoutId)));
239
+ const remainingSections = removeSuggestedBouts(entries, suggestedBoutIds).map(createListSection);
240
+ return [{ key: 'suggested-for-viewer', label: 'Suggested for You', bouts: suggestedBouts }, ...remainingSections];
241
+ },
242
+ plannedStationByBout() {
243
+ return createPlannedStationByBout({
244
+ assignments: this.plannedBoutAssignments,
245
+ stations: this.plannedDirectingStations
246
+ });
203
247
  },
248
+ placementLayout() {
249
+ return createRoundLayout(0);
250
+ }
251
+ },
252
+ watch: {
253
+ bouts: {
254
+ deep: true,
255
+ handler() {
256
+ this.scheduleConnectorUpdate();
257
+ }
258
+ },
259
+ plannedDirectingStations: {
260
+ deep: true,
261
+ handler() {
262
+ this.scheduleConnectorUpdate();
263
+ }
264
+ },
265
+ plannedBoutAssignments: {
266
+ deep: true,
267
+ handler() {
268
+ this.scheduleConnectorUpdate();
269
+ }
270
+ },
271
+ bracketSize() {
272
+ this.scheduleConnectorUpdate();
273
+ },
274
+ listView(value) {
275
+ if (!value) this.$nextTick(this.observeTableauSize);
276
+ this.scheduleConnectorUpdate();
277
+ }
278
+ },
279
+ mounted() {
280
+ this.observeTableauSize();
281
+ window.addEventListener('resize', this.scheduleConnectorUpdate);
282
+ document.fonts?.ready?.then(this.scheduleConnectorUpdate);
283
+ this.scheduleConnectorUpdate();
284
+ },
285
+ beforeUnmount() {
286
+ this.resizeObserver?.disconnect();
287
+ window.removeEventListener('resize', this.scheduleConnectorUpdate);
288
+ if (this.scheduledFrame !== null && typeof cancelAnimationFrame === 'function') {
289
+ cancelAnimationFrame(this.scheduledFrame);
290
+ }
204
291
  },
205
-
206
292
  methods: {
207
- // --- helpers ---
208
- _findBoutByIds(idsOrBout) {
209
- // BoutCard emits ids for start/resume/edit; assign emits ids too.
210
- // normalize to actual bout object
293
+ findBoutByIds(idsOrBout) {
211
294
  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;
217
- },
295
+ if (idsOrBout.BoutId && idsOrBout.Person1) return idsOrBout;
218
296
 
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);
297
+ const boutId = idsOrBout.BoutId ?? idsOrBout.boutId;
298
+ return this.bouts.find(bout => String(bout.BoutId) === String(boutId)) || null;
224
299
  },
225
-
226
- // Start/Resume from BoutCard
227
300
  handleDirectingFromIds(ids) {
228
- const bout = this._findBoutByIds(ids);
229
- if (!bout) return;
230
- this.$emit("action:directingBout", bout);
301
+ const bout = this.findBoutByIds(ids);
302
+ if (bout) this.$emit('action:directingBout', bout);
231
303
  },
232
-
233
- // Edit from BoutCard
234
304
  handleEditFromIds(ids) {
235
- const bout = this._findBoutByIds(ids);
236
- if (!bout) return;
237
- this.$emit("action:editBout", bout);
305
+ const bout = this.findBoutByIds(ids);
306
+ if (bout) this.$emit('action:editBout', bout);
238
307
  },
239
-
240
- // TableauColumn / non-list view already emits bout objects
241
308
  handleEditBout(bout) {
242
- this.$emit("action:editBout", bout);
309
+ this.$emit('action:editBout', bout);
243
310
  },
244
311
  handleDirectorBout(bout) {
245
- this.$emit("action:directingBout", bout);
312
+ this.$emit('action:directingBout', bout);
313
+ },
314
+ getRoundLayout(roundIndex) {
315
+ return createRoundLayout(roundIndex);
316
+ },
317
+ getPlannedStation(bout) {
318
+ return getPlannedStationForBout(this.plannedStationByBout, bout);
246
319
  },
320
+ getPlannedStationLabel(bout) {
321
+ const station = this.getPlannedStation(bout);
322
+ if (!station) return '';
323
+ const directorName = station.Director?.DisplayName || 'Director TBD';
324
+ return `Suggested: ${station.RingName} / ${directorName}`;
325
+ },
326
+ registerNodeAnchor({ id, kind, el }) {
327
+ const anchors = kind === 'input' ? this.inputAnchors : this.outputAnchors;
328
+ const anchorId = String(id);
247
329
 
248
- groupBoutsByRound(bouts) {
249
- return (bouts || []).reduce((acc, bout) => {
250
- const key = bout.RoundLabel || "Unknown Round";
251
- if (!acc[key]) acc[key] = [];
252
- acc[key].push(bout);
253
- return acc;
254
- }, {});
330
+ if (el) anchors.set(anchorId, el);
331
+ if (!el) anchors.delete(anchorId);
332
+ this.scheduleConnectorUpdate();
255
333
  },
256
- },
334
+ observeTableauSize() {
335
+ if (typeof ResizeObserver === 'undefined' || !this.$refs.tableauRoot) return;
336
+ this.resizeObserver?.disconnect();
337
+ this.resizeObserver = new ResizeObserver(this.scheduleConnectorUpdate);
338
+ this.resizeObserver.observe(this.$refs.tableauRoot);
339
+ },
340
+ scheduleConnectorUpdate() {
341
+ if (this.scheduledFrame !== null) return;
342
+ this.scheduledFrame = -1;
343
+
344
+ const update = () => {
345
+ this.scheduledFrame = null;
346
+ this.recomputeConnectors();
347
+ };
348
+
349
+ this.$nextTick(() => {
350
+ if (typeof requestAnimationFrame === 'function') {
351
+ this.scheduledFrame = requestAnimationFrame(update);
352
+ return;
353
+ }
354
+ update();
355
+ });
356
+ },
357
+ recomputeConnectors() {
358
+ const rootElement = this.$refs.tableauRoot;
359
+ if (this.listView || !rootElement) {
360
+ this.connectorPaths = [];
361
+ return;
362
+ }
363
+
364
+ const layout = createDomAnchorLayout({
365
+ rootElement,
366
+ getInputAnchor: id => this.inputAnchors.get(String(id)),
367
+ getOutputAnchor: id => this.outputAnchors.get(String(id))
368
+ });
369
+
370
+ this.svgSize = layout.size;
371
+ const paths = buildBracketPaths({
372
+ bouts: this.bouts,
373
+ getInputPoint: layout.getInputPoint,
374
+ getOutputPoint: layout.getOutputPoint
375
+ });
376
+ this.connectorPaths = addPlannedStrokesToConnectors(paths, this.plannedStationByBout);
377
+ }
378
+ }
257
379
  };
258
380
  </script>
259
-
@@ -0,0 +1,29 @@
1
+ import { shallowMount } from '@vue/test-utils';
2
+ import { describe, expect, it } from 'vitest';
3
+ import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
4
+ import BracketDirectorAssignmentPanel from './BracketDirectorAssignmentPanel.vue';
5
+
6
+ const stations = [
7
+ { RingId: 1, RingName: 'Ring 1', Director: { PersonId: 10, DisplayName: 'Director One' } },
8
+ { RingId: 2, RingName: 'Ring 2', Director: { PersonId: 20, DisplayName: 'Director Two' } }
9
+ ];
10
+
11
+ describe('BracketDirectorAssignmentPanel', () => {
12
+ it('renders assigned directors followed by the add summary card', () => {
13
+ const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations } });
14
+ const cards = wrapper.findAllComponents(BracketDirectorAssignmentCard);
15
+
16
+ expect(cards).toHaveLength(3);
17
+ expect(cards.slice(0, 2).map(card => card.props('station'))).toEqual(stations);
18
+ expect(cards[2].props('variant')).toBe('add');
19
+ });
20
+
21
+ it('forwards add intent to its parent', async () => {
22
+ const wrapper = shallowMount(BracketDirectorAssignmentPanel, { props: { stations: [] } });
23
+
24
+ wrapper.findComponent(BracketDirectorAssignmentCard).vm.$emit('action:add');
25
+ await wrapper.vm.$nextTick();
26
+
27
+ expect(wrapper.emitted('request:add-planned-director')).toEqual([[]]);
28
+ });
29
+ });
@@ -0,0 +1,46 @@
1
+ <template>
2
+ <aside
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"
5
+ >
6
+ <div class="mb-4">
7
+ <BaseText color="primaryHighlight" size="md" text="Director Assignments" weight="bold" />
8
+ <BaseText
9
+ class="mt-1"
10
+ color="quaternary"
11
+ size="xs"
12
+ text="Plan ring coverage. Scheduled bouts may still be moved between rings."
13
+ />
14
+ </div>
15
+
16
+ <div class="space-y-3">
17
+ <BracketDirectorAssignmentCard v-for="station in stations" :key="station.RingId" :station="station" />
18
+ <BracketDirectorAssignmentCard
19
+ v-if="showAddCard"
20
+ variant="add"
21
+ @action:add="$emit('request:add-planned-director')"
22
+ />
23
+ </div>
24
+ </aside>
25
+ </template>
26
+
27
+ <script>
28
+ import BaseText from '../../../../Atoms/Text/BaseText.vue';
29
+ import BracketDirectorAssignmentCard from '../../../Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue';
30
+
31
+ export default {
32
+ name: 'BracketDirectorAssignmentPanel',
33
+ components: { BaseText, BracketDirectorAssignmentCard },
34
+ emits: ['request:add-planned-director'],
35
+ props: {
36
+ stations: {
37
+ type: Array,
38
+ default: () => []
39
+ },
40
+ showAddCard: {
41
+ type: Boolean,
42
+ default: true
43
+ }
44
+ }
45
+ };
46
+ </script>