@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,275 +0,0 @@
1
- /**
2
- * Slide Table Styles
3
- *
4
- * Enhanced table styles for slide views.
5
- */
6
-
7
- @layer slides {
8
- .slide-view .data-table,
9
- .slide-view .level-table {
10
- width: 100%;
11
- border-collapse: separate;
12
- border-spacing: 0;
13
- margin-bottom: var(--space-lg);
14
- border-radius: var(--radius-lg);
15
- overflow: hidden;
16
- box-shadow: 0 2px 12px rgba(59, 130, 246, 0.08);
17
- }
18
-
19
- .slide-view .data-table th,
20
- .slide-view .data-table td,
21
- .slide-view .level-table th,
22
- .slide-view .level-table td {
23
- padding: var(--space-sm) var(--space-md);
24
- text-align: left;
25
- border-bottom: 1px solid rgba(59, 130, 246, 0.1);
26
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
27
- }
28
-
29
- .slide-view .data-table tbody tr,
30
- .slide-view .level-table tbody tr {
31
- background: rgba(255, 255, 255, 0.6);
32
- backdrop-filter: blur(5px);
33
- }
34
-
35
- .slide-view .data-table tbody tr:hover,
36
- .slide-view .level-table tbody tr:hover {
37
- background: linear-gradient(
38
- 90deg,
39
- rgba(59, 130, 246, 0.05) 0%,
40
- rgba(147, 51, 234, 0.03) 100%
41
- );
42
- transform: scale(1.01);
43
- box-shadow: 0 2px 8px rgba(59, 130, 246, 0.1);
44
- }
45
-
46
- .slide-view .data-table th,
47
- .slide-view .level-table th {
48
- background: linear-gradient(
49
- 135deg,
50
- rgba(59, 130, 246, 0.08) 0%,
51
- rgba(147, 51, 234, 0.05) 100%
52
- );
53
- backdrop-filter: blur(10px);
54
- font-weight: 600;
55
- font-size: var(--font-size-sm);
56
- color: var(--color-text-muted);
57
- text-transform: uppercase;
58
- letter-spacing: 0.05em;
59
- }
60
-
61
- .slide-view .level-table th:first-child {
62
- width: 140px;
63
- }
64
-
65
- /* Modifiers in slides */
66
- .slide-modifier {
67
- display: inline-block;
68
- padding: 0.125rem 0.375rem;
69
- border-radius: var(--radius-sm);
70
- font-size: var(--font-size-xs);
71
- font-weight: 600;
72
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
73
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
74
- }
75
-
76
- .slide-modifier:hover {
77
- transform: translateY(-1px);
78
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
79
- }
80
-
81
- .slide-modifier-positive {
82
- background: linear-gradient(
83
- 135deg,
84
- rgba(16, 185, 129, 0.2),
85
- rgba(16, 185, 129, 0.15)
86
- );
87
- color: var(--color-success);
88
- border: 1px solid rgba(16, 185, 129, 0.3);
89
- }
90
-
91
- .slide-modifier-negative {
92
- background: linear-gradient(
93
- 135deg,
94
- rgba(239, 68, 68, 0.2),
95
- rgba(239, 68, 68, 0.15)
96
- );
97
- color: var(--color-error);
98
- border: 1px solid rgba(239, 68, 68, 0.3);
99
- }
100
-
101
- .slide-modifier-neutral {
102
- background: linear-gradient(
103
- 135deg,
104
- rgba(148, 163, 184, 0.15),
105
- rgba(148, 163, 184, 0.1)
106
- );
107
- color: var(--color-text-muted);
108
- border: 1px solid rgba(148, 163, 184, 0.3);
109
- }
110
-
111
- /* Badges in slides */
112
- .slide-view .badge,
113
- .slide-view .level-badge {
114
- display: inline-block;
115
- padding: var(--space-xs) var(--space-sm);
116
- border-radius: var(--radius-md);
117
- font-size: var(--font-size-xs);
118
- font-weight: 500;
119
- position: relative;
120
- box-shadow:
121
- 0 2px 8px rgba(0, 0, 0, 0.08),
122
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
123
- border: 1px solid rgba(255, 255, 255, 0.3);
124
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
125
- animation: badgePopIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) backwards;
126
- overflow: hidden;
127
- }
128
-
129
- /* Staggered delays for sequential appearance */
130
- .slide-view .badge:nth-of-type(1),
131
- .slide-view .level-badge:nth-of-type(1) {
132
- animation-delay: 0.1s;
133
- }
134
-
135
- .slide-view .badge:nth-of-type(2),
136
- .slide-view .level-badge:nth-of-type(2) {
137
- animation-delay: 0.15s;
138
- }
139
-
140
- .slide-view .badge:nth-of-type(3),
141
- .slide-view .level-badge:nth-of-type(3) {
142
- animation-delay: 0.2s;
143
- }
144
-
145
- .slide-view .badge:nth-of-type(4),
146
- .slide-view .level-badge:nth-of-type(4) {
147
- animation-delay: 0.25s;
148
- }
149
-
150
- .slide-view .badge:nth-of-type(5),
151
- .slide-view .level-badge:nth-of-type(5) {
152
- animation-delay: 0.3s;
153
- }
154
-
155
- /* Shimmer effect on hover */
156
- .slide-view .badge::before,
157
- .slide-view .level-badge::before {
158
- content: "";
159
- position: absolute;
160
- top: 0;
161
- left: -100%;
162
- width: 100%;
163
- height: 100%;
164
- background: linear-gradient(
165
- 90deg,
166
- transparent,
167
- rgba(255, 255, 255, 0.4),
168
- transparent
169
- );
170
- transition: left 0.5s;
171
- }
172
-
173
- .slide-view .badge:hover,
174
- .slide-view .level-badge:hover {
175
- transform: translateY(-2px) scale(1.05) rotate(-1deg);
176
- box-shadow:
177
- 0 6px 16px rgba(59, 130, 246, 0.2),
178
- inset 0 1px 2px rgba(255, 255, 255, 0.7);
179
- }
180
-
181
- .slide-view .badge:hover::before,
182
- .slide-view .level-badge:hover::before {
183
- left: 100%;
184
- }
185
-
186
- /* Level bars and dots in slides */
187
- .slide-view .level-bar {
188
- display: flex;
189
- gap: 6px;
190
- }
191
-
192
- .slide-view .level-dot {
193
- width: 14px;
194
- height: 14px;
195
- border-radius: 50%;
196
- background: rgba(226, 232, 240, 0.5);
197
- border: 2px solid rgba(203, 213, 225, 0.8);
198
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
199
- position: relative;
200
- }
201
-
202
- .slide-view .level-dot.filled {
203
- box-shadow:
204
- 0 2px 8px rgba(59, 130, 246, 0.4),
205
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
206
- animation:
207
- dotFillUp 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards,
208
- dotPulse 2s ease-in-out 0.8s infinite;
209
- }
210
-
211
- /* Staggered animation delays for sequential fill */
212
- .slide-view .level-dot.filled:nth-child(1) {
213
- animation-delay: 0s, 0.8s;
214
- }
215
-
216
- .slide-view .level-dot.filled:nth-child(2) {
217
- animation-delay: 0.1s, 0.9s;
218
- }
219
-
220
- .slide-view .level-dot.filled:nth-child(3) {
221
- animation-delay: 0.2s, 1s;
222
- }
223
-
224
- .slide-view .level-dot.filled:nth-child(4) {
225
- animation-delay: 0.3s, 1.1s;
226
- }
227
-
228
- .slide-view .level-dot.filled:nth-child(5) {
229
- animation-delay: 0.4s, 1.2s;
230
- }
231
-
232
- .slide-view .level-dot.filled.level-1 {
233
- background: linear-gradient(135deg, #fecaca, #fca5a5);
234
- border-color: #f87171;
235
- box-shadow:
236
- 0 2px 8px rgba(239, 68, 68, 0.4),
237
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
238
- }
239
-
240
- .slide-view .level-dot.filled.level-2 {
241
- background: linear-gradient(135deg, #fde68a, #fcd34d);
242
- border-color: #fbbf24;
243
- box-shadow:
244
- 0 2px 8px rgba(245, 158, 11, 0.4),
245
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
246
- }
247
-
248
- .slide-view .level-dot.filled.level-3 {
249
- background: linear-gradient(135deg, #bbf7d0, #86efac);
250
- border-color: #34d399;
251
- box-shadow:
252
- 0 2px 8px rgba(16, 185, 129, 0.4),
253
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
254
- }
255
-
256
- .slide-view .level-dot.filled.level-4 {
257
- background: linear-gradient(135deg, #a5f3fc, #67e8f9);
258
- border-color: #22d3ee;
259
- box-shadow:
260
- 0 2px 8px rgba(6, 182, 212, 0.4),
261
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
262
- }
263
-
264
- .slide-view .level-dot.filled.level-5 {
265
- background: linear-gradient(135deg, #c4b5fd, #a78bfa);
266
- border-color: #a78bfa;
267
- box-shadow:
268
- 0 2px 8px rgba(139, 92, 246, 0.4),
269
- inset 0 1px 2px rgba(255, 255, 255, 0.5);
270
- }
271
-
272
- .slide-view .level-dot:hover {
273
- transform: scale(1.2);
274
- }
275
- }
@@ -1,86 +0,0 @@
1
- /**
2
- * Grade presentation helpers
3
- *
4
- * Shared utilities for formatting grade data across DOM and markdown outputs.
5
- */
6
-
7
- /**
8
- * Get grade display name (shows both professional and management titles)
9
- * @param {Object} grade
10
- * @returns {string}
11
- */
12
- export function getGradeDisplayName(grade) {
13
- if (grade.professionalTitle && grade.managementTitle) {
14
- if (grade.professionalTitle === grade.managementTitle) {
15
- return grade.professionalTitle;
16
- }
17
- return `${grade.professionalTitle} / ${grade.managementTitle}`;
18
- }
19
- return grade.professionalTitle || grade.managementTitle || grade.id;
20
- }
21
-
22
- /**
23
- * @typedef {Object} GradeListItem
24
- * @property {string} id
25
- * @property {string} displayName
26
- * @property {number} ordinalRank
27
- * @property {string|null} typicalExperienceRange
28
- * @property {Object} baseSkillLevels
29
- * @property {string|null} impactScope
30
- */
31
-
32
- /**
33
- * Transform grades for list view
34
- * @param {Array} grades - Raw grade entities
35
- * @returns {{ items: GradeListItem[] }}
36
- */
37
- export function prepareGradesList(grades) {
38
- const sortedGrades = [...grades].sort(
39
- (a, b) => a.ordinalRank - b.ordinalRank,
40
- );
41
-
42
- const items = sortedGrades.map((grade) => ({
43
- id: grade.id,
44
- displayName: getGradeDisplayName(grade),
45
- ordinalRank: grade.ordinalRank,
46
- typicalExperienceRange: grade.typicalExperienceRange || null,
47
- baseSkillLevels: grade.baseSkillLevels || {},
48
- impactScope: grade.expectations?.impactScope || null,
49
- }));
50
-
51
- return { items };
52
- }
53
-
54
- /**
55
- * @typedef {Object} GradeDetailView
56
- * @property {string} id
57
- * @property {string} displayName
58
- * @property {string} professionalTitle
59
- * @property {string} managementTitle
60
- * @property {number} ordinalRank
61
- * @property {string|null} typicalExperienceRange
62
- * @property {Object} baseSkillLevels
63
- * @property {Object} baseBehaviourMaturity
64
- * @property {Object} expectations
65
- */
66
-
67
- /**
68
- * Transform grade for detail view
69
- * @param {Object} grade - Raw grade entity
70
- * @returns {GradeDetailView|null}
71
- */
72
- export function prepareGradeDetail(grade) {
73
- if (!grade) return null;
74
-
75
- return {
76
- id: grade.id,
77
- displayName: getGradeDisplayName(grade),
78
- professionalTitle: grade.professionalTitle || null,
79
- managementTitle: grade.managementTitle || null,
80
- ordinalRank: grade.ordinalRank,
81
- typicalExperienceRange: grade.typicalExperienceRange || null,
82
- baseSkillLevels: grade.baseSkillLevels || {},
83
- baseBehaviourMaturity: grade.baseBehaviourMaturity || {},
84
- expectations: grade.expectations || {},
85
- };
86
- }
@@ -1,122 +0,0 @@
1
- /**
2
- * Grades pages
3
- */
4
-
5
- import { render, div, h1, h3, p, formatLevel } from "../lib/render.js";
6
- import { getState } from "../lib/state.js";
7
- import { createBadge } from "../components/card.js";
8
- import { renderNotFound } from "../components/error-page.js";
9
- import { prepareGradesList } from "../formatters/grade/shared.js";
10
- import { gradeToDOM } from "../formatters/grade/dom.js";
11
- import { getConceptEmoji } from "@forwardimpact/map/levels";
12
-
13
- /**
14
- * Render grades list page
15
- */
16
- export function renderGradesList() {
17
- const { data } = getState();
18
- const { framework } = data;
19
- const gradeEmoji = getConceptEmoji(framework, "grade");
20
-
21
- // Transform data for list view
22
- const { items } = prepareGradesList(data.grades);
23
-
24
- const page = div(
25
- { className: "grades-page" },
26
- // Header
27
- div(
28
- { className: "page-header" },
29
- h1(
30
- { className: "page-title" },
31
- `${gradeEmoji} ${framework.entityDefinitions.grade.title}`,
32
- ),
33
- p(
34
- { className: "page-description" },
35
- framework.entityDefinitions.grade.description.trim(),
36
- ),
37
- ),
38
-
39
- // Grades timeline
40
- div(
41
- { className: "grades-timeline" },
42
- ...items.map((grade) => createGradeTimelineItem(grade)),
43
- ),
44
- );
45
-
46
- render(page);
47
- }
48
-
49
- /**
50
- * Create a grade timeline item
51
- * @param {Object} grade
52
- * @returns {HTMLElement}
53
- */
54
- function createGradeTimelineItem(grade) {
55
- const item = div(
56
- { className: "grade-timeline-item" },
57
- div({ className: "grade-level-marker" }, String(grade.ordinalRank)),
58
- div(
59
- { className: "grade-timeline-content card card-clickable" },
60
- div(
61
- { className: "card-header" },
62
- h3({ className: "card-title" }, grade.displayName),
63
- createBadge(grade.id, "default"),
64
- ),
65
- grade.typicalExperienceRange
66
- ? p(
67
- { className: "text-muted", style: "margin: 0.25rem 0" },
68
- `${grade.typicalExperienceRange} experience`,
69
- )
70
- : null,
71
- div(
72
- { className: "card-meta", style: "margin-top: 0.5rem" },
73
- createBadge(
74
- `Primary: ${formatLevel(grade.baseSkillLevels?.primary)}`,
75
- "primary",
76
- ),
77
- createBadge(
78
- `Secondary: ${formatLevel(grade.baseSkillLevels?.secondary)}`,
79
- "secondary",
80
- ),
81
- createBadge(
82
- `Broad: ${formatLevel(grade.baseSkillLevels?.broad)}`,
83
- "broad",
84
- ),
85
- ),
86
- grade.scope
87
- ? p(
88
- { className: "card-description", style: "margin-top: 0.75rem" },
89
- `Scope: ${grade.scope}`,
90
- )
91
- : null,
92
- ),
93
- );
94
-
95
- item.querySelector(".card").addEventListener("click", () => {
96
- window.location.hash = `/grade/${grade.id}`;
97
- });
98
-
99
- return item;
100
- }
101
-
102
- /**
103
- * Render grade detail page
104
- * @param {Object} params - Route params
105
- */
106
- export function renderGradeDetail(params) {
107
- const { data } = getState();
108
- const grade = data.grades.find((g) => g.id === params.id);
109
-
110
- if (!grade) {
111
- renderNotFound({
112
- entityType: "Grade",
113
- entityId: params.id,
114
- backPath: "/grade",
115
- backText: "← Back to Grades",
116
- });
117
- return;
118
- }
119
-
120
- // Use DOM formatter - it handles transformation internally
121
- render(gradeToDOM(grade, { framework: data.framework }));
122
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Grade Slide View
3
- *
4
- * Printer-friendly view of a grade.
5
- */
6
-
7
- import { div, h1, p } from "../lib/render.js";
8
- import { gradeToDOM } from "../formatters/index.js";
9
-
10
- /**
11
- * Render grade slide
12
- * @param {Object} params
13
- * @param {Function} params.render
14
- * @param {Object} params.data
15
- * @param {Object} params.params
16
- */
17
- export function renderGradeSlide({ render, data, params }) {
18
- const grade = data.grades.find((g) => g.id === params.id);
19
-
20
- if (!grade) {
21
- render(
22
- div(
23
- { className: "slide-error" },
24
- h1({}, "Grade Not Found"),
25
- p({}, `No grade found with ID: ${params.id}`),
26
- ),
27
- );
28
- return;
29
- }
30
-
31
- render(gradeToDOM(grade, { framework: data.framework, showBackLink: false }));
32
- }