placementt-core 1.400.1064 → 1.400.1065

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.
@@ -1958,8 +1958,23 @@ export type ScheduledVisit = {
1958
1958
  /** Snapshotted off `forms` when assigned, so editing the form later never
1959
1959
  * rewrites the questions on checks already answered. */
1960
1960
  formSchema?: CustomFormSchema;
1961
- /** Raised by the assignee on completion when something needs escalating. */
1961
+ /** LEGACY. Escalation flags now live on the check's private subdocument see
1962
+ * ScheduledCheckPrivate. Still declared because docs written before the split
1963
+ * carry it and the backfill has to read it; nothing writes it any more.
1964
+ *
1965
+ * It moved because Firestore rules are document-level: a guidance check is read
1966
+ * by the pupil it is about, and a pupil learning that staff flagged a concern
1967
+ * about them, with no context, is the harm the flag exists to avoid. Filtering
1968
+ * it out in the component hides it visually and nothing else. */
1962
1969
  concern?: boolean;
1970
+ /** Denormalised "a pupil may read this check".
1971
+ *
1972
+ * True only on careersGuidance checks. It exists because the rule that lets a
1973
+ * pupil read their own 1:1s cannot tell a guidance check from a leaver check
1974
+ * without it — and a blanket "read your own checks" rule would hand a SEND
1975
+ * leaver the pastoral notes on their own transition calls. The client query
1976
+ * carries the same filter, because Firestore requires it to. */
1977
+ studentVisible?: boolean;
1963
1978
  /** The slot's booking window, on checks generated from a window-mode guidance
1964
1979
  * slot. Absent on fixed-date and ad-hoc checks, which are booked from birth.
1965
1980
  *
@@ -1996,6 +2011,31 @@ export type ScheduledVisit = {
1996
2011
  /** Stamped when a check passes the grace period unanswered. */
1997
2012
  missedAt?: string;
1998
2013
  };
2014
+ /** The subcollection and document id both private subdocuments use. One name, in
2015
+ * core, so the web client and the backend cannot address different documents.
2016
+ *
2017
+ * `<parent>/{id}/private/staff` — staff-only fields split out of a document a pupil
2018
+ * is allowed to read. Firestore has no field-level read, so the only way to withhold
2019
+ * a field from a reader who may have the document is to put it in another one. */
2020
+ export declare const PRIVATE_SUBCOLLECTION = "private";
2021
+ export declare const PRIVATE_STAFF_DOC = "staff";
2022
+ /** Staff-only fields belonging to a scheduledChecks doc. See PRIVATE_SUBCOLLECTION. */
2023
+ export type ScheduledCheckPrivate = {
2024
+ /** Raised by the assignee on completion when something needs escalating. */
2025
+ concern?: boolean;
2026
+ /** The series the parent check belongs to, copied in so a cycle's concern count
2027
+ * is one collection-group query rather than a read per completed check. */
2028
+ seriesId?: string;
2029
+ oId: string;
2030
+ };
2031
+ /** Staff-only fields belonging to a careersRecord doc. See PRIVATE_SUBCOLLECTION. */
2032
+ export type CareersRecordPrivate = {
2033
+ /** Never shown to the pupil. The safeguarding-adjacent context a member of staff
2034
+ * writes about a meeting, kept in its own document so the entry the pupil reads
2035
+ * cannot carry it. */
2036
+ staffNotes?: string;
2037
+ oId: string;
2038
+ };
1999
2039
  /**
2000
2040
  * One dated 1:1 in a school's careers programme — "Year 11 get an options
2001
2041
  * conversation in October and a personal guidance meeting in February".
@@ -2021,6 +2061,14 @@ export type GuidanceSlot = SurveySlot & {
2021
2061
  /** Window mode only: how long the window runs from the slot's resolved date.
2022
2062
  * Absent = up to the next slot. */
2023
2063
  windowDays?: number;
2064
+ /** Whether the PUPIL is emailed before their booked meeting, overriding the
2065
+ * school-wide GuidanceConfig.remindPupils for this slot. Per slot because the
2066
+ * answer genuinely differs: a booked personal guidance meeting is an appointment
2067
+ * the pupil is expected to turn up to, and a whole-cohort assembly is not.
2068
+ *
2069
+ * Absent falls back to the school's setting, which is itself off by default —
2070
+ * see guidanceRemindsPupil. */
2071
+ remindPupil?: boolean;
2024
2072
  };
2025
2073
  /**
2026
2074
  * A named cohort inside the guidance programme, with its own slots — "pupil premium
@@ -2064,6 +2112,11 @@ export type GuidanceConfig = {
2064
2112
  };
2065
2113
  /** Named filtered cohorts whose slots supersede the year group's — see GuidanceGroup. */
2066
2114
  groups?: GuidanceGroup[];
2115
+ /** Whether pupils are emailed before their booked 1:1s, school-wide. Off unless
2116
+ * a careers leader turns it on: emailing a cohort of children is a decision for
2117
+ * the school, not something that should arrive in a deploy. Individual slots may
2118
+ * override it — see GuidanceSlot.remindPupil. */
2119
+ remindPupils?: boolean;
2067
2120
  setupCompletedAt?: string;
2068
2121
  setupBy?: string;
2069
2122
  updatedAt?: string;
@@ -3878,7 +3931,13 @@ export type CareersRecordEntry = {
3878
3931
  durationMins?: number;
3879
3932
  /** Shared with the pupil. BM3 expects pupils to be able to access their record. */
3880
3933
  summary: string;
3881
- /** Never shown to the pupil. Kept separate so the summary can be surfaced safely. */
3934
+ /** LEGACY. Staff notes now live on the entry's private subdocument see
3935
+ * CareersRecordPrivate. Still declared because entries written before the split
3936
+ * carry it, the backfill has to read it, and the Compass exports assert it never
3937
+ * leaves the platform.
3938
+ *
3939
+ * It moved because the pupil reads this document. Keeping the notes out of the
3940
+ * component was never enough: the whole doc reaches the browser. */
3882
3941
  staffNotes?: string;
3883
3942
  /** "Any subsequent agreed decisions" — named in the benchmark, so evidence in its own right. */
3884
3943
  actions?: CareersRecordAction[];
@@ -3912,6 +3971,20 @@ export declare const CAREERS_RECORD_MODE_LABELS: Record<CareersRecordEntry["mode
3912
3971
  * @return {CareersRecordEntry["mode"]} the record's mode.
3913
3972
  */
3914
3973
  export declare function checkTypeToRecordMode(checkType: ScheduledVisit["checkType"]): CareersRecordEntry["mode"];
3974
+ /**
3975
+ * Whether the pupil is emailed before a 1:1 from this slot.
3976
+ *
3977
+ * The slot has the final say and the school's setting is the default, so a school can
3978
+ * turn the whole thing on and still leave the assembly silent. Both absent means no:
3979
+ * pupil email is opt-in, because the reminders this feature is built on were written
3980
+ * for staff and a school running the programme has not thereby agreed to email its
3981
+ * children.
3982
+ *
3983
+ * @param {Pick<GuidanceSlot, "remindPupil">} [slot] the slot the check came from.
3984
+ * @param {Pick<GuidanceConfig, "remindPupils">} [config] the school's guidance setup.
3985
+ * @return {boolean} whether to email the pupil.
3986
+ */
3987
+ export declare function guidanceRemindsPupil(slot?: Pick<GuidanceSlot, "remindPupil">, config?: Pick<GuidanceConfig, "remindPupils">): boolean;
3915
3988
  /**
3916
3989
  * Who a check is about, off the check itself.
3917
3990
  *
@@ -1,8 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CAREERS_RECORD_MODE_LABELS = exports.CAREERS_RECORD_TYPE_LABELS = void 0;
3
+ exports.CAREERS_RECORD_MODE_LABELS = exports.CAREERS_RECORD_TYPE_LABELS = exports.PRIVATE_STAFF_DOC = exports.PRIVATE_SUBCOLLECTION = void 0;
4
4
  exports.checkTypeToRecordMode = checkTypeToRecordMode;
5
+ exports.guidanceRemindsPupil = guidanceRemindsPupil;
5
6
  exports.checkTargetName = checkTargetName;
7
+ /** The subcollection and document id both private subdocuments use. One name, in
8
+ * core, so the web client and the backend cannot address different documents.
9
+ *
10
+ * `<parent>/{id}/private/staff` — staff-only fields split out of a document a pupil
11
+ * is allowed to read. Firestore has no field-level read, so the only way to withhold
12
+ * a field from a reader who may have the document is to put it in another one. */
13
+ exports.PRIVATE_SUBCOLLECTION = "private";
14
+ exports.PRIVATE_STAFF_DOC = "staff";
6
15
  /** Staff-facing labels for each entry type. In core because the web form, the setup
7
16
  * wizard and the backend CSV export all name them and must not drift. */
8
17
  exports.CAREERS_RECORD_TYPE_LABELS = {
@@ -44,6 +53,22 @@ function checkTypeToRecordMode(checkType) {
44
53
  default: return "inPerson";
45
54
  }
46
55
  }
56
+ /**
57
+ * Whether the pupil is emailed before a 1:1 from this slot.
58
+ *
59
+ * The slot has the final say and the school's setting is the default, so a school can
60
+ * turn the whole thing on and still leave the assembly silent. Both absent means no:
61
+ * pupil email is opt-in, because the reminders this feature is built on were written
62
+ * for staff and a school running the programme has not thereby agreed to email its
63
+ * children.
64
+ *
65
+ * @param {Pick<GuidanceSlot, "remindPupil">} [slot] the slot the check came from.
66
+ * @param {Pick<GuidanceConfig, "remindPupils">} [config] the school's guidance setup.
67
+ * @return {boolean} whether to email the pupil.
68
+ */
69
+ function guidanceRemindsPupil(slot, config) {
70
+ return slot?.remindPupil ?? config?.remindPupils ?? false;
71
+ }
47
72
  /**
48
73
  * Who a check is about, off the check itself.
49
74
  *
@@ -1 +1 @@
1
- {"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AAs7HA,sDASC;AAaD,0CAEC;AAzDD;yEACyE;AAC5D,QAAA,0BAA0B,GAA2C;IAC9E,gBAAgB,EAAE,2BAA2B;IAC7C,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,iBAAiB;IACjC,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,oCAAoC;IAChD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,wBAAwB;IACvC,KAAK,EAAE,OAAO;CACjB,CAAC;AAEW,QAAA,0BAA0B,GAA+C;IAClF,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,SAAgB,qBAAqB,CAAC,SAAsC;IACxE,QAAQ,SAAS,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC;QAC5B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,OAAO,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,eAAe,CAAC,KAA6D;IACzF,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
1
+ {"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AAs/HA,sDASC;AAeD,oDAKC;AAaD,0CAEC;AA5kED;;;;;kFAKkF;AACrE,QAAA,qBAAqB,GAAG,SAAS,CAAC;AAClC,QAAA,iBAAiB,GAAG,OAAO,CAAC;AAw/DzC;yEACyE;AAC5D,QAAA,0BAA0B,GAA2C;IAC9E,gBAAgB,EAAE,2BAA2B;IAC7C,mBAAmB,EAAE,sBAAsB;IAC3C,cAAc,EAAE,iBAAiB;IACjC,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,oCAAoC;IAChD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,wBAAwB;IACvC,KAAK,EAAE,OAAO;CACjB,CAAC;AAEW,QAAA,0BAA0B,GAA+C;IAClF,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,YAAY;IACnB,KAAK,EAAE,OAAO;CACjB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,SAAgB,qBAAqB,CAAC,SAAsC;IACxE,QAAQ,SAAS,EAAE,CAAC;QACpB,KAAK,MAAM,CAAC,CAAC,OAAO,OAAO,CAAC;QAC5B,KAAK,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC;QAC7B,KAAK,WAAW,CAAC;QACjB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;QACb,OAAO,CAAC,CAAC,OAAO,UAAU,CAAC;IAC3B,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAgB,oBAAoB,CAChC,IAAwC,EACxC,MAA6C;IAE7C,OAAO,IAAI,EAAE,WAAW,IAAI,MAAM,EAAE,YAAY,IAAI,KAAK,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,eAAe,CAAC,KAA6D;IACzF,OAAO,GAAG,KAAK,CAAC,cAAc,IAAI,EAAE,IAAI,KAAK,CAAC,aAAa,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/E,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "private": false,
3
3
  "name": "placementt-core",
4
4
  "author": "Placementt",
5
- "version": "1.400.1064",
5
+ "version": "1.400.1065",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
8
8
  "scripts": {
@@ -1946,9 +1946,25 @@ export type ScheduledVisit = {
1946
1946
  /** Snapshotted off `forms` when assigned, so editing the form later never
1947
1947
  * rewrites the questions on checks already answered. */
1948
1948
  formSchema?: CustomFormSchema,
1949
- /** Raised by the assignee on completion when something needs escalating. */
1949
+ /** LEGACY. Escalation flags now live on the check's private subdocument see
1950
+ * ScheduledCheckPrivate. Still declared because docs written before the split
1951
+ * carry it and the backfill has to read it; nothing writes it any more.
1952
+ *
1953
+ * It moved because Firestore rules are document-level: a guidance check is read
1954
+ * by the pupil it is about, and a pupil learning that staff flagged a concern
1955
+ * about them, with no context, is the harm the flag exists to avoid. Filtering
1956
+ * it out in the component hides it visually and nothing else. */
1950
1957
  concern?: boolean,
1951
1958
 
1959
+ /** Denormalised "a pupil may read this check".
1960
+ *
1961
+ * True only on careersGuidance checks. It exists because the rule that lets a
1962
+ * pupil read their own 1:1s cannot tell a guidance check from a leaver check
1963
+ * without it — and a blanket "read your own checks" rule would hand a SEND
1964
+ * leaver the pastoral notes on their own transition calls. The client query
1965
+ * carries the same filter, because Firestore requires it to. */
1966
+ studentVisible?: boolean,
1967
+
1952
1968
  // ── Booking (D9) ──
1953
1969
  /** The slot's booking window, on checks generated from a window-mode guidance
1954
1970
  * slot. Absent on fixed-date and ad-hoc checks, which are booked from birth.
@@ -1988,6 +2004,34 @@ export type ScheduledVisit = {
1988
2004
  missedAt?: string,
1989
2005
  }
1990
2006
 
2007
+ /** The subcollection and document id both private subdocuments use. One name, in
2008
+ * core, so the web client and the backend cannot address different documents.
2009
+ *
2010
+ * `<parent>/{id}/private/staff` — staff-only fields split out of a document a pupil
2011
+ * is allowed to read. Firestore has no field-level read, so the only way to withhold
2012
+ * a field from a reader who may have the document is to put it in another one. */
2013
+ export const PRIVATE_SUBCOLLECTION = "private";
2014
+ export const PRIVATE_STAFF_DOC = "staff";
2015
+
2016
+ /** Staff-only fields belonging to a scheduledChecks doc. See PRIVATE_SUBCOLLECTION. */
2017
+ export type ScheduledCheckPrivate = {
2018
+ /** Raised by the assignee on completion when something needs escalating. */
2019
+ concern?: boolean,
2020
+ /** The series the parent check belongs to, copied in so a cycle's concern count
2021
+ * is one collection-group query rather than a read per completed check. */
2022
+ seriesId?: string,
2023
+ oId: string,
2024
+ };
2025
+
2026
+ /** Staff-only fields belonging to a careersRecord doc. See PRIVATE_SUBCOLLECTION. */
2027
+ export type CareersRecordPrivate = {
2028
+ /** Never shown to the pupil. The safeguarding-adjacent context a member of staff
2029
+ * writes about a meeting, kept in its own document so the entry the pupil reads
2030
+ * cannot carry it. */
2031
+ staffNotes?: string,
2032
+ oId: string,
2033
+ };
2034
+
1991
2035
  /**
1992
2036
  * One dated 1:1 in a school's careers programme — "Year 11 get an options
1993
2037
  * conversation in October and a personal guidance meeting in February".
@@ -2014,6 +2058,15 @@ export type GuidanceSlot = SurveySlot & {
2014
2058
  /** Window mode only: how long the window runs from the slot's resolved date.
2015
2059
  * Absent = up to the next slot. */
2016
2060
  windowDays?: number,
2061
+
2062
+ /** Whether the PUPIL is emailed before their booked meeting, overriding the
2063
+ * school-wide GuidanceConfig.remindPupils for this slot. Per slot because the
2064
+ * answer genuinely differs: a booked personal guidance meeting is an appointment
2065
+ * the pupil is expected to turn up to, and a whole-cohort assembly is not.
2066
+ *
2067
+ * Absent falls back to the school's setting, which is itself off by default —
2068
+ * see guidanceRemindsPupil. */
2069
+ remindPupil?: boolean,
2017
2070
  };
2018
2071
 
2019
2072
  /**
@@ -2055,6 +2108,11 @@ export type GuidanceConfig = {
2055
2108
  slotsByYearGroup?: {[yearGroup: string]: GuidanceSlot[]},
2056
2109
  /** Named filtered cohorts whose slots supersede the year group's — see GuidanceGroup. */
2057
2110
  groups?: GuidanceGroup[],
2111
+ /** Whether pupils are emailed before their booked 1:1s, school-wide. Off unless
2112
+ * a careers leader turns it on: emailing a cohort of children is a decision for
2113
+ * the school, not something that should arrive in a deploy. Individual slots may
2114
+ * override it — see GuidanceSlot.remindPupil. */
2115
+ remindPupils?: boolean,
2058
2116
  setupCompletedAt?: string, // ISO — stamped server-side on first save
2059
2117
  setupBy?: string, // staff uid — stamped server-side
2060
2118
  updatedAt?: string,
@@ -3967,7 +4025,13 @@ export type CareersRecordEntry = {
3967
4025
  /** Shared with the pupil. BM3 expects pupils to be able to access their record. */
3968
4026
  summary: string,
3969
4027
 
3970
- /** Never shown to the pupil. Kept separate so the summary can be surfaced safely. */
4028
+ /** LEGACY. Staff notes now live on the entry's private subdocument see
4029
+ * CareersRecordPrivate. Still declared because entries written before the split
4030
+ * carry it, the backfill has to read it, and the Compass exports assert it never
4031
+ * leaves the platform.
4032
+ *
4033
+ * It moved because the pupil reads this document. Keeping the notes out of the
4034
+ * component was never enough: the whole doc reaches the browser. */
3971
4035
  staffNotes?: string,
3972
4036
 
3973
4037
  /** "Any subsequent agreed decisions" — named in the benchmark, so evidence in its own right. */
@@ -4031,6 +4095,26 @@ export function checkTypeToRecordMode(checkType: ScheduledVisit["checkType"]): C
4031
4095
  }
4032
4096
  }
4033
4097
 
4098
+ /**
4099
+ * Whether the pupil is emailed before a 1:1 from this slot.
4100
+ *
4101
+ * The slot has the final say and the school's setting is the default, so a school can
4102
+ * turn the whole thing on and still leave the assembly silent. Both absent means no:
4103
+ * pupil email is opt-in, because the reminders this feature is built on were written
4104
+ * for staff and a school running the programme has not thereby agreed to email its
4105
+ * children.
4106
+ *
4107
+ * @param {Pick<GuidanceSlot, "remindPupil">} [slot] the slot the check came from.
4108
+ * @param {Pick<GuidanceConfig, "remindPupils">} [config] the school's guidance setup.
4109
+ * @return {boolean} whether to email the pupil.
4110
+ */
4111
+ export function guidanceRemindsPupil(
4112
+ slot?: Pick<GuidanceSlot, "remindPupil">,
4113
+ config?: Pick<GuidanceConfig, "remindPupils">,
4114
+ ): boolean {
4115
+ return slot?.remindPupil ?? config?.remindPupils ?? false;
4116
+ }
4117
+
4034
4118
  /**
4035
4119
  * Who a check is about, off the check itself.
4036
4120
  *