@forwardimpact/pathway 0.25.15 → 0.25.21
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/bin/fit-pathway.js +62 -54
- package/package.json +1 -3
- package/src/commands/agent-io.js +120 -0
- package/src/commands/agent.js +266 -349
- package/src/commands/init.js +2 -2
- package/src/commands/job.js +237 -183
- package/src/components/comparison-radar.js +118 -103
- package/src/components/progression-table.js +244 -208
- package/src/formatters/index.js +0 -19
- package/src/formatters/interview/markdown.js +100 -88
- package/src/formatters/job/description.js +76 -75
- package/src/formatters/job/dom.js +113 -97
- package/src/formatters/level/dom.js +87 -102
- package/src/formatters/questions/markdown.js +37 -33
- package/src/formatters/questions/shared.js +142 -75
- package/src/formatters/skill/dom.js +102 -93
- package/src/lib/comparison-radar-chart.js +256 -0
- package/src/lib/radar-utils.js +199 -0
- package/src/lib/radar.js +25 -662
- package/src/pages/agent-builder-download.js +170 -0
- package/src/pages/agent-builder-preview.js +344 -0
- package/src/pages/agent-builder.js +6 -550
- package/src/pages/progress-comparison.js +110 -0
- package/src/pages/progress.js +11 -111
- package/src/pages/self-assessment-steps.js +494 -0
- package/src/pages/self-assessment.js +54 -504
- package/src/formatters/behaviour/microdata.js +0 -106
- package/src/formatters/discipline/microdata.js +0 -117
- package/src/formatters/driver/microdata.js +0 -91
- package/src/formatters/level/microdata.js +0 -141
- package/src/formatters/microdata-shared.js +0 -184
- package/src/formatters/skill/microdata.js +0 -151
- package/src/formatters/stage/microdata.js +0 -116
- package/src/formatters/track/microdata.js +0 -111
|
@@ -5,6 +5,91 @@
|
|
|
5
5
|
import { formatLevel } from "../../lib/render.js";
|
|
6
6
|
import { getConceptEmoji } from "@forwardimpact/map/levels";
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Append follow-ups to lines
|
|
10
|
+
* @param {string[]} lines
|
|
11
|
+
* @param {Object} q - Question object
|
|
12
|
+
*/
|
|
13
|
+
function appendFollowUps(lines, q) {
|
|
14
|
+
if (q.followUps.length > 0) {
|
|
15
|
+
lines.push("", "**Follow-ups:**");
|
|
16
|
+
for (const followUp of q.followUps) {
|
|
17
|
+
lines.push(` → ${followUp}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Append looking-for items to lines
|
|
24
|
+
* @param {string[]} lines
|
|
25
|
+
* @param {Object} q - Question object
|
|
26
|
+
*/
|
|
27
|
+
function appendLookingFor(lines, q) {
|
|
28
|
+
if (q.lookingFor && q.lookingFor.length > 0) {
|
|
29
|
+
lines.push("", "**What to look for:**");
|
|
30
|
+
for (const item of q.lookingFor) {
|
|
31
|
+
lines.push(`- ${item}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Format skill question sections
|
|
38
|
+
* @param {string[]} lines
|
|
39
|
+
* @param {Array} sections
|
|
40
|
+
* @param {string} emoji
|
|
41
|
+
*/
|
|
42
|
+
function formatSkillSections(lines, sections, emoji) {
|
|
43
|
+
if (sections.length === 0) return;
|
|
44
|
+
lines.push(`## ${emoji} Skill Questions`, "");
|
|
45
|
+
for (const section of sections) {
|
|
46
|
+
lines.push(`### ${section.name} (${formatLevel(section.level)})`, "");
|
|
47
|
+
for (const q of section.questions) {
|
|
48
|
+
lines.push(`**Q**: ${q.question}`);
|
|
49
|
+
appendFollowUps(lines, q);
|
|
50
|
+
appendLookingFor(lines, q);
|
|
51
|
+
lines.push("");
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Format scenario-based question sections (capability or behaviour)
|
|
58
|
+
* @param {string[]} lines
|
|
59
|
+
* @param {Array} sections
|
|
60
|
+
* @param {string} heading
|
|
61
|
+
* @param {string} promptsKey - Key for guided prompts
|
|
62
|
+
* @param {string} promptsLabel - Display label for prompts
|
|
63
|
+
*/
|
|
64
|
+
function formatScenarioSections(
|
|
65
|
+
lines,
|
|
66
|
+
sections,
|
|
67
|
+
heading,
|
|
68
|
+
promptsKey,
|
|
69
|
+
promptsLabel,
|
|
70
|
+
) {
|
|
71
|
+
if (sections.length === 0) return;
|
|
72
|
+
lines.push(`## ${heading}`, "");
|
|
73
|
+
for (const section of sections) {
|
|
74
|
+
lines.push(`### ${section.name} (${formatLevel(section.level)})`, "");
|
|
75
|
+
for (const q of section.questions) {
|
|
76
|
+
lines.push(`**Scenario**: ${q.question}`);
|
|
77
|
+
if (q.context) {
|
|
78
|
+
lines.push(`> ${q.context}`);
|
|
79
|
+
}
|
|
80
|
+
if (q[promptsKey] && q[promptsKey].length > 0) {
|
|
81
|
+
lines.push("", `**${promptsLabel}:**`);
|
|
82
|
+
for (const prompt of q[promptsKey]) {
|
|
83
|
+
lines.push(`- ${prompt}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
appendFollowUps(lines, q);
|
|
87
|
+
appendLookingFor(lines, q);
|
|
88
|
+
lines.push("");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
8
93
|
/**
|
|
9
94
|
* Format interview detail as markdown
|
|
10
95
|
* @param {Object} view - Interview detail view from presenter
|
|
@@ -24,102 +109,29 @@ export function interviewToMarkdown(view, { framework } = {}) {
|
|
|
24
109
|
"",
|
|
25
110
|
];
|
|
26
111
|
|
|
27
|
-
// Group sections by type
|
|
28
112
|
const skillSections = view.sections.filter((s) => s.type === "skill");
|
|
29
113
|
const behaviourSections = view.sections.filter((s) => s.type === "behaviour");
|
|
30
114
|
const capabilitySections = view.sections.filter(
|
|
31
115
|
(s) => s.type === "capability",
|
|
32
116
|
);
|
|
33
117
|
|
|
34
|
-
|
|
35
|
-
if (skillSections.length > 0) {
|
|
36
|
-
lines.push(`## ${skillEmoji} Skill Questions`, "");
|
|
37
|
-
for (const section of skillSections) {
|
|
38
|
-
lines.push(`### ${section.name} (${formatLevel(section.level)})`, "");
|
|
39
|
-
for (const q of section.questions) {
|
|
40
|
-
lines.push(`**Q**: ${q.question}`);
|
|
41
|
-
if (q.followUps.length > 0) {
|
|
42
|
-
lines.push("", "**Follow-ups:**");
|
|
43
|
-
for (const followUp of q.followUps) {
|
|
44
|
-
lines.push(` → ${followUp}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
if (q.lookingFor && q.lookingFor.length > 0) {
|
|
48
|
-
lines.push("", "**What to look for:**");
|
|
49
|
-
for (const item of q.lookingFor) {
|
|
50
|
-
lines.push(`- ${item}`);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
lines.push("");
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
118
|
+
formatSkillSections(lines, skillSections, skillEmoji);
|
|
57
119
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (q.context) {
|
|
66
|
-
lines.push(`> ${q.context}`);
|
|
67
|
-
}
|
|
68
|
-
if (q.decompositionPrompts && q.decompositionPrompts.length > 0) {
|
|
69
|
-
lines.push("", "**Guide the candidate through:**");
|
|
70
|
-
for (const prompt of q.decompositionPrompts) {
|
|
71
|
-
lines.push(`- ${prompt}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (q.followUps.length > 0) {
|
|
75
|
-
lines.push("", "**Follow-ups:**");
|
|
76
|
-
for (const followUp of q.followUps) {
|
|
77
|
-
lines.push(` → ${followUp}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (q.lookingFor && q.lookingFor.length > 0) {
|
|
81
|
-
lines.push("", "**What to look for:**");
|
|
82
|
-
for (const item of q.lookingFor) {
|
|
83
|
-
lines.push(`- ${item}`);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
lines.push("");
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
120
|
+
formatScenarioSections(
|
|
121
|
+
lines,
|
|
122
|
+
capabilitySections,
|
|
123
|
+
"🧩 Decomposition Questions",
|
|
124
|
+
"decompositionPrompts",
|
|
125
|
+
"Guide the candidate through",
|
|
126
|
+
);
|
|
90
127
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (q.context) {
|
|
99
|
-
lines.push(`> ${q.context}`);
|
|
100
|
-
}
|
|
101
|
-
if (q.simulationPrompts && q.simulationPrompts.length > 0) {
|
|
102
|
-
lines.push("", "**Steer the simulation:**");
|
|
103
|
-
for (const prompt of q.simulationPrompts) {
|
|
104
|
-
lines.push(`- ${prompt}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (q.followUps.length > 0) {
|
|
108
|
-
lines.push("", "**Follow-ups:**");
|
|
109
|
-
for (const followUp of q.followUps) {
|
|
110
|
-
lines.push(` → ${followUp}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (q.lookingFor && q.lookingFor.length > 0) {
|
|
114
|
-
lines.push("", "**What to look for:**");
|
|
115
|
-
for (const item of q.lookingFor) {
|
|
116
|
-
lines.push(`- ${item}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
lines.push("");
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
128
|
+
formatScenarioSections(
|
|
129
|
+
lines,
|
|
130
|
+
behaviourSections,
|
|
131
|
+
`${behaviourEmoji} Stakeholder Simulation`,
|
|
132
|
+
"simulationPrompts",
|
|
133
|
+
"Steer the simulation",
|
|
134
|
+
);
|
|
123
135
|
|
|
124
136
|
return lines.join("\n");
|
|
125
137
|
}
|
|
@@ -13,6 +13,80 @@ import Mustache from "mustache";
|
|
|
13
13
|
import { BEHAVIOUR_MATURITY_ORDER } from "@forwardimpact/map/levels";
|
|
14
14
|
import { trimValue, trimFields } from "../shared.js";
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Build expectations paragraph from job expectations
|
|
18
|
+
* @param {Object|undefined} expectations
|
|
19
|
+
* @returns {string}
|
|
20
|
+
*/
|
|
21
|
+
function buildExpectationsParagraph(expectations) {
|
|
22
|
+
if (!expectations) return "";
|
|
23
|
+
const exp = expectations;
|
|
24
|
+
const sentences = [];
|
|
25
|
+
|
|
26
|
+
if (exp.impactScope) {
|
|
27
|
+
sentences.push(`This role encompasses ${exp.impactScope.toLowerCase()}.`);
|
|
28
|
+
}
|
|
29
|
+
if (exp.autonomyExpectation) {
|
|
30
|
+
let autonomySentence = `You will ${exp.autonomyExpectation.toLowerCase()}`;
|
|
31
|
+
if (exp.influenceScope) {
|
|
32
|
+
autonomySentence +=
|
|
33
|
+
`, ${exp.influenceScope.toLowerCase()}` +
|
|
34
|
+
(exp.influenceScope.endsWith(".") ? "" : ".");
|
|
35
|
+
} else {
|
|
36
|
+
autonomySentence += exp.autonomyExpectation.endsWith(".") ? "" : ".";
|
|
37
|
+
}
|
|
38
|
+
sentences.push(autonomySentence);
|
|
39
|
+
} else if (exp.influenceScope) {
|
|
40
|
+
sentences.push(
|
|
41
|
+
exp.influenceScope + (exp.influenceScope.endsWith(".") ? "" : "."),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (exp.complexityHandled) {
|
|
45
|
+
sentences.push(`You will handle ${exp.complexityHandled.toLowerCase()}.`);
|
|
46
|
+
}
|
|
47
|
+
return sentences.length > 0 ? sentences.join(" ") : "";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Build capability skill sections at the highest proficiency
|
|
52
|
+
* @param {Object} job
|
|
53
|
+
* @returns {Array}
|
|
54
|
+
*/
|
|
55
|
+
function buildCapabilitySkills(job) {
|
|
56
|
+
const derivedResponsibilities = job.derivedResponsibilities || [];
|
|
57
|
+
if (derivedResponsibilities.length === 0) return [];
|
|
58
|
+
|
|
59
|
+
const highestProficiency = derivedResponsibilities[0].proficiency;
|
|
60
|
+
const topResponsibilities = derivedResponsibilities.filter(
|
|
61
|
+
(r) => r.proficiency === highestProficiency,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const skillsByCapability = {};
|
|
65
|
+
for (const skill of job.skillMatrix) {
|
|
66
|
+
if (skill.proficiency !== highestProficiency) continue;
|
|
67
|
+
if (!skillsByCapability[skill.capability]) {
|
|
68
|
+
skillsByCapability[skill.capability] = [];
|
|
69
|
+
}
|
|
70
|
+
skillsByCapability[skill.capability].push(skill);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return topResponsibilities
|
|
74
|
+
.filter((r) => skillsByCapability[r.capability]?.length > 0)
|
|
75
|
+
.map((r) => {
|
|
76
|
+
const skills = [...skillsByCapability[r.capability]].sort((a, b) =>
|
|
77
|
+
(a.skillName || "").localeCompare(b.skillName || ""),
|
|
78
|
+
);
|
|
79
|
+
return {
|
|
80
|
+
capabilityHeading: r.capabilityName.toUpperCase(),
|
|
81
|
+
responsibilityDescription: r.responsibility,
|
|
82
|
+
skills: skills.map((s) => ({
|
|
83
|
+
skillName: s.skillName,
|
|
84
|
+
proficiencyDescription: s.proficiencyDescription || "",
|
|
85
|
+
})),
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
16
90
|
/**
|
|
17
91
|
* Prepare job data for template rendering
|
|
18
92
|
* @param {Object} params
|
|
@@ -29,42 +103,7 @@ function prepareJobDescriptionData({ job, discipline, level, track }) {
|
|
|
29
103
|
roleSummary = roleSummary.replace(/\{roleTitle\}/g, roleTitle);
|
|
30
104
|
roleSummary = roleSummary.replace(/\{specialization\}/g, specialization);
|
|
31
105
|
|
|
32
|
-
|
|
33
|
-
let expectationsParagraph = "";
|
|
34
|
-
if (job.expectations) {
|
|
35
|
-
const exp = job.expectations;
|
|
36
|
-
const expectationSentences = [];
|
|
37
|
-
|
|
38
|
-
if (exp.impactScope) {
|
|
39
|
-
expectationSentences.push(
|
|
40
|
-
`This role encompasses ${exp.impactScope.toLowerCase()}.`,
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
if (exp.autonomyExpectation) {
|
|
44
|
-
let autonomySentence = `You will ${exp.autonomyExpectation.toLowerCase()}`;
|
|
45
|
-
if (exp.influenceScope) {
|
|
46
|
-
autonomySentence +=
|
|
47
|
-
`, ${exp.influenceScope.toLowerCase()}` +
|
|
48
|
-
(exp.influenceScope.endsWith(".") ? "" : ".");
|
|
49
|
-
} else {
|
|
50
|
-
autonomySentence += exp.autonomyExpectation.endsWith(".") ? "" : ".";
|
|
51
|
-
}
|
|
52
|
-
expectationSentences.push(autonomySentence);
|
|
53
|
-
} else if (exp.influenceScope) {
|
|
54
|
-
expectationSentences.push(
|
|
55
|
-
exp.influenceScope + (exp.influenceScope.endsWith(".") ? "" : "."),
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
if (exp.complexityHandled) {
|
|
59
|
-
expectationSentences.push(
|
|
60
|
-
`You will handle ${exp.complexityHandled.toLowerCase()}.`,
|
|
61
|
-
);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (expectationSentences.length > 0) {
|
|
65
|
-
expectationsParagraph = expectationSentences.join(" ");
|
|
66
|
-
}
|
|
67
|
-
}
|
|
106
|
+
const expectationsParagraph = buildExpectationsParagraph(job.expectations);
|
|
68
107
|
|
|
69
108
|
// Sort behaviours by maturity level (highest first)
|
|
70
109
|
const sortedBehaviours = [...job.behaviourProfile].sort((a, b) => {
|
|
@@ -76,45 +115,7 @@ function prepareJobDescriptionData({ job, discipline, level, track }) {
|
|
|
76
115
|
return indexB - indexA;
|
|
77
116
|
});
|
|
78
117
|
|
|
79
|
-
|
|
80
|
-
let capabilitySkills = [];
|
|
81
|
-
const derivedResponsibilities = job.derivedResponsibilities || [];
|
|
82
|
-
if (derivedResponsibilities.length > 0) {
|
|
83
|
-
// derivedResponsibilities is sorted: highest proficiency first, then by skill count
|
|
84
|
-
const highestProficiency = derivedResponsibilities[0].proficiency;
|
|
85
|
-
|
|
86
|
-
// Filter responsibilities to only the highest proficiency
|
|
87
|
-
const topResponsibilities = derivedResponsibilities.filter(
|
|
88
|
-
(r) => r.proficiency === highestProficiency,
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
// Group skill matrix entries by capability at the highest level
|
|
92
|
-
const skillsByCapability = {};
|
|
93
|
-
for (const skill of job.skillMatrix) {
|
|
94
|
-
if (skill.proficiency !== highestProficiency) continue;
|
|
95
|
-
if (!skillsByCapability[skill.capability]) {
|
|
96
|
-
skillsByCapability[skill.capability] = [];
|
|
97
|
-
}
|
|
98
|
-
skillsByCapability[skill.capability].push(skill);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Build capability sections in skill count order
|
|
102
|
-
capabilitySkills = topResponsibilities
|
|
103
|
-
.filter((r) => skillsByCapability[r.capability]?.length > 0)
|
|
104
|
-
.map((r) => {
|
|
105
|
-
const skills = [...skillsByCapability[r.capability]].sort((a, b) =>
|
|
106
|
-
(a.skillName || "").localeCompare(b.skillName || ""),
|
|
107
|
-
);
|
|
108
|
-
return {
|
|
109
|
-
capabilityHeading: r.capabilityName.toUpperCase(),
|
|
110
|
-
responsibilityDescription: r.responsibility,
|
|
111
|
-
skills: skills.map((s) => ({
|
|
112
|
-
skillName: s.skillName,
|
|
113
|
-
proficiencyDescription: s.proficiencyDescription || "",
|
|
114
|
-
})),
|
|
115
|
-
};
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
+
const capabilitySkills = buildCapabilitySkills(job);
|
|
118
119
|
|
|
119
120
|
// Build qualification summary with placeholder replacement
|
|
120
121
|
const qualificationSummary =
|
|
@@ -33,6 +33,107 @@ import { createToolkitTable } from "../toolkit/dom.js";
|
|
|
33
33
|
* @param {string} [options.jobTemplate] - Mustache template for job description
|
|
34
34
|
* @returns {HTMLElement}
|
|
35
35
|
*/
|
|
36
|
+
/**
|
|
37
|
+
* Build a job data object from a view for template rendering
|
|
38
|
+
* @param {Object} view
|
|
39
|
+
* @returns {Object}
|
|
40
|
+
*/
|
|
41
|
+
function buildJobFromView(view) {
|
|
42
|
+
return {
|
|
43
|
+
title: view.title,
|
|
44
|
+
skillMatrix: view.skillMatrix,
|
|
45
|
+
behaviourProfile: view.behaviourProfile,
|
|
46
|
+
expectations: view.expectations,
|
|
47
|
+
derivedResponsibilities: view.derivedResponsibilities,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Create the tables section for a job detail view
|
|
53
|
+
* @param {Object} view
|
|
54
|
+
* @returns {HTMLElement}
|
|
55
|
+
*/
|
|
56
|
+
function createJobTablesSection(view) {
|
|
57
|
+
return div(
|
|
58
|
+
{ className: "job-tables-section" },
|
|
59
|
+
createDetailSection({
|
|
60
|
+
title: "Behaviour Profile",
|
|
61
|
+
content: createBehaviourProfile(view.behaviourProfile),
|
|
62
|
+
}),
|
|
63
|
+
createDetailSection({
|
|
64
|
+
title: "Skill Matrix",
|
|
65
|
+
content: createSkillMatrix(view.skillMatrix, {
|
|
66
|
+
capabilityOrder: view.capabilityOrder,
|
|
67
|
+
}),
|
|
68
|
+
}),
|
|
69
|
+
view.toolkit && view.toolkit.length > 0
|
|
70
|
+
? createDetailSection({
|
|
71
|
+
title: "Tool Kit",
|
|
72
|
+
content: createToolkitTable(view.toolkit),
|
|
73
|
+
})
|
|
74
|
+
: null,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create the page header with breadcrumb links
|
|
80
|
+
* @param {Object} view
|
|
81
|
+
* @param {boolean} showBackLink
|
|
82
|
+
* @returns {HTMLElement}
|
|
83
|
+
*/
|
|
84
|
+
function createJobHeader(view, showBackLink) {
|
|
85
|
+
return div(
|
|
86
|
+
{ className: "page-header" },
|
|
87
|
+
showBackLink
|
|
88
|
+
? createBackLink("/job-builder", "← Back to Job Builder")
|
|
89
|
+
: null,
|
|
90
|
+
h1({ className: "page-title" }, view.title),
|
|
91
|
+
div(
|
|
92
|
+
{ className: "page-description" },
|
|
93
|
+
"Generated from: ",
|
|
94
|
+
a({ href: `#/discipline/${view.disciplineId}` }, view.disciplineName),
|
|
95
|
+
" × ",
|
|
96
|
+
a({ href: `#/level/${view.levelId}` }, view.levelId),
|
|
97
|
+
" × ",
|
|
98
|
+
a({ href: `#/track/${view.trackId}` }, view.trackName),
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create the expectations section if expectations exist
|
|
105
|
+
* @param {Object} view
|
|
106
|
+
* @returns {HTMLElement|null}
|
|
107
|
+
*/
|
|
108
|
+
function createExpectationsSection(view) {
|
|
109
|
+
if (!view.expectations || Object.keys(view.expectations).length === 0) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return createDetailSection({
|
|
113
|
+
title: "Expectations",
|
|
114
|
+
content: createExpectationsCard(view.expectations),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Create the radar charts section
|
|
120
|
+
* @param {Object} view
|
|
121
|
+
* @returns {HTMLElement}
|
|
122
|
+
*/
|
|
123
|
+
function createRadarSection(view) {
|
|
124
|
+
return div(
|
|
125
|
+
{ className: "section auto-grid-lg" },
|
|
126
|
+
createBehaviourRadar(view.behaviourProfile, {
|
|
127
|
+
title: "Behaviours Radar",
|
|
128
|
+
size: 420,
|
|
129
|
+
}),
|
|
130
|
+
createSkillRadar(view.skillMatrix, {
|
|
131
|
+
title: "Skills Radar",
|
|
132
|
+
size: 420,
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
36
137
|
export function jobToDOM(view, options = {}) {
|
|
37
138
|
const {
|
|
38
139
|
showBackLink = true,
|
|
@@ -46,107 +147,22 @@ export function jobToDOM(view, options = {}) {
|
|
|
46
147
|
} = options;
|
|
47
148
|
|
|
48
149
|
const hasEntities = discipline && level && jobTemplate;
|
|
150
|
+
const job = hasEntities ? buildJobFromView(view) : null;
|
|
151
|
+
const descParams = hasEntities
|
|
152
|
+
? { job, discipline, level, track, template: jobTemplate }
|
|
153
|
+
: null;
|
|
49
154
|
|
|
50
155
|
return div(
|
|
51
156
|
{ className: "job-detail-page" },
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
: null,
|
|
58
|
-
h1({ className: "page-title" }, view.title),
|
|
59
|
-
div(
|
|
60
|
-
{ className: "page-description" },
|
|
61
|
-
"Generated from: ",
|
|
62
|
-
a({ href: `#/discipline/${view.disciplineId}` }, view.disciplineName),
|
|
63
|
-
" × ",
|
|
64
|
-
a({ href: `#/level/${view.levelId}` }, view.levelId),
|
|
65
|
-
" × ",
|
|
66
|
-
a({ href: `#/track/${view.trackId}` }, view.trackName),
|
|
67
|
-
),
|
|
68
|
-
),
|
|
69
|
-
|
|
70
|
-
// Expectations card
|
|
71
|
-
view.expectations && Object.keys(view.expectations).length > 0
|
|
72
|
-
? createDetailSection({
|
|
73
|
-
title: "Expectations",
|
|
74
|
-
content: createExpectationsCard(view.expectations),
|
|
75
|
-
})
|
|
157
|
+
createJobHeader(view, showBackLink),
|
|
158
|
+
createExpectationsSection(view),
|
|
159
|
+
createRadarSection(view),
|
|
160
|
+
showJobDescriptionHtml && descParams
|
|
161
|
+
? createJobDescriptionHtml(descParams)
|
|
76
162
|
: null,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
{ className: "section auto-grid-lg" },
|
|
81
|
-
createBehaviourRadar(view.behaviourProfile, {
|
|
82
|
-
title: "Behaviours Radar",
|
|
83
|
-
size: 420,
|
|
84
|
-
}),
|
|
85
|
-
createSkillRadar(view.skillMatrix, {
|
|
86
|
-
title: "Skills Radar",
|
|
87
|
-
size: 420,
|
|
88
|
-
}),
|
|
89
|
-
),
|
|
90
|
-
|
|
91
|
-
// Job Description HTML (for print view)
|
|
92
|
-
showJobDescriptionHtml && hasEntities
|
|
93
|
-
? createJobDescriptionHtml({
|
|
94
|
-
job: {
|
|
95
|
-
title: view.title,
|
|
96
|
-
skillMatrix: view.skillMatrix,
|
|
97
|
-
behaviourProfile: view.behaviourProfile,
|
|
98
|
-
expectations: view.expectations,
|
|
99
|
-
derivedResponsibilities: view.derivedResponsibilities,
|
|
100
|
-
},
|
|
101
|
-
discipline,
|
|
102
|
-
level,
|
|
103
|
-
track,
|
|
104
|
-
template: jobTemplate,
|
|
105
|
-
})
|
|
106
|
-
: null,
|
|
107
|
-
|
|
108
|
-
// Behaviour profile, Skill matrix, Toolkit, Driver coverage tables
|
|
109
|
-
showTables
|
|
110
|
-
? div(
|
|
111
|
-
{ className: "job-tables-section" },
|
|
112
|
-
// Behaviour profile table
|
|
113
|
-
createDetailSection({
|
|
114
|
-
title: "Behaviour Profile",
|
|
115
|
-
content: createBehaviourProfile(view.behaviourProfile),
|
|
116
|
-
}),
|
|
117
|
-
|
|
118
|
-
createDetailSection({
|
|
119
|
-
title: "Skill Matrix",
|
|
120
|
-
content: createSkillMatrix(view.skillMatrix, {
|
|
121
|
-
capabilityOrder: view.capabilityOrder,
|
|
122
|
-
}),
|
|
123
|
-
}),
|
|
124
|
-
|
|
125
|
-
// Toolkit (after skill matrix)
|
|
126
|
-
view.toolkit && view.toolkit.length > 0
|
|
127
|
-
? createDetailSection({
|
|
128
|
-
title: "Tool Kit",
|
|
129
|
-
content: createToolkitTable(view.toolkit),
|
|
130
|
-
})
|
|
131
|
-
: null,
|
|
132
|
-
)
|
|
133
|
-
: null,
|
|
134
|
-
|
|
135
|
-
// Job Description (copyable markdown)
|
|
136
|
-
showJobDescriptionMarkdown && hasEntities
|
|
137
|
-
? createJobDescriptionSection({
|
|
138
|
-
job: {
|
|
139
|
-
title: view.title,
|
|
140
|
-
skillMatrix: view.skillMatrix,
|
|
141
|
-
behaviourProfile: view.behaviourProfile,
|
|
142
|
-
expectations: view.expectations,
|
|
143
|
-
derivedResponsibilities: view.derivedResponsibilities,
|
|
144
|
-
},
|
|
145
|
-
discipline,
|
|
146
|
-
level,
|
|
147
|
-
track,
|
|
148
|
-
template: jobTemplate,
|
|
149
|
-
})
|
|
163
|
+
showTables ? createJobTablesSection(view) : null,
|
|
164
|
+
showJobDescriptionMarkdown && descParams
|
|
165
|
+
? createJobDescriptionSection(descParams)
|
|
150
166
|
: null,
|
|
151
167
|
);
|
|
152
168
|
}
|