@forwardimpact/pathway 0.12.0 → 0.14.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 (54) hide show
  1. package/bin/fit-pathway.js +32 -12
  2. package/package.json +3 -3
  3. package/src/commands/build.js +98 -2
  4. package/src/commands/index.js +1 -0
  5. package/src/commands/interview.js +52 -14
  6. package/src/commands/job.js +1 -0
  7. package/src/commands/questions.js +13 -10
  8. package/src/commands/stage.js +8 -8
  9. package/src/commands/update.js +133 -0
  10. package/src/components/command-prompt.js +85 -0
  11. package/src/components/nav.js +2 -2
  12. package/src/components/top-bar.js +97 -0
  13. package/src/css/bundles/app.css +2 -0
  14. package/src/css/components/badges.css +41 -11
  15. package/src/css/components/command-prompt.css +98 -0
  16. package/src/css/components/layout.css +0 -3
  17. package/src/css/components/nav.css +121 -81
  18. package/src/css/components/surfaces.css +1 -1
  19. package/src/css/components/top-bar.css +180 -0
  20. package/src/css/pages/agent-builder.css +0 -9
  21. package/src/css/pages/landing.css +4 -0
  22. package/src/css/pages/lifecycle.css +5 -2
  23. package/src/css/reset.css +1 -1
  24. package/src/css/tokens.css +25 -11
  25. package/src/css/views/slide-base.css +2 -1
  26. package/src/formatters/agent/dom.js +0 -26
  27. package/src/formatters/agent/profile.js +13 -7
  28. package/src/formatters/agent/skill.js +4 -4
  29. package/src/formatters/interview/markdown.js +62 -3
  30. package/src/formatters/interview/shared.js +89 -52
  31. package/src/formatters/questions/markdown.js +15 -0
  32. package/src/formatters/questions/shared.js +70 -58
  33. package/src/formatters/stage/dom.js +13 -10
  34. package/src/formatters/stage/microdata.js +14 -8
  35. package/src/formatters/stage/shared.js +4 -4
  36. package/src/index.html +69 -44
  37. package/src/lib/cli-command.js +145 -0
  38. package/src/lib/state.js +2 -0
  39. package/src/lib/yaml-loader.js +39 -21
  40. package/src/main.js +47 -26
  41. package/src/pages/agent-builder.js +0 -28
  42. package/src/pages/behaviour.js +3 -1
  43. package/src/pages/discipline.js +3 -1
  44. package/src/pages/driver.js +6 -1
  45. package/src/pages/grade.js +6 -1
  46. package/src/pages/interview.js +61 -5
  47. package/src/pages/job.js +1 -0
  48. package/src/pages/landing.js +7 -0
  49. package/src/pages/skill.js +9 -2
  50. package/src/pages/track.js +6 -1
  51. package/src/slides/job.js +1 -0
  52. package/templates/agent.template.md +17 -10
  53. package/templates/install.template.sh +33 -0
  54. package/templates/skill.template.md +15 -7
package/src/index.html CHANGED
@@ -43,56 +43,81 @@
43
43
  </script>
44
44
  </head>
45
45
  <body>
46
- <div id="app">
47
- <nav id="app-nav">
48
- <div class="nav-brand">
49
- <a href="#/">Engineering Pathway</a>
50
- <span class="brand-tag">#BenchTools</span>
51
- </div>
46
+ <div id="app" class="drawer-open">
47
+ <header class="top-bar" id="top-bar">
52
48
  <button
53
- class="nav-toggle"
54
- id="nav-toggle"
55
- aria-label="Toggle navigation"
49
+ class="top-bar__toggle"
50
+ id="sidebar-toggle"
51
+ aria-label="Toggle sidebar"
56
52
  >
57
- <span></span>
58
- <span></span>
59
- <span></span>
53
+ <svg viewBox="0 0 24 24">
54
+ <rect x="3" y="3" width="18" height="18" rx="3" />
55
+ <line x1="9" y1="3" x2="9" y2="21" />
56
+ </svg>
60
57
  </button>
61
- <ul class="nav-links" id="nav-links">
62
- <li><a href="#/discipline">Disciplines</a></li>
63
- <li><a href="#/grade">Grades</a></li>
64
- <li><a href="#/track">Tracks</a></li>
65
- <li><a href="#/behaviour">Behaviours</a></li>
66
- <li><a href="#/skill">Skills</a></li>
67
- <li><a href="#/driver">Drivers</a></li>
68
- <li><a href="#/stage">Stages</a></li>
69
- <li><a href="#/tool">Tools</a></li>
70
- <li><a href="#/job-builder" class="nav-cta">Build a Job</a></li>
71
- <li><a href="#/agent-builder" class="nav-cta">Build an Agent</a></li>
72
- <li><a href="#/interview-prep" class="nav-cta">Interview Prep</a></li>
73
- <li>
74
- <a href="#/career-progress" class="nav-cta">Career Progress</a>
75
- </li>
76
- <li>
77
- <a href="#/self-assessment" class="nav-cta">Self-Assessment</a>
78
- </li>
79
- </ul>
80
- </nav>
81
-
82
- <main id="app-content">
83
- <div class="loading">
84
- <div class="loading-spinner"></div>
85
- <p>Loading...</p>
58
+ <div class="top-bar__command">
59
+ <span class="top-bar__prompt">$</span>
60
+ <span class="top-bar__command-text" id="cli-command"
61
+ >npx fit-pathway</span
62
+ >
63
+ <button class="top-bar__copy" id="cli-copy" aria-label="Copy command">
64
+ <svg viewBox="0 0 24 24">
65
+ <rect x="9" y="9" width="13" height="13" rx="2" />
66
+ <path
67
+ d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
68
+ />
69
+ </svg>
70
+ </button>
86
71
  </div>
87
- </main>
72
+ </header>
88
73
 
89
- <footer id="app-footer">
90
- <p>Engineering Pathway</p>
91
- <nav class="footer-links">
92
- <a href="slides.html">Slides</a>
93
- <a href="handout.html">Handouts</a>
74
+ <aside id="app-drawer">
75
+ <div class="drawer-header">
76
+ <a href="#/" class="nav-brand">
77
+ <span class="brand-emoji">🧭</span>
78
+ <span class="brand-title">Engineering Pathway</span>
79
+ <span class="brand-tag">#BenchTools</span>
80
+ </a>
81
+ </div>
82
+ <nav class="drawer-nav" id="drawer-nav">
83
+ <div class="drawer-section">
84
+ <span class="drawer-section-label">Browse</span>
85
+ <a href="#/discipline">Disciplines</a>
86
+ <a href="#/grade">Grades</a>
87
+ <a href="#/track">Tracks</a>
88
+ <a href="#/behaviour">Behaviours</a>
89
+ <a href="#/skill">Skills</a>
90
+ <a href="#/driver">Drivers</a>
91
+ <a href="#/stage">Stages</a>
92
+ <a href="#/tool">Tools</a>
93
+ </div>
94
+ <div class="drawer-section">
95
+ <span class="drawer-section-label">Work</span>
96
+ <a href="#/job-builder" class="drawer-cta">Build a Job</a>
97
+ <a href="#/agent-builder" class="drawer-cta">Build an Agent</a>
98
+ <a href="#/interview-prep" class="drawer-cta">Interview Prep</a>
99
+ <a href="#/career-progress" class="drawer-cta">Career Progress</a>
100
+ <a href="#/self-assessment" class="drawer-cta">Self-Assessment</a>
101
+ </div>
94
102
  </nav>
95
- </footer>
103
+ </aside>
104
+
105
+ <div class="app-body">
106
+ <main id="app-content">
107
+ <div class="loading">
108
+ <div class="loading-spinner"></div>
109
+ <p>Loading...</p>
110
+ </div>
111
+ </main>
112
+
113
+ <footer id="app-footer">
114
+ <p>Engineering Pathway</p>
115
+ <nav class="footer-links">
116
+ <a href="slides.html">Slides</a>
117
+ <a href="handout.html">Handouts</a>
118
+ </nav>
119
+ </footer>
120
+ </div>
96
121
  </div>
97
122
 
98
123
  <script type="module" src="main.js"></script>
@@ -0,0 +1,145 @@
1
+ /**
2
+ * CLI Command Mapping
3
+ *
4
+ * Maps hash routes to their equivalent `npx fit-pathway` CLI commands.
5
+ */
6
+
7
+ /**
8
+ * Route-to-CLI command rules, checked in order.
9
+ * Each rule has a pattern (regex) and a function that produces the CLI string.
10
+ * @type {Array<{ pattern: RegExp, toCommand: (match: RegExpMatchArray) => string }>}
11
+ */
12
+ const ROUTE_COMMANDS = [
13
+ // Landing
14
+ { pattern: /^\/$/, toCommand: () => "npx fit-pathway" },
15
+
16
+ // Entity lists
17
+ { pattern: /^\/skill$/, toCommand: () => "npx fit-pathway skill" },
18
+ { pattern: /^\/behaviour$/, toCommand: () => "npx fit-pathway behaviour" },
19
+ {
20
+ pattern: /^\/discipline$/,
21
+ toCommand: () => "npx fit-pathway discipline",
22
+ },
23
+ { pattern: /^\/track$/, toCommand: () => "npx fit-pathway track" },
24
+ { pattern: /^\/grade$/, toCommand: () => "npx fit-pathway grade" },
25
+ { pattern: /^\/driver$/, toCommand: () => "npx fit-pathway driver" },
26
+ { pattern: /^\/stage$/, toCommand: () => "npx fit-pathway stage" },
27
+ { pattern: /^\/tool$/, toCommand: () => "npx fit-pathway tool" },
28
+
29
+ // Entity details
30
+ {
31
+ pattern: /^\/skill\/(.+)$/,
32
+ toCommand: (m) => `npx fit-pathway skill ${m[1]}`,
33
+ },
34
+ {
35
+ pattern: /^\/behaviour\/(.+)$/,
36
+ toCommand: (m) => `npx fit-pathway behaviour ${m[1]}`,
37
+ },
38
+ {
39
+ pattern: /^\/discipline\/(.+)$/,
40
+ toCommand: (m) => `npx fit-pathway discipline ${m[1]}`,
41
+ },
42
+ {
43
+ pattern: /^\/track\/(.+)$/,
44
+ toCommand: (m) => `npx fit-pathway track ${m[1]}`,
45
+ },
46
+ {
47
+ pattern: /^\/grade\/(.+)$/,
48
+ toCommand: (m) => `npx fit-pathway grade ${m[1]}`,
49
+ },
50
+ {
51
+ pattern: /^\/driver\/(.+)$/,
52
+ toCommand: (m) => `npx fit-pathway driver ${m[1]}`,
53
+ },
54
+ {
55
+ pattern: /^\/stage\/(.+)$/,
56
+ toCommand: (m) => `npx fit-pathway stage ${m[1]}`,
57
+ },
58
+
59
+ // Job builder + detail
60
+ {
61
+ pattern: /^\/job-builder$/,
62
+ toCommand: () => "npx fit-pathway job --list",
63
+ },
64
+ {
65
+ pattern: /^\/job\/([^/]+)\/([^/]+)\/([^/]+)$/,
66
+ toCommand: (m) => `npx fit-pathway job ${m[1]} ${m[2]} --track=${m[3]}`,
67
+ },
68
+ {
69
+ pattern: /^\/job\/([^/]+)\/([^/]+)$/,
70
+ toCommand: (m) => `npx fit-pathway job ${m[1]} ${m[2]}`,
71
+ },
72
+
73
+ // Interview builder + detail
74
+ {
75
+ pattern: /^\/interview-prep$/,
76
+ toCommand: () => "npx fit-pathway interview --list",
77
+ },
78
+ {
79
+ pattern: /^\/interview\/([^/]+)\/([^/]+)\/([^/]+)$/,
80
+ toCommand: (m) =>
81
+ `npx fit-pathway interview ${m[1]} ${m[2]} --track=${m[3]}`,
82
+ },
83
+ {
84
+ pattern: /^\/interview\/([^/]+)\/([^/]+)$/,
85
+ toCommand: (m) => `npx fit-pathway interview ${m[1]} ${m[2]}`,
86
+ },
87
+
88
+ // Career progress builder + detail
89
+ {
90
+ pattern: /^\/career-progress$/,
91
+ toCommand: () => "npx fit-pathway progress --list",
92
+ },
93
+ {
94
+ pattern: /^\/progress\/([^/]+)\/([^/]+)\/([^/]+)$/,
95
+ toCommand: (m) =>
96
+ `npx fit-pathway progress ${m[1]} ${m[2]} --track=${m[3]}`,
97
+ },
98
+ {
99
+ pattern: /^\/progress\/([^/]+)\/([^/]+)$/,
100
+ toCommand: (m) => `npx fit-pathway progress ${m[1]} ${m[2]}`,
101
+ },
102
+
103
+ // Self-assessment
104
+ {
105
+ pattern: /^\/self-assessment$/,
106
+ toCommand: () => "npx fit-pathway self-assessment",
107
+ },
108
+ {
109
+ pattern: /^\/self-assessment\/results$/,
110
+ toCommand: () => "npx fit-pathway self-assessment",
111
+ },
112
+
113
+ // Agent builder + detail
114
+ {
115
+ pattern: /^\/agent-builder$/,
116
+ toCommand: () => "npx fit-pathway agent --list",
117
+ },
118
+ {
119
+ pattern: /^\/agent\/([^/]+)\/([^/]+)\/([^/]+)$/,
120
+ toCommand: (m) =>
121
+ `npx fit-pathway agent ${m[1]} --track=${m[2]} --stage=${m[3]}`,
122
+ },
123
+ {
124
+ pattern: /^\/agent\/([^/]+)\/([^/]+)$/,
125
+ toCommand: (m) => `npx fit-pathway agent ${m[1]} --track=${m[2]}`,
126
+ },
127
+ {
128
+ pattern: /^\/agent\/([^/]+)$/,
129
+ toCommand: (m) => `npx fit-pathway agent ${m[1]}`,
130
+ },
131
+ ];
132
+
133
+ /**
134
+ * Get the CLI command equivalent for a given hash path
135
+ * @param {string} hashPath - The hash path (without #), e.g. "/skill/testing"
136
+ * @returns {string} The CLI command string
137
+ */
138
+ export function getCliCommand(hashPath) {
139
+ const path = hashPath.split("?")[0] || "/";
140
+ for (const { pattern, toCommand } of ROUTE_COMMANDS) {
141
+ const match = path.match(pattern);
142
+ if (match) return toCommand(match);
143
+ }
144
+ return "npx fit-pathway";
145
+ }
package/src/lib/state.js CHANGED
@@ -132,6 +132,7 @@ export function getFilters(entity) {
132
132
  * @property {string} title - Application title
133
133
  * @property {string} tag - Brand hashtag/tag
134
134
  * @property {string} description - Application description
135
+ * @property {string} emojiIcon - Emoji icon for the framework
135
136
  */
136
137
 
137
138
  /**
@@ -144,5 +145,6 @@ export function getBranding() {
144
145
  title: framework.title || "Engineering Pathway",
145
146
  tag: framework.tag || "#BenchTools",
146
147
  description: framework.description || "",
148
+ emojiIcon: framework.emojiIcon || "🧭",
147
149
  };
148
150
  }
@@ -237,35 +237,52 @@ async function loadCapabilitiesFromDir(capabilitiesDir) {
237
237
  }
238
238
 
239
239
  /**
240
- * Load questions from folder structure using skill/behaviour IDs
240
+ * Load questions from folder structure using skill/behaviour/capability IDs
241
241
  * @param {string} questionsDir - Path to questions directory
242
242
  * @param {Array} skills - Skills array (with id property)
243
243
  * @param {Array} behaviours - Behaviours array (with id property)
244
+ * @param {Array} capabilities - Capabilities array (with id property)
244
245
  * @returns {Promise<Object>}
245
246
  */
246
- async function loadQuestionFolder(questionsDir, skills, behaviours) {
247
- const [skillEntries, behaviourEntries] = await Promise.all([
248
- Promise.all(
249
- skills.map(async (skill) => {
250
- const content = await tryLoadYamlFile(
251
- `${questionsDir}/skills/${skill.id}.yaml`,
252
- );
253
- return [skill.id, content || {}];
254
- }),
255
- ),
256
- Promise.all(
257
- behaviours.map(async (behaviour) => {
258
- const content = await tryLoadYamlFile(
259
- `${questionsDir}/behaviours/${behaviour.id}.yaml`,
260
- );
261
- return [behaviour.id, content || {}];
262
- }),
263
- ),
264
- ]);
247
+ async function loadQuestionFolder(
248
+ questionsDir,
249
+ skills,
250
+ behaviours,
251
+ capabilities,
252
+ ) {
253
+ const [skillEntries, behaviourEntries, capabilityEntries] = await Promise.all(
254
+ [
255
+ Promise.all(
256
+ skills.map(async (skill) => {
257
+ const content = await tryLoadYamlFile(
258
+ `${questionsDir}/skills/${skill.id}.yaml`,
259
+ );
260
+ return [skill.id, content || {}];
261
+ }),
262
+ ),
263
+ Promise.all(
264
+ behaviours.map(async (behaviour) => {
265
+ const content = await tryLoadYamlFile(
266
+ `${questionsDir}/behaviours/${behaviour.id}.yaml`,
267
+ );
268
+ return [behaviour.id, content || {}];
269
+ }),
270
+ ),
271
+ Promise.all(
272
+ capabilities.map(async (capability) => {
273
+ const content = await tryLoadYamlFile(
274
+ `${questionsDir}/capabilities/${capability.id}.yaml`,
275
+ );
276
+ return [capability.id, content || {}];
277
+ }),
278
+ ),
279
+ ],
280
+ );
265
281
 
266
282
  return {
267
283
  skillLevels: Object.fromEntries(skillEntries),
268
284
  behaviourMaturities: Object.fromEntries(behaviourEntries),
285
+ capabilityLevels: Object.fromEntries(capabilityEntries),
269
286
  };
270
287
  }
271
288
 
@@ -293,11 +310,12 @@ export async function loadAllData(dataDir = "./data") {
293
310
  loadYamlFile(`${dataDir}/framework.yaml`),
294
311
  ]);
295
312
 
296
- // Load questions using skill/behaviour IDs
313
+ // Load questions using skill/behaviour/capability IDs
297
314
  const questions = await loadQuestionFolder(
298
315
  `${dataDir}/questions`,
299
316
  skills,
300
317
  behaviours,
318
+ capabilities,
301
319
  );
302
320
 
303
321
  return {
package/src/main.js CHANGED
@@ -6,6 +6,8 @@ import { createPagesRouter } from "./lib/router-pages.js";
6
6
  import { setData, setError, getBranding } from "./lib/state.js";
7
7
  import { loadAllData } from "./lib/yaml-loader.js";
8
8
  import { render, div, h1, p, showError } from "./lib/render.js";
9
+ import { updateActiveNav } from "./components/nav.js";
10
+ import { setupTopBar, updateCommand } from "./components/top-bar.js";
9
11
 
10
12
  const router = createPagesRouter({
11
13
  onNotFound: renderNotFound,
@@ -41,8 +43,9 @@ import { renderAgentBuilder } from "./pages/agent-builder.js";
41
43
  * Initialize the application
42
44
  */
43
45
  async function init() {
44
- // Set up navigation toggle for mobile
45
- setupMobileNav();
46
+ // Set up top bar (sidebar toggle + CLI command display)
47
+ setupTopBar();
48
+ setupDrawerOverlay();
46
49
 
47
50
  // Load data
48
51
  try {
@@ -63,6 +66,14 @@ async function init() {
63
66
 
64
67
  // Start router
65
68
  router.start();
69
+
70
+ // Update active nav link and CLI command on route changes
71
+ const updateNav = () => {
72
+ updateActiveNav(window.location.hash.slice(1) || "/");
73
+ updateCommand();
74
+ };
75
+ window.addEventListener("hashchange", updateNav);
76
+ updateNav();
66
77
  }
67
78
 
68
79
  /**
@@ -175,13 +186,19 @@ function populateBranding() {
175
186
  // Update document title
176
187
  document.title = branding.title;
177
188
 
178
- // Update nav brand
179
- const navBrand = document.querySelector(".nav-brand a");
180
- if (navBrand) {
181
- navBrand.textContent = branding.title;
189
+ // Update drawer brand
190
+ const brandTitle = document.querySelector(".nav-brand .brand-title");
191
+ if (brandTitle) {
192
+ brandTitle.textContent = branding.title;
193
+ }
194
+
195
+ // Update drawer brand emoji
196
+ const brandEmoji = document.querySelector(".nav-brand .brand-emoji");
197
+ if (brandEmoji) {
198
+ brandEmoji.textContent = branding.emojiIcon;
182
199
  }
183
200
 
184
- // Update nav brand tag
201
+ // Update drawer brand tag
185
202
  const brandTag = document.querySelector(".nav-brand .brand-tag");
186
203
  if (brandTag) {
187
204
  brandTag.textContent = branding.tag;
@@ -195,26 +212,30 @@ function populateBranding() {
195
212
  }
196
213
 
197
214
  /**
198
- * Set up mobile navigation toggle
215
+ * Set up drawer overlay for mobile backdrop and auto-close behavior
199
216
  */
200
- function setupMobileNav() {
201
- const toggle = document.getElementById("nav-toggle");
202
- const links = document.getElementById("nav-links");
203
-
204
- if (toggle && links) {
205
- toggle.addEventListener("click", () => {
206
- links.classList.toggle("nav-open");
207
- toggle.classList.toggle("nav-toggle-active");
208
- });
209
-
210
- // Close menu when a link is clicked
211
- links.addEventListener("click", (e) => {
212
- if (e.target.tagName === "A") {
213
- links.classList.remove("nav-open");
214
- toggle.classList.remove("nav-toggle-active");
215
- }
216
- });
217
- }
217
+ function setupDrawerOverlay() {
218
+ const app = document.getElementById("app");
219
+ const drawer = document.getElementById("app-drawer");
220
+
221
+ if (!drawer) return;
222
+
223
+ // Create overlay element for mobile backdrop
224
+ const overlay = document.createElement("div");
225
+ overlay.className = "drawer-overlay";
226
+ app.insertBefore(overlay, drawer);
227
+
228
+ // Close drawer when overlay is clicked (mobile)
229
+ overlay.addEventListener("click", () => {
230
+ app.classList.remove("drawer-open");
231
+ });
232
+
233
+ // Close drawer on mobile when a link is clicked
234
+ drawer.addEventListener("click", (e) => {
235
+ if (e.target.tagName === "A" && window.innerWidth <= 768) {
236
+ app.classList.remove("drawer-open");
237
+ }
238
+ });
218
239
  }
219
240
 
220
241
  // Start the app
@@ -562,9 +562,6 @@ function createAllStagesPreview(context) {
562
562
  content: createToolkitTable(toolkit),
563
563
  })
564
564
  : null,
565
-
566
- // CLI hint
567
- createCliHint(humanDiscipline.id, humanTrack.id),
568
565
  );
569
566
  }
570
567
 
@@ -687,9 +684,6 @@ function createSingleStagePreview(context, stage) {
687
684
  content: createToolkitTable(toolkit),
688
685
  })
689
686
  : null,
690
-
691
- // CLI hint
692
- createCliHint(humanDiscipline.id, humanTrack.id, stage.id),
693
687
  );
694
688
  }
695
689
 
@@ -936,29 +930,7 @@ async function importJSZip() {
936
930
  return module.default;
937
931
  }
938
932
 
939
- /**
940
- * Create CLI hint section
941
- * @param {string} disciplineId - Discipline ID
942
- * @param {string} trackId - Track ID
943
- * @param {string} [stageId] - Optional stage ID
944
- * @returns {HTMLElement}
945
- */
946
- function createCliHint(disciplineId, trackId, stageId) {
947
- const stageArg = stageId ? ` --stage=${stageId}` : "";
948
- const command = `npx pathway agent ${disciplineId} ${trackId}${stageArg} --output=.github`;
949
-
950
- const container = section(
951
- { className: "agent-section cli-hint" },
952
- h2({}, "CLI Alternative"),
953
- p({}, "Generate this agent from the command line:"),
954
- createCodeDisplay({
955
- content: command,
956
- language: "bash",
957
- }),
958
- );
959
933
 
960
- return container;
961
- }
962
934
 
963
935
  /**
964
936
  * Create help section explaining how agent builder works
@@ -9,6 +9,7 @@ import { renderNotFound } from "../components/error-page.js";
9
9
  import { prepareBehavioursList } from "../formatters/behaviour/shared.js";
10
10
  import { behaviourToDOM } from "../formatters/behaviour/dom.js";
11
11
  import { behaviourToCardConfig } from "../lib/card-mappers.js";
12
+ import { getConceptEmoji } from "@forwardimpact/schema/levels";
12
13
 
13
14
  /**
14
15
  * Render behaviours list page
@@ -16,6 +17,7 @@ import { behaviourToCardConfig } from "../lib/card-mappers.js";
16
17
  export function renderBehavioursList() {
17
18
  const { data } = getState();
18
19
  const { framework } = data;
20
+ const behaviourEmoji = getConceptEmoji(framework, "behaviour");
19
21
 
20
22
  // Transform data for list view
21
23
  const { items } = prepareBehavioursList(data.behaviours);
@@ -27,7 +29,7 @@ export function renderBehavioursList() {
27
29
  { className: "page-header" },
28
30
  h1(
29
31
  { className: "page-title" },
30
- framework.entityDefinitions.behaviour.title,
32
+ `${behaviourEmoji} ${framework.entityDefinitions.behaviour.title}`,
31
33
  ),
32
34
  p(
33
35
  { className: "page-description" },
@@ -10,6 +10,7 @@ import { renderNotFound } from "../components/error-page.js";
10
10
  import { prepareDisciplinesList } from "../formatters/discipline/shared.js";
11
11
  import { disciplineToDOM } from "../formatters/discipline/dom.js";
12
12
  import { disciplineToCardConfig } from "../lib/card-mappers.js";
13
+ import { getConceptEmoji } from "@forwardimpact/schema/levels";
13
14
 
14
15
  /**
15
16
  * Format discipline group name for display
@@ -42,6 +43,7 @@ function renderDisciplineGroupHeader(groupName, count) {
42
43
  export function renderDisciplinesList() {
43
44
  const { data } = getState();
44
45
  const { framework } = data;
46
+ const disciplineEmoji = getConceptEmoji(framework, "discipline");
45
47
 
46
48
  // Transform data for list view (grouped by professional/management)
47
49
  const { groups } = prepareDisciplinesList(data.disciplines);
@@ -53,7 +55,7 @@ export function renderDisciplinesList() {
53
55
  { className: "page-header" },
54
56
  h1(
55
57
  { className: "page-title" },
56
- framework.entityDefinitions.discipline.title,
58
+ `${disciplineEmoji} ${framework.entityDefinitions.discipline.title}`,
57
59
  ),
58
60
  p(
59
61
  { className: "page-description" },
@@ -9,6 +9,7 @@ import { renderNotFound } from "../components/error-page.js";
9
9
  import { prepareDriversList } from "../formatters/driver/shared.js";
10
10
  import { driverToDOM } from "../formatters/driver/dom.js";
11
11
  import { driverToCardConfig } from "../lib/card-mappers.js";
12
+ import { getConceptEmoji } from "@forwardimpact/schema/levels";
12
13
 
13
14
  /**
14
15
  * Render drivers list page
@@ -16,6 +17,7 @@ import { driverToCardConfig } from "../lib/card-mappers.js";
16
17
  export function renderDriversList() {
17
18
  const { data } = getState();
18
19
  const { framework } = data;
20
+ const driverEmoji = getConceptEmoji(framework, "driver");
19
21
 
20
22
  // Transform data for list view
21
23
  const { items } = prepareDriversList(data.drivers);
@@ -25,7 +27,10 @@ export function renderDriversList() {
25
27
  // Header
26
28
  div(
27
29
  { className: "page-header" },
28
- h1({ className: "page-title" }, framework.entityDefinitions.driver.title),
30
+ h1(
31
+ { className: "page-title" },
32
+ `${driverEmoji} ${framework.entityDefinitions.driver.title}`,
33
+ ),
29
34
  p(
30
35
  { className: "page-description" },
31
36
  framework.entityDefinitions.driver.description.trim(),
@@ -8,6 +8,7 @@ import { createBadge } from "../components/card.js";
8
8
  import { renderNotFound } from "../components/error-page.js";
9
9
  import { prepareGradesList } from "../formatters/grade/shared.js";
10
10
  import { gradeToDOM } from "../formatters/grade/dom.js";
11
+ import { getConceptEmoji } from "@forwardimpact/schema/levels";
11
12
 
12
13
  /**
13
14
  * Render grades list page
@@ -15,6 +16,7 @@ import { gradeToDOM } from "../formatters/grade/dom.js";
15
16
  export function renderGradesList() {
16
17
  const { data } = getState();
17
18
  const { framework } = data;
19
+ const gradeEmoji = getConceptEmoji(framework, "grade");
18
20
 
19
21
  // Transform data for list view
20
22
  const { items } = prepareGradesList(data.grades);
@@ -24,7 +26,10 @@ export function renderGradesList() {
24
26
  // Header
25
27
  div(
26
28
  { className: "page-header" },
27
- h1({ className: "page-title" }, framework.entityDefinitions.grade.title),
29
+ h1(
30
+ { className: "page-title" },
31
+ `${gradeEmoji} ${framework.entityDefinitions.grade.title}`,
32
+ ),
28
33
  p(
29
34
  { className: "page-description" },
30
35
  framework.entityDefinitions.grade.description.trim(),