@forwardimpact/pathway 0.21.0 → 0.23.0

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 (123) hide show
  1. package/README.md +3 -3
  2. package/bin/fit-pathway.js +22 -22
  3. package/package.json +4 -3
  4. package/src/commands/agent.js +14 -10
  5. package/src/commands/behaviour.js +11 -1
  6. package/src/commands/build.js +11 -2
  7. package/src/commands/command-factory.js +4 -2
  8. package/src/commands/dev.js +9 -2
  9. package/src/commands/discipline.js +19 -2
  10. package/src/commands/driver.js +11 -1
  11. package/src/commands/index.js +1 -1
  12. package/src/commands/init.js +1 -1
  13. package/src/commands/interview.js +8 -8
  14. package/src/commands/job.js +41 -28
  15. package/src/commands/level.js +76 -0
  16. package/src/commands/progress.js +20 -20
  17. package/src/commands/questions.js +3 -3
  18. package/src/commands/skill.js +11 -1
  19. package/src/commands/stage.js +11 -1
  20. package/src/commands/tool.js +4 -3
  21. package/src/commands/track.js +11 -1
  22. package/src/components/action-buttons.js +3 -3
  23. package/src/components/builder.js +25 -25
  24. package/src/components/card.js +8 -104
  25. package/src/components/comparison-radar.js +4 -4
  26. package/src/components/detail.js +18 -120
  27. package/src/components/error-page.js +8 -68
  28. package/src/components/grid.js +12 -106
  29. package/src/components/list.js +7 -116
  30. package/src/components/nav.js +7 -60
  31. package/src/components/radar-chart.js +3 -3
  32. package/src/components/skill-matrix.js +7 -7
  33. package/src/css/bundles/app.css +25 -21
  34. package/src/css/bundles/handout.css +33 -33
  35. package/src/css/bundles/slides.css +25 -25
  36. package/src/css/pages/landing.css +5 -5
  37. package/src/formatters/index.js +5 -5
  38. package/src/formatters/interview/shared.js +23 -23
  39. package/src/formatters/job/description.js +18 -18
  40. package/src/formatters/job/dom.js +12 -12
  41. package/src/formatters/job/markdown.js +7 -7
  42. package/src/formatters/json-ld.js +24 -24
  43. package/src/formatters/{grade → level}/dom.js +31 -27
  44. package/src/formatters/{grade → level}/markdown.js +19 -28
  45. package/src/formatters/{grade → level}/microdata.js +28 -38
  46. package/src/formatters/level/shared.js +86 -0
  47. package/src/formatters/progress/markdown.js +2 -2
  48. package/src/formatters/progress/shared.js +51 -51
  49. package/src/formatters/questions/markdown.js +8 -6
  50. package/src/formatters/questions/shared.js +7 -7
  51. package/src/formatters/skill/dom.js +4 -4
  52. package/src/formatters/skill/markdown.js +1 -1
  53. package/src/formatters/skill/microdata.js +3 -3
  54. package/src/formatters/skill/shared.js +3 -3
  55. package/src/formatters/track/shared.js +1 -1
  56. package/src/handout-main.js +12 -12
  57. package/src/handout.html +32 -13
  58. package/src/index.html +33 -14
  59. package/src/lib/card-mappers.js +16 -16
  60. package/src/lib/cli-command.js +3 -3
  61. package/src/lib/cli-output.js +2 -2
  62. package/src/lib/error-boundary.js +3 -66
  63. package/src/lib/errors.js +7 -45
  64. package/src/lib/job-cache.js +12 -12
  65. package/src/lib/markdown.js +2 -109
  66. package/src/lib/reactive.js +7 -73
  67. package/src/lib/render.js +53 -201
  68. package/src/lib/router-core.js +2 -156
  69. package/src/lib/router-pages.js +2 -11
  70. package/src/lib/router-slides.js +2 -197
  71. package/src/lib/state.js +16 -65
  72. package/src/lib/utils.js +3 -10
  73. package/src/lib/yaml-loader.js +22 -80
  74. package/src/main.js +10 -10
  75. package/src/pages/agent-builder.js +12 -12
  76. package/src/pages/assessment-results.js +28 -24
  77. package/src/pages/interview-builder.js +6 -6
  78. package/src/pages/interview.js +8 -8
  79. package/src/pages/job-builder.js +7 -7
  80. package/src/pages/job.js +8 -8
  81. package/src/pages/landing.js +8 -8
  82. package/src/pages/level.js +122 -0
  83. package/src/pages/progress-builder.js +8 -8
  84. package/src/pages/progress.js +74 -74
  85. package/src/pages/self-assessment.js +7 -7
  86. package/src/pages/skill.js +1 -1
  87. package/src/slide-main.js +23 -23
  88. package/src/slides/chapter.js +4 -4
  89. package/src/slides/index.js +11 -11
  90. package/src/slides/interview.js +2 -2
  91. package/src/slides/job.js +4 -4
  92. package/src/slides/level.js +32 -0
  93. package/src/slides/overview.js +10 -10
  94. package/src/slides/progress.js +13 -13
  95. package/src/slides.html +32 -13
  96. package/src/types.js +1 -1
  97. package/templates/job.template.md +2 -2
  98. package/src/commands/grade.js +0 -60
  99. package/src/css/base.css +0 -56
  100. package/src/css/components/badges.css +0 -232
  101. package/src/css/components/buttons.css +0 -101
  102. package/src/css/components/forms.css +0 -191
  103. package/src/css/components/layout.css +0 -218
  104. package/src/css/components/nav.css +0 -206
  105. package/src/css/components/progress.css +0 -166
  106. package/src/css/components/states.css +0 -82
  107. package/src/css/components/surfaces.css +0 -347
  108. package/src/css/components/tables.css +0 -362
  109. package/src/css/components/top-bar.css +0 -180
  110. package/src/css/components/typography.css +0 -121
  111. package/src/css/components/utilities.css +0 -41
  112. package/src/css/pages/detail.css +0 -119
  113. package/src/css/reset.css +0 -50
  114. package/src/css/tokens.css +0 -162
  115. package/src/css/views/handout.css +0 -30
  116. package/src/css/views/print.css +0 -634
  117. package/src/css/views/slide-animations.css +0 -113
  118. package/src/css/views/slide-base.css +0 -331
  119. package/src/css/views/slide-sections.css +0 -597
  120. package/src/css/views/slide-tables.css +0 -275
  121. package/src/formatters/grade/shared.js +0 -86
  122. package/src/pages/grade.js +0 -122
  123. package/src/slides/grade.js +0 -32
@@ -1,13 +1,20 @@
1
1
  /**
2
- * Reusable detail view component
2
+ * Detail view components
3
+ *
4
+ * Re-exports generic components from @forwardimpact/libui/components/detail
5
+ * and provides domain-specific level display components.
3
6
  */
4
7
 
8
+ export {
9
+ createDetailHeader,
10
+ createDetailSection,
11
+ createLinksList,
12
+ createTagsList,
13
+ createDetailItem,
14
+ } from "@forwardimpact/libui/components/detail";
15
+
5
16
  import {
6
17
  div,
7
- h1,
8
- h2,
9
- p,
10
- a,
11
18
  span,
12
19
  table,
13
20
  thead,
@@ -15,63 +22,13 @@ import {
15
22
  tr,
16
23
  th,
17
24
  td,
18
- section,
19
- } from "../lib/render.js";
20
- import { createBackLink } from "./nav.js";
21
- import { createTag } from "./card.js";
22
- import { formatLevel } from "../lib/render.js";
25
+ formatLevel,
26
+ } from "@forwardimpact/libui/render";
23
27
  import {
24
- SKILL_LEVEL_ORDER,
28
+ SKILL_PROFICIENCY_ORDER,
25
29
  BEHAVIOUR_MATURITY_ORDER,
26
30
  } from "@forwardimpact/map/levels";
27
31
 
28
- /**
29
- * Create a detail page header
30
- * @param {Object} options
31
- * @param {string} options.title
32
- * @param {string} [options.description]
33
- * @param {string} options.backLink - Path to go back to
34
- * @param {string} [options.backText] - Back link text
35
- * @param {HTMLElement[]} [options.badges]
36
- * @param {HTMLElement[]} [options.actions] - Action buttons
37
- * @returns {HTMLElement}
38
- */
39
- export function createDetailHeader({
40
- title,
41
- description,
42
- backLink,
43
- backText = "← Back to list",
44
- badges = [],
45
- actions = [],
46
- }) {
47
- return div(
48
- { className: "page-header" },
49
- createBackLink(backLink, backText),
50
- div(
51
- { className: "card-header" },
52
- h1({ className: "page-title" }, title),
53
- badges.length > 0 ? div({ className: "page-meta" }, ...badges) : null,
54
- ),
55
- description ? p({ className: "page-description" }, description) : null,
56
- actions.length > 0 ? div({ className: "page-actions" }, ...actions) : null,
57
- );
58
- }
59
-
60
- /**
61
- * Create a detail section
62
- * @param {Object} options
63
- * @param {string} options.title
64
- * @param {HTMLElement} options.content
65
- * @returns {HTMLElement}
66
- */
67
- export function createDetailSection({ title, content }) {
68
- return section(
69
- { className: "section section-detail" },
70
- h2({ className: "section-title" }, title),
71
- content,
72
- );
73
- }
74
-
75
32
  /**
76
33
  * Create a level descriptions table
77
34
  * @param {Object} descriptions - Level descriptions object
@@ -80,7 +37,7 @@ export function createDetailSection({ title, content }) {
80
37
  */
81
38
  export function createLevelTable(descriptions, type = "skill") {
82
39
  const levels =
83
- type === "skill" ? SKILL_LEVEL_ORDER : BEHAVIOUR_MATURITY_ORDER;
40
+ type === "skill" ? SKILL_PROFICIENCY_ORDER : BEHAVIOUR_MATURITY_ORDER;
84
41
 
85
42
  const levelLabels = Object.fromEntries(
86
43
  levels.map((level, index) => [level, String(index + 1)]),
@@ -89,7 +46,7 @@ export function createLevelTable(descriptions, type = "skill") {
89
46
  const maxLevels = levels.length;
90
47
 
91
48
  const rows = levels.map((level) => {
92
- const description = descriptions?.[level] || "";
49
+ const description = descriptions?.[level] || "\u2014";
93
50
  const levelIndex = parseInt(levelLabels[level]);
94
51
  return tr(
95
52
  {},
@@ -147,66 +104,7 @@ export function createLevelCell(levelIndex, maxLevels, levelName) {
147
104
  export function createEmptyLevelCell() {
148
105
  return td(
149
106
  { className: "level-cell" },
150
- span({ className: "level-label text-muted" }, ""),
151
- );
152
- }
153
-
154
- /**
155
- * Create a links list
156
- * @param {Array<{id: string, name: string}>} items
157
- * @param {string} basePath - Base path for links (e.g., '/skill')
158
- * @param {string} [emptyMessage='None']
159
- * @param {Function} [getDisplayName] - Optional function to get display name
160
- * @returns {HTMLElement}
161
- */
162
- export function createLinksList(
163
- items,
164
- basePath,
165
- emptyMessage = "None",
166
- getDisplayName,
167
- ) {
168
- if (!items || items.length === 0) {
169
- return p({ className: "text-muted" }, emptyMessage);
170
- }
171
-
172
- const displayFn = getDisplayName || ((item) => item.name);
173
- const links = items.map((item) =>
174
- a({ href: `#${basePath}/${item.id}` }, displayFn(item)),
175
- );
176
-
177
- return div({ className: "links-list" }, ...links);
178
- }
179
-
180
- /**
181
- * Create a tags list
182
- * @param {string[]} tags
183
- * @param {string} [emptyMessage='None']
184
- * @returns {HTMLElement}
185
- */
186
- export function createTagsList(tags, emptyMessage = "None") {
187
- if (!tags || tags.length === 0) {
188
- return p({ className: "text-muted" }, emptyMessage);
189
- }
190
-
191
- return div({ className: "info-tags" }, ...tags.map((tag) => createTag(tag)));
192
- }
193
-
194
- /**
195
- * Create a detail grid item
196
- * @param {string} label
197
- * @param {string|HTMLElement} value
198
- * @returns {HTMLElement}
199
- */
200
- export function createDetailItem(label, value) {
201
- const valueEl =
202
- typeof value === "string"
203
- ? div({ className: "detail-item-value" }, value)
204
- : value;
205
-
206
- return div(
207
- { className: "detail-item" },
208
- div({ className: "detail-item-label" }, label),
209
- valueEl,
107
+ span({ className: "level-label text-muted" }, "\u2014"),
210
108
  );
211
109
  }
212
110
 
@@ -1,72 +1,12 @@
1
1
  /**
2
2
  * Error page components
3
+ *
4
+ * Re-exports from @forwardimpact/libui/components/error-page.
3
5
  */
4
6
 
5
- import { render, div, h1, p, a } from "../lib/render.js";
6
-
7
- /**
8
- * Render a not found error page
9
- * @param {Object} options - Configuration options
10
- * @param {string} options.entityType - Type of entity not found (e.g., 'Skill', 'Behaviour')
11
- * @param {string} options.entityId - ID that was not found
12
- * @param {string} options.backPath - Path to navigate back to
13
- * @param {string} options.backText - Text for back link
14
- */
15
- export function renderNotFound({ entityType, entityId, backPath, backText }) {
16
- render(
17
- div(
18
- { className: "error-message" },
19
- h1({}, `${entityType} Not Found`),
20
- p({}, `No ${entityType.toLowerCase()} found with ID: ${entityId}`),
21
- a({ href: `#${backPath}` }, backText),
22
- ),
23
- );
24
- }
25
-
26
- /**
27
- * Create a not found error element (without rendering)
28
- * @param {Object} options - Configuration options
29
- * @param {string} options.entityType - Type of entity not found
30
- * @param {string} options.entityId - ID that was not found
31
- * @param {string} options.backPath - Path to navigate back to
32
- * @param {string} options.backText - Text for back link
33
- * @returns {HTMLElement}
34
- */
35
- export function createNotFound({ entityType, entityId, backPath, backText }) {
36
- return div(
37
- { className: "error-message" },
38
- h1({}, `${entityType} Not Found`),
39
- p({}, `No ${entityType.toLowerCase()} found with ID: ${entityId}`),
40
- a({ href: `#${backPath}` }, backText),
41
- );
42
- }
43
-
44
- /**
45
- * Create an invalid state error element
46
- * @param {Object} options - Configuration options
47
- * @param {string} options.title - Error title
48
- * @param {string} options.message - Error message
49
- * @param {string} options.backPath - Path to navigate back to
50
- * @param {string} options.backText - Text for back link
51
- * @returns {HTMLElement}
52
- */
53
- export function createErrorMessage({ title, message, backPath, backText }) {
54
- return div(
55
- { className: "error-message" },
56
- h1({}, title),
57
- p({}, message),
58
- a({ href: `#${backPath}` }, backText),
59
- );
60
- }
61
-
62
- /**
63
- * Render an invalid state error page
64
- * @param {Object} options - Configuration options
65
- * @param {string} options.title - Error title
66
- * @param {string} options.message - Error message
67
- * @param {string} options.backPath - Path to navigate back to
68
- * @param {string} options.backText - Text for back link
69
- */
70
- export function renderError({ title, message, backPath, backText }) {
71
- render(createErrorMessage({ title, message, backPath, backText }));
72
- }
7
+ export {
8
+ renderNotFound,
9
+ createNotFound,
10
+ createErrorMessage,
11
+ renderError,
12
+ } from "@forwardimpact/libui/components/error-page";
@@ -1,109 +1,15 @@
1
1
  /**
2
2
  * Grid components and utilities
3
3
  *
4
- * This module provides reusable grid components that wrap the CSS grid utilities.
5
- * All grids use the unified CSS classes: auto-grid-xs, auto-grid-sm, auto-grid-md, auto-grid-lg
6
- *
7
- * Grid sizes:
8
- * - xs: 150px min column width (compact stats, small cards)
9
- * - sm: 200px min column width (form controls, medium cards)
10
- * - md: 300px min column width (detail items, content cards)
11
- * - lg: 400px min column width (large content like radar charts)
12
- */
13
-
14
- import { div } from "../lib/render.js";
15
-
16
- /**
17
- * Grid size options
18
- * @typedef {'xs' | 'sm' | 'md' | 'lg'} GridSize
19
- */
20
-
21
- /**
22
- * Gap size options
23
- * @typedef {'sm' | 'md' | 'lg' | 'xl'} GapSize
24
- */
25
-
26
- /**
27
- * Create an auto-fit grid container
28
- * @param {GridSize} size - Grid size variant (xs, sm, md, lg)
29
- * @param {HTMLElement[]} children - Child elements to place in grid
30
- * @param {Object} [options] - Optional configuration
31
- * @param {string} [options.className] - Additional CSS classes
32
- * @param {GapSize} [options.gap] - Override default gap (sm, md, lg, xl)
33
- * @returns {HTMLElement}
34
- */
35
- export function createAutoGrid(size, children, options = {}) {
36
- const { className = "", gap } = options;
37
- const classes = [`auto-grid-${size}`, gap ? `gap-${gap}` : "", className]
38
- .filter(Boolean)
39
- .join(" ");
40
-
41
- return div({ className: classes }, ...children);
42
- }
43
-
44
- /**
45
- * Create a fixed-column grid container
46
- * @param {2 | 3 | 6} columns - Number of columns
47
- * @param {HTMLElement[]} children - Child elements to place in grid
48
- * @param {Object} [options] - Optional configuration
49
- * @param {string} [options.className] - Additional CSS classes
50
- * @returns {HTMLElement}
51
- */
52
- export function createFixedGrid(columns, children, options = {}) {
53
- const { className = "" } = options;
54
- const classes = ["grid", `grid-${columns}`, className]
55
- .filter(Boolean)
56
- .join(" ");
57
-
58
- return div({ className: classes }, ...children);
59
- }
60
-
61
- /**
62
- * Create a grid for form selectors (discipline/grade/track dropdowns)
63
- * Uses auto-grid-sm (200px min)
64
- * @param {HTMLElement[]} children - Form control elements
65
- * @returns {HTMLElement}
66
- */
67
- export function createSelectorGrid(children) {
68
- return createAutoGrid("sm", children, { gap: "lg" });
69
- }
70
-
71
- /**
72
- * Create a grid for detail items (key-value pairs)
73
- * Uses auto-grid-md (300px min)
74
- * @param {HTMLElement[]} children - Detail item elements
75
- * @returns {HTMLElement}
76
- */
77
- export function createDetailGrid(children) {
78
- return createAutoGrid("md", children);
79
- }
80
-
81
- /**
82
- * Create a grid for radar charts or large content
83
- * Uses auto-grid-lg (400px min)
84
- * @param {HTMLElement[]} children - Large content elements
85
- * @returns {HTMLElement}
86
- */
87
- export function createRadarGrid(children) {
88
- return createAutoGrid("lg", children);
89
- }
90
-
91
- /**
92
- * Create a grid for compact stats
93
- * Uses auto-grid-xs (150px min)
94
- * @param {HTMLElement[]} children - Stat elements
95
- * @returns {HTMLElement}
96
- */
97
- export function createStatsGrid(children) {
98
- return createAutoGrid("xs", children);
99
- }
100
-
101
- /**
102
- * Create a grid for card-like items (tips, expectations)
103
- * Uses auto-grid-sm (200px min)
104
- * @param {HTMLElement[]} children - Card elements
105
- * @returns {HTMLElement}
106
- */
107
- export function createCardGrid(children) {
108
- return createAutoGrid("sm", children);
109
- }
4
+ * Re-exports from @forwardimpact/libui/components/grid.
5
+ */
6
+
7
+ export {
8
+ createAutoGrid,
9
+ createFixedGrid,
10
+ createSelectorGrid,
11
+ createDetailGrid,
12
+ createRadarGrid,
13
+ createStatsGrid,
14
+ createCardGrid,
15
+ } from "@forwardimpact/libui/components/grid";
@@ -1,120 +1,11 @@
1
1
  /**
2
2
  * Reusable list component
3
+ *
4
+ * Re-exports from @forwardimpact/libui/components/list.
3
5
  */
4
6
 
5
- import { div, h2, input, select, option } from "../lib/render.js";
6
- import { createCard, createBadge } from "./card.js";
7
-
8
- /**
9
- * Create a search/filter bar
10
- * @param {Object} options
11
- * @param {Function} options.onSearch - Search callback
12
- * @param {Array<{value: string, label: string}>} [options.filterOptions] - Filter dropdown options
13
- * @param {Function} [options.onFilter] - Filter callback
14
- * @param {string} [options.searchPlaceholder='Search...']
15
- * @param {string} [options.filterPlaceholder='All']
16
- * @returns {HTMLElement}
17
- */
18
- export function createSearchBar({
19
- onSearch,
20
- filterOptions,
21
- onFilter,
22
- searchPlaceholder = "Search...",
23
- filterPlaceholder = "All",
24
- }) {
25
- const searchInput = input({
26
- type: "text",
27
- className: "form-input",
28
- placeholder: searchPlaceholder,
29
- });
30
-
31
- searchInput.addEventListener("input", (e) => {
32
- onSearch(e.target.value);
33
- });
34
-
35
- const children = [searchInput];
36
-
37
- if (filterOptions && onFilter) {
38
- const filterSelect = select(
39
- { className: "form-select" },
40
- option({ value: "" }, filterPlaceholder),
41
- ...filterOptions.map((opt) => option({ value: opt.value }, opt.label)),
42
- );
43
-
44
- filterSelect.addEventListener("change", (e) => {
45
- onFilter(e.target.value);
46
- });
47
-
48
- children.push(filterSelect);
49
- }
50
-
51
- return div({ className: "search-bar" }, ...children);
52
- }
53
-
54
- /**
55
- * Create a list of cards
56
- * @param {Array} items - Items to render
57
- * @param {Function} renderItem - Function to render each item as a card config
58
- * @param {string} [emptyMessage='No items found']
59
- * @returns {HTMLElement}
60
- */
61
- export function createCardList(
62
- items,
63
- renderItem,
64
- emptyMessage = "No items found",
65
- ) {
66
- if (!items || items.length === 0) {
67
- return div(
68
- { className: "empty-state" },
69
- div({ className: "empty-message" }, emptyMessage),
70
- );
71
- }
72
-
73
- const cards = items.map((item) => {
74
- const config = renderItem(item);
75
- return createCard(config);
76
- });
77
-
78
- return div({ className: "grid grid-3" }, ...cards);
79
- }
80
-
81
- /**
82
- * Create a grouped list (like skills by capability)
83
- * @param {Object} groups - Object with group names as keys and arrays as values
84
- * @param {Function} renderItem - Function to render each item
85
- * @param {Function} [renderGroupHeader] - Function to render group header
86
- * @returns {HTMLElement}
87
- */
88
- export function createGroupedList(groups, renderItem, renderGroupHeader) {
89
- const sections = Object.entries(groups).map(([groupName, items]) => {
90
- const header = renderGroupHeader
91
- ? renderGroupHeader(groupName, items.length)
92
- : div(
93
- { className: "capability-header" },
94
- h2({ className: "capability-title" }, formatGroupName(groupName)),
95
- createBadge(`${items.length}`, "default"),
96
- );
97
-
98
- const cards = items.map((item) => {
99
- const config = renderItem(item);
100
- return createCard(config);
101
- });
102
-
103
- return div(
104
- { className: "capability-group" },
105
- header,
106
- div({ className: "grid grid-3" }, ...cards),
107
- );
108
- });
109
-
110
- return div({ className: "grouped-list" }, ...sections);
111
- }
112
-
113
- /**
114
- * Format a group name for display
115
- * @param {string} name
116
- * @returns {string}
117
- */
118
- function formatGroupName(name) {
119
- return name.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
120
- }
7
+ export {
8
+ createSearchBar,
9
+ createCardList,
10
+ createGroupedList,
11
+ } from "@forwardimpact/libui/components/list";
@@ -1,64 +1,11 @@
1
1
  /**
2
2
  * Navigation component helpers
3
+ *
4
+ * Re-exports from @forwardimpact/libui/components/nav.
3
5
  */
4
6
 
5
- import { div, a } from "../lib/render.js";
6
-
7
- /**
8
- * Update the active navigation link in the drawer
9
- * @param {string} path - Current path
10
- */
11
- export function updateActiveNav(path) {
12
- const links = document.querySelectorAll("#drawer-nav a");
13
- links.forEach((link) => {
14
- const href = link.getAttribute("href").slice(1); // Remove #
15
- const isActive = path === href || (href !== "/" && path.startsWith(href));
16
- link.classList.toggle("active", isActive);
17
- });
18
- }
19
-
20
- /**
21
- * Create a back link
22
- * @param {string} href - Link destination
23
- * @param {string} [text='Back'] - Link text
24
- * @returns {HTMLElement}
25
- */
26
- export function createBackLink(href, text = "← Back") {
27
- return a({ href: `#${href}`, className: "back-link" }, text);
28
- }
29
-
30
- /**
31
- * Create breadcrumbs
32
- * @param {Array<{label: string, href?: string}>} items
33
- * @returns {HTMLElement}
34
- */
35
- export function createBreadcrumbs(items) {
36
- const crumbs = items.map((item, index) => {
37
- const isLast = index === items.length - 1;
38
- if (isLast || !item.href) {
39
- return span({ className: "breadcrumb-item" }, item.label);
40
- }
41
- return a(
42
- { href: `#${item.href}`, className: "breadcrumb-item" },
43
- item.label,
44
- );
45
- });
46
-
47
- const separator = " / ";
48
- const children = [];
49
- crumbs.forEach((crumb, i) => {
50
- children.push(crumb);
51
- if (i < crumbs.length - 1) {
52
- children.push(document.createTextNode(separator));
53
- }
54
- });
55
-
56
- return div({ className: "breadcrumbs" }, ...children);
57
- }
58
-
59
- function span(attrs, text) {
60
- const el = document.createElement("span");
61
- if (attrs.className) el.className = attrs.className;
62
- if (text) el.textContent = text;
63
- return el;
64
- }
7
+ export {
8
+ updateActiveNav,
9
+ createBackLink,
10
+ createBreadcrumbs,
11
+ } from "@forwardimpact/libui/components/nav";
@@ -8,7 +8,7 @@
8
8
  import { RadarChart } from "../lib/radar.js";
9
9
  import { div, h3 } from "../lib/render.js";
10
10
  import {
11
- getSkillLevelIndex,
11
+ getSkillProficiencyIndex,
12
12
  getBehaviourMaturityIndex,
13
13
  formatLevel,
14
14
  } from "../lib/render.js";
@@ -33,9 +33,9 @@ export function createSkillRadar(skillMatrix, options = {}) {
33
33
 
34
34
  const data = skillMatrix.map((skill) => ({
35
35
  label: skill.skillName,
36
- value: getSkillLevelIndex(skill.level),
36
+ value: getSkillProficiencyIndex(skill.proficiency),
37
37
  maxValue: 5,
38
- description: `${formatLevel(skill.type)} skill - ${formatLevel(skill.level)}`,
38
+ description: `${formatLevel(skill.type)} skill - ${formatLevel(skill.proficiency)}`,
39
39
  }));
40
40
 
41
41
  const chart = new RadarChart({
@@ -15,10 +15,10 @@ import {
15
15
  td,
16
16
  a,
17
17
  } from "../lib/render.js";
18
- import { getSkillLevelIndex } from "../lib/render.js";
18
+ import { getSkillProficiencyIndex } from "../lib/render.js";
19
19
  import { createLevelCell } from "./detail.js";
20
20
  import { createBadge } from "./card.js";
21
- import { SKILL_LEVEL_ORDER } from "@forwardimpact/map/levels";
21
+ import { SKILL_PROFICIENCY_ORDER } from "@forwardimpact/map/levels";
22
22
  import { truncate } from "../formatters/shared.js";
23
23
 
24
24
  /**
@@ -28,8 +28,8 @@ import { truncate } from "../formatters/shared.js";
28
28
  */
29
29
  function sortByLevelDescending(skills) {
30
30
  return [...skills].sort((a, b) => {
31
- const levelA = SKILL_LEVEL_ORDER.indexOf(a.level);
32
- const levelB = SKILL_LEVEL_ORDER.indexOf(b.level);
31
+ const levelA = SKILL_PROFICIENCY_ORDER.indexOf(a.level);
32
+ const levelB = SKILL_PROFICIENCY_ORDER.indexOf(b.level);
33
33
  if (levelB !== levelA) {
34
34
  return levelB - levelA;
35
35
  }
@@ -50,7 +50,7 @@ export function createSkillMatrix(skillMatrix) {
50
50
  const sortedSkills = sortByLevelDescending(skillMatrix);
51
51
 
52
52
  const rows = sortedSkills.map((skill) => {
53
- const levelIndex = getSkillLevelIndex(skill.level);
53
+ const levelIndex = getSkillProficiencyIndex(skill.proficiency);
54
54
 
55
55
  return tr(
56
56
  { className: skill.isHumanOnly ? "human-only-row" : "" },
@@ -69,10 +69,10 @@ export function createSkillMatrix(skillMatrix) {
69
69
  : null,
70
70
  ),
71
71
  td({}, createBadge(skill.capability, skill.capability)),
72
- createLevelCell(levelIndex, 5, skill.level),
72
+ createLevelCell(levelIndex, 5, skill.proficiency),
73
73
  td(
74
74
  { className: "skill-description" },
75
- truncate(skill.levelDescription, 80),
75
+ truncate(skill.proficiencyDescription, 80),
76
76
  ),
77
77
  );
78
78
  });