@forwardimpact/pathway 0.21.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.
- package/README.md +3 -3
- package/bin/fit-pathway.js +22 -22
- package/package.json +3 -3
- package/src/commands/agent.js +7 -7
- package/src/commands/index.js +1 -1
- package/src/commands/init.js +1 -1
- package/src/commands/interview.js +8 -8
- package/src/commands/job.js +19 -19
- package/src/commands/level.js +60 -0
- package/src/commands/progress.js +20 -20
- package/src/commands/questions.js +3 -3
- package/src/components/action-buttons.js +3 -3
- package/src/components/builder.js +25 -25
- package/src/components/comparison-radar.js +3 -3
- package/src/components/detail.js +2 -2
- package/src/components/grid.js +1 -1
- package/src/components/radar-chart.js +3 -3
- package/src/components/skill-matrix.js +7 -7
- package/src/css/pages/landing.css +5 -5
- package/src/formatters/index.js +5 -5
- package/src/formatters/interview/shared.js +20 -20
- package/src/formatters/job/description.js +17 -17
- package/src/formatters/job/dom.js +12 -12
- package/src/formatters/job/markdown.js +7 -7
- package/src/formatters/json-ld.js +24 -24
- package/src/formatters/{grade → level}/dom.js +31 -27
- package/src/formatters/{grade → level}/markdown.js +19 -28
- package/src/formatters/{grade → level}/microdata.js +28 -38
- package/src/formatters/level/shared.js +86 -0
- package/src/formatters/progress/markdown.js +2 -2
- package/src/formatters/progress/shared.js +48 -48
- package/src/formatters/questions/markdown.js +8 -6
- package/src/formatters/questions/shared.js +7 -7
- package/src/formatters/skill/dom.js +4 -4
- package/src/formatters/skill/markdown.js +1 -1
- package/src/formatters/skill/microdata.js +3 -3
- package/src/formatters/skill/shared.js +2 -2
- package/src/handout-main.js +12 -12
- package/src/index.html +1 -1
- package/src/lib/card-mappers.js +16 -16
- package/src/lib/cli-command.js +3 -3
- package/src/lib/cli-output.js +2 -2
- package/src/lib/job-cache.js +11 -11
- package/src/lib/render.js +5 -5
- package/src/lib/state.js +2 -2
- package/src/lib/yaml-loader.js +9 -9
- package/src/main.js +10 -10
- package/src/pages/agent-builder.js +11 -11
- package/src/pages/assessment-results.js +27 -23
- package/src/pages/interview-builder.js +6 -6
- package/src/pages/interview.js +8 -8
- package/src/pages/job-builder.js +6 -6
- package/src/pages/job.js +7 -7
- package/src/pages/landing.js +8 -8
- package/src/pages/level.js +122 -0
- package/src/pages/progress-builder.js +8 -8
- package/src/pages/progress.js +74 -74
- package/src/pages/self-assessment.js +7 -7
- package/src/slide-main.js +22 -22
- package/src/slides/chapter.js +4 -4
- package/src/slides/index.js +10 -10
- package/src/slides/interview.js +2 -2
- package/src/slides/job.js +3 -3
- package/src/slides/level.js +32 -0
- package/src/slides/overview.js +9 -9
- package/src/slides/progress.js +13 -13
- package/src/types.js +1 -1
- package/templates/job.template.md +2 -2
- package/src/commands/grade.js +0 -60
- package/src/formatters/grade/shared.js +0 -86
- package/src/pages/grade.js +0 -122
- package/src/slides/grade.js +0 -32
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Levels pages
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { render, div, h1, h3, p, formatLevel } from "../lib/render.js";
|
|
6
|
+
import { getState } from "../lib/state.js";
|
|
7
|
+
import { createBadge } from "../components/card.js";
|
|
8
|
+
import { renderNotFound } from "../components/error-page.js";
|
|
9
|
+
import { prepareLevelsList } from "../formatters/level/shared.js";
|
|
10
|
+
import { levelToDOM } from "../formatters/level/dom.js";
|
|
11
|
+
import { getConceptEmoji } from "@forwardimpact/map/levels";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Render levels list page
|
|
15
|
+
*/
|
|
16
|
+
export function renderLevelsList() {
|
|
17
|
+
const { data } = getState();
|
|
18
|
+
const { framework } = data;
|
|
19
|
+
const levelEmoji = getConceptEmoji(framework, "level");
|
|
20
|
+
|
|
21
|
+
// Transform data for list view
|
|
22
|
+
const { items } = prepareLevelsList(data.levels);
|
|
23
|
+
|
|
24
|
+
const page = div(
|
|
25
|
+
{ className: "levels-page" },
|
|
26
|
+
// Header
|
|
27
|
+
div(
|
|
28
|
+
{ className: "page-header" },
|
|
29
|
+
h1(
|
|
30
|
+
{ className: "page-title" },
|
|
31
|
+
`${levelEmoji} ${framework.entityDefinitions.level.title}`,
|
|
32
|
+
),
|
|
33
|
+
p(
|
|
34
|
+
{ className: "page-description" },
|
|
35
|
+
framework.entityDefinitions.level.description.trim(),
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
|
|
39
|
+
// Levels timeline
|
|
40
|
+
div(
|
|
41
|
+
{ className: "levels-timeline" },
|
|
42
|
+
...items.map((level) => createLevelTimelineItem(level)),
|
|
43
|
+
),
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
render(page);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Create a level timeline item
|
|
51
|
+
* @param {Object} level
|
|
52
|
+
* @returns {HTMLElement}
|
|
53
|
+
*/
|
|
54
|
+
function createLevelTimelineItem(level) {
|
|
55
|
+
const item = div(
|
|
56
|
+
{ className: "level-timeline-item" },
|
|
57
|
+
div({ className: "level-level-marker" }, String(level.ordinalRank)),
|
|
58
|
+
div(
|
|
59
|
+
{ className: "level-timeline-content card card-clickable" },
|
|
60
|
+
div(
|
|
61
|
+
{ className: "card-header" },
|
|
62
|
+
h3({ className: "card-title" }, level.displayName),
|
|
63
|
+
createBadge(level.id, "default"),
|
|
64
|
+
),
|
|
65
|
+
level.typicalExperienceRange
|
|
66
|
+
? p(
|
|
67
|
+
{ className: "text-muted", style: "margin: 0.25rem 0" },
|
|
68
|
+
`${level.typicalExperienceRange} experience`,
|
|
69
|
+
)
|
|
70
|
+
: null,
|
|
71
|
+
div(
|
|
72
|
+
{ className: "card-meta", style: "margin-top: 0.5rem" },
|
|
73
|
+
createBadge(
|
|
74
|
+
`Primary: ${formatLevel(level.baseSkillProficiencies?.primary)}`,
|
|
75
|
+
"primary",
|
|
76
|
+
),
|
|
77
|
+
createBadge(
|
|
78
|
+
`Secondary: ${formatLevel(level.baseSkillProficiencies?.secondary)}`,
|
|
79
|
+
"secondary",
|
|
80
|
+
),
|
|
81
|
+
createBadge(
|
|
82
|
+
`Broad: ${formatLevel(level.baseSkillProficiencies?.broad)}`,
|
|
83
|
+
"broad",
|
|
84
|
+
),
|
|
85
|
+
),
|
|
86
|
+
level.scope
|
|
87
|
+
? p(
|
|
88
|
+
{ className: "card-description", style: "margin-top: 0.75rem" },
|
|
89
|
+
`Scope: ${level.scope}`,
|
|
90
|
+
)
|
|
91
|
+
: null,
|
|
92
|
+
),
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
item.querySelector(".card").addEventListener("click", () => {
|
|
96
|
+
window.location.hash = `/level/${level.id}`;
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return item;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Render level detail page
|
|
104
|
+
* @param {Object} params - Route params
|
|
105
|
+
*/
|
|
106
|
+
export function renderLevelDetail(params) {
|
|
107
|
+
const { data } = getState();
|
|
108
|
+
const level = data.levels.find((g) => g.id === params.id);
|
|
109
|
+
|
|
110
|
+
if (!level) {
|
|
111
|
+
renderNotFound({
|
|
112
|
+
entityType: "Level",
|
|
113
|
+
entityId: params.id,
|
|
114
|
+
backPath: "/level",
|
|
115
|
+
backText: "← Back to Levels",
|
|
116
|
+
});
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Use DOM formatter - it handles transformation internally
|
|
121
|
+
render(levelToDOM(level, { framework: data.framework }));
|
|
122
|
+
}
|
|
@@ -18,7 +18,7 @@ export function renderCareerProgress() {
|
|
|
18
18
|
title: "Career Progress",
|
|
19
19
|
description:
|
|
20
20
|
"Select your current role to visualize career progression. See what skills and behaviours " +
|
|
21
|
-
"change as you advance to the next
|
|
21
|
+
"change as you advance to the next level, or compare expectations across different tracks.",
|
|
22
22
|
formTitle: "Select Your Current Role",
|
|
23
23
|
emptyPreviewText:
|
|
24
24
|
"Select all three components to preview progression paths.",
|
|
@@ -26,25 +26,25 @@ export function renderCareerProgress() {
|
|
|
26
26
|
previewPresenter: (selection) =>
|
|
27
27
|
prepareCareerProgressPreview({
|
|
28
28
|
...selection,
|
|
29
|
-
|
|
29
|
+
levels: data.levels,
|
|
30
30
|
tracks: data.tracks,
|
|
31
31
|
}),
|
|
32
32
|
detailPath: (sel) =>
|
|
33
33
|
sel.track
|
|
34
|
-
? `/progress/${sel.discipline}/${sel.
|
|
35
|
-
: `/progress/${sel.discipline}/${sel.
|
|
34
|
+
? `/progress/${sel.discipline}/${sel.level}/${sel.track}`
|
|
35
|
+
: `/progress/${sel.discipline}/${sel.level}`,
|
|
36
36
|
renderPreview: createProgressPreview,
|
|
37
37
|
labels: {
|
|
38
|
-
|
|
38
|
+
level: "Current Level",
|
|
39
39
|
},
|
|
40
40
|
helpItems: [
|
|
41
41
|
{
|
|
42
|
-
label: "📈
|
|
43
|
-
text: "See exactly which skills and behaviours need to grow to advance to the next
|
|
42
|
+
label: "📈 Level Progression",
|
|
43
|
+
text: "See exactly which skills and behaviours need to grow to advance to the next level rank.",
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
label: "🔀 Track Comparison",
|
|
47
|
-
text: "Compare how expectations differ across tracks at the same
|
|
47
|
+
text: "Compare how expectations differ across tracks at the same level - useful for exploring lateral moves.",
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
label: "🎯 Development Focus",
|
package/src/pages/progress.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Career progress detail page
|
|
3
|
-
* Shows skill and behaviour progression comparison across discipline ×
|
|
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
|
-
|
|
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,
|
|
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
|
|
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 || !
|
|
40
|
+
if (!discipline || !level) {
|
|
41
41
|
renderError({
|
|
42
42
|
title: "Role Not Found",
|
|
43
|
-
message: "Invalid role combination. Discipline or
|
|
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
|
-
|
|
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
|
|
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
|
|
81
|
-
const
|
|
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: `#/
|
|
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
|
-
|
|
126
|
+
currentLevel: level,
|
|
127
127
|
currentTrack: track,
|
|
128
128
|
currentJobView,
|
|
129
|
-
|
|
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}/${
|
|
140
|
-
: `#/job/${disciplineId}/${
|
|
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
|
|
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.
|
|
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.
|
|
160
|
-
* @param {Object} params.data - Full data object with disciplines,
|
|
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
|
-
|
|
165
|
+
currentLevel,
|
|
166
166
|
currentTrack,
|
|
167
167
|
currentJobView,
|
|
168
|
-
|
|
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
|
|
177
|
+
// State to track current selections - default to same discipline, same track, next level
|
|
178
178
|
let selectedDisciplineId = discipline.id;
|
|
179
|
-
let
|
|
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 {
|
|
186
|
+
return { levels: [], tracks: [], allowsTrackless: false };
|
|
187
187
|
|
|
188
|
-
const
|
|
188
|
+
const validLevels = [];
|
|
189
189
|
const validTracks = new Set();
|
|
190
190
|
let allowsTrackless = false;
|
|
191
191
|
|
|
192
|
-
for (const
|
|
192
|
+
for (const level of data.levels) {
|
|
193
193
|
// Check trackless combination
|
|
194
194
|
if (
|
|
195
|
-
isValidCombination({ discipline: selectedDisc,
|
|
195
|
+
isValidCombination({ discipline: selectedDisc, level, track: null })
|
|
196
196
|
) {
|
|
197
|
-
if (!
|
|
198
|
-
|
|
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,
|
|
205
|
-
if (!
|
|
206
|
-
|
|
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
|
-
|
|
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
|
|
230
|
-
if (!selectedDisciplineId || !
|
|
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
|
|
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
|
|
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 || !
|
|
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
|
-
|
|
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
|
-
|
|
277
|
+
currentLevel,
|
|
278
278
|
currentTrack,
|
|
279
279
|
targetDiscipline,
|
|
280
|
-
|
|
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 (${
|
|
345
|
-
targetLabel: `Target (${
|
|
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 (${
|
|
356
|
-
targetLabel: `Target (${
|
|
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}/${
|
|
383
|
-
: `#/job/${targetDiscipline.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 ${
|
|
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
|
|
398
|
+
let levelSelectEl = null;
|
|
399
399
|
let trackSelectEl = null;
|
|
400
400
|
|
|
401
401
|
/**
|
|
402
|
-
* Update
|
|
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
|
|
408
|
-
if (
|
|
409
|
-
|
|
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
|
|
413
|
-
|
|
412
|
+
placeholderOpt.textContent = "Select level...";
|
|
413
|
+
levelSelectEl.appendChild(placeholderOpt);
|
|
414
414
|
|
|
415
|
-
for (const
|
|
415
|
+
for (const level of availableOptions.levels) {
|
|
416
416
|
const opt = document.createElement("option");
|
|
417
|
-
opt.value =
|
|
418
|
-
opt.textContent =
|
|
419
|
-
|
|
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.
|
|
424
|
-
|
|
423
|
+
if (availableOptions.levels.find((g) => g.id === selectedLevelId)) {
|
|
424
|
+
levelSelectEl.value = selectedLevelId;
|
|
425
425
|
} else {
|
|
426
|
-
|
|
427
|
-
|
|
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
|
|
475
|
-
|
|
476
|
-
id: "compare-
|
|
477
|
-
items: availableOptions.
|
|
478
|
-
initialValue:
|
|
479
|
-
placeholder: "Select
|
|
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
|
-
|
|
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 (
|
|
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,
|
|
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-
|
|
536
|
-
|
|
535
|
+
label({ for: "compare-level-select" }, "Target Level"),
|
|
536
|
+
levelSelectEl,
|
|
537
537
|
),
|
|
538
538
|
div(
|
|
539
539
|
{ className: "form-group" },
|
|
@@ -19,7 +19,7 @@ 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
|
-
|
|
22
|
+
SKILL_PROFICIENCY_ORDER,
|
|
23
23
|
BEHAVIOUR_MATURITY_ORDER,
|
|
24
24
|
groupSkillsByCapability,
|
|
25
25
|
getCapabilityOrder,
|
|
@@ -107,7 +107,7 @@ function getWizardSteps(data) {
|
|
|
107
107
|
steps.push({
|
|
108
108
|
id: "results",
|
|
109
109
|
name: "Results",
|
|
110
|
-
icon: getConceptEmoji(framework, "
|
|
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
|
|
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
|
-
...
|
|
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
|
|
507
|
-
type === "skill" ? item.
|
|
508
|
-
const description =
|
|
506
|
+
const proficiencyDescriptions =
|
|
507
|
+
type === "skill" ? item.proficiencyDescriptions : item.maturityDescriptions;
|
|
508
|
+
const description = proficiencyDescriptions?.[level] || "";
|
|
509
509
|
|
|
510
510
|
return button(
|
|
511
511
|
{
|