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.
Files changed (76) hide show
  1. package/lib/constants.d.ts +13 -1
  2. package/lib/constants.js +86 -1
  3. package/lib/constants.js.map +1 -1
  4. package/lib/features/analytics/useAnalytics.d.ts +2 -0
  5. package/lib/features/analytics/useAnalytics.js +22 -19
  6. package/lib/features/analytics/useAnalytics.js.map +1 -1
  7. package/lib/features/global/downtime/useDowntime.d.ts +1 -0
  8. package/lib/features/global/downtime/useDowntime.js +9 -7
  9. package/lib/features/global/downtime/useDowntime.js.map +1 -1
  10. package/lib/features/global/users/useUserFunctions.js +1 -1
  11. package/lib/features/global/users/useUserFunctions.js.map +1 -1
  12. package/lib/features/jobs/jobsSlice.d.ts +10 -2
  13. package/lib/features/jobs/jobsSlice.js +5 -2
  14. package/lib/features/jobs/jobsSlice.js.map +1 -1
  15. package/lib/features/placements/studentPlacements/activePlacement.d.ts +5 -1
  16. package/lib/features/placements/studentPlacements/activePlacement.js +7 -3
  17. package/lib/features/placements/studentPlacements/activePlacement.js.map +1 -1
  18. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.d.ts +3 -2
  19. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js +4 -1
  20. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js.map +1 -1
  21. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.d.ts +2 -2
  22. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js +1 -1
  23. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js.map +1 -1
  24. package/lib/features/placements/studentPlacements/useStudentPlacements.d.ts +2 -12
  25. package/lib/features/placements/studentPlacements/useStudentPlacements.js +1 -26
  26. package/lib/features/placements/studentPlacements/useStudentPlacements.js.map +1 -1
  27. package/lib/features/updates/useUpdates.d.ts +1 -0
  28. package/lib/features/updates/useUpdates.js +13 -12
  29. package/lib/features/updates/useUpdates.js.map +1 -1
  30. package/lib/firebase/firebase.d.ts +5 -3
  31. package/lib/firebase/firebase.js +23 -12
  32. package/lib/firebase/firebase.js.map +1 -1
  33. package/lib/firebase/firebaseConfig.js.map +1 -1
  34. package/lib/firebase/firebaseQuery.d.ts +6 -2
  35. package/lib/firebase/firebaseQuery.js +11 -3
  36. package/lib/firebase/firebaseQuery.js.map +1 -1
  37. package/lib/firebase/readDatabase.d.ts +2 -4
  38. package/lib/firebase/readDatabase.js +30 -5
  39. package/lib/firebase/readDatabase.js.map +1 -1
  40. package/lib/firebase/writeDatabase.d.ts +6 -2
  41. package/lib/firebase/writeDatabase.js +2 -1
  42. package/lib/firebase/writeDatabase.js.map +1 -1
  43. package/lib/hooks.d.ts +277 -192
  44. package/lib/hooks.js +1437 -704
  45. package/lib/hooks.js.map +1 -1
  46. package/lib/reduxHooks.d.ts +122 -5
  47. package/lib/reduxHooks.js +132 -29
  48. package/lib/reduxHooks.js.map +1 -1
  49. package/lib/tasksAndTips.d.ts +19 -7
  50. package/lib/tasksAndTips.js +637 -164
  51. package/lib/tasksAndTips.js.map +1 -1
  52. package/lib/typeDefinitions.d.ts +321 -110
  53. package/lib/util.d.ts +15 -3
  54. package/lib/util.js +47 -10
  55. package/lib/util.js.map +1 -1
  56. package/package.json +7 -4
  57. package/src/constants.ts +91 -3
  58. package/src/features/analytics/useAnalytics.tsx +25 -17
  59. package/src/features/global/downtime/useDowntime.tsx +11 -7
  60. package/src/features/global/users/useUserFunctions.tsx +1 -1
  61. package/src/features/jobs/jobsSlice.ts +9 -3
  62. package/src/features/placements/studentPlacements/activePlacement.ts +8 -3
  63. package/src/features/placements/studentPlacements/completedStudentPlacementsSlice.ts +5 -2
  64. package/src/features/placements/studentPlacements/upcomingStudentPlacementsSlice.ts +2 -2
  65. package/src/features/placements/studentPlacements/useStudentPlacements.tsx +4 -28
  66. package/src/features/updates/useUpdates.tsx +14 -12
  67. package/src/firebase/firebase.tsx +26 -15
  68. package/src/firebase/firebaseConfig.tsx +1 -1
  69. package/src/firebase/firebaseQuery.tsx +11 -3
  70. package/src/firebase/readDatabase.tsx +34 -6
  71. package/src/firebase/writeDatabase.tsx +3 -1
  72. package/src/hooks.tsx +1804 -935
  73. package/src/reduxHooks.ts +144 -32
  74. package/src/tasksAndTips.ts +689 -166
  75. package/src/typeDefinitions.ts +373 -109
  76. package/src/util.ts +63 -18
@@ -1,18 +1,18 @@
1
- import {arrayUnion, orderBy, where} from "firebase/firestore";
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|ProviderData) => Promise<TaskQueryReturnObject|TaskQueryReturnObject[]>,
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|ProviderData, cohort: CohortData|[string, CohortData][]) => Promise<TaskQueryReturnObject|TaskQueryReturnObject[]>,
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, _, cohorts) => {
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, _, cohorts) => {
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, _, cohorts) => {
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, _, cohorts) => {
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, _, cohorts) => {
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, _, cohorts) => {
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 placements",
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, _, cohorts) => {
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, _, cohorts) => {
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, _, cohort) => {
466
- console.log(user, cohort);
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, _, cohort) => {
687
+ callback: async ({user, cohorts}) => {
472
688
  if (!getAccess(user, "verifyListings")) return;
473
- if (!Array.isArray(cohort)) return;
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 required onboarding documents for ${placement.name}`,
502
- message: `${placement.name} has requested you to complete some onboarding documents.`,
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
- requestedVisibleAddresses: {
513
- callback: async (user) => {
514
- const accessRequests = await firebaseQuery.getCount("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleAddresses")]);
515
- if (accessRequests === 0) return;
516
- if (accessRequests === 1) {
517
- const userRequestingAccess = Object.entries(await firebaseQuery.getDocsWhere("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleAddresses")]) || {})[0] as [string, UserData];
518
- return {
519
- dismissible: false,
520
- severity: "primary",
521
- title: `${userRequestingAccess[1].details.forename} ${userRequestingAccess[1].details.surname} has requested access to view addresses.`,
522
- message: `Click to review the addresses and grant access to the user.`,
523
- link: `/${user.product}/users/${userRequestingAccess[0]}`,
524
- buttonTitle: "View request",
525
- } as TaskQueryReturnObject;
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
- return {
529
- dismissible: false,
530
- severity: "warning",
531
- title: `Multiple users have requested access to view addresses.`,
532
- message: `Click to review the addresses and grant access to the user.`,
533
- link: `/${user.product}/organisation/staff/all`,
534
- buttonTitle: "View request",
535
- } as TaskQueryReturnObject;
536
- },
537
- },
538
- requestedVisiblePlacementListings: {
539
- callback: async (user) => {
540
- const accessRequests = await firebaseQuery.getCount("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleListings")]);
541
- if (accessRequests === 0) return;
542
- if (accessRequests === 1) {
543
- const userRequestingAccess = Object.entries(await firebaseQuery.getDocsWhere("users", [where("oId", "==", user.oId), where("product", "==", "providers"), orderBy("requestedVisibleListings")]) || {})[0] as [string, UserData];
544
- return {
545
- dismissible: false,
546
- severity: "primary",
547
- title: `${userRequestingAccess[1].details.forename} ${userRequestingAccess[1].details.surname} has requested access to view placement listings.`,
548
- message: `Click to review the placement listings and grant access to the user.`,
549
- link: `/${user.product}/users/${userRequestingAccess[0]}`,
550
- buttonTitle: "View request",
551
- } as TaskQueryReturnObject;
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
- return {
555
- dismissible: false,
556
- severity: "warning",
557
- title: `Multiple users have requested access to view placement listings.`,
558
- message: `Click to review the placement listings and grant access to the user.`,
559
- link: `/${user.product}/organisation/staff/all`,
560
- buttonTitle: "View request",
561
- } as TaskQueryReturnObject;
562
- },
563
- },
564
- applicationRequireReview: {
565
- callback: async (user) => {
566
- const applicationCount = await firebaseQuery.getCount("applications", [where("providerId", "==", user.oId), where("reqUserType", "==", "Staff"), where("status", "==", "submitted")]);
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
- return {
570
- dismissible: false,
571
- severity: "primary",
572
- title: `${applicationCount} applications require your review.`,
573
- message: `Click to view ${applicationCount} that require your attention.`,
574
- link: `/${user.product}/placementListings/applicants`,
575
- buttonTitle: "View applications",
576
- } as TaskQueryReturnObject;
577
- },
578
- },
579
- completeStudentDocs: {
580
- callback: async (user) => {
581
- return {} as TaskQueryReturnObject;
582
- },
583
- },
584
- reviewOnboarding: {
585
- callback: async (user) => {
586
- return {} as TaskQueryReturnObject;
587
- },
588
- },
589
- completeListing: {
590
- callback: async (user) => {
591
- return {} as TaskQueryReturnObject;
592
- },
593
- },
594
- completeAddress: {
595
- callback: async (user) => {
596
- return {} as TaskQueryReturnObject;
597
- },
598
- },
599
- registrationRequests: {
600
- callback: async (user) => {
601
- return {} as TaskQueryReturnObject;
602
- },
603
- },
604
- placementStarting: {
605
- callback: async (user) => {
606
- return {} as TaskQueryReturnObject;
607
- },
608
- },
609
- completeFeedback: {
610
- callback: async (user) => {
611
- return {} as TaskQueryReturnObject;
612
- },
613
- },
614
- setUpFeedback: {
615
- callback: async (user) => {
616
- return {} as TaskQueryReturnObject;
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
- export const getTips = async (user: UserData, organisation: InstituteData|ProviderData):Promise<(TaskQueryReturnObject)[]> => {
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(user, organisation);
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|ProviderData, cohort?: CohortData):Promise<(TaskQueryReturnObject)[]> => {
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
- const processedTasks = await Object.entries(providerTasks).reduce(async (acc, [itemName, item]) => {
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
- const queryResult = await item.callback(user);
679
- if (!queryResult) return await acc;
1191
+ // const queryResult = await item.callback(user);
1192
+ // if (!queryResult) return await acc;
680
1193
 
681
- const queryResultArray = Array.isArray(queryResult) ? queryResult : [queryResult];
682
- const results = queryResultArray.map((r) => ({itemName: itemName as ProviderTaskNames, ...r}));
1194
+ // const queryResultArray = Array.isArray(queryResult) ? queryResult : [queryResult];
1195
+ // const results = queryResultArray.map((r) => ({itemName: itemName as ProviderTaskNames, ...r}));
683
1196
 
684
- (await acc).push(...results);
685
- return await acc;
686
- }, Promise.resolve<TaskQueryReturnObject[]>([]));
687
- return processedTasks;};
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|ProviderData, cohort?: CohortData):Promise<(TaskQueryReturnObject)[]> => {
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
- if (!fCohort) {
693
- // get all associated cohorts.
694
- if (user.viewCohorts === "none") return([]);
695
- if (user.userGroup === "admin" || user.viewCohorts === "all") {
696
- const cohorts = await firebaseQuery.getDocsWhere("cohorts", [where("product", "==", user.product), where("oId", "==", user.oId), where("stage", "==", "created")]) as {[key:string]: CohortData};
697
- fCohort = Object.entries(cohorts);
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.viewCohorts === "some") {
700
- const cohorts = await user.visibleCohorts?.split(",").reduce(async (acc, cohortId) => {
701
- const cohort = await firebaseQuery.getDocData(["cohorts", cohortId]) as CohortData;
702
- if (cohort.stage !== "created") {
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[cohortId] = cohort;
1222
+ acc[itemId] = item;
706
1223
  return acc;
707
1224
  }, Promise.resolve<[string, CohortData][]>([]));
708
- fCohort = cohorts;
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];