placementt-core 11.0.533 → 11.0.892
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/constants.d.ts +13 -1
- package/lib/constants.js +86 -1
- package/lib/constants.js.map +1 -1
- package/lib/features/analytics/useAnalytics.d.ts +2 -0
- package/lib/features/analytics/useAnalytics.js +22 -19
- package/lib/features/analytics/useAnalytics.js.map +1 -1
- package/lib/features/global/downtime/useDowntime.d.ts +1 -0
- package/lib/features/global/downtime/useDowntime.js +9 -7
- package/lib/features/global/downtime/useDowntime.js.map +1 -1
- package/lib/features/global/users/useUserFunctions.js +1 -1
- package/lib/features/global/users/useUserFunctions.js.map +1 -1
- package/lib/features/jobs/jobsSlice.d.ts +10 -2
- package/lib/features/jobs/jobsSlice.js +5 -2
- package/lib/features/jobs/jobsSlice.js.map +1 -1
- package/lib/features/placements/studentPlacements/activePlacement.d.ts +5 -1
- package/lib/features/placements/studentPlacements/activePlacement.js +7 -3
- package/lib/features/placements/studentPlacements/activePlacement.js.map +1 -1
- package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.d.ts +3 -2
- package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js +4 -1
- package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js.map +1 -1
- package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.d.ts +2 -2
- package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js +1 -1
- package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js.map +1 -1
- package/lib/features/placements/studentPlacements/useStudentPlacements.d.ts +2 -12
- package/lib/features/placements/studentPlacements/useStudentPlacements.js +1 -26
- package/lib/features/placements/studentPlacements/useStudentPlacements.js.map +1 -1
- package/lib/features/updates/useUpdates.d.ts +1 -0
- package/lib/features/updates/useUpdates.js +13 -12
- package/lib/features/updates/useUpdates.js.map +1 -1
- package/lib/firebase/firebase.d.ts +5 -3
- package/lib/firebase/firebase.js +23 -12
- package/lib/firebase/firebase.js.map +1 -1
- package/lib/firebase/firebaseConfig.js.map +1 -1
- package/lib/firebase/firebaseQuery.d.ts +6 -2
- package/lib/firebase/firebaseQuery.js +11 -3
- package/lib/firebase/firebaseQuery.js.map +1 -1
- package/lib/firebase/readDatabase.d.ts +2 -4
- package/lib/firebase/readDatabase.js +30 -5
- package/lib/firebase/readDatabase.js.map +1 -1
- package/lib/firebase/writeDatabase.d.ts +6 -2
- package/lib/firebase/writeDatabase.js +2 -1
- package/lib/firebase/writeDatabase.js.map +1 -1
- package/lib/hooks.d.ts +277 -192
- package/lib/hooks.js +1437 -704
- package/lib/hooks.js.map +1 -1
- package/lib/reduxHooks.d.ts +122 -5
- package/lib/reduxHooks.js +132 -29
- package/lib/reduxHooks.js.map +1 -1
- package/lib/tasksAndTips.d.ts +19 -7
- package/lib/tasksAndTips.js +637 -164
- package/lib/tasksAndTips.js.map +1 -1
- package/lib/typeDefinitions.d.ts +321 -110
- package/lib/util.d.ts +15 -3
- package/lib/util.js +47 -10
- package/lib/util.js.map +1 -1
- package/package.json +7 -4
- package/src/constants.ts +91 -3
- package/src/features/analytics/useAnalytics.tsx +25 -17
- package/src/features/global/downtime/useDowntime.tsx +11 -7
- package/src/features/global/users/useUserFunctions.tsx +1 -1
- package/src/features/jobs/jobsSlice.ts +9 -3
- package/src/features/placements/studentPlacements/activePlacement.ts +8 -3
- package/src/features/placements/studentPlacements/completedStudentPlacementsSlice.ts +5 -2
- package/src/features/placements/studentPlacements/upcomingStudentPlacementsSlice.ts +2 -2
- package/src/features/placements/studentPlacements/useStudentPlacements.tsx +4 -28
- package/src/features/updates/useUpdates.tsx +14 -12
- package/src/firebase/firebase.tsx +26 -15
- package/src/firebase/firebaseConfig.tsx +1 -1
- package/src/firebase/firebaseQuery.tsx +11 -3
- package/src/firebase/readDatabase.tsx +34 -6
- package/src/firebase/writeDatabase.tsx +3 -1
- package/src/hooks.tsx +1804 -935
- package/src/reduxHooks.ts +144 -32
- package/src/tasksAndTips.ts +689 -166
- package/src/typeDefinitions.ts +373 -109
- package/src/util.ts +63 -18
package/src/tasksAndTips.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import {arrayUnion,
|
|
1
|
+
import {arrayUnion, where} from "firebase/firestore";
|
|
2
2
|
import FirebaseQuery from "./firebase/firebaseQuery";
|
|
3
|
-
import {CohortData, InstituteData, ProviderData, StudentPlacementData, UserData} from "./typeDefinitions";
|
|
3
|
+
import {CohortData, InstituteData, OrganisationAddress, ProviderData, SchoolData, StudentPlacementData, UserData} from "./typeDefinitions";
|
|
4
4
|
import {getAccess} from "./firebase/util";
|
|
5
5
|
import { convertDate } from "./firebase/util";
|
|
6
6
|
|
|
7
7
|
const firebaseQuery = new FirebaseQuery;
|
|
8
8
|
|
|
9
|
-
type InstituteTipNames = "createCohort"|"uploadStaff"|"assignStaffRoles"|"uploadPlacements"|"allowExternalPlacementUpload"|"uploadStaffGuidance"|"uploadStudentGuidance"
|
|
9
|
+
type InstituteTipNames = "createCohort"|"addSchools"|"uploadStaff"|"assignStaffRoles"|"uploadPlacements"|"allowExternalPlacementUpload"|"uploadStaffGuidance"|"uploadStudentGuidance"
|
|
10
10
|
type ProviderTipNames = string
|
|
11
11
|
type StudentTipNames = string
|
|
12
12
|
|
|
13
|
-
export type InstituteTaskNames = "missingParentEmail"|"verifyInsurance"|"verifyRiskAssessment"|"verifyDbsCheck"|"inactiveStudents"|"uploadStudents"|"inactiveStaff"|"requiredStage"|"approveExternalPlacement"|"overdueStage"
|
|
13
|
+
export type InstituteTaskNames = "missingParentEmail"|"approveAlumniConversation"|"outstandingReminders"|"invalidStaffEmails"|"invalidStudentEmails"|"invalidParentEmails"|"invalidProviderEmails"|"verifyInsurance"|"verifyRiskAssessment"|"verifyDbsCheck"|"inactiveStudents"|"uploadStudents"|"inactiveStaff"|"requiredStage"|"approveExternalPlacement"|"overdueStage"
|
|
14
14
|
export type StudentTaskNames = "completeOnboarding"
|
|
15
|
-
export type ProviderTaskNames = "applicationRequireReview"|"requestedVisiblePlacementListings"|"requestedVisibleAddresses"|"completeStudentDocs"|"reviewOnboarding"|"completeListing"|"completeAddress"|"registrationRequests"|"placementStarting"|"completeFeedback"|"setUpFeedback"
|
|
15
|
+
export type ProviderTaskNames = "applicationRequireReview"|"activateStaff"|"requestedVisiblePlacementListings"|"requestedVisibleAddresses"|"completeStudentDocs"|"uploadOnboarding"|"reviewOnboarding"|"completeListing"|"completeAddress"|"registrationRequests"|"placementStarting"|"completeFeedback"|"setUpFeedback"
|
|
16
16
|
|
|
17
17
|
// IF UPDATING LOGIC WITHIN THIS FILE, PLACEMENTT-BACKEND LOGIC MUST ALSO BE CHANGED ACCORDINGLY
|
|
18
18
|
|
|
@@ -34,7 +34,7 @@ type TasksObject = {
|
|
|
34
34
|
*/
|
|
35
35
|
type InstituteTipsObject = {
|
|
36
36
|
[key in InstituteTipNames]: {
|
|
37
|
-
callback: (user: UserData, organisation?:InstituteData
|
|
37
|
+
callback: (user: UserData, organisation?:InstituteData&ProviderData, additional?:{[key: string]: OrganisationAddress}) => Promise<TaskQueryReturnObject|TaskQueryReturnObject[]>,
|
|
38
38
|
};
|
|
39
39
|
};
|
|
40
40
|
type ProviderTipsObject = {
|
|
@@ -50,7 +50,7 @@ type StudentTipsObject = {
|
|
|
50
50
|
|
|
51
51
|
export type InstituteTaskObject = {
|
|
52
52
|
[key in InstituteTaskNames]: {
|
|
53
|
-
callback: (user: UserData, organisation:InstituteData
|
|
53
|
+
callback: ({user, organisation, cohorts, schools}:{user: UserData, organisation:InstituteData&ProviderData, cohorts: CohortData|[string, CohortData][], schools?: SchoolData|[string, SchoolData][]}) => Promise<TaskQueryReturnObject|TaskQueryReturnObject[]>,
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
56
|
|
|
@@ -69,6 +69,21 @@ const studentTips:StudentTipsObject = {}
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
const instituteTips:InstituteTipsObject = {
|
|
72
|
+
addSchools: {
|
|
73
|
+
callback: async (user, institute, schools) => {
|
|
74
|
+
if (!getAccess(user, "addSchools") || institute?.package !== "institutes-two") return;
|
|
75
|
+
if (Object.keys(schools as {[key: string]: OrganisationAddress}).length < 2) {
|
|
76
|
+
return {
|
|
77
|
+
title: "Add your schools",
|
|
78
|
+
message: "Add your schools. These will show up in the 'Cohorts' tab where you can assign cohorts of students to them.",
|
|
79
|
+
link: "/institutes/organisation/overview",
|
|
80
|
+
buttonTitle: "Add schools",
|
|
81
|
+
dismissible: true
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
},
|
|
86
|
+
},
|
|
72
87
|
createCohort: {
|
|
73
88
|
callback: async (user) => {
|
|
74
89
|
if (!getAccess(user, "createCohorts")) return;
|
|
@@ -183,9 +198,210 @@ const instituteTips:InstituteTipsObject = {
|
|
|
183
198
|
// Accept a cohort to any task
|
|
184
199
|
|
|
185
200
|
const instituteTasks:InstituteTaskObject = {
|
|
201
|
+
invalidStaffEmails: {
|
|
202
|
+
callback: async ({user, cohorts}) => {
|
|
203
|
+
if (!getAccess(user, "viewStaff") || !Array.isArray(cohorts)) return;
|
|
204
|
+
|
|
205
|
+
const staffCount = (await firebaseQuery.getCount(["users"], [where("oId", "==", user.oId), where("userType", "==", "Staff"), where("flags", "array-contains", "userEmailFailed")]));
|
|
206
|
+
if (staffCount > 0) {
|
|
207
|
+
return {
|
|
208
|
+
dismissible: false,
|
|
209
|
+
severity: "error",
|
|
210
|
+
title: `${staffCount} staff have invalid emails.`,
|
|
211
|
+
message: `${staffCount} staff accounts have invalid email addresses. Delete and reupload these users.`,
|
|
212
|
+
link: "/institutes/organisation/staff/all",
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
approveAlumniConversation: {
|
|
219
|
+
callback: async ({user, organisation, schools}) => {
|
|
220
|
+
if (!schools) {
|
|
221
|
+
if (!organisation.alumniConversations) return;
|
|
222
|
+
const convos = (await firebaseQuery.getCount(["alumniConversations"], [where("oId", "==", user.oId), where("open", "==", true), where("delivered", "==", "pending")]));
|
|
223
|
+
if (convos > 0) {
|
|
224
|
+
return {
|
|
225
|
+
dismissible: false,
|
|
226
|
+
severity: "info",
|
|
227
|
+
title: `${convos} alumni conversation message${convos > 1 ? "s" : ""} needs approval.`,
|
|
228
|
+
message: `There are ${convos} message${convos > 1 ? "s" : ""} to alumni for you to review and release.`,
|
|
229
|
+
link: "/institutes/network/alumni/conversations",
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
if (!Array.isArray(schools)) {
|
|
235
|
+
// One school
|
|
236
|
+
const convos = (await firebaseQuery.getCount(["alumniConversations"], [where("oId", "==", user.oId), where("schoolId", "==", schools.id), where("open", "==", true), where("delivered", "==", "pending")]));
|
|
237
|
+
if (convos > 0) {
|
|
238
|
+
return {
|
|
239
|
+
dismissible: false,
|
|
240
|
+
severity: "info",
|
|
241
|
+
title: `${convos} alumni conversation message${convos > 1 ? "s" : ""} needs approval.`,
|
|
242
|
+
message: `There are ${convos} message${convos > 1 ? "s" : ""} to alumni for you to review and release.`,
|
|
243
|
+
link: `/institutes/network/alumni/conversations/${schools.id}`,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const items = await Promise.all(schools.map(async ([id, school]) => {
|
|
250
|
+
if (!school.alumniConversations) return;
|
|
251
|
+
const convos = (await firebaseQuery.getCount(["alumniConversations"], [where("oId", "==", user.oId), where("schoolId", "==", id), where("open", "==", true), where("delivered", "==", "pending")]));
|
|
252
|
+
if (convos > 0) {
|
|
253
|
+
return {
|
|
254
|
+
dismissible: false,
|
|
255
|
+
severity: "info",
|
|
256
|
+
title: `${convos} alumni conversation message${convos > 1 ? "s" : ""} needs approval.`,
|
|
257
|
+
message: `${school.name}, has ${convos} outstanding alumni message${convos > 1 ? "s" : ""} to approve. Click to view.`,
|
|
258
|
+
link: `/institutes/network/alumni/conversations/${school.id}`,
|
|
259
|
+
} as TaskQueryReturnObject;
|
|
260
|
+
}
|
|
261
|
+
return;
|
|
262
|
+
}));
|
|
263
|
+
return items.filter((v) => v);
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
invalidStudentEmails: {
|
|
267
|
+
callback: async ({user, cohorts}) => {
|
|
268
|
+
if (!getAccess(user, "viewStudents") || user.viewStudents === "none") return;
|
|
269
|
+
|
|
270
|
+
if (!Array.isArray(cohorts)) {
|
|
271
|
+
const studentCount = (await firebaseQuery.getCount(["users"], [where("oId", "==", user.oId), where("userType", "==", "Students"), where("cohort", "==", cohorts.id), where("flags", "array-contains", "userEmailFailed")]));
|
|
272
|
+
if (studentCount > 0) {
|
|
273
|
+
return {
|
|
274
|
+
dismissible: false,
|
|
275
|
+
severity: "error",
|
|
276
|
+
title: `${studentCount} students have invalid emails.`,
|
|
277
|
+
message: `${studentCount} student accounts have invalid email addresses. Delete and reupload these users.`,
|
|
278
|
+
link: `/institutes/cohorts/${cohorts.id}/students`,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const items = await Promise.all(cohorts.map(async ([id, cohort]) => {
|
|
285
|
+
const studentCount = (await firebaseQuery.getCount(["users"], [where("oId", "==", user.oId), where("userType", "==", "Students"), where("cohort", "==", id), where("flags", "array-contains", "userEmailFailed")]));
|
|
286
|
+
if (studentCount > 0) {
|
|
287
|
+
return {
|
|
288
|
+
dismissible: false,
|
|
289
|
+
severity: "error",
|
|
290
|
+
title: `${studentCount} students have invalid emails.`,
|
|
291
|
+
message: `Your cohort, ${cohort.name}, has ${studentCount} student accounts with invalid email addresses. Delete and reupload these users.`,
|
|
292
|
+
link: `/institutes/cohorts/${id}/students`,
|
|
293
|
+
} as TaskQueryReturnObject;
|
|
294
|
+
}
|
|
295
|
+
return;
|
|
296
|
+
}));
|
|
297
|
+
return items.filter((v) => v);
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
outstandingReminders: {
|
|
301
|
+
callback: async ({user, cohorts}) => {
|
|
302
|
+
if (!Array.isArray(cohorts)) {
|
|
303
|
+
const reminderCount = (await firebaseQuery.getCount(["reminders"], [where("oId", "==", user.oId), where("uid", "==", user.id), where("dueDate", "<=", convertDate(new Date(), "dbstring") as string), where("cohort", "==", cohorts.id), where("status", "==", "upcoming")]));
|
|
304
|
+
if (reminderCount > 0) {
|
|
305
|
+
return {
|
|
306
|
+
dismissible: false,
|
|
307
|
+
severity: "primary",
|
|
308
|
+
title: `You have ${reminderCount} reminder${reminderCount > 1 ? "s" : ""}.`,
|
|
309
|
+
message: `You have ${reminderCount} outstanding placement reminder${reminderCount > 1 ? "s" : ""}. Click to view.`,
|
|
310
|
+
link: `/institutes/cohorts/${cohorts.id}/placements`,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const items = await Promise.all(cohorts.map(async ([id, cohort]) => {
|
|
317
|
+
const reminderCount = (await firebaseQuery.getCount(["reminders"], [where("oId", "==", user.oId), where("uid", "==", user.id), where("dueDate", "<=", convertDate(new Date(), "dbstring") as string), where("cohort", "==", id), where("status", "==", "upcoming")]));
|
|
318
|
+
if (reminderCount > 0) {
|
|
319
|
+
return {
|
|
320
|
+
dismissible: false,
|
|
321
|
+
severity: "primary",
|
|
322
|
+
title: `You have ${reminderCount} reminder${reminderCount > 1 ? "s" : ""}.`,
|
|
323
|
+
message: `Your cohort, ${cohort.name}, has ${reminderCount} outstanding placement reminder${reminderCount > 1 ? "s" : ""}. Click to view.`,
|
|
324
|
+
link: `/institutes/cohorts/${id}/placements`,
|
|
325
|
+
} as TaskQueryReturnObject;
|
|
326
|
+
}
|
|
327
|
+
return;
|
|
328
|
+
}));
|
|
329
|
+
return items.filter((v) => v);
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
invalidParentEmails: {
|
|
333
|
+
callback: async ({user, cohorts}) => {
|
|
334
|
+
if (!getAccess(user, "signOffPlacements") || user.viewStudents === "none") return;
|
|
335
|
+
|
|
336
|
+
if (!Array.isArray(cohorts)) {
|
|
337
|
+
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("flags", "array-contains", "parentEmailFailed")]));
|
|
338
|
+
if (placementCount > 0) {
|
|
339
|
+
return {
|
|
340
|
+
dismissible: false,
|
|
341
|
+
severity: "error",
|
|
342
|
+
title: `${placementCount} placements have invalid parent emails.`,
|
|
343
|
+
message: `Your cohort '${cohorts.name}' has placements with invalid parent emails. Click to view these placements.`,
|
|
344
|
+
link: `/institutes/cohorts/${cohorts.id}/placements?id=upcoming`,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const items = await Promise.all(cohorts.map(async ([id, cohort]) => {
|
|
351
|
+
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", id), where("flags", "array-contains", "parentEmailFailed")]));
|
|
352
|
+
if (placementCount > 0) {
|
|
353
|
+
return {
|
|
354
|
+
dismissible: false,
|
|
355
|
+
severity: "error",
|
|
356
|
+
title: `${placementCount} placements in '${cohort.name}' have invalid parent emails.`,
|
|
357
|
+
message: `Your cohort '${cohort.name}' has placements with invalid parent emails. Click to view these placements.`,
|
|
358
|
+
link: `/institutes/cohorts/${id}/placements?id=upcoming`,
|
|
359
|
+
buttonTitle: "Review placements",
|
|
360
|
+
} as TaskQueryReturnObject;
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}));
|
|
364
|
+
return items.filter((v) => v);
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
invalidProviderEmails: {
|
|
368
|
+
callback: async ({user, cohorts}) => {
|
|
369
|
+
if (!getAccess(user, "signOffPlacements") || user.viewStudents === "none") return;
|
|
370
|
+
|
|
371
|
+
if (!Array.isArray(cohorts)) {
|
|
372
|
+
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("flags", "array-contains", "providerEmailFailed")]));
|
|
373
|
+
if (placementCount > 0) {
|
|
374
|
+
return {
|
|
375
|
+
dismissible: false,
|
|
376
|
+
severity: "error",
|
|
377
|
+
title: `${placementCount} placements have invalid provider emails.`,
|
|
378
|
+
message: `Your cohort '${cohorts.name}' has placements with invalid provider emails. Click to view these placements.`,
|
|
379
|
+
link: `/institutes/cohorts/${cohorts.id}/placements?id=upcoming`,
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const items = await Promise.all(cohorts.map(async ([id, cohort]) => {
|
|
386
|
+
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", id), where("flags", "array-contains", "providerEmailFailed")]));
|
|
387
|
+
if (placementCount > 0) {
|
|
388
|
+
return {
|
|
389
|
+
dismissible: false,
|
|
390
|
+
severity: "error",
|
|
391
|
+
title: `${placementCount} placements in '${cohort.name}' have invalid provider emails.`,
|
|
392
|
+
message: `Your cohort '${cohort.name}' has placements with invalid provider emails. Click to view these placements.`,
|
|
393
|
+
link: `/institutes/cohorts/${id}/placements?id=upcoming`,
|
|
394
|
+
buttonTitle: "Review placements",
|
|
395
|
+
} as TaskQueryReturnObject;
|
|
396
|
+
}
|
|
397
|
+
return;
|
|
398
|
+
}));
|
|
399
|
+
return items.filter((v) => v);
|
|
400
|
+
},
|
|
401
|
+
},
|
|
186
402
|
requiredStage: {
|
|
187
|
-
callback: async (user,
|
|
188
|
-
if (!getAccess(user, "signOffPlacements")) return;
|
|
403
|
+
callback: async ({user, cohorts}) => {
|
|
404
|
+
if (!getAccess(user, "signOffPlacements") || user.viewStudents === "none") return;
|
|
189
405
|
|
|
190
406
|
if (!Array.isArray(cohorts)) {
|
|
191
407
|
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("reqUserType", "==", user.userType)]));
|
|
@@ -219,8 +435,8 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
219
435
|
},
|
|
220
436
|
},
|
|
221
437
|
verifyInsurance: {
|
|
222
|
-
callback: async (user,
|
|
223
|
-
if (!getAccess(user, "verifyInsurance")) return;
|
|
438
|
+
callback: async ({user, cohorts}) => {
|
|
439
|
+
if (!getAccess(user, "verifyInsurance") || user.viewStudents === "none") return;
|
|
224
440
|
|
|
225
441
|
if (!Array.isArray(cohorts)) {
|
|
226
442
|
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("insurance", "==", "awaitingReview")]));
|
|
@@ -254,8 +470,8 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
254
470
|
},
|
|
255
471
|
},
|
|
256
472
|
verifyDbsCheck: {
|
|
257
|
-
callback: async (user,
|
|
258
|
-
if (!getAccess(user, "verifyDbsChecks")) return;
|
|
473
|
+
callback: async ({user, cohorts}) => {
|
|
474
|
+
if (!getAccess(user, "verifyDbsChecks") || user.viewStudents === "none") return;
|
|
259
475
|
|
|
260
476
|
if (!Array.isArray(cohorts)) {
|
|
261
477
|
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("dbsCheck", "==", "awaitingReview")]));
|
|
@@ -289,8 +505,8 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
289
505
|
},
|
|
290
506
|
},
|
|
291
507
|
verifyRiskAssessment: {
|
|
292
|
-
callback: async (user,
|
|
293
|
-
if (!getAccess(user, "verifyRiskAssessments")) return;
|
|
508
|
+
callback: async ({user, cohorts}) => {
|
|
509
|
+
if (!getAccess(user, "verifyRiskAssessments") || user.viewStudents === "none") return;
|
|
294
510
|
|
|
295
511
|
if (!Array.isArray(cohorts)) {
|
|
296
512
|
const placementCount = (await firebaseQuery.getCount(["placements"], [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("riskAssessment", "==", "awaitingReview")]));
|
|
@@ -324,8 +540,8 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
324
540
|
},
|
|
325
541
|
},
|
|
326
542
|
missingParentEmail: {
|
|
327
|
-
callback: async (user,
|
|
328
|
-
if (!getAccess(user, "editStudents")) return;
|
|
543
|
+
callback: async ({user, cohorts}) => {
|
|
544
|
+
if (!getAccess(user, "editStudents") || user.viewStudents === "none") return;
|
|
329
545
|
if (!Array.isArray(cohorts)) {
|
|
330
546
|
const requiresParents = Boolean(cohorts.workflow.find((node) => node.userType === "Parent"))
|
|
331
547
|
if (!requiresParents) return;
|
|
@@ -371,8 +587,8 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
371
587
|
},
|
|
372
588
|
},
|
|
373
589
|
inactiveStudents: {
|
|
374
|
-
callback: async (user,
|
|
375
|
-
if (!getAccess(user, "activateStudents")) return;
|
|
590
|
+
callback: async ({user, cohorts}) => {
|
|
591
|
+
if (!getAccess(user, "activateStudents") || user.viewStudents === "none") return;
|
|
376
592
|
if (!Array.isArray(cohorts)) {
|
|
377
593
|
const constraints = [where("oId", "==", user.oId), where("cohort", "==", cohorts.id), where("status", "==", "inactive")];
|
|
378
594
|
|
|
@@ -406,7 +622,7 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
406
622
|
title: `${studentCount} students in '${cohort.name}' are inactive.`,
|
|
407
623
|
message: `Your cohort '${cohort.name}' has inactive students. Activate them to enable them to use the platform.`,
|
|
408
624
|
link: `/institutes/cohorts/${cohort.id}/students?status=inactive`,
|
|
409
|
-
buttonTitle: "Review
|
|
625
|
+
buttonTitle: "Review students",
|
|
410
626
|
} as TaskQueryReturnObject;
|
|
411
627
|
}
|
|
412
628
|
return;
|
|
@@ -415,9 +631,9 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
415
631
|
},
|
|
416
632
|
},
|
|
417
633
|
uploadStudents: {
|
|
418
|
-
callback: async (user,
|
|
634
|
+
callback: async ({user, cohorts}) => {
|
|
419
635
|
if (!Array.isArray(cohorts)) return;
|
|
420
|
-
if (!getAccess(user, "addStudents")) return;
|
|
636
|
+
if (!getAccess(user, "addStudents") || user.viewStudents === "none") return;
|
|
421
637
|
if (user.product !== "institutes") return;
|
|
422
638
|
const returnObj: TaskQueryReturnObject = {
|
|
423
639
|
link: "",
|
|
@@ -441,7 +657,7 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
441
657
|
},
|
|
442
658
|
},
|
|
443
659
|
inactiveStaff: {
|
|
444
|
-
callback: async (user,
|
|
660
|
+
callback: async ({user, cohorts}) => {
|
|
445
661
|
if (!getAccess(user, "addStaff")) return;
|
|
446
662
|
|
|
447
663
|
if (!Array.isArray(cohorts) || user.product === "students") return;
|
|
@@ -462,15 +678,15 @@ const instituteTasks:InstituteTaskObject = {
|
|
|
462
678
|
},
|
|
463
679
|
},
|
|
464
680
|
overdueStage: {
|
|
465
|
-
callback: async (user,
|
|
466
|
-
console.log(user,
|
|
681
|
+
callback: async ({user, cohorts}) => {
|
|
682
|
+
console.log(user, cohorts);
|
|
467
683
|
return undefined;
|
|
468
684
|
},
|
|
469
685
|
},
|
|
470
686
|
approveExternalPlacement: {
|
|
471
|
-
callback: async (user,
|
|
687
|
+
callback: async ({user, cohorts}) => {
|
|
472
688
|
if (!getAccess(user, "verifyListings")) return;
|
|
473
|
-
if (!Array.isArray(
|
|
689
|
+
if (!Array.isArray(cohorts)) return;
|
|
474
690
|
const externalCount = await firebaseQuery.getCount(["placementListings"],
|
|
475
691
|
[where(`savedBy.${user.oId}.exists`, "==", true), where(`savedBy.${user.oId}.status`, "==", "uploaded"), where("mapConsent", "==", "institute")]);
|
|
476
692
|
console.log("EXT", externalCount);
|
|
@@ -493,13 +709,12 @@ const studentTasks:StudentTaskObject = {
|
|
|
493
709
|
completeOnboarding: {
|
|
494
710
|
callback: async (user) => {
|
|
495
711
|
const placementsWithoutOnboarding = await firebaseQuery.getDocsWhere("placements", [where("uid", "==", user.id), where("onboarding.deadline", "<=", convertDate(new Date(), "dbstring")), where("onboarding.completed.submitted", "==", false)]) as {[key: string]: StudentPlacementData};
|
|
496
|
-
console.log("placementsWithoutONboarding", placementsWithoutOnboarding);
|
|
497
712
|
if (Object.keys(placementsWithoutOnboarding).length === 0) return;
|
|
498
713
|
const items = Object.entries(placementsWithoutOnboarding).map(([k, placement]) => ({
|
|
499
714
|
dismissible: false,
|
|
500
715
|
severity: "primary",
|
|
501
|
-
title: `Complete
|
|
502
|
-
message:
|
|
716
|
+
title: `Complete onboarding for ${placement.name}`,
|
|
717
|
+
message: `Review onboarding for your placement starting on ${convertDate(placement.startDate, "visual")}`,
|
|
503
718
|
link: `/${user.product}/placements/${k}`,
|
|
504
719
|
buttonTitle: "View onboarding",
|
|
505
720
|
} as TaskQueryReturnObject))
|
|
@@ -508,117 +723,410 @@ const studentTasks:StudentTaskObject = {
|
|
|
508
723
|
}
|
|
509
724
|
}
|
|
510
725
|
|
|
511
|
-
const providerTasks:ProviderTaskObject = {
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
726
|
+
// const providerTasks:ProviderTaskObject = {
|
|
727
|
+
// requestedVisibleAddresses: {
|
|
728
|
+
// callback: async (user) => {
|
|
729
|
+
// if (!getAccess(user, "addStaff")) return;
|
|
730
|
+
// const accessRequests = await firebaseQuery.getCount("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleAddresses")]) - ((Array.isArray(user?.requestedVisibleAddresses) && user?.requestedVisibleAddresses.length > 0) ? 1 : 0);
|
|
731
|
+
// if (accessRequests === 0) return;
|
|
732
|
+
// if (accessRequests === 1) {
|
|
733
|
+
// const userRequestingAccess = Object.entries(await firebaseQuery.getDocsWhere("users", [where("product", "==", "providers"), where("oId", "==", user.oId), orderBy("requestedVisibleAddresses")]) || {})[0] as [string, UserData];
|
|
734
|
+
// return {
|
|
735
|
+
// dismissible: false,
|
|
736
|
+
// severity: "primary",
|
|
737
|
+
// title: `${userRequestingAccess[1].details.forename} ${userRequestingAccess[1].details.surname} has requested access to view addresses.`,
|
|
738
|
+
// message: `Click to review the addresses and grant access to the user.`,
|
|
739
|
+
// link: `/${user.product}/users/${userRequestingAccess[0]}`,
|
|
740
|
+
// buttonTitle: "View request",
|
|
741
|
+
// } as TaskQueryReturnObject;
|
|
742
|
+
// }
|
|
527
743
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
744
|
+
// return {
|
|
745
|
+
// dismissible: false,
|
|
746
|
+
// severity: "warning",
|
|
747
|
+
// title: `Multiple users have requested access to view addresses.`,
|
|
748
|
+
// message: `Click to review the addresses and grant access to the user.`,
|
|
749
|
+
// link: `/${user.product}/organisation/staff/all`,
|
|
750
|
+
// buttonTitle: "View request",
|
|
751
|
+
// } as TaskQueryReturnObject;
|
|
752
|
+
// },
|
|
753
|
+
// },
|
|
754
|
+
// requestedVisiblePlacementListings: {
|
|
755
|
+
// callback: async (user) => {
|
|
756
|
+
// if (!getAccess(user, "addStaff")) return;
|
|
757
|
+
// const accessRequests = await firebaseQuery.getCount("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleListings")])- ((Array.isArray(user?.requestedVisibleListings) && user?.requestedVisibleListings.length > 0) ? 1 : 0);;
|
|
758
|
+
// if (accessRequests === 0) return;
|
|
759
|
+
// if (accessRequests === 1) {
|
|
760
|
+
// const userRequestingAccess = Object.entries(await firebaseQuery.getDocsWhere("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleListings")]) || {})[0] as [string, UserData];
|
|
761
|
+
// return {
|
|
762
|
+
// dismissible: false,
|
|
763
|
+
// severity: "primary",
|
|
764
|
+
// title: `${userRequestingAccess[1].details.forename} ${userRequestingAccess[1].details.surname} has requested access to view placement listings.`,
|
|
765
|
+
// message: `Click to review the placement listings and grant access to the user.`,
|
|
766
|
+
// link: `/${user.product}/users/${userRequestingAccess[0]}`,
|
|
767
|
+
// buttonTitle: "View request",
|
|
768
|
+
// } as TaskQueryReturnObject;
|
|
769
|
+
// }
|
|
553
770
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (applicationCount === 0) return;
|
|
771
|
+
// return {
|
|
772
|
+
// dismissible: false,
|
|
773
|
+
// severity: "warning",
|
|
774
|
+
// title: `Multiple users have requested access to view placement listings.`,
|
|
775
|
+
// message: `Click to review the placement listings and grant access to the user.`,
|
|
776
|
+
// link: `/${user.product}/organisation/staff/all`,
|
|
777
|
+
// buttonTitle: "View request",
|
|
778
|
+
// } as TaskQueryReturnObject;
|
|
779
|
+
// },
|
|
780
|
+
// },
|
|
781
|
+
// applicationRequireReview: {
|
|
782
|
+
// callback: async (user) => {
|
|
783
|
+
// const constraints = [where("providerId", "==", user.oId), where("reqUserType", "==", "Staff"), where("status", "==", "submitted")];
|
|
568
784
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
785
|
+
// if (user.userGroup !== "admin") {
|
|
786
|
+
|
|
787
|
+
// if (!user.viewPlacementListings || user.viewPlacementListings === "none") return;
|
|
788
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
789
|
+
|
|
790
|
+
// if (user.viewPlacementListings === "request") {
|
|
791
|
+
// if (!user.visibleListings || user.visibleListings?.length === 0) return;
|
|
792
|
+
// constraints.push(where("listingId", 'in', user.visibleListings));
|
|
793
|
+
// } else {
|
|
794
|
+
// // viewPlacementListings must be 'all'
|
|
795
|
+
|
|
796
|
+
// if (user.viewAddresses === "request") {
|
|
797
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
798
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
799
|
+
// }
|
|
800
|
+
// }
|
|
801
|
+
// }
|
|
802
|
+
// const applicationCount = await firebaseQuery.getCount("applications", constraints);
|
|
803
|
+
// if (applicationCount === 0) return;
|
|
804
|
+
|
|
805
|
+
// return {
|
|
806
|
+
// dismissible: false,
|
|
807
|
+
// severity: "primary",
|
|
808
|
+
// title: `${applicationCount} applications require your review.`,
|
|
809
|
+
// message: `Click to view ${applicationCount} that require your attention.`,
|
|
810
|
+
// link: `/${user.product}/placementListings/applicants`,
|
|
811
|
+
// buttonTitle: "View applications",
|
|
812
|
+
// } as TaskQueryReturnObject;
|
|
813
|
+
// },
|
|
814
|
+
// },
|
|
815
|
+
// completeStudentDocs: {
|
|
816
|
+
// callback: async (user) => {
|
|
817
|
+
// return;
|
|
818
|
+
// return {} as TaskQueryReturnObject;
|
|
819
|
+
// },
|
|
820
|
+
// },
|
|
821
|
+
// reviewOnboarding: {
|
|
822
|
+
// callback: async (user) => {
|
|
823
|
+
// const constraints = [where("providerId", "==", user.oId), where("onboarding.completed.accepted", "==", false), where("onboarding.completed.submitted", "==", true), where("endDate", ">=", dateToString(new Date()))]
|
|
824
|
+
|
|
825
|
+
// if (user.userGroup !== "admin") {
|
|
826
|
+
|
|
827
|
+
// if (!user.viewPlacementListings || user.viewPlacementListings === "none") return;
|
|
828
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
829
|
+
|
|
830
|
+
// if (user.viewPlacementListings === "request") {
|
|
831
|
+
// if (!user.visibleListings || user.visibleListings?.length === 0) return;
|
|
832
|
+
// constraints.push(where("placementId", 'in', user.visibleListings));
|
|
833
|
+
// } else {
|
|
834
|
+
// // viewPlacementListings must be 'all'
|
|
835
|
+
|
|
836
|
+
// if (user.viewAddresses === "request") {
|
|
837
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
838
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
839
|
+
// }
|
|
840
|
+
// }
|
|
841
|
+
// }
|
|
842
|
+
// const toReview = await firebaseQuery.getCount("placements", constraints);
|
|
843
|
+
|
|
844
|
+
// if (toReview === 0) return;
|
|
845
|
+
// if (toReview === 1) {
|
|
846
|
+
// const placement = Object.entries(await firebaseQuery.getDocsWhere("placements", constraints) || {})[0] as [string, StudentPlacementData];
|
|
847
|
+
// const student = await firebaseQuery.getDocData(["users", placement[1].uid]) as UserData;
|
|
848
|
+
// return {
|
|
849
|
+
// dismissible: false,
|
|
850
|
+
// severity: "primary",
|
|
851
|
+
// title: `${student.details.forename} ${student.details.surname} has completed their onboarding for their placement from ${convertDate(placement[1].startDate, "visual")} to ${convertDate(placement[1].endDate, "visual")}`,
|
|
852
|
+
// message: `Click to view the placement and review the onboarding.`,
|
|
853
|
+
// link: `/${user.product}/placements/${placement[0]}`,
|
|
854
|
+
// buttonTitle: "View",
|
|
855
|
+
// } as TaskQueryReturnObject;
|
|
856
|
+
// }
|
|
857
|
+
|
|
858
|
+
// return {
|
|
859
|
+
// dismissible: false,
|
|
860
|
+
// severity: "primary",
|
|
861
|
+
// title: `${toReview} student have completed their onboarding.`,
|
|
862
|
+
// message: `Click to view your placements and approve completed onboarding`,
|
|
863
|
+
// link: `/${user.product}/placementListings/placements`,
|
|
864
|
+
// buttonTitle: "View placements",
|
|
865
|
+
// } as TaskQueryReturnObject;
|
|
866
|
+
// },
|
|
867
|
+
// },
|
|
868
|
+
// uploadOnboarding: {
|
|
869
|
+
// callback: async (user) => {
|
|
870
|
+
|
|
871
|
+
// const constraints = [where("providerId", "==", user.oId), where("onboarding", "==", null), where("endDate", ">=", dateToString(new Date()))];
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
// if (user.userGroup !== "admin") {
|
|
875
|
+
|
|
876
|
+
// if (!user.viewPlacementListings || user.viewPlacementListings === "none") return;
|
|
877
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
878
|
+
|
|
879
|
+
// if (user.viewPlacementListings === "request") {
|
|
880
|
+
// if (!user.visibleListings || user.visibleListings?.length === 0) return;
|
|
881
|
+
// constraints.push(where("placementId", 'in', user.visibleListings));
|
|
882
|
+
// } else {
|
|
883
|
+
// // viewPlacementListings must be 'all'
|
|
884
|
+
|
|
885
|
+
// if (user.viewAddresses === "request") {
|
|
886
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
887
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
888
|
+
// }
|
|
889
|
+
// }
|
|
890
|
+
// }
|
|
891
|
+
|
|
892
|
+
// const withoutOnboarding = await firebaseQuery.getCount("placements", constraints);
|
|
893
|
+
|
|
894
|
+
// if (withoutOnboarding === 0) return;
|
|
895
|
+
// if (withoutOnboarding === 1) {
|
|
896
|
+
// const placement = Object.entries(await firebaseQuery.getDocsWhere("placements", constraints) || {})[0] as [string, StudentPlacementData];
|
|
897
|
+
// const student = await firebaseQuery.getDocData(["users", placement[1].uid]) as UserData;
|
|
898
|
+
// return {
|
|
899
|
+
// dismissible: false,
|
|
900
|
+
// severity: "primary",
|
|
901
|
+
// title: `Send onboarding documents to ${student.details.forename} ${student.details.surname}'s placement from ${convertDate(placement[1].startDate, "visual")} to ${convertDate(placement[1].endDate, "visual")}`,
|
|
902
|
+
// message: `Click to view the placement and add or dismiss onboarding reminders.`,
|
|
903
|
+
// link: `/${user.product}/placements/${placement[0]}`,
|
|
904
|
+
// buttonTitle: "View",
|
|
905
|
+
// } as TaskQueryReturnObject;
|
|
906
|
+
// }
|
|
907
|
+
|
|
908
|
+
// return {
|
|
909
|
+
// dismissible: false,
|
|
910
|
+
// severity: "primary",
|
|
911
|
+
// title: `Set up onboarding for ${withoutOnboarding} placements to prepare yourself and your students.`,
|
|
912
|
+
// message: `Click to view your placements and add or dismiss onboarding reminders.`,
|
|
913
|
+
// link: `/${user.product}/placementListings/placements`,
|
|
914
|
+
// buttonTitle: "View placements",
|
|
915
|
+
// } as TaskQueryReturnObject;
|
|
916
|
+
// },
|
|
917
|
+
// },
|
|
918
|
+
// completeListing: {
|
|
919
|
+
// callback: async (user) => {
|
|
920
|
+
// const constraints = [where("providerId", "==", user.oId), where("status", "==", "draft")];
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
// if (user.userGroup !== "admin") {
|
|
924
|
+
|
|
925
|
+
// if (!user.viewPlacementListings || user.viewPlacementListings === "none") return;
|
|
926
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
927
|
+
|
|
928
|
+
// if (user.viewPlacementListings === "request") {
|
|
929
|
+
// if (!user.visibleListings || user.visibleListings?.length === 0) return;
|
|
930
|
+
// constraints.push(where(documentId(), 'in', user.visibleListings));
|
|
931
|
+
// } else {
|
|
932
|
+
// // viewPlacementListings must be 'all'
|
|
933
|
+
|
|
934
|
+
// if (user.viewAddresses === "request") {
|
|
935
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
936
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
937
|
+
// }
|
|
938
|
+
// }
|
|
939
|
+
// }
|
|
940
|
+
// const incompleteListings = await firebaseQuery.getCount("placementListings", constraints);
|
|
941
|
+
// if (incompleteListings === 0) return;
|
|
942
|
+
// if (incompleteListings === 1) {
|
|
943
|
+
// const incompleteListing = Object.entries(await firebaseQuery.getDocsWhere("placementListings", constraints) || {})[0] as [string, PlacementListing];
|
|
944
|
+
// const address = incompleteListing[1].addressId ? await firebaseQuery.getDocData(["addresses", incompleteListing[1].addressId]) as OrganisationAddress : undefined;
|
|
945
|
+
// return {
|
|
946
|
+
// dismissible: false,
|
|
947
|
+
// severity: "info",
|
|
948
|
+
// title: `Your listing '${incompleteListing[1].title || "unnamed"}' at ${address ? `${address["address-line1"]}, ${address.postal_code.toUpperCase()}, ${capitaliseWords(camelCaseToNormal(address.country))}` : "unknown address"} requires more information before publishing.`,
|
|
949
|
+
// message: `Click to complete and publish the placement listing.`,
|
|
950
|
+
// link: `/${user.product}/addListing/${incompleteListing[0]}`,
|
|
951
|
+
// buttonTitle: "View listing",
|
|
952
|
+
// } as TaskQueryReturnObject;
|
|
953
|
+
// }
|
|
954
|
+
|
|
955
|
+
// return {
|
|
956
|
+
// dismissible: false,
|
|
957
|
+
// severity: "info",
|
|
958
|
+
// title: `You have ${incompleteListings} draft listings waiting to be published.`,
|
|
959
|
+
// message: `Click to review and publish the placement listings.`,
|
|
960
|
+
// link: `/${user.product}/placementListings/listings`,
|
|
961
|
+
// buttonTitle: "View listings",
|
|
962
|
+
// } as TaskQueryReturnObject;
|
|
963
|
+
// },
|
|
964
|
+
// },
|
|
965
|
+
// completeAddress: {
|
|
966
|
+
// callback: async (user) => {
|
|
967
|
+
// const constraints = [where("product", "==", "providers"), where("oId", "==", user.oId), where("stage", "!=", "complete")];
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
// if (user.userGroup !== "admin") {
|
|
971
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
620
972
|
|
|
621
|
-
|
|
973
|
+
// if (user.viewAddresses === "request") {
|
|
974
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
975
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
976
|
+
// }
|
|
977
|
+
// }
|
|
978
|
+
// const incompleteAddresses = await firebaseQuery.getCount("addresses", constraints);
|
|
979
|
+
// if (incompleteAddresses === 0) return;
|
|
980
|
+
// if (incompleteAddresses === 1) {
|
|
981
|
+
// const address = Object.entries(await firebaseQuery.getDocsWhere("addresses", constraints) || {})[0] as [string, OrganisationAddress];
|
|
982
|
+
// return {
|
|
983
|
+
// dismissible: false,
|
|
984
|
+
// severity: "info",
|
|
985
|
+
// title: `Your address: ${address[1]["address-line1"]}, ${address[1].postal_code.toUpperCase()}, ${capitaliseWords(camelCaseToNormal(address[1].country))} is currently incomplete.`,
|
|
986
|
+
// message: `Click to complete the addresses.`,
|
|
987
|
+
// link: `/${user.product}/addAddress/${address[0]}`,
|
|
988
|
+
// buttonTitle: "View address",
|
|
989
|
+
// } as TaskQueryReturnObject;
|
|
990
|
+
// }
|
|
991
|
+
|
|
992
|
+
// return {
|
|
993
|
+
// dismissible: false,
|
|
994
|
+
// severity: "info",
|
|
995
|
+
// title: `You have ${incompleteAddresses} draft addresses waiting to be published.`,
|
|
996
|
+
// message: `Click to review the addresses.`,
|
|
997
|
+
// link: `/${user.product}/organisation/addresses`,
|
|
998
|
+
// buttonTitle: "View addresses",
|
|
999
|
+
// } as TaskQueryReturnObject;
|
|
1000
|
+
// },
|
|
1001
|
+
// },
|
|
1002
|
+
// registrationRequests: {
|
|
1003
|
+
// callback: async (user) => {
|
|
1004
|
+
// if (!getAccess(user, "addStaff")) return;
|
|
1005
|
+
|
|
1006
|
+
// const regRequests = await firebaseQuery.getCount("requests", [where("product", "==", user.product), where("oId", "==", user.oId)]);
|
|
1007
|
+
|
|
1008
|
+
// if (regRequests === 0) return;
|
|
1009
|
+
// if (regRequests === 1) {
|
|
1010
|
+
// const request = Object.entries(await firebaseQuery.getDocsWhere("requests", [where("product", "==", user.product), where("oId", "==", user.oId)]) || {})[0] as [string, RegistrationRequest];
|
|
1011
|
+
// return {
|
|
1012
|
+
// dismissible: false,
|
|
1013
|
+
// severity: "primary",
|
|
1014
|
+
// title: `${request[1].forename} ${request[1].surname} has requested to access your organisation.`,
|
|
1015
|
+
// message: `Click to review these request.`,
|
|
1016
|
+
// link: `/${user.product}/organisation/staff/requests`,
|
|
1017
|
+
// buttonTitle: "View requests",
|
|
1018
|
+
// } as TaskQueryReturnObject;
|
|
1019
|
+
// }
|
|
1020
|
+
|
|
1021
|
+
// return {
|
|
1022
|
+
// dismissible: false,
|
|
1023
|
+
// severity: "primary",
|
|
1024
|
+
// title: `${regRequests} people have requested to register with your organisation.`,
|
|
1025
|
+
// message: `Click to review these requests.`,
|
|
1026
|
+
// link: `/${user.product}/organisation/staff/requests`,
|
|
1027
|
+
// buttonTitle: "View requests",
|
|
1028
|
+
// } as TaskQueryReturnObject;
|
|
1029
|
+
// },
|
|
1030
|
+
// },
|
|
1031
|
+
// activateStaff: {
|
|
1032
|
+
// callback: async (user) => {
|
|
1033
|
+
// if (!getAccess(user, "addStaff")) return;
|
|
1034
|
+
|
|
1035
|
+
// const inactiveAccounts = await firebaseQuery.getCount("users", [where("product", "==", user.product), where("oId", "==", user.oId), where("status", "==", "inactive")]);
|
|
1036
|
+
|
|
1037
|
+
// if (inactiveAccounts === 0) return;
|
|
1038
|
+
// if (inactiveAccounts === 1) {
|
|
1039
|
+
// const account = Object.entries(await firebaseQuery.getDocsWhere("users", [where("product", "==", user.product), where("oId", "==", user.oId), where("status", "==", "inactive")]) || {})[0] as [string, UserData];
|
|
1040
|
+
// return {
|
|
1041
|
+
// dismissible: false,
|
|
1042
|
+
// severity: "info",
|
|
1043
|
+
// title: `Activate ${account[1].details.forename} ${account[1].details.surname}'s staff account.`,
|
|
1044
|
+
// message: "Activate this account to give the user access to Placementt.",
|
|
1045
|
+
// link: `/${user.product}/organisation/staff/all`,
|
|
1046
|
+
// buttonTitle: "View accounts",
|
|
1047
|
+
// } as TaskQueryReturnObject;
|
|
1048
|
+
// }
|
|
1049
|
+
|
|
1050
|
+
// return {
|
|
1051
|
+
// dismissible: false,
|
|
1052
|
+
// severity: "info",
|
|
1053
|
+
// title: `${inactiveAccounts} staff have inactive active accounts.`,
|
|
1054
|
+
// message: "Activate these accounts to give the user access to Placementt.",
|
|
1055
|
+
// link: `/${user.product}/organisation/staff/all`,
|
|
1056
|
+
// buttonTitle: "View accounts",
|
|
1057
|
+
// } as TaskQueryReturnObject;
|
|
1058
|
+
// },
|
|
1059
|
+
// },
|
|
1060
|
+
// placementStarting: {
|
|
1061
|
+
// callback: async (user) => {
|
|
1062
|
+
// const sevenDaysInFuture = new Date();
|
|
1063
|
+
// sevenDaysInFuture.setDate(sevenDaysInFuture.getDate() + 7)
|
|
1064
|
+
|
|
1065
|
+
// const constraints = [where("providerId", "==", user.oId), where("startDate", "<=", convertDate(sevenDaysInFuture, "dbstring")), where("startDate", ">", dateToString(new Date()))];
|
|
1066
|
+
|
|
1067
|
+
// if (user.userGroup !== "admin") {
|
|
1068
|
+
|
|
1069
|
+
// if (!user.viewPlacementListings || user.viewPlacementListings === "none") return;
|
|
1070
|
+
// if (!user.viewAddresses || user.viewAddresses === "none") return;
|
|
1071
|
+
|
|
1072
|
+
// if (user.viewPlacementListings === "request") {
|
|
1073
|
+
// if (!user.visibleListings || user.visibleListings?.length === 0) return;
|
|
1074
|
+
// constraints.push(where(documentId(), 'in', user.visibleListings));
|
|
1075
|
+
// } else {
|
|
1076
|
+
// // viewPlacementListings must be 'all'
|
|
1077
|
+
|
|
1078
|
+
// if (user.viewAddresses === "request") {
|
|
1079
|
+
// if (!user.visibleAddresses || user.visibleAddresses?.length === 0) return;
|
|
1080
|
+
// constraints.push(where("addressId", 'in', user.visibleAddresses));
|
|
1081
|
+
// }
|
|
1082
|
+
// }
|
|
1083
|
+
// }
|
|
1084
|
+
// const placementsStartingSoon = await firebaseQuery.getCount("placements", constraints);
|
|
1085
|
+
|
|
1086
|
+
// if (placementsStartingSoon === 0) return;
|
|
1087
|
+
// if (placementsStartingSoon === 1) {
|
|
1088
|
+
// const placement = Object.entries(await firebaseQuery.getDocsWhere("placements", constraints) || {})[0] as [string, StudentPlacementData];
|
|
1089
|
+
// const student = placement[1].uid ? await firebaseQuery.getDocData(["users", placement[1].uid]) as UserData : {
|
|
1090
|
+
// details: {
|
|
1091
|
+
// forename: placement[1].studentForename,
|
|
1092
|
+
// surname: placement[1].studentSurname,
|
|
1093
|
+
// }
|
|
1094
|
+
// };
|
|
1095
|
+
// return {
|
|
1096
|
+
// dismissible: false,
|
|
1097
|
+
// severity: "success",
|
|
1098
|
+
// title: `${student.details.forename} ${student.details.surname}'s placement from ${convertDate(placement[1].startDate, "visual")} to ${convertDate(placement[1].endDate, "visual")} is starting in less than a week.`,
|
|
1099
|
+
// message: `Click to view the placement and acquaint yourself with the student.`,
|
|
1100
|
+
// link: `/${user.product}/placements/${placement[0]}`,
|
|
1101
|
+
// buttonTitle: "View",
|
|
1102
|
+
// } as TaskQueryReturnObject;
|
|
1103
|
+
// }
|
|
1104
|
+
|
|
1105
|
+
// return {
|
|
1106
|
+
// dismissible: false,
|
|
1107
|
+
// severity: "success",
|
|
1108
|
+
// title: `${placementsStartingSoon} placements starting soon.`,
|
|
1109
|
+
// message: `Click to view scheduled placements.`,
|
|
1110
|
+
// link: `/${user.product}/placementListings/placements`,
|
|
1111
|
+
// buttonTitle: "View placements",
|
|
1112
|
+
// } as TaskQueryReturnObject;
|
|
1113
|
+
// },
|
|
1114
|
+
// },
|
|
1115
|
+
// completeFeedback: {
|
|
1116
|
+
// callback: async (user) => {
|
|
1117
|
+
// return;
|
|
1118
|
+
// return {} as TaskQueryReturnObject;
|
|
1119
|
+
// },
|
|
1120
|
+
// },
|
|
1121
|
+
// setUpFeedback: {
|
|
1122
|
+
// callback: async (user) => {
|
|
1123
|
+
// return;
|
|
1124
|
+
// return {} as TaskQueryReturnObject;
|
|
1125
|
+
// },
|
|
1126
|
+
// },
|
|
1127
|
+
// }
|
|
1128
|
+
|
|
1129
|
+
export const getTips = async (user: UserData, organisation: InstituteData|ProviderData, addresses?: {[key: string]: OrganisationAddress}):Promise<(TaskQueryReturnObject)[]> => {
|
|
622
1130
|
const tipsObject = {
|
|
623
1131
|
providers: providerTips,
|
|
624
1132
|
institutes: instituteTips,
|
|
@@ -627,9 +1135,14 @@ export const getTips = async (user: UserData, organisation: InstituteData|Provid
|
|
|
627
1135
|
const includedItems = Object.entries(tipsObject[user.product]).filter(([k]) => !user.dismissedTips?.includes(k));
|
|
628
1136
|
|
|
629
1137
|
const processedTips = await includedItems.reduce(async (acc, [itemName, item]) => {
|
|
1138
|
+
const callbackParams:[UserData, InstituteData|ProviderData] = [user, organisation];
|
|
1139
|
+
|
|
1140
|
+
if ((itemName === "addAddresses" || itemName === "addSchools") && addresses) {
|
|
1141
|
+
callbackParams.push(addresses as any);
|
|
1142
|
+
}
|
|
630
1143
|
const queryResult = await (item as {
|
|
631
1144
|
callback: (user: UserData, organisation?:InstituteData|ProviderData) => Promise<TaskQueryReturnObject|TaskQueryReturnObject[]>,
|
|
632
|
-
}).callback(
|
|
1145
|
+
}).callback(...callbackParams);
|
|
633
1146
|
if (!queryResult) return await acc;
|
|
634
1147
|
|
|
635
1148
|
const queryResultArray = Array.isArray(queryResult) ? queryResult : [queryResult];
|
|
@@ -641,17 +1154,17 @@ export const getTips = async (user: UserData, organisation: InstituteData|Provid
|
|
|
641
1154
|
return processedTips;
|
|
642
1155
|
}
|
|
643
1156
|
|
|
644
|
-
export const getTasks = async (user: UserData, organisation?: InstituteData
|
|
1157
|
+
export const getTasks = async ({user, organisation, cohort, school}:{user: UserData, organisation?: InstituteData&ProviderData, cohort?: CohortData, school?: SchoolData}):Promise<(TaskQueryReturnObject)[]> => {
|
|
645
1158
|
// Cohort is either a specific one or all.
|
|
646
1159
|
|
|
647
1160
|
if (user.product === "institutes" && user.userType === "Staff" && organisation) {
|
|
648
|
-
return await getInstituteTasks(user, organisation, cohort);
|
|
1161
|
+
return await getInstituteTasks({user, organisation, cohort, school});
|
|
649
1162
|
}
|
|
650
1163
|
if (user.product === "students" || user.userType === "Students") {
|
|
651
1164
|
return await getStudentTasks(user, organisation, cohort);
|
|
652
1165
|
}
|
|
653
1166
|
if (user.product === "providers" && organisation) {
|
|
654
|
-
return await getProviderTasks(user, organisation);
|
|
1167
|
+
// return await getProviderTasks(user, organisation);
|
|
655
1168
|
}
|
|
656
1169
|
|
|
657
1170
|
return [];
|
|
@@ -672,49 +1185,59 @@ const getStudentTasks = async (user: UserData, organisation?: InstituteData|Prov
|
|
|
672
1185
|
return processedTasks;
|
|
673
1186
|
};
|
|
674
1187
|
|
|
675
|
-
const getProviderTasks = async (user: UserData, organisation: InstituteData|ProviderData, cohort?: CohortData):Promise<(TaskQueryReturnObject)[]> => {
|
|
676
|
-
|
|
1188
|
+
// const getProviderTasks = async (user: UserData, organisation: InstituteData|ProviderData, cohort?: CohortData):Promise<(TaskQueryReturnObject)[]> => {
|
|
1189
|
+
// const processedTasks = await Object.entries(providerTasks).reduce(async (acc, [itemName, item]) => {
|
|
677
1190
|
|
|
678
|
-
|
|
679
|
-
|
|
1191
|
+
// const queryResult = await item.callback(user);
|
|
1192
|
+
// if (!queryResult) return await acc;
|
|
680
1193
|
|
|
681
|
-
|
|
682
|
-
|
|
1194
|
+
// const queryResultArray = Array.isArray(queryResult) ? queryResult : [queryResult];
|
|
1195
|
+
// const results = queryResultArray.map((r) => ({itemName: itemName as ProviderTaskNames, ...r}));
|
|
683
1196
|
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
1197
|
+
// (await acc).push(...results);
|
|
1198
|
+
// return await acc;
|
|
1199
|
+
// }, Promise.resolve<TaskQueryReturnObject[]>([]));
|
|
1200
|
+
// return processedTasks;};
|
|
688
1201
|
|
|
689
|
-
const getInstituteTasks = async (user: UserData, organisation: InstituteData
|
|
1202
|
+
const getInstituteTasks = async ({user, organisation, cohort, school}:{user: UserData, organisation: InstituteData&ProviderData, cohort?: CohortData, school?: SchoolData}):Promise<(TaskQueryReturnObject)[]> => {
|
|
690
1203
|
let fCohort:CohortData|[string, CohortData][]|undefined = cohort;
|
|
1204
|
+
let fSchool:SchoolData|[string, SchoolData][]|undefined = school;
|
|
691
1205
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (user.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
1206
|
+
const getCohortsOrSchoolsIfNotProvided = async (type: "cohorts"|"schools") => {
|
|
1207
|
+
if (user[`view${type}`] === "none") return([]);
|
|
1208
|
+
if (user.userGroup === "admin" || user[`view${type}`] === "all") {
|
|
1209
|
+
const constraints = [where("oId", "==", user.oId)];
|
|
1210
|
+
if (type === "cohorts") {
|
|
1211
|
+
constraints.push(where("product", "==", user.product), where("stage", "==", "created"));
|
|
1212
|
+
}
|
|
1213
|
+
const cohorts = await firebaseQuery.getDocsWhere(type, constraints) as {[key:string]: CohortData|SchoolData};
|
|
1214
|
+
return Object.entries(cohorts);
|
|
698
1215
|
}
|
|
699
|
-
if (user
|
|
700
|
-
const
|
|
701
|
-
const
|
|
702
|
-
if (
|
|
1216
|
+
if (user[`view${type}`] === "some") {
|
|
1217
|
+
const items = await user[`visible${type}s`]?.reduce(async (acc, itemId) => {
|
|
1218
|
+
const item = await firebaseQuery.getDocData([type, itemId]) as CohortData;
|
|
1219
|
+
if (type === "cohorts" && item.stage !== "created") {
|
|
703
1220
|
return acc;
|
|
704
1221
|
}
|
|
705
|
-
acc[
|
|
1222
|
+
acc[itemId] = item;
|
|
706
1223
|
return acc;
|
|
707
1224
|
}, Promise.resolve<[string, CohortData][]>([]));
|
|
708
|
-
|
|
1225
|
+
return items;
|
|
709
1226
|
}
|
|
710
1227
|
}
|
|
711
|
-
|
|
1228
|
+
|
|
1229
|
+
if (!fCohort) {
|
|
1230
|
+
fCohort = await getCohortsOrSchoolsIfNotProvided("cohorts");
|
|
1231
|
+
}
|
|
1232
|
+
if (!fSchool) {
|
|
1233
|
+
fSchool = await getCohortsOrSchoolsIfNotProvided("schools");
|
|
1234
|
+
}
|
|
712
1235
|
const processedTasks = await Object.entries(instituteTasks).reduce(async (acc, [itemName, item]) => {
|
|
713
1236
|
if (!fCohort) {
|
|
714
1237
|
console.log("No cohorts to retrieve tasks for");
|
|
715
1238
|
return([]);
|
|
716
1239
|
}
|
|
717
|
-
const queryResult = await item.callback(user, organisation, fCohort);
|
|
1240
|
+
const queryResult = await item.callback({user, organisation, cohorts: fCohort, schools: fSchool});
|
|
718
1241
|
if (!queryResult) return await acc;
|
|
719
1242
|
|
|
720
1243
|
const queryResultArray = Array.isArray(queryResult) ? queryResult : [queryResult];
|