@forwardimpact/pathway 0.20.0 → 0.22.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 (96) hide show
  1. package/README.md +3 -3
  2. package/bin/fit-pathway.js +28 -28
  3. package/package.json +4 -4
  4. package/src/commands/agent.js +8 -8
  5. package/src/commands/build.js +7 -7
  6. package/src/commands/dev.js +7 -7
  7. package/src/commands/driver.js +1 -1
  8. package/src/commands/index.js +1 -1
  9. package/src/commands/init.js +1 -1
  10. package/src/commands/interview.js +8 -8
  11. package/src/commands/job.js +19 -19
  12. package/src/commands/level.js +60 -0
  13. package/src/commands/progress.js +20 -20
  14. package/src/commands/questions.js +3 -3
  15. package/src/commands/skill.js +1 -1
  16. package/src/commands/track.js +1 -1
  17. package/src/commands/update.js +12 -14
  18. package/src/components/action-buttons.js +3 -3
  19. package/src/components/builder.js +25 -25
  20. package/src/components/checklist.js +1 -1
  21. package/src/components/comparison-radar.js +3 -3
  22. package/src/components/detail.js +3 -3
  23. package/src/components/grid.js +1 -1
  24. package/src/components/radar-chart.js +3 -3
  25. package/src/components/skill-matrix.js +7 -7
  26. package/src/css/pages/landing.css +5 -5
  27. package/src/formatters/behaviour/dom.js +1 -1
  28. package/src/formatters/discipline/dom.js +1 -1
  29. package/src/formatters/driver/dom.js +1 -1
  30. package/src/formatters/index.js +5 -5
  31. package/src/formatters/interview/dom.js +1 -1
  32. package/src/formatters/interview/markdown.js +1 -1
  33. package/src/formatters/interview/shared.js +20 -20
  34. package/src/formatters/job/description.js +18 -18
  35. package/src/formatters/job/dom.js +12 -12
  36. package/src/formatters/job/markdown.js +7 -7
  37. package/src/formatters/json-ld.js +24 -24
  38. package/src/formatters/{grade → level}/dom.js +32 -28
  39. package/src/formatters/{grade → level}/markdown.js +20 -29
  40. package/src/formatters/{grade → level}/microdata.js +28 -38
  41. package/src/formatters/level/shared.js +86 -0
  42. package/src/formatters/progress/markdown.js +2 -2
  43. package/src/formatters/progress/shared.js +48 -48
  44. package/src/formatters/questions/markdown.js +8 -6
  45. package/src/formatters/questions/shared.js +7 -7
  46. package/src/formatters/skill/dom.js +4 -4
  47. package/src/formatters/skill/markdown.js +2 -2
  48. package/src/formatters/skill/microdata.js +3 -3
  49. package/src/formatters/skill/shared.js +3 -3
  50. package/src/formatters/track/dom.js +1 -1
  51. package/src/formatters/track/markdown.js +1 -1
  52. package/src/handout-main.js +13 -16
  53. package/src/handout.html +4 -4
  54. package/src/index.html +5 -5
  55. package/src/lib/card-mappers.js +17 -17
  56. package/src/lib/cli-command.js +3 -3
  57. package/src/lib/cli-output.js +2 -2
  58. package/src/lib/job-cache.js +11 -11
  59. package/src/lib/render.js +6 -6
  60. package/src/lib/state.js +2 -2
  61. package/src/lib/yaml-loader.js +9 -9
  62. package/src/main.js +10 -10
  63. package/src/pages/agent-builder.js +11 -11
  64. package/src/pages/assessment-results.js +27 -23
  65. package/src/pages/behaviour.js +1 -1
  66. package/src/pages/discipline.js +1 -1
  67. package/src/pages/driver.js +1 -1
  68. package/src/pages/interview-builder.js +6 -6
  69. package/src/pages/interview.js +9 -9
  70. package/src/pages/job-builder.js +6 -6
  71. package/src/pages/job.js +7 -7
  72. package/src/pages/landing.js +9 -9
  73. package/src/pages/level.js +122 -0
  74. package/src/pages/progress-builder.js +8 -8
  75. package/src/pages/progress.js +74 -74
  76. package/src/pages/self-assessment.js +8 -8
  77. package/src/pages/skill.js +1 -4
  78. package/src/pages/stage.js +1 -1
  79. package/src/pages/tool.js +1 -1
  80. package/src/pages/track.js +1 -1
  81. package/src/slide-main.js +22 -22
  82. package/src/slides/chapter.js +4 -4
  83. package/src/slides/index.js +11 -11
  84. package/src/slides/interview.js +2 -2
  85. package/src/slides/job.js +3 -3
  86. package/src/slides/level.js +32 -0
  87. package/src/slides/overview.js +9 -9
  88. package/src/slides/progress.js +13 -13
  89. package/src/slides.html +4 -4
  90. package/src/types.js +1 -1
  91. package/templates/install.template.sh +11 -8
  92. package/templates/job.template.md +2 -2
  93. package/src/commands/grade.js +0 -60
  94. package/src/formatters/grade/shared.js +0 -86
  95. package/src/pages/grade.js +0 -122
  96. package/src/slides/grade.js +0 -32
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Career progress detail page
3
- * Shows skill and behaviour progression comparison across discipline × grade × track
3
+ * Shows skill and behaviour progression comparison across discipline × level × track
4
4
  */
5
5
 
6
6
  import { render, div, h1, h2, p, a, label, section } from "../lib/render.js";
@@ -20,7 +20,7 @@ import {
20
20
  import {
21
21
  prepareCurrentJob,
22
22
  prepareCustomProgression,
23
- getDefaultTargetGrade,
23
+ getDefaultTargetLevel,
24
24
  isValidCombination,
25
25
  } from "../formatters/progress/shared.js";
26
26
 
@@ -29,18 +29,18 @@ import {
29
29
  * @param {Object} params - Route params
30
30
  */
31
31
  export function renderProgressDetail(params) {
32
- const { discipline: disciplineId, grade: gradeId, track: trackId } = params;
32
+ const { discipline: disciplineId, level: levelId, track: trackId } = params;
33
33
  const { data } = getState();
34
34
 
35
35
  // Find the components
36
36
  const discipline = data.disciplines.find((d) => d.id === disciplineId);
37
- const grade = data.grades.find((g) => g.id === gradeId);
37
+ const level = data.levels.find((g) => g.id === levelId);
38
38
  const track = trackId ? data.tracks.find((t) => t.id === trackId) : null;
39
39
 
40
- if (!discipline || !grade) {
40
+ if (!discipline || !level) {
41
41
  renderError({
42
42
  title: "Role Not Found",
43
- message: "Invalid role combination. Discipline or grade not found.",
43
+ message: "Invalid role combination. Discipline or level not found.",
44
44
  backPath: "/career-progress",
45
45
  backText: "← Back to Career Progress",
46
46
  });
@@ -61,7 +61,7 @@ export function renderProgressDetail(params) {
61
61
  // Prepare current job view
62
62
  const currentJobView = prepareCurrentJob({
63
63
  discipline,
64
- grade,
64
+ level,
65
65
  track,
66
66
  skills: data.skills,
67
67
  behaviours: data.behaviours,
@@ -70,15 +70,15 @@ export function renderProgressDetail(params) {
70
70
  if (!currentJobView) {
71
71
  renderError({
72
72
  title: "Invalid Combination",
73
- message: "This discipline, track, and grade combination is not valid.",
73
+ message: "This discipline, track, and level combination is not valid.",
74
74
  backPath: "/career-progress",
75
75
  backText: "← Back to Career Progress",
76
76
  });
77
77
  return;
78
78
  }
79
79
 
80
- // Find next grade for default comparison
81
- const nextGrade = getDefaultTargetGrade(grade, data.grades);
80
+ // Find next level for default comparison
81
+ const nextLevel = getDefaultTargetLevel(level, data.levels);
82
82
 
83
83
  const page = div(
84
84
  { className: "progress-detail-page" },
@@ -92,7 +92,7 @@ export function renderProgressDetail(params) {
92
92
  "Current role: ",
93
93
  a({ href: `#/discipline/${discipline.id}` }, discipline.specialization),
94
94
  " × ",
95
- a({ href: `#/grade/${grade.id}` }, grade.id),
95
+ a({ href: `#/level/${level.id}` }, level.id),
96
96
  track
97
97
  ? [" × ", a({ href: `#/track/${track.id}` }, track.name)]
98
98
  : " (Generalist)",
@@ -123,10 +123,10 @@ export function renderProgressDetail(params) {
123
123
  // Comparison selectors section
124
124
  createComparisonSelectorsSection({
125
125
  discipline,
126
- currentGrade: grade,
126
+ currentLevel: level,
127
127
  currentTrack: track,
128
128
  currentJobView,
129
- nextGrade,
129
+ nextLevel,
130
130
  data,
131
131
  }),
132
132
 
@@ -136,8 +136,8 @@ export function renderProgressDetail(params) {
136
136
  a(
137
137
  {
138
138
  href: trackId
139
- ? `#/job/${disciplineId}/${gradeId}/${trackId}`
140
- : `#/job/${disciplineId}/${gradeId}`,
139
+ ? `#/job/${disciplineId}/${levelId}/${trackId}`
140
+ : `#/job/${disciplineId}/${levelId}`,
141
141
  className: "btn btn-secondary",
142
142
  },
143
143
  "View Full Job Definition",
@@ -150,22 +150,22 @@ export function renderProgressDetail(params) {
150
150
 
151
151
  /**
152
152
  * Create the comparison selectors section
153
- * Defaults to same discipline, same track, next grade up
153
+ * Defaults to same discipline, same track, next level up
154
154
  * @param {Object} params
155
155
  * @param {Object} params.discipline - Current discipline
156
- * @param {Object} params.currentGrade - Current grade
156
+ * @param {Object} params.currentLevel - Current level
157
157
  * @param {Object} params.currentTrack - Current track
158
158
  * @param {Object} params.currentJobView - Current job view from presenter
159
- * @param {Object|null} params.nextGrade - Next grade (for default selection)
160
- * @param {Object} params.data - Full data object with disciplines, grades, tracks, skills, behaviours
159
+ * @param {Object|null} params.nextLevel - Next level (for default selection)
160
+ * @param {Object} params.data - Full data object with disciplines, levels, tracks, skills, behaviours
161
161
  * @returns {HTMLElement}
162
162
  */
163
163
  function createComparisonSelectorsSection({
164
164
  discipline,
165
- currentGrade,
165
+ currentLevel,
166
166
  currentTrack,
167
167
  currentJobView,
168
- nextGrade,
168
+ nextLevel,
169
169
  data,
170
170
  }) {
171
171
  // Create a container for dynamic comparison results
@@ -174,36 +174,36 @@ function createComparisonSelectorsSection({
174
174
  id: "comparison-results",
175
175
  });
176
176
 
177
- // State to track current selections - default to same discipline, same track, next grade
177
+ // State to track current selections - default to same discipline, same track, next level
178
178
  let selectedDisciplineId = discipline.id;
179
- let selectedGradeId = nextGrade?.id || "";
179
+ let selectedLevelId = nextLevel?.id || "";
180
180
  let selectedTrackId = currentTrack?.id || "";
181
181
 
182
182
  // Get available options based on selected discipline
183
183
  function getAvailableOptions(disciplineId) {
184
184
  const selectedDisc = data.disciplines.find((d) => d.id === disciplineId);
185
185
  if (!selectedDisc)
186
- return { grades: [], tracks: [], allowsTrackless: false };
186
+ return { levels: [], tracks: [], allowsTrackless: false };
187
187
 
188
- const validGrades = [];
188
+ const validLevels = [];
189
189
  const validTracks = new Set();
190
190
  let allowsTrackless = false;
191
191
 
192
- for (const grade of data.grades) {
192
+ for (const level of data.levels) {
193
193
  // Check trackless combination
194
194
  if (
195
- isValidCombination({ discipline: selectedDisc, grade, track: null })
195
+ isValidCombination({ discipline: selectedDisc, level, track: null })
196
196
  ) {
197
- if (!validGrades.find((g) => g.id === grade.id)) {
198
- validGrades.push(grade);
197
+ if (!validLevels.find((g) => g.id === level.id)) {
198
+ validLevels.push(level);
199
199
  }
200
200
  allowsTrackless = true;
201
201
  }
202
202
  // Check each track combination
203
203
  for (const track of data.tracks) {
204
- if (isValidCombination({ discipline: selectedDisc, grade, track })) {
205
- if (!validGrades.find((g) => g.id === grade.id)) {
206
- validGrades.push(grade);
204
+ if (isValidCombination({ discipline: selectedDisc, level, track })) {
205
+ if (!validLevels.find((g) => g.id === level.id)) {
206
+ validLevels.push(level);
207
207
  }
208
208
  validTracks.add(track.id);
209
209
  }
@@ -211,7 +211,7 @@ function createComparisonSelectorsSection({
211
211
  }
212
212
 
213
213
  return {
214
- grades: validGrades.sort((a, b) => a.level - b.level),
214
+ levels: validLevels.sort((a, b) => a.level - b.level),
215
215
  tracks: data.tracks
216
216
  .filter((t) => validTracks.has(t.id))
217
217
  .sort((a, b) => a.name.localeCompare(b.name)),
@@ -226,14 +226,14 @@ function createComparisonSelectorsSection({
226
226
  // Clear previous results
227
227
  comparisonResultsContainer.innerHTML = "";
228
228
 
229
- // Track can be empty string for generalist, but discipline and grade are required
230
- if (!selectedDisciplineId || !selectedGradeId) {
229
+ // Track can be empty string for generalist, but discipline and level are required
230
+ if (!selectedDisciplineId || !selectedLevelId) {
231
231
  comparisonResultsContainer.appendChild(
232
232
  div(
233
233
  { className: "comparison-placeholder" },
234
234
  p(
235
235
  { className: "text-muted" },
236
- "Select a discipline and grade to see the comparison.",
236
+ "Select a discipline and level to see the comparison.",
237
237
  ),
238
238
  ),
239
239
  );
@@ -243,20 +243,20 @@ function createComparisonSelectorsSection({
243
243
  const targetDiscipline = data.disciplines.find(
244
244
  (d) => d.id === selectedDisciplineId,
245
245
  );
246
- const targetGrade = data.grades.find((g) => g.id === selectedGradeId);
246
+ const targetLevel = data.levels.find((g) => g.id === selectedLevelId);
247
247
  // selectedTrackId can be empty string for generalist
248
248
  const targetTrack = selectedTrackId
249
249
  ? data.tracks.find((t) => t.id === selectedTrackId)
250
250
  : null;
251
251
 
252
- if (!targetDiscipline || !targetGrade) {
252
+ if (!targetDiscipline || !targetLevel) {
253
253
  return;
254
254
  }
255
255
 
256
256
  // Check if comparing to same role
257
257
  if (
258
258
  targetDiscipline.id === discipline.id &&
259
- targetGrade.id === currentGrade.id &&
259
+ targetLevel.id === currentLevel.id &&
260
260
  targetTrack?.id === currentTrack?.id
261
261
  ) {
262
262
  comparisonResultsContainer.appendChild(
@@ -274,10 +274,10 @@ function createComparisonSelectorsSection({
274
274
  // Use formatter shared module to analyze the progression
275
275
  const progressionView = prepareCustomProgression({
276
276
  discipline,
277
- currentGrade,
277
+ currentLevel,
278
278
  currentTrack,
279
279
  targetDiscipline,
280
- targetGrade,
280
+ targetLevel,
281
281
  targetTrack,
282
282
  skills: data.skills,
283
283
  behaviours: data.behaviours,
@@ -341,8 +341,8 @@ function createComparisonSelectorsSection({
341
341
  target.skillMatrix,
342
342
  {
343
343
  title: "Skills Comparison",
344
- currentLabel: `Current (${currentGrade.id})`,
345
- targetLabel: `Target (${targetGrade.id})`,
344
+ currentLabel: `Current (${currentLevel.id})`,
345
+ targetLabel: `Target (${targetLevel.id})`,
346
346
  size: 400,
347
347
  capabilities: data.capabilities,
348
348
  },
@@ -352,8 +352,8 @@ function createComparisonSelectorsSection({
352
352
  target.behaviourProfile,
353
353
  {
354
354
  title: "Behaviours Comparison",
355
- currentLabel: `Current (${currentGrade.id})`,
356
- targetLabel: `Target (${targetGrade.id})`,
355
+ currentLabel: `Current (${currentLevel.id})`,
356
+ targetLabel: `Target (${targetLevel.id})`,
357
357
  size: 400,
358
358
  },
359
359
  ),
@@ -379,11 +379,11 @@ function createComparisonSelectorsSection({
379
379
  a(
380
380
  {
381
381
  href: targetTrack
382
- ? `#/job/${targetDiscipline.id}/${targetGrade.id}/${targetTrack.id}`
383
- : `#/job/${targetDiscipline.id}/${targetGrade.id}`,
382
+ ? `#/job/${targetDiscipline.id}/${targetLevel.id}/${targetTrack.id}`
383
+ : `#/job/${targetDiscipline.id}/${targetLevel.id}`,
384
384
  className: "btn btn-secondary",
385
385
  },
386
- `View ${targetGrade.id}${targetTrack ? ` ${targetTrack.name}` : ""} Job Definition →`,
386
+ `View ${targetLevel.id}${targetTrack ? ` ${targetTrack.name}` : ""} Job Definition →`,
387
387
  ),
388
388
  ),
389
389
  );
@@ -395,36 +395,36 @@ function createComparisonSelectorsSection({
395
395
  let availableOptions = getAvailableOptions(selectedDisciplineId);
396
396
 
397
397
  // References to select elements for updating
398
- let gradeSelectEl = null;
398
+ let levelSelectEl = null;
399
399
  let trackSelectEl = null;
400
400
 
401
401
  /**
402
- * Update grade and track selectors when discipline changes
402
+ * Update level and track selectors when discipline changes
403
403
  */
404
404
  function updateSelectorsForDiscipline(newDisciplineId) {
405
405
  availableOptions = getAvailableOptions(newDisciplineId);
406
406
 
407
- // Update grade selector
408
- if (gradeSelectEl) {
409
- gradeSelectEl.innerHTML = "";
407
+ // Update level selector
408
+ if (levelSelectEl) {
409
+ levelSelectEl.innerHTML = "";
410
410
  const placeholderOpt = document.createElement("option");
411
411
  placeholderOpt.value = "";
412
- placeholderOpt.textContent = "Select grade...";
413
- gradeSelectEl.appendChild(placeholderOpt);
412
+ placeholderOpt.textContent = "Select level...";
413
+ levelSelectEl.appendChild(placeholderOpt);
414
414
 
415
- for (const grade of availableOptions.grades) {
415
+ for (const level of availableOptions.levels) {
416
416
  const opt = document.createElement("option");
417
- opt.value = grade.id;
418
- opt.textContent = grade.id;
419
- gradeSelectEl.appendChild(opt);
417
+ opt.value = level.id;
418
+ opt.textContent = level.id;
419
+ levelSelectEl.appendChild(opt);
420
420
  }
421
421
 
422
422
  // Try to keep current selection if valid
423
- if (availableOptions.grades.find((g) => g.id === selectedGradeId)) {
424
- gradeSelectEl.value = selectedGradeId;
423
+ if (availableOptions.levels.find((g) => g.id === selectedLevelId)) {
424
+ levelSelectEl.value = selectedLevelId;
425
425
  } else {
426
- selectedGradeId = "";
427
- gradeSelectEl.value = "";
426
+ selectedLevelId = "";
427
+ levelSelectEl.value = "";
428
428
  }
429
429
  }
430
430
 
@@ -471,15 +471,15 @@ function createComparisonSelectorsSection({
471
471
  }
472
472
  }
473
473
 
474
- // Create grade and track selects with stored references
475
- gradeSelectEl = createSelectWithValue({
476
- id: "compare-grade-select",
477
- items: availableOptions.grades,
478
- initialValue: selectedGradeId,
479
- placeholder: "Select grade...",
474
+ // Create level and track selects with stored references
475
+ levelSelectEl = createSelectWithValue({
476
+ id: "compare-level-select",
477
+ items: availableOptions.levels,
478
+ initialValue: selectedLevelId,
479
+ placeholder: "Select level...",
480
480
  getDisplayName: (g) => g.id,
481
481
  onChange: (value) => {
482
- selectedGradeId = value;
482
+ selectedLevelId = value;
483
483
  updateComparison();
484
484
  },
485
485
  });
@@ -497,7 +497,7 @@ function createComparisonSelectorsSection({
497
497
  });
498
498
 
499
499
  // Trigger initial comparison if we have defaults
500
- if (selectedGradeId && selectedTrackId) {
500
+ if (selectedLevelId && selectedTrackId) {
501
501
  // Use setTimeout to ensure DOM is ready
502
502
  setTimeout(() => updateComparison(), 0);
503
503
  }
@@ -508,7 +508,7 @@ function createComparisonSelectorsSection({
508
508
  h2({ className: "section-title" }, "📈 Compare Progression"),
509
509
  p(
510
510
  { className: "text-muted", style: "margin-bottom: 1rem" },
511
- "Compare your current role with another discipline, grade, or track combination.",
511
+ "Compare your current role with another discipline, level, or track combination.",
512
512
  ),
513
513
 
514
514
  // Selector row
@@ -532,8 +532,8 @@ function createComparisonSelectorsSection({
532
532
  ),
533
533
  div(
534
534
  { className: "form-group" },
535
- label({ for: "compare-grade-select" }, "Target Grade"),
536
- gradeSelectEl,
535
+ label({ for: "compare-level-select" }, "Target Level"),
536
+ levelSelectEl,
537
537
  ),
538
538
  div(
539
539
  { className: "form-group" },
@@ -19,13 +19,13 @@ import { getState } from "../lib/state.js";
19
19
  import { createBadge } from "../components/card.js";
20
20
  import { createDisciplineSelect } from "../lib/form-controls.js";
21
21
  import {
22
- SKILL_LEVEL_ORDER,
22
+ SKILL_PROFICIENCY_ORDER,
23
23
  BEHAVIOUR_MATURITY_ORDER,
24
24
  groupSkillsByCapability,
25
25
  getCapabilityOrder,
26
26
  getCapabilityEmoji,
27
27
  getConceptEmoji,
28
- } from "@forwardimpact/schema/levels";
28
+ } from "@forwardimpact/map/levels";
29
29
  import { formatLevel } from "../lib/render.js";
30
30
 
31
31
  /**
@@ -107,7 +107,7 @@ function getWizardSteps(data) {
107
107
  steps.push({
108
108
  id: "results",
109
109
  name: "Results",
110
- icon: getConceptEmoji(framework, "grade"),
110
+ icon: getConceptEmoji(framework, "level"),
111
111
  type: "results",
112
112
  });
113
113
 
@@ -265,7 +265,7 @@ function renderIntroStep(data) {
265
265
  h2({}, "Welcome to the Self-Assessment"),
266
266
  p(
267
267
  {},
268
- "This assessment helps you understand your current skill levels and behaviours, " +
268
+ "This assessment helps you understand your current skill proficiencies and behaviours, " +
269
269
  "then matches you with suitable roles in the organization.",
270
270
  ),
271
271
 
@@ -472,7 +472,7 @@ function createSkillAssessmentItem(skill, relevance) {
472
472
 
473
473
  div(
474
474
  { className: "level-selector" },
475
- ...SKILL_LEVEL_ORDER.map((level, index) =>
475
+ ...SKILL_PROFICIENCY_ORDER.map((level, index) =>
476
476
  createLevelButton(skill, level, index, "skill"),
477
477
  ),
478
478
  // Clear button
@@ -503,9 +503,9 @@ function createLevelButton(item, level, index, type) {
503
503
  const stateKey = type === "skill" ? "skills" : "behaviours";
504
504
  const currentLevel = assessmentState[stateKey][item.id];
505
505
  const isSelected = currentLevel === level;
506
- const levelDescriptions =
507
- type === "skill" ? item.levelDescriptions : item.maturityDescriptions;
508
- const description = levelDescriptions?.[level] || "";
506
+ const proficiencyDescriptions =
507
+ type === "skill" ? item.proficiencyDescriptions : item.maturityDescriptions;
508
+ const description = proficiencyDescriptions?.[level] || "";
509
509
 
510
510
  return button(
511
511
  {
@@ -10,10 +10,7 @@ import { renderNotFound } from "../components/error-page.js";
10
10
  import { prepareSkillsList } from "../formatters/skill/shared.js";
11
11
  import { skillToDOM } from "../formatters/skill/dom.js";
12
12
  import { skillToCardConfig } from "../lib/card-mappers.js";
13
- import {
14
- getCapabilityEmoji,
15
- getConceptEmoji,
16
- } from "@forwardimpact/schema/levels";
13
+ import { getCapabilityEmoji, getConceptEmoji } from "@forwardimpact/map/levels";
17
14
  import { generateSkillMarkdown } from "@forwardimpact/libpathway";
18
15
  import { formatAgentSkill } from "../formatters/agent/skill.js";
19
16
 
@@ -7,7 +7,7 @@ import { getState } from "../lib/state.js";
7
7
  import { createCardList } from "../components/list.js";
8
8
  import { renderNotFound } from "../components/error-page.js";
9
9
  import { prepareStagesList, stageToDOM } from "../formatters/stage/index.js";
10
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
10
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
11
11
 
12
12
  /**
13
13
  * Map stage to card configuration
package/src/pages/tool.js CHANGED
@@ -10,7 +10,7 @@ import { prepareToolsList } from "../formatters/tool/shared.js";
10
10
  import { createBadge } from "../components/card.js";
11
11
  import { createCardList } from "../components/list.js";
12
12
  import { toolToCardConfig } from "../lib/card-mappers.js";
13
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
13
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
14
14
 
15
15
  /**
16
16
  * Render tools list page
@@ -9,7 +9,7 @@ import { renderNotFound } from "../components/error-page.js";
9
9
  import { prepareTracksList } from "../formatters/track/shared.js";
10
10
  import { trackToDOM } from "../formatters/track/dom.js";
11
11
  import { trackToCardConfig } from "../lib/card-mappers.js";
12
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
12
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
13
13
 
14
14
  /**
15
15
  * Render tracks list page
package/src/slide-main.js CHANGED
@@ -18,7 +18,7 @@ import { renderSkillSlide } from "./slides/skill.js";
18
18
  import { renderBehaviourSlide } from "./slides/behaviour.js";
19
19
  import { renderDriverSlide } from "./slides/driver.js";
20
20
  import { renderDisciplineSlide } from "./slides/discipline.js";
21
- import { renderGradeSlide } from "./slides/grade.js";
21
+ import { renderLevelSlide } from "./slides/level.js";
22
22
  import { renderTrackSlide } from "./slides/track.js";
23
23
  import { renderJobSlide } from "./slides/job.js";
24
24
  import { renderInterviewSlide } from "./slides/interview.js";
@@ -140,9 +140,9 @@ function setupRoutes() {
140
140
  });
141
141
  });
142
142
 
143
- // Grades
144
- router.on("/grade/:id", (params) => {
145
- renderGradeSlide({ render: renderSlide, data: getState().data, params });
143
+ // Levels
144
+ router.on("/level/:id", (params) => {
145
+ renderLevelSlide({ render: renderSlide, data: getState().data, params });
146
146
  });
147
147
 
148
148
  // Tracks
@@ -150,12 +150,12 @@ function setupRoutes() {
150
150
  renderTrackSlide({ render: renderSlide, data: getState().data, params });
151
151
  });
152
152
 
153
- // Jobs - new format: discipline/grade/track (track optional)
154
- router.on("/job/:discipline/:grade/:track", (params) => {
153
+ // Jobs - new format: discipline/level/track (track optional)
154
+ router.on("/job/:discipline/:level/:track", (params) => {
155
155
  renderJobSlide({ render: renderSlide, data: getState().data, params });
156
156
  });
157
157
 
158
- router.on("/job/:discipline/:grade", (params) => {
158
+ router.on("/job/:discipline/:level", (params) => {
159
159
  renderJobSlide({
160
160
  render: renderSlide,
161
161
  data: getState().data,
@@ -163,8 +163,8 @@ function setupRoutes() {
163
163
  });
164
164
  });
165
165
 
166
- // Interviews - new format: discipline/grade/track (track optional)
167
- router.on("/interview/:discipline/:grade/:track", (params) => {
166
+ // Interviews - new format: discipline/level/track (track optional)
167
+ router.on("/interview/:discipline/:level/:track", (params) => {
168
168
  renderInterviewSlide({
169
169
  render: renderSlide,
170
170
  data: getState().data,
@@ -172,7 +172,7 @@ function setupRoutes() {
172
172
  });
173
173
  });
174
174
 
175
- router.on("/interview/:discipline/:grade", (params) => {
175
+ router.on("/interview/:discipline/:level", (params) => {
176
176
  renderInterviewSlide({
177
177
  render: renderSlide,
178
178
  data: getState().data,
@@ -180,12 +180,12 @@ function setupRoutes() {
180
180
  });
181
181
  });
182
182
 
183
- // Progress - new format: discipline/grade/track (track optional)
184
- router.on("/progress/:discipline/:grade/:track", (params) => {
183
+ // Progress - new format: discipline/level/track (track optional)
184
+ router.on("/progress/:discipline/:level/:track", (params) => {
185
185
  renderProgressSlide({ render: renderSlide, data: getState().data, params });
186
186
  });
187
187
 
188
- router.on("/progress/:discipline/:grade", (params) => {
188
+ router.on("/progress/:discipline/:level", (params) => {
189
189
  renderProgressSlide({
190
190
  render: renderSlide,
191
191
  data: getState().data,
@@ -211,15 +211,15 @@ function buildSlideOrder(data) {
211
211
  data.disciplines.forEach((d) => order.push(`/discipline/${d.id}`));
212
212
  }
213
213
 
214
- // Grades (moved before Tracks)
215
- if (data.grades && data.grades.length > 0) {
214
+ // Levels (moved before Tracks)
215
+ if (data.levels && data.levels.length > 0) {
216
216
  boundaries.push(order.length);
217
- order.push("/chapter/grade");
218
- order.push("/overview/grade");
219
- data.grades.forEach((g) => order.push(`/grade/${g.id}`));
217
+ order.push("/chapter/level");
218
+ order.push("/overview/level");
219
+ data.levels.forEach((g) => order.push(`/level/${g.id}`));
220
220
  }
221
221
 
222
- // Tracks (moved after Grades)
222
+ // Tracks (moved after Levels)
223
223
  if (data.tracks && data.tracks.length > 0) {
224
224
  boundaries.push(order.length);
225
225
  order.push("/chapter/track");
@@ -254,7 +254,7 @@ function buildSlideOrder(data) {
254
254
  // Jobs
255
255
  const jobs = generateAllJobs({
256
256
  disciplines: data.disciplines,
257
- grades: data.grades,
257
+ levels: data.levels,
258
258
  tracks: data.tracks,
259
259
  skills: data.skills,
260
260
  behaviours: data.behaviours,
@@ -267,8 +267,8 @@ function buildSlideOrder(data) {
267
267
  jobs.forEach((job) =>
268
268
  order.push(
269
269
  job.track
270
- ? `/job/${job.discipline.id}/${job.grade.id}/${job.track.id}`
271
- : `/job/${job.discipline.id}/${job.grade.id}`,
270
+ ? `/job/${job.discipline.id}/${job.level.id}/${job.track.id}`
271
+ : `/job/${job.discipline.id}/${job.level.id}`,
272
272
  ),
273
273
  );
274
274
  }
@@ -38,10 +38,10 @@ export function renderChapterSlide({ render, data, params }) {
38
38
  emojiIcon: framework.entityDefinitions.discipline.emojiIcon,
39
39
  description: framework.entityDefinitions.discipline.description,
40
40
  },
41
- grade: {
42
- title: framework.entityDefinitions.grade.title,
43
- emojiIcon: framework.entityDefinitions.grade.emojiIcon,
44
- description: framework.entityDefinitions.grade.description,
41
+ level: {
42
+ title: framework.entityDefinitions.level.title,
43
+ emojiIcon: framework.entityDefinitions.level.emojiIcon,
44
+ description: framework.entityDefinitions.level.description,
45
45
  },
46
46
  track: {
47
47
  title: framework.entityDefinitions.track.title,
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { div, heading1, heading2, p, a, ul, li, span } from "../lib/render.js";
8
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
8
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
9
9
  import { generateAllJobs } from "@forwardimpact/libpathway/derivation";
10
10
 
11
11
  /**
@@ -55,25 +55,25 @@ export function renderSlideIndex({ render, data }) {
55
55
  ),
56
56
  ),
57
57
 
58
- // Grades
58
+ // Levels
59
59
  div(
60
60
  { className: "slide-section" },
61
61
  a(
62
- { href: "#/overview/grade" },
62
+ { href: "#/overview/level" },
63
63
  heading2(
64
64
  { className: "slide-section-title" },
65
- `${getConceptEmoji(data.framework, "grade")} `,
66
- span({ className: "gradient-text" }, "Grades"),
65
+ `${getConceptEmoji(data.framework, "level")} `,
66
+ span({ className: "gradient-text" }, "Levels"),
67
67
  ),
68
68
  ),
69
69
  ul(
70
70
  { className: "related-list" },
71
- ...data.grades.map((grade) =>
71
+ ...data.levels.map((level) =>
72
72
  li(
73
73
  {},
74
74
  a(
75
- { href: `#/grade/${grade.id}` },
76
- `${grade.id} - ${grade.professionalTitle}`,
75
+ { href: `#/level/${level.id}` },
76
+ `${level.id} - ${level.professionalTitle}`,
77
77
  ),
78
78
  ),
79
79
  ),
@@ -174,7 +174,7 @@ export function renderSlideIndex({ render, data }) {
174
174
  { className: "related-list" },
175
175
  ...generateAllJobs({
176
176
  disciplines: data.disciplines,
177
- grades: data.grades,
177
+ levels: data.levels,
178
178
  tracks: data.tracks,
179
179
  skills: data.skills,
180
180
  behaviours: data.behaviours,
@@ -185,8 +185,8 @@ export function renderSlideIndex({ render, data }) {
185
185
  a(
186
186
  {
187
187
  href: job.track
188
- ? `#/job/${job.discipline.id}/${job.grade.id}/${job.track.id}`
189
- : `#/job/${job.discipline.id}/${job.grade.id}`,
188
+ ? `#/job/${job.discipline.id}/${job.level.id}/${job.track.id}`
189
+ : `#/job/${job.discipline.id}/${job.level.id}`,
190
190
  },
191
191
  job.title,
192
192
  ),