@forwardimpact/pathway 0.25.20 → 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/package.json +1 -1
- package/src/formatters/index.js +0 -19
- 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
package/package.json
CHANGED
package/src/formatters/index.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
|
|
8
8
|
// Shared utilities
|
|
9
9
|
export * from "./shared.js";
|
|
10
|
-
export * from "./microdata-shared.js";
|
|
11
10
|
|
|
12
11
|
// Job formatters
|
|
13
12
|
export { jobToMarkdown } from "./job/markdown.js";
|
|
@@ -23,15 +22,10 @@ export { progressToDOM } from "./progress/dom.js";
|
|
|
23
22
|
|
|
24
23
|
// Driver formatters
|
|
25
24
|
export { driverToDOM } from "./driver/dom.js";
|
|
26
|
-
export {
|
|
27
|
-
driverListToMicrodata,
|
|
28
|
-
driverToMicrodata,
|
|
29
|
-
} from "./driver/microdata.js";
|
|
30
25
|
|
|
31
26
|
// Skill formatters
|
|
32
27
|
export { skillListToMarkdown, skillToMarkdown } from "./skill/markdown.js";
|
|
33
28
|
export { skillToDOM } from "./skill/dom.js";
|
|
34
|
-
export { skillListToMicrodata, skillToMicrodata } from "./skill/microdata.js";
|
|
35
29
|
|
|
36
30
|
// Behaviour formatters
|
|
37
31
|
export {
|
|
@@ -39,10 +33,6 @@ export {
|
|
|
39
33
|
behaviourToMarkdown,
|
|
40
34
|
} from "./behaviour/markdown.js";
|
|
41
35
|
export { behaviourToDOM } from "./behaviour/dom.js";
|
|
42
|
-
export {
|
|
43
|
-
behaviourListToMicrodata,
|
|
44
|
-
behaviourToMicrodata,
|
|
45
|
-
} from "./behaviour/microdata.js";
|
|
46
36
|
|
|
47
37
|
// Discipline formatters
|
|
48
38
|
export {
|
|
@@ -50,23 +40,14 @@ export {
|
|
|
50
40
|
disciplineToMarkdown,
|
|
51
41
|
} from "./discipline/markdown.js";
|
|
52
42
|
export { disciplineToDOM } from "./discipline/dom.js";
|
|
53
|
-
export {
|
|
54
|
-
disciplineListToMicrodata,
|
|
55
|
-
disciplineToMicrodata,
|
|
56
|
-
} from "./discipline/microdata.js";
|
|
57
43
|
|
|
58
44
|
// Level formatters
|
|
59
45
|
export { levelListToMarkdown, levelToMarkdown } from "./level/markdown.js";
|
|
60
46
|
export { levelToDOM } from "./level/dom.js";
|
|
61
|
-
export { levelListToMicrodata, levelToMicrodata } from "./level/microdata.js";
|
|
62
47
|
|
|
63
48
|
// Track formatters
|
|
64
49
|
export { trackListToMarkdown, trackToMarkdown } from "./track/markdown.js";
|
|
65
50
|
export { trackToDOM } from "./track/dom.js";
|
|
66
|
-
export { trackListToMicrodata, trackToMicrodata } from "./track/microdata.js";
|
|
67
|
-
|
|
68
|
-
// Stage formatters
|
|
69
|
-
export { stageListToMicrodata, stageToMicrodata } from "./stage/microdata.js";
|
|
70
51
|
|
|
71
52
|
// JSON-LD formatters
|
|
72
53
|
export {
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Behaviour formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with behaviour.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
propRaw,
|
|
12
|
-
metaTag,
|
|
13
|
-
section,
|
|
14
|
-
dl,
|
|
15
|
-
ul,
|
|
16
|
-
escapeHtml,
|
|
17
|
-
formatLevelName,
|
|
18
|
-
htmlDocument,
|
|
19
|
-
} from "../microdata-shared.js";
|
|
20
|
-
import { prepareBehavioursList, prepareBehaviourDetail } from "./shared.js";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Format behaviour list as microdata HTML
|
|
24
|
-
* @param {Array} behaviours - Raw behaviour entities
|
|
25
|
-
* @returns {string} HTML with microdata
|
|
26
|
-
*/
|
|
27
|
-
export function behaviourListToMicrodata(behaviours) {
|
|
28
|
-
const { items } = prepareBehavioursList(behaviours);
|
|
29
|
-
|
|
30
|
-
const content = items
|
|
31
|
-
.map(
|
|
32
|
-
(
|
|
33
|
-
behaviour,
|
|
34
|
-
) => `${openTag("article", { itemtype: "Behaviour", itemid: `#${behaviour.id}` })}
|
|
35
|
-
${prop("h2", "name", behaviour.name)}
|
|
36
|
-
${prop("p", "description", behaviour.truncatedDescription)}
|
|
37
|
-
</article>`,
|
|
38
|
-
)
|
|
39
|
-
.join("\n");
|
|
40
|
-
|
|
41
|
-
return htmlDocument(
|
|
42
|
-
"Behaviours",
|
|
43
|
-
`<main>
|
|
44
|
-
<h1>Behaviours</h1>
|
|
45
|
-
${content}
|
|
46
|
-
</main>`,
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Format behaviour detail as microdata HTML
|
|
52
|
-
* @param {Object} behaviour - Raw behaviour entity
|
|
53
|
-
* @param {Object} context - Additional context
|
|
54
|
-
* @param {Array} context.drivers - All drivers
|
|
55
|
-
* @returns {string} HTML with microdata
|
|
56
|
-
*/
|
|
57
|
-
export function behaviourToMicrodata(behaviour, { drivers }) {
|
|
58
|
-
const view = prepareBehaviourDetail(behaviour, { drivers });
|
|
59
|
-
|
|
60
|
-
if (!view) return "";
|
|
61
|
-
|
|
62
|
-
const sections = [];
|
|
63
|
-
|
|
64
|
-
// Maturity descriptions - uses MaturityDescriptions itemtype
|
|
65
|
-
const maturityPairs = Object.entries(view.maturityDescriptions).map(
|
|
66
|
-
([maturity, desc]) => ({
|
|
67
|
-
term: formatLevelName(maturity),
|
|
68
|
-
definition: desc,
|
|
69
|
-
itemprop: `${maturity.replace(/_([a-z])/g, (_, c) => c.toUpperCase())}Description`,
|
|
70
|
-
}),
|
|
71
|
-
);
|
|
72
|
-
sections.push(
|
|
73
|
-
section(
|
|
74
|
-
"Maturity Levels",
|
|
75
|
-
`${openTag("div", { itemtype: "MaturityDescriptions", itemprop: "maturityDescriptions" })}
|
|
76
|
-
${dl(maturityPairs)}
|
|
77
|
-
</div>`,
|
|
78
|
-
2,
|
|
79
|
-
),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
// Related drivers
|
|
83
|
-
if (view.relatedDrivers.length > 0) {
|
|
84
|
-
const driverItems = view.relatedDrivers.map(
|
|
85
|
-
(d) => `<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a>`,
|
|
86
|
-
);
|
|
87
|
-
sections.push(section("Linked to Drivers", ul(driverItems), 2));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const body = `<main>
|
|
91
|
-
${openTag("article", { itemtype: "Behaviour", itemid: `#${view.id}` })}
|
|
92
|
-
${prop("h1", "name", view.name)}
|
|
93
|
-
${metaTag("id", view.id)}
|
|
94
|
-
${propRaw(
|
|
95
|
-
"div",
|
|
96
|
-
"human",
|
|
97
|
-
`${openTag("div", { itemtype: "BehaviourHumanSection" })}
|
|
98
|
-
${prop("p", "description", view.description)}
|
|
99
|
-
${sections.join("\n")}
|
|
100
|
-
</div>`,
|
|
101
|
-
)}
|
|
102
|
-
</article>
|
|
103
|
-
</main>`;
|
|
104
|
-
|
|
105
|
-
return htmlDocument(view.name, body);
|
|
106
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Discipline formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with discipline.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
metaTag,
|
|
12
|
-
linkTag,
|
|
13
|
-
section,
|
|
14
|
-
ul,
|
|
15
|
-
escapeHtml,
|
|
16
|
-
htmlDocument,
|
|
17
|
-
} from "../microdata-shared.js";
|
|
18
|
-
import { prepareDisciplinesList, prepareDisciplineDetail } from "./shared.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Format discipline list as microdata HTML
|
|
22
|
-
* @param {Array} disciplines - Raw discipline entities
|
|
23
|
-
* @returns {string} HTML with microdata
|
|
24
|
-
*/
|
|
25
|
-
export function disciplineListToMicrodata(disciplines) {
|
|
26
|
-
const { items } = prepareDisciplinesList(disciplines);
|
|
27
|
-
|
|
28
|
-
const content = items
|
|
29
|
-
.map(
|
|
30
|
-
(
|
|
31
|
-
d,
|
|
32
|
-
) => `${openTag("article", { itemtype: "Discipline", itemid: `#${d.id}` })}
|
|
33
|
-
${prop("h2", "specialization", d.name)}
|
|
34
|
-
<p>Core: ${d.coreSkillsCount} | Supporting: ${d.supportingSkillsCount} | Broad: ${d.broadSkillsCount}</p>
|
|
35
|
-
</article>`,
|
|
36
|
-
)
|
|
37
|
-
.join("\n");
|
|
38
|
-
|
|
39
|
-
return htmlDocument(
|
|
40
|
-
"Disciplines",
|
|
41
|
-
`<main>
|
|
42
|
-
<h1>Disciplines</h1>
|
|
43
|
-
${content}
|
|
44
|
-
</main>`,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Format discipline detail as microdata HTML
|
|
50
|
-
* @param {Object} discipline - Raw discipline entity
|
|
51
|
-
* @param {Object} context - Additional context
|
|
52
|
-
* @param {Array} context.skills - All skills
|
|
53
|
-
* @param {Array} context.behaviours - All behaviours
|
|
54
|
-
* @param {boolean} [context.showBehaviourModifiers=true] - Whether to show behaviour modifiers section
|
|
55
|
-
* @returns {string} HTML with microdata
|
|
56
|
-
*/
|
|
57
|
-
export function disciplineToMicrodata(
|
|
58
|
-
discipline,
|
|
59
|
-
{ skills, behaviours, showBehaviourModifiers = true } = {},
|
|
60
|
-
) {
|
|
61
|
-
const view = prepareDisciplineDetail(discipline, { skills, behaviours });
|
|
62
|
-
|
|
63
|
-
if (!view) return "";
|
|
64
|
-
|
|
65
|
-
const sections = [];
|
|
66
|
-
|
|
67
|
-
// Core skills - using coreSkills property
|
|
68
|
-
if (view.coreSkills.length > 0) {
|
|
69
|
-
const skillLinks = view.coreSkills.map(
|
|
70
|
-
(s) =>
|
|
71
|
-
`${openTag("span", { itemprop: "coreSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
|
|
72
|
-
);
|
|
73
|
-
sections.push(section("Core Skills", ul(skillLinks), 2));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Supporting skills - using supportingSkills property
|
|
77
|
-
if (view.supportingSkills.length > 0) {
|
|
78
|
-
const skillLinks = view.supportingSkills.map(
|
|
79
|
-
(s) =>
|
|
80
|
-
`${openTag("span", { itemprop: "supportingSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
|
|
81
|
-
);
|
|
82
|
-
sections.push(section("Supporting Skills", ul(skillLinks), 2));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Broad skills - using broadSkills property
|
|
86
|
-
if (view.broadSkills.length > 0) {
|
|
87
|
-
const skillLinks = view.broadSkills.map(
|
|
88
|
-
(s) =>
|
|
89
|
-
`${openTag("span", { itemprop: "broadSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
|
|
90
|
-
);
|
|
91
|
-
sections.push(section("Broad Skills", ul(skillLinks), 2));
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Behaviour modifiers - using BehaviourModifier itemtype
|
|
95
|
-
if (showBehaviourModifiers && view.behaviourModifiers.length > 0) {
|
|
96
|
-
const modifierItems = view.behaviourModifiers.map((b) => {
|
|
97
|
-
const modifierStr = b.modifier > 0 ? `+${b.modifier}` : `${b.modifier}`;
|
|
98
|
-
return `${openTag("span", { itemtype: "BehaviourModifier", itemprop: "behaviourModifiers" })}
|
|
99
|
-
${linkTag("targetBehaviour", `#${b.id}`)}
|
|
100
|
-
<a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
|
|
101
|
-
</span>`;
|
|
102
|
-
});
|
|
103
|
-
sections.push(section("Behaviour Modifiers", ul(modifierItems), 2));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const body = `<main>
|
|
107
|
-
${openTag("article", { itemtype: "Discipline", itemid: `#${view.id}` })}
|
|
108
|
-
${prop("h1", "specialization", view.name)}
|
|
109
|
-
${metaTag("id", view.id)}
|
|
110
|
-
${discipline.roleTitle ? prop("p", "roleTitle", discipline.roleTitle) : ""}
|
|
111
|
-
${prop("p", "description", view.description)}
|
|
112
|
-
${sections.join("\n")}
|
|
113
|
-
</article>
|
|
114
|
-
</main>`;
|
|
115
|
-
|
|
116
|
-
return htmlDocument(view.name, body);
|
|
117
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Driver formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with drivers.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
metaTag,
|
|
12
|
-
section,
|
|
13
|
-
ul,
|
|
14
|
-
escapeHtml,
|
|
15
|
-
htmlDocument,
|
|
16
|
-
} from "../microdata-shared.js";
|
|
17
|
-
import { prepareDriversList, prepareDriverDetail } from "./shared.js";
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Format driver list as microdata HTML
|
|
21
|
-
* @param {Array} drivers - Raw driver entities
|
|
22
|
-
* @returns {string} HTML with microdata
|
|
23
|
-
*/
|
|
24
|
-
export function driverListToMicrodata(drivers) {
|
|
25
|
-
const { items } = prepareDriversList(drivers);
|
|
26
|
-
|
|
27
|
-
const content = items
|
|
28
|
-
.map(
|
|
29
|
-
(
|
|
30
|
-
driver,
|
|
31
|
-
) => `${openTag("article", { itemtype: "Driver", itemid: `#${driver.id}` })}
|
|
32
|
-
${prop("h2", "name", driver.name)}
|
|
33
|
-
${prop("p", "description", driver.truncatedDescription)}
|
|
34
|
-
<p>Skills: ${driver.contributingSkillsCount} | Behaviours: ${driver.contributingBehavioursCount}</p>
|
|
35
|
-
</article>`,
|
|
36
|
-
)
|
|
37
|
-
.join("\n");
|
|
38
|
-
|
|
39
|
-
return htmlDocument(
|
|
40
|
-
"Drivers",
|
|
41
|
-
`<main>
|
|
42
|
-
<h1>Drivers</h1>
|
|
43
|
-
${content}
|
|
44
|
-
</main>`,
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Format driver detail as microdata HTML
|
|
50
|
-
* @param {Object} driver - Raw driver entity
|
|
51
|
-
* @param {Object} context - Additional context
|
|
52
|
-
* @param {Array} context.skills - All skills
|
|
53
|
-
* @param {Array} context.behaviours - All behaviours
|
|
54
|
-
* @returns {string} HTML with microdata
|
|
55
|
-
*/
|
|
56
|
-
export function driverToMicrodata(driver, { skills, behaviours }) {
|
|
57
|
-
const view = prepareDriverDetail(driver, { skills, behaviours });
|
|
58
|
-
|
|
59
|
-
if (!view) return "";
|
|
60
|
-
|
|
61
|
-
const sections = [];
|
|
62
|
-
|
|
63
|
-
// Contributing skills - using contributingSkills property
|
|
64
|
-
if (view.contributingSkills.length > 0) {
|
|
65
|
-
const skillLinks = view.contributingSkills.map(
|
|
66
|
-
(s) =>
|
|
67
|
-
`${openTag("span", { itemprop: "contributingSkills" })}<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a></span>`,
|
|
68
|
-
);
|
|
69
|
-
sections.push(section("Contributing Skills", ul(skillLinks), 2));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Contributing behaviours - using contributingBehaviours property
|
|
73
|
-
if (view.contributingBehaviours.length > 0) {
|
|
74
|
-
const behaviourLinks = view.contributingBehaviours.map(
|
|
75
|
-
(b) =>
|
|
76
|
-
`${openTag("span", { itemprop: "contributingBehaviours" })}<a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a></span>`,
|
|
77
|
-
);
|
|
78
|
-
sections.push(section("Contributing Behaviours", ul(behaviourLinks), 2));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const body = `<main>
|
|
82
|
-
${openTag("article", { itemtype: "Driver", itemid: `#${view.id}` })}
|
|
83
|
-
${prop("h1", "name", view.name)}
|
|
84
|
-
${metaTag("id", view.id)}
|
|
85
|
-
${prop("p", "description", view.description)}
|
|
86
|
-
${sections.join("\n")}
|
|
87
|
-
</article>
|
|
88
|
-
</main>`;
|
|
89
|
-
|
|
90
|
-
return htmlDocument(view.name, body);
|
|
91
|
-
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Level formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with levels.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
metaTag,
|
|
12
|
-
linkTag,
|
|
13
|
-
section,
|
|
14
|
-
dl,
|
|
15
|
-
escapeHtml,
|
|
16
|
-
formatLevelName,
|
|
17
|
-
htmlDocument,
|
|
18
|
-
} from "../microdata-shared.js";
|
|
19
|
-
import { prepareLevelsList, prepareLevelDetail } from "./shared.js";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Format level list as microdata HTML
|
|
23
|
-
* @param {Array} levels - Raw level entities
|
|
24
|
-
* @returns {string} HTML with microdata
|
|
25
|
-
*/
|
|
26
|
-
export function levelListToMicrodata(levels) {
|
|
27
|
-
const { items } = prepareLevelsList(levels);
|
|
28
|
-
|
|
29
|
-
const content = items
|
|
30
|
-
.map(
|
|
31
|
-
(g) => `${openTag("article", { itemtype: "Level", itemid: `#${g.id}` })}
|
|
32
|
-
${prop("h2", "id", g.id)}
|
|
33
|
-
<p>${escapeHtml(g.displayName)}</p>
|
|
34
|
-
${g.typicalExperienceRange ? prop("p", "typicalExperienceRange", g.typicalExperienceRange) : ""}
|
|
35
|
-
${metaTag("ordinalRank", String(g.ordinalRank))}
|
|
36
|
-
</article>`,
|
|
37
|
-
)
|
|
38
|
-
.join("\n");
|
|
39
|
-
|
|
40
|
-
return htmlDocument(
|
|
41
|
-
"Levels",
|
|
42
|
-
`<main>
|
|
43
|
-
<h1>Levels</h1>
|
|
44
|
-
${content}
|
|
45
|
-
</main>`,
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Format level detail as microdata HTML
|
|
51
|
-
* @param {Object} level - Raw level entity
|
|
52
|
-
* @returns {string} HTML with microdata
|
|
53
|
-
*/
|
|
54
|
-
export function levelToMicrodata(level) {
|
|
55
|
-
const view = prepareLevelDetail(level);
|
|
56
|
-
|
|
57
|
-
if (!view) return "";
|
|
58
|
-
|
|
59
|
-
const sections = [];
|
|
60
|
-
|
|
61
|
-
// Titles section
|
|
62
|
-
if (view.professionalTitle || view.managementTitle) {
|
|
63
|
-
const titlePairs = [];
|
|
64
|
-
if (view.professionalTitle) {
|
|
65
|
-
titlePairs.push({
|
|
66
|
-
term: "Professional Track",
|
|
67
|
-
definition: view.professionalTitle,
|
|
68
|
-
itemprop: "professionalTitle",
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
if (view.managementTitle) {
|
|
72
|
-
titlePairs.push({
|
|
73
|
-
term: "Management Track",
|
|
74
|
-
definition: view.managementTitle,
|
|
75
|
-
itemprop: "managementTitle",
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
sections.push(section("Titles", dl(titlePairs), 2));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Base skill proficiencies - using BaseSkillProficiencies itemtype
|
|
82
|
-
if (
|
|
83
|
-
view.baseSkillProficiencies &&
|
|
84
|
-
Object.keys(view.baseSkillProficiencies).length > 0
|
|
85
|
-
) {
|
|
86
|
-
const levelPairs = Object.entries(view.baseSkillProficiencies).map(
|
|
87
|
-
([type, level]) => ({
|
|
88
|
-
term: formatLevelName(type),
|
|
89
|
-
definition: formatLevelName(level),
|
|
90
|
-
itemprop: type,
|
|
91
|
-
}),
|
|
92
|
-
);
|
|
93
|
-
sections.push(
|
|
94
|
-
section(
|
|
95
|
-
"Base Skill Proficiencies",
|
|
96
|
-
`${openTag("div", { itemtype: "BaseSkillProficiencies", itemprop: "baseSkillProficiencies" })}
|
|
97
|
-
${dl(levelPairs)}
|
|
98
|
-
</div>`,
|
|
99
|
-
2,
|
|
100
|
-
),
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Base behaviour maturity - link to BehaviourMaturity
|
|
105
|
-
if (view.baseBehaviourMaturity) {
|
|
106
|
-
const maturityContent = `${linkTag("baseBehaviourMaturity", `#${level.baseBehaviourMaturity}`)}
|
|
107
|
-
<p>${formatLevelName(level.baseBehaviourMaturity)}</p>`;
|
|
108
|
-
sections.push(section("Base Behaviour Maturity", maturityContent, 2));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Expectations - using LevelExpectations itemtype
|
|
112
|
-
if (view.expectations && Object.keys(view.expectations).length > 0) {
|
|
113
|
-
const expectationPairs = Object.entries(view.expectations).map(
|
|
114
|
-
([key, value]) => ({
|
|
115
|
-
term: formatLevelName(key),
|
|
116
|
-
definition: value,
|
|
117
|
-
itemprop: key,
|
|
118
|
-
}),
|
|
119
|
-
);
|
|
120
|
-
sections.push(
|
|
121
|
-
section(
|
|
122
|
-
"Expectations",
|
|
123
|
-
`${openTag("div", { itemtype: "LevelExpectations", itemprop: "expectations" })}
|
|
124
|
-
${dl(expectationPairs)}
|
|
125
|
-
</div>`,
|
|
126
|
-
2,
|
|
127
|
-
),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const body = `<main>
|
|
132
|
-
${openTag("article", { itemtype: "Level", itemid: `#${view.id}` })}
|
|
133
|
-
<h1>${prop("span", "id", view.id)} — ${escapeHtml(view.displayName)}</h1>
|
|
134
|
-
${metaTag("ordinalRank", String(view.ordinalRank))}
|
|
135
|
-
${view.typicalExperienceRange ? prop("p", "typicalExperienceRange", `Experience: ${view.typicalExperienceRange}`) : ""}
|
|
136
|
-
${sections.join("\n")}
|
|
137
|
-
</article>
|
|
138
|
-
</main>`;
|
|
139
|
-
|
|
140
|
-
return htmlDocument(`${view.id} - ${view.displayName}`, body);
|
|
141
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared microdata HTML utilities
|
|
3
|
-
*
|
|
4
|
-
* Helper functions for generating clean, class-less HTML with microdata attributes
|
|
5
|
-
* aligned with the RDF schema at https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const VOCAB_BASE = "https://www.forwardimpact.team/schema/rdf/";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Create an opening tag with microdata attributes
|
|
12
|
-
* @param {string} tag - HTML tag name
|
|
13
|
-
* @param {Object} [attrs] - Optional attributes
|
|
14
|
-
* @param {string} [attrs.itemtype] - Microdata type (without vocab prefix)
|
|
15
|
-
* @param {string} [attrs.itemprop] - Microdata property name
|
|
16
|
-
* @param {string} [attrs.itemid] - Microdata item ID
|
|
17
|
-
* @returns {string}
|
|
18
|
-
*/
|
|
19
|
-
export function openTag(tag, attrs = {}) {
|
|
20
|
-
const parts = [tag];
|
|
21
|
-
|
|
22
|
-
if (attrs.itemtype) {
|
|
23
|
-
parts.push(`itemscope`);
|
|
24
|
-
parts.push(`itemtype="${VOCAB_BASE}${attrs.itemtype}"`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (attrs.itemprop) {
|
|
28
|
-
parts.push(`itemprop="${attrs.itemprop}"`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (attrs.itemid) {
|
|
32
|
-
parts.push(`itemid="${attrs.itemid}"`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return `<${parts.join(" ")}>`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Create a self-closing meta element with microdata
|
|
40
|
-
* @param {string} itemprop - Property name
|
|
41
|
-
* @param {string} content - Content value
|
|
42
|
-
* @returns {string}
|
|
43
|
-
*/
|
|
44
|
-
export function metaTag(itemprop, content) {
|
|
45
|
-
return `<meta itemprop="${itemprop}" content="${escapeAttr(content)}">`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Create a link element with microdata
|
|
50
|
-
* @param {string} itemprop - Property name
|
|
51
|
-
* @param {string} href - Link target
|
|
52
|
-
* @returns {string}
|
|
53
|
-
*/
|
|
54
|
-
export function linkTag(itemprop, href) {
|
|
55
|
-
return `<link itemprop="${itemprop}" href="${escapeAttr(href)}">`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Wrap content in an element with itemprop
|
|
60
|
-
* @param {string} tag - HTML tag name
|
|
61
|
-
* @param {string} itemprop - Property name
|
|
62
|
-
* @param {string} content - Content to wrap
|
|
63
|
-
* @returns {string}
|
|
64
|
-
*/
|
|
65
|
-
export function prop(tag, itemprop, content) {
|
|
66
|
-
return `<${tag} itemprop="${itemprop}">${escapeHtml(content)}</${tag}>`;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Wrap raw HTML content in an element with itemprop (no escaping)
|
|
71
|
-
* @param {string} tag - HTML tag name
|
|
72
|
-
* @param {string} itemprop - Property name
|
|
73
|
-
* @param {string} html - HTML content to wrap
|
|
74
|
-
* @returns {string}
|
|
75
|
-
*/
|
|
76
|
-
export function propRaw(tag, itemprop, html) {
|
|
77
|
-
return `<${tag} itemprop="${itemprop}">${html}</${tag}>`;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Create a section with optional heading
|
|
82
|
-
* @param {string} heading - Section heading text
|
|
83
|
-
* @param {string} content - Section content
|
|
84
|
-
* @param {number} [level=2] - Heading level (2-6)
|
|
85
|
-
* @returns {string}
|
|
86
|
-
*/
|
|
87
|
-
export function section(heading, content, level = 2) {
|
|
88
|
-
const hTag = `h${Math.min(Math.max(level, 1), 6)}`;
|
|
89
|
-
return `<section>
|
|
90
|
-
<${hTag}>${escapeHtml(heading)}</${hTag}>
|
|
91
|
-
${content}
|
|
92
|
-
</section>`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Create an unordered list
|
|
97
|
-
* @param {string[]} items - List items (already HTML)
|
|
98
|
-
* @param {string} [itemprop] - Optional property for list items
|
|
99
|
-
* @returns {string}
|
|
100
|
-
*/
|
|
101
|
-
export function ul(items, itemprop) {
|
|
102
|
-
if (!items.length) return "";
|
|
103
|
-
const lis = items
|
|
104
|
-
.map((item) =>
|
|
105
|
-
itemprop ? `<li itemprop="${itemprop}">${item}</li>` : `<li>${item}</li>`,
|
|
106
|
-
)
|
|
107
|
-
.join("\n");
|
|
108
|
-
return `<ul>\n${lis}\n</ul>`;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Create a definition list from key-value pairs
|
|
113
|
-
* @param {Array<{term: string, definition: string, itemprop?: string}>} pairs
|
|
114
|
-
* @returns {string}
|
|
115
|
-
*/
|
|
116
|
-
export function dl(pairs) {
|
|
117
|
-
if (!pairs.length) return "";
|
|
118
|
-
const content = pairs
|
|
119
|
-
.map(({ term, definition, itemprop }) => {
|
|
120
|
-
const dd = itemprop
|
|
121
|
-
? `<dd itemprop="${itemprop}">${escapeHtml(definition)}</dd>`
|
|
122
|
-
: `<dd>${escapeHtml(definition)}</dd>`;
|
|
123
|
-
return `<dt>${escapeHtml(term)}</dt>\n${dd}`;
|
|
124
|
-
})
|
|
125
|
-
.join("\n");
|
|
126
|
-
return `<dl>\n${content}\n</dl>`;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Escape HTML special characters
|
|
131
|
-
* @param {string} str
|
|
132
|
-
* @returns {string}
|
|
133
|
-
*/
|
|
134
|
-
export function escapeHtml(str) {
|
|
135
|
-
if (str == null) return "";
|
|
136
|
-
return String(str)
|
|
137
|
-
.replace(/&/g, "&")
|
|
138
|
-
.replace(/</g, "<")
|
|
139
|
-
.replace(/>/g, ">");
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Escape attribute value
|
|
144
|
-
* @param {string} str
|
|
145
|
-
* @returns {string}
|
|
146
|
-
*/
|
|
147
|
-
export function escapeAttr(str) {
|
|
148
|
-
if (str == null) return "";
|
|
149
|
-
return String(str)
|
|
150
|
-
.replace(/&/g, "&")
|
|
151
|
-
.replace(/"/g, """)
|
|
152
|
-
.replace(/</g, "<")
|
|
153
|
-
.replace(/>/g, ">");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Format level name for display (capitalize, replace underscores)
|
|
158
|
-
* @param {string} level
|
|
159
|
-
* @returns {string}
|
|
160
|
-
*/
|
|
161
|
-
export function formatLevelName(level) {
|
|
162
|
-
if (!level) return "";
|
|
163
|
-
return level.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* Generate a full microdata HTML document
|
|
168
|
-
* @param {string} title - Document title
|
|
169
|
-
* @param {string} body - Body content
|
|
170
|
-
* @returns {string}
|
|
171
|
-
*/
|
|
172
|
-
export function htmlDocument(title, body) {
|
|
173
|
-
return `<!DOCTYPE html>
|
|
174
|
-
<html lang="en">
|
|
175
|
-
<head>
|
|
176
|
-
<meta charset="UTF-8">
|
|
177
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
178
|
-
<title>${escapeHtml(title)}</title>
|
|
179
|
-
</head>
|
|
180
|
-
<body>
|
|
181
|
-
${body}
|
|
182
|
-
</body>
|
|
183
|
-
</html>`;
|
|
184
|
-
}
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with capability.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
propRaw,
|
|
12
|
-
metaTag,
|
|
13
|
-
section,
|
|
14
|
-
dl,
|
|
15
|
-
ul,
|
|
16
|
-
escapeHtml,
|
|
17
|
-
formatLevelName,
|
|
18
|
-
htmlDocument,
|
|
19
|
-
} from "../microdata-shared.js";
|
|
20
|
-
import { prepareSkillsList, prepareSkillDetail } from "./shared.js";
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Format skill list as microdata HTML
|
|
24
|
-
* @param {Array} skills - Raw skill entities
|
|
25
|
-
* @param {Array} capabilities - Capability entities
|
|
26
|
-
* @returns {string} HTML with microdata
|
|
27
|
-
*/
|
|
28
|
-
export function skillListToMicrodata(skills, capabilities) {
|
|
29
|
-
const { groups, groupOrder } = prepareSkillsList(skills, capabilities);
|
|
30
|
-
|
|
31
|
-
const content = groupOrder
|
|
32
|
-
.map((capability) => {
|
|
33
|
-
const capabilitySkills = groups[capability];
|
|
34
|
-
const skillItems = capabilitySkills
|
|
35
|
-
.map(
|
|
36
|
-
(
|
|
37
|
-
skill,
|
|
38
|
-
) => `${openTag("article", { itemtype: "Skill", itemid: `#${skill.id}` })}
|
|
39
|
-
${prop("h3", "name", skill.name)}
|
|
40
|
-
${prop("p", "description", skill.truncatedDescription)}
|
|
41
|
-
${metaTag("capability", capability)}
|
|
42
|
-
</article>`,
|
|
43
|
-
)
|
|
44
|
-
.join("\n");
|
|
45
|
-
|
|
46
|
-
return section(formatLevelName(capability), skillItems, 2);
|
|
47
|
-
})
|
|
48
|
-
.join("\n");
|
|
49
|
-
|
|
50
|
-
return htmlDocument(
|
|
51
|
-
"Skills",
|
|
52
|
-
`<main>
|
|
53
|
-
<h1>Skills</h1>
|
|
54
|
-
${content}
|
|
55
|
-
</main>`,
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Format skill detail as microdata HTML
|
|
61
|
-
* @param {Object} skill - Raw skill entity
|
|
62
|
-
* @param {Object} context - Additional context
|
|
63
|
-
* @param {Array} context.disciplines - All disciplines
|
|
64
|
-
* @param {Array} context.tracks - All tracks
|
|
65
|
-
* @param {Array} context.drivers - All drivers
|
|
66
|
-
* @param {Array} context.capabilities - Capability entities
|
|
67
|
-
* @returns {string} HTML with microdata
|
|
68
|
-
*/
|
|
69
|
-
export function skillToMicrodata(
|
|
70
|
-
skill,
|
|
71
|
-
{ disciplines, tracks, drivers, capabilities },
|
|
72
|
-
) {
|
|
73
|
-
const view = prepareSkillDetail(skill, {
|
|
74
|
-
disciplines,
|
|
75
|
-
tracks,
|
|
76
|
-
drivers,
|
|
77
|
-
capabilities,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
if (!view) return "";
|
|
81
|
-
|
|
82
|
-
const sections = [];
|
|
83
|
-
|
|
84
|
-
// Human-only badge
|
|
85
|
-
if (view.isHumanOnly) {
|
|
86
|
-
sections.push(`<p><strong>Human-Only</strong> — Requires interpersonal skills; excluded from agents</p>
|
|
87
|
-
${metaTag("isHumanOnly", "true")}`);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Level descriptions - uses ProficiencyDescriptions itemtype
|
|
91
|
-
const levelPairs = Object.entries(view.proficiencyDescriptions).map(
|
|
92
|
-
([level, desc]) => ({
|
|
93
|
-
term: formatLevelName(level),
|
|
94
|
-
definition: desc,
|
|
95
|
-
itemprop: `${level}Description`,
|
|
96
|
-
}),
|
|
97
|
-
);
|
|
98
|
-
sections.push(
|
|
99
|
-
section(
|
|
100
|
-
"Level Descriptions",
|
|
101
|
-
`${openTag("div", { itemtype: "ProficiencyDescriptions", itemprop: "proficiencyDescriptions" })}
|
|
102
|
-
${dl(levelPairs)}
|
|
103
|
-
</div>`,
|
|
104
|
-
2,
|
|
105
|
-
),
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// Related disciplines
|
|
109
|
-
if (view.relatedDisciplines.length > 0) {
|
|
110
|
-
const disciplineItems = view.relatedDisciplines.map(
|
|
111
|
-
(d) =>
|
|
112
|
-
`<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a> (${escapeHtml(d.skillType)})`,
|
|
113
|
-
);
|
|
114
|
-
sections.push(section("Used in Disciplines", ul(disciplineItems), 2));
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Related tracks with modifiers
|
|
118
|
-
if (view.relatedTracks.length > 0) {
|
|
119
|
-
const trackItems = view.relatedTracks.map((t) => {
|
|
120
|
-
const modifierStr = t.modifier > 0 ? `+${t.modifier}` : `${t.modifier}`;
|
|
121
|
-
return `<a href="#${escapeHtml(t.id)}">${escapeHtml(t.name)}</a>: ${modifierStr}`;
|
|
122
|
-
});
|
|
123
|
-
sections.push(section("Modified by Tracks", ul(trackItems), 2));
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Related drivers
|
|
127
|
-
if (view.relatedDrivers.length > 0) {
|
|
128
|
-
const driverItems = view.relatedDrivers.map(
|
|
129
|
-
(d) => `<a href="#${escapeHtml(d.id)}">${escapeHtml(d.name)}</a>`,
|
|
130
|
-
);
|
|
131
|
-
sections.push(section("Linked to Drivers", ul(driverItems), 2));
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const body = `<main>
|
|
135
|
-
${openTag("article", { itemtype: "Skill", itemid: `#${view.id}` })}
|
|
136
|
-
${prop("h1", "name", view.name)}
|
|
137
|
-
${metaTag("id", view.id)}
|
|
138
|
-
${metaTag("capability", view.capability)}
|
|
139
|
-
${propRaw(
|
|
140
|
-
"div",
|
|
141
|
-
"human",
|
|
142
|
-
`${openTag("div", { itemtype: "SkillHumanSection" })}
|
|
143
|
-
${prop("p", "description", view.description)}
|
|
144
|
-
${sections.join("\n")}
|
|
145
|
-
</div>`,
|
|
146
|
-
)}
|
|
147
|
-
</article>
|
|
148
|
-
</main>`;
|
|
149
|
-
|
|
150
|
-
return htmlDocument(view.name, body);
|
|
151
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Stage formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with stages.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
metaTag,
|
|
12
|
-
linkTag,
|
|
13
|
-
section,
|
|
14
|
-
ul,
|
|
15
|
-
escapeHtml,
|
|
16
|
-
htmlDocument,
|
|
17
|
-
} from "../microdata-shared.js";
|
|
18
|
-
import { prepareStagesList, prepareStageDetail } from "./shared.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Format stage list as microdata HTML
|
|
22
|
-
* @param {Array} stages - Raw stage entities
|
|
23
|
-
* @returns {string} HTML with microdata
|
|
24
|
-
*/
|
|
25
|
-
export function stageListToMicrodata(stages) {
|
|
26
|
-
const { items } = prepareStagesList(stages);
|
|
27
|
-
|
|
28
|
-
const content = items
|
|
29
|
-
.map((stage) => {
|
|
30
|
-
const handoffText =
|
|
31
|
-
stage.handoffs.length > 0
|
|
32
|
-
? `→ ${stage.handoffs.map((h) => h.target).join(", ")}`
|
|
33
|
-
: "";
|
|
34
|
-
return `${openTag("article", { itemtype: "Stage", itemid: `#${stage.id}` })}
|
|
35
|
-
${prop("h2", "name", `${stage.emojiIcon} ${stage.name}`)}
|
|
36
|
-
${prop("p", "description", stage.truncatedDescription)}
|
|
37
|
-
${handoffText ? `<p>Handoffs: ${handoffText}</p>` : ""}
|
|
38
|
-
</article>`;
|
|
39
|
-
})
|
|
40
|
-
.join("\n");
|
|
41
|
-
|
|
42
|
-
return htmlDocument(
|
|
43
|
-
"Stages",
|
|
44
|
-
`<main>
|
|
45
|
-
<h1>Stages</h1>
|
|
46
|
-
${content}
|
|
47
|
-
</main>`,
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Format stage detail as microdata HTML
|
|
53
|
-
* @param {Object} stage - Raw stage entity
|
|
54
|
-
* @returns {string} HTML with microdata
|
|
55
|
-
*/
|
|
56
|
-
export function stageToMicrodata(stage) {
|
|
57
|
-
const view = prepareStageDetail(stage);
|
|
58
|
-
|
|
59
|
-
if (!view) return "";
|
|
60
|
-
|
|
61
|
-
const sections = [];
|
|
62
|
-
|
|
63
|
-
// Read checklist
|
|
64
|
-
if (view.readChecklist.length > 0) {
|
|
65
|
-
const readItems = view.readChecklist.map((c) => escapeHtml(c));
|
|
66
|
-
sections.push(
|
|
67
|
-
section("Read-Then-Do Checklist", ul(readItems, "readChecklist"), 2),
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Constraints
|
|
72
|
-
if (view.constraints.length > 0) {
|
|
73
|
-
const constraintItems = view.constraints.map((c) => escapeHtml(c));
|
|
74
|
-
sections.push(
|
|
75
|
-
section("Constraints", ul(constraintItems, "constraints"), 2),
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Confirm checklist
|
|
80
|
-
if (view.confirmChecklist.length > 0) {
|
|
81
|
-
const confirmItems = view.confirmChecklist.map((c) => escapeHtml(c));
|
|
82
|
-
sections.push(
|
|
83
|
-
section(
|
|
84
|
-
"Do-Then-Confirm Checklist",
|
|
85
|
-
ul(confirmItems, "confirmChecklist"),
|
|
86
|
-
2,
|
|
87
|
-
),
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Handoffs - using Handoff itemtype
|
|
92
|
-
if (view.handoffs.length > 0) {
|
|
93
|
-
const handoffItems = view.handoffs.map(
|
|
94
|
-
(
|
|
95
|
-
h,
|
|
96
|
-
) => `${openTag("article", { itemtype: "Handoff", itemprop: "handoffs" })}
|
|
97
|
-
${linkTag("targetStage", `#${h.target}`)}
|
|
98
|
-
<p><strong>${prop("span", "label", h.label)}</strong> → ${escapeHtml(h.target)}</p>
|
|
99
|
-
${prop("p", "prompt", h.prompt)}
|
|
100
|
-
</article>`,
|
|
101
|
-
);
|
|
102
|
-
sections.push(section("Handoffs", handoffItems.join("\n"), 2));
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const body = `<main>
|
|
106
|
-
${openTag("article", { itemtype: "Stage", itemid: `#${view.id}` })}
|
|
107
|
-
${prop("h1", "name", view.name)}
|
|
108
|
-
${metaTag("id", view.id)}
|
|
109
|
-
${stage.emojiIcon ? metaTag("emojiIcon", stage.emojiIcon) : ""}
|
|
110
|
-
${prop("p", "description", view.description)}
|
|
111
|
-
${sections.join("\n")}
|
|
112
|
-
</article>
|
|
113
|
-
</main>`;
|
|
114
|
-
|
|
115
|
-
return htmlDocument(view.name, body);
|
|
116
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Track formatting for microdata HTML output
|
|
3
|
-
*
|
|
4
|
-
* Generates clean, class-less HTML with microdata aligned with track.schema.json
|
|
5
|
-
* RDF vocab: https://www.forwardimpact.team/schema/rdf/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
openTag,
|
|
10
|
-
prop,
|
|
11
|
-
metaTag,
|
|
12
|
-
linkTag,
|
|
13
|
-
section,
|
|
14
|
-
ul,
|
|
15
|
-
escapeHtml,
|
|
16
|
-
htmlDocument,
|
|
17
|
-
} from "../microdata-shared.js";
|
|
18
|
-
import { prepareTracksList, prepareTrackDetail } from "./shared.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Format track list as microdata HTML
|
|
22
|
-
* @param {Array} tracks - Raw track entities
|
|
23
|
-
* @returns {string} HTML with microdata
|
|
24
|
-
*/
|
|
25
|
-
export function trackListToMicrodata(tracks) {
|
|
26
|
-
const { items } = prepareTracksList(tracks);
|
|
27
|
-
|
|
28
|
-
const content = items
|
|
29
|
-
.map((track) => {
|
|
30
|
-
return `${openTag("article", { itemtype: "Track", itemid: `#${track.id}` })}
|
|
31
|
-
${prop("h2", "name", track.name)}
|
|
32
|
-
</article>`;
|
|
33
|
-
})
|
|
34
|
-
.join("\n");
|
|
35
|
-
|
|
36
|
-
return htmlDocument(
|
|
37
|
-
"Tracks",
|
|
38
|
-
`<main>
|
|
39
|
-
<h1>Tracks</h1>
|
|
40
|
-
${content}
|
|
41
|
-
</main>`,
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Format track detail as microdata HTML
|
|
47
|
-
* @param {Object} track - Raw track entity
|
|
48
|
-
* @param {Object} context - Additional context
|
|
49
|
-
* @param {Array} context.skills - All skills
|
|
50
|
-
* @param {Array} context.behaviours - All behaviours
|
|
51
|
-
* @param {Array} context.disciplines - All disciplines
|
|
52
|
-
* @returns {string} HTML with microdata
|
|
53
|
-
*/
|
|
54
|
-
export function trackToMicrodata(track, { skills, behaviours, disciplines }) {
|
|
55
|
-
const view = prepareTrackDetail(track, { skills, behaviours, disciplines });
|
|
56
|
-
|
|
57
|
-
if (!view) return "";
|
|
58
|
-
|
|
59
|
-
const sections = [];
|
|
60
|
-
|
|
61
|
-
// Skill modifiers - using SkillModifier itemtype with targetCapability
|
|
62
|
-
if (view.skillModifiers.length > 0) {
|
|
63
|
-
const modifierItems = view.skillModifiers.map((m) => {
|
|
64
|
-
const modifierStr = m.modifier > 0 ? `+${m.modifier}` : `${m.modifier}`;
|
|
65
|
-
|
|
66
|
-
if (m.isCapability && m.skills && m.skills.length > 0) {
|
|
67
|
-
// Capability with expanded skills
|
|
68
|
-
const skillLinks = m.skills
|
|
69
|
-
.map(
|
|
70
|
-
(s) => `<a href="#${escapeHtml(s.id)}">${escapeHtml(s.name)}</a>`,
|
|
71
|
-
)
|
|
72
|
-
.join(", ");
|
|
73
|
-
return `${openTag("div", { itemtype: "SkillModifier", itemprop: "skillModifiers" })}
|
|
74
|
-
${linkTag("targetCapability", `#${m.id}`)}
|
|
75
|
-
<strong>${escapeHtml(m.name)} Capability</strong> (${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>)
|
|
76
|
-
<p>${skillLinks}</p>
|
|
77
|
-
</div>`;
|
|
78
|
-
} else {
|
|
79
|
-
// Individual skill or capability without skills
|
|
80
|
-
return `${openTag("span", { itemtype: "SkillModifier", itemprop: "skillModifiers" })}
|
|
81
|
-
${linkTag("targetCapability", `#${m.id}`)}
|
|
82
|
-
<strong>${escapeHtml(m.name)}</strong>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
|
|
83
|
-
</span>`;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
sections.push(section("Skill Modifiers", modifierItems.join("\n"), 2));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Behaviour modifiers - using BehaviourModifier itemtype
|
|
90
|
-
if (view.behaviourModifiers.length > 0) {
|
|
91
|
-
const modifierItems = view.behaviourModifiers.map((b) => {
|
|
92
|
-
const modifierStr = b.modifier > 0 ? `+${b.modifier}` : `${b.modifier}`;
|
|
93
|
-
return `${openTag("span", { itemtype: "BehaviourModifier", itemprop: "behaviourModifiers" })}
|
|
94
|
-
${linkTag("targetBehaviour", `#${b.id}`)}
|
|
95
|
-
<a href="#${escapeHtml(b.id)}">${escapeHtml(b.name)}</a>: ${openTag("span", { itemprop: "modifierValue" })}${modifierStr}</span>
|
|
96
|
-
</span>`;
|
|
97
|
-
});
|
|
98
|
-
sections.push(section("Behaviour Modifiers", ul(modifierItems), 2));
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const body = `<main>
|
|
102
|
-
${openTag("article", { itemtype: "Track", itemid: `#${view.id}` })}
|
|
103
|
-
${prop("h1", "name", view.name)}
|
|
104
|
-
${metaTag("id", view.id)}
|
|
105
|
-
${prop("p", "description", view.description)}
|
|
106
|
-
${sections.join("\n")}
|
|
107
|
-
</article>
|
|
108
|
-
</main>`;
|
|
109
|
-
|
|
110
|
-
return htmlDocument(view.name, body);
|
|
111
|
-
}
|