placementt-core 1.400.1250 → 1.400.1252
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/bulkEngagement.d.ts +74 -0
- package/lib/bulkEngagement.js +97 -0
- package/lib/bulkEngagement.js.map +1 -0
- package/lib/bulkEngagement.test.d.ts +1 -0
- package/lib/bulkEngagement.test.js +102 -0
- package/lib/bulkEngagement.test.js.map +1 -0
- package/lib/callables/engagement.d.ts +140 -0
- package/lib/callables/engagement.js +64 -0
- package/lib/callables/engagement.js.map +1 -0
- package/lib/callables/index.d.ts +1 -0
- package/lib/callables/index.js +1 -0
- package/lib/callables/index.js.map +1 -1
- package/lib/callables/registry.d.ts +256 -0
- package/lib/callables/registry.js +3 -0
- package/lib/callables/registry.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/renewals.d.ts +16 -60
- package/lib/renewals.js +35 -87
- package/lib/renewals.js.map +1 -1
- package/lib/renewals.test.js +12 -50
- package/lib/renewals.test.js.map +1 -1
- package/lib/typeDefinitions.d.ts +0 -11
- package/package.json +1 -1
- package/src/bulkEngagement.test.ts +125 -0
- package/src/bulkEngagement.ts +138 -0
- package/src/callables/engagement.ts +63 -0
- package/src/callables/index.ts +1 -0
- package/src/callables/registry.ts +3 -0
- package/src/index.ts +3 -2
- package/src/renewals.test.ts +14 -61
- package/src/renewals.ts +30 -122
- package/src/typeDefinitions.ts +0 -10
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { InstituteData } from "./typeDefinitions";
|
|
2
|
+
import { ResolvedProducts } from "./productResolver";
|
|
3
|
+
/**
|
|
4
|
+
* Bulk engagement emails: one admin email template, sent now to many schools.
|
|
5
|
+
* The admin decides what goes out and when; this only suggests which schools
|
|
6
|
+
* it fits, and flags what is missing for each.
|
|
7
|
+
*
|
|
8
|
+
* Pure: the backend reads each school and hands it in.
|
|
9
|
+
*/
|
|
10
|
+
/** Something about a school that makes the email go out incomplete, or not at all. */
|
|
11
|
+
export type BulkEngagementFlag = "noContacts" | "noRenewalDate" | "noQuote" | "noFinanceContact" | "sentRecently";
|
|
12
|
+
export declare const BULK_ENGAGEMENT_FLAG_LABELS: Record<BulkEngagementFlag, string>;
|
|
13
|
+
/** Flags that stop a school being sent the email at all. */
|
|
14
|
+
export declare const BULK_ENGAGEMENT_BLOCKING_FLAGS: BulkEngagementFlag[];
|
|
15
|
+
/** The account types the send popup filters by, and what it calls them. */
|
|
16
|
+
export declare const BULK_ENGAGEMENT_PACKAGE_LABELS: {
|
|
17
|
+
[pkg: string]: string;
|
|
18
|
+
};
|
|
19
|
+
/** The products the send popup filters by. Flags on ResolvedProducts. */
|
|
20
|
+
export type BulkEngagementProduct = "wex" | "employerEvents" | "destinations";
|
|
21
|
+
export declare const BULK_ENGAGEMENT_PRODUCTS: BulkEngagementProduct[];
|
|
22
|
+
export declare const BULK_ENGAGEMENT_PRODUCT_LABELS: Record<BulkEngagementProduct, string>;
|
|
23
|
+
/** What the admin has ticked in the send popup's filters. Empty means any. */
|
|
24
|
+
export type BulkEngagementFilters = {
|
|
25
|
+
packages: string[];
|
|
26
|
+
products: string[];
|
|
27
|
+
};
|
|
28
|
+
/** A school that had this email this recently isn't suggested it again. */
|
|
29
|
+
export declare const RECENTLY_SENT_DAYS = 60;
|
|
30
|
+
export type BulkEngagementSchool = {
|
|
31
|
+
institute: InstituteData;
|
|
32
|
+
products: ResolvedProducts;
|
|
33
|
+
now: Date;
|
|
34
|
+
/** How many of the school's contacts it would go to. */
|
|
35
|
+
contactCount: number;
|
|
36
|
+
/** Whether the school has a price for next year. */
|
|
37
|
+
hasQuote: boolean;
|
|
38
|
+
/** The email of the school's next onboarding step, if any. */
|
|
39
|
+
nextOnboardingTemplate?: string;
|
|
40
|
+
/** ISO date this email last went to the school, if it has. */
|
|
41
|
+
lastSent?: string;
|
|
42
|
+
};
|
|
43
|
+
export type BulkEngagementSuggestion = {
|
|
44
|
+
/** Ticked to start with in the send popup. */
|
|
45
|
+
suggested: boolean;
|
|
46
|
+
/** Why it is suggested, for the admin. */
|
|
47
|
+
reason?: string;
|
|
48
|
+
flags: BulkEngagementFlag[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Whether one email suits one school, and what is missing for it.
|
|
52
|
+
*
|
|
53
|
+
* Renewal emails are suggested by days to renewal, and any other admin email
|
|
54
|
+
* to schools whose next onboarding step it is. Neither is suggested to a
|
|
55
|
+
* school that had it in the last 60 days.
|
|
56
|
+
*
|
|
57
|
+
* @param {string} templateKey the admin email template.
|
|
58
|
+
* @param {BulkEngagementSchool} school the school.
|
|
59
|
+
* @return {BulkEngagementSuggestion} whether to suggest it, why, and its flags.
|
|
60
|
+
*/
|
|
61
|
+
export declare const bulkEngagementSuggestion: (templateKey: string, school: BulkEngagementSchool) => BulkEngagementSuggestion;
|
|
62
|
+
/**
|
|
63
|
+
* Whether a school passes the send popup's filters: its package is one of
|
|
64
|
+
* those ticked, and it has any of the products ticked. An empty filter
|
|
65
|
+
* matches everything.
|
|
66
|
+
*
|
|
67
|
+
* @param {object} school the school's package and products.
|
|
68
|
+
* @param {BulkEngagementFilters} filters what is ticked.
|
|
69
|
+
* @return {boolean} whether the school matches.
|
|
70
|
+
*/
|
|
71
|
+
export declare const matchesBulkFilters: (school: {
|
|
72
|
+
package: string;
|
|
73
|
+
products: string[];
|
|
74
|
+
}, { packages, products }: BulkEngagementFilters) => boolean;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.matchesBulkFilters = exports.bulkEngagementSuggestion = exports.RECENTLY_SENT_DAYS = exports.BULK_ENGAGEMENT_PRODUCT_LABELS = exports.BULK_ENGAGEMENT_PRODUCTS = exports.BULK_ENGAGEMENT_PACKAGE_LABELS = exports.BULK_ENGAGEMENT_BLOCKING_FLAGS = exports.BULK_ENGAGEMENT_FLAG_LABELS = void 0;
|
|
4
|
+
const renewals_1 = require("./renewals");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
exports.BULK_ENGAGEMENT_FLAG_LABELS = {
|
|
7
|
+
noContacts: "No one to send to",
|
|
8
|
+
noRenewalDate: "No renewal date",
|
|
9
|
+
noQuote: "No quote",
|
|
10
|
+
noFinanceContact: "No finance contact",
|
|
11
|
+
sentRecently: "Sent in the last 60 days",
|
|
12
|
+
};
|
|
13
|
+
/** Flags that stop a school being sent the email at all. */
|
|
14
|
+
exports.BULK_ENGAGEMENT_BLOCKING_FLAGS = ["noContacts"];
|
|
15
|
+
/** The account types the send popup filters by, and what it calls them. */
|
|
16
|
+
exports.BULK_ENGAGEMENT_PACKAGE_LABELS = {
|
|
17
|
+
"institutes-one": "School",
|
|
18
|
+
"institutes-two": "Trust",
|
|
19
|
+
"careersHub": "Careers hub",
|
|
20
|
+
"careersSchool": "Hub school",
|
|
21
|
+
};
|
|
22
|
+
exports.BULK_ENGAGEMENT_PRODUCTS = ["wex", "employerEvents", "destinations"];
|
|
23
|
+
exports.BULK_ENGAGEMENT_PRODUCT_LABELS = {
|
|
24
|
+
wex: "Work experience",
|
|
25
|
+
employerEvents: "Employer events",
|
|
26
|
+
destinations: "Destinations",
|
|
27
|
+
};
|
|
28
|
+
/** A school that had this email this recently isn't suggested it again. */
|
|
29
|
+
exports.RECENTLY_SENT_DAYS = 60;
|
|
30
|
+
/**
|
|
31
|
+
* Days to renewal each renewal email is suggested for, inclusive. "paying"
|
|
32
|
+
* suggests it to every paying school, date or not.
|
|
33
|
+
*/
|
|
34
|
+
const RENEWAL_SUGGEST_DAYS = {
|
|
35
|
+
engagementRenewalPrice: [61, 365],
|
|
36
|
+
engagementRenewalReview: "paying",
|
|
37
|
+
engagementRenewalDue: [31, 90],
|
|
38
|
+
engagementRenewalInvoice: [0, 45],
|
|
39
|
+
// A date this far off has most likely just moved on a year.
|
|
40
|
+
engagementRenewalThanks: [300, 400],
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Whether one email suits one school, and what is missing for it.
|
|
44
|
+
*
|
|
45
|
+
* Renewal emails are suggested by days to renewal, and any other admin email
|
|
46
|
+
* to schools whose next onboarding step it is. Neither is suggested to a
|
|
47
|
+
* school that had it in the last 60 days.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} templateKey the admin email template.
|
|
50
|
+
* @param {BulkEngagementSchool} school the school.
|
|
51
|
+
* @return {BulkEngagementSuggestion} whether to suggest it, why, and its flags.
|
|
52
|
+
*/
|
|
53
|
+
const bulkEngagementSuggestion = (templateKey, school) => {
|
|
54
|
+
const { institute, products, now, contactCount, hasQuote, nextOnboardingTemplate, lastSent } = school;
|
|
55
|
+
const params = constants_1.emailTemplates[templateKey]?.params || [];
|
|
56
|
+
const renewal = (0, renewals_1.renewalInfo)({ institute, products, now });
|
|
57
|
+
const recent = Boolean(lastSent) && now.getTime() - Date.parse(lastSent) < exports.RECENTLY_SENT_DAYS * 86400000;
|
|
58
|
+
const flags = [];
|
|
59
|
+
if (!contactCount)
|
|
60
|
+
flags.push("noContacts");
|
|
61
|
+
if (params.includes("renewalDate") && !renewal.renewalDate)
|
|
62
|
+
flags.push("noRenewalDate");
|
|
63
|
+
if (params.includes("quote") && !hasQuote)
|
|
64
|
+
flags.push("noQuote");
|
|
65
|
+
if (renewals_1.RENEWAL_FINANCE_TEMPLATES.includes(templateKey) && !institute.financeContact)
|
|
66
|
+
flags.push("noFinanceContact");
|
|
67
|
+
if (recent)
|
|
68
|
+
flags.push("sentRecently");
|
|
69
|
+
const none = { suggested: false, flags };
|
|
70
|
+
if (recent || flags.some((f) => exports.BULK_ENGAGEMENT_BLOCKING_FLAGS.includes(f)))
|
|
71
|
+
return none;
|
|
72
|
+
const window = RENEWAL_SUGGEST_DAYS[templateKey];
|
|
73
|
+
if (!window)
|
|
74
|
+
return nextOnboardingTemplate === templateKey ? { suggested: true, reason: "Their next onboarding step", flags } : none;
|
|
75
|
+
if (!renewal.applies)
|
|
76
|
+
return none;
|
|
77
|
+
if (window === "paying")
|
|
78
|
+
return { suggested: true, reason: "Paying customer", flags };
|
|
79
|
+
const days = renewal.daysToRenewal;
|
|
80
|
+
if (days === undefined || days < window[0] || days > window[1])
|
|
81
|
+
return none;
|
|
82
|
+
return { suggested: true, reason: `Renews in ${days} days`, flags };
|
|
83
|
+
};
|
|
84
|
+
exports.bulkEngagementSuggestion = bulkEngagementSuggestion;
|
|
85
|
+
/**
|
|
86
|
+
* Whether a school passes the send popup's filters: its package is one of
|
|
87
|
+
* those ticked, and it has any of the products ticked. An empty filter
|
|
88
|
+
* matches everything.
|
|
89
|
+
*
|
|
90
|
+
* @param {object} school the school's package and products.
|
|
91
|
+
* @param {BulkEngagementFilters} filters what is ticked.
|
|
92
|
+
* @return {boolean} whether the school matches.
|
|
93
|
+
*/
|
|
94
|
+
const matchesBulkFilters = (school, { packages, products }) => (!packages.length || packages.includes(school.package)) &&
|
|
95
|
+
(!products.length || products.some((p) => school.products.includes(p)));
|
|
96
|
+
exports.matchesBulkFilters = matchesBulkFilters;
|
|
97
|
+
//# sourceMappingURL=bulkEngagement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulkEngagement.js","sourceRoot":"","sources":["../src/bulkEngagement.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
jest.mock("firebase/firestore", () => ({ where: jest.fn() }));
|
|
4
|
+
jest.mock("./firebase/firebaseQuery", () => ({ __esModule: true, default: class {
|
|
5
|
+
} }));
|
|
6
|
+
const productResolver_1 = require("./productResolver");
|
|
7
|
+
const bulkEngagement_1 = require("./bulkEngagement");
|
|
8
|
+
const school = (renewalDate, overrides = {}) => ({
|
|
9
|
+
name: "Calder Park Academy",
|
|
10
|
+
package: "institutes-one",
|
|
11
|
+
workExperience: true,
|
|
12
|
+
cs: { lifecycleStatus: "maintenance", productTypes: ["workExperience"], ...(renewalDate ? { renewalDate } : {}) },
|
|
13
|
+
...overrides,
|
|
14
|
+
});
|
|
15
|
+
const suggest = (templateKey, institute, date, extra = {}) => (0, bulkEngagement_1.bulkEngagementSuggestion)(templateKey, {
|
|
16
|
+
institute,
|
|
17
|
+
products: (0, productResolver_1.resolveProducts)(institute),
|
|
18
|
+
now: new Date(`${date}T09:00:00Z`),
|
|
19
|
+
contactCount: extra.contactCount ?? 2,
|
|
20
|
+
hasQuote: extra.hasQuote ?? true,
|
|
21
|
+
nextOnboardingTemplate: extra.nextOnboardingTemplate,
|
|
22
|
+
lastSent: extra.lastSent,
|
|
23
|
+
});
|
|
24
|
+
describe("bulk engagement suggestions", () => {
|
|
25
|
+
it("suggests the 60-day email to schools renewing in 31 to 90 days", () => {
|
|
26
|
+
expect(suggest("engagementRenewalDue", school("2027-01-10"), "2026-11-04")).toMatchObject({ suggested: true, reason: "Renews in 67 days" });
|
|
27
|
+
expect(suggest("engagementRenewalDue", school("2027-06-10"), "2026-11-04").suggested).toBe(false);
|
|
28
|
+
expect(suggest("engagementRenewalDue", school("2026-11-20"), "2026-11-04").suggested).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it("suggests the invoice email inside 45 days, and next year's price further out", () => {
|
|
31
|
+
expect(suggest("engagementRenewalInvoice", school("2026-11-20"), "2026-11-04").suggested).toBe(true);
|
|
32
|
+
expect(suggest("engagementRenewalPrice", school("2027-06-10"), "2026-11-04").suggested).toBe(true);
|
|
33
|
+
expect(suggest("engagementRenewalPrice", school("2026-11-20"), "2026-11-04").suggested).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
it("suggests the year in review to every paying school, and thanks to those just renewed", () => {
|
|
36
|
+
expect(suggest("engagementRenewalReview", school(), "2026-11-04").suggested).toBe(true);
|
|
37
|
+
expect(suggest("engagementRenewalThanks", school("2027-10-20"), "2026-11-04").suggested).toBe(true);
|
|
38
|
+
expect(suggest("engagementRenewalThanks", school("2027-01-10"), "2026-11-04").suggested).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it("leaves out hub schools", () => {
|
|
41
|
+
expect(suggest("engagementRenewalDue", school("2027-01-10", { package: "careersSchool" }), "2026-11-04").suggested).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
it("doesn't suggest an email the school had in the last 60 days", () => {
|
|
44
|
+
const recent = suggest("engagementRenewalDue", school("2027-01-10"), "2026-11-04", { lastSent: "2026-10-01T09:00:00Z" });
|
|
45
|
+
expect(recent).toMatchObject({ suggested: false, flags: expect.arrayContaining(["sentRecently"]) });
|
|
46
|
+
expect(suggest("engagementRenewalDue", school("2027-01-10"), "2026-11-04", { lastSent: "2026-06-01T09:00:00Z" }).suggested).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
it("flags what the email is missing", () => {
|
|
49
|
+
expect(suggest("engagementRenewalDue", school(), "2026-11-04", { hasQuote: false }).flags)
|
|
50
|
+
.toEqual(["noRenewalDate", "noQuote", "noFinanceContact"]);
|
|
51
|
+
const withFinance = school("2027-01-10", { financeContact: { name: "Pat Lee", email: "pat@school.org" } });
|
|
52
|
+
expect(suggest("engagementRenewalDue", withFinance, "2026-11-04").flags).toEqual([]);
|
|
53
|
+
});
|
|
54
|
+
it("never suggests a school with no one to send to", () => {
|
|
55
|
+
expect(suggest("engagementRenewalReview", school(), "2026-11-04", { contactCount: 0 })).toMatchObject({ suggested: false, flags: ["noContacts"] });
|
|
56
|
+
});
|
|
57
|
+
it("suggests an onboarding email to schools whose next step it is", () => {
|
|
58
|
+
expect(suggest("engagementPickFeatures", school(), "2026-11-04", { nextOnboardingTemplate: "engagementPickFeatures" }))
|
|
59
|
+
.toMatchObject({ suggested: true, reason: "Their next onboarding step" });
|
|
60
|
+
expect(suggest("engagementPickFeatures", school(), "2026-11-04", { nextOnboardingTemplate: "engagementPlanYear" }).suggested).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("matchesBulkFilters", () => {
|
|
64
|
+
const trust = { package: "institutes-two", products: ["wex", "destinations"] };
|
|
65
|
+
const hub = { package: "careersHub", products: ["employerEvents"] };
|
|
66
|
+
const none = { package: "institutes-one", products: [] };
|
|
67
|
+
it("matches everything when nothing is ticked", () => {
|
|
68
|
+
[trust, hub, none].forEach((s) => expect((0, bulkEngagement_1.matchesBulkFilters)(s, { packages: [], products: [] })).toBe(true));
|
|
69
|
+
});
|
|
70
|
+
it("matches a package that is one of those ticked", () => {
|
|
71
|
+
const packages = ["institutes-two", "careersHub"];
|
|
72
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages, products: [] })).toBe(true);
|
|
73
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(hub, { packages, products: [] })).toBe(true);
|
|
74
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(none, { packages, products: [] })).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
it("matches a school with any of the products ticked, not all of them", () => {
|
|
77
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages: [], products: ["destinations", "employerEvents"] })).toBe(true);
|
|
78
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages: [], products: ["employerEvents"] })).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
it("never matches a school with no products once a product is ticked", () => {
|
|
81
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(none, { packages: [], products: ["wex"] })).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
it("needs both filters to pass", () => {
|
|
84
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages: ["institutes-two"], products: ["wex"] })).toBe(true);
|
|
85
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages: ["careersHub"], products: ["wex"] })).toBe(false);
|
|
86
|
+
expect((0, bulkEngagement_1.matchesBulkFilters)(trust, { packages: ["institutes-two"], products: ["employerEvents"] })).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
it("labels every package and product in sentence case", () => {
|
|
89
|
+
expect(bulkEngagement_1.BULK_ENGAGEMENT_PACKAGE_LABELS).toEqual({
|
|
90
|
+
"institutes-one": "School",
|
|
91
|
+
"institutes-two": "Trust",
|
|
92
|
+
"careersHub": "Careers hub",
|
|
93
|
+
"careersSchool": "Hub school",
|
|
94
|
+
});
|
|
95
|
+
expect(bulkEngagement_1.BULK_ENGAGEMENT_PRODUCTS.map((p) => bulkEngagement_1.BULK_ENGAGEMENT_PRODUCT_LABELS[p])).toEqual(["Work experience", "Employer events", "Destinations"]);
|
|
96
|
+
});
|
|
97
|
+
it("offers products that resolveProducts sets", () => {
|
|
98
|
+
const products = (0, productResolver_1.resolveProducts)({ package: "careersHub", workExperience: true });
|
|
99
|
+
expect(bulkEngagement_1.BULK_ENGAGEMENT_PRODUCTS.filter((p) => products[p])).toEqual(["employerEvents"]);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=bulkEngagement.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulkEngagement.test.js","sourceRoot":"","sources":["../src/bulkEngagement.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const engagementCallables: {
|
|
3
|
+
"bulkEngagement-getBulkEngagementSchools": {
|
|
4
|
+
request: z.ZodObject<{
|
|
5
|
+
/** An admin email template (product "admin" in emailTemplates). */
|
|
6
|
+
templateKey: z.ZodString;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
templateKey: string;
|
|
9
|
+
}, {
|
|
10
|
+
templateKey: string;
|
|
11
|
+
}>;
|
|
12
|
+
response: z.ZodObject<{
|
|
13
|
+
schools: z.ZodArray<z.ZodObject<{
|
|
14
|
+
oId: z.ZodString;
|
|
15
|
+
name: z.ZodString;
|
|
16
|
+
package: z.ZodString;
|
|
17
|
+
/** The products the admin can filter this school by. */
|
|
18
|
+
products: z.ZodArray<z.ZodEnum<["wex", "employerEvents", "destinations"]>, "many">;
|
|
19
|
+
suggested: z.ZodBoolean;
|
|
20
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
21
|
+
flags: z.ZodArray<z.ZodEnum<["noContacts", "noRenewalDate", "noQuote", "noFinanceContact", "sentRecently"]>, "many">;
|
|
22
|
+
/** How many of the school's contacts it would go to. */
|
|
23
|
+
contacts: z.ZodNumber;
|
|
24
|
+
/** ISO date this email last went to the school, if it has. */
|
|
25
|
+
lastSent: z.ZodOptional<z.ZodString>;
|
|
26
|
+
/** The school's params, for previewing the email as they'd get it. */
|
|
27
|
+
params: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
name: string;
|
|
30
|
+
oId: string;
|
|
31
|
+
package: string;
|
|
32
|
+
flags: ("noContacts" | "noRenewalDate" | "noQuote" | "noFinanceContact" | "sentRecently")[];
|
|
33
|
+
contacts: number;
|
|
34
|
+
params: Record<string, string>;
|
|
35
|
+
products: ("employerEvents" | "wex" | "destinations")[];
|
|
36
|
+
suggested: boolean;
|
|
37
|
+
reason?: string | undefined;
|
|
38
|
+
lastSent?: string | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
name: string;
|
|
41
|
+
oId: string;
|
|
42
|
+
package: string;
|
|
43
|
+
flags: ("noContacts" | "noRenewalDate" | "noQuote" | "noFinanceContact" | "sentRecently")[];
|
|
44
|
+
contacts: number;
|
|
45
|
+
params: Record<string, string>;
|
|
46
|
+
products: ("employerEvents" | "wex" | "destinations")[];
|
|
47
|
+
suggested: boolean;
|
|
48
|
+
reason?: string | undefined;
|
|
49
|
+
lastSent?: string | undefined;
|
|
50
|
+
}>, "many">;
|
|
51
|
+
}, "strip", z.ZodTypeAny, {
|
|
52
|
+
schools: {
|
|
53
|
+
name: string;
|
|
54
|
+
oId: string;
|
|
55
|
+
package: string;
|
|
56
|
+
flags: ("noContacts" | "noRenewalDate" | "noQuote" | "noFinanceContact" | "sentRecently")[];
|
|
57
|
+
contacts: number;
|
|
58
|
+
params: Record<string, string>;
|
|
59
|
+
products: ("employerEvents" | "wex" | "destinations")[];
|
|
60
|
+
suggested: boolean;
|
|
61
|
+
reason?: string | undefined;
|
|
62
|
+
lastSent?: string | undefined;
|
|
63
|
+
}[];
|
|
64
|
+
}, {
|
|
65
|
+
schools: {
|
|
66
|
+
name: string;
|
|
67
|
+
oId: string;
|
|
68
|
+
package: string;
|
|
69
|
+
flags: ("noContacts" | "noRenewalDate" | "noQuote" | "noFinanceContact" | "sentRecently")[];
|
|
70
|
+
contacts: number;
|
|
71
|
+
params: Record<string, string>;
|
|
72
|
+
products: ("employerEvents" | "wex" | "destinations")[];
|
|
73
|
+
suggested: boolean;
|
|
74
|
+
reason?: string | undefined;
|
|
75
|
+
lastSent?: string | undefined;
|
|
76
|
+
}[];
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
"bulkEngagement-sendBulkEngagementEmail": {
|
|
80
|
+
request: z.ZodObject<{
|
|
81
|
+
templateKey: z.ZodString;
|
|
82
|
+
oIds: z.ZodArray<z.ZodString, "many">;
|
|
83
|
+
subject: z.ZodString;
|
|
84
|
+
/** The Slate body, edited for this send. The template itself is unchanged. */
|
|
85
|
+
body: z.ZodArray<z.ZodUnknown, "many">;
|
|
86
|
+
/** Must be in our storage bucket; checked on the server. */
|
|
87
|
+
heroImage: z.ZodEffects<z.ZodOptional<z.ZodNullable<z.ZodString>>, string | undefined, string | null | undefined>;
|
|
88
|
+
/** Send the weekly follow-ups: up to three, or until an onboarding step is done. */
|
|
89
|
+
followUps: z.ZodBoolean;
|
|
90
|
+
}, "strip", z.ZodTypeAny, {
|
|
91
|
+
body: unknown[];
|
|
92
|
+
subject: string;
|
|
93
|
+
templateKey: string;
|
|
94
|
+
oIds: string[];
|
|
95
|
+
followUps: boolean;
|
|
96
|
+
heroImage?: string | undefined;
|
|
97
|
+
}, {
|
|
98
|
+
body: unknown[];
|
|
99
|
+
subject: string;
|
|
100
|
+
templateKey: string;
|
|
101
|
+
oIds: string[];
|
|
102
|
+
followUps: boolean;
|
|
103
|
+
heroImage?: string | null | undefined;
|
|
104
|
+
}>;
|
|
105
|
+
/** Each school's outcome: who it went to, or why it didn't. */
|
|
106
|
+
response: z.ZodObject<{
|
|
107
|
+
results: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
108
|
+
sent: z.ZodArray<z.ZodString, "many">;
|
|
109
|
+
error: z.ZodOptional<z.ZodString>;
|
|
110
|
+
}, "strip", z.ZodTypeAny, {
|
|
111
|
+
sent: string[];
|
|
112
|
+
error?: string | undefined;
|
|
113
|
+
}, {
|
|
114
|
+
sent: string[];
|
|
115
|
+
error?: string | undefined;
|
|
116
|
+
}>>;
|
|
117
|
+
}, "strip", z.ZodTypeAny, {
|
|
118
|
+
results: Record<string, {
|
|
119
|
+
sent: string[];
|
|
120
|
+
error?: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
}, {
|
|
123
|
+
results: Record<string, {
|
|
124
|
+
sent: string[];
|
|
125
|
+
error?: string | undefined;
|
|
126
|
+
}>;
|
|
127
|
+
}>;
|
|
128
|
+
};
|
|
129
|
+
/** One-off: removes the open renewal email tasks the old automatic schedule made. */
|
|
130
|
+
"bulkEngagement-clearRenewalEmailTasks": {
|
|
131
|
+
request: z.ZodOptional<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>>;
|
|
132
|
+
response: z.ZodObject<{
|
|
133
|
+
cleared: z.ZodNumber;
|
|
134
|
+
}, "strip", z.ZodTypeAny, {
|
|
135
|
+
cleared: number;
|
|
136
|
+
}, {
|
|
137
|
+
cleared: number;
|
|
138
|
+
}>;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.engagementCallables = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
/**
|
|
7
|
+
* Contracts for bulk engagement emails (`Placementt-Backend/functions/src/bulkEngagement.ts`).
|
|
8
|
+
*
|
|
9
|
+
* An admin picks one admin email template and the schools to send it to (the
|
|
10
|
+
* backend suggests some; bulkEngagement.ts in core says which), and it goes
|
|
11
|
+
* now, through the same path as a single engagement email, so each send is
|
|
12
|
+
* an email interaction on the school. All of them are admin-only.
|
|
13
|
+
*/
|
|
14
|
+
const flag = zod_1.z.enum(["noContacts", "noRenewalDate", "noQuote", "noFinanceContact", "sentRecently"]);
|
|
15
|
+
const product = zod_1.z.enum(["wex", "employerEvents", "destinations"]);
|
|
16
|
+
exports.engagementCallables = {
|
|
17
|
+
"bulkEngagement-getBulkEngagementSchools": {
|
|
18
|
+
request: zod_1.z.object({
|
|
19
|
+
/** An admin email template (product "admin" in emailTemplates). */
|
|
20
|
+
templateKey: zod_1.z.string().min(1),
|
|
21
|
+
}),
|
|
22
|
+
response: zod_1.z.object({
|
|
23
|
+
schools: zod_1.z.array(zod_1.z.object({
|
|
24
|
+
oId: zod_1.z.string(),
|
|
25
|
+
name: zod_1.z.string(),
|
|
26
|
+
package: zod_1.z.string(),
|
|
27
|
+
/** The products the admin can filter this school by. */
|
|
28
|
+
products: zod_1.z.array(product),
|
|
29
|
+
suggested: zod_1.z.boolean(),
|
|
30
|
+
reason: zod_1.z.string().optional(),
|
|
31
|
+
flags: zod_1.z.array(flag),
|
|
32
|
+
/** How many of the school's contacts it would go to. */
|
|
33
|
+
contacts: zod_1.z.number(),
|
|
34
|
+
/** ISO date this email last went to the school, if it has. */
|
|
35
|
+
lastSent: zod_1.z.string().optional(),
|
|
36
|
+
/** The school's params, for previewing the email as they'd get it. */
|
|
37
|
+
params: zod_1.z.record(zod_1.z.string(), zod_1.z.string()),
|
|
38
|
+
})),
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
"bulkEngagement-sendBulkEngagementEmail": {
|
|
42
|
+
request: zod_1.z.object({
|
|
43
|
+
templateKey: zod_1.z.string().min(1),
|
|
44
|
+
oIds: zod_1.z.array(zod_1.z.string().min(1)).min(1, "Pick at least one school.").max(500),
|
|
45
|
+
subject: zod_1.z.string().trim().min(1, "Give it a subject."),
|
|
46
|
+
/** The Slate body, edited for this send. The template itself is unchanged. */
|
|
47
|
+
body: zod_1.z.array(zod_1.z.unknown()).min(1),
|
|
48
|
+
/** Must be in our storage bucket; checked on the server. */
|
|
49
|
+
heroImage: (0, types_1.optionalField)(zod_1.z.string()),
|
|
50
|
+
/** Send the weekly follow-ups: up to three, or until an onboarding step is done. */
|
|
51
|
+
followUps: zod_1.z.boolean(),
|
|
52
|
+
}),
|
|
53
|
+
/** Each school's outcome: who it went to, or why it didn't. */
|
|
54
|
+
response: zod_1.z.object({
|
|
55
|
+
results: zod_1.z.record(zod_1.z.string(), zod_1.z.object({ sent: zod_1.z.array(zod_1.z.string()), error: zod_1.z.string().optional() })),
|
|
56
|
+
}),
|
|
57
|
+
},
|
|
58
|
+
/** One-off: removes the open renewal email tasks the old automatic schedule made. */
|
|
59
|
+
"bulkEngagement-clearRenewalEmailTasks": {
|
|
60
|
+
request: zod_1.z.object({}).optional(),
|
|
61
|
+
response: zod_1.z.object({ cleared: zod_1.z.number() }),
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=engagement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engagement.js","sourceRoot":"","sources":["../../src/callables/engagement.ts"],"names":[],"mappings":"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"}
|
package/lib/callables/index.d.ts
CHANGED
package/lib/callables/index.js
CHANGED
|
@@ -32,4 +32,5 @@ __exportStar(require("./userManagement"), exports);
|
|
|
32
32
|
__exportStar(require("./outreach"), exports);
|
|
33
33
|
__exportStar(require("./formImport"), exports);
|
|
34
34
|
__exportStar(require("./activities"), exports);
|
|
35
|
+
__exportStar(require("./engagement"), exports);
|
|
35
36
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B;AAC3B,+CAA6B;AAC7B,+CAA6B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/callables/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0CAAwB;AACxB,6CAA2B;AAC3B,uDAAqC;AACrC,8CAA4B;AAC5B,2CAAyB;AACzB,6CAA2B;AAC3B,oDAAkC;AAClC,uCAAqB;AACrB,oDAAkC;AAClC,4CAA0B;AAC1B,oDAAkC;AAClC,wCAAsB;AACtB,0CAAwB;AACxB,4CAA0B;AAC1B,mDAAiC;AACjC,6CAA2B;AAC3B,+CAA6B;AAC7B,+CAA6B;AAC7B,+CAA6B"}
|