@forwardimpact/pathway 0.1.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.
- package/LICENSE +201 -0
- package/README.md +104 -0
- package/app/commands/agent.js +430 -0
- package/app/commands/behaviour.js +61 -0
- package/app/commands/command-factory.js +211 -0
- package/app/commands/discipline.js +58 -0
- package/app/commands/driver.js +94 -0
- package/app/commands/grade.js +60 -0
- package/app/commands/index.js +20 -0
- package/app/commands/init.js +67 -0
- package/app/commands/interview.js +68 -0
- package/app/commands/job.js +157 -0
- package/app/commands/progress.js +77 -0
- package/app/commands/questions.js +179 -0
- package/app/commands/serve.js +143 -0
- package/app/commands/site.js +121 -0
- package/app/commands/skill.js +76 -0
- package/app/commands/stage.js +129 -0
- package/app/commands/track.js +70 -0
- package/app/components/action-buttons.js +66 -0
- package/app/components/behaviour-profile.js +53 -0
- package/app/components/builder.js +341 -0
- package/app/components/card.js +98 -0
- package/app/components/checklist.js +145 -0
- package/app/components/comparison-radar.js +237 -0
- package/app/components/detail.js +230 -0
- package/app/components/error-page.js +72 -0
- package/app/components/grid.js +109 -0
- package/app/components/list.js +120 -0
- package/app/components/modifier-table.js +142 -0
- package/app/components/nav.js +64 -0
- package/app/components/progression-table.js +320 -0
- package/app/components/radar-chart.js +102 -0
- package/app/components/skill-matrix.js +97 -0
- package/app/css/base.css +56 -0
- package/app/css/bundles/app.css +40 -0
- package/app/css/bundles/handout.css +43 -0
- package/app/css/bundles/slides.css +40 -0
- package/app/css/components/badges.css +215 -0
- package/app/css/components/buttons.css +101 -0
- package/app/css/components/forms.css +105 -0
- package/app/css/components/layout.css +209 -0
- package/app/css/components/nav.css +166 -0
- package/app/css/components/progress.css +166 -0
- package/app/css/components/states.css +82 -0
- package/app/css/components/surfaces.css +243 -0
- package/app/css/components/tables.css +362 -0
- package/app/css/components/typography.css +122 -0
- package/app/css/components/utilities.css +41 -0
- package/app/css/pages/agent-builder.css +391 -0
- package/app/css/pages/assessment-results.css +453 -0
- package/app/css/pages/detail.css +59 -0
- package/app/css/pages/interview-builder.css +148 -0
- package/app/css/pages/job-builder.css +134 -0
- package/app/css/pages/landing.css +92 -0
- package/app/css/pages/lifecycle.css +118 -0
- package/app/css/pages/progress-builder.css +274 -0
- package/app/css/pages/self-assessment.css +502 -0
- package/app/css/reset.css +50 -0
- package/app/css/tokens.css +153 -0
- package/app/css/views/handout.css +30 -0
- package/app/css/views/print.css +608 -0
- package/app/css/views/slide-animations.css +113 -0
- package/app/css/views/slide-base.css +330 -0
- package/app/css/views/slide-sections.css +597 -0
- package/app/css/views/slide-tables.css +275 -0
- package/app/formatters/agent/dom.js +540 -0
- package/app/formatters/agent/profile.js +133 -0
- package/app/formatters/agent/skill.js +58 -0
- package/app/formatters/behaviour/dom.js +91 -0
- package/app/formatters/behaviour/markdown.js +54 -0
- package/app/formatters/behaviour/shared.js +64 -0
- package/app/formatters/discipline/dom.js +187 -0
- package/app/formatters/discipline/markdown.js +87 -0
- package/app/formatters/discipline/shared.js +131 -0
- package/app/formatters/driver/dom.js +103 -0
- package/app/formatters/driver/shared.js +92 -0
- package/app/formatters/grade/dom.js +208 -0
- package/app/formatters/grade/markdown.js +94 -0
- package/app/formatters/grade/shared.js +86 -0
- package/app/formatters/index.js +50 -0
- package/app/formatters/interview/dom.js +97 -0
- package/app/formatters/interview/markdown.js +66 -0
- package/app/formatters/interview/shared.js +332 -0
- package/app/formatters/job/description.js +176 -0
- package/app/formatters/job/dom.js +411 -0
- package/app/formatters/job/markdown.js +102 -0
- package/app/formatters/progress/dom.js +135 -0
- package/app/formatters/progress/markdown.js +86 -0
- package/app/formatters/progress/shared.js +339 -0
- package/app/formatters/questions/json.js +43 -0
- package/app/formatters/questions/markdown.js +303 -0
- package/app/formatters/questions/shared.js +274 -0
- package/app/formatters/questions/yaml.js +76 -0
- package/app/formatters/shared.js +71 -0
- package/app/formatters/skill/dom.js +168 -0
- package/app/formatters/skill/markdown.js +109 -0
- package/app/formatters/skill/shared.js +125 -0
- package/app/formatters/stage/dom.js +135 -0
- package/app/formatters/stage/index.js +12 -0
- package/app/formatters/stage/shared.js +111 -0
- package/app/formatters/track/dom.js +128 -0
- package/app/formatters/track/markdown.js +105 -0
- package/app/formatters/track/shared.js +181 -0
- package/app/handout-main.js +421 -0
- package/app/handout.html +21 -0
- package/app/index.html +59 -0
- package/app/lib/card-mappers.js +173 -0
- package/app/lib/cli-output.js +270 -0
- package/app/lib/error-boundary.js +70 -0
- package/app/lib/errors.js +49 -0
- package/app/lib/form-controls.js +47 -0
- package/app/lib/job-cache.js +86 -0
- package/app/lib/markdown.js +114 -0
- package/app/lib/radar.js +866 -0
- package/app/lib/reactive.js +77 -0
- package/app/lib/render.js +212 -0
- package/app/lib/router-core.js +160 -0
- package/app/lib/router-pages.js +16 -0
- package/app/lib/router-slides.js +202 -0
- package/app/lib/state.js +148 -0
- package/app/lib/utils.js +14 -0
- package/app/lib/yaml-loader.js +327 -0
- package/app/main.js +213 -0
- package/app/model/agent.js +702 -0
- package/app/model/checklist.js +137 -0
- package/app/model/derivation.js +699 -0
- package/app/model/index-generator.js +71 -0
- package/app/model/interview.js +539 -0
- package/app/model/job.js +222 -0
- package/app/model/levels.js +591 -0
- package/app/model/loader.js +564 -0
- package/app/model/matching.js +858 -0
- package/app/model/modifiers.js +158 -0
- package/app/model/profile.js +266 -0
- package/app/model/progression.js +507 -0
- package/app/model/validation.js +1385 -0
- package/app/pages/agent-builder.js +823 -0
- package/app/pages/assessment-results.js +507 -0
- package/app/pages/behaviour.js +70 -0
- package/app/pages/discipline.js +71 -0
- package/app/pages/driver.js +106 -0
- package/app/pages/grade.js +117 -0
- package/app/pages/interview-builder.js +50 -0
- package/app/pages/interview.js +304 -0
- package/app/pages/job-builder.js +50 -0
- package/app/pages/job.js +58 -0
- package/app/pages/landing.js +305 -0
- package/app/pages/progress-builder.js +58 -0
- package/app/pages/progress.js +495 -0
- package/app/pages/self-assessment.js +729 -0
- package/app/pages/skill.js +113 -0
- package/app/pages/stage.js +231 -0
- package/app/pages/track.js +69 -0
- package/app/slide-main.js +360 -0
- package/app/slides/behaviour.js +38 -0
- package/app/slides/chapter.js +82 -0
- package/app/slides/discipline.js +40 -0
- package/app/slides/driver.js +39 -0
- package/app/slides/grade.js +32 -0
- package/app/slides/index.js +198 -0
- package/app/slides/interview.js +58 -0
- package/app/slides/job.js +55 -0
- package/app/slides/overview.js +126 -0
- package/app/slides/progress.js +83 -0
- package/app/slides/skill.js +40 -0
- package/app/slides/track.js +39 -0
- package/app/slides.html +56 -0
- package/app/types.js +147 -0
- package/bin/pathway.js +489 -0
- package/examples/agents/.claude/skills/architecture-design/SKILL.md +88 -0
- package/examples/agents/.claude/skills/cloud-platforms/SKILL.md +90 -0
- package/examples/agents/.claude/skills/code-quality-review/SKILL.md +67 -0
- package/examples/agents/.claude/skills/data-modeling/SKILL.md +99 -0
- package/examples/agents/.claude/skills/developer-experience/SKILL.md +99 -0
- package/examples/agents/.claude/skills/devops-cicd/SKILL.md +96 -0
- package/examples/agents/.claude/skills/full-stack-development/SKILL.md +90 -0
- package/examples/agents/.claude/skills/knowledge-management/SKILL.md +100 -0
- package/examples/agents/.claude/skills/pattern-generalization/SKILL.md +102 -0
- package/examples/agents/.claude/skills/sre-practices/SKILL.md +117 -0
- package/examples/agents/.claude/skills/technical-debt-management/SKILL.md +123 -0
- package/examples/agents/.claude/skills/technical-writing/SKILL.md +129 -0
- package/examples/agents/.github/agents/se-platform-code.agent.md +181 -0
- package/examples/agents/.github/agents/se-platform-plan.agent.md +178 -0
- package/examples/agents/.github/agents/se-platform-review.agent.md +113 -0
- package/examples/agents/.vscode/settings.json +8 -0
- package/examples/behaviours/_index.yaml +8 -0
- package/examples/behaviours/outcome_ownership.yaml +44 -0
- package/examples/behaviours/polymathic_knowledge.yaml +42 -0
- package/examples/behaviours/precise_communication.yaml +40 -0
- package/examples/behaviours/relentless_curiosity.yaml +38 -0
- package/examples/behaviours/systems_thinking.yaml +41 -0
- package/examples/capabilities/_index.yaml +8 -0
- package/examples/capabilities/business.yaml +251 -0
- package/examples/capabilities/delivery.yaml +352 -0
- package/examples/capabilities/people.yaml +100 -0
- package/examples/capabilities/reliability.yaml +318 -0
- package/examples/capabilities/scale.yaml +394 -0
- package/examples/disciplines/_index.yaml +5 -0
- package/examples/disciplines/data_engineering.yaml +76 -0
- package/examples/disciplines/software_engineering.yaml +76 -0
- package/examples/drivers.yaml +205 -0
- package/examples/framework.yaml +58 -0
- package/examples/grades.yaml +118 -0
- package/examples/questions/behaviours/outcome_ownership.yaml +52 -0
- package/examples/questions/behaviours/polymathic_knowledge.yaml +48 -0
- package/examples/questions/behaviours/precise_communication.yaml +55 -0
- package/examples/questions/behaviours/relentless_curiosity.yaml +51 -0
- package/examples/questions/behaviours/systems_thinking.yaml +53 -0
- package/examples/questions/skills/architecture_design.yaml +54 -0
- package/examples/questions/skills/cloud_platforms.yaml +48 -0
- package/examples/questions/skills/code_quality.yaml +49 -0
- package/examples/questions/skills/data_modeling.yaml +46 -0
- package/examples/questions/skills/devops.yaml +47 -0
- package/examples/questions/skills/full_stack_development.yaml +48 -0
- package/examples/questions/skills/sre_practices.yaml +44 -0
- package/examples/questions/skills/stakeholder_management.yaml +49 -0
- package/examples/questions/skills/team_collaboration.yaml +43 -0
- package/examples/questions/skills/technical_writing.yaml +43 -0
- package/examples/self-assessments.yaml +66 -0
- package/examples/stages.yaml +76 -0
- package/examples/tracks/_index.yaml +6 -0
- package/examples/tracks/manager.yaml +53 -0
- package/examples/tracks/platform.yaml +54 -0
- package/examples/tracks/sre.yaml +58 -0
- package/examples/vscode-settings.yaml +22 -0
- package/package.json +68 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handout View Main Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Displays all slides of a category on a single page for printing handouts.
|
|
5
|
+
* Routes:
|
|
6
|
+
* / - Index with links to all categories
|
|
7
|
+
* /driver - All driver slides
|
|
8
|
+
* /skill - All skill slides
|
|
9
|
+
* /behaviour - All behaviour slides
|
|
10
|
+
* /job - All discipline, track, and grade slides
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { setData, getState } from "./lib/state.js";
|
|
14
|
+
import { loadAllData } from "./lib/yaml-loader.js";
|
|
15
|
+
import {
|
|
16
|
+
div,
|
|
17
|
+
h1,
|
|
18
|
+
p,
|
|
19
|
+
a,
|
|
20
|
+
ul,
|
|
21
|
+
li,
|
|
22
|
+
span,
|
|
23
|
+
heading1,
|
|
24
|
+
heading2,
|
|
25
|
+
} from "./lib/render.js";
|
|
26
|
+
|
|
27
|
+
// Import model functions
|
|
28
|
+
import { getCapabilityOrder, getConceptEmoji } from "./model/levels.js";
|
|
29
|
+
|
|
30
|
+
// Import formatters
|
|
31
|
+
import {
|
|
32
|
+
driverToDOM,
|
|
33
|
+
skillToDOM,
|
|
34
|
+
behaviourToDOM,
|
|
35
|
+
disciplineToDOM,
|
|
36
|
+
gradeToDOM,
|
|
37
|
+
trackToDOM,
|
|
38
|
+
} from "./formatters/index.js";
|
|
39
|
+
import { sortTracksByType } from "./formatters/track/shared.js";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Create a chapter cover page
|
|
43
|
+
* @param {Object} params
|
|
44
|
+
* @param {string} params.emoji - Chapter emoji
|
|
45
|
+
* @param {string} params.title - Chapter title
|
|
46
|
+
* @param {string} params.description - Chapter description
|
|
47
|
+
* @returns {HTMLElement}
|
|
48
|
+
*/
|
|
49
|
+
function createChapterCover({ emoji, title, description }) {
|
|
50
|
+
return div(
|
|
51
|
+
{ className: "chapter-cover" },
|
|
52
|
+
h1(
|
|
53
|
+
{ className: "chapter-title" },
|
|
54
|
+
emoji,
|
|
55
|
+
" ",
|
|
56
|
+
span({ className: "gradient-text" }, title),
|
|
57
|
+
),
|
|
58
|
+
p({ className: "chapter-description" }, description.trim()),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get handout content container
|
|
64
|
+
* @returns {HTMLElement}
|
|
65
|
+
*/
|
|
66
|
+
function getHandoutContent() {
|
|
67
|
+
return document.getElementById("handout-content");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Hide loading indicator
|
|
72
|
+
*/
|
|
73
|
+
function hideLoading() {
|
|
74
|
+
const loading = document.getElementById("slide-loading");
|
|
75
|
+
if (loading) {
|
|
76
|
+
loading.classList.add("hidden");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Show loading indicator
|
|
82
|
+
*/
|
|
83
|
+
function showLoading() {
|
|
84
|
+
const loading = document.getElementById("slide-loading");
|
|
85
|
+
if (loading) {
|
|
86
|
+
loading.classList.remove("hidden");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Render content to handout container
|
|
92
|
+
* @param {HTMLElement} content
|
|
93
|
+
*/
|
|
94
|
+
function renderHandout(content) {
|
|
95
|
+
const container = getHandoutContent();
|
|
96
|
+
container.innerHTML = "";
|
|
97
|
+
container.appendChild(content);
|
|
98
|
+
hideLoading();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Render the handout index page
|
|
103
|
+
* @param {Object} data
|
|
104
|
+
*/
|
|
105
|
+
function renderIndex(data) {
|
|
106
|
+
const { framework } = data;
|
|
107
|
+
|
|
108
|
+
const content = div(
|
|
109
|
+
{ className: "slide slide-index" },
|
|
110
|
+
div(
|
|
111
|
+
{ className: "page-header" },
|
|
112
|
+
heading1(
|
|
113
|
+
{ className: "page-title" },
|
|
114
|
+
`${framework.emoji} ${framework.title} Handouts`,
|
|
115
|
+
),
|
|
116
|
+
p(
|
|
117
|
+
{ className: "page-description" },
|
|
118
|
+
"Printable handouts with all items in each category on a single page.",
|
|
119
|
+
),
|
|
120
|
+
),
|
|
121
|
+
|
|
122
|
+
div(
|
|
123
|
+
{ className: "slide-section" },
|
|
124
|
+
heading2(
|
|
125
|
+
{ className: "slide-section-title" },
|
|
126
|
+
"📄 ",
|
|
127
|
+
span({ className: "gradient-text" }, "Available Handouts"),
|
|
128
|
+
),
|
|
129
|
+
ul(
|
|
130
|
+
{ className: "related-list" },
|
|
131
|
+
li(
|
|
132
|
+
{},
|
|
133
|
+
a(
|
|
134
|
+
{ href: "#/job" },
|
|
135
|
+
`${getConceptEmoji(framework, "job")} ${framework.entityDefinitions.job.title}`,
|
|
136
|
+
),
|
|
137
|
+
" - ",
|
|
138
|
+
`${data.disciplines.length} disciplines, ${data.tracks.length} tracks, ${data.grades.length} grades`,
|
|
139
|
+
),
|
|
140
|
+
li(
|
|
141
|
+
{},
|
|
142
|
+
a(
|
|
143
|
+
{ href: "#/skill" },
|
|
144
|
+
`${getConceptEmoji(framework, "skill")} ${framework.entityDefinitions.skill.title}`,
|
|
145
|
+
),
|
|
146
|
+
" - ",
|
|
147
|
+
`${data.skills.length} skill definitions`,
|
|
148
|
+
),
|
|
149
|
+
li(
|
|
150
|
+
{},
|
|
151
|
+
a(
|
|
152
|
+
{ href: "#/behaviour" },
|
|
153
|
+
`${getConceptEmoji(framework, "behaviour")} ${framework.entityDefinitions.behaviour.title}`,
|
|
154
|
+
),
|
|
155
|
+
" - ",
|
|
156
|
+
`${data.behaviours.length} behaviour definitions`,
|
|
157
|
+
),
|
|
158
|
+
li(
|
|
159
|
+
{},
|
|
160
|
+
a(
|
|
161
|
+
{ href: "#/driver" },
|
|
162
|
+
`${getConceptEmoji(framework, "driver")} ${framework.entityDefinitions.driver.title}`,
|
|
163
|
+
),
|
|
164
|
+
" - ",
|
|
165
|
+
`${data.drivers.length} driver definitions`,
|
|
166
|
+
),
|
|
167
|
+
),
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
renderHandout(content);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Render all driver slides
|
|
176
|
+
* @param {Object} data
|
|
177
|
+
*/
|
|
178
|
+
function renderDriverHandout(data) {
|
|
179
|
+
const { framework } = data;
|
|
180
|
+
|
|
181
|
+
const slides = data.drivers.map((driver) => {
|
|
182
|
+
return driverToDOM(driver, {
|
|
183
|
+
skills: data.skills,
|
|
184
|
+
behaviours: data.behaviours,
|
|
185
|
+
framework: data.framework,
|
|
186
|
+
showBackLink: false,
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const content = div(
|
|
191
|
+
{},
|
|
192
|
+
createChapterCover({
|
|
193
|
+
emoji: getConceptEmoji(framework, "driver"),
|
|
194
|
+
title: framework.entityDefinitions.driver.title,
|
|
195
|
+
description: framework.entityDefinitions.driver.description,
|
|
196
|
+
}),
|
|
197
|
+
...slides,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
renderHandout(content);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Render all skill slides
|
|
205
|
+
* @param {Object} data
|
|
206
|
+
*/
|
|
207
|
+
function renderSkillHandout(data) {
|
|
208
|
+
const { framework } = data;
|
|
209
|
+
|
|
210
|
+
// Get capability order from data
|
|
211
|
+
const capabilityOrder = getCapabilityOrder(data.capabilities);
|
|
212
|
+
|
|
213
|
+
// Sort skills by capability order, then by name within capability
|
|
214
|
+
const sortedSkills = [...data.skills].sort((a, b) => {
|
|
215
|
+
const aIndex = capabilityOrder.indexOf(a.capability);
|
|
216
|
+
const bIndex = capabilityOrder.indexOf(b.capability);
|
|
217
|
+
if (aIndex !== bIndex) {
|
|
218
|
+
return aIndex - bIndex;
|
|
219
|
+
}
|
|
220
|
+
return a.name.localeCompare(b.name);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const slides = sortedSkills.map((skill) => {
|
|
224
|
+
return skillToDOM(skill, {
|
|
225
|
+
disciplines: data.disciplines,
|
|
226
|
+
tracks: data.tracks,
|
|
227
|
+
drivers: data.drivers,
|
|
228
|
+
capabilities: data.capabilities,
|
|
229
|
+
showBackLink: false,
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
const content = div(
|
|
234
|
+
{},
|
|
235
|
+
createChapterCover({
|
|
236
|
+
emoji: getConceptEmoji(framework, "skill"),
|
|
237
|
+
title: framework.entityDefinitions.skill.title,
|
|
238
|
+
description: framework.entityDefinitions.skill.description,
|
|
239
|
+
}),
|
|
240
|
+
...slides,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
renderHandout(content);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Render all behaviour slides
|
|
248
|
+
* @param {Object} data
|
|
249
|
+
*/
|
|
250
|
+
function renderBehaviourHandout(data) {
|
|
251
|
+
const { framework } = data;
|
|
252
|
+
|
|
253
|
+
const slides = data.behaviours.map((behaviour) => {
|
|
254
|
+
return behaviourToDOM(behaviour, {
|
|
255
|
+
drivers: data.drivers,
|
|
256
|
+
framework: data.framework,
|
|
257
|
+
showBackLink: false,
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const content = div(
|
|
262
|
+
{},
|
|
263
|
+
createChapterCover({
|
|
264
|
+
emoji: getConceptEmoji(framework, "behaviour"),
|
|
265
|
+
title: framework.entityDefinitions.behaviour.title,
|
|
266
|
+
description: framework.entityDefinitions.behaviour.description,
|
|
267
|
+
}),
|
|
268
|
+
...slides,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
renderHandout(content);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Render all job component slides (disciplines, grades, tracks)
|
|
276
|
+
* @param {Object} data
|
|
277
|
+
*/
|
|
278
|
+
function renderJobHandout(data) {
|
|
279
|
+
const { framework } = data;
|
|
280
|
+
|
|
281
|
+
const disciplineSlides = data.disciplines.map((discipline) => {
|
|
282
|
+
return disciplineToDOM(discipline, {
|
|
283
|
+
skills: data.skills,
|
|
284
|
+
behaviours: data.behaviours,
|
|
285
|
+
framework: data.framework,
|
|
286
|
+
showBackLink: false,
|
|
287
|
+
showBehaviourModifiers: false,
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const gradeSlides = data.grades.map((grade) => {
|
|
292
|
+
return gradeToDOM(grade, {
|
|
293
|
+
framework: data.framework,
|
|
294
|
+
showBackLink: false,
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const trackSlides = sortTracksByType(data.tracks).map((track) => {
|
|
299
|
+
return trackToDOM(track, {
|
|
300
|
+
skills: data.skills,
|
|
301
|
+
behaviours: data.behaviours,
|
|
302
|
+
disciplines: data.disciplines,
|
|
303
|
+
framework: data.framework,
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const content = div(
|
|
308
|
+
{},
|
|
309
|
+
// Disciplines chapter
|
|
310
|
+
createChapterCover({
|
|
311
|
+
emoji: getConceptEmoji(framework, "discipline"),
|
|
312
|
+
title: framework.entityDefinitions.discipline.title,
|
|
313
|
+
description: framework.entityDefinitions.discipline.description,
|
|
314
|
+
}),
|
|
315
|
+
...disciplineSlides,
|
|
316
|
+
|
|
317
|
+
// Tracks chapter
|
|
318
|
+
createChapterCover({
|
|
319
|
+
emoji: getConceptEmoji(framework, "track"),
|
|
320
|
+
title: framework.entityDefinitions.track.title,
|
|
321
|
+
description: framework.entityDefinitions.track.description,
|
|
322
|
+
}),
|
|
323
|
+
...trackSlides,
|
|
324
|
+
|
|
325
|
+
// Grades chapter
|
|
326
|
+
createChapterCover({
|
|
327
|
+
emoji: getConceptEmoji(framework, "grade"),
|
|
328
|
+
title: framework.entityDefinitions.grade.title,
|
|
329
|
+
description: framework.entityDefinitions.grade.description,
|
|
330
|
+
}),
|
|
331
|
+
...gradeSlides,
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
renderHandout(content);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Handle routing based on hash
|
|
339
|
+
*/
|
|
340
|
+
function handleRoute() {
|
|
341
|
+
const data = getState().data;
|
|
342
|
+
const hash = window.location.hash || "#/";
|
|
343
|
+
const path = hash.slice(1); // Remove #
|
|
344
|
+
|
|
345
|
+
switch (path) {
|
|
346
|
+
case "/":
|
|
347
|
+
case "":
|
|
348
|
+
renderIndex(data);
|
|
349
|
+
break;
|
|
350
|
+
case "/driver":
|
|
351
|
+
renderDriverHandout(data);
|
|
352
|
+
break;
|
|
353
|
+
case "/skill":
|
|
354
|
+
renderSkillHandout(data);
|
|
355
|
+
break;
|
|
356
|
+
case "/behaviour":
|
|
357
|
+
renderBehaviourHandout(data);
|
|
358
|
+
break;
|
|
359
|
+
case "/job":
|
|
360
|
+
renderJobHandout(data);
|
|
361
|
+
break;
|
|
362
|
+
default:
|
|
363
|
+
renderIndex(data);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Populate the page brand header with framework title and hashtag
|
|
369
|
+
* @param {Object} framework - Framework data from YAML
|
|
370
|
+
*/
|
|
371
|
+
function populateBrandHeader(framework) {
|
|
372
|
+
const header = document.getElementById("page-brand-header");
|
|
373
|
+
if (!header) return;
|
|
374
|
+
|
|
375
|
+
// Update document title
|
|
376
|
+
document.title = `${framework.title} - Handout View`;
|
|
377
|
+
|
|
378
|
+
header.innerHTML = "";
|
|
379
|
+
header.appendChild(
|
|
380
|
+
a(
|
|
381
|
+
{ className: "brand-title", href: "#/" },
|
|
382
|
+
`${framework.emoji} ${framework.title}`,
|
|
383
|
+
),
|
|
384
|
+
);
|
|
385
|
+
header.appendChild(span({ className: "brand-tag" }, framework.tag));
|
|
386
|
+
header.style.display = "";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Initialize the handout viewer
|
|
391
|
+
*/
|
|
392
|
+
async function init() {
|
|
393
|
+
showLoading();
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const data = await loadAllData();
|
|
397
|
+
setData(data);
|
|
398
|
+
|
|
399
|
+
// Populate brand header
|
|
400
|
+
populateBrandHeader(data.framework);
|
|
401
|
+
|
|
402
|
+
// Set up hash change listener
|
|
403
|
+
window.addEventListener("hashchange", handleRoute);
|
|
404
|
+
|
|
405
|
+
// Initial render
|
|
406
|
+
handleRoute();
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.error("Failed to initialize handout viewer:", error);
|
|
409
|
+
const container = getHandoutContent();
|
|
410
|
+
container.innerHTML = `
|
|
411
|
+
<div class="slide-error">
|
|
412
|
+
<h1>Initialization Error</h1>
|
|
413
|
+
<p>${error.message}</p>
|
|
414
|
+
</div>
|
|
415
|
+
`;
|
|
416
|
+
hideLoading();
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Start the application
|
|
421
|
+
init();
|
package/app/handout.html
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Engineering Pathway - Handout View</title>
|
|
7
|
+
<link rel="stylesheet" href="css/bundles/handout.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body class="slide-view handout-view">
|
|
10
|
+
<header
|
|
11
|
+
id="page-brand-header"
|
|
12
|
+
class="page-brand-header"
|
|
13
|
+
style="display: none"
|
|
14
|
+
></header>
|
|
15
|
+
<div id="slide-loading" class="slide-loading">
|
|
16
|
+
<p>Loading...</p>
|
|
17
|
+
</div>
|
|
18
|
+
<main id="handout-content"></main>
|
|
19
|
+
<script type="module" src="handout-main.js"></script>
|
|
20
|
+
</body>
|
|
21
|
+
</html>
|
package/app/index.html
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Engineering Pathway</title>
|
|
7
|
+
<link rel="stylesheet" href="css/bundles/app.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app">
|
|
11
|
+
<nav id="app-nav">
|
|
12
|
+
<div class="nav-brand">
|
|
13
|
+
<a href="#/">Engineering Pathway</a>
|
|
14
|
+
<span class="brand-tag">#BenchTools</span>
|
|
15
|
+
</div>
|
|
16
|
+
<button
|
|
17
|
+
class="nav-toggle"
|
|
18
|
+
id="nav-toggle"
|
|
19
|
+
aria-label="Toggle navigation"
|
|
20
|
+
>
|
|
21
|
+
<span></span>
|
|
22
|
+
<span></span>
|
|
23
|
+
<span></span>
|
|
24
|
+
</button>
|
|
25
|
+
<ul class="nav-links" id="nav-links">
|
|
26
|
+
<li><a href="#/discipline">Disciplines</a></li>
|
|
27
|
+
<li><a href="#/track">Tracks</a></li>
|
|
28
|
+
<li><a href="#/grade">Grades</a></li>
|
|
29
|
+
<li><a href="#/skill">Skills</a></li>
|
|
30
|
+
<li><a href="#/behaviour">Behaviours</a></li>
|
|
31
|
+
<li><a href="#/stage">Stages</a></li>
|
|
32
|
+
<li><a href="#/driver">Drivers</a></li>
|
|
33
|
+
<li><a href="#/job-builder" class="nav-cta">Build a Job</a></li>
|
|
34
|
+
<li><a href="#/agent-builder" class="nav-cta">Build an Agent</a></li>
|
|
35
|
+
<li><a href="#/interview-prep" class="nav-cta">Interview Prep</a></li>
|
|
36
|
+
<li>
|
|
37
|
+
<a href="#/career-progress" class="nav-cta">Career Progress</a>
|
|
38
|
+
</li>
|
|
39
|
+
<li>
|
|
40
|
+
<a href="#/self-assessment" class="nav-cta">Self-Assessment</a>
|
|
41
|
+
</li>
|
|
42
|
+
</ul>
|
|
43
|
+
</nav>
|
|
44
|
+
|
|
45
|
+
<main id="app-content">
|
|
46
|
+
<div class="loading">
|
|
47
|
+
<div class="loading-spinner"></div>
|
|
48
|
+
<p>Loading...</p>
|
|
49
|
+
</div>
|
|
50
|
+
</main>
|
|
51
|
+
|
|
52
|
+
<footer id="app-footer">
|
|
53
|
+
<p>Engineering Pathway</p>
|
|
54
|
+
</footer>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<script type="module" src="main.js"></script>
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Card Mapper Functions
|
|
3
|
+
*
|
|
4
|
+
* Reusable functions that map entities to card configurations.
|
|
5
|
+
* Used by both regular pages and overview slides.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createBadge } from "../components/card.js";
|
|
9
|
+
import { formatLevel } from "./render.js";
|
|
10
|
+
import { getCapabilityEmoji } from "../model/levels.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Map discipline to card config
|
|
14
|
+
* @param {Object} discipline
|
|
15
|
+
* @returns {Object}
|
|
16
|
+
*/
|
|
17
|
+
export function disciplineToCardConfig(discipline) {
|
|
18
|
+
return {
|
|
19
|
+
title: discipline.name,
|
|
20
|
+
description: discipline.truncatedDescription,
|
|
21
|
+
href: `/discipline/${discipline.id}`,
|
|
22
|
+
meta: [
|
|
23
|
+
createBadge(`${discipline.coreSkillsCount} core`, "primary"),
|
|
24
|
+
createBadge(
|
|
25
|
+
`${discipline.supportingSkillsCount} supporting`,
|
|
26
|
+
"secondary",
|
|
27
|
+
),
|
|
28
|
+
createBadge(`${discipline.broadSkillsCount} broad`, "broad"),
|
|
29
|
+
],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Map skill to card config
|
|
35
|
+
* @param {Object} skill
|
|
36
|
+
* @param {Array} capabilities
|
|
37
|
+
* @returns {Object}
|
|
38
|
+
*/
|
|
39
|
+
export function skillToCardConfig(skill, capabilities) {
|
|
40
|
+
return {
|
|
41
|
+
title: skill.name,
|
|
42
|
+
description: skill.truncatedDescription,
|
|
43
|
+
href: `/skill/${skill.id}`,
|
|
44
|
+
badges: [
|
|
45
|
+
createBadge(
|
|
46
|
+
formatCapability(skill.capability, capabilities),
|
|
47
|
+
skill.capability,
|
|
48
|
+
),
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Map behaviour to card config
|
|
55
|
+
* @param {Object} behaviour
|
|
56
|
+
* @returns {Object}
|
|
57
|
+
*/
|
|
58
|
+
export function behaviourToCardConfig(behaviour) {
|
|
59
|
+
return {
|
|
60
|
+
title: behaviour.name,
|
|
61
|
+
description: behaviour.truncatedDescription,
|
|
62
|
+
href: `/behaviour/${behaviour.id}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Map driver to card config
|
|
68
|
+
* @param {Object} driver
|
|
69
|
+
* @returns {Object}
|
|
70
|
+
*/
|
|
71
|
+
export function driverToCardConfig(driver) {
|
|
72
|
+
return {
|
|
73
|
+
title: driver.name,
|
|
74
|
+
description: driver.truncatedDescription,
|
|
75
|
+
href: `/driver/${driver.id}`,
|
|
76
|
+
meta: [
|
|
77
|
+
createBadge(`${driver.contributingSkillsCount} skills`, "default"),
|
|
78
|
+
createBadge(
|
|
79
|
+
`${driver.contributingBehavioursCount} behaviours`,
|
|
80
|
+
"primary",
|
|
81
|
+
),
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Map grade to card config (for timeline)
|
|
88
|
+
* @param {Object} grade
|
|
89
|
+
* @returns {Object}
|
|
90
|
+
*/
|
|
91
|
+
export function gradeToCardConfig(grade) {
|
|
92
|
+
return {
|
|
93
|
+
title: grade.displayName,
|
|
94
|
+
description: grade.scope || grade.truncatedDescription,
|
|
95
|
+
href: `/grade/${grade.id}`,
|
|
96
|
+
badges: [createBadge(grade.id, "default")],
|
|
97
|
+
meta: [
|
|
98
|
+
createBadge(
|
|
99
|
+
`Primary: ${formatLevel(grade.baseSkillLevels?.primary)}`,
|
|
100
|
+
"primary",
|
|
101
|
+
),
|
|
102
|
+
createBadge(
|
|
103
|
+
`Secondary: ${formatLevel(grade.baseSkillLevels?.secondary)}`,
|
|
104
|
+
"secondary",
|
|
105
|
+
),
|
|
106
|
+
createBadge(
|
|
107
|
+
`Broad: ${formatLevel(grade.baseSkillLevels?.broad)}`,
|
|
108
|
+
"broad",
|
|
109
|
+
),
|
|
110
|
+
],
|
|
111
|
+
yearsExperience: grade.yearsExperience,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Map track to card config
|
|
117
|
+
* @param {Object} track
|
|
118
|
+
* @returns {Object}
|
|
119
|
+
*/
|
|
120
|
+
export function trackToCardConfig(track) {
|
|
121
|
+
const badges = [];
|
|
122
|
+
if (track.isProfessional) {
|
|
123
|
+
badges.push(createBadge("Professional", "secondary"));
|
|
124
|
+
}
|
|
125
|
+
if (track.isManagement) {
|
|
126
|
+
badges.push(createBadge("Management", "default"));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
title: track.name,
|
|
131
|
+
description: track.truncatedDescription,
|
|
132
|
+
href: `/track/${track.id}`,
|
|
133
|
+
meta: badges,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Map job combination to card config
|
|
139
|
+
* @param {Object} job
|
|
140
|
+
* @returns {Object}
|
|
141
|
+
*/
|
|
142
|
+
export function jobToCardConfig(job) {
|
|
143
|
+
return {
|
|
144
|
+
title: job.title,
|
|
145
|
+
description: `${job.discipline.specialization || job.discipline.name} at ${job.grade.professionalTitle} level on ${job.track.name} track`,
|
|
146
|
+
href: `/job/${job.discipline.id}/${job.track.id}/${job.grade.id}`,
|
|
147
|
+
badges: [createBadge(job.grade.id, "default")],
|
|
148
|
+
meta: [createBadge(job.track.name, "secondary")],
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Format capability for display
|
|
154
|
+
* @param {string} capability
|
|
155
|
+
* @param {Array} capabilities
|
|
156
|
+
* @returns {string}
|
|
157
|
+
*/
|
|
158
|
+
function formatCapability(capability, capabilities) {
|
|
159
|
+
const capabilityLabels = {
|
|
160
|
+
delivery: "Delivery",
|
|
161
|
+
scale: "Scale",
|
|
162
|
+
reliability: "Reliability",
|
|
163
|
+
data: "Data",
|
|
164
|
+
ai: "AI",
|
|
165
|
+
process: "Process",
|
|
166
|
+
business: "Business",
|
|
167
|
+
people: "People",
|
|
168
|
+
documentation: "Documentation",
|
|
169
|
+
};
|
|
170
|
+
const label = capabilityLabels[capability] || formatLevel(capability);
|
|
171
|
+
const emoji = getCapabilityEmoji(capabilities, capability);
|
|
172
|
+
return `${emoji} ${label}`;
|
|
173
|
+
}
|