@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
@@ -4,15 +4,15 @@
4
4
  * Shows career progression analysis in the terminal.
5
5
  *
6
6
  * Usage:
7
- * npx pathway progress <discipline> <grade> # Progress for trackless job
8
- * npx pathway progress <discipline> <grade> --track=<track> # Progress with track
9
- * npx pathway progress <discipline> <from_grade> --compare=<to_grade> # Compare grades
7
+ * npx pathway progress <discipline> <level> # Progress for trackless job
8
+ * npx pathway progress <discipline> <level> --track=<track> # Progress with track
9
+ * npx pathway progress <discipline> <from_level> --compare=<to_level> # Compare levels
10
10
  */
11
11
 
12
12
  import { createCompositeCommand } from "./command-factory.js";
13
13
  import {
14
14
  prepareProgressDetail,
15
- getDefaultTargetGrade,
15
+ getDefaultTargetLevel,
16
16
  } from "../formatters/progress/shared.js";
17
17
  import { progressToMarkdown } from "../formatters/progress/markdown.js";
18
18
 
@@ -26,53 +26,53 @@ function formatProgress(view) {
26
26
 
27
27
  export const runProgressCommand = createCompositeCommand({
28
28
  commandName: "progress",
29
- requiredArgs: ["discipline_id", "grade_id"],
29
+ requiredArgs: ["discipline_id", "level_id"],
30
30
  findEntities: (data, args, options) => {
31
31
  const discipline = data.disciplines.find((d) => d.id === args[0]);
32
- const grade = data.grades.find((g) => g.id === args[1]);
32
+ const level = data.levels.find((g) => g.id === args[1]);
33
33
  const track = options.track
34
34
  ? data.tracks.find((t) => t.id === options.track)
35
35
  : null;
36
36
 
37
- let targetGrade;
37
+ let targetLevel;
38
38
  if (options.compare) {
39
- targetGrade = data.grades.find((g) => g.id === options.compare);
40
- if (!targetGrade) {
41
- console.error(`Target grade not found: ${options.compare}`);
39
+ targetLevel = data.levels.find((g) => g.id === options.compare);
40
+ if (!targetLevel) {
41
+ console.error(`Target level not found: ${options.compare}`);
42
42
  process.exit(1);
43
43
  }
44
44
  } else {
45
- targetGrade = getDefaultTargetGrade(grade, data.grades);
46
- if (!targetGrade) {
47
- console.error("No next grade available for progression.");
45
+ targetLevel = getDefaultTargetLevel(level, data.levels);
46
+ if (!targetLevel) {
47
+ console.error("No next level available for progression.");
48
48
  process.exit(1);
49
49
  }
50
50
  }
51
51
 
52
- return { discipline, grade, track, targetGrade };
52
+ return { discipline, level, track, targetLevel };
53
53
  },
54
54
  validateEntities: (entities, _data, options) => {
55
55
  if (!entities.discipline) {
56
56
  return `Discipline not found`;
57
57
  }
58
- if (!entities.grade) {
59
- return `Grade not found`;
58
+ if (!entities.level) {
59
+ return `Level not found`;
60
60
  }
61
61
  if (options.track && !entities.track) {
62
62
  return `Track not found: ${options.track}`;
63
63
  }
64
- if (!entities.targetGrade) {
65
- return `Target grade not found`;
64
+ if (!entities.targetLevel) {
65
+ return `Target level not found`;
66
66
  }
67
67
  return null;
68
68
  },
69
69
  presenter: (entities, data) =>
70
70
  prepareProgressDetail({
71
71
  fromDiscipline: entities.discipline,
72
- fromGrade: entities.grade,
72
+ fromLevel: entities.level,
73
73
  fromTrack: entities.track,
74
74
  toDiscipline: entities.discipline,
75
- toGrade: entities.targetGrade,
75
+ toLevel: entities.targetLevel,
76
76
  toTrack: entities.track,
77
77
  skills: data.skills,
78
78
  behaviours: data.behaviours,
@@ -49,7 +49,7 @@ function showQuestionsSummary(data) {
49
49
  console.log(`\n❓ Questions\n`);
50
50
 
51
51
  // Skill questions by level
52
- const skillLevels = [
52
+ const skillProficiencies = [
53
53
  "awareness",
54
54
  "foundational",
55
55
  "working",
@@ -57,10 +57,10 @@ function showQuestionsSummary(data) {
57
57
  "expert",
58
58
  ];
59
59
  const roleTypes = ["professionalQuestions", "managementQuestions"];
60
- const skillRows = skillLevels.map((level) => {
60
+ const skillRows = skillProficiencies.map((level) => {
61
61
  let count = 0;
62
62
  for (const skill of skills) {
63
- const sq = questions.skillLevels?.[skill.id];
63
+ const sq = questions.skillProficiencies?.[skill.id];
64
64
  if (sq) {
65
65
  for (const roleType of roleTypes) {
66
66
  count += (sq[roleType]?.[level] || []).length;
@@ -14,7 +14,7 @@
14
14
  import { createEntityCommand } from "./command-factory.js";
15
15
  import { skillToMarkdown } from "../formatters/skill/markdown.js";
16
16
  import { prepareSkillsList } from "../formatters/skill/shared.js";
17
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
17
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
18
18
  import { formatTable, formatError } from "../lib/cli-output.js";
19
19
  import { generateSkillMarkdown } from "@forwardimpact/libpathway/agent";
20
20
  import { formatAgentSkill } from "../formatters/agent/skill.js";
@@ -14,7 +14,7 @@ import { createEntityCommand } from "./command-factory.js";
14
14
  import { trackToMarkdown } from "../formatters/track/markdown.js";
15
15
  import { sortTracksByName } from "../formatters/track/shared.js";
16
16
  import { formatTable } from "../lib/cli-output.js";
17
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
17
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
18
18
 
19
19
  /**
20
20
  * Format track summary output
@@ -3,20 +3,21 @@
3
3
  *
4
4
  * Re-downloads the distribution bundle from the published site URL
5
5
  * and updates the local ~/.fit/pathway/ installation.
6
+ * Updates the global @forwardimpact/pathway package if the version changed.
6
7
  */
7
8
 
8
9
  import { cp, mkdir, rm, readFile, writeFile, access } from "fs/promises";
9
10
  import { join } from "path";
10
11
  import { homedir } from "os";
11
12
  import { execFileSync, execSync } from "child_process";
12
- import { loadFrameworkConfig } from "@forwardimpact/schema/loader";
13
+ import { loadFrameworkConfig } from "@forwardimpact/map/loader";
13
14
 
14
15
  const INSTALL_DIR = join(homedir(), ".fit", "pathway");
15
16
 
16
17
  /**
17
18
  * Run the update command.
18
19
  * Reads siteUrl from the installed framework.yaml, re-downloads the bundle,
19
- * extracts data, and runs npm install to update dependencies.
20
+ * extracts data, and updates the global pathway package if the version changed.
20
21
  *
21
22
  * @param {Object} params - Command parameters
22
23
  * @param {string} params.dataDir - Path to data directory (may be the installed one)
@@ -82,7 +83,7 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
82
83
  ]);
83
84
  console.log(" ✓ Extracted");
84
85
 
85
- // 3. Compare versions
86
+ // 3. Compare versions from bundle's package.json (version manifest)
86
87
  const newPkgPath = join(extractDir, "package.json");
87
88
  const oldPkgPath = join(INSTALL_DIR, "package.json");
88
89
  const newPkg = JSON.parse(await readFile(newPkgPath, "utf8"));
@@ -104,21 +105,18 @@ export async function runUpdateCommand({ dataDir: _dataDir, options }) {
104
105
  await cp(join(extractDir, "data"), installDataDir, { recursive: true });
105
106
  console.log(" ✓ Data updated");
106
107
 
107
- // 5. Update package.json if version changed
108
+ // 5. Update version manifest
109
+ await writeFile(oldPkgPath, JSON.stringify(newPkg, null, 2) + "\n");
110
+
111
+ // 6. Update global pathway package if version changed
108
112
  if (oldVersion !== newVersion) {
109
113
  console.log(` Updating pathway ${oldVersion} → ${newVersion}...`);
110
- await writeFile(oldPkgPath, JSON.stringify(newPkg, null, 2) + "\n");
111
- console.log(" ✓ package.json updated");
114
+ execSync(`npm install -g @forwardimpact/pathway@${newVersion}`, {
115
+ stdio: "ignore",
116
+ });
117
+ console.log(" ✓ Global package updated");
112
118
  }
113
119
 
114
- // 6. Run npm install
115
- console.log(" Installing dependencies...");
116
- execSync("npm install --production --ignore-scripts --no-audit --no-fund", {
117
- cwd: INSTALL_DIR,
118
- stdio: "ignore",
119
- });
120
- console.log(" ✓ Dependencies installed");
121
-
122
120
  // 7. Report
123
121
  console.log(`
124
122
  ✅ Update complete!
@@ -30,7 +30,7 @@ export function createNavButton({ label, href, variant = "primary" }) {
30
30
  /**
31
31
  * Create a button to navigate to job builder with a parameter
32
32
  * @param {Object} options - Configuration options
33
- * @param {string} options.paramName - Parameter name (discipline, grade, track)
33
+ * @param {string} options.paramName - Parameter name (discipline, level, track)
34
34
  * @param {string} options.paramValue - Parameter value (the ID)
35
35
  * @param {string} [options.label] - Optional custom label
36
36
  * @returns {HTMLElement}
@@ -38,7 +38,7 @@ export function createNavButton({ label, href, variant = "primary" }) {
38
38
  export function createJobBuilderButton({ paramName, paramValue, label }) {
39
39
  const defaultLabels = {
40
40
  discipline: "Build Job with this Discipline →",
41
- grade: "Build Job at this Grade →",
41
+ level: "Build Job at this Level →",
42
42
  track: "Build Job with this Track →",
43
43
  };
44
44
 
@@ -52,7 +52,7 @@ export function createJobBuilderButton({ paramName, paramValue, label }) {
52
52
  /**
53
53
  * Create a button to navigate to interview prep with a parameter
54
54
  * @param {Object} options - Configuration options
55
- * @param {string} options.paramName - Parameter name (discipline, grade, track)
55
+ * @param {string} options.paramName - Parameter name (discipline, level, track)
56
56
  * @param {string} options.paramValue - Parameter value (the ID)
57
57
  * @param {string} [options.label] - Optional custom label
58
58
  * @returns {HTMLElement}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Builder component for discipline/grade/track selection pages
2
+ * Builder component for discipline/level/track selection pages
3
3
  */
4
4
 
5
5
  import {
@@ -32,7 +32,7 @@ import { createReactive } from "../lib/reactive.js";
32
32
  /**
33
33
  * @typedef {Object} BuilderSelection
34
34
  * @property {Object} discipline - Selected discipline
35
- * @property {Object} grade - Selected grade
35
+ * @property {Object} level - Selected level
36
36
  * @property {Object} track - Selected track
37
37
  */
38
38
 
@@ -78,10 +78,10 @@ export function createBuilder({
78
78
  const selection = createReactive({
79
79
  discipline: urlParams.get("discipline") || "",
80
80
  track: urlParams.get("track") || "",
81
- grade: urlParams.get("grade") || "",
81
+ level: urlParams.get("level") || "",
82
82
  });
83
83
 
84
- const sortedGrades = [...data.grades].sort((a, b) => a.level - b.level);
84
+ const sortedLevels = [...data.levels].sort((a, b) => a.level - b.level);
85
85
 
86
86
  // Create elements that need references
87
87
  const previewContainer = div(
@@ -155,9 +155,9 @@ export function createBuilder({
155
155
  }
156
156
 
157
157
  // Subscribe to selection changes - all updates happen here
158
- selection.subscribe(({ discipline, track, grade }) => {
159
- // Track is now optional - only discipline and grade are required
160
- if (!discipline || !grade) {
158
+ selection.subscribe(({ discipline, track, level }) => {
159
+ // Track is now optional - only discipline and level are required
160
+ if (!discipline || !level) {
161
161
  previewContainer.innerHTML = "";
162
162
  previewContainer.appendChild(
163
163
  p({ className: "text-muted" }, emptyPreviewText),
@@ -168,9 +168,9 @@ export function createBuilder({
168
168
 
169
169
  const disciplineObj = data.disciplines.find((d) => d.id === discipline);
170
170
  const trackObj = track ? data.tracks.find((t) => t.id === track) : null;
171
- const gradeObj = data.grades.find((g) => g.id === grade);
171
+ const levelObj = data.levels.find((g) => g.id === level);
172
172
 
173
- if (!disciplineObj || !gradeObj) {
173
+ if (!disciplineObj || !levelObj) {
174
174
  previewContainer.innerHTML = "";
175
175
  previewContainer.appendChild(
176
176
  p({ className: "text-muted" }, "Invalid selection. Please try again."),
@@ -182,7 +182,7 @@ export function createBuilder({
182
182
  const selectionObj = {
183
183
  discipline: disciplineObj,
184
184
  track: trackObj,
185
- grade: gradeObj,
185
+ level: levelObj,
186
186
  };
187
187
  const preview = previewPresenter(selectionObj, data);
188
188
 
@@ -193,8 +193,8 @@ export function createBuilder({
193
193
 
194
194
  // Wire up button
195
195
  actionButton.addEventListener("click", () => {
196
- const { discipline, track, grade } = selection.get();
197
- window.location.hash = detailPath({ discipline, track, grade });
196
+ const { discipline, track, level } = selection.get();
197
+ window.location.hash = detailPath({ discipline, track, level });
198
198
  });
199
199
 
200
200
  // Build the page
@@ -234,17 +234,17 @@ export function createBuilder({
234
234
  getDisplayName: (d) => d.specialization || d.name,
235
235
  }),
236
236
  ),
237
- // Grade selector (second)
237
+ // Level selector (second)
238
238
  div(
239
239
  { className: "form-group" },
240
- label({ className: "form-label" }, labels.grade || "Grade"),
240
+ label({ className: "form-label" }, labels.level || "Level"),
241
241
  createSelectWithValue({
242
- id: "grade-select",
243
- items: sortedGrades,
244
- initialValue: selection.get().grade,
245
- placeholder: "Select a grade...",
242
+ id: "level-select",
243
+ items: sortedLevels,
244
+ initialValue: selection.get().level,
245
+ placeholder: "Select a level...",
246
246
  onChange: (value) => {
247
- selection.update((prev) => ({ ...prev, grade: value }));
247
+ selection.update((prev) => ({ ...prev, level: value }));
248
248
  },
249
249
  getDisplayName: (g) => g.id,
250
250
  }),
@@ -284,7 +284,7 @@ export function createBuilder({
284
284
 
285
285
  // Trigger initial update if preselected
286
286
  const initial = selection.get();
287
- if (initial.discipline || initial.track || initial.grade) {
287
+ if (initial.discipline || initial.track || initial.level) {
288
288
  setTimeout(() => selection.set(selection.get()), 0);
289
289
  }
290
290
 
@@ -370,12 +370,12 @@ export function createProgressPreview(preview, selection) {
370
370
  );
371
371
  }
372
372
 
373
- const { discipline, grade, track } = selection;
373
+ const { discipline, level, track } = selection;
374
374
 
375
375
  // Build badges array - track is optional
376
376
  const badges = [
377
377
  createBadge(discipline.specialization, "discipline"),
378
- createBadge(grade.id, "grade"),
378
+ createBadge(level.id, "level"),
379
379
  ];
380
380
  if (track) {
381
381
  badges.push(createBadge(track.name, "track"));
@@ -394,19 +394,19 @@ export function createProgressPreview(preview, selection) {
394
394
  div({ className: "preview-label" }, "Progression Paths Available"),
395
395
  div(
396
396
  { className: "preview-paths" },
397
- preview.nextGrade
397
+ preview.nextLevel
398
398
  ? div(
399
399
  { className: "path-item" },
400
400
  span({ className: "path-icon" }, "📈"),
401
401
  span(
402
402
  {},
403
- `Next Grade: ${preview.nextGrade.id} - ${preview.nextGrade.name}`,
403
+ `Next Level: ${preview.nextLevel.id} - ${preview.nextLevel.name}`,
404
404
  ),
405
405
  )
406
406
  : div(
407
407
  { className: "path-item text-muted" },
408
408
  span({ className: "path-icon" }, "🏆"),
409
- span({}, "You're at the highest grade!"),
409
+ span({}, "You're at the highest level!"),
410
410
  ),
411
411
  preview.validTracks.length > 0
412
412
  ? div(
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import { div, span, details, summary } from "../lib/render.js";
9
- import { getCapabilityEmoji } from "@forwardimpact/schema/levels";
9
+ import { getCapabilityEmoji } from "@forwardimpact/map/levels";
10
10
 
11
11
  /**
12
12
  * Create checklist display grouped by capability
@@ -9,7 +9,7 @@
9
9
  import { ComparisonRadarChart } from "../lib/radar.js";
10
10
  import { div, h3 } from "../lib/render.js";
11
11
  import {
12
- getSkillLevelIndex,
12
+ getSkillProficiencyIndex,
13
13
  getBehaviourMaturityIndex,
14
14
  formatLevel,
15
15
  } from "../lib/render.js";
@@ -97,7 +97,7 @@ export function createComparisonSkillRadar(
97
97
 
98
98
  currentData.push({
99
99
  label: skillName,
100
- value: currentSkill ? getSkillLevelIndex(currentSkill.level) : 0,
100
+ value: currentSkill ? getSkillProficiencyIndex(currentSkill.level) : 0,
101
101
  maxValue: 5,
102
102
  description: currentSkill
103
103
  ? `${formatLevel(currentSkill.type)} - ${formatLevel(currentSkill.level)}`
@@ -106,7 +106,7 @@ export function createComparisonSkillRadar(
106
106
 
107
107
  targetData.push({
108
108
  label: skillName,
109
- value: targetSkill ? getSkillLevelIndex(targetSkill.level) : 0,
109
+ value: targetSkill ? getSkillProficiencyIndex(targetSkill.level) : 0,
110
110
  maxValue: 5,
111
111
  description: targetSkill
112
112
  ? `${formatLevel(targetSkill.type)} - ${formatLevel(targetSkill.level)}`
@@ -21,9 +21,9 @@ import { createBackLink } from "./nav.js";
21
21
  import { createTag } from "./card.js";
22
22
  import { formatLevel } from "../lib/render.js";
23
23
  import {
24
- SKILL_LEVEL_ORDER,
24
+ SKILL_PROFICIENCY_ORDER,
25
25
  BEHAVIOUR_MATURITY_ORDER,
26
- } from "@forwardimpact/schema/levels";
26
+ } from "@forwardimpact/map/levels";
27
27
 
28
28
  /**
29
29
  * Create a detail page header
@@ -80,7 +80,7 @@ export function createDetailSection({ title, content }) {
80
80
  */
81
81
  export function createLevelTable(descriptions, type = "skill") {
82
82
  const levels =
83
- type === "skill" ? SKILL_LEVEL_ORDER : BEHAVIOUR_MATURITY_ORDER;
83
+ type === "skill" ? SKILL_PROFICIENCY_ORDER : BEHAVIOUR_MATURITY_ORDER;
84
84
 
85
85
  const levelLabels = Object.fromEntries(
86
86
  levels.map((level, index) => [level, String(index + 1)]),
@@ -59,7 +59,7 @@ export function createFixedGrid(columns, children, options = {}) {
59
59
  }
60
60
 
61
61
  /**
62
- * Create a grid for form selectors (discipline/grade/track dropdowns)
62
+ * Create a grid for form selectors (discipline/level/track dropdowns)
63
63
  * Uses auto-grid-sm (200px min)
64
64
  * @param {HTMLElement[]} children - Form control elements
65
65
  * @returns {HTMLElement}
@@ -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/schema/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
  });
@@ -63,20 +63,20 @@
63
63
  margin: 0;
64
64
  }
65
65
 
66
- /* Grades timeline */
67
- .grades-timeline {
66
+ /* Levels timeline */
67
+ .levels-timeline {
68
68
  display: flex;
69
69
  flex-direction: column;
70
70
  gap: var(--space-md);
71
71
  }
72
72
 
73
- .grade-timeline-item {
73
+ .level-timeline-item {
74
74
  display: flex;
75
75
  gap: var(--space-lg);
76
76
  align-items: flex-start;
77
77
  }
78
78
 
79
- .grade-level-marker {
79
+ .level-level-marker {
80
80
  width: 50px;
81
81
  height: 50px;
82
82
  border-radius: 50%;
@@ -90,7 +90,7 @@
90
90
  flex-shrink: 0;
91
91
  }
92
92
 
93
- .grade-timeline-content {
93
+ .level-timeline-content {
94
94
  flex: 1;
95
95
  }
96
96
  }
@@ -21,7 +21,7 @@ import { createLevelCell } from "../../components/detail.js";
21
21
  import {
22
22
  BEHAVIOUR_MATURITY_ORDER,
23
23
  getConceptEmoji,
24
- } from "@forwardimpact/schema/levels";
24
+ } from "@forwardimpact/map/levels";
25
25
  import { prepareBehaviourDetail } from "./shared.js";
26
26
  import { createJsonLdScript, behaviourToJsonLd } from "../json-ld.js";
27
27
 
@@ -18,7 +18,7 @@ import {
18
18
  createJobBuilderButton,
19
19
  createInterviewPrepButton,
20
20
  } from "../../components/action-buttons.js";
21
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
21
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
22
22
  import { prepareDisciplineDetail } from "./shared.js";
23
23
  import { createJsonLdScript, disciplineToJsonLd } from "../json-ld.js";
24
24
  import { createBadge } from "../../components/card.js";
@@ -5,7 +5,7 @@
5
5
  import { div, heading1, heading2, p, a, span } from "../../lib/render.js";
6
6
  import { createBackLink } from "../../components/nav.js";
7
7
  import { prepareDriverDetail } from "./shared.js";
8
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
8
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
9
9
  import { createJsonLdScript, driverToJsonLd } from "../json-ld.js";
10
10
 
11
11
  /**
@@ -55,10 +55,10 @@ export {
55
55
  disciplineToMicrodata,
56
56
  } from "./discipline/microdata.js";
57
57
 
58
- // Grade formatters
59
- export { gradeListToMarkdown, gradeToMarkdown } from "./grade/markdown.js";
60
- export { gradeToDOM } from "./grade/dom.js";
61
- export { gradeListToMicrodata, gradeToMicrodata } from "./grade/microdata.js";
58
+ // Level formatters
59
+ export { levelListToMarkdown, levelToMarkdown } from "./level/markdown.js";
60
+ export { levelToDOM } from "./level/dom.js";
61
+ export { levelListToMicrodata, levelToMicrodata } from "./level/microdata.js";
62
62
 
63
63
  // Track formatters
64
64
  export { trackListToMarkdown, trackToMarkdown } from "./track/markdown.js";
@@ -75,7 +75,7 @@ export {
75
75
  behaviourToJsonLd,
76
76
  disciplineToJsonLd,
77
77
  trackToJsonLd,
78
- gradeToJsonLd,
78
+ levelToJsonLd,
79
79
  driverToJsonLd,
80
80
  stageToJsonLd,
81
81
  } from "./json-ld.js";
@@ -5,7 +5,7 @@
5
5
  import { div, heading1, heading2, p, span } from "../../lib/render.js";
6
6
  import { createBackLink } from "../../components/nav.js";
7
7
  import { createLevelDots } from "../../components/detail.js";
8
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
8
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
9
9
 
10
10
  /**
11
11
  * Format interview detail as DOM elements
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import { formatLevel } from "../../lib/render.js";
6
- import { getConceptEmoji } from "@forwardimpact/schema/levels";
6
+ import { getConceptEmoji } from "@forwardimpact/map/levels";
7
7
 
8
8
  /**
9
9
  * Format interview detail as markdown