placementt-core 1.400.1226 → 1.400.1228
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/outreachRules.d.ts +13 -0
- package/lib/outreachRules.js +34 -1
- package/lib/outreachRules.js.map +1 -1
- package/lib/outreachRules.test.js +15 -0
- package/lib/outreachRules.test.js.map +1 -1
- package/lib/typeDefinitions.d.ts +50 -0
- package/lib/typeDefinitions.js.map +1 -1
- package/package.json +1 -1
- package/src/outreachRules.test.ts +17 -1
- package/src/outreachRules.ts +34 -0
- package/src/typeDefinitions.ts +48 -0
package/lib/outreachRules.d.ts
CHANGED
|
@@ -47,6 +47,19 @@ export declare const resolveBlackouts: (today: string) => OutreachBlackout[];
|
|
|
47
47
|
* @return {OutreachBlackout|undefined} the blackout containing the date.
|
|
48
48
|
*/
|
|
49
49
|
export declare const blackoutOn: (date: string, blackouts: OutreachBlackout[]) => OutreachBlackout | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* @param {Date} date an instant.
|
|
52
|
+
* @return {string} its date in London, YYYY-MM-DD.
|
|
53
|
+
*/
|
|
54
|
+
export declare const londonYmd: (date: Date) => string;
|
|
55
|
+
/**
|
|
56
|
+
* 7:30am London on the next school day after today: a weekday outside every
|
|
57
|
+
* school holiday. When approved engagement emails go out.
|
|
58
|
+
*
|
|
59
|
+
* @param {Date} now the clock.
|
|
60
|
+
* @return {string} ISO instant.
|
|
61
|
+
*/
|
|
62
|
+
export declare const nextSchoolMorning: (now: Date) => string;
|
|
50
63
|
/**
|
|
51
64
|
* A term week is any week whose Tuesday is outside every blackout.
|
|
52
65
|
*
|
package/lib/outreachRules.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.defaultCooldownUntil = exports.isTermWeek = exports.blackoutOn = exports.resolveBlackouts = exports.schoolHolidays = exports.easterSunday = exports.phaseOf = exports.ymdDaysBetween = exports.addYmdDays = void 0;
|
|
3
|
+
exports.defaultCooldownUntil = exports.isTermWeek = exports.nextSchoolMorning = exports.londonYmd = exports.blackoutOn = exports.resolveBlackouts = exports.schoolHolidays = exports.easterSunday = exports.phaseOf = exports.ymdDaysBetween = exports.addYmdDays = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Pure outreach rules shared by the web admin and the backend: school phase,
|
|
6
6
|
* term weeks and cooldown dates. Dates are YYYY-MM-DD strings (Europe/London
|
|
@@ -144,6 +144,39 @@ exports.resolveBlackouts = resolveBlackouts;
|
|
|
144
144
|
*/
|
|
145
145
|
const blackoutOn = (date, blackouts) => blackouts.find((b) => date >= b.start && date <= b.end);
|
|
146
146
|
exports.blackoutOn = blackoutOn;
|
|
147
|
+
/**
|
|
148
|
+
* @param {Date} date an instant.
|
|
149
|
+
* @return {string} its date in London, YYYY-MM-DD.
|
|
150
|
+
*/
|
|
151
|
+
const londonYmd = (date) => new Intl.DateTimeFormat("en-CA", { timeZone: "Europe/London", year: "numeric", month: "2-digit", day: "2-digit" }).format(date);
|
|
152
|
+
exports.londonYmd = londonYmd;
|
|
153
|
+
/**
|
|
154
|
+
* @param {string} date YYYY-MM-DD.
|
|
155
|
+
* @return {number} London's offset from UTC that day, in hours (0 or 1).
|
|
156
|
+
*/
|
|
157
|
+
const londonOffsetHours = (date) => {
|
|
158
|
+
const name = new Intl.DateTimeFormat("en-GB", { timeZone: "Europe/London", timeZoneName: "shortOffset" })
|
|
159
|
+
.formatToParts(new Date(`${date}T12:00:00Z`)).find((p) => p.type === "timeZoneName")?.value;
|
|
160
|
+
return name === "GMT+1" ? 1 : 0;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* 7:30am London on the next school day after today: a weekday outside every
|
|
164
|
+
* school holiday. When approved engagement emails go out.
|
|
165
|
+
*
|
|
166
|
+
* @param {Date} now the clock.
|
|
167
|
+
* @return {string} ISO instant.
|
|
168
|
+
*/
|
|
169
|
+
const nextSchoolMorning = (now) => {
|
|
170
|
+
const today = (0, exports.londonYmd)(now);
|
|
171
|
+
const blackouts = (0, exports.resolveBlackouts)(today);
|
|
172
|
+
let day = (0, exports.addYmdDays)(today, 1);
|
|
173
|
+
const weekend = (d) => [0, 6].includes(new Date(toMs(d)).getUTCDay());
|
|
174
|
+
while (weekend(day) || (0, exports.blackoutOn)(day, blackouts))
|
|
175
|
+
day = (0, exports.addYmdDays)(day, 1);
|
|
176
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
177
|
+
return new Date(Date.UTC(y, m - 1, d, 7 - londonOffsetHours(day), 30)).toISOString();
|
|
178
|
+
};
|
|
179
|
+
exports.nextSchoolMorning = nextSchoolMorning;
|
|
147
180
|
/**
|
|
148
181
|
* A term week is any week whose Tuesday is outside every blackout.
|
|
149
182
|
*
|
package/lib/outreachRules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outreachRules.js","sourceRoot":"","sources":["../src/outreachRules.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"outreachRules.js","sourceRoot":"","sources":["../src/outreachRules.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,4 +91,19 @@ describe("defaultCooldownUntil", () => {
|
|
|
91
91
|
expect((0, outreachRules_1.defaultCooldownUntil)("2026-11-10")).toBe("2027-04-10");
|
|
92
92
|
});
|
|
93
93
|
});
|
|
94
|
+
describe("nextSchoolMorning", () => {
|
|
95
|
+
it("is 7:30am London the next weekday", () => {
|
|
96
|
+
expect((0, outreachRules_1.nextSchoolMorning)(new Date("2026-11-02T10:00:00Z"))).toBe("2026-11-03T07:30:00.000Z");
|
|
97
|
+
expect((0, outreachRules_1.nextSchoolMorning)(new Date("2026-11-06T16:00:00Z"))).toBe("2026-11-09T07:30:00.000Z");
|
|
98
|
+
});
|
|
99
|
+
it("is 6:30 UTC in British Summer Time", () => {
|
|
100
|
+
expect((0, outreachRules_1.nextSchoolMorning)(new Date("2026-06-11T10:00:00Z"))).toBe("2026-06-12T06:30:00.000Z");
|
|
101
|
+
});
|
|
102
|
+
it("skips school holidays", () => {
|
|
103
|
+
expect((0, outreachRules_1.nextSchoolMorning)(new Date("2026-12-11T10:00:00Z"))).toBe("2027-01-04T07:30:00.000Z");
|
|
104
|
+
});
|
|
105
|
+
it("reads the date in London, not UTC", () => {
|
|
106
|
+
expect((0, outreachRules_1.londonYmd)(new Date("2026-06-11T23:30:00Z"))).toBe("2026-06-12");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
94
109
|
//# sourceMappingURL=outreachRules.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outreachRules.test.js","sourceRoot":"","sources":["../src/outreachRules.test.ts"],"names":[],"mappings":";;AAAA,6DAAkD;AAClD,
|
|
1
|
+
{"version":3,"file":"outreachRules.test.js","sourceRoot":"","sources":["../src/outreachRules.test.ts"],"names":[],"mappings":"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"}
|
package/lib/typeDefinitions.d.ts
CHANGED
|
@@ -3869,6 +3869,12 @@ export type EmailInteraction = {
|
|
|
3869
3869
|
* cycle later instead of just stopping. A reminder chain spawned BY a fallback
|
|
3870
3870
|
* must NOT set this, or it would escalate recursively. */
|
|
3871
3871
|
fallback?: ReminderFallbackType;
|
|
3872
|
+
/** Narrows the backend's 24 hour duplicate-email guard: an earlier send only
|
|
3873
|
+
* blocks a new one when their keys match. Placement emails use the stage they
|
|
3874
|
+
* are about (`stage:<id>` for action required, `to:<stage name>` for status
|
|
3875
|
+
* changes), so a new stage's email isn't mistaken for a repeat. Absent on
|
|
3876
|
+
* sends from before it existed and on emails that don't use it. */
|
|
3877
|
+
cooldownKey?: string;
|
|
3872
3878
|
};
|
|
3873
3879
|
/** Skills framework a skill can be tagged against. "custom" is the school's own selection
|
|
3874
3880
|
* and is never used as a tag on a SkillLabel. */
|
|
@@ -4478,6 +4484,50 @@ export type EngagementEmailLog = {
|
|
|
4478
4484
|
recipients: string[];
|
|
4479
4485
|
sentAt: string;
|
|
4480
4486
|
campaignId?: string;
|
|
4487
|
+
/** The approved draft it was sent from. */
|
|
4488
|
+
draftId?: string;
|
|
4489
|
+
};
|
|
4490
|
+
/** Where an engagement email is in its approval: written as a draft, approved
|
|
4491
|
+
* by an admin for the next school morning, then sent; or skipped by an admin,
|
|
4492
|
+
* expired unapproved, or dropped because the school did the step first. */
|
|
4493
|
+
export type EngagementDraftStatus = "draft" | "approved" | "sent" | "skipped" | "expired" | "dropped";
|
|
4494
|
+
/** `engagementDrafts/{autoId}`: one engagement email to one institute. The
|
|
4495
|
+
* daily check writes every email as one of these and nothing sends until an
|
|
4496
|
+
* admin approves it. */
|
|
4497
|
+
export type EngagementDraft = {
|
|
4498
|
+
oId: string;
|
|
4499
|
+
instituteName: string;
|
|
4500
|
+
kind: "marketing" | "onboarding";
|
|
4501
|
+
key: string;
|
|
4502
|
+
variant?: "setup" | "resend";
|
|
4503
|
+
touch?: number;
|
|
4504
|
+
templateId: number;
|
|
4505
|
+
label: string;
|
|
4506
|
+
description: string;
|
|
4507
|
+
path: string;
|
|
4508
|
+
campaignId?: string;
|
|
4509
|
+
/** Onboarding: `engagement.onboarding.repeats` once this is sent. */
|
|
4510
|
+
repeats?: number;
|
|
4511
|
+
recipients: {
|
|
4512
|
+
email: string;
|
|
4513
|
+
forename: string;
|
|
4514
|
+
surname: string;
|
|
4515
|
+
}[];
|
|
4516
|
+
/** Drafts with the same template and step, approved together. */
|
|
4517
|
+
batchKey: string;
|
|
4518
|
+
status: EngagementDraftStatus;
|
|
4519
|
+
createdAt: string;
|
|
4520
|
+
approvedAt?: string;
|
|
4521
|
+
approvedBy?: string;
|
|
4522
|
+
/** 7:30am London on the school day after approval. */
|
|
4523
|
+
sendAt?: string;
|
|
4524
|
+
sentAt?: string;
|
|
4525
|
+
sentTo?: string[];
|
|
4526
|
+
/** When it was skipped, expired or dropped, and why. */
|
|
4527
|
+
closedAt?: string;
|
|
4528
|
+
closedReason?: string;
|
|
4529
|
+
/** An admin changed the description or recipients. */
|
|
4530
|
+
edited?: boolean;
|
|
4481
4531
|
};
|
|
4482
4532
|
export type CustomerTask = {
|
|
4483
4533
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"typeDefinitions.js","sourceRoot":"","sources":["../src/typeDefinitions.ts"],"names":[],"mappings":";;;AAmxIA,sDASC;AAeD,oDAKC;AAaD,0CAEC;AArrED;;;;;kFAKkF;AACrE,QAAA,qBAAqB,GAAG,SAAS,CAAC;AAClC,QAAA,iBAAiB,GAAG,OAAO,CAAC;AAimEzC;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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {isInBlackout} from "./customerCareConfig";
|
|
2
|
-
import {addYmdDays, blackoutOn, ymdDaysBetween, defaultCooldownUntil, easterSunday, isTermWeek, phaseOf, resolveBlackouts, schoolHolidays} from "./outreachRules";
|
|
2
|
+
import {addYmdDays, blackoutOn, ymdDaysBetween, defaultCooldownUntil, easterSunday, isTermWeek, londonYmd, nextSchoolMorning, phaseOf, resolveBlackouts, schoolHolidays} from "./outreachRules";
|
|
3
3
|
|
|
4
4
|
// customerCareConfig pulls in productResolver, whose imports jest cannot parse; only isInBlackout is needed here.
|
|
5
5
|
jest.mock("./productResolver", () => ({resolveProducts: jest.fn()}));
|
|
@@ -102,3 +102,19 @@ describe("defaultCooldownUntil", () => {
|
|
|
102
102
|
expect(defaultCooldownUntil("2026-11-10")).toBe("2027-04-10");
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
|
+
|
|
106
|
+
describe("nextSchoolMorning", () => {
|
|
107
|
+
it("is 7:30am London the next weekday", () => {
|
|
108
|
+
expect(nextSchoolMorning(new Date("2026-11-02T10:00:00Z"))).toBe("2026-11-03T07:30:00.000Z");
|
|
109
|
+
expect(nextSchoolMorning(new Date("2026-11-06T16:00:00Z"))).toBe("2026-11-09T07:30:00.000Z");
|
|
110
|
+
});
|
|
111
|
+
it("is 6:30 UTC in British Summer Time", () => {
|
|
112
|
+
expect(nextSchoolMorning(new Date("2026-06-11T10:00:00Z"))).toBe("2026-06-12T06:30:00.000Z");
|
|
113
|
+
});
|
|
114
|
+
it("skips school holidays", () => {
|
|
115
|
+
expect(nextSchoolMorning(new Date("2026-12-11T10:00:00Z"))).toBe("2027-01-04T07:30:00.000Z");
|
|
116
|
+
});
|
|
117
|
+
it("reads the date in London, not UTC", () => {
|
|
118
|
+
expect(londonYmd(new Date("2026-06-11T23:30:00Z"))).toBe("2026-06-12");
|
|
119
|
+
});
|
|
120
|
+
});
|
package/src/outreachRules.ts
CHANGED
|
@@ -152,6 +152,40 @@ export const resolveBlackouts = (today: string): OutreachBlackout[] => {
|
|
|
152
152
|
export const blackoutOn = (date: string, blackouts: OutreachBlackout[]): OutreachBlackout | undefined =>
|
|
153
153
|
blackouts.find((b) => date >= b.start && date <= b.end);
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @param {Date} date an instant.
|
|
157
|
+
* @return {string} its date in London, YYYY-MM-DD.
|
|
158
|
+
*/
|
|
159
|
+
export const londonYmd = (date: Date): string =>
|
|
160
|
+
new Intl.DateTimeFormat("en-CA", {timeZone: "Europe/London", year: "numeric", month: "2-digit", day: "2-digit"}).format(date);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* @param {string} date YYYY-MM-DD.
|
|
164
|
+
* @return {number} London's offset from UTC that day, in hours (0 or 1).
|
|
165
|
+
*/
|
|
166
|
+
const londonOffsetHours = (date: string): number => {
|
|
167
|
+
const name = new Intl.DateTimeFormat("en-GB", {timeZone: "Europe/London", timeZoneName: "shortOffset"})
|
|
168
|
+
.formatToParts(new Date(`${date}T12:00:00Z`)).find((p) => p.type === "timeZoneName")?.value;
|
|
169
|
+
return name === "GMT+1" ? 1 : 0;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 7:30am London on the next school day after today: a weekday outside every
|
|
174
|
+
* school holiday. When approved engagement emails go out.
|
|
175
|
+
*
|
|
176
|
+
* @param {Date} now the clock.
|
|
177
|
+
* @return {string} ISO instant.
|
|
178
|
+
*/
|
|
179
|
+
export const nextSchoolMorning = (now: Date): string => {
|
|
180
|
+
const today = londonYmd(now);
|
|
181
|
+
const blackouts = resolveBlackouts(today);
|
|
182
|
+
let day = addYmdDays(today, 1);
|
|
183
|
+
const weekend = (d: string) => [0, 6].includes(new Date(toMs(d)).getUTCDay());
|
|
184
|
+
while (weekend(day) || blackoutOn(day, blackouts)) day = addYmdDays(day, 1);
|
|
185
|
+
const [y, m, d] = day.split("-").map(Number);
|
|
186
|
+
return new Date(Date.UTC(y, m - 1, d, 7 - londonOffsetHours(day), 30)).toISOString();
|
|
187
|
+
};
|
|
188
|
+
|
|
155
189
|
/**
|
|
156
190
|
* A term week is any week whose Tuesday is outside every blackout.
|
|
157
191
|
*
|
package/src/typeDefinitions.ts
CHANGED
|
@@ -3857,6 +3857,12 @@ export type EmailInteraction = {
|
|
|
3857
3857
|
* cycle later instead of just stopping. A reminder chain spawned BY a fallback
|
|
3858
3858
|
* must NOT set this, or it would escalate recursively. */
|
|
3859
3859
|
fallback?: ReminderFallbackType,
|
|
3860
|
+
/** Narrows the backend's 24 hour duplicate-email guard: an earlier send only
|
|
3861
|
+
* blocks a new one when their keys match. Placement emails use the stage they
|
|
3862
|
+
* are about (`stage:<id>` for action required, `to:<stage name>` for status
|
|
3863
|
+
* changes), so a new stage's email isn't mistaken for a repeat. Absent on
|
|
3864
|
+
* sends from before it existed and on emails that don't use it. */
|
|
3865
|
+
cooldownKey?: string,
|
|
3860
3866
|
}
|
|
3861
3867
|
|
|
3862
3868
|
/** Skills framework a skill can be tagged against. "custom" is the school's own selection
|
|
@@ -4647,6 +4653,48 @@ export type EngagementEmailLog = {
|
|
|
4647
4653
|
recipients: string[],
|
|
4648
4654
|
sentAt: string,
|
|
4649
4655
|
campaignId?: string,
|
|
4656
|
+
/** The approved draft it was sent from. */
|
|
4657
|
+
draftId?: string,
|
|
4658
|
+
};
|
|
4659
|
+
|
|
4660
|
+
/** Where an engagement email is in its approval: written as a draft, approved
|
|
4661
|
+
* by an admin for the next school morning, then sent; or skipped by an admin,
|
|
4662
|
+
* expired unapproved, or dropped because the school did the step first. */
|
|
4663
|
+
export type EngagementDraftStatus = "draft"|"approved"|"sent"|"skipped"|"expired"|"dropped";
|
|
4664
|
+
|
|
4665
|
+
/** `engagementDrafts/{autoId}`: one engagement email to one institute. The
|
|
4666
|
+
* daily check writes every email as one of these and nothing sends until an
|
|
4667
|
+
* admin approves it. */
|
|
4668
|
+
export type EngagementDraft = {
|
|
4669
|
+
oId: string,
|
|
4670
|
+
instituteName: string,
|
|
4671
|
+
kind: "marketing"|"onboarding",
|
|
4672
|
+
key: string,
|
|
4673
|
+
variant?: "setup"|"resend",
|
|
4674
|
+
touch?: number,
|
|
4675
|
+
templateId: number,
|
|
4676
|
+
label: string,
|
|
4677
|
+
description: string,
|
|
4678
|
+
path: string,
|
|
4679
|
+
campaignId?: string,
|
|
4680
|
+
/** Onboarding: `engagement.onboarding.repeats` once this is sent. */
|
|
4681
|
+
repeats?: number,
|
|
4682
|
+
recipients: {email: string, forename: string, surname: string}[],
|
|
4683
|
+
/** Drafts with the same template and step, approved together. */
|
|
4684
|
+
batchKey: string,
|
|
4685
|
+
status: EngagementDraftStatus,
|
|
4686
|
+
createdAt: string,
|
|
4687
|
+
approvedAt?: string,
|
|
4688
|
+
approvedBy?: string,
|
|
4689
|
+
/** 7:30am London on the school day after approval. */
|
|
4690
|
+
sendAt?: string,
|
|
4691
|
+
sentAt?: string,
|
|
4692
|
+
sentTo?: string[],
|
|
4693
|
+
/** When it was skipped, expired or dropped, and why. */
|
|
4694
|
+
closedAt?: string,
|
|
4695
|
+
closedReason?: string,
|
|
4696
|
+
/** An admin changed the description or recipients. */
|
|
4697
|
+
edited?: boolean,
|
|
4650
4698
|
};
|
|
4651
4699
|
|
|
4652
4700
|
export type CustomerTask = {
|