@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
@@ -0,0 +1,143 @@
1
+ import { mount, shallowMount } from '@vue/test-utils';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import TableauFencerCard from '../TableauFencerCard/TableauFencerCard.vue';
4
+ import TableauBoutCard from './TableauBoutCard.vue';
5
+
6
+ const createBout = status => ({
7
+ BoutId: 42,
8
+ DEBoutId: 7,
9
+ Status: status,
10
+ Score1: 4,
11
+ Score2: 3,
12
+ Person1: { PersonId: 1, DisplayName: 'Fencer One', EventPersons: [] },
13
+ Person2: { PersonId: 2, DisplayName: 'Fencer Two', EventPersons: [] }
14
+ });
15
+
16
+ const mountCard = bout =>
17
+ shallowMount(TableauBoutCard, {
18
+ props: {
19
+ bout,
20
+ hostingClubColors: {},
21
+ roundName: 'Final',
22
+ bracketSize: 'Final'
23
+ }
24
+ });
25
+
26
+ describe('TableauBoutCard selection', () => {
27
+ it('keeps the fencer layout outside the native selection button', () => {
28
+ const wrapper = mount(TableauBoutCard, {
29
+ props: {
30
+ bout: createBout('Scheduled'),
31
+ hostingClubColors: {},
32
+ roundName: 'Final',
33
+ bracketSize: 'Final'
34
+ }
35
+ });
36
+ const button = wrapper.find('[data-testid="tableau-bout"]');
37
+
38
+ expect(button.element.tagName).toBe('BUTTON');
39
+ expect(button.attributes('aria-label')).toBe('Fencer One versus Fencer Two');
40
+ expect(button.find('div, section').exists()).toBe(false);
41
+ expect(wrapper.findAllComponents(TableauFencerCard)).toHaveLength(2);
42
+ });
43
+
44
+ it('emits directing intent for a scheduled bout', async () => {
45
+ const bout = createBout('Scheduled');
46
+ const wrapper = mountCard(bout);
47
+
48
+ await wrapper.find('[data-testid="tableau-bout"]').trigger('click');
49
+
50
+ expect(wrapper.emitted('action:directingBout')).toEqual([[bout]]);
51
+ expect(wrapper.emitted('action:editBout')).toBeUndefined();
52
+ });
53
+
54
+ it('does not draw planned-assignment color over the fencer card', () => {
55
+ const wrapper = mountCard(createBout('Scheduled'));
56
+
57
+ expect(wrapper.find('[data-testid="planned-assignment-indicator"]').exists()).toBe(false);
58
+ });
59
+
60
+ it('emits editing intent for a completed bout', async () => {
61
+ const bout = createBout('Completed');
62
+ const wrapper = mountCard(bout);
63
+
64
+ await wrapper.find('[data-testid="tableau-bout"]').trigger('click');
65
+
66
+ expect(wrapper.emitted('action:editBout')).toEqual([[bout]]);
67
+ expect(wrapper.emitted('action:directingBout')).toBeUndefined();
68
+ });
69
+
70
+ it('emits directing intent for an active bout', async () => {
71
+ const bout = createBout('Active');
72
+ const wrapper = mountCard(bout);
73
+
74
+ await wrapper.find('[data-testid="tableau-bout"]').trigger('click');
75
+
76
+ expect(wrapper.emitted('action:directingBout')).toEqual([[bout]]);
77
+ });
78
+
79
+ it('does not emit directing intent for a BYE bout', async () => {
80
+ const bout = {
81
+ ...createBout('Scheduled'),
82
+ Person2: { PersonId: 0, DisplayName: 'BYE', EventPersons: [] }
83
+ };
84
+ const wrapper = mountCard(bout);
85
+
86
+ await wrapper.find('[data-testid="tableau-bout"]').trigger('click');
87
+
88
+ expect(wrapper.emitted('action:directingBout')).toBeUndefined();
89
+ expect(wrapper.find('[data-testid="tableau-bout"]').attributes('disabled')).toBeDefined();
90
+ });
91
+
92
+ it('rounds the outside corners and highlights the whole bout on hover', () => {
93
+ const wrapper = mountCard(createBout('Scheduled'));
94
+ const fencerRows = wrapper.findAllComponents(TableauFencerCard);
95
+ const boutButton = wrapper.find('[data-testid="tableau-bout"]');
96
+
97
+ expect(fencerRows.map(row => row.props('corners'))).toEqual(['top', 'bottom']);
98
+ expect(boutButton.classes()).toContain('hover:ring-bright');
99
+ expect(boutButton.classes()).toContain('hover:ring-2');
100
+ });
101
+
102
+ it('shows the assigned ring and time for an active bout', () => {
103
+ const wrapper = mountCard({
104
+ ...createBout('Active'),
105
+ RingName: 'Ring 3',
106
+ TimeLeft: 95,
107
+ TimerStatus: 'stopped'
108
+ });
109
+
110
+ expect(wrapper.find('[data-testid="active-ring"]').exists()).toBe(true);
111
+ expect(wrapper.find('[data-testid="active-ring"] base-text-stub').attributes('text')).toBe('Ring 3');
112
+ expect(wrapper.find('[data-testid="active-time"] base-text-stub').attributes('text')).toBe('1:35');
113
+ });
114
+
115
+ it('hides active chrome after its ring and timer display fields are cleared', () => {
116
+ const wrapper = mountCard({
117
+ ...createBout('Active'),
118
+ RingName: '',
119
+ TimeLeft: 0,
120
+ TimerStatus: 'stopped'
121
+ });
122
+
123
+ expect(wrapper.find('[data-testid="active-ring"]').exists()).toBe(false);
124
+ expect(wrapper.find('[data-testid="active-time"]').exists()).toBe(false);
125
+ });
126
+
127
+ it('ticks the presentation timer without mutating the bout prop', async () => {
128
+ vi.useFakeTimers();
129
+ const bout = { ...createBout('Active'), TimeLeft: 90, TimerStatus: 'running' };
130
+ const wrapper = mountCard(bout);
131
+
132
+ try {
133
+ vi.advanceTimersByTime(1000);
134
+ await wrapper.vm.$nextTick();
135
+
136
+ expect(wrapper.vm.displayTime).toBe('1:29');
137
+ expect(bout.TimeLeft).toBe(90);
138
+ } finally {
139
+ wrapper.unmount();
140
+ vi.useRealTimers();
141
+ }
142
+ });
143
+ });
@@ -1,202 +1,256 @@
1
- <template>
2
- <section :class="getTopSpace">
3
- <div v-if="roundName === 'Third Place'" class="flex justify-center border-b mb-4 mt-8" :class="large ? 'w-72': 'w-40'">
4
- <BaseText :text="roundName" size="sm" color="quaternary"/>
5
- </div>
6
- <div v-if="roundName === 'Final'" class="flex justify-center border-b mb-4 mt-0" :class="large ? 'w-72': 'w-40'">
7
- <BaseText :text="roundName" size="sm" color="quaternary"/>
8
- </div>
9
- <div v-if="showGrandFinal" class="flex justify-center border-b mb-4 mt-0" :class="large ? 'w-72': 'w-40'">
10
- <BaseText :text="roundName" size="sm" color="quaternary"/>
11
- </div>
12
- <!-- Replacement headers for finals -->
13
- <div v-if="roundName !== 'Grand Final' || showGrandFinal">
14
- <section @click="handleBoutClick(bout)" >
15
- <TableauFencerCard
16
- :fencer="person1Display"
17
- :score="score1"
18
- :position="dePosition1"
19
- :color="hostingClubColors.Color1"
20
- :scoreColor="getScoreColor(true)"
21
- :large="large"
22
- :highLight="highlight"
23
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
24
- :faded="shouldFade(bout, true)"
25
- @action:highLight="handleHighlight"
26
- />
27
- </section>
28
- <!-- Card break -->
29
- <div :class="getCardSpace"></div>
30
- <!-- Card break -->
31
- <section @click="handleBoutClick(bout)">
32
- <TableauFencerCard
33
- :fencer="person2Display"
34
- :score="score2"
35
- :position="dePosition2"
36
- :color="hostingClubColors.Color2"
37
- :scoreColor="getScoreColor(false)"
38
- :large="large"
39
- :highLight="highlight"
40
- :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
41
- :faded="shouldFade(bout, false)"
42
- @action:highLight="handleHighlight"
43
- />
44
- </section>
45
- </div>
46
- </section>
47
- </template>
48
-
49
- <script>
50
- import BaseText from "../../../Atoms/Text/BaseText.vue";
51
- import TableauFencerCard from '../TableauFencerCard/TableauFencerCard.vue';
52
- import { spacingConfig } from './SpacingConfig';
53
-
54
- export default {
55
- name: 'TableauBoutCard',
56
- emits: ['action:directingBout', 'action:editBout'],
57
- components: { BaseText, TableauFencerCard },
58
- props: {
59
- bout: {
60
- type: Object,
61
- required: true
62
- },
63
- hostingClubColors: {
64
- type: Object,
65
- required: true
66
- },
67
- large: {
68
- type: Boolean,
69
- default: true
70
- },
71
- roundName: {
72
- type: String,
73
- required: true
74
- },
75
- bracketSize: {
76
- type: String,
77
- required: true
78
- },
79
- isCountingBackwardsMaxScore: {
80
- type: Number,
81
- default: 0
82
- }
83
- },
84
- data() {
85
- return {
86
- highlight: false,
87
- currentConfig: {}
88
- };
89
- },
90
- mounted() {
91
- this.updateConfig();
92
- },
93
- watch: {
94
- large(newVal, oldVal) {
95
- if (newVal !== oldVal) {
96
- this.updateConfig();
97
- }
98
- },
99
- bracketSize(newVal, oldVal) {
100
- if (newVal !== oldVal) {
101
- this.updateConfig();
102
- }
103
- }
104
- },
105
- methods: {
106
- updateConfig() {
107
- const size = this.large ? 'large' : 'small';
108
- this.currentConfig = spacingConfig[this.bracketSize]?.[size]?.[this.roundName] || {};
109
- },
110
- handleBoutClick(bout) {
111
- if (bout.Status === 'Completed') {
112
- this.$emit('action:editBout', bout);
113
- } else {
114
- this.$emit('action:directingBout', bout);
115
- }
116
- },
117
- getScoreColor(isPerson1) {
118
- if (isPerson1) {
119
- if (this.bout.Person2.DisplayName === 'BYE' && !((this.bout.Person1.DisplayName === 'BYE') && (this.bout.Person2.DisplayName === 'BYE'))) return 'bg-winGreen'
120
-
121
- if (this.bout.Score1 > this.bout.Score2) {
122
- return 'bg-winGreen';
123
- }
124
-
125
- if (this.bout.Score1 < this.bout.Score2 && this.bout.Status === 'Active') {
126
- return 'bg-lossRed';
127
- }
128
- }
129
-
130
- if (!isPerson1) {
131
- if (this.bout.Person1.DisplayName === 'BYE' && !((this.bout.Person1.DisplayName === 'BYE') && (this.bout.Person2.DisplayName === 'BYE'))) return 'bg-winGreen'
132
-
133
- if (this.bout.Score1 < this.bout.Score2) {
134
- return 'bg-winGreen';
135
- }
136
-
137
- if (this.bout.Score1 > this.bout.Score2 && this.bout.Status === 'Active') {
138
- return 'bg-lossRed';
139
- }
140
- }
141
- return 'bg-dropdownSelect';
142
- },
143
- handleHighlight(value) {
144
- if (this.bout.Person1.DisplayName.length < 1 || this.bout.Person2.DisplayName.length < 1) return;
145
- if (this.bout.Person1.DisplayName === 'BYE' || this.bout.Person2.DisplayName === 'BYE') return;
146
- this.highlight = value;
147
- },
148
- shouldFade(bout, person1){
149
- if (this.bout.Person1.DisplayName === 'BYE' && this.bout.Person2.DisplayName === 'BYE') return false;
150
-
151
- if (person1) {
152
- if (bout.Person1.DisplayName === 'BYE') return true;
153
- if (parseInt(bout.Score1) < parseInt(bout.Score2)) return true;
154
- }
155
-
156
- if (!person1) {
157
- if (bout.Person2.DisplayName === 'BYE') return true;
158
- if (parseInt(bout.Score2) < parseInt(bout.Score1)) return true;
159
- }
160
-
161
- return false
162
- }
163
- },
164
- computed: {
165
- dePosition1() {
166
- return this.bout.Person1?.EventPersons?.[0]?.DEPosition ?? '';
167
- },
168
- score1() {
169
- return `${this.bout.Score1}`;
170
- },
171
- dePosition2() {
172
- return this.bout.Person2?.EventPersons?.[0]?.DEPosition ?? '';
173
- },
174
- score2() {
175
- return `${this.bout.Score2}`;
176
- },
177
- getTopSpace() {
178
- return this.currentConfig?.getTopSpace || '';
179
- },
180
- getCardSpace() {
181
- return this.currentConfig?.getCardSpace || '';
182
- },
183
- person1Display() {
184
- if (this.bout.Status === "Scheduled" && this.bout.Person1.DisplayName === 'BYE') {
185
- return { ...this.bout.Person1, DisplayName: '', Club: { Name: '' } };
186
- }
187
- return this.bout.Person1;
188
- },
189
- person2Display() {
190
- if (this.bout.Status === "Scheduled" && this.bout.Person2.DisplayName === 'BYE') {
191
- return { ...this.bout.Person2, DisplayName: '', Club: { Name: '' } };
192
- }
193
- return this.bout.Person2;
194
- },
195
- showGrandFinal() {
196
- return this.roundName === 'Grand Final' &&
197
- this.bout.Person1.DisplayName !== 'BYE' &&
198
- this.bout.Person2.DisplayName !== 'BYE';
199
- }
200
- }
201
- };
202
- </script>
1
+ <template>
2
+ <section v-if="showBout" class="relative w-40">
3
+ <div
4
+ v-if="showRingLabel"
5
+ data-testid="active-ring"
6
+ class="pointer-events-none absolute -top-4 left-0 z-20 flex h-4 w-40 items-center rounded-t bg-primary px-2"
7
+ >
8
+ <BaseText :text="ringName" size="xs" color="neutral" weight="bold" class="truncate uppercase" />
9
+ </div>
10
+
11
+ <div class="relative w-40 text-left" :class="{ 'opacity-70': !isSelectable }">
12
+ <TableauFencerCard
13
+ :fencer="person1Display"
14
+ :score="score1"
15
+ :position="dePosition1"
16
+ :color="hostingClubColors.Color1"
17
+ :scoreColor="getScoreColor('person1')"
18
+ :showScore="showScore1"
19
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
20
+ corners="top"
21
+ />
22
+
23
+ <div class="relative h-px bg-dropdownSelect">
24
+ <span
25
+ :ref="element => registerAnchor('input', element)"
26
+ class="pointer-events-none absolute left-0 top-1/2 h-px w-px -translate-y-1/2 opacity-0"
27
+ ></span>
28
+ <span
29
+ :ref="element => registerAnchor('output', element)"
30
+ class="pointer-events-none absolute right-0 top-1/2 h-px w-px -translate-y-1/2 opacity-0"
31
+ ></span>
32
+ </div>
33
+
34
+ <TableauFencerCard
35
+ :fencer="person2Display"
36
+ :score="score2"
37
+ :position="dePosition2"
38
+ :color="hostingClubColors.Color2"
39
+ :scoreColor="getScoreColor('person2')"
40
+ :showScore="showScore2"
41
+ :isCountingBackwardsMaxScore="isCountingBackwardsMaxScore"
42
+ corners="bottom"
43
+ />
44
+ <button
45
+ type="button"
46
+ data-testid="tableau-bout"
47
+ class="absolute inset-0 z-10 w-40 rounded text-left shadow-md outline-none ring-1 ring-transparent transition hover:shadow-lg hover:ring-2 hover:ring-bright focus-visible:ring-2 focus-visible:ring-bright disabled:cursor-default"
48
+ :disabled="!isSelectable"
49
+ :aria-label="boutLabel"
50
+ @click="handleBoutClick"
51
+ ></button>
52
+ </div>
53
+
54
+ <div
55
+ v-if="showActiveChrome"
56
+ data-testid="active-time"
57
+ class="pointer-events-none absolute left-full top-1/2 z-20 flex h-[57px] w-10 -translate-y-1/2 items-center justify-center rounded-r-md bg-primary px-1"
58
+ >
59
+ <BaseText :text="displayTime" size="xs" color="neutral" weight="bold" class="font-sourceCodePro" />
60
+ </div>
61
+ </section>
62
+ </template>
63
+
64
+ <script>
65
+ import BaseText from '../../../Atoms/Text/BaseText.vue';
66
+ import TableauFencerCard from '../TableauFencerCard/TableauFencerCard.vue';
67
+
68
+ const EMPTY_FENCER = { DisplayName: '', Club: { Name: '' }, EventPersons: [] };
69
+
70
+ const getFencerName = fencer => String(fencer?.DisplayName || '');
71
+ const isByeName = fencer => getFencerName(fencer).toUpperCase() === 'BYE';
72
+ const isMissingFencer = fencer => !fencer?.PersonId || Number(fencer.PersonId) === 0;
73
+ const isUnavailableFencer = fencer => isMissingFencer(fencer) || isByeName(fencer);
74
+ const isCompletedBout = bout => String(bout?.Status || '').toLowerCase() === 'completed';
75
+ const getNumericScore = score => Number(score ?? 0);
76
+ const normalizeSeconds = value => Math.max(0, Number(value) || 0);
77
+ const formatSeconds = value => {
78
+ const seconds = normalizeSeconds(value);
79
+ return `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`;
80
+ };
81
+ const isRunningActiveBout = bout =>
82
+ String(bout?.Status || '').toLowerCase() === 'active' && String(bout?.TimerStatus || '').toLowerCase() === 'running';
83
+
84
+ const scoreColorRules = {
85
+ person1: [
86
+ { matches: bout => !isByeName(bout.Person1) && isByeName(bout.Person2), color: 'bg-winGreen' },
87
+ { matches: bout => getNumericScore(bout.Score1) > getNumericScore(bout.Score2), color: 'bg-winGreen' },
88
+ {
89
+ matches: bout =>
90
+ getNumericScore(bout.Score1) < getNumericScore(bout.Score2) && String(bout.Status).toLowerCase() === 'active',
91
+ color: 'bg-lossRed'
92
+ }
93
+ ],
94
+ person2: [
95
+ { matches: bout => !isByeName(bout.Person2) && isByeName(bout.Person1), color: 'bg-winGreen' },
96
+ { matches: bout => getNumericScore(bout.Score2) > getNumericScore(bout.Score1), color: 'bg-winGreen' },
97
+ {
98
+ matches: bout =>
99
+ getNumericScore(bout.Score2) < getNumericScore(bout.Score1) && String(bout.Status).toLowerCase() === 'active',
100
+ color: 'bg-lossRed'
101
+ }
102
+ ]
103
+ };
104
+
105
+ const getMatchingScoreColor = (bout, fencerKey) =>
106
+ scoreColorRules[fencerKey].find(rule => rule.matches(bout))?.color || 'bg-dropdownSelect';
107
+
108
+ const getDisplayFencer = (bout, fencer) => {
109
+ if (!fencer) return EMPTY_FENCER;
110
+ if (String(bout.Status).toLowerCase() !== 'scheduled' || !isByeName(fencer)) return fencer;
111
+ return { ...fencer, DisplayName: '', Club: { Name: '' } };
112
+ };
113
+
114
+ export default {
115
+ name: 'TableauBoutCard',
116
+ components: { BaseText, TableauFencerCard },
117
+ emits: ['action:directingBout', 'action:editBout', 'register-node-anchor'],
118
+ props: {
119
+ bout: {
120
+ type: Object,
121
+ required: true
122
+ },
123
+ hostingClubColors: {
124
+ type: Object,
125
+ required: true
126
+ },
127
+ roundName: {
128
+ type: String,
129
+ required: true
130
+ },
131
+ bracketSize: {
132
+ type: String,
133
+ required: true
134
+ },
135
+ isCountingBackwardsMaxScore: {
136
+ type: Number,
137
+ default: 0
138
+ }
139
+ },
140
+ data() {
141
+ return {
142
+ remainingTime: normalizeSeconds(this.bout.TimeLeft),
143
+ timerInterval: null
144
+ };
145
+ },
146
+ computed: {
147
+ boutNodeId() {
148
+ return this.bout.DEBoutId ?? this.bout.BoutId;
149
+ },
150
+ boutLabel() {
151
+ return `${this.person1Display.DisplayName || 'Unassigned'} versus ${
152
+ this.person2Display.DisplayName || 'Unassigned'
153
+ }`;
154
+ },
155
+ dePosition1() {
156
+ return this.bout.Person1?.EventPersons?.[0]?.DEPosition ?? '';
157
+ },
158
+ dePosition2() {
159
+ return this.bout.Person2?.EventPersons?.[0]?.DEPosition ?? '';
160
+ },
161
+ displayTime() {
162
+ return formatSeconds(this.remainingTime);
163
+ },
164
+ isActiveBout() {
165
+ return String(this.bout.Status || '').toLowerCase() === 'active';
166
+ },
167
+ isDirectable() {
168
+ return !isUnavailableFencer(this.bout.Person1) && !isUnavailableFencer(this.bout.Person2);
169
+ },
170
+ isSelectable() {
171
+ return isCompletedBout(this.bout) || this.isDirectable;
172
+ },
173
+ person1Display() {
174
+ return getDisplayFencer(this.bout, this.bout.Person1);
175
+ },
176
+ person2Display() {
177
+ return getDisplayFencer(this.bout, this.bout.Person2);
178
+ },
179
+ ringName() {
180
+ return String(this.bout.RingName || '');
181
+ },
182
+ score1() {
183
+ return String(this.bout.Score1 ?? 0);
184
+ },
185
+ score2() {
186
+ return String(this.bout.Score2 ?? 0);
187
+ },
188
+ showBout() {
189
+ if (this.roundName !== 'Grand Final') return true;
190
+ return !isUnavailableFencer(this.bout.Person1) && !isUnavailableFencer(this.bout.Person2);
191
+ },
192
+ showActiveChrome() {
193
+ return this.isActiveBout && (this.ringName.length > 0 || this.remainingTime > 0 || this.shouldTickTimer);
194
+ },
195
+ showScore1() {
196
+ return !isUnavailableFencer(this.bout.Person1);
197
+ },
198
+ showScore2() {
199
+ return !isUnavailableFencer(this.bout.Person2);
200
+ },
201
+ showRingLabel() {
202
+ return this.isActiveBout && this.ringName.length > 0;
203
+ },
204
+ shouldTickTimer() {
205
+ return isRunningActiveBout(this.bout) && this.remainingTime > 0;
206
+ }
207
+ },
208
+ watch: {
209
+ 'bout.TimeLeft'(value) {
210
+ this.remainingTime = normalizeSeconds(value);
211
+ this.restartTimer();
212
+ },
213
+ 'bout.TimerStatus'() {
214
+ this.restartTimer();
215
+ },
216
+ 'bout.Status'() {
217
+ this.restartTimer();
218
+ }
219
+ },
220
+ mounted() {
221
+ this.restartTimer();
222
+ },
223
+ beforeUnmount() {
224
+ this.stopTimer();
225
+ },
226
+ methods: {
227
+ getScoreColor(fencerKey) {
228
+ return getMatchingScoreColor(this.bout, fencerKey);
229
+ },
230
+ handleBoutClick() {
231
+ if (isCompletedBout(this.bout)) {
232
+ this.$emit('action:editBout', this.bout);
233
+ return;
234
+ }
235
+ if (this.isDirectable) this.$emit('action:directingBout', this.bout);
236
+ },
237
+ restartTimer() {
238
+ this.stopTimer();
239
+ if (!this.shouldTickTimer) return;
240
+ this.timerInterval = setInterval(this.tickTimer, 1000);
241
+ },
242
+ registerAnchor(kind, element) {
243
+ if (this.boutNodeId === null || this.boutNodeId === undefined) return;
244
+ this.$emit('register-node-anchor', { id: this.boutNodeId, kind, el: element });
245
+ },
246
+ stopTimer() {
247
+ if (this.timerInterval) clearInterval(this.timerInterval);
248
+ this.timerInterval = null;
249
+ },
250
+ tickTimer() {
251
+ this.remainingTime = Math.max(0, this.remainingTime - 1);
252
+ if (this.remainingTime === 0) this.stopTimer();
253
+ }
254
+ }
255
+ };
256
+ </script>