@forwardimpact/pathway 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/bin/fit-pathway.js +28 -28
- package/package.json +4 -4
- package/src/commands/agent.js +8 -8
- package/src/commands/build.js +7 -7
- package/src/commands/dev.js +7 -7
- package/src/commands/driver.js +1 -1
- 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/commands/skill.js +1 -1
- package/src/commands/track.js +1 -1
- package/src/commands/update.js +12 -14
- package/src/components/action-buttons.js +3 -3
- package/src/components/builder.js +25 -25
- package/src/components/checklist.js +1 -1
- package/src/components/comparison-radar.js +3 -3
- package/src/components/detail.js +3 -3
- 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/behaviour/dom.js +1 -1
- package/src/formatters/discipline/dom.js +1 -1
- package/src/formatters/driver/dom.js +1 -1
- package/src/formatters/index.js +5 -5
- package/src/formatters/interview/dom.js +1 -1
- package/src/formatters/interview/markdown.js +1 -1
- package/src/formatters/interview/shared.js +20 -20
- package/src/formatters/job/description.js +18 -18
- 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 +32 -28
- package/src/formatters/{grade → level}/markdown.js +20 -29
- 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 +2 -2
- package/src/formatters/skill/microdata.js +3 -3
- package/src/formatters/skill/shared.js +3 -3
- package/src/formatters/track/dom.js +1 -1
- package/src/formatters/track/markdown.js +1 -1
- package/src/handout-main.js +13 -16
- package/src/handout.html +4 -4
- package/src/index.html +5 -5
- package/src/lib/card-mappers.js +17 -17
- 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 +6 -6
- 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/behaviour.js +1 -1
- package/src/pages/discipline.js +1 -1
- package/src/pages/driver.js +1 -1
- package/src/pages/interview-builder.js +6 -6
- package/src/pages/interview.js +9 -9
- package/src/pages/job-builder.js +6 -6
- package/src/pages/job.js +7 -7
- package/src/pages/landing.js +9 -9
- 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 +8 -8
- package/src/pages/skill.js +1 -4
- package/src/pages/stage.js +1 -1
- package/src/pages/tool.js +1 -1
- package/src/pages/track.js +1 -1
- package/src/slide-main.js +22 -22
- package/src/slides/chapter.js +4 -4
- package/src/slides/index.js +11 -11
- 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/slides.html +4 -4
- package/src/types.js +1 -1
- package/templates/install.template.sh +11 -8
- 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
package/src/slides/interview.js
CHANGED
|
@@ -20,7 +20,7 @@ import { interviewToDOM } from "../formatters/index.js";
|
|
|
20
20
|
*/
|
|
21
21
|
export function renderInterviewSlide({ render, data, params }) {
|
|
22
22
|
const discipline = data.disciplines.find((d) => d.id === params.discipline);
|
|
23
|
-
const
|
|
23
|
+
const level = data.levels.find((g) => g.id === params.level);
|
|
24
24
|
const track = data.tracks.find((t) => t.id === params.track);
|
|
25
25
|
|
|
26
26
|
// Get interview type from URL query or default to full
|
|
@@ -29,7 +29,7 @@ export function renderInterviewSlide({ render, data, params }) {
|
|
|
29
29
|
|
|
30
30
|
const view = prepareInterviewDetail({
|
|
31
31
|
discipline,
|
|
32
|
-
|
|
32
|
+
level,
|
|
33
33
|
track,
|
|
34
34
|
skills: data.skills,
|
|
35
35
|
behaviours: data.behaviours,
|
package/src/slides/job.js
CHANGED
|
@@ -17,12 +17,12 @@ import { jobToDOM } from "../formatters/index.js";
|
|
|
17
17
|
*/
|
|
18
18
|
export function renderJobSlide({ render, data, params }) {
|
|
19
19
|
const discipline = data.disciplines.find((d) => d.id === params.discipline);
|
|
20
|
-
const
|
|
20
|
+
const level = data.levels.find((g) => g.id === params.level);
|
|
21
21
|
const track = data.tracks.find((t) => t.id === params.track);
|
|
22
22
|
|
|
23
23
|
const view = prepareJobDetail({
|
|
24
24
|
discipline,
|
|
25
|
-
|
|
25
|
+
level,
|
|
26
26
|
track,
|
|
27
27
|
skills: data.skills,
|
|
28
28
|
behaviours: data.behaviours,
|
|
@@ -49,7 +49,7 @@ export function renderJobSlide({ render, data, params }) {
|
|
|
49
49
|
showJobDescriptionHtml: true,
|
|
50
50
|
showJobDescriptionMarkdown: false,
|
|
51
51
|
discipline,
|
|
52
|
-
|
|
52
|
+
level,
|
|
53
53
|
track,
|
|
54
54
|
}),
|
|
55
55
|
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Level Slide View
|
|
3
|
+
*
|
|
4
|
+
* Printer-friendly view of a level.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { div, h1, p } from "../lib/render.js";
|
|
8
|
+
import { levelToDOM } from "../formatters/index.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Render level slide
|
|
12
|
+
* @param {Object} params
|
|
13
|
+
* @param {Function} params.render
|
|
14
|
+
* @param {Object} params.data
|
|
15
|
+
* @param {Object} params.params
|
|
16
|
+
*/
|
|
17
|
+
export function renderLevelSlide({ render, data, params }) {
|
|
18
|
+
const level = data.levels.find((g) => g.id === params.id);
|
|
19
|
+
|
|
20
|
+
if (!level) {
|
|
21
|
+
render(
|
|
22
|
+
div(
|
|
23
|
+
{ className: "slide-error" },
|
|
24
|
+
h1({}, "Level Not Found"),
|
|
25
|
+
p({}, `No level found with ID: ${params.id}`),
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render(levelToDOM(level, { framework: data.framework, showBackLink: false }));
|
|
32
|
+
}
|
package/src/slides/overview.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
skillToCardConfig,
|
|
13
13
|
behaviourToCardConfig,
|
|
14
14
|
driverToCardConfig,
|
|
15
|
-
|
|
15
|
+
levelToCardConfig,
|
|
16
16
|
trackToCardConfig,
|
|
17
17
|
jobToCardConfig,
|
|
18
18
|
} from "../lib/card-mappers.js";
|
|
@@ -20,7 +20,7 @@ import { prepareDisciplinesList } from "../formatters/discipline/shared.js";
|
|
|
20
20
|
import { prepareSkillsList } from "../formatters/skill/shared.js";
|
|
21
21
|
import { prepareBehavioursList } from "../formatters/behaviour/shared.js";
|
|
22
22
|
import { prepareDriversList } from "../formatters/driver/shared.js";
|
|
23
|
-
import {
|
|
23
|
+
import { prepareLevelsList } from "../formatters/level/shared.js";
|
|
24
24
|
import { prepareTracksList } from "../formatters/track/shared.js";
|
|
25
25
|
import { generateAllJobs } from "@forwardimpact/libpathway/derivation";
|
|
26
26
|
|
|
@@ -92,12 +92,12 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
92
92
|
mapper: disciplineToCardConfig,
|
|
93
93
|
isGrouped: true,
|
|
94
94
|
},
|
|
95
|
-
|
|
96
|
-
title: framework.entityDefinitions.
|
|
97
|
-
emojiIcon: framework.entityDefinitions.
|
|
98
|
-
description: framework.entityDefinitions.
|
|
99
|
-
entities:
|
|
100
|
-
mapper:
|
|
95
|
+
level: {
|
|
96
|
+
title: framework.entityDefinitions.level.title,
|
|
97
|
+
emojiIcon: framework.entityDefinitions.level.emojiIcon,
|
|
98
|
+
description: framework.entityDefinitions.level.description,
|
|
99
|
+
entities: prepareLevelsList(data.levels).items,
|
|
100
|
+
mapper: levelToCardConfig,
|
|
101
101
|
},
|
|
102
102
|
track: {
|
|
103
103
|
title: framework.entityDefinitions.track.title,
|
|
@@ -112,7 +112,7 @@ export function renderOverviewSlide({ render, data, params }) {
|
|
|
112
112
|
description: framework.entityDefinitions.job.description,
|
|
113
113
|
entities: generateAllJobs({
|
|
114
114
|
disciplines: data.disciplines,
|
|
115
|
-
|
|
115
|
+
levels: data.levels,
|
|
116
116
|
tracks: data.tracks,
|
|
117
117
|
skills: data.skills,
|
|
118
118
|
behaviours: data.behaviours,
|
package/src/slides/progress.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import { div, h1, p } from "../lib/render.js";
|
|
8
8
|
import {
|
|
9
9
|
prepareProgressDetail,
|
|
10
|
-
|
|
10
|
+
getDefaultTargetLevel,
|
|
11
11
|
} from "../formatters/progress/shared.js";
|
|
12
12
|
import { progressToDOM } from "../formatters/index.js";
|
|
13
13
|
|
|
@@ -20,12 +20,12 @@ import { progressToDOM } from "../formatters/index.js";
|
|
|
20
20
|
*/
|
|
21
21
|
export function renderProgressSlide({ render, data, params }) {
|
|
22
22
|
const discipline = data.disciplines.find((d) => d.id === params.discipline);
|
|
23
|
-
const
|
|
23
|
+
const level = data.levels.find((g) => g.id === params.level);
|
|
24
24
|
const track = params.track
|
|
25
25
|
? data.tracks.find((t) => t.id === params.track)
|
|
26
26
|
: null;
|
|
27
27
|
|
|
28
|
-
if (!discipline || !
|
|
28
|
+
if (!discipline || !level) {
|
|
29
29
|
render(
|
|
30
30
|
div(
|
|
31
31
|
{ className: "slide-error" },
|
|
@@ -36,23 +36,23 @@ export function renderProgressSlide({ render, data, params }) {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// Get compare
|
|
39
|
+
// Get compare level from URL query or default to next level
|
|
40
40
|
const urlParams = new URLSearchParams(window.location.search);
|
|
41
|
-
const
|
|
41
|
+
const compareLevelId = urlParams.get("compare");
|
|
42
42
|
|
|
43
|
-
let
|
|
44
|
-
if (
|
|
45
|
-
|
|
43
|
+
let targetLevel;
|
|
44
|
+
if (compareLevelId) {
|
|
45
|
+
targetLevel = data.levels.find((g) => g.id === compareLevelId);
|
|
46
46
|
} else {
|
|
47
|
-
|
|
47
|
+
targetLevel = getDefaultTargetLevel(level, data.levels);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (!
|
|
50
|
+
if (!targetLevel) {
|
|
51
51
|
render(
|
|
52
52
|
div(
|
|
53
53
|
{ className: "slide-error" },
|
|
54
54
|
h1({}, "No Progression Available"),
|
|
55
|
-
p({}, "No next
|
|
55
|
+
p({}, "No next level available for this role."),
|
|
56
56
|
),
|
|
57
57
|
);
|
|
58
58
|
return;
|
|
@@ -60,10 +60,10 @@ export function renderProgressSlide({ render, data, params }) {
|
|
|
60
60
|
|
|
61
61
|
const view = prepareProgressDetail({
|
|
62
62
|
fromDiscipline: discipline,
|
|
63
|
-
|
|
63
|
+
fromLevel: level,
|
|
64
64
|
fromTrack: track,
|
|
65
65
|
toDiscipline: discipline,
|
|
66
|
-
|
|
66
|
+
toLevel: targetLevel,
|
|
67
67
|
toTrack: track,
|
|
68
68
|
skills: data.skills,
|
|
69
69
|
behaviours: data.behaviours,
|
package/src/slides.html
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
{
|
|
10
10
|
"imports": {
|
|
11
11
|
"mustache": "https://esm.sh/mustache@4.2.0",
|
|
12
|
-
"@forwardimpact/
|
|
13
|
-
"@forwardimpact/
|
|
14
|
-
"@forwardimpact/
|
|
15
|
-
"@forwardimpact/
|
|
12
|
+
"@forwardimpact/map": "/map/lib/index.js",
|
|
13
|
+
"@forwardimpact/map/levels": "/map/lib/levels.js",
|
|
14
|
+
"@forwardimpact/map/loader": "/map/lib/loader.js",
|
|
15
|
+
"@forwardimpact/map/validation": "/map/lib/validation.js",
|
|
16
16
|
"@forwardimpact/libpathway": "/model/lib/index.js",
|
|
17
17
|
"@forwardimpact/libpathway/derivation": "/model/lib/derivation.js",
|
|
18
18
|
"@forwardimpact/libpathway/modifiers": "/model/lib/modifiers.js",
|
package/src/types.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* @property {boolean} [humanOnly] - Whether this skill requires human presence
|
|
20
20
|
* @property {'primary'|'secondary'|'tertiary'} type - Skill type in this role
|
|
21
21
|
* @property {string} level - Level ID (e.g., "advanced", "expert")
|
|
22
|
-
* @property {string}
|
|
22
|
+
* @property {string} proficiencyDescription - Human-readable level description
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
// ============================================================================
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
# {{{frameworkTitle}}} — Local Install
|
|
3
3
|
# Generated by @forwardimpact/pathway v{{{version}}}
|
|
4
4
|
#
|
|
5
|
-
# Installs
|
|
5
|
+
# Installs fit-pathway globally via npm and downloads organization data
|
|
6
|
+
# to ~/.fit/pathway/data/.
|
|
6
7
|
#
|
|
7
8
|
# Usage:
|
|
8
9
|
# curl -fsSL {{{siteUrl}}}/install.sh | bash
|
|
@@ -15,7 +16,12 @@ INSTALL_DIR="${HOME}/.fit/pathway"
|
|
|
15
16
|
command -v node >/dev/null 2>&1 || { echo "Error: Node.js 18+ is required. https://nodejs.org"; exit 1; }
|
|
16
17
|
command -v npm >/dev/null 2>&1 || { echo "Error: npm is required."; exit 1; }
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
# Install fit-pathway globally (provides the fit-pathway binary on PATH)
|
|
20
|
+
echo "Installing @forwardimpact/pathway globally..."
|
|
21
|
+
npm install -g @forwardimpact/pathway@{{{version}}}
|
|
22
|
+
|
|
23
|
+
# Download organization data to ~/.fit/pathway/data/
|
|
24
|
+
echo "Downloading organization data to ${INSTALL_DIR}/data/..."
|
|
19
25
|
mkdir -p "${INSTALL_DIR}"
|
|
20
26
|
|
|
21
27
|
TMPFILE=$(mktemp)
|
|
@@ -23,11 +29,8 @@ trap 'rm -f "$TMPFILE"' EXIT
|
|
|
23
29
|
curl -fsSL "${SITE_URL}/bundle.tar.gz" -o "$TMPFILE"
|
|
24
30
|
tar -xzf "$TMPFILE" -C "${INSTALL_DIR}" --strip-components=1
|
|
25
31
|
|
|
26
|
-
(cd "${INSTALL_DIR}" && npm install --production --ignore-scripts --no-audit --no-fund --silent)
|
|
27
|
-
|
|
28
32
|
echo ""
|
|
29
33
|
echo "Done. Usage:"
|
|
30
|
-
echo "
|
|
31
|
-
echo "
|
|
32
|
-
echo "
|
|
33
|
-
echo " npx fit-pathway agent --list"
|
|
34
|
+
echo " fit-pathway skill --list"
|
|
35
|
+
echo " fit-pathway job --list"
|
|
36
|
+
echo " fit-pathway agent --list"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# {{{title}}}
|
|
2
2
|
|
|
3
|
-
- **Level:** {{{
|
|
3
|
+
- **Level:** {{{levelId}}}
|
|
4
4
|
- **Experience:** {{{typicalExperienceRange}}}
|
|
5
5
|
{{#hasTrack}}- **Track:** {{{trackName}}}
|
|
6
6
|
{{/hasTrack}}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
{{{responsibilityDescription}}}:
|
|
35
35
|
|
|
36
36
|
{{#skills}}
|
|
37
|
-
- **{{{skillName}}}:** {{{
|
|
37
|
+
- **{{{skillName}}}:** {{{proficiencyDescription}}}
|
|
38
38
|
{{/skills}}
|
|
39
39
|
{{/capabilitySkills}}
|
|
40
40
|
{{/hasCapabilitySkills}}
|
package/src/commands/grade.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grade CLI Command
|
|
3
|
-
*
|
|
4
|
-
* Handles grade summary, listing, and detail display in the terminal.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* npx pathway grade # Summary with stats
|
|
8
|
-
* npx pathway grade --list # IDs only (for piping)
|
|
9
|
-
* npx pathway grade <id> # Detail view
|
|
10
|
-
* npx pathway grade --validate # Validation checks
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { createEntityCommand } from "./command-factory.js";
|
|
14
|
-
import { gradeToMarkdown } from "../formatters/grade/markdown.js";
|
|
15
|
-
import { formatTable } from "../lib/cli-output.js";
|
|
16
|
-
import { getConceptEmoji } from "@forwardimpact/schema/levels";
|
|
17
|
-
import { capitalize } from "../formatters/shared.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Format grade summary output
|
|
21
|
-
* @param {Array} grades - Raw grade entities
|
|
22
|
-
* @param {Object} data - Full data context
|
|
23
|
-
*/
|
|
24
|
-
function formatSummary(grades, data) {
|
|
25
|
-
const { framework } = data;
|
|
26
|
-
const emoji = framework ? getConceptEmoji(framework, "grade") : "📊";
|
|
27
|
-
|
|
28
|
-
console.log(`\n${emoji} Grades\n`);
|
|
29
|
-
|
|
30
|
-
const rows = grades.map((g) => [
|
|
31
|
-
g.id,
|
|
32
|
-
g.displayName || g.id,
|
|
33
|
-
g.typicalExperienceRange || "-",
|
|
34
|
-
capitalize(g.baseSkillLevels?.primary || "-"),
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
console.log(formatTable(["ID", "Name", "Experience", "Primary Level"], rows));
|
|
38
|
-
console.log(`\nTotal: ${grades.length} grades`);
|
|
39
|
-
console.log(`\nRun 'npx pathway grade --list' for IDs`);
|
|
40
|
-
console.log(`Run 'npx pathway grade <id>' for details\n`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Format grade detail output
|
|
45
|
-
* @param {Object} grade - Raw grade entity
|
|
46
|
-
* @param {Object} framework - Framework config
|
|
47
|
-
*/
|
|
48
|
-
function formatDetail(grade, framework) {
|
|
49
|
-
console.log(gradeToMarkdown(grade, framework));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export const runGradeCommand = createEntityCommand({
|
|
53
|
-
entityName: "grade",
|
|
54
|
-
pluralName: "grades",
|
|
55
|
-
findEntity: (data, id) => data.grades.find((g) => g.id === id),
|
|
56
|
-
presentDetail: (entity) => entity,
|
|
57
|
-
formatSummary,
|
|
58
|
-
formatDetail,
|
|
59
|
-
emojiIcon: "📊",
|
|
60
|
-
});
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grade presentation helpers
|
|
3
|
-
*
|
|
4
|
-
* Shared utilities for formatting grade data across DOM and markdown outputs.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get grade display name (shows both professional and management titles)
|
|
9
|
-
* @param {Object} grade
|
|
10
|
-
* @returns {string}
|
|
11
|
-
*/
|
|
12
|
-
export function getGradeDisplayName(grade) {
|
|
13
|
-
if (grade.professionalTitle && grade.managementTitle) {
|
|
14
|
-
if (grade.professionalTitle === grade.managementTitle) {
|
|
15
|
-
return grade.professionalTitle;
|
|
16
|
-
}
|
|
17
|
-
return `${grade.professionalTitle} / ${grade.managementTitle}`;
|
|
18
|
-
}
|
|
19
|
-
return grade.professionalTitle || grade.managementTitle || grade.id;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @typedef {Object} GradeListItem
|
|
24
|
-
* @property {string} id
|
|
25
|
-
* @property {string} displayName
|
|
26
|
-
* @property {number} ordinalRank
|
|
27
|
-
* @property {string|null} typicalExperienceRange
|
|
28
|
-
* @property {Object} baseSkillLevels
|
|
29
|
-
* @property {string|null} impactScope
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Transform grades for list view
|
|
34
|
-
* @param {Array} grades - Raw grade entities
|
|
35
|
-
* @returns {{ items: GradeListItem[] }}
|
|
36
|
-
*/
|
|
37
|
-
export function prepareGradesList(grades) {
|
|
38
|
-
const sortedGrades = [...grades].sort(
|
|
39
|
-
(a, b) => a.ordinalRank - b.ordinalRank,
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const items = sortedGrades.map((grade) => ({
|
|
43
|
-
id: grade.id,
|
|
44
|
-
displayName: getGradeDisplayName(grade),
|
|
45
|
-
ordinalRank: grade.ordinalRank,
|
|
46
|
-
typicalExperienceRange: grade.typicalExperienceRange || null,
|
|
47
|
-
baseSkillLevels: grade.baseSkillLevels || {},
|
|
48
|
-
impactScope: grade.expectations?.impactScope || null,
|
|
49
|
-
}));
|
|
50
|
-
|
|
51
|
-
return { items };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* @typedef {Object} GradeDetailView
|
|
56
|
-
* @property {string} id
|
|
57
|
-
* @property {string} displayName
|
|
58
|
-
* @property {string} professionalTitle
|
|
59
|
-
* @property {string} managementTitle
|
|
60
|
-
* @property {number} ordinalRank
|
|
61
|
-
* @property {string|null} typicalExperienceRange
|
|
62
|
-
* @property {Object} baseSkillLevels
|
|
63
|
-
* @property {Object} baseBehaviourMaturity
|
|
64
|
-
* @property {Object} expectations
|
|
65
|
-
*/
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Transform grade for detail view
|
|
69
|
-
* @param {Object} grade - Raw grade entity
|
|
70
|
-
* @returns {GradeDetailView|null}
|
|
71
|
-
*/
|
|
72
|
-
export function prepareGradeDetail(grade) {
|
|
73
|
-
if (!grade) return null;
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
id: grade.id,
|
|
77
|
-
displayName: getGradeDisplayName(grade),
|
|
78
|
-
professionalTitle: grade.professionalTitle || null,
|
|
79
|
-
managementTitle: grade.managementTitle || null,
|
|
80
|
-
ordinalRank: grade.ordinalRank,
|
|
81
|
-
typicalExperienceRange: grade.typicalExperienceRange || null,
|
|
82
|
-
baseSkillLevels: grade.baseSkillLevels || {},
|
|
83
|
-
baseBehaviourMaturity: grade.baseBehaviourMaturity || {},
|
|
84
|
-
expectations: grade.expectations || {},
|
|
85
|
-
};
|
|
86
|
-
}
|
package/src/pages/grade.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grades 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 { prepareGradesList } from "../formatters/grade/shared.js";
|
|
10
|
-
import { gradeToDOM } from "../formatters/grade/dom.js";
|
|
11
|
-
import { getConceptEmoji } from "@forwardimpact/schema/levels";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Render grades list page
|
|
15
|
-
*/
|
|
16
|
-
export function renderGradesList() {
|
|
17
|
-
const { data } = getState();
|
|
18
|
-
const { framework } = data;
|
|
19
|
-
const gradeEmoji = getConceptEmoji(framework, "grade");
|
|
20
|
-
|
|
21
|
-
// Transform data for list view
|
|
22
|
-
const { items } = prepareGradesList(data.grades);
|
|
23
|
-
|
|
24
|
-
const page = div(
|
|
25
|
-
{ className: "grades-page" },
|
|
26
|
-
// Header
|
|
27
|
-
div(
|
|
28
|
-
{ className: "page-header" },
|
|
29
|
-
h1(
|
|
30
|
-
{ className: "page-title" },
|
|
31
|
-
`${gradeEmoji} ${framework.entityDefinitions.grade.title}`,
|
|
32
|
-
),
|
|
33
|
-
p(
|
|
34
|
-
{ className: "page-description" },
|
|
35
|
-
framework.entityDefinitions.grade.description.trim(),
|
|
36
|
-
),
|
|
37
|
-
),
|
|
38
|
-
|
|
39
|
-
// Grades timeline
|
|
40
|
-
div(
|
|
41
|
-
{ className: "grades-timeline" },
|
|
42
|
-
...items.map((grade) => createGradeTimelineItem(grade)),
|
|
43
|
-
),
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
render(page);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Create a grade timeline item
|
|
51
|
-
* @param {Object} grade
|
|
52
|
-
* @returns {HTMLElement}
|
|
53
|
-
*/
|
|
54
|
-
function createGradeTimelineItem(grade) {
|
|
55
|
-
const item = div(
|
|
56
|
-
{ className: "grade-timeline-item" },
|
|
57
|
-
div({ className: "grade-level-marker" }, String(grade.ordinalRank)),
|
|
58
|
-
div(
|
|
59
|
-
{ className: "grade-timeline-content card card-clickable" },
|
|
60
|
-
div(
|
|
61
|
-
{ className: "card-header" },
|
|
62
|
-
h3({ className: "card-title" }, grade.displayName),
|
|
63
|
-
createBadge(grade.id, "default"),
|
|
64
|
-
),
|
|
65
|
-
grade.typicalExperienceRange
|
|
66
|
-
? p(
|
|
67
|
-
{ className: "text-muted", style: "margin: 0.25rem 0" },
|
|
68
|
-
`${grade.typicalExperienceRange} experience`,
|
|
69
|
-
)
|
|
70
|
-
: null,
|
|
71
|
-
div(
|
|
72
|
-
{ className: "card-meta", style: "margin-top: 0.5rem" },
|
|
73
|
-
createBadge(
|
|
74
|
-
`Primary: ${formatLevel(grade.baseSkillLevels?.primary)}`,
|
|
75
|
-
"primary",
|
|
76
|
-
),
|
|
77
|
-
createBadge(
|
|
78
|
-
`Secondary: ${formatLevel(grade.baseSkillLevels?.secondary)}`,
|
|
79
|
-
"secondary",
|
|
80
|
-
),
|
|
81
|
-
createBadge(
|
|
82
|
-
`Broad: ${formatLevel(grade.baseSkillLevels?.broad)}`,
|
|
83
|
-
"broad",
|
|
84
|
-
),
|
|
85
|
-
),
|
|
86
|
-
grade.scope
|
|
87
|
-
? p(
|
|
88
|
-
{ className: "card-description", style: "margin-top: 0.75rem" },
|
|
89
|
-
`Scope: ${grade.scope}`,
|
|
90
|
-
)
|
|
91
|
-
: null,
|
|
92
|
-
),
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
item.querySelector(".card").addEventListener("click", () => {
|
|
96
|
-
window.location.hash = `/grade/${grade.id}`;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return item;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Render grade detail page
|
|
104
|
-
* @param {Object} params - Route params
|
|
105
|
-
*/
|
|
106
|
-
export function renderGradeDetail(params) {
|
|
107
|
-
const { data } = getState();
|
|
108
|
-
const grade = data.grades.find((g) => g.id === params.id);
|
|
109
|
-
|
|
110
|
-
if (!grade) {
|
|
111
|
-
renderNotFound({
|
|
112
|
-
entityType: "Grade",
|
|
113
|
-
entityId: params.id,
|
|
114
|
-
backPath: "/grade",
|
|
115
|
-
backText: "← Back to Grades",
|
|
116
|
-
});
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Use DOM formatter - it handles transformation internally
|
|
121
|
-
render(gradeToDOM(grade, { framework: data.framework }));
|
|
122
|
-
}
|
package/src/slides/grade.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Grade Slide View
|
|
3
|
-
*
|
|
4
|
-
* Printer-friendly view of a grade.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { div, h1, p } from "../lib/render.js";
|
|
8
|
-
import { gradeToDOM } from "../formatters/index.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Render grade slide
|
|
12
|
-
* @param {Object} params
|
|
13
|
-
* @param {Function} params.render
|
|
14
|
-
* @param {Object} params.data
|
|
15
|
-
* @param {Object} params.params
|
|
16
|
-
*/
|
|
17
|
-
export function renderGradeSlide({ render, data, params }) {
|
|
18
|
-
const grade = data.grades.find((g) => g.id === params.id);
|
|
19
|
-
|
|
20
|
-
if (!grade) {
|
|
21
|
-
render(
|
|
22
|
-
div(
|
|
23
|
-
{ className: "slide-error" },
|
|
24
|
-
h1({}, "Grade Not Found"),
|
|
25
|
-
p({}, `No grade found with ID: ${params.id}`),
|
|
26
|
-
),
|
|
27
|
-
);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
render(gradeToDOM(grade, { framework: data.framework, showBackLink: false }));
|
|
32
|
-
}
|