@dcrackel/hematournamentui 1.0.712 → 1.0.713

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 (99) hide show
  1. package/.prettierignore +7 -7
  2. package/.storybook/main.js +3 -1
  3. package/README.md +30 -0
  4. package/package.json +96 -96
  5. package/prettier.config.js +16 -16
  6. package/src/main.js +1 -1
  7. package/src/mocks/getDEBouts-TableOf128.js +37 -37
  8. package/src/mocks/getDEBouts-TableOf64.js +35 -35
  9. package/src/stories/Atoms/Input/BaseInput.test.js +97 -97
  10. package/src/stories/Atoms/Input/BaseInput.vue +149 -149
  11. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.stories.js +23 -23
  12. package/src/stories/Molecules/Indicators/MissingHemaRatingsId/MissingHemaRatingsId.vue +35 -35
  13. package/src/stories/Molecules/Indicators/RegistrationStatus/RegistrationStatus.vue +30 -30
  14. package/src/stories/Molecules/Menus/HoverActions/HoverActions.test.js +138 -0
  15. package/src/stories/Molecules/Menus/HoverActions/HoverActions.vue +212 -0
  16. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.stories.js +6 -0
  17. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.test.js +18 -18
  18. package/src/stories/Molecules/Modals/DirectorModal/DirectorModal.vue +1 -1
  19. package/src/stories/Organisms/Cards/BoutCard/BoutCard.vue +5 -1
  20. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardActive/BoutCardActive.vue +19 -10
  21. package/src/stories/Organisms/Cards/BoutCard/Variants/BoutCardInactive/BoutCardInactive.vue +11 -7
  22. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.stories.js +33 -33
  23. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.test.js +45 -45
  24. package/src/stories/Organisms/Cards/BracketDirectorAssignmentCard/BracketDirectorAssignmentCard.vue +103 -103
  25. package/src/stories/Organisms/Cards/Director/DirectorCard.test.js +82 -0
  26. package/src/stories/Organisms/Cards/Director/DirectorCard.vue +32 -51
  27. package/src/stories/Organisms/Cards/EditBout/EditBout.test.js +51 -51
  28. package/src/stories/Organisms/Cards/EventCard/EventCard.test.js +106 -106
  29. package/src/stories/Organisms/Cards/FencerCard/FencerCard.vue +298 -298
  30. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.stories.js +77 -60
  31. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.test.js +144 -143
  32. package/src/stories/Organisms/Cards/TableauBoutCard/TableauBoutCard.vue +289 -256
  33. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.stories.js +69 -69
  34. package/src/stories/Organisms/Cards/TableauFencerCard/TableauFencerCard.vue +80 -80
  35. package/src/stories/Organisms/Cards/WaitlistFencerCard/WaitlistFencerCard.vue +153 -153
  36. package/src/stories/Organisms/ComplexInputs/DropDown/DropDownMenu.vue +4 -0
  37. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -31
  38. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -109
  39. package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -192
  40. package/src/stories/Organisms/Containers/Tableau/Tableau.stories.js +1 -0
  41. package/src/stories/Organisms/Containers/Tableau/Tableau.test.js +247 -204
  42. package/src/stories/Organisms/Containers/Tableau/Tableau.vue +89 -15
  43. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.test.js +29 -29
  44. package/src/stories/Organisms/Containers/Tableau/components/BracketDirectorAssignmentPanel.vue +46 -46
  45. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.test.js +21 -21
  46. package/src/stories/Organisms/Containers/Tableau/components/TableauSvgOverlay.vue +39 -39
  47. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.js +145 -145
  48. package/src/stories/Organisms/Containers/Tableau/layout/TableauLayoutEngine.test.js +64 -64
  49. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.js +36 -57
  50. package/src/stories/Organisms/Containers/Tableau/plannedAssignments.test.js +101 -65
  51. package/src/stories/Organisms/Containers/TableauColumn/TableauColumn.vue +14 -1
  52. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.stories.js +52 -52
  53. package/src/stories/Organisms/Wizards/EditEventWizard/BasicEventInfo/BasicEventInfo.vue +10 -3
  54. package/src/stories/Organisms/Wizards/EditEventWizard/DirectElimination/DirectElimination.vue +226 -226
  55. package/src/stories/Organisms/Wizards/EditEventWizard/DirectEliminationPromotion/DirectEliminationPromotion.vue +204 -204
  56. package/src/stories/Organisms/Wizards/EditEventWizard/EditEventWizard.stories.js +137 -137
  57. package/src/stories/Organisms/Wizards/EditEventWizard/EventCustomization/EventCustomization.vue +172 -172
  58. package/src/stories/Organisms/Wizards/EditEventWizard/PoolConfiguration/PoolConfiguration.vue +216 -216
  59. package/src/stories/Organisms/Wizards/EditEventWizard/PriceFields.test.js +79 -79
  60. package/src/stories/Organisms/Wizards/EditEventWizard/RuleSerialization.test.js +29 -29
  61. package/src/stories/Organisms/Wizards/EditEventWizard/WeaponSelection.test.js +138 -0
  62. package/src/stories/Templates/EventManagement/Bracket/Bracket.stories.js +123 -12
  63. package/src/stories/Templates/EventManagement/Bracket/Bracket.test.js +178 -166
  64. package/src/stories/Templates/EventManagement/Bracket/Bracket.vue +35 -7
  65. package/src/stories/Templates/EventManagement/Bracket/BracketDirectorAssignment.test.js +242 -0
  66. package/src/stories/Templates/EventManagement/Bracket/BracketMobile.test.js +131 -0
  67. package/src/stories/Templates/EventManagement/Bracket/BracketPlannedSuggestions.js +42 -39
  68. package/src/stories/Templates/EventManagement/Bracket/BracketStories.test.js +80 -0
  69. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.js +93 -0
  70. package/src/stories/Templates/EventManagement/Bracket/BracketStoryAssignments.test.js +152 -0
  71. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.js +39 -39
  72. package/src/stories/Templates/EventManagement/Bracket/BracketStoryHarness.test.js +79 -79
  73. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.stories.js +93 -93
  74. package/src/stories/Templates/EventManagement/EditEventInfo/EditEventInfo.vue +91 -91
  75. package/src/stories/Templates/EventManagement/PoolLive/PoolLive.test.js +121 -121
  76. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.stories.js +9 -2
  77. package/src/stories/Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue +5 -4
  78. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +81 -81
  79. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -39
  80. package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +101 -101
  81. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.stories.js +71 -71
  82. package/src/stories/Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue +86 -86
  83. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.stories.js +86 -86
  84. package/src/stories/Templates/PersonManagement/Admin/MergeAccounts/MergeAccounts.vue +247 -247
  85. package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -170
  86. package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -156
  87. package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -329
  88. package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -329
  89. package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -42
  90. package/src/stories/Templates/TournamentManagement/TournamentManagement.stories.js +84 -84
  91. package/src/stories/Templates/TournamentManagement/TournamentManagement.test.js +114 -114
  92. package/src/stories/Templates/TournamentManagement/TournamentManagement.vue +363 -363
  93. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.stories.js +116 -116
  94. package/src/stories/Templates/TournamentManagement/TournamentPageOne/TournamentPageOne.vue +307 -307
  95. package/src/stories/Util/Validations/PersonManagementValidations.js +45 -45
  96. package/src/stories/Util/Validations/PriceValidation.js +6 -6
  97. package/src/stories/Util/boutDirectorAssignment.js +46 -0
  98. package/src/stories/Util/registrationStatus.js +11 -11
  99. package/src/stories/Util/tabs.js +19 -47
package/.prettierignore CHANGED
@@ -1,7 +1,7 @@
1
- node_modules/
2
- dist/
3
- storybook-static/
4
- coverage/
5
- tailwind/output.css
6
- package-lock.json
7
- *.log
1
+ node_modules/
2
+ dist/
3
+ storybook-static/
4
+ coverage/
5
+ tailwind/output.css
6
+ package-lock.json
7
+ *.log
@@ -4,7 +4,9 @@ const config = {
4
4
  addons: ['@storybook/addon-links', '@chromatic-com/storybook', '@storybook/addon-docs'],
5
5
  framework: {
6
6
  name: '@storybook/vue3-vite',
7
- options: {}
7
+ options: {
8
+ docgen: 'vue-component-meta'
9
+ }
8
10
  },
9
11
  docs: {}
10
12
  };
package/README.md CHANGED
@@ -26,6 +26,36 @@ import '@dcrackel/hematournamentui/tailwind.css';
26
26
 
27
27
  Use `npm run build:tailwind` for Storybook's local `tailwind/output.css` workflow. Use `npm run build:package-css` when preparing CSS for the parent app or package publish.
28
28
 
29
+ ## Single bracket bout director assignment
30
+
31
+ Hover over a bracket pairing for a compact action dropdown; clicking the pairing still opens the bout directly. The dropdown offers Open bout, Resume bout, or Edit bout according to status. Assign director is enabled by default for scheduled bouts except the final and grand final; a parent can explicitly disable `showBoutDirectorAssignment` when needed. When a director is assigned, the hover menu shows their name as static text in the assigned ring/connector color above Assign Director, with bright blue as a fallback when no color is supplied. Active and completed bouts show their saved director above Resume bout or Edit bout. Unassigned bouts omit the name. No permanent icon is rendered.
32
+
33
+ ```vue
34
+ <Bracket
35
+ v-bind="bracketProps"
36
+ :showBoutDirectorAssignment="canManageAssignments"
37
+ @request:bout-director="openStaffDirectorPicker"
38
+ />
39
+ ```
40
+
41
+ `request:bout-director` carries a detached copy of the current bout, including `BoutId` and `DEBoutId` when supplied. The app opens its Staff-based picker, saves the choice, and passes updated planned coverage through `plannedDirectingStations` and `plannedBoutAssignments`. This action does not start the bout or change referee, score, sequence, or completion fields. Active, completed, and unidentified bouts have no assignment action. Planning is independent of directing eligibility, so future slots represented as BYE placeholders can also be assigned. Scheduled future bouts can be assigned before both competitors are known.
42
+
43
+ The dropdown opens after a short hover delay and stays open while moving into it. Keyboard focus reveals it immediately; Arrow Down enters its actions, arrows move between actions, and Escape dismisses it and restores focus. It closes on outside clicks or scrolling. The `HoverBoutActions` Storybook example includes a mock parent picker. Production distribution and section assignment remain app-owned. For standalone `TableauBoutCard` usage, pass `plannedDirectorName` and `plannedDirectorColor` to display planned assignment context without changing the bout's saved referee fields. `TableauColumn` accepts the `plannedStationByBout` lookup used by `Tableau` and forwards each planned director name and color. Each complete connector uses the receiving bout's planned director/ring color, including both feeders and the joining line. A receiving bout with no planned director has a neutral connector.
44
+
45
+ ## Storybook planned coverage
46
+
47
+ All bracket stories enable director assignment. The default examples start with two directors. The story parent splits scheduled main-bracket bouts into contiguous halves for two directors and quarters for four, through the semifinals, inclusive. Finals, placement, and consolation rounds receive no automatic assignments; eligible bouts there remain available for anyone to claim, including a fresh director.
48
+
49
+ Three, five, and six directors use near-equal contiguous groups within each round. Known opening BYEs add no work; future empty slots remain plannable. With two or four directors, section boundaries stay aligned with bracket halves or quarters even when BYEs make workloads uneven. With other counts, boundaries can shift as rounds narrow. Adding a director redistributes scheduled coverage and preserves existing plans for active and completed bouts. Manual bout assignment remains available through the semifinals. Finals cannot be preassigned and remain available to claim when both competitors are known.
50
+
51
+ The ThreeDirectors, FourDirectors, FiveDirectors, and SixDirectors stories demonstrate the counts. Distribution lives in the story parent simulation; reusable components still receive explicit `plannedDirectingStations` and `plannedBoutAssignments` from the consuming app.
52
+
53
+ ## Phone directing view
54
+
55
+ Below 768px, the bracket shows the bout list with connection status and planned ring suggestions. The organising panel and assignment actions are unavailable on phones, even when their props are enabled. Completed bouts sit in a collapsed section for score corrections. Returning to desktop restores the selected List View or Tableau.
56
+
57
+ Phone event navigation keeps Pools, Pool Results, Bracket, Final Results, and Penalties accessible in a scrolling row. Edit, Staff, Displays, and Checkin remain in the desktop organiser menu. Selecting List View on desktop does not change the navigation. The PhoneDirector bracket story demonstrates this responsive layout; use a viewport narrower than 768px.
58
+
29
59
  ## Event waitlist setting
30
60
 
31
61
  The event wizard exposes an Enable Waitlist toggle beneath Max Fencers on Basic Info. The boolean event field `WaitlistEnabled` defaults to `true` when absent and preserves an explicit `false`. Both `update:submit` and `save:step` include the field on the event payload, separately from `rules`. The consuming app persists it and returns the saved value through the `event` prop; registration availability and capacity enforcement remain app-owned.
package/package.json CHANGED
@@ -1,96 +1,96 @@
1
- {
2
- "name": "@dcrackel/hematournamentui",
3
- "private": false,
4
- "version": "1.0.712",
5
- "type": "module",
6
- "main": "dist/HemaTournamentUI-lib.umd.js",
7
- "module": "dist/HemaTournamentUI-lib.es.js",
8
- "style": "dist/hematournamentui.css",
9
- "exports": {
10
- ".": {
11
- "import": "./dist/HemaTournamentUI-lib.es.js",
12
- "require": "./dist/HemaTournamentUI-lib.umd.js"
13
- },
14
- "./styles.css": "./dist/hematournamentui.css",
15
- "./tailwind.css": "./dist/hematournamentui-tailwind.css"
16
- },
17
- "scripts": {
18
- "dev-donotuse": "vite",
19
- "build": "npm run format:check && npm version patch && vite build && npm run build:package-css",
20
- "build:check": "npm run format:check && npm run lint && vite build && npm run build:package-css",
21
- "build:package-css": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./dist/hematournamentui-tailwind.css",
22
- "publish:package": "npm publish",
23
- "preview": "vite preview",
24
- "build-storybook": "npm run build:tailwind && storybook build",
25
- "build:tailwind": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./tailwind/output.css",
26
- "storybook": "concurrently \"npm run watch:tailwind\" \"npm run watch:storybook\"",
27
- "watch:storybook": "storybook dev -p 6006",
28
- "watch:tailwind": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./tailwind/output.css --watch",
29
- "format": "prettier --write .",
30
- "format:check": "prettier --check .",
31
- "lint": "eslint --ext .ts,.vue .",
32
- "test": "vitest",
33
- "coverage": "vitest run --coverage",
34
- "postinstall": "patch-package"
35
- },
36
- "dependencies": {
37
- "@fontsource/fira-code": "^5.2.7",
38
- "@fontsource/inter": "^5.2.8",
39
- "@fontsource/roboto-mono": "^5.2.9",
40
- "@storybook/icons": "^2.0.2",
41
- "@tiptap/extension-bold": "^2.1.13",
42
- "@tiptap/extension-highlight": "^2.1.13",
43
- "@tiptap/extension-text-align": "^2.1.13",
44
- "@tiptap/starter-kit": "^2.1.13",
45
- "@tiptap/vue-3": "^2.1.13",
46
- "cropperjs": "^2.1.1",
47
- "font-awesome": "^4.7.0",
48
- "glob": "^13.0.6",
49
- "lru-cache": "^11.3.6",
50
- "node-fetch": "^3.3.2",
51
- "postcss-nesting": "^14.0.0",
52
- "rimraf": "^6.1.3",
53
- "vue": "^3.5.34",
54
- "vue-avatar-cropper": "^6.1.1",
55
- "vue-google-autocomplete": "^1.1.4",
56
- "vue3-draggable": "^2.0.9",
57
- "vuedraggable": "^4.1.0"
58
- },
59
- "devDependencies": {
60
- "@babel/eslint-parser": "^7.28.6",
61
- "@chromatic-com/storybook": "^5.5.1",
62
- "@eslint/eslintrc": "^3.3.5",
63
- "@storybook/addon-docs": "^10.6.2",
64
- "@storybook/addon-links": "^10.6.2",
65
- "@storybook/react": "^10.6.2",
66
- "@storybook/vue3-vite": "^10.6.2",
67
- "@tailwindcss/cli": "^4.3.0",
68
- "@tailwindcss/postcss": "^4.3.0",
69
- "@typescript-eslint/eslint-plugin": "^8.59.3",
70
- "@typescript-eslint/parser": "^8.59.3",
71
- "@vitejs/plugin-vue": "^6.0.7",
72
- "@vitest/coverage-v8": "^4.1.11",
73
- "@vue/test-utils": "^2.4.10",
74
- "@vuepic/vue-datepicker": "^12.1.0",
75
- "autoprefixer": "^10.5.0",
76
- "concurrently": "^9.2.1",
77
- "eslint": "^9.9.0",
78
- "eslint-plugin-jest": "^29.15.2",
79
- "eslint-plugin-prettier": "^5.5.5",
80
- "eslint-plugin-storybook": "^10.6.2",
81
- "eslint-plugin-vue": "^10.9.1",
82
- "happy-dom": "^20.9.0",
83
- "patch-package": "^8.0.1",
84
- "postcss": "^8.5.14",
85
- "prettier": "^3.9.6",
86
- "storybook": "^10.6.2",
87
- "storybook-vue3-router": "^7.0.0",
88
- "tailwindcss": "^4.3.0",
89
- "vite": "^8.0.13",
90
- "vitest": "^4.1.11",
91
- "vue-eslint-parser": "^10.4.0"
92
- },
93
- "overrides": {
94
- "@storybook/preview-api": "^9.1.5"
95
- }
96
- }
1
+ {
2
+ "name": "@dcrackel/hematournamentui",
3
+ "private": false,
4
+ "version": "1.0.713",
5
+ "type": "module",
6
+ "main": "dist/HemaTournamentUI-lib.umd.js",
7
+ "module": "dist/HemaTournamentUI-lib.es.js",
8
+ "style": "dist/hematournamentui.css",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/HemaTournamentUI-lib.es.js",
12
+ "require": "./dist/HemaTournamentUI-lib.umd.js"
13
+ },
14
+ "./styles.css": "./dist/hematournamentui.css",
15
+ "./tailwind.css": "./dist/hematournamentui-tailwind.css"
16
+ },
17
+ "scripts": {
18
+ "dev-donotuse": "vite",
19
+ "build": "npm run format:check && npm version patch && vite build && npm run build:package-css",
20
+ "build:check": "npm run format:check && npm run lint && vite build && npm run build:package-css",
21
+ "build:package-css": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./dist/hematournamentui-tailwind.css",
22
+ "publish:package": "npm publish",
23
+ "preview": "vite preview",
24
+ "build-storybook": "npm run build:tailwind && storybook build",
25
+ "build:tailwind": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./tailwind/output.css",
26
+ "storybook": "concurrently \"npm run watch:tailwind\" \"npm run watch:storybook\"",
27
+ "watch:storybook": "storybook dev -p 6006",
28
+ "watch:tailwind": "npx @tailwindcss/cli -i ./tailwind/tailwind.css -o ./tailwind/output.css --watch",
29
+ "format": "prettier --write .",
30
+ "format:check": "prettier --check .",
31
+ "lint": "eslint --ext .ts,.vue .",
32
+ "test": "vitest",
33
+ "coverage": "vitest run --coverage",
34
+ "postinstall": "patch-package"
35
+ },
36
+ "dependencies": {
37
+ "@fontsource/fira-code": "^5.2.7",
38
+ "@fontsource/inter": "^5.2.8",
39
+ "@fontsource/roboto-mono": "^5.2.9",
40
+ "@storybook/icons": "^2.0.2",
41
+ "@tiptap/extension-bold": "^2.1.13",
42
+ "@tiptap/extension-highlight": "^2.1.13",
43
+ "@tiptap/extension-text-align": "^2.1.13",
44
+ "@tiptap/starter-kit": "^2.1.13",
45
+ "@tiptap/vue-3": "^2.1.13",
46
+ "cropperjs": "^2.1.1",
47
+ "font-awesome": "^4.7.0",
48
+ "glob": "^13.0.6",
49
+ "lru-cache": "^11.3.6",
50
+ "node-fetch": "^3.3.2",
51
+ "postcss-nesting": "^14.0.0",
52
+ "rimraf": "^6.1.3",
53
+ "vue": "^3.5.34",
54
+ "vue-avatar-cropper": "^6.1.1",
55
+ "vue-google-autocomplete": "^1.1.4",
56
+ "vue3-draggable": "^2.0.9",
57
+ "vuedraggable": "^4.1.0"
58
+ },
59
+ "devDependencies": {
60
+ "@babel/eslint-parser": "^7.28.6",
61
+ "@chromatic-com/storybook": "^5.5.1",
62
+ "@eslint/eslintrc": "^3.3.5",
63
+ "@storybook/addon-docs": "^10.6.2",
64
+ "@storybook/addon-links": "^10.6.2",
65
+ "@storybook/react": "^10.6.2",
66
+ "@storybook/vue3-vite": "^10.6.2",
67
+ "@tailwindcss/cli": "^4.3.0",
68
+ "@tailwindcss/postcss": "^4.3.0",
69
+ "@typescript-eslint/eslint-plugin": "^8.59.3",
70
+ "@typescript-eslint/parser": "^8.59.3",
71
+ "@vitejs/plugin-vue": "^6.0.7",
72
+ "@vitest/coverage-v8": "^4.1.11",
73
+ "@vue/test-utils": "^2.4.10",
74
+ "@vuepic/vue-datepicker": "^12.1.0",
75
+ "autoprefixer": "^10.5.0",
76
+ "concurrently": "^9.2.1",
77
+ "eslint": "^9.9.0",
78
+ "eslint-plugin-jest": "^29.15.2",
79
+ "eslint-plugin-prettier": "^5.5.5",
80
+ "eslint-plugin-storybook": "^10.6.2",
81
+ "eslint-plugin-vue": "^10.9.1",
82
+ "happy-dom": "^20.9.0",
83
+ "patch-package": "^8.0.1",
84
+ "postcss": "^8.5.14",
85
+ "prettier": "^3.9.6",
86
+ "storybook": "^10.6.2",
87
+ "storybook-vue3-router": "^7.0.0",
88
+ "tailwindcss": "^4.3.0",
89
+ "vite": "^8.0.13",
90
+ "vitest": "^4.1.11",
91
+ "vue-eslint-parser": "^10.4.0"
92
+ },
93
+ "overrides": {
94
+ "@storybook/preview-api": "^9.1.5"
95
+ }
96
+ }
@@ -1,16 +1,16 @@
1
- export default {
2
- arrowParens: 'avoid',
3
- bracketSameLine: false,
4
- bracketSpacing: true,
5
- endOfLine: 'auto',
6
- htmlWhitespaceSensitivity: 'css',
7
- printWidth: 120,
8
- proseWrap: 'preserve',
9
- semi: true,
10
- singleAttributePerLine: false,
11
- singleQuote: true,
12
- tabWidth: 2,
13
- trailingComma: 'none',
14
- useTabs: false,
15
- vueIndentScriptAndStyle: false
16
- };
1
+ export default {
2
+ arrowParens: 'avoid',
3
+ bracketSameLine: false,
4
+ bracketSpacing: true,
5
+ endOfLine: 'auto',
6
+ htmlWhitespaceSensitivity: 'css',
7
+ printWidth: 120,
8
+ proseWrap: 'preserve',
9
+ semi: true,
10
+ singleAttributePerLine: false,
11
+ singleQuote: true,
12
+ tabWidth: 2,
13
+ trailingComma: 'none',
14
+ useTabs: false,
15
+ vueIndentScriptAndStyle: false
16
+ };
package/src/main.js CHANGED
@@ -1 +1 @@
1
- import '../tailwind/output.css';
1
+ import '../tailwind/output.css';
@@ -1,37 +1,37 @@
1
- import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
-
3
- const roundLabelRules = [
4
- { matches: boutId => boutId >= 1 && boutId <= 64, label: 'Table of 128' },
5
- { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
6
- { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
7
- { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
8
- { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
9
- { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
10
- { matches: boutId => boutId === 127, label: 'Final' }
11
- ];
12
-
13
- const getCurrentRoundLabel = bout => {
14
- const boutId = Number(bout.DEBoutId);
15
- return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
16
- };
17
-
18
- const normalizeTableOf128Bout = bout => ({
19
- ...bout,
20
- RoundLabel: getCurrentRoundLabel(bout)
21
- });
22
-
23
- const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
24
-
25
- const getDEBoutsTableOf128 = {
26
- ...historicalSixtySevenFencerBracket,
27
- message: 'Current Table of 128 fixture retrieved',
28
- pools: [
29
- {
30
- ...historicalPool,
31
- Bouts: historicalPool.Bouts.map(normalizeTableOf128Bout)
32
- },
33
- ...otherPools
34
- ]
35
- };
36
-
37
- export default getDEBoutsTableOf128;
1
+ import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
+
3
+ const roundLabelRules = [
4
+ { matches: boutId => boutId >= 1 && boutId <= 64, label: 'Table of 128' },
5
+ { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
6
+ { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
7
+ { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
8
+ { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
9
+ { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
10
+ { matches: boutId => boutId === 127, label: 'Final' }
11
+ ];
12
+
13
+ const getCurrentRoundLabel = bout => {
14
+ const boutId = Number(bout.DEBoutId);
15
+ return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
16
+ };
17
+
18
+ const normalizeTableOf128Bout = bout => ({
19
+ ...bout,
20
+ RoundLabel: getCurrentRoundLabel(bout)
21
+ });
22
+
23
+ const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
24
+
25
+ const getDEBoutsTableOf128 = {
26
+ ...historicalSixtySevenFencerBracket,
27
+ message: 'Current Table of 128 fixture retrieved',
28
+ pools: [
29
+ {
30
+ ...historicalPool,
31
+ Bouts: historicalPool.Bouts.map(normalizeTableOf128Bout)
32
+ },
33
+ ...otherPools
34
+ ]
35
+ };
36
+
37
+ export default getDEBoutsTableOf128;
@@ -1,35 +1,35 @@
1
- import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
-
3
- const FIRST_TABLE_OF_64_BOUT_ID = 65;
4
-
5
- const roundLabelRules = [
6
- { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
7
- { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
8
- { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
9
- { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
10
- { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
11
- { matches: boutId => boutId === 127, label: 'Final' }
12
- ];
13
-
14
- const isTableOf64Bout = bout => Number(bout.DEBoutId) >= FIRST_TABLE_OF_64_BOUT_ID;
15
-
16
- const getCurrentRoundLabel = bout => {
17
- const boutId = Number(bout.DEBoutId);
18
- return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
19
- };
20
-
21
- const normalizeTableOf64Bout = bout => ({
22
- ...bout,
23
- RoundLabel: getCurrentRoundLabel(bout)
24
- });
25
-
26
- const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
27
- const tableOf64Bouts = historicalPool.Bouts.filter(isTableOf64Bout).map(normalizeTableOf64Bout);
28
-
29
- const getDEBoutsTableOf64 = {
30
- ...historicalSixtySevenFencerBracket,
31
- message: 'Current Table of 64 fixture retrieved',
32
- pools: [{ ...historicalPool, Bouts: tableOf64Bouts }, ...otherPools]
33
- };
34
-
35
- export default getDEBoutsTableOf64;
1
+ import historicalSixtySevenFencerBracket from './getDEBouts-67Fencers.js';
2
+
3
+ const FIRST_TABLE_OF_64_BOUT_ID = 65;
4
+
5
+ const roundLabelRules = [
6
+ { matches: boutId => boutId >= 65 && boutId <= 96, label: 'Table of 64' },
7
+ { matches: boutId => boutId >= 97 && boutId <= 112, label: 'Table of 32' },
8
+ { matches: boutId => boutId >= 113 && boutId <= 120, label: 'Table of 16' },
9
+ { matches: boutId => boutId >= 121 && boutId <= 124, label: 'Table of 8' },
10
+ { matches: boutId => boutId >= 125 && boutId <= 126, label: 'Semifinal' },
11
+ { matches: boutId => boutId === 127, label: 'Final' }
12
+ ];
13
+
14
+ const isTableOf64Bout = bout => Number(bout.DEBoutId) >= FIRST_TABLE_OF_64_BOUT_ID;
15
+
16
+ const getCurrentRoundLabel = bout => {
17
+ const boutId = Number(bout.DEBoutId);
18
+ return roundLabelRules.find(rule => rule.matches(boutId))?.label || bout.RoundLabel;
19
+ };
20
+
21
+ const normalizeTableOf64Bout = bout => ({
22
+ ...bout,
23
+ RoundLabel: getCurrentRoundLabel(bout)
24
+ });
25
+
26
+ const [historicalPool, ...otherPools] = historicalSixtySevenFencerBracket.pools;
27
+ const tableOf64Bouts = historicalPool.Bouts.filter(isTableOf64Bout).map(normalizeTableOf64Bout);
28
+
29
+ const getDEBoutsTableOf64 = {
30
+ ...historicalSixtySevenFencerBracket,
31
+ message: 'Current Table of 64 fixture retrieved',
32
+ pools: [{ ...historicalPool, Bouts: tableOf64Bouts }, ...otherPools]
33
+ };
34
+
35
+ export default getDEBoutsTableOf64;
@@ -1,97 +1,97 @@
1
- import BaseInput from './BaseInput.vue';
2
- import { mount } from '@vue/test-utils';
3
- import { describe, expect, test, it } from 'vitest';
4
-
5
- describe('BaseInput.vue', () => {
6
- it('renders with default props', () => {
7
- const wrapper = mount(BaseInput);
8
- const inputElement = wrapper.find('input');
9
-
10
- expect(inputElement.attributes('placeholder')).toBe('');
11
- expect(wrapper.vm.internalValue).toBe('');
12
- });
13
-
14
- it('initializes with the correct value prop', () => {
15
- const initialValue = 'Hello, World!';
16
- const wrapper = mount(BaseInput, {
17
- props: {
18
- value: initialValue
19
- }
20
- });
21
-
22
- expect(wrapper.vm.internalValue).toBe(initialValue);
23
- });
24
-
25
- it('updates internalValue when value prop changes', async () => {
26
- const wrapper = mount(BaseInput);
27
- await wrapper.setProps({ value: 'New Value' });
28
-
29
- expect(wrapper.vm.internalValue).toBe('New Value');
30
- });
31
-
32
- it('emits the latest value synchronously before the next action', () => {
33
- const wrapper = mount(BaseInput);
34
- const inputElement = wrapper.find('input');
35
-
36
- inputElement.element.value = 'Test Input';
37
- inputElement.element.dispatchEvent(new Event('input', { bubbles: true }));
38
- expect(wrapper.emitted('update:value')).toEqual([['Test Input']]);
39
- });
40
-
41
- it('applies input border styles when addBorder is enabled', () => {
42
- const wrapper = mount(BaseInput, {
43
- props: {
44
- addBorder: true,
45
- invalid: false
46
- }
47
- });
48
- const inputElement = wrapper.find('input');
49
- expect(inputElement.classes()).toContain('border');
50
- expect(inputElement.classes()).toContain('rounded-lg');
51
- expect(inputElement.classes()).toContain('text-quinary');
52
- expect(inputElement.classes()).toContain('text-sm');
53
- expect(inputElement.classes()).toContain('p-2');
54
- expect(inputElement.classes()).toContain('w-full');
55
- expect(inputElement.classes()).toContain('border-dropdownSelect');
56
- });
57
-
58
- it('applies border-alarm class when invalid is true', () => {
59
- const wrapper = mount(BaseInput, {
60
- props: {
61
- addBorder: true,
62
- invalid: true
63
- }
64
- });
65
- const inputElement = wrapper.find('input');
66
- expect(inputElement.classes()).toContain('border-alarm');
67
- });
68
-
69
- it('does not apply border-alarm class when invalid is false', () => {
70
- const wrapper = mount(BaseInput, {
71
- props: {
72
- addBorder: true,
73
- invalid: false
74
- }
75
- });
76
- const inputElement = wrapper.find('input');
77
- expect(inputElement.classes()).not.toContain('border-alarm');
78
- expect(inputElement.classes()).toContain('border-dropdownSelect');
79
- });
80
-
81
- it('delivers a cleared value before the input is unmounted', () => {
82
- const wrapper = mount(BaseInput, { props: { value: '123', inputType: 'number' } });
83
- const input = wrapper.find('input');
84
- input.element.value = '';
85
- input.element.dispatchEvent(new Event('input', { bubbles: true }));
86
- expect(wrapper.emitted('update:value')).toEqual([['']]);
87
- wrapper.unmount();
88
- });
89
-
90
- it('does not emit edits while disabled', () => {
91
- const wrapper = mount(BaseInput, { props: { disabled: true, value: 'original' } });
92
- const input = wrapper.find('input');
93
- input.element.value = 'changed';
94
- input.element.dispatchEvent(new Event('input', { bubbles: true }));
95
- expect(wrapper.emitted('update:value')).toBeUndefined();
96
- });
97
- });
1
+ import BaseInput from './BaseInput.vue';
2
+ import { mount } from '@vue/test-utils';
3
+ import { describe, expect, test, it } from 'vitest';
4
+
5
+ describe('BaseInput.vue', () => {
6
+ it('renders with default props', () => {
7
+ const wrapper = mount(BaseInput);
8
+ const inputElement = wrapper.find('input');
9
+
10
+ expect(inputElement.attributes('placeholder')).toBe('');
11
+ expect(wrapper.vm.internalValue).toBe('');
12
+ });
13
+
14
+ it('initializes with the correct value prop', () => {
15
+ const initialValue = 'Hello, World!';
16
+ const wrapper = mount(BaseInput, {
17
+ props: {
18
+ value: initialValue
19
+ }
20
+ });
21
+
22
+ expect(wrapper.vm.internalValue).toBe(initialValue);
23
+ });
24
+
25
+ it('updates internalValue when value prop changes', async () => {
26
+ const wrapper = mount(BaseInput);
27
+ await wrapper.setProps({ value: 'New Value' });
28
+
29
+ expect(wrapper.vm.internalValue).toBe('New Value');
30
+ });
31
+
32
+ it('emits the latest value synchronously before the next action', () => {
33
+ const wrapper = mount(BaseInput);
34
+ const inputElement = wrapper.find('input');
35
+
36
+ inputElement.element.value = 'Test Input';
37
+ inputElement.element.dispatchEvent(new Event('input', { bubbles: true }));
38
+ expect(wrapper.emitted('update:value')).toEqual([['Test Input']]);
39
+ });
40
+
41
+ it('applies input border styles when addBorder is enabled', () => {
42
+ const wrapper = mount(BaseInput, {
43
+ props: {
44
+ addBorder: true,
45
+ invalid: false
46
+ }
47
+ });
48
+ const inputElement = wrapper.find('input');
49
+ expect(inputElement.classes()).toContain('border');
50
+ expect(inputElement.classes()).toContain('rounded-lg');
51
+ expect(inputElement.classes()).toContain('text-quinary');
52
+ expect(inputElement.classes()).toContain('text-sm');
53
+ expect(inputElement.classes()).toContain('p-2');
54
+ expect(inputElement.classes()).toContain('w-full');
55
+ expect(inputElement.classes()).toContain('border-dropdownSelect');
56
+ });
57
+
58
+ it('applies border-alarm class when invalid is true', () => {
59
+ const wrapper = mount(BaseInput, {
60
+ props: {
61
+ addBorder: true,
62
+ invalid: true
63
+ }
64
+ });
65
+ const inputElement = wrapper.find('input');
66
+ expect(inputElement.classes()).toContain('border-alarm');
67
+ });
68
+
69
+ it('does not apply border-alarm class when invalid is false', () => {
70
+ const wrapper = mount(BaseInput, {
71
+ props: {
72
+ addBorder: true,
73
+ invalid: false
74
+ }
75
+ });
76
+ const inputElement = wrapper.find('input');
77
+ expect(inputElement.classes()).not.toContain('border-alarm');
78
+ expect(inputElement.classes()).toContain('border-dropdownSelect');
79
+ });
80
+
81
+ it('delivers a cleared value before the input is unmounted', () => {
82
+ const wrapper = mount(BaseInput, { props: { value: '123', inputType: 'number' } });
83
+ const input = wrapper.find('input');
84
+ input.element.value = '';
85
+ input.element.dispatchEvent(new Event('input', { bubbles: true }));
86
+ expect(wrapper.emitted('update:value')).toEqual([['']]);
87
+ wrapper.unmount();
88
+ });
89
+
90
+ it('does not emit edits while disabled', () => {
91
+ const wrapper = mount(BaseInput, { props: { disabled: true, value: 'original' } });
92
+ const input = wrapper.find('input');
93
+ input.element.value = 'changed';
94
+ input.element.dispatchEvent(new Event('input', { bubbles: true }));
95
+ expect(wrapper.emitted('update:value')).toBeUndefined();
96
+ });
97
+ });