placementt-core 1.400.993 → 1.400.995
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/lib/callables/registry.d.ts +164 -28
- package/lib/callables/surveys.d.ts +92 -14
- package/lib/callables/surveys.js +43 -4
- package/lib/callables/surveys.js.map +1 -1
- package/lib/constants.d.ts +9 -0
- package/lib/constants.js +16 -1
- package/lib/constants.js.map +1 -1
- package/lib/portfolioAggregate.d.ts +1 -1
- package/lib/portfolioAggregate.js +8 -1
- package/lib/portfolioAggregate.js.map +1 -1
- package/lib/surveys/types.d.ts +72 -3
- package/lib/surveys/types.js +36 -1
- package/lib/surveys/types.js.map +1 -1
- package/lib/typeDefinitions.d.ts +28 -6
- package/package.json +1 -1
- package/src/callables/surveys.ts +44 -5
- package/src/constants.ts +16 -0
- package/src/portfolioAggregate.ts +10 -3
- package/src/surveys/types.ts +85 -3
- package/src/typeDefinitions.ts +28 -6
package/src/callables/surveys.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {z} from "zod";
|
|
2
2
|
import {CallableGroup} from "./types";
|
|
3
|
-
import {SURVEY_KINDS_LIST} from "../surveys/types";
|
|
3
|
+
import {SURVEY_COHORT_SCOPES, SURVEY_KINDS_LIST} from "../surveys/types";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Contracts for the unified survey engine
|
|
@@ -12,6 +12,7 @@ import {SURVEY_KINDS_LIST} from "../surveys/types";
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const surveyKind = z.enum(SURVEY_KINDS_LIST as [string, ...string[]]);
|
|
15
|
+
const cohortScope = z.enum(SURVEY_COHORT_SCOPES as [string, ...string[]]);
|
|
15
16
|
|
|
16
17
|
const raters = z.object({
|
|
17
18
|
students: z.boolean().optional(),
|
|
@@ -25,7 +26,7 @@ export const surveyCallables = {
|
|
|
25
26
|
oId: z.string(),
|
|
26
27
|
kind: surveyKind,
|
|
27
28
|
/** One cycle per year group. Omitted for institute-wide kinds. */
|
|
28
|
-
yearGroups: z.array(z.
|
|
29
|
+
yearGroups: z.array(z.string()).optional(),
|
|
29
30
|
scheduledSendDate: z.string(), // ISO
|
|
30
31
|
/** Overrides the year derived from the send date — the leaver thread's
|
|
31
32
|
* Christmas leg sends in the year after the cohort's own. */
|
|
@@ -49,10 +50,10 @@ export const surveyCallables = {
|
|
|
49
50
|
response: z.object({
|
|
50
51
|
created: z.array(z.object({
|
|
51
52
|
cycleId: z.string(),
|
|
52
|
-
yearGroup: z.
|
|
53
|
+
yearGroup: z.string().optional(),
|
|
53
54
|
invited: z.number(),
|
|
54
55
|
})),
|
|
55
|
-
skippedEmpty: z.array(z.
|
|
56
|
+
skippedEmpty: z.array(z.string()),
|
|
56
57
|
skippedNoEmail: z.number(),
|
|
57
58
|
academicYear: z.string(),
|
|
58
59
|
}),
|
|
@@ -65,7 +66,7 @@ export const surveyCallables = {
|
|
|
65
66
|
}),
|
|
66
67
|
response: z.object({
|
|
67
68
|
cycleId: z.string(),
|
|
68
|
-
yearGroup: z.
|
|
69
|
+
yearGroup: z.string().optional(),
|
|
69
70
|
invited: z.number(),
|
|
70
71
|
skippedNoEmail: z.number().optional(),
|
|
71
72
|
alreadyScheduled: z.boolean().optional(),
|
|
@@ -116,6 +117,44 @@ export const surveyCallables = {
|
|
|
116
117
|
request: z.object({cycleId: z.string(), jobId: z.string().optional()}),
|
|
117
118
|
response: z.object({deleted: z.number()}),
|
|
118
119
|
},
|
|
120
|
+
"surveys-saveSurveyCohortSetup": {
|
|
121
|
+
request: z.object({
|
|
122
|
+
oId: z.string(),
|
|
123
|
+
scope: cohortScope,
|
|
124
|
+
/** Year group ("9") or skills group id ("all", "yg9", a mapping id). */
|
|
125
|
+
cohort: z.string(),
|
|
126
|
+
/** JSON-serialised DataViewerSelectionBackend. */
|
|
127
|
+
studentSelection: z.string().optional(),
|
|
128
|
+
selectionSummary: z.string().optional(),
|
|
129
|
+
/** null clears the cohort's template and falls back to the default copy. */
|
|
130
|
+
emailTemplateId: z.string().nullable().optional(),
|
|
131
|
+
/** false → store the answer for future years only, leaving the cycles
|
|
132
|
+
* already provisioned alone. Defaults to true. */
|
|
133
|
+
applyToExisting: z.boolean().optional(),
|
|
134
|
+
}),
|
|
135
|
+
response: z.object({
|
|
136
|
+
saved: z.boolean(),
|
|
137
|
+
/** Cycles that picked the answer up, and how many people they now reach. */
|
|
138
|
+
updatedCycles: z.number(),
|
|
139
|
+
invited: z.number(),
|
|
140
|
+
}),
|
|
141
|
+
},
|
|
142
|
+
"surveys-provisionSurveysNow": {
|
|
143
|
+
request: z.object({
|
|
144
|
+
oId: z.string(),
|
|
145
|
+
/** Omit for every programme the institute runs. */
|
|
146
|
+
scope: cohortScope.optional(),
|
|
147
|
+
/** Provision the upcoming academic year rather than the current one. */
|
|
148
|
+
nextYear: z.boolean().optional(),
|
|
149
|
+
}),
|
|
150
|
+
response: z.object({
|
|
151
|
+
created: z.number(),
|
|
152
|
+
academicYear: z.string(),
|
|
153
|
+
/** True when the current year had nothing left to create and the next
|
|
154
|
+
* academic year was provisioned instead. */
|
|
155
|
+
rolledForward: z.boolean(),
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
119
158
|
"surveys-getMySurveyRequest": {
|
|
120
159
|
request: z.object({requestId: z.string()}),
|
|
121
160
|
// Shape varies by kind (see HYDRATE in surveys.ts), so left open.
|
package/src/constants.ts
CHANGED
|
@@ -980,6 +980,12 @@ export const emailTemplates:{[key: string]: EmailTemplateConfig} = {
|
|
|
980
980
|
button: {text: "Connect with Employers", link: "/login"},
|
|
981
981
|
sections: ["employerDatabaseSummarySection"],
|
|
982
982
|
},
|
|
983
|
+
skillsInvite: {
|
|
984
|
+
description: "Sent to students, parents and employers inviting them to complete a skills assessment.",
|
|
985
|
+
params: ["studentForename", "instituteName"],
|
|
986
|
+
type: "skillsInvite",
|
|
987
|
+
button: {text: "Complete Assessment", link: "/institutes/surveys/skills/{{requestId}}"},
|
|
988
|
+
},
|
|
983
989
|
aspirationInvite: {
|
|
984
990
|
description: "Sent to students inviting them to complete the career aspirations survey.",
|
|
985
991
|
params: ["studentForename", "instituteName"],
|
|
@@ -1513,3 +1519,13 @@ export const JOB_VALUES: string[] = [
|
|
|
1513
1519
|
"Working with technology",
|
|
1514
1520
|
];
|
|
1515
1521
|
|
|
1522
|
+
|
|
1523
|
+
/**
|
|
1524
|
+
* The `UserData.details` key holding a student's year group tag.
|
|
1525
|
+
*
|
|
1526
|
+
* A defined standard key rather than one of the dynamic upload columns: the Wonde
|
|
1527
|
+
* sync writes it, the manual spreadsheet upload always offers it, and every survey's
|
|
1528
|
+
* recipient filter resolves a cohort by matching `details.year` against a tag from
|
|
1529
|
+
* `InstituteData.yearGroups`. The tag is opaque — matched as a string, never parsed.
|
|
1530
|
+
*/
|
|
1531
|
+
export const STUDENT_YEAR_FIELD = "year";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
StudentPlacementData,
|
|
22
22
|
UserData,
|
|
23
23
|
} from "./typeDefinitions";
|
|
24
|
-
import {
|
|
24
|
+
import {skillColour} from "./constants";
|
|
25
25
|
|
|
26
26
|
// ── Brand ────────────────────────────────────────────────────────────────────
|
|
27
27
|
// The portfolio uses the CareerThread platform palette, which is distinct from
|
|
@@ -120,7 +120,10 @@ export const ACTIVITY_TYPE_TO_PFA: Record<StudentActivity["activityType"], PfaPa
|
|
|
120
120
|
// Self-Management would naturally sit under Independent living and Attitude &
|
|
121
121
|
// Resilience under Good health, but those pathways aren't rendered yet, so they
|
|
122
122
|
// are folded into the two reported ones so no skill silently disappears.
|
|
123
|
-
|
|
123
|
+
// Categories a framework brings with it are mapped here too — anything unmapped
|
|
124
|
+
// (e.g. a school's own category) falls back to PFA_DEFAULT_PATHWAY.
|
|
125
|
+
export const SKILL_CATEGORY_TO_PFA: Record<string, PfaPathway> = {
|
|
126
|
+
// Platform categories.
|
|
124
127
|
["Communication"]: "communityInclusion",
|
|
125
128
|
["Teamwork"]: "communityInclusion",
|
|
126
129
|
["Self-Management"]: "employmentAndFurtherLearning",
|
|
@@ -128,6 +131,10 @@ export const SKILL_CATEGORY_TO_PFA: Record<SkillLabel["category"], PfaPathway> =
|
|
|
128
131
|
["Attitude & Resilience"]: "communityInclusion",
|
|
129
132
|
["Work Readiness"]: "employmentAndFurtherLearning",
|
|
130
133
|
["Career Development"]: "employmentAndFurtherLearning",
|
|
134
|
+
// SDS meta-skills wheel pillars ("Self-Management" is shared with the platform
|
|
135
|
+
// set above and maps the same way).
|
|
136
|
+
["Social Intelligence"]: "communityInclusion",
|
|
137
|
+
["Innovation"]: "employmentAndFurtherLearning",
|
|
131
138
|
};
|
|
132
139
|
|
|
133
140
|
// ── Aggregate output shape ────────────────────────────────────────────────────
|
|
@@ -316,7 +323,7 @@ function buildSkill(
|
|
|
316
323
|
skillId,
|
|
317
324
|
label: label.label,
|
|
318
325
|
category: label.category,
|
|
319
|
-
color:
|
|
326
|
+
color: skillColour(label),
|
|
320
327
|
latestScore: latestStudent,
|
|
321
328
|
firstScore,
|
|
322
329
|
distanceTravelled: Math.round((latestStudent - firstScore) * 100) / 100,
|
package/src/surveys/types.ts
CHANGED
|
@@ -94,8 +94,9 @@ export type SurveyCycle = {
|
|
|
94
94
|
* Legacy leaverGroups used the slash form ("2026/27") and are converted on migration. */
|
|
95
95
|
academicYear: string,
|
|
96
96
|
/** The single year group this cycle targets. Absent for alumni check-in cycles,
|
|
97
|
-
* which are institute-wide. Replaces AspirationCycle.yearGroupsIncluded.
|
|
98
|
-
|
|
97
|
+
* which are institute-wide. Replaces AspirationCycle.yearGroupsIncluded. Opaque:
|
|
98
|
+
* whatever tag the school's import wrote to details.year, matched as a string. */
|
|
99
|
+
yearGroup?: string,
|
|
99
100
|
/** SkillsMapping.id ("all" for the All students group) — skills cycles only. */
|
|
100
101
|
groupId?: string,
|
|
101
102
|
/** Chains the cycles of one journey: the three leaver kinds for a cohort share
|
|
@@ -186,7 +187,7 @@ export type SurveyRequest = {
|
|
|
186
187
|
email: string,
|
|
187
188
|
/** Post-leaving contact address; where invites go once the school email dies. */
|
|
188
189
|
personalEmail?: string,
|
|
189
|
-
yearGroup?:
|
|
190
|
+
yearGroup?: string,
|
|
190
191
|
status: SurveyRequestStatus,
|
|
191
192
|
completed: boolean,
|
|
192
193
|
/** ISO — when the invite was emailed. */
|
|
@@ -221,3 +222,84 @@ export type SurveyRequest = {
|
|
|
221
222
|
parentToken?: string,
|
|
222
223
|
parentTokenExpiry?: string,
|
|
223
224
|
};
|
|
225
|
+
|
|
226
|
+
// ─── Per-cohort setup ─────────────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The two questions a school can only answer once per cohort — who is in it, and
|
|
230
|
+
* which email its invitations use.
|
|
231
|
+
*
|
|
232
|
+
* A survey's SCHEDULE holds for every year to come, so setup asks for it once. Its
|
|
233
|
+
* COHORT does not: no school's student records agree on how a year group is written,
|
|
234
|
+
* so provisioning cannot resolve "Year 9" on its own, and until someone says who
|
|
235
|
+
* Year 9 is, that year group's surveys have nobody to send to. Rather than ask again
|
|
236
|
+
* every time a survey is provisioned, the answer is stored here and every later
|
|
237
|
+
* survey for that cohort inherits it — including next year's, whose cohort is the
|
|
238
|
+
* same query against a new set of students.
|
|
239
|
+
*
|
|
240
|
+
* Stored on institutes/{oId}.surveyCohorts, keyed by surveyCohortKey().
|
|
241
|
+
*/
|
|
242
|
+
export type SurveyCohortSetup = {
|
|
243
|
+
/** JSON-serialised DataViewerSelectionBackend — a FILTER, not a frozen list, so
|
|
244
|
+
* it re-resolves against whoever is in the year group at each send. */
|
|
245
|
+
studentSelection?: string,
|
|
246
|
+
/** Human summary of the selection ("All students matching Year group: 9"), for
|
|
247
|
+
* the setup screens — the JSON is not readable enough to show staff. */
|
|
248
|
+
selectionSummary?: string,
|
|
249
|
+
/** How many students it resolved to when it was saved. Indicative only. */
|
|
250
|
+
resolvedCount?: number,
|
|
251
|
+
/** emailTemplates doc id used for this cohort's invitations. Absent = the
|
|
252
|
+
* platform's default copy for the kind. */
|
|
253
|
+
emailTemplateId?: string,
|
|
254
|
+
savedAt?: string,
|
|
255
|
+
/** uid of the staff member who answered. */
|
|
256
|
+
savedBy?: string,
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* A cohort's scope. Kinds that share a cohort share a scope, so answering once for
|
|
261
|
+
* Year 11 leavers covers all three legs of their journey.
|
|
262
|
+
*/
|
|
263
|
+
export type SurveyCohortScope = "aspirations"|"leavers"|"skills";
|
|
264
|
+
|
|
265
|
+
/** Every scope as a runtime array — for validation. */
|
|
266
|
+
export const SURVEY_COHORT_SCOPES: SurveyCohortScope[] = ["aspirations", "leavers", "skills"];
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* The key one cohort's setup is stored under: the scope plus the cohort within it —
|
|
270
|
+
* a year group for aspirations and leavers ("aspirations_y9"), a skills group id for
|
|
271
|
+
* skills ("skills_all", "skills_yg9", "skills_<mappingId>").
|
|
272
|
+
*
|
|
273
|
+
* @param {SurveyCohortScope} scope which programme the cohort belongs to.
|
|
274
|
+
* @param {string} cohort the year group tag, or the skills group id.
|
|
275
|
+
* @return {string} the storage key.
|
|
276
|
+
*/
|
|
277
|
+
export const surveyCohortKey = (scope: SurveyCohortScope, cohort: string): string =>
|
|
278
|
+
`${scope}_${surveyCohortSlug(cohort)}`;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* A year group tag reduced to characters that are safe in a Firestore field path and
|
|
282
|
+
* document id.
|
|
283
|
+
*
|
|
284
|
+
* A tag is whatever the school's MIS calls it — "9", "Year 9", "Yr 9 (KS4)" — and it
|
|
285
|
+
* is never parsed or renumbered. But a key segment can't hold a slash or a dot, so
|
|
286
|
+
* anything outside [A-Za-z0-9_-] collapses to a hyphen for id purposes only. The tag
|
|
287
|
+
* itself is stored and matched verbatim.
|
|
288
|
+
*
|
|
289
|
+
* @param {string} tag the year group tag or group id.
|
|
290
|
+
* @return {string} the tag as a key segment.
|
|
291
|
+
*/
|
|
292
|
+
export const surveyCohortSlug = (tag: string): string =>
|
|
293
|
+
String(tag).trim().replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
294
|
+
|
|
295
|
+
/** The scope a survey kind's cohorts are keyed under. */
|
|
296
|
+
export const SURVEY_KIND_COHORT_SCOPE: {[k in SurveyKind]?: SurveyCohortScope} = {
|
|
297
|
+
aspirations: "aspirations",
|
|
298
|
+
skills: "skills",
|
|
299
|
+
leaverIntentions: "leavers",
|
|
300
|
+
leaverResults: "leavers",
|
|
301
|
+
leaverChristmas: "leavers",
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/** Every cohort's setup, as stored on the institute. */
|
|
305
|
+
export type SurveyCohortSetupMap = {[key: string]: SurveyCohortSetup};
|
package/src/typeDefinitions.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {DocumentData, DocumentReference, OrderByDirection, Timestamp, WhereFilterOp} from "firebase/firestore";
|
|
2
2
|
import {Descendant} from "slate";
|
|
3
|
-
import {emailTemplates
|
|
3
|
+
import {emailTemplates} from "./constants";
|
|
4
4
|
import type {ChecklistItem, GiftHistory} from "./customerCareConfig";
|
|
5
|
-
import type {SurveyKind, SurveySlot} from "./surveys/types";
|
|
5
|
+
import type {SurveyCohortSetupMap, SurveyKind, SurveySlot} from "./surveys/types";
|
|
6
6
|
|
|
7
7
|
export type FilterObject = {
|
|
8
8
|
[key: string]: {
|
|
@@ -847,6 +847,12 @@ export type InstituteData = {
|
|
|
847
847
|
students: number,
|
|
848
848
|
staff: number,
|
|
849
849
|
studentsFields: string[],
|
|
850
|
+
/** The year group tags present in this school's student records, in order, as
|
|
851
|
+
* written by whichever importer put them there (the Wonde sync, or a manual
|
|
852
|
+
* spreadsheet upload). Opaque strings — a tag is whatever the school calls it.
|
|
853
|
+
* Every year-group picker offers this list, and a selected tag resolves students
|
|
854
|
+
* by an exact string match on details.year. */
|
|
855
|
+
yearGroups?: string[],
|
|
850
856
|
staffFields: string[],
|
|
851
857
|
employerFields?: string[],
|
|
852
858
|
studentsActive: number,
|
|
@@ -934,6 +940,10 @@ export type InstituteData = {
|
|
|
934
940
|
// Written by the setup wizard (aspirations-saveDestinationsConfig); read by
|
|
935
941
|
// destinationsProvisionCron each September to create that year's surveys/groups.
|
|
936
942
|
destinationsConfig?: DestinationsConfig,
|
|
943
|
+
/** Who is in each survey cohort, and which email template it uses — the two
|
|
944
|
+
* answers a schedule can't supply on its own. Keyed by surveyCohortKey(); read
|
|
945
|
+
* by provisioning so every year's surveys inherit the answer. */
|
|
946
|
+
surveyCohorts?: SurveyCohortSetupMap,
|
|
937
947
|
acceptingAlumni?: boolean,
|
|
938
948
|
approveAlumni?: boolean,
|
|
939
949
|
shareAlumni?: boolean
|
|
@@ -976,6 +986,11 @@ export type InstituteData = {
|
|
|
976
986
|
/** Annual survey schedule for the default "All students" group. Auto-provisioned
|
|
977
987
|
* into each academic year by the daily cron. */
|
|
978
988
|
skillsSurveys?: SkillsSurveySlot[],
|
|
989
|
+
/** Which of the school's year group tags (InstituteData.yearGroups) take part in
|
|
990
|
+
* skills surveys. Provisioning creates one cycle per tag from `skillsSurveys`, so
|
|
991
|
+
* staff chase Year 9 separately from Year 10. Empty/absent = one survey for
|
|
992
|
+
* everyone. Tags are matched against details.year, never parsed. */
|
|
993
|
+
skillsYearGroups?: string[],
|
|
979
994
|
scheduledSkillsAssessments?: {
|
|
980
995
|
[key in "yearStart" | "yearEnd" | "eachTerm" | "preWorkExperience" | "postWorkExperience" | "employerInteraction" | "extracurricularActivity"]?: {
|
|
981
996
|
students?: boolean,
|
|
@@ -1532,7 +1547,7 @@ export type EmailTemplateConfig = {
|
|
|
1532
1547
|
button?: {text: string, link: string},
|
|
1533
1548
|
secondaryButton?: {text: string, link: string},
|
|
1534
1549
|
optionalButtons?: {text: string, link: string}[],
|
|
1535
|
-
type: "workflow"|"feedback"|"event"|"campaign"|"database"|"onboarding"|"aspirationInvite"|"leaverInvite"|"leaverResultsInvite"|"leaverChristmasInvite",
|
|
1550
|
+
type: "workflow"|"feedback"|"event"|"campaign"|"database"|"onboarding"|"skillsInvite"|"aspirationInvite"|"leaverInvite"|"leaverResultsInvite"|"leaverChristmasInvite",
|
|
1536
1551
|
sections?: EmailSectionNames[],
|
|
1537
1552
|
}
|
|
1538
1553
|
|
|
@@ -2053,7 +2068,8 @@ export type DestinationsConfig = {
|
|
|
2053
2068
|
// ── Aspirations (Form A) ──
|
|
2054
2069
|
aspirations: {
|
|
2055
2070
|
enabled: boolean,
|
|
2056
|
-
yearGroups
|
|
2071
|
+
/** Year group tags from InstituteData.yearGroups. Matched, never parsed. */
|
|
2072
|
+
yearGroups: string[],
|
|
2057
2073
|
/** Shared schedule applied to every selected year group. Any number of sends
|
|
2058
2074
|
* per year — a school running one a term is as valid as one a year. */
|
|
2059
2075
|
surveys: AspirationSurveySlot[],
|
|
@@ -2067,7 +2083,8 @@ export type DestinationsConfig = {
|
|
|
2067
2083
|
// ── Leavers (Form B + checkpoints) ──
|
|
2068
2084
|
leavers: {
|
|
2069
2085
|
enabled: boolean,
|
|
2070
|
-
yearGroups
|
|
2086
|
+
/** Year group tags from InstituteData.yearGroups. Matched, never parsed. */
|
|
2087
|
+
yearGroups: string[],
|
|
2071
2088
|
// Each leg is scheduled by relative rule with the MM-DD as its materialised
|
|
2072
2089
|
// fallback, the same pair aspiration and skills slots use.
|
|
2073
2090
|
intentionsSendMMDD: string, // Form B, before they leave — default "06-15"
|
|
@@ -2872,7 +2889,12 @@ export type SkillsFrameworkKey = "custom" | "skillsBuilder" | "sdsMetaskills";
|
|
|
2872
2889
|
export type SkillLabel = {
|
|
2873
2890
|
label: string,
|
|
2874
2891
|
description: string,
|
|
2875
|
-
|
|
2892
|
+
/** The group this skill sits under, stored on the document — pickers and reports
|
|
2893
|
+
* head skills by it. A framework brings its own set (the SDS meta-skills wheel has
|
|
2894
|
+
* exactly three: Self-Management, Social Intelligence, Innovation); generic and
|
|
2895
|
+
* school-created labels use the platform set in `skillLabelColours`. Free text, so
|
|
2896
|
+
* a framework is never forced into the platform categories. */
|
|
2897
|
+
category: string,
|
|
2876
2898
|
/** Display colour stored on the document. Seeded via the admin
|
|
2877
2899
|
* "Seed skill labels & colours" action (framework labels carry their own
|
|
2878
2900
|
* scheme, e.g. the SDS meta-skills wheel pillars). Read via skillColour(),
|