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
package/lib/hooks.js CHANGED
@@ -5,23 +5,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.useRefDimensions = void 0;
7
7
  exports.useStudentPlacementList = useStudentPlacementList;
8
- exports.useNewInstitutePlacementList = useNewInstitutePlacementList;
9
8
  exports.useOldInstitutePlacementList = useOldInstitutePlacementList;
9
+ exports.useNewInstitutePlacementList = useNewInstitutePlacementList;
10
+ exports.useAlumniPaginator = useAlumniPaginator;
11
+ exports.useVeryOldInstitutePlacementList = useVeryOldInstitutePlacementList;
10
12
  exports.useFilterTablePaginator = useFilterTablePaginator;
11
- exports.usePlacementListingPaginator = usePlacementListingPaginator;
13
+ exports.useProviderContactPaginator = useProviderContactPaginator;
12
14
  exports.useCohortUserPaginator = useCohortUserPaginator;
13
15
  exports.useAdmissionsPaginator = useAdmissionsPaginator;
14
16
  exports.useLazyLoadQueryList = useLazyLoadQueryList;
15
- exports.usePublicPlacementListingLoader = usePublicPlacementListingLoader;
16
- exports.useCreateApplicationRenderer = useCreateApplicationRenderer;
17
17
  exports.useProposePlacementRenderer = useProposePlacementRenderer;
18
18
  exports.useCreateCohortRenderer = useCreateCohortRenderer;
19
19
  exports.useUserUploadHandler = useUserUploadHandler;
20
20
  exports.useWorkflowEditor = useWorkflowEditor;
21
21
  exports.useApplicantWorkflowEditor = useApplicantWorkflowEditor;
22
- exports.useInstitutePlacementListingHandler = useInstitutePlacementListingHandler;
22
+ exports.useInstituteProviderContactsHandler = useInstituteProviderContactsHandler;
23
23
  exports.useGetIndividualPlacementForPlacementPage = useGetIndividualPlacementForPlacementPage;
24
24
  exports.useOnboardingPopup = useOnboardingPopup;
25
+ exports.useLoadAddresses = useLoadAddresses;
26
+ exports.useLoadListings = useLoadListings;
27
+ exports.useLoadProviderPlacements = useLoadProviderPlacements;
28
+ exports.useLoadApplications = useLoadApplications;
29
+ exports.useDataViewerPaginator = useDataViewerPaginator;
25
30
  const firestore_1 = require("firebase/firestore");
26
31
  const react_1 = require("react");
27
32
  const constants_1 = require("./constants");
@@ -60,19 +65,19 @@ function useStudentPlacementList({ user, student, queryConstraint, ql = DEFAULTQ
60
65
  setStudentId(student.id);
61
66
  return;
62
67
  }
63
- if (!user.viewCohorts || !user.viewUsers || user.viewCohorts === "none") {
68
+ if (!user.viewCohorts || !user.viewStudents || user.viewCohorts === "none") {
64
69
  setStudentId(undefined);
65
70
  return;
66
71
  }
67
- if (user.viewCohorts === "all" && user.viewUsers == "all") {
72
+ if (user.viewCohorts === "all" && user.viewStudents == "all") {
68
73
  setStudentId(student.id);
69
74
  }
70
- if (user.viewCohorts === "some" && ((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.split(",").includes(student.cohort || ""))) {
71
- if (user.viewUsers === "all") {
75
+ if (user.viewCohorts === "some" && ((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(student.cohort || ""))) {
76
+ if (user.viewStudents === "all") {
72
77
  setStudentId(student.id);
73
78
  return;
74
79
  }
75
- if ((_b = user.studentFilterValues) === null || _b === void 0 ? void 0 : _b.split(", ").includes(student.details[user.studentFilter || ""])) {
80
+ if ((_b = user.studentFilterValues) === null || _b === void 0 ? void 0 : _b.includes(student.details[user.studentFilter || ""])) {
76
81
  setStudentId(student.id);
77
82
  return;
78
83
  }
@@ -121,7 +126,7 @@ function useStudentPlacementList({ user, student, queryConstraint, ql = DEFAULTQ
121
126
  };
122
127
  return ({ ...{ placements, loadMoreIcon, loadMorePlacements, setQuery, setInitialQueryLimit, reset, changeQueryConstraints } });
123
128
  }
124
- function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT, inProgress }) {
129
+ function useOldInstitutePlacementList({ id, user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT, inProgress }) {
125
130
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(true);
126
131
  const [query, setQuery] = (0, react_1.useState)();
127
132
  const [initialQueryLimit, setInitialQueryLimit] = (0, react_1.useState)(ql);
@@ -131,7 +136,6 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
131
136
  const [startPlacementAfter, setStartPlacementAfter] = (0, react_1.useState)(); // uid, pId
132
137
  const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
133
138
  const placementsIndex = algoliaClient.initIndex("placements");
134
- const usersIndex = algoliaClient.initIndex("users");
135
139
  (0, react_1.useEffect)(() => {
136
140
  if (user.product !== "institutes" || user.userType !== "Staff") {
137
141
  setOId(undefined);
@@ -147,30 +151,6 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
147
151
  }
148
152
  const searchPlacements = async () => {
149
153
  let placementsFound = {};
150
- let userSearchString = `userType:Students AND status:active AND oId:${user.oId} AND product:${user.product}`;
151
- if (cohort) {
152
- userSearchString = userSearchString + ` AND cohort:${cohort}`;
153
- }
154
- if (user.product === "institutes" && user.userType === "Staff") {
155
- const searchStudentHits = await usersIndex.search(query, {
156
- filters: userSearchString
157
- });
158
- if (searchStudentHits) {
159
- console.log("FOUND", searchStudentHits.hits.length, "students");
160
- await Promise.all(searchStudentHits.hits.map(async (hit) => {
161
- console.log("STUDENT", hit.objectID);
162
- const constraints = [...(cohort ? [(0, firestore_1.where)("cohort", "==", cohort)] : [])];
163
- if (inProgress !== undefined) {
164
- constraints.push(inProgress ? (0, firestore_1.where)("inProgress", "==", true) : (0, firestore_1.where)("completed", "==", true));
165
- }
166
- const fPlacements = await (0, readDatabase_1.getPlacementsWhere)({ w: constraints, uid: hit.objectID, oId: hit.oId });
167
- console.log("PLACEMENTS", fPlacements);
168
- Object.entries(fPlacements).forEach(([k, v]) => {
169
- placementsFound[k] = { ...v, student: hit };
170
- });
171
- }));
172
- }
173
- }
174
154
  let placementSearchString = `oId:${user.oId} AND ` + (inProgress !== undefined ? (inProgress ? "inProgress:true" : "completed:true") : "");
175
155
  if (cohort) {
176
156
  placementSearchString = placementSearchString + ` AND cohort:${cohort}`;
@@ -216,7 +196,7 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
216
196
  }
217
197
  setLoadMoreIcon(true);
218
198
  let fPlacements = {};
219
- if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.split(",").includes(cohort))) || (user.viewCohorts === "all" && user.viewUsers === "all")))) {
199
+ if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort))) || (user.viewCohorts === "all" && user.viewStudents === "all")))) {
220
200
  const queryConstraintOrdered = Boolean(queryConstraints && queryConstraints.find((v) => v.type === "orderBy"));
221
201
  const constraints = (fStartPlacementAfter === null || fStartPlacementAfter === void 0 ? void 0 : fStartPlacementAfter.length) === 2 && fStartPlacementAfter[0] ?
222
202
  [(0, firestore_1.limit)(placements ? DEFAULTQUERYLIMIT : initialQueryLimit),
@@ -227,13 +207,16 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
227
207
  // query && constraints.unshift(where("name", "==", query));
228
208
  cohort && constraints.unshift((0, firestore_1.where)("cohort", "==", cohort));
229
209
  const placementsQuery = await (0, readDatabase_1.getPlacementsWhere)({ w: constraints, oId: oId, raw: true });
210
+ console.log("PLACEMENTS RETRIEVED", placementsQuery.size);
230
211
  const placementsWithStudentData = placementsQuery.empty ? [] : await Promise.all(placementsQuery.docs.map(async (placement) => {
231
212
  const pData = placement.data();
232
213
  const student = pData.uid === user.id ? user : (await (0, readDatabase_1.getUserById)(pData.uid).catch(() => false));
233
- if (user.viewUsers === "some") {
214
+ console.log("STUDENT", student, "PLACEMENT", id);
215
+ // if (!student) return false;
216
+ if (user.viewStudents === "some") {
234
217
  if (!(user.studentFilter && user.studentFilterValues))
235
218
  return false;
236
- if (!user.studentFilterValues.split(", ").includes(student.details[user.studentFilter])) {
219
+ if (!user.studentFilterValues.includes(student.details[user.studentFilter])) {
237
220
  return false;
238
221
  }
239
222
  }
@@ -258,7 +241,197 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
258
241
  };
259
242
  return ({ ...{ placements, loadMoreIcon, loadMorePlacements, setQuery, setInitialQueryLimit, reset, changeQueryConstraints } });
260
243
  }
261
- function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT }) {
244
+ function useNewInstitutePlacementList({ id, user, filters, view, cohort, queryConstraints, ql = DEFAULTQUERYLIMIT, inProgress }) {
245
+ const [query, setQuery] = (0, react_1.useState)();
246
+ const sorts = {
247
+ ["Student Forename - Asc"]: {
248
+ value: "studentForename",
249
+ direction: "asc",
250
+ },
251
+ ["Student Forename - Desc"]: {
252
+ value: "studentForename",
253
+ direction: "desc",
254
+ },
255
+ ["Student Surname - Asc"]: {
256
+ value: "studentSurname",
257
+ direction: "asc",
258
+ },
259
+ ["Student Surname - Desc"]: {
260
+ value: "studentSurname",
261
+ direction: "desc",
262
+ },
263
+ ["Student Email - Asc"]: {
264
+ value: "studentEmail",
265
+ direction: "asc",
266
+ },
267
+ ["Student Email - Desc"]: {
268
+ value: "studentEmaile",
269
+ direction: "desc",
270
+ },
271
+ ["Provider email - Asc"]: {
272
+ value: "providerEmail",
273
+ direction: "asc",
274
+ },
275
+ ["Provider email - Desc"]: {
276
+ value: "providerEmail",
277
+ direction: "desc",
278
+ }
279
+ };
280
+ const additionalProcessing = async (k, placement) => {
281
+ if (user.viewStudents === "some") {
282
+ if (!(user.studentFilter && user.studentFilterValues))
283
+ return false;
284
+ const student = await (0, readDatabase_1.getUserById)(placement.uid).catch(() => false);
285
+ if (!student)
286
+ return;
287
+ if (!user.studentFilterValues.includes(student.details[user.studentFilter])) {
288
+ return false;
289
+ }
290
+ }
291
+ return { ...placement, id: k };
292
+ };
293
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query, additionalEntryProcessing: additionalProcessing, onSearch: async (s, sort, page, filters, limit) => await algoliaPlacementSearch(user, s, sort, page, filters, limit, cohort, inProgress) });
294
+ (0, react_1.useEffect)(() => {
295
+ var _a;
296
+ // Sets the query of for the DataViewerPaginator
297
+ if (user.product !== "institutes" || user.userType !== "Staff") {
298
+ setQuery(undefined);
299
+ return;
300
+ }
301
+ if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort))) || (user.viewCohorts === "all" && user.viewStudents === "all")))) {
302
+ const constraints = [["oId", "==", user.oId], ["draft", "==", false]];
303
+ cohort && constraints.push(["cohort", "==", cohort]);
304
+ queryConstraints && constraints.unshift(...queryConstraints);
305
+ inProgress !== undefined && constraints.push(["inProgress", "==", inProgress]);
306
+ setQuery([{
307
+ path: ["placements"],
308
+ where: constraints
309
+ }]);
310
+ return;
311
+ }
312
+ setQuery(undefined);
313
+ }, [user, queryConstraints, cohort]);
314
+ return { tableData, page, loading, updateSearch, setFilters, setView, pageUp, pageDown, sorts, updateSort, sort };
315
+ }
316
+ function useAlumniPaginator({ user, alumniConvoUser, filters, view, school, queryConstraints, ql = DEFAULTQUERYLIMIT }) {
317
+ const [query, setQuery] = (0, react_1.useState)();
318
+ const sorts = {};
319
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query });
320
+ const firebaseQuery = new firebaseQuery_1.default();
321
+ (0, react_1.useEffect)(() => {
322
+ const createQuery = async () => {
323
+ // Sets the query of for the DataViewerPaginator
324
+ const getQueryAccess = async () => {
325
+ var _a;
326
+ const constraints = [];
327
+ if (user) {
328
+ constraints.push(["oId", "==", user.oId]);
329
+ if (user.userGroup === "admin" && user.userType === "Staff")
330
+ return constraints;
331
+ if (user.userType === "Staff") {
332
+ if (user.viewSchools === "all")
333
+ return constraints;
334
+ if (user.viewSchools === "none")
335
+ return false;
336
+ if (user.viewSchools === "some") {
337
+ if (!school)
338
+ return false;
339
+ if ((_a = user === null || user === void 0 ? void 0 : user.visibleSchools) === null || _a === void 0 ? void 0 : _a.includes(school))
340
+ return constraints;
341
+ }
342
+ return false;
343
+ }
344
+ return false;
345
+ }
346
+ if (alumniConvoUser) {
347
+ constraints.push(["oId", "==", alumniConvoUser.oId]);
348
+ console.log("ALUMNI CONVO USER");
349
+ if ((school || alumniConvoUser.schoolId) && school === alumniConvoUser.schoolId)
350
+ return constraints;
351
+ console.log("ALUMNI ACCESS TRUE");
352
+ if (alumniConvoUser.schoolId) {
353
+ const school = await firebaseQuery.getDocData(["schools", alumniConvoUser.schoolId]);
354
+ console.log("SCHOOL ACCESS", school, school.alumniConversations);
355
+ return school.alumniConversations ? constraints : false;
356
+ }
357
+ else {
358
+ const institute = await firebaseQuery.getDocData(["institutes", alumniConvoUser.oId]);
359
+ return institute.alumniConversations ? constraints : false;
360
+ }
361
+ }
362
+ return false;
363
+ };
364
+ const constraints = await getQueryAccess();
365
+ console.log("CONSTRAINTS", constraints);
366
+ if (!constraints)
367
+ return;
368
+ school && constraints.push(["schoolId", "==", school]);
369
+ queryConstraints && constraints.unshift(...queryConstraints);
370
+ return constraints;
371
+ };
372
+ console.log("Creating query");
373
+ createQuery().then((constraints) => {
374
+ setQuery([{
375
+ path: ["alumni"],
376
+ where: constraints
377
+ }]);
378
+ });
379
+ }, [user, queryConstraints, school]);
380
+ (0, react_1.useEffect)(() => {
381
+ console.log("Alumni data", query, tableData);
382
+ }, [tableData]);
383
+ return { tableData, page, loading, setFilters, setView, pageUp, pageDown, sorts };
384
+ }
385
+ const algoliaPlacementSearch = async (user, query, sort, page, filters, limit, cohort, inProgress) => {
386
+ const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
387
+ const placementsIndex = algoliaClient.initIndex(sort ? Object.values(sort[1]).join("_") : "placements");
388
+ // const usersIndex = algoliaClient.initIndex("users");
389
+ // let userSearchString = `userType:Students AND status:active AND oId:${user.oId} AND product:${user.product}`
390
+ // if (cohort) {
391
+ // userSearchString = userSearchString + ` AND cohort:${cohort}`;
392
+ // }
393
+ // if (user.product === "institutes" && user.userType === "Staff") {
394
+ // const searchStudentHits = await usersIndex.search<UserData>(query, {
395
+ // filters: userSearchString,
396
+ // hitsPerPage: limit,
397
+ // page: page
398
+ // });
399
+ // if (searchStudentHits) {
400
+ // console.log("FOUND", searchStudentHits.hits.length, "students");
401
+ // await Promise.all(searchStudentHits.hits.map(async (hit) => {
402
+ // console.log("STUDENT", hit.objectID);
403
+ // const constraints = [...(cohort ? [where("cohort", "==", cohort)] : [])]
404
+ // if (inProgress !== undefined) {
405
+ // constraints.push(inProgress ? where("inProgress", "==", true) : where("completed", "==", true));
406
+ // }
407
+ // const fPlacements = await getPlacementsWhere({w: constraints, uid: hit.objectID, oId: hit.oId}) as {[key: string]: StudentPlacementData};
408
+ // console.log("PLACEMENTS", fPlacements)
409
+ // Object.entries(fPlacements).forEach(([k, v]) => {
410
+ // placementsFound[k] = {...v, student: hit};
411
+ // })
412
+ // }))
413
+ // }
414
+ // }
415
+ let placementSearchString = `oId:${user.oId} AND ` + (inProgress !== undefined ? (inProgress ? "inProgress:true" : "completed:true") : "");
416
+ if (cohort) {
417
+ placementSearchString = placementSearchString + ` AND cohort:${cohort}`;
418
+ }
419
+ filters && Object.entries(filters).filter(([, filter]) => filter.value).map(([id, filter]) => {
420
+ placementSearchString = placementSearchString + ` AND ${id}:${filter.value}`;
421
+ });
422
+ const options = {
423
+ filters: placementSearchString,
424
+ hitsPerPage: limit,
425
+ page: page ? page - 1 : undefined,
426
+ };
427
+ const searchPlacementHits = await placementsIndex.search(query || "", options);
428
+ console.log(searchPlacementHits.hits);
429
+ const i = (searchPlacementHits ? (await Promise.all(searchPlacementHits.hits.map(async (hit) => {
430
+ return [hit.objectID, hit];
431
+ }))) : []).filter((e) => e !== undefined);
432
+ return Object.fromEntries(i);
433
+ };
434
+ function useVeryOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT }) {
262
435
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(true);
263
436
  const [query, setQuery] = (0, react_1.useState)("");
264
437
  const [initialQueryLimit, setInitialQueryLimit] = (0, react_1.useState)(ql);
@@ -299,7 +472,7 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
299
472
  setLoadMoreIcon(true);
300
473
  // If can view all, query placements directly. Otherwise query students.
301
474
  let fPlacements = {};
302
- if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.split(", ").includes(cohort))) || (user.viewCohorts === "all" && user.viewUsers === "all") || query) && (fStartPlacementAfter && fStartPlacementAfter[0] === "placement"))) {
475
+ if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort))) || (user.viewCohorts === "all" && user.viewStudents === "all") || query) && (fStartPlacementAfter && fStartPlacementAfter[0] === "placement"))) {
303
476
  const queryConstraintOrdered = Boolean(queryConstraints && queryConstraints.find((v) => v.type === "orderBy"));
304
477
  const constraints = (fStartPlacementAfter === null || fStartPlacementAfter === void 0 ? void 0 : fStartPlacementAfter.length) === 4 && fStartPlacementAfter[1] ?
305
478
  [(0, firestore_1.limit)(placements ? DEFAULTQUERYLIMIT : initialQueryLimit),
@@ -314,10 +487,10 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
314
487
  const placementsWithStudentData = placementsQuery.empty ? [] : await Promise.all(placementsQuery.docs.map(async (placement) => {
315
488
  const pData = placement.data();
316
489
  const student = pData.uid === user.id ? user : (await (0, readDatabase_1.getUserById)(pData.uid).catch(() => false));
317
- if (user.viewUsers === "some") {
490
+ if (user.viewStudents === "some") {
318
491
  if (!(user.studentFilter && user.studentFilterValues))
319
492
  return false;
320
- if (!user.studentFilterValues.split(", ").includes(student.details[user.studentFilter])) {
493
+ if (!user.studentFilterValues.includes(student.details[user.studentFilter])) {
321
494
  return false;
322
495
  }
323
496
  }
@@ -342,13 +515,13 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
342
515
  return;
343
516
  }
344
517
  }
345
- if (!(user.studentFilterValues && user.studentFilter && user.viewUsers === "some")) {
518
+ if (!(user.studentFilterValues && user.studentFilter && user.viewStudents === "some")) {
346
519
  console.log("fenaibn");
347
520
  return;
348
521
  }
349
522
  ;
350
523
  const getStudentPlacements = async (ffStartPlacementAfter = fStartPlacementAfter, ffPlacements = fPlacements) => {
351
- var _a, _b;
524
+ var _a;
352
525
  if (ffStartPlacementAfter && ffStartPlacementAfter[0] === "placement") {
353
526
  ffStartPlacementAfter = [undefined, undefined, undefined, 0];
354
527
  }
@@ -358,7 +531,7 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
358
531
  [(0, firestore_1.limit)(1), (0, firestore_1.orderBy)((0, firestore_1.documentId)())];
359
532
  constraints.push((0, firestore_1.where)("oId", "==", oId), (0, firestore_1.where)("userType", "==", "Students"));
360
533
  if (ffStartPlacementAfter && ffStartPlacementAfter[3]) {
361
- const currentFilter = (_a = user === null || user === void 0 ? void 0 : user.studentFilterValues) === null || _a === void 0 ? void 0 : _a.split(", ")[ffStartPlacementAfter[3]];
534
+ const currentFilter = (_a = user === null || user === void 0 ? void 0 : user.studentFilterValues) === null || _a === void 0 ? void 0 : _a[ffStartPlacementAfter[3]];
362
535
  if (currentFilter && user.studentFilter) {
363
536
  constraints.push((0, firestore_1.where)(user.studentFilter, "==", currentFilter));
364
537
  }
@@ -367,7 +540,7 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
367
540
  const student = Object.entries((await firebaseQuery.getDocsWhere("users", constraints)))[0];
368
541
  // console.log("student", student);
369
542
  // If there is no students but more filters
370
- if (!student && ffStartPlacementAfter && ffStartPlacementAfter[3] + 1 < (((_b = user === null || user === void 0 ? void 0 : user.studentFilterValues) === null || _b === void 0 ? void 0 : _b.split(", ")) || []).length) {
543
+ if (!student && ffStartPlacementAfter && ffStartPlacementAfter[3] + 1 < ((user === null || user === void 0 ? void 0 : user.studentFilterValues) || []).length) {
371
544
  // console.log("No more students. Calling recursion");
372
545
  return await getStudentPlacements(["student", undefined, undefined, ffStartPlacementAfter[3] + 1], ffPlacements);
373
546
  }
@@ -577,7 +750,7 @@ function useFilterTablePaginator({ data }) {
577
750
  if (!Object.keys(itemList).length)
578
751
  return;
579
752
  console.log("Fetching filter table updates");
580
- const itemListUpdateQuery = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, "users"), (0, firestore_1.where)((0, firestore_1.documentId)(), "in", Object.keys(itemList)));
753
+ const itemListUpdateQuery = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, ...querySchema.path), (0, firestore_1.where)((0, firestore_1.documentId)(), "in", Object.keys(itemList)));
581
754
  const itemUpdateSnapshot = (0, firestore_1.onSnapshot)(itemListUpdateQuery, (querySnapshot) => {
582
755
  querySnapshot.docs.forEach((doc) => {
583
756
  setTableData((data) => {
@@ -609,186 +782,37 @@ function useFilterTablePaginator({ data }) {
609
782
  }, [page]);
610
783
  return ({ ...{ tableData, setPage, setFilters, page } });
611
784
  }
612
- function usePlacementListingPaginator({ data, user }) {
613
- const [tableData, setTableData] = (0, react_1.useState)({});
614
- const [page, setPage] = (0, react_1.useState)([1, 0]);
615
- const [filters, setFilters] = (0, react_1.useState)();
616
- const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
617
- const [prevEntryIds, setPrevEntryIds] = (0, react_1.useState)({});
785
+ function useProviderContactPaginator({ data, user, view, filters }) {
786
+ const [query, setQuery] = (0, react_1.useState)();
618
787
  const firebaseQuery = new firebaseQuery_1.default();
619
- const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
620
- let cursorPos;
621
- if (page[0] > page[1]) {
622
- cursorPos = currentQueryAnchor.endQueryPos;
623
- }
624
- else {
625
- cursorPos = currentQueryAnchor.startQueryPos;
626
- }
627
- const querySchema = data === null || data === void 0 ? void 0 : data[cursorPos];
628
- const createQuery = (queryData) => {
629
- const constraints = [
630
- (0, firestore_1.where)("savedById", "==", user.userType === "Staff" ? user.oId : user.id),
631
- (0, firestore_1.where)("savedByProduct", "==", user.product),
632
- (0, firestore_1.where)("savedByUserType", "==", user.userType === "Staff" ? "Organisation" : "Student"),
633
- ];
634
- if (user.userType === "Students") {
635
- constraints.push((0, firestore_1.where)("listed", "==", true));
636
- }
637
- (queryData === null || queryData === void 0 ? void 0 : queryData.where) && queryData.where.forEach((w) => {
638
- constraints.push((0, firestore_1.where)(...w));
639
- });
640
- filters && Object.entries(filters).forEach(([key, value]) => {
641
- constraints.push((0, firestore_1.where)(key, "==", value));
642
- });
643
- if (queryData === null || queryData === void 0 ? void 0 : queryData.orderBy) {
644
- if (queryData.orderBy === "documentId") {
645
- constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
646
- }
647
- else {
648
- constraints.push((0, firestore_1.orderBy)(queryData.orderBy));
649
- }
650
- }
651
- else {
652
- constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
653
- }
654
- if (page[0] > page[1] && !cursorDirection) { // Going up
655
- currentQueryAnchor.endKey && constraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
656
- constraints.push((0, firestore_1.limit)(10));
657
- if (!loadMoreFromQuery) {
658
- currentQueryAnchor = { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.endQueryPos };
659
- }
660
- }
661
- else if (page[0] < page[1] && !cursorDirection) { // Going down
662
- if (!loadMoreFromQuery) {
663
- currentQueryAnchor = { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.startQueryPos };
664
- }
665
- constraints.push((0, firestore_1.limitToLast)(10));
666
- if (currentQueryAnchor.startKey) {
667
- currentQueryAnchor.startKey && constraints.push((0, firestore_1.endBefore)(currentQueryAnchor.startKey));
668
- }
669
- else {
670
- currentQueryAnchor.startKey && constraints.push((0, firestore_1.endAt)(currentQueryAnchor.startKey));
671
- }
672
- }
673
- else {
674
- if (cursorDirection === "decrease") {
675
- constraints.push((0, firestore_1.limitToLast)(10));
676
- }
677
- else {
678
- constraints.push((0, firestore_1.limit)(10));
679
- }
680
- }
681
- return constraints;
682
- };
683
- const constraints = createQuery(querySchema);
684
- // console.log(queryId, "constraints", constraints)
685
- const q = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, "savedPlacements"), ...(constraints));
686
- const queryResults = {};
687
- const queryData = await (0, firestore_1.getDocs)(q);
688
- // console.log("queryData.size", queryData.size)
689
- const reverseIfBack = (docs) => {
690
- if (page[0] < page[1]) {
691
- return docs.reverse();
692
- }
693
- return docs;
694
- };
695
- let index = 0;
696
- reverseIfBack(queryData.docs).forEach(async (doc) => {
697
- if (Object.keys(queryResults).length + Object.keys(itemList).length === 10) {
698
- return;
699
- }
700
- let position = Object.keys(itemList).length + (page[0] - 1) * 10 + index + 1;
701
- if (page[0] < page[1]) {
702
- position = (page[0]) * 10 - index - Object.keys(itemList).length;
703
- }
704
- // console.log(index, "doc.id", doc.id, position, "E", prevEntries[doc.id])
705
- if (itemList[doc.id] || (prevEntries[doc.id] && prevEntries[doc.id] !== position)) {
706
- console.log("Removing ", doc.id, ": E=", prevEntries[doc.id], ", G=", position);
707
- return;
708
- }
709
- const item = doc.data();
710
- item.id = doc.id;
711
- queryResults[doc.id] = item;
712
- index = index + 1;
713
- if (prevEntries[doc.id])
714
- return;
715
- prevEntries[doc.id] = position;
716
- });
717
- if (cursorDirection === "decrease" || page[0] < page[1]) {
718
- itemList = { ...Object.fromEntries(Object.entries(queryResults).reverse()), ...itemList };
719
- }
720
- else {
721
- itemList = { ...itemList, ...queryResults };
722
- }
723
- setPrevEntryIds(prevEntries);
724
- if (queryData.size < 10 && Object.keys(itemList).length < 10) {
725
- if (page[0] > page[1] && cursorPos + 1 < data.length) {
726
- console.log("Increase query index");
727
- return getDataFromQuery(itemList, { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.endQueryPos + 1 }, "increase", prevEntries);
728
- }
729
- else if (page[0] < page[1] && cursorPos > 0) {
730
- console.log("Decrease query index");
731
- return getDataFromQuery(itemList, { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.startQueryPos - 1 }, "decrease", prevEntries);
732
- }
733
- }
734
- if (Object.keys(itemList).length < 10 && queryData.size === 10) {
735
- console.log("Shorter than ten");
736
- // if(loadMoreFromQuery){return}
737
- return getDataFromQuery(itemList, { ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] }, undefined, prevEntries, true);
738
- }
739
- if (queryData.size === 0 &&
740
- Object.keys(itemList).length === 0 &&
741
- currentQueryAnchor.endQueryPos + 1 === data.length &&
742
- page[0] > 1) {
743
- setTableData({});
744
- setQueryAnchor((a) => ({ ...a, startKey: "" }));
745
- return;
746
- }
747
- setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
748
- const finalData = Object.fromEntries(await Promise.all(Object.entries(itemList).map(async ([k, v]) => {
749
- const placement = await firebaseQuery.getDocData(["placementListings", v.placementId || ""]);
750
- const address = await firebaseQuery.getDocData(["addresses", placement.addressId || ""]);
751
- const provider = await firebaseQuery.getDocData(["providers", placement.providerId || ""]);
752
- const status = getPlacementStatus(provider, placement, v);
753
- return [k, { ...address, ...provider, ...placement, email: placement.providerEmail, status: status, savedPlacement: v }];
754
- })));
755
- setTableData(finalData);
756
- };
757
- const getPlacementStatus = (provider, placement, savedPlacement) => {
758
- if (savedPlacement.status === "Blocked")
759
- return "blocked";
760
- if (savedPlacement.status === "Accepted")
761
- return "active";
762
- if (user.product === "admin") {
763
- if (placement.mapConsent === undefined)
764
- return "notReviewed";
765
- if (placement.mapConsent === false)
766
- return "notPublic";
767
- return "unknown";
788
+ const getAdditionalData = async (k, v) => {
789
+ var _a, _b;
790
+ if ((_b = (_a = v.savedBy) === null || _a === void 0 ? void 0 : _a[user.oId].activities) === null || _b === void 0 ? void 0 : _b.includes("workExperience")) {
791
+ const placementsCount = await firebaseQuery.getCount("placementListings", [(0, firestore_1.where)(`savedBy.${user.oId}.exists`, "==", true), (0, firestore_1.where)("providerContactId", "==", k)]);
792
+ return { ...v, plcaements: placementsCount };
768
793
  }
769
- ;
770
- if (!provider.insurance)
771
- return "awaitingProviderApproval";
772
- return "requiresApproval";
794
+ return v;
773
795
  };
796
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading } = useDataViewerPaginator({ view, filters, queryLimit: 20, data: query, additionalEntryProcessing: getAdditionalData });
774
797
  (0, react_1.useEffect)(() => {
775
- if (!filters)
776
- return;
777
- setPage([1, 0]);
778
- setTableData({});
779
- setQueryAnchor({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
780
- setPrevEntryIds({});
781
- }, [filters]);
782
- // Fetch new data when queries or page change
783
- (0, react_1.useEffect)(() => {
784
- getDataFromQuery();
785
- }, [page]);
786
- return ({ ...{ tableData, setPage, setFilters, page } });
798
+ const constraints = [
799
+ [`savedBy.${user.oId}.exists`, "==", true],
800
+ ];
801
+ if (user.userType === "Students") {
802
+ constraints.push([`savedBy.${user.oId}.cohorts.${user.cohort}.listed`, "==", true]);
803
+ }
804
+ setQuery([{
805
+ path: ["providerContacts"],
806
+ where: constraints
807
+ }]);
808
+ }, [user]);
809
+ return ({ ...{ tableData, pageUp, pageDown, setFilters, page, setView, loading } });
787
810
  }
788
- function useCohortUserPaginator({ user, cohort, data, search, userType }) {
811
+ function useCohortUserPaginator({ user, cohort, data, search, userType, sort }) {
789
812
  const [tableData, setTableData] = (0, react_1.useState)({});
790
- const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
813
+ const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startDoc: undefined, endDoc: undefined, startQueryPos: 0, endQueryPos: 0 });
791
814
  const [filters, setFilters] = (0, react_1.useState)();
815
+ const [sortResultsBy, setSortResultsBy] = (0, react_1.useState)(sort);
792
816
  const [prevEntryIds, setPrevEntryIds] = (0, react_1.useState)({});
793
817
  const [page, setPage] = (0, react_1.useState)([1, 0]);
794
818
  const [dataListenerUnsubscribe, setDataListenerUnsubscribe] = (0, react_1.useState)();
@@ -796,6 +820,14 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
796
820
  const [prevSearch, setPrevSearch] = (0, react_1.useState)();
797
821
  const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
798
822
  const usersIndex = algoliaClient.initIndex("users");
823
+ const sortOptions = {
824
+ ["Forename - Asc"]: ["details.forename", "asc"],
825
+ ["Forename - Desc"]: ["details.forename", "desc"],
826
+ ["Surname - Asc"]: ["details.surname", "desc"],
827
+ ["Surname - Desc"]: ["details.surname", "asc"],
828
+ ["Email - Asc"]: ["email", "asc"],
829
+ ["Email - Desc"]: ["email", "desc"],
830
+ };
799
831
  (0, react_1.useEffect)(() => {
800
832
  var _a;
801
833
  if (user.userType !== "Staff") {
@@ -804,8 +836,8 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
804
836
  return;
805
837
  }
806
838
  if ((!user.viewCohorts && user.userGroup !== "admin") ||
807
- user.viewCohorts === "none" ||
808
- (user.viewCohorts === "some" && cohort !== "all" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.split(",").includes(cohort || "")))) {
839
+ (user.viewCohorts === "none" && user.userGroup !== "admin") ||
840
+ (user.viewCohorts === "some" && user.userGroup !== "admin" && cohort !== "all" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort || "")))) {
809
841
  setQueries(undefined);
810
842
  return;
811
843
  }
@@ -821,13 +853,13 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
821
853
  });
822
854
  constraints.push((0, firestore_1.where)("oId", "==", user.oId));
823
855
  cohort && cohort !== "all" && constraints.push((0, firestore_1.where)("cohort", "==", cohort));
824
- if (user.userGroup === "admin" || user.viewUsers === "all") {
856
+ if (user.userGroup === "admin" || user.viewStudents === "all") {
825
857
  finalConstraints.push(constraints);
826
858
  continue;
827
859
  }
828
860
  if (!user.studentFilter || !user.studentFilterValues)
829
861
  continue;
830
- user.studentFilterValues.split(", ").forEach((filterValue) => {
862
+ user.studentFilterValues.forEach((filterValue) => {
831
863
  user.studentFilter && finalConstraints.push([...constraints, (0, firestore_1.where)("details." + user.studentFilter, "==", filterValue)]);
832
864
  });
833
865
  }
@@ -837,7 +869,7 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
837
869
  });
838
870
  }, []);
839
871
  const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
840
- if (!queries) {
872
+ if (!(queries === null || queries === void 0 ? void 0 : queries.length)) {
841
873
  setTableData({});
842
874
  return;
843
875
  }
@@ -854,17 +886,42 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
854
886
  const createQuery = (mConstraints) => {
855
887
  console.log("mConstraints", mConstraints);
856
888
  const fConstraints = [...mConstraints];
889
+ let addOrderBy = true;
857
890
  filters && Object.entries(filters).forEach(([key, value]) => {
858
- if (typeof value === "object") {
891
+ if (Array.isArray(value)) {
892
+ value.forEach((v) => {
893
+ if (typeof v === "object") {
894
+ if (v instanceof firestore_1.QueryOrderByConstraint) {
895
+ addOrderBy = false;
896
+ }
897
+ fConstraints.push(v);
898
+ }
899
+ else {
900
+ fConstraints.push((0, firestore_1.where)(key, "==", v));
901
+ }
902
+ });
903
+ }
904
+ else if (typeof value === "object") {
905
+ if (value instanceof firestore_1.QueryOrderByConstraint) {
906
+ addOrderBy = false;
907
+ }
859
908
  fConstraints.push(value);
860
909
  }
861
910
  else {
862
911
  fConstraints.push((0, firestore_1.where)(key, "==", value));
863
912
  }
864
913
  });
865
- fConstraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
914
+ console.log("Add order by", addOrderBy);
915
+ if (addOrderBy) {
916
+ if (sortResultsBy) {
917
+ fConstraints.push((0, firestore_1.orderBy)(sortOptions[sortResultsBy][0], sortOptions[sortResultsBy][1]));
918
+ }
919
+ else {
920
+ fConstraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
921
+ }
922
+ }
866
923
  if (page[0] > page[1] && !cursorDirection) { // Going up
867
- currentQueryAnchor.endKey && fConstraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
924
+ currentQueryAnchor.endDoc && fConstraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endDoc));
868
925
  fConstraints.push((0, firestore_1.limit)(10));
869
926
  if (!loadMoreFromQuery) {
870
927
  currentQueryAnchor = { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.endQueryPos };
@@ -875,11 +932,11 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
875
932
  currentQueryAnchor = { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.startQueryPos };
876
933
  }
877
934
  fConstraints.push((0, firestore_1.limitToLast)(10));
878
- if (currentQueryAnchor.startKey) {
879
- currentQueryAnchor.startKey && fConstraints.push((0, firestore_1.endBefore)(currentQueryAnchor.startKey));
935
+ if (currentQueryAnchor.startDoc) {
936
+ currentQueryAnchor.startDoc && fConstraints.push((0, firestore_1.endBefore)(currentQueryAnchor.startDoc));
880
937
  }
881
938
  else {
882
- currentQueryAnchor.startKey && fConstraints.push((0, firestore_1.endAt)(currentQueryAnchor.startKey));
939
+ currentQueryAnchor.startDoc && fConstraints.push((0, firestore_1.endAt)(currentQueryAnchor.startDoc));
883
940
  }
884
941
  }
885
942
  else {
@@ -918,9 +975,7 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
918
975
  console.log("Removing ", doc.id, ": E=", prevEntries[doc.id], ", G=", position);
919
976
  return;
920
977
  }
921
- const item = doc.data();
922
- item.id = doc.id;
923
- queryResults[doc.id] = item;
978
+ queryResults[doc.id] = doc;
924
979
  index = index + 1;
925
980
  if (prevEntries[doc.id])
926
981
  return;
@@ -942,7 +997,7 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
942
997
  }
943
998
  }
944
999
  if (Object.keys(itemList).length < 10 && queryData.size === 10) {
945
- return getDataFromQuery(itemList, { ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] }, undefined, prevEntries, true);
1000
+ return getDataFromQuery(itemList, { ...currentQueryAnchor, startDoc: Object.values(itemList)[0], endDoc: Object.values(itemList).slice(-1)[0] }, undefined, prevEntries, true);
946
1001
  }
947
1002
  if (queryData.size === 0 &&
948
1003
  Object.keys(itemList).length === 0 &&
@@ -968,8 +1023,8 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
968
1023
  setDataListenerUnsubscribe(() => itemUpdateSnapshot);
969
1024
  };
970
1025
  listenForUpdates();
971
- setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
972
- setTableData(itemList);
1026
+ setQueryAnchor({ ...currentQueryAnchor, startDoc: Object.values(itemList)[0], endDoc: Object.values(itemList).slice(-1)[0] });
1027
+ setTableData(Object.fromEntries(Object.entries(itemList).map(([k, v]) => [k, { id: k, ...v.data() }])));
973
1028
  };
974
1029
  const searchUsers = async () => {
975
1030
  if (!search)
@@ -1006,10 +1061,10 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
1006
1061
  setPage([1, 0]);
1007
1062
  console.log("Set page");
1008
1063
  setTableData({});
1009
- setQueryAnchor({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
1064
+ setQueryAnchor({ startQueryPos: 0, endQueryPos: 0 });
1010
1065
  setPrevEntryIds({});
1011
1066
  dataListenerUnsubscribe && dataListenerUnsubscribe();
1012
- }, [filters, search]);
1067
+ }, [filters, search, sortResultsBy]);
1013
1068
  // Fetch new data when queries or page change
1014
1069
  (0, react_1.useEffect)(() => {
1015
1070
  if (search) {
@@ -1020,7 +1075,8 @@ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
1020
1075
  getDataFromQuery();
1021
1076
  dataListenerUnsubscribe && dataListenerUnsubscribe();
1022
1077
  }, [page, queries, prevSearch]);
1023
- return ({ ...{ tableData, setPage, page, setFilters } });
1078
+ const setSort = (s) => setSortResultsBy(s);
1079
+ return ({ ...{ tableData, setPage, page, setFilters, setSort, sortOptions: Object.keys(sortOptions), sortBy: sortResultsBy } });
1024
1080
  }
1025
1081
  function useAdmissionsPaginator({ data }) {
1026
1082
  const [tableData, setTableData] = (0, react_1.useState)({});
@@ -1066,371 +1122,440 @@ function useLazyLoadQueryList({ path, constraints, number, onItemFetched }) {
1066
1122
  return [doc.id, onItemFetched ? await onItemFetched(itemObj) : itemObj];
1067
1123
  })));
1068
1124
  setItems((i) => ({ ...i, ...processedItems }));
1069
- setLoadMoreIcon(false);
1070
- };
1071
- (0, react_1.useEffect)(() => {
1072
- loadMore();
1073
- }, []);
1074
- return ({ ...{ items, loadMore, loadMoreIcon, reset } });
1075
- }
1076
- function usePublicPlacementListingLoader({ providerId, number = 5 }) {
1077
- const [items, setItems] = (0, react_1.useState)({});
1078
- const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(false);
1079
- const [lastItem, setLastItem] = (0, react_1.useState)();
1080
- const firebaseQuery = new firebaseQuery_1.default();
1081
- const reset = () => {
1082
- setItems({});
1083
- setLastItem(undefined);
1084
- };
1085
- const loadMore = async () => {
1086
- setLoadMoreIcon(true);
1087
- let formattedConstraints = [
1088
- (0, firestore_1.where)("status", "==", "listed"),
1089
- (0, firestore_1.limit)(number)
1090
- ];
1091
- if (providerId) {
1092
- formattedConstraints.push((0, firestore_1.where)("providerId", "==", providerId));
1093
- }
1094
- if (lastItem) {
1095
- formattedConstraints.push((0, firestore_1.startAfter)(lastItem));
1096
- }
1097
- const documents = await firebaseQuery.getDocsWhere("placementListings", formattedConstraints, true);
1098
- console.log("docs", documents.docs);
1099
- setLastItem(documents.docs[documents.docs.length - 1]);
1100
- const processedItems = Object.fromEntries(await Promise.all(Object.values(documents.docs).map(async (doc) => {
1101
- var _a, _b;
1102
- let itemObj = { ...doc.data(), id: doc.id };
1103
- if (itemObj.addressId) {
1104
- const address = await firebaseQuery.getDocData(["addresses", itemObj.addressId]);
1105
- delete address.id;
1106
- itemObj = { ...address, ...itemObj };
1107
- }
1108
- if (itemObj.applicantWorkflowId) {
1109
- const applicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", itemObj.applicantWorkflowId])).workflow.filter((i) => i.id === 1)[0];
1110
- const applicantFiles = applicantWorkflow.files ? Object.fromEntries(await Promise.all((_a = applicantWorkflow.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
1111
- const file = await firebaseQuery.getDocData(["files", fileId]);
1112
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${file.fileName}`));
1113
- return [fileId, file];
1114
- }))) : [];
1115
- const applicantForms = applicantWorkflow.forms ? Object.fromEntries(await Promise.all((_b = applicantWorkflow.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
1116
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1117
- }))) : [];
1118
- applicantWorkflow.viewableFiles = applicantFiles;
1119
- applicantWorkflow.formDetails = applicantForms;
1120
- itemObj = { ...itemObj, applicantWorkflow: [applicantWorkflow] };
1121
- }
1122
- return [doc.id, itemObj];
1123
- })));
1124
- setItems((i) => ({ ...i, ...processedItems }));
1125
- setLoadMoreIcon(false);
1126
- };
1127
- (0, react_1.useEffect)(() => {
1128
- loadMore();
1129
- }, []);
1130
- return ({ ...{ items, loadMore, loadMoreIcon, reset } });
1131
- }
1132
- function useCreateApplicationRenderer({ user, listingId, listing, provider, application, applicationId, orgContext }) {
1133
- const firebaseQuery = new firebaseQuery_1.default();
1134
- let applicationWithoutAdditionalData = { ...(application || {}) };
1135
- delete applicationWithoutAdditionalData.listing;
1136
- delete applicationWithoutAdditionalData.address;
1137
- delete applicationWithoutAdditionalData.provider;
1138
- const [fApplication, setFApplication] = (0, react_1.useState)(application ? applicationWithoutAdditionalData : {
1139
- uid: user.userType === "Students" ? user.id : undefined,
1140
- listingId: listingId,
1141
- stage: 1,
1142
- reqUserType: "Students",
1143
- status: "draft"
1144
- });
1145
- const [fApplicationId, setFApplicationId] = (0, react_1.useState)(applicationId);
1146
- const [draftSaved, setDraftSaved] = (0, react_1.useState)(false);
1147
- const [fProvider, setFProvider] = (0, react_1.useState)(provider);
1148
- const [fListing, setFListing] = (0, react_1.useState)(listing);
1149
- const [student, setStudent] = (0, react_1.useState)(user.userType === "Students" ? user : undefined);
1150
- const [profileUrl, setProfileUrl] = (0, react_1.useState)();
1151
- const [successPopup, setSuccessPopup] = (0, react_1.useState)();
1152
- const [uploadedFiles, setUploadedFiles] = (0, react_1.useState)();
1153
- const [currentStageComplete, setCurrentStageComplete] = (0, react_1.useState)();
1154
- (0, react_1.useEffect)(() => {
1155
- const getListing = async () => {
1156
- console.log("Checking ID");
1157
- if (!listingId)
1158
- return;
1159
- console.log("Getting listing");
1160
- const listingData = listing || await firebaseQuery.getDocData(["placementListings", listingId]);
1161
- const address = (user.product === "providers" && orgContext) ? orgContext.addresses[listingData.addressId || ""] : await firebaseQuery.getDocData(["addresses", listingData.addressId]);
1162
- const workflow = (user.product === "providers" && orgContext) ? orgContext.applicantWorkflows[listingData.applicantWorkflowId || ""] : await firebaseQuery.getDocData(["applicantWorkflows", listingData.applicantWorkflowId]);
1163
- workflow.workflow = await Promise.all(workflow.workflow.map(async (s) => {
1164
- var _a, _b;
1165
- const applicantFiles = s.files ? Object.fromEntries(await Promise.all((_a = s.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
1166
- const file = await firebaseQuery.getDocData(["files", fileId]);
1167
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${file.fileName}`));
1168
- return [fileId, file];
1169
- }))) : [];
1170
- const applicantForms = s.forms ? Object.fromEntries(await Promise.all((_b = s.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
1171
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1172
- }))) : [];
1173
- return { ...s, viewableFiles: applicantFiles, formDetails: applicantForms };
1174
- }));
1175
- delete address.id;
1176
- delete workflow.id;
1177
- console.log("Setting listing");
1178
- setFListing({ ...listingData, ...address, applicantWorkflow: workflow.workflow });
1179
- console.log("Getting provider", listingData === null || listingData === void 0 ? void 0 : listingData.providerId);
1180
- if (((fProvider === null || fProvider === void 0 ? void 0 : fProvider.id) === (application === null || application === void 0 ? void 0 : application.providerId)) && user.product === "providers") {
1181
- setFProvider({ details: orgContext === null || orgContext === void 0 ? void 0 : orgContext.details, id: user.oId });
1182
- }
1183
- else if (listingData === null || listingData === void 0 ? void 0 : listingData.providerId) {
1184
- console.log("Getting provider from DB");
1185
- const provider = await firebaseQuery.getDocData(["providers", listingData.providerId]);
1186
- console.log("Provider", provider);
1187
- setFProvider({ details: provider, id: listingData.providerId });
1188
- }
1189
- };
1190
- getListing();
1191
- }, [listingId, listing]);
1192
- (0, react_1.useEffect)(() => {
1193
- if ((student === null || student === void 0 ? void 0 : student.id) !== (application === null || application === void 0 ? void 0 : application.uid)) {
1194
- if (user.userType === "Students") {
1195
- setStudent(user);
1196
- }
1197
- else if (user.product === "providers" && (application === null || application === void 0 ? void 0 : application.uid)) {
1198
- firebaseQuery.getDocData(["users", application.uid]).then((s) => setStudent(s));
1199
- }
1200
- }
1201
- }, []);
1202
- (0, react_1.useEffect)(() => {
1203
- setFListing(listing);
1204
- }, [listing]);
1205
- (0, react_1.useEffect)(() => {
1206
- if (provider === null || provider === void 0 ? void 0 : provider.profile)
1207
- return;
1208
- if (!(provider === null || provider === void 0 ? void 0 : provider.id))
1209
- return;
1210
- (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `providers/${provider === null || provider === void 0 ? void 0 : provider.id}/profilePic.png`)).then(setProfileUrl).catch(() => null);
1211
- }, [provider]);
1212
- (0, react_1.useEffect)(() => {
1213
- console.log("APPLICATIONID", applicationId);
1214
- if (!applicationId) {
1215
- if (!listingId)
1216
- return;
1217
- firebaseQuery.getDocsWhere("applications", [(0, firestore_1.where)("status", "not-in", ["approved", "declined"]), (0, firestore_1.where)("uid", "==", user.id), (0, firestore_1.where)("listingId", "==", listingId)]).then((existingApplication) => {
1218
- console.log("EXISTING", existingApplication);
1219
- // get uploaded files
1220
- if (existingApplication && Object.keys(existingApplication).length) {
1221
- setFApplication(Object.values(existingApplication)[0]);
1222
- setFApplicationId(Object.keys(existingApplication)[0]);
1223
- }
1224
- });
1225
- return;
1226
- }
1227
- if (applicationId)
1228
- setFApplicationId(applicationId);
1229
- }, [application, applicationId]);
1230
- const getCurrentStage = async (stage) => {
1231
- var _a, _b, _c, _d;
1232
- if (!(fListing === null || fListing === void 0 ? void 0 : fListing.applicantWorkflowId))
1233
- throw new Error("No workflow stage");
1234
- const mApplicantWorkflow = await firebaseQuery.getDocData(["applicantWorkflows", fListing.applicantWorkflowId]);
1235
- const stageObj = (_a = mApplicantWorkflow === null || mApplicantWorkflow === void 0 ? void 0 : mApplicantWorkflow.workflow) === null || _a === void 0 ? void 0 : _a.find((s) => s.id === stage);
1236
- if (!stageObj)
1237
- throw new Error("Can't find stage.");
1238
- const applicantFiles = stageObj.files ? Object.fromEntries(await Promise.all((_b = stageObj.files) === null || _b === void 0 ? void 0 : _b.map(async (fileId) => {
1239
- const file = await firebaseQuery.getDocData(["files", fileId]);
1240
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${file.fileName}`));
1241
- return [fileId, file];
1242
- }))) : [];
1243
- const applicantForms = stageObj.forms ? Object.fromEntries(await Promise.all((_c = stageObj.forms) === null || _c === void 0 ? void 0 : _c.map(async (formId) => {
1244
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1245
- }))) : [];
1246
- stageObj.viewableFiles = applicantFiles;
1247
- stageObj.formDetails = applicantForms;
1248
- return { stage: stageObj, completedSections: ((_d = fApplication === null || fApplication === void 0 ? void 0 : fApplication.completedSections) === null || _d === void 0 ? void 0 : _d[stage]) || {} };
1249
- };
1250
- (0, react_1.useEffect)(() => {
1251
- const getUploadedFiles = async () => {
1252
- if (!fApplication.completedSections) {
1253
- setUploadedFiles({});
1254
- return;
1255
- }
1256
- ;
1257
- const fileIds = Object.values(fApplication.completedSections)
1258
- .flatMap(stageData => Object.values(stageData.filesUploaded || {}).flatMap(fileIds => fileIds));
1259
- const fileDataPromises = fileIds.map(async (fileId) => {
1260
- const fileData = await firebaseQuery.getDocData(["files", fileId]);
1261
- fileData.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${fileData.fileName}`));
1262
- return [fileId, fileData];
1263
- });
1264
- const fileDataArray = await Promise.all(fileDataPromises);
1265
- const fileDataObj = Object.fromEntries(fileDataArray);
1266
- setUploadedFiles(fileDataObj);
1267
- };
1268
- const addApplication = async () => {
1269
- console.log("ADDING APPLICATION");
1270
- if (!(fListing === null || fListing === void 0 ? void 0 : fListing.id) || !(fProvider === null || fProvider === void 0 ? void 0 : fProvider.id) || !(student === null || student === void 0 ? void 0 : student.id))
1271
- return;
1272
- const applicationData = {
1273
- uid: student.id,
1274
- listingId: fListing === null || fListing === void 0 ? void 0 : fListing.id,
1275
- applicantWorkflowId: fListing === null || fListing === void 0 ? void 0 : fListing.applicantWorkflowId,
1276
- providerId: fProvider.id,
1277
- stage: 1,
1278
- status: "draft",
1279
- created: (new Date()).toISOString(),
1280
- ...fApplication,
1281
- };
1282
- const mApplicationId = (await firebaseQuery.add(["applications"], applicationData)).id;
1283
- console.log("APPLICATION ADDED");
1284
- setFApplicationId(mApplicationId);
1285
- setFApplication(applicationData);
1286
- setDraftSaved(true);
1287
- return;
1288
- };
1289
- getUploadedFiles();
1290
- console.log("Checking IDs");
1291
- console.log(fListing === null || fListing === void 0 ? void 0 : fListing.id, fProvider === null || fProvider === void 0 ? void 0 : fProvider.id, student === null || student === void 0 ? void 0 : student.id);
1292
- if (!(fListing === null || fListing === void 0 ? void 0 : fListing.id) || !(fProvider === null || fProvider === void 0 ? void 0 : fProvider.id) || !(student === null || student === void 0 ? void 0 : student.id))
1293
- return;
1294
- if (user.product === "providers")
1295
- throw new Error("Providers cannot create applications");
1296
- console.log("Checking dates and sections");
1297
- if (!fApplication.completedSections && !fApplication.startDate && !fApplication.endDate)
1298
- return;
1299
- console.log("Application updated");
1300
- if (!fApplicationId && !applicationId) {
1301
- // save new
1302
- console.log("Add application");
1303
- addApplication();
1304
- return;
1305
- }
1306
- // update
1307
- firebaseQuery.update(["applications", (fApplicationId || applicationId)], fApplication);
1308
- setDraftSaved(true);
1309
- }, [fApplication]);
1310
- const openSuccessPopup = (type) => {
1311
- setSuccessPopup(type);
1312
- setTimeout(() => {
1313
- // onClose();
1314
- }, 1500);
1315
- };
1316
- (0, react_1.useEffect)(() => {
1317
- const areStagesCompleted = async () => {
1318
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1319
- if (!fListing)
1320
- return;
1321
- if (fApplication.stage === undefined)
1322
- return undefined;
1323
- const currentStage = await getCurrentStage(fApplication.stage);
1324
- console.log("Checking current stage is complete");
1325
- for (const fileViewed of ((_a = currentStage.stage) === null || _a === void 0 ? void 0 : _a.files) || []) {
1326
- console.log("Checking file viewed", fileViewed, "in", (_b = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _b === void 0 ? void 0 : _b.filesViewed);
1327
- if (!((_d = (_c = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _c === void 0 ? void 0 : _c.filesViewed) === null || _d === void 0 ? void 0 : _d.includes(fileViewed))) {
1328
- console.log("File not viewed");
1329
- return false;
1330
- }
1331
- else {
1332
- console.log("File viewed");
1333
- }
1334
- }
1335
- for (const formCompleted of ((_e = currentStage === null || currentStage === void 0 ? void 0 : currentStage.stage) === null || _e === void 0 ? void 0 : _e.forms) || []) {
1336
- console.log("Checking form completed", formCompleted, "in", (_f = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _f === void 0 ? void 0 : _f.formsCompleted);
1337
- if (!Object.keys(((_g = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _g === void 0 ? void 0 : _g.formsCompleted) || {}).includes(formCompleted)) {
1338
- console.log("Form not completed");
1339
- return false;
1340
- }
1341
- else {
1342
- console.log("Form completed");
1343
- }
1344
- }
1345
- for (let i = 0; i++; i < (((_h = currentStage === null || currentStage === void 0 ? void 0 : currentStage.stage) === null || _h === void 0 ? void 0 : _h.requiredFiles) || []).length) {
1346
- if (!Object.keys(((_j = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _j === void 0 ? void 0 : _j.filesUploaded) || {}).includes(i.toString())) {
1347
- console.log("Form not uploaded");
1348
- return false;
1349
- }
1350
- else {
1351
- console.log("Form uploaded");
1352
- }
1353
- }
1354
- return true;
1355
- };
1356
- areStagesCompleted().then(setCurrentStageComplete);
1357
- }, [fApplication, fListing]);
1358
- const viewFile = (file, onOpen) => {
1359
- var _a, _b;
1360
- if (fApplication.reqUserType !== user.userType)
1361
- return;
1362
- if (fApplication.stage === undefined)
1363
- throw new Error("Missing applciation stage.");
1364
- const viewableFiles = (_b = (_a = fListing === null || fListing === void 0 ? void 0 : fListing.applicantWorkflow) === null || _a === void 0 ? void 0 : _a.find((stage) => stage.id === fApplication.stage)) === null || _b === void 0 ? void 0 : _b.viewableFiles;
1365
- setFApplication((a) => {
1366
- var _a, _b;
1367
- const oldA = { ...a };
1368
- const viewedFiles = ((_b = (_a = a.completedSections) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.filesViewed) || [];
1369
- if (viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.includes(file))
1370
- return a;
1371
- (viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url) && onOpen(viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url);
1372
- viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.push(file);
1373
- const newA = (0, util_1.editNestedObject)(["completedSections", 1, "filesViewed"], oldA, viewedFiles);
1374
- return newA;
1375
- });
1376
- };
1377
- const addFile = (files, fileId) => {
1378
- if (fApplication.reqUserType !== user.userType)
1379
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1380
- if (fApplication.stage === undefined)
1381
- throw new Error("Missing applciation stage.");
1382
- if (!files.length)
1383
- throw new Error("No files to upload");
1384
- setFApplication((a) => (0, util_1.editNestedObject)(["completedSections", fApplication.stage, "filesUploaded", fileId], a, files));
1385
- };
1386
- const setFormComplete = (formId, e) => {
1387
- if (fApplication.reqUserType !== user.userType)
1388
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1389
- if (fApplication.stage === undefined)
1390
- throw new Error("Missing applciation stage.");
1391
- setFApplication((a) => (0, util_1.editNestedObject)(["completedSections", fApplication.stage, "formsCompleted", formId], a, e));
1392
- };
1393
- const successText = {
1394
- submitted: {
1395
- title: "Application submitted",
1396
- desc: "We've sent your application to the placement provider. You will hear what the next steps are soon.",
1397
- },
1398
- draftSaved: {
1399
- title: "Draft saved",
1400
- desc: "Your draft has been saved. Go to the 'Applications' section from your home screen to edit.",
1401
- },
1402
- stageComplete: {
1403
- title: "Stage complete",
1404
- desc: "We have sent this to the next stage in the application process. We will update you with any progress.",
1405
- },
1406
- outcome: {
1407
- title: "Outcome submitted",
1408
- desc: "We have sent the student an email with the outcome of their application.",
1409
- },
1410
- };
1411
- const onFApply = async (draft) => {
1412
- if (draft) {
1413
- openSuccessPopup("draftSaved");
1414
- return;
1415
- }
1416
- // Check all items have been filled in.
1417
- if (!fApplication.startDate || !fApplication.endDate)
1418
- throw new Error("Please select dates for your placement.");
1419
- await (0, firebase_1.executeCallable)("applications-submit", { applicationId: fApplicationId });
1420
- openSuccessPopup("submitted");
1421
- };
1422
- const progressStage = async (type, e) => {
1423
- // Check all stages completed.
1424
- if (!currentStageComplete)
1425
- throw new Error("Complete all forms before submitting.");
1426
- if (fApplication.reqUserType !== user.userType)
1427
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1428
- if (fApplication.stage === undefined)
1429
- throw new Error("Missing applciation stage.");
1430
- await (0, firebase_1.executeCallable)("applications-changeStage", { applicationId: fApplicationId, type: type, feedback: e === null || e === void 0 ? void 0 : e.feedback });
1125
+ setLoadMoreIcon(false);
1431
1126
  };
1432
- return { successText, onFApply, progressStage, currentStageComplete, uploadedFiles, getCurrentStage, setFormComplete, viewFile, addFile, setSuccessPopup, openSuccessPopup, setFApplication, fApplication, draftSaved, profileUrl, successPopup, fApplicationId, fListing, student, fProvider };
1127
+ (0, react_1.useEffect)(() => {
1128
+ loadMore();
1129
+ }, []);
1130
+ return ({ ...{ items, loadMore, loadMoreIcon, reset } });
1433
1131
  }
1132
+ // type PublicPlacementListingLoaderParams = {
1133
+ // providerId?: string,
1134
+ // number: number,
1135
+ // }
1136
+ // export function usePublicPlacementListingLoader({providerId, number=5}:PublicPlacementListingLoaderParams) {
1137
+ // const [items, setItems] = useState<{[key:string]:{[key:string]:unknown}}>({});
1138
+ // const [loadMoreIcon, setLoadMoreIcon] = useState<boolean>(false);
1139
+ // const [lastItem, setLastItem] = useState<QueryDocumentSnapshot<DocumentData>>();
1140
+ // const firebaseQuery = new FirebaseQuery();
1141
+ // const reset = () => {
1142
+ // setItems({});
1143
+ // setLastItem(undefined);
1144
+ // };
1145
+ // const loadMore = async () => {
1146
+ // setLoadMoreIcon(true);
1147
+ // let formattedConstraints:QueryConstraint[] = [
1148
+ // where("status", "==", "listed"),
1149
+ // limit(number)
1150
+ // ];
1151
+ // if (providerId) {
1152
+ // formattedConstraints.push(where("providerId", "==", providerId));
1153
+ // }
1154
+ // if (lastItem) {
1155
+ // formattedConstraints.push(startAfter(lastItem));
1156
+ // }
1157
+ // const documents = await firebaseQuery.getDocsWhere("placementListings", formattedConstraints, true) as QuerySnapshot<DocumentData>;
1158
+ // console.log("docs", documents.docs);
1159
+ // setLastItem(documents.docs[documents.docs.length-1]);
1160
+ // const processedItems = Object.fromEntries(await Promise.all(Object.values(documents.docs).map(async (doc) => {
1161
+ // let itemObj = {...doc.data(), id: doc.id} as PlacementListing;
1162
+ // if (itemObj.addressId) {
1163
+ // const address = await firebaseQuery.getDocData(["addresses", itemObj.addressId]) as Address;
1164
+ // delete address.id;
1165
+ // itemObj = {...address, ...itemObj};
1166
+ // }
1167
+ // if (itemObj.applicantWorkflowId) {
1168
+ // const applicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", itemObj.applicantWorkflowId]) as ApplicantWorkflow).workflow.filter((i) => i.id === 1)[0];
1169
+ // const applicantFiles = applicantWorkflow.files ? Object.fromEntries(await Promise.all(applicantWorkflow.files?.map(async (fileId) => {
1170
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1171
+ // file.url = await getDownloadURL(ref(storage, `providers/${itemObj.providerId}/${file.fileName}`));
1172
+ // return [fileId, file];
1173
+ // }))) : [];
1174
+ // const applicantForms = applicantWorkflow.forms ? Object.fromEntries(await Promise.all(applicantWorkflow.forms?.map(async (formId) => {
1175
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1176
+ // }))) : [];
1177
+ // applicantWorkflow.viewableFiles = applicantFiles;
1178
+ // applicantWorkflow.formDetails = applicantForms;
1179
+ // itemObj = {...itemObj, applicantWorkflow: [applicantWorkflow]};
1180
+ // }
1181
+ // return [doc.id, itemObj];
1182
+ // })))
1183
+ // setItems((i) => ({...i, ...processedItems}));
1184
+ // setLoadMoreIcon(false);
1185
+ // };
1186
+ // useEffect(() => {
1187
+ // loadMore();
1188
+ // }, []);
1189
+ // return ({...{items, loadMore, loadMoreIcon, reset}});
1190
+ // }
1191
+ // export type ApplicationHookParams = {
1192
+ // successText: {
1193
+ // submitted: {
1194
+ // title: string;
1195
+ // desc: string;
1196
+ // };
1197
+ // draftSaved: {
1198
+ // title: string;
1199
+ // desc: string;
1200
+ // };
1201
+ // stageComplete: {
1202
+ // title: string;
1203
+ // desc: string;
1204
+ // };
1205
+ // outcome: {
1206
+ // title: string;
1207
+ // desc: string;
1208
+ // };
1209
+ // };
1210
+ // setSuccessPopup: import("react").Dispatch<import("react").SetStateAction<"submitted" | "draftSaved" | "stageComplete" | "outcome" | undefined>>;
1211
+ // openSuccessPopup: (type: "submitted" | "draftSaved" | "stageComplete" | "outcome") => void;
1212
+ // setFApplication: import("react").Dispatch<import("react").SetStateAction<Partial<Application>>>;
1213
+ // fApplication: Partial<Application>;
1214
+ // draftSaved: boolean;
1215
+ // profileUrl: string | undefined;
1216
+ // successPopup: "submitted" | "draftSaved" | "stageComplete" | "outcome" | undefined;
1217
+ // fApplicationId: string | undefined;
1218
+ // fListing: PlacementListing | false | undefined;
1219
+ // student: UserData | undefined;
1220
+ // fProvider: {
1221
+ // details?: ProviderData;
1222
+ // profile?: string;
1223
+ // id?: string;
1224
+ // } | undefined;
1225
+ // onFApply: (draft?: boolean) => Promise<void>;
1226
+ // setFormComplete: (formId: string, e: {
1227
+ // [key: string]: unknown;
1228
+ // }) => void;
1229
+ // viewFile: (file: string, onOpen: (url: string) => void) => void
1230
+ // addFile: (files: string[], fileId: number) => void;
1231
+ // uploadedFiles?: {
1232
+ // [key: string]: FileItem;
1233
+ // };
1234
+ // getCurrentStage: (stage: number) => Promise<{
1235
+ // stage: ApplicantStage;
1236
+ // completedSections: {
1237
+ // submitted?: string | undefined;
1238
+ // filesViewed?: string[] | undefined;
1239
+ // formsCompleted?: {
1240
+ // [key: string]: unknown;
1241
+ // } | undefined;
1242
+ // filesUploaded?: {
1243
+ // [key: number]: string[];
1244
+ // } | undefined;
1245
+ // };
1246
+ // }>;
1247
+ // currentStageComplete?: boolean;
1248
+ // progressStage: (type: number | "accept" | "reject", e?: {
1249
+ // feedback?: string;
1250
+ // }) => Promise<void>;
1251
+ // };
1252
+ // export function useCreateApplicationRenderer({user, listingId, listing, provider, application, applicationId, orgContext}:
1253
+ // {user:UserData, listingId:string, applicationId?: string, listing?: PlacementListing, application?: Partial<Application>,
1254
+ // orgContext?: {details: ProviderData, addresses: {[key: string]: OrganisationAddress}, applicantWorkflows: {[key: string]: ApplicantWorkflow}},
1255
+ // provider?: {details?: ProviderData, profile?: string, id?: string}}) {
1256
+ // const firebaseQuery = new FirebaseQuery();
1257
+ // let applicationWithoutAdditionalData = {...(application || {})} as any;
1258
+ // delete applicationWithoutAdditionalData.listing;
1259
+ // delete applicationWithoutAdditionalData.address;
1260
+ // delete applicationWithoutAdditionalData.provider;
1261
+ // const [fApplication, setFApplication] = useState<Partial<Application>>(application ? applicationWithoutAdditionalData : {
1262
+ // uid: user.userType === "Students" ? user.id : undefined,
1263
+ // listingId: listingId,
1264
+ // addressId: listing?.addressId,
1265
+ // stage: 1,
1266
+ // reqUserType: "Students",
1267
+ // status: "draft"});
1268
+ // const [fApplicationId, setFApplicationId] = useState<string|undefined>(applicationId);
1269
+ // const [draftSaved, setDraftSaved] = useState(false);
1270
+ // const [fProvider, setFProvider] = useState<{details?: ProviderData, profile?: string, id?: string}|undefined>(provider);
1271
+ // const [fListing, setFListing] = useState<PlacementListing|false|undefined>(Object.keys(listing || {}).length > 5 ? listing : undefined);
1272
+ // const [student, setStudent] = useState<UserData|undefined>(user.userType === "Students" ? user : undefined);
1273
+ // const [profileUrl, setProfileUrl] = useState<string>();
1274
+ // const [successPopup, setSuccessPopup] = useState<"submitted"|"draftSaved"|"stageComplete"|"outcome">();
1275
+ // const [uploadedFiles, setUploadedFiles] = useState<{[key: string]: FileItem}>();
1276
+ // const [currentStageComplete, setCurrentStageComplete] = useState<boolean>();
1277
+ // useEffect(() => {
1278
+ // const getListing = async () => {
1279
+ // console.log("Checking ID")
1280
+ // if (!listingId) return;
1281
+ // console.log("Getting listing")
1282
+ // console.log("LISTING PARAM", listing, Object.keys(listing || {}).length > 5);
1283
+ // const listingData = (Object.keys(listing || {}).length > 5) ? listing : (await firebaseQuery.getDocData(["placementListings", listingId]).catch(() => false) as PlacementListing|false);
1284
+ // console.log("LISTINGDATA", listingData, Object.keys(listing || {}).length > 5 ? {a: "string"} : "AAA");
1285
+ // const address = listingData ? (user.product === "providers" && orgContext) ? orgContext.addresses[listingData.addressId || ""] : await firebaseQuery.getDocData(["addresses", listingData.addressId as string]) as Address : undefined;
1286
+ // const workflow = listingData ? (user.product === "providers" && orgContext) ? orgContext.applicantWorkflows[listingData.applicantWorkflowId || ""] as ApplicantWorkflow : await firebaseQuery.getDocData(["applicantWorkflows", listingData.applicantWorkflowId as string]) as ApplicantWorkflow : undefined;
1287
+ // if (workflow && listingData) {
1288
+ // workflow.workflow = await Promise.all(workflow.workflow.map(async (s) => {
1289
+ // const applicantFiles = s.files ? Object.fromEntries(await Promise.all(s.files?.map(async (fileId: string) => {
1290
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1291
+ // file.url = await getDownloadURL(ref(storage, `providers/${listingData?.providerId}/${file.fileName}`));
1292
+ // return [fileId, file];
1293
+ // }))) : [];
1294
+ // const applicantForms = s.forms ? Object.fromEntries(await Promise.all(s.forms?.map(async (formId: string) => {
1295
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1296
+ // }))) : [];
1297
+ // return {...s, viewableFiles: applicantFiles, formDetails: applicantForms};
1298
+ // }));
1299
+ // delete workflow.id;
1300
+ // }
1301
+ // if (address) {
1302
+ // delete address.id;
1303
+ // }
1304
+ // console.log("Setting listing")
1305
+ // setFListing((listingData && workflow) ? {...listingData, ...address, applicantWorkflow: workflow.workflow} as PlacementListing : false);
1306
+ // if ((fProvider?.id === application?.providerId) && user.product === "providers") {
1307
+ // setFProvider({details: orgContext?.details, id: user.oId});
1308
+ // } else if (listingData && listingData?.providerId) {
1309
+ // console.log("Getting provider from DB");
1310
+ // const provider = await firebaseQuery.getDocData(["providers", listingData.providerId]) as ProviderData;
1311
+ // console.log("Provider", provider);
1312
+ // setFProvider({details: provider, id: listingData.providerId});
1313
+ // }
1314
+ // }
1315
+ // getListing();
1316
+ // }, [listingId, listing]);
1317
+ // useEffect(() => {
1318
+ // if (student?.id !== application?.uid) {
1319
+ // if (user.userType === "Students") {
1320
+ // setStudent(user);
1321
+ // } else if (user.product === "providers" && application?.uid) {
1322
+ // firebaseQuery.getDocData(["users", application.uid]).then((s) => setStudent(s as UserData));
1323
+ // }
1324
+ // }
1325
+ // }, []);
1326
+ // useEffect(() => {
1327
+ // setFListing(Object.keys(listing || {}).length > 5 ? listing : undefined);
1328
+ // }, [listing]);
1329
+ // useEffect(() => {
1330
+ // if (provider?.profile) return;
1331
+ // if (!provider?.id) return;
1332
+ // getDownloadURL(ref(storage, `providers/${provider?.id}/profilePic.png`)).then(setProfileUrl).catch(() => null);
1333
+ // }, [provider]);
1334
+ // useEffect(() => {
1335
+ // console.log("APPLICATIONID", applicationId);
1336
+ // if (!applicationId) {
1337
+ // if (!listingId) return;
1338
+ // firebaseQuery.getDocsWhere("applications", [where("status", "not-in", ["approved", "declined"]), where("uid", "==", user.id), where("listingId", "==", listingId)]).then((existingApplication) => {
1339
+ // console.log("EXISTING", existingApplication);
1340
+ // // get uploaded files
1341
+ // if (existingApplication && Object.keys(existingApplication).length) {
1342
+ // setFApplication(Object.values(existingApplication)[0]);
1343
+ // setFApplicationId(Object.keys(existingApplication)[0]);
1344
+ // }
1345
+ // });
1346
+ // return;
1347
+ // }
1348
+ // if (applicationId) {
1349
+ // setFApplicationId(applicationId);
1350
+ // if (application) {
1351
+ // let applicationWithoutAdditionalData = {...(application || {})} as any;
1352
+ // delete applicationWithoutAdditionalData.listing;
1353
+ // delete applicationWithoutAdditionalData.address;
1354
+ // delete applicationWithoutAdditionalData.provider;
1355
+ // setFApplication(applicationWithoutAdditionalData);
1356
+ // } else {
1357
+ // firebaseQuery.getDocData(["applications", applicationId]).then(setFApplication);
1358
+ // }
1359
+ // }
1360
+ // }, [application, applicationId]);
1361
+ // const getCurrentStage = async (stage: number): Promise<{
1362
+ // stage: ApplicantStage; completedSections: {
1363
+ // submitted?: string | undefined;
1364
+ // filesViewed?: string[] | undefined;
1365
+ // formsCompleted?: {
1366
+ // [key: string]: unknown;
1367
+ // } | undefined;
1368
+ // filesUploaded?: {
1369
+ // [key: number]: string[];
1370
+ // } | undefined;
1371
+ // };
1372
+ // }> => {
1373
+ // console.log("fLSITING CURRENT STAGE", fListing);
1374
+ // if (!fListing) throw new Error("Listing deleted");
1375
+ // if (!fListing?.applicantWorkflowId) throw new Error("No workflow stage");
1376
+ // const mApplicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", fListing.applicantWorkflowId]) as ApplicantWorkflow);
1377
+ // const stageObj = mApplicantWorkflow?.workflow?.find((s) => s.id === stage);
1378
+ // if (!stageObj) throw new Error("Can't find stage.");
1379
+ // const applicantFiles = stageObj.files ? Object.fromEntries(await Promise.all(stageObj.files?.map(async (fileId) => {
1380
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1381
+ // file.url = await getDownloadURL(ref(storage, `providers/${mApplicantWorkflow?.oId}/${file.fileName}`));
1382
+ // return [fileId, file];
1383
+ // }))) : [];
1384
+ // const applicantForms = stageObj.forms ? Object.fromEntries(await Promise.all(stageObj.forms?.map(async (formId) => {
1385
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1386
+ // }))) : [];
1387
+ // stageObj.viewableFiles = applicantFiles;
1388
+ // stageObj.formDetails = applicantForms;
1389
+ // return {stage: stageObj, completedSections: fApplication?.completedSections?.[stage] || {}};
1390
+ // };
1391
+ // useEffect(() => {
1392
+ // const getUploadedFiles = async () => {
1393
+ // if (!fApplication.completedSections) {
1394
+ // setUploadedFiles({});
1395
+ // return
1396
+ // };
1397
+ // const fileIds = Object.values(fApplication.completedSections)
1398
+ // .flatMap(stageData =>
1399
+ // Object.values(stageData.filesUploaded || {}).flatMap(fileIds => fileIds)
1400
+ // );
1401
+ // const fileDataPromises = fileIds.map(async (fileId) => {
1402
+ // const fileData = await firebaseQuery.getDocData(["files", fileId]) as FileItem;
1403
+ // fileData.url = await getDownloadURL(ref(storage, `userFiles/${fileData.fileName}`));
1404
+ // return [fileId, fileData] as [string, FileItem]
1405
+ // });
1406
+ // const fileDataArray = await Promise.all(fileDataPromises);
1407
+ // const fileDataObj = Object.fromEntries(fileDataArray)
1408
+ // setUploadedFiles(fileDataObj)
1409
+ // }
1410
+ // const addApplication = async () => {
1411
+ // console.log("ADDING APPLICATION");
1412
+ // if (!fListing || !fListing?.id || !fProvider?.id || !student?.id) return;
1413
+ // const applicationData = {
1414
+ // uid: student.id,
1415
+ // listingId: fListing?.id,
1416
+ // addressId: fListing.addressId,
1417
+ // applicantWorkflowId: fListing?.applicantWorkflowId,
1418
+ // providerId: fProvider.id,
1419
+ // stage: 1,
1420
+ // status: "draft",
1421
+ // created: (new Date()).toISOString(),
1422
+ // ...fApplication,
1423
+ // } as Partial<Application>;
1424
+ // const mApplicationId = (await firebaseQuery.add(["applications"], applicationData)).id;
1425
+ // console.log("APPLICATION ADDED");
1426
+ // setFApplicationId(mApplicationId);
1427
+ // setFApplication(applicationData);
1428
+ // setDraftSaved(true);
1429
+ // return;
1430
+ // };
1431
+ // getUploadedFiles();
1432
+ // console.log("Checking IDs");
1433
+ // if (!fListing || !fListing?.id || !fProvider?.id || !student?.id) return;
1434
+ // if (user.product === "providers") return;
1435
+ // console.log("Checking dates and sections");
1436
+ // if (!fApplication.completedSections && !fApplication.startDate && !fApplication.endDate) return;
1437
+ // console.log("Application updated");
1438
+ // if (!fApplicationId && !applicationId) {
1439
+ // // save new
1440
+ // console.log("Add application")
1441
+ // addApplication();
1442
+ // return;
1443
+ // }
1444
+ // // update
1445
+ // firebaseQuery.update(["applications", (fApplicationId || applicationId) as string], fApplication);
1446
+ // setDraftSaved(true);
1447
+ // }, [fApplication]);
1448
+ // const openSuccessPopup = (type: "submitted"|"draftSaved"|"stageComplete"|"outcome") => {
1449
+ // setSuccessPopup(type);
1450
+ // setTimeout(() => {
1451
+ // // onClose();
1452
+ // }, 1500);
1453
+ // };
1454
+ // useEffect(() => {
1455
+ // const areStagesCompleted = async ():Promise<boolean|undefined> => {
1456
+ // if (!fListing) return;
1457
+ // if (fApplication.stage === undefined) return undefined;
1458
+ // const currentStage = await getCurrentStage(fApplication.stage);
1459
+ // console.log("Checking current stage is complete");
1460
+ // for (const fileViewed of currentStage.stage?.files || []) {
1461
+ // console.log("Checking file viewed", fileViewed, "in", currentStage?.completedSections?.filesViewed);
1462
+ // if (!currentStage?.completedSections?.filesViewed?.includes(fileViewed)) {
1463
+ // console.log("File not viewed");
1464
+ // return false;
1465
+ // } else {
1466
+ // console.log("File viewed");
1467
+ // }
1468
+ // }
1469
+ // for (const formCompleted of currentStage?.stage?.forms || []) {
1470
+ // console.log("Checking form completed", formCompleted, "in", currentStage?.completedSections?.formsCompleted);
1471
+ // if (!Object.keys(currentStage?.completedSections?.formsCompleted || {}).includes(formCompleted)) {
1472
+ // console.log("Form not completed");
1473
+ // return false;
1474
+ // } else {
1475
+ // console.log("Form completed")
1476
+ // }
1477
+ // }
1478
+ // for (let i = 0; i++; i < (currentStage?.stage?.requiredFiles || []).length) {
1479
+ // if (!Object.keys(currentStage?.completedSections?.filesUploaded || {}).includes(i.toString())) {
1480
+ // console.log("Form not uploaded");
1481
+ // return false;
1482
+ // } else {
1483
+ // console.log("Form uploaded");
1484
+ // }
1485
+ // }
1486
+ // return true;
1487
+ // };
1488
+ // areStagesCompleted().then(setCurrentStageComplete);
1489
+ // }, [fApplication, fListing])
1490
+ // const viewFile = (file: string, onOpen: (url: string) => void) => {
1491
+ // if (fApplication.reqUserType !== user.userType) return;
1492
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1493
+ // if (!fListing) throw new Error("No associated listing.");
1494
+ // const viewableFiles = fListing?.applicantWorkflow?.find((stage) => stage.id === fApplication.stage)?.viewableFiles;
1495
+ // setFApplication((a) => {
1496
+ // const oldA = {...a};
1497
+ // const viewedFiles = a.completedSections?.[1]?.filesViewed || [];
1498
+ // if (viewedFiles?.includes(file)) return a;
1499
+ // viewableFiles?.[file].url && onOpen(viewableFiles?.[file].url);
1500
+ // viewedFiles?.push(file);
1501
+ // const newA = editNestedObject(["completedSections", 1, "filesViewed"], oldA, viewedFiles) as Partial<Application>;
1502
+ // return newA;
1503
+ // });
1504
+ // };
1505
+ // const addFile = (files: string[], fileId: number) => {
1506
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1507
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1508
+ // if (!files.length) throw new Error("No files to upload");
1509
+ // setFApplication((a) => editNestedObject(["completedSections", fApplication.stage as number, "filesUploaded", fileId], a, files) as Application);
1510
+ // };
1511
+ // const setFormComplete = (formId: string, e: {[key: string]: unknown}) => {
1512
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1513
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1514
+ // setFApplication((a) => editNestedObject(["completedSections", fApplication.stage as number, "formsCompleted", formId], a, e) as Application);
1515
+ // };
1516
+ // const successText = {
1517
+ // submitted: {
1518
+ // title: "Application submitted",
1519
+ // desc: "We've sent your application to the placement provider. You will hear what the next steps are soon.",
1520
+ // },
1521
+ // draftSaved: {
1522
+ // title: "Draft saved",
1523
+ // desc: "Your draft has been saved. Go to the 'Applications' section from your home screen to edit.",
1524
+ // },
1525
+ // stageComplete: {
1526
+ // title: "Stage complete",
1527
+ // desc: "We have sent this to the next stage in the application process. We will update you with any progress.",
1528
+ // },
1529
+ // outcome: {
1530
+ // title: "Outcome submitted",
1531
+ // desc: "We have sent the student an email with the outcome of their application.",
1532
+ // },
1533
+ // };
1534
+ // const onFApply = async (draft?: boolean) => {
1535
+ // if (draft) {
1536
+ // openSuccessPopup("draftSaved")
1537
+ // return;
1538
+ // }
1539
+ // if (!fApplicationId) return;
1540
+ // // Check all items have been filled in.
1541
+ // if (!fApplication.startDate || !fApplication.endDate) throw new Error("Please select dates for your placement.");
1542
+ // await executeCallable("applications-submit", {applicationId: fApplicationId});
1543
+ // const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]) as Application;
1544
+ // setFApplication(newApplication);
1545
+ // openSuccessPopup("submitted")
1546
+ // };
1547
+ // const progressStage = async (type: number|"accept"|"reject", e?: {feedback?: string}) => {
1548
+ // // Check all stages completed.
1549
+ // if (!fApplicationId) return;
1550
+ // if (!currentStageComplete) throw new Error("Complete all forms before submitting.");
1551
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1552
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1553
+ // await executeCallable("applications-changeStage", {applicationId: fApplicationId, type: type, feedback: e?.feedback});
1554
+ // const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]) as Application;
1555
+ // setFApplication(newApplication);
1556
+ // };
1557
+ // return {successText, onFApply, progressStage, currentStageComplete, uploadedFiles, getCurrentStage, setFormComplete, viewFile, addFile, setSuccessPopup, openSuccessPopup, setFApplication, fApplication, draftSaved, profileUrl, successPopup, fApplicationId, fListing, student, fProvider}
1558
+ // }
1434
1559
  function useProposePlacementRenderer({ user, orgContext, placement }) {
1435
1560
  const [businessSectionComplete, setBusinessSectionComplete] = (0, react_1.useState)(false);
1436
1561
  const [addressSectionComplete, setAddressSectionComplete] = (0, react_1.useState)(false);
@@ -1481,11 +1606,9 @@ function useProposePlacementRenderer({ user, orgContext, placement }) {
1481
1606
  }, [cohort]);
1482
1607
  const submitSection = (type, data) => {
1483
1608
  setFormData((f) => {
1484
- const { id, ...values } = data;
1485
1609
  return {
1486
1610
  ...f,
1487
- ...values,
1488
- id: ((placement === null || placement === void 0 ? void 0 : placement.placementId) || (formData === null || formData === void 0 ? void 0 : formData.id))
1611
+ ...data
1489
1612
  };
1490
1613
  });
1491
1614
  if ((placement === null || placement === void 0 ? void 0 : placement.placementId) && type === "dates") {
@@ -1498,22 +1621,44 @@ function useProposePlacementRenderer({ user, orgContext, placement }) {
1498
1621
  setFormData(undefined);
1499
1622
  };
1500
1623
  const proposePlacement = async (draft = false) => {
1624
+ const getPlacementStatus = (startDate, endDate) => {
1625
+ const today = new Date();
1626
+ if (startDate <= today && endDate >= today)
1627
+ return { active: !draft ? true : false, inProgress: true, completed: false };
1628
+ else if (endDate <= today)
1629
+ return { completed: true, inProgress: false, active: false };
1630
+ return { completed: false, inProgress: true, active: false };
1631
+ };
1501
1632
  if (!formData || !student) {
1502
1633
  throw new Error("Cannot find placement details.");
1503
1634
  }
1504
- console.log("formData", formData);
1505
- if (formData.id && formData.uid && formData.id && draft === formData.draft) {
1506
- return await firebaseQuery.update(["placements", formData.id], formData);
1507
- }
1508
- console.log("STUDENTID", student.id);
1509
- return await (0, writeDatabase_1.addPlacement)(formData, student.id, draft).catch((e) => {
1510
- console.log("error");
1511
- console.log(e);
1512
- throw e;
1513
- }).then((e) => {
1635
+ try {
1636
+ console.log("formData", formData);
1637
+ const status = getPlacementStatus(new Date(formData.startDate), new Date(formData.endDate));
1638
+ const newFormData = { ...formData, ...status };
1639
+ if (newFormData.id && newFormData.uid && draft === newFormData.draft)
1640
+ await firebaseQuery.update(["placements", newFormData.id], newFormData);
1641
+ else
1642
+ await (0, writeDatabase_1.addPlacement)(newFormData, student.id, draft);
1514
1643
  setComplete(true);
1515
- return e;
1516
- });
1644
+ return { status };
1645
+ }
1646
+ catch (error) {
1647
+ console.log("Error:", error);
1648
+ throw error;
1649
+ }
1650
+ /*
1651
+ console.log("STUDENTID", student.id);
1652
+
1653
+ return await addPlacement(formData, student.id, draft).catch((e) => {
1654
+ console.log("error");
1655
+ console.log(e);
1656
+ throw e;
1657
+ }).then((e) => {
1658
+ setComplete(true);
1659
+ return e;
1660
+ });
1661
+ */
1517
1662
  };
1518
1663
  const deletePlacement = async (id) => {
1519
1664
  return await firebaseQuery.delete(["placements", id]);
@@ -1598,9 +1743,6 @@ function useUserUploadHandler({ product, oId, userType, user, onComplete, userGr
1598
1743
  const [alert, setAlert] = (0, react_1.useState)();
1599
1744
  const { execute } = (0, firebase_1.useExecuteCallableJob)({ user: user });
1600
1745
  const requiredFields = ["forename", "surname", "email"];
1601
- if (userType === "Students") {
1602
- requiredFields.push("year");
1603
- }
1604
1746
  const checkData = (userData) => {
1605
1747
  setAlert(undefined);
1606
1748
  userData = userData.filter((u) => Object.entries(u).some(([, v]) => v));
@@ -1643,10 +1785,6 @@ function useUserUploadHandler({ product, oId, userType, user, onComplete, userGr
1643
1785
  }
1644
1786
  }
1645
1787
  // Add more test cases
1646
- if (userType === "Students" && (!user.year || !((user.year | 0) > 0 && user.year % 1 === 0))) {
1647
- setAlert({ msg: "'Year' must be a number, e.g. 10.", severity: "error" });
1648
- return false;
1649
- }
1650
1788
  }
1651
1789
  if (emptyRequiredCell) {
1652
1790
  return false;
@@ -1685,10 +1823,10 @@ function useUserUploadHandler({ product, oId, userType, user, onComplete, userGr
1685
1823
  setAlert(undefined);
1686
1824
  if (fUsers.length) {
1687
1825
  console.log("fUsers", fUsers);
1688
- execute("userManagement-addUsers", { product: product, oId: oId, users: fUsers, userType: userType, userGroupId: userGroupId, cohortId: cohortId });
1826
+ const jobId = await execute("userManagement-addUsers", { product: product, oId: oId, users: fUsers, userType: userType, userGroupId: userGroupId, cohortId: cohortId });
1827
+ onComplete && onComplete(jobId);
1689
1828
  }
1690
1829
  console.log("Complete", onComplete);
1691
- onComplete && onComplete();
1692
1830
  console.log("Finished");
1693
1831
  };
1694
1832
  const onChange = () => {
@@ -2239,34 +2377,34 @@ function useGenericWorkflowEditor({ user, initialData, defaultData, onSubmit })
2239
2377
  includedForms, fSubmitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
2240
2378
  onDeleteArrow, fWorkflowNodes, containerRef, setError, setArrows, setFWorkflowNodes } });
2241
2379
  }
2242
- function useInstitutePlacementListingHandler({ user }) {
2380
+ function useInstituteProviderContactsHandler({ user }) {
2243
2381
  const [emptyCellsWarning, setEmptyCellsWarning] = (0, react_1.useState)(false);
2244
2382
  const [alert, setAlert] = (0, react_1.useState)();
2245
- const requiredFields = ["name", "jobTitle", "email", "addressOne", "city", "postCode", "country"];
2383
+ const requiredFields = ["business", "forename", "email"];
2246
2384
  const { execute } = (0, firebase_1.useExecuteCallableJob)({ user: user });
2247
- const checkData = (placements) => {
2385
+ const checkData = (providerContacts) => {
2248
2386
  setAlert(undefined);
2249
- placements = placements.filter((u) => Object.entries(u).some(([, v]) => v));
2250
- if (!Object.entries(placements)) {
2387
+ providerContacts = providerContacts.filter((u) => Object.entries(u).some(([, v]) => v));
2388
+ if (!Object.entries(providerContacts)) {
2251
2389
  return [];
2252
2390
  }
2253
- console.log("P", placements);
2254
- if (placements.filter((u) => u.email).length < placements.length) {
2391
+ console.log("P", providerContacts);
2392
+ if (providerContacts.filter((u) => u.email).length < providerContacts.length) {
2255
2393
  setAlert({ msg: "Your data contains missing email addresses.", severity: "error" });
2256
2394
  return false;
2257
2395
  }
2258
2396
  let emptyOptionalCell = false;
2259
2397
  let emptyRequiredCell = false;
2260
- for (const placement of placements) {
2261
- if (!checkEmailValidity(placement)) {
2398
+ for (const providerContact of providerContacts) {
2399
+ if (!checkEmailValidity(providerContact)) {
2262
2400
  return false;
2263
2401
  }
2264
- if (Object.keys(placement).includes("")) {
2402
+ if (Object.keys(providerContact).includes("")) {
2265
2403
  setAlert({ msg: "Data cannot be uploaded in unnamed columns.", severity: "error" });
2266
2404
  return false;
2267
2405
  }
2268
- for (const field in placement) {
2269
- if (!placement[field]) {
2406
+ for (const field in providerContact) {
2407
+ if (!providerContact[field]) {
2270
2408
  if (requiredFields.includes(field)) {
2271
2409
  setAlert({ msg: "All users must contain " + requiredFields.join(", "), severity: "error" });
2272
2410
  emptyRequiredCell = true;
@@ -2286,39 +2424,26 @@ function useInstitutePlacementListingHandler({ user }) {
2286
2424
  else {
2287
2425
  setEmptyCellsWarning(false);
2288
2426
  }
2289
- return placements;
2427
+ return providerContacts;
2290
2428
  };
2291
- const checkEmailValidity = (placement) => {
2292
- if (!(0, util_1.validateEmail)(placement.email)) {
2293
- setAlert({ msg: `Error in email formatting: ${placement.email}. Amend errors and reupload.`, severity: "error" });
2429
+ const checkEmailValidity = (providerContact) => {
2430
+ if (!(0, util_1.validateEmail)(providerContact.email)) {
2431
+ setAlert({ msg: `Error in email formatting: ${providerContact.email}. Amend errors and reupload.`, severity: "error" });
2294
2432
  return false;
2295
2433
  }
2296
2434
  return true;
2297
2435
  };
2298
- const uploadPlacements = async (placements) => {
2299
- let fPlacements = [];
2300
- console.log("PP", placements);
2301
- const cleanUpload = checkData(placements);
2436
+ const uploadProviderContacts = async (providers, schoolId) => {
2437
+ let fProviders = [];
2438
+ console.log("PP", providers);
2439
+ const cleanUpload = checkData(providers);
2302
2440
  if (!cleanUpload) {
2303
2441
  return false;
2304
2442
  }
2305
- fPlacements = cleanUpload;
2443
+ fProviders = cleanUpload;
2306
2444
  setAlert(undefined);
2307
- if (fPlacements.length) {
2308
- const formattedPlacements = fPlacements.map((v) => ({
2309
- name: v.provider,
2310
- title: v.jobTitle,
2311
- providerEmail: v.email,
2312
- providerPhone: v.phone,
2313
- ["address-line1"]: v.addressOne,
2314
- ["address-line2"]: v.addressTwo,
2315
- locality: v.city,
2316
- postal_code: v.postcode,
2317
- country: v.country,
2318
- }));
2319
- console.log("P", placements);
2320
- console.log("callable params", { placements: formattedPlacements, instituteId: user.product === "admin" ? "admin" : user.oId });
2321
- execute("placementListing-add", { data: formattedPlacements, instituteId: user.product === "admin" ? "admin" : user.oId });
2445
+ if (fProviders.length) {
2446
+ execute("providerContacts-add", { providerContacts: fProviders, instituteId: user.product === "admin" ? "admin" : user.oId, schoolId: schoolId });
2322
2447
  }
2323
2448
  return true;
2324
2449
  };
@@ -2326,7 +2451,7 @@ function useInstitutePlacementListingHandler({ user }) {
2326
2451
  setAlert(undefined);
2327
2452
  setEmptyCellsWarning(false);
2328
2453
  };
2329
- return ({ ...{ uploadPlacements, alert, onChange } });
2454
+ return ({ ...{ uploadProviderContacts, alert, onChange } });
2330
2455
  }
2331
2456
  function useGetIndividualPlacementForPlacementPage({ user, placementId, organisation }) {
2332
2457
  var _a;
@@ -2340,7 +2465,7 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2340
2465
  const [disableEmail, setDisableEmail] = (0, react_1.useState)({ parent: false, provider: false });
2341
2466
  const [rejectELIPopup, setRejectELIPopup] = (0, react_1.useState)(false);
2342
2467
  const [eliPopupOpen, setEliPopupOpen] = (0, react_1.useState)(false);
2343
- const [eliURL, setELIURL] = (0, react_1.useState)("");
2468
+ const [eliData, setELIData] = (0, react_1.useState)();
2344
2469
  const [rejectExternalDocPopup, setRejectExternalDocPopup] = (0, react_1.useState)(false);
2345
2470
  const [externalDocPopupOpen, setExternalDocPopupOpen] = (0, react_1.useState)(false);
2346
2471
  const [riskAssessmentURL, setRiskAssessmentURL] = (0, react_1.useState)("");
@@ -2353,6 +2478,9 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2353
2478
  const [uploadRA, setUploadRA] = (0, react_1.useState)(false);
2354
2479
  const [uploadDBS, setUploadDBS] = (0, react_1.useState)(false);
2355
2480
  const [onboardingPopup, setOnboardingPopup] = (0, react_1.useState)(false);
2481
+ const [dismissOnboardingPopup, setDismissOnboardingPopup] = (0, react_1.useState)(false);
2482
+ const [addOnboardingDocsPopup, setAddOnboardingDocsPopup] = (0, react_1.useState)(false);
2483
+ const [shareStudentRequestPopup, setShareStudentRequestPopup] = (0, react_1.useState)(false);
2356
2484
  const [editable, setEditable] = (0, react_1.useState)(false);
2357
2485
  const [withdrawFromPlacementPopup, setWithdrawFromPlacementPopup] = (0, react_1.useState)(false);
2358
2486
  const firebaseQuery = new firebaseQuery_1.default();
@@ -2395,25 +2523,43 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2395
2523
  setStudent(user);
2396
2524
  }
2397
2525
  else {
2398
- (0, readDatabase_1.getUserById)(placement.uid).then(setStudent);
2526
+ if (placement.uid) {
2527
+ (0, readDatabase_1.getUserById)(placement.uid, undefined, false).then(setStudent);
2528
+ }
2529
+ else {
2530
+ setStudent({
2531
+ details: {
2532
+ forename: placement.studentForename || "",
2533
+ surname: placement.studentSurname || "",
2534
+ },
2535
+ email: placement.studentEmail || ""
2536
+ });
2537
+ }
2399
2538
  }
2400
2539
  }, [placement]);
2401
2540
  (0, react_1.useEffect)(() => {
2402
2541
  if (!workflow || !placement)
2403
2542
  return;
2404
- const currentWorkflowStage = { ...workflow.find((obj) => obj.id === placement.status) };
2405
- // console.log("currentWorkflowStage", currentWorkflowStage)
2406
- currentWorkflowStage.id = placement.status;
2407
- // Get form data for current stage
2408
- if (currentWorkflowStage.forms) {
2409
- (0, readDatabase_1.getFormsFromId)(["forms"], currentWorkflowStage.forms).then((details) => {
2410
- currentWorkflowStage.formDetails = details;
2411
- setWStage(currentWorkflowStage);
2412
- });
2413
- }
2414
- else {
2415
- setWStage(currentWorkflowStage);
2416
- }
2543
+ const getAdditionalStageData = async () => {
2544
+ const currentWorkflowStage = { ...workflow.find((obj) => obj.id === placement.status) };
2545
+ // console.log("currentWorkflowStage", currentWorkflowStage)
2546
+ currentWorkflowStage.id = placement.status;
2547
+ // Get form data for current stage
2548
+ if (currentWorkflowStage.forms) {
2549
+ (0, readDatabase_1.getFormsFromId)(["forms"], currentWorkflowStage.forms).then((details) => {
2550
+ currentWorkflowStage.formDetails = details;
2551
+ });
2552
+ }
2553
+ if (currentWorkflowStage.files) {
2554
+ currentWorkflowStage.files = Object.fromEntries(await Promise.all(currentWorkflowStage.files.map(async (file) => {
2555
+ const fileItem = await firebaseQuery.getDocData(["files", file]);
2556
+ const url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `institutes/${placement.oId}/${fileItem.fileName}`));
2557
+ return [file, { name: fileItem.name, url: url }];
2558
+ })));
2559
+ }
2560
+ return currentWorkflowStage;
2561
+ };
2562
+ getAdditionalStageData().then(setWStage);
2417
2563
  }, [placement, workflow]);
2418
2564
  const editStage = async (nextStageId) => {
2419
2565
  if (!placementId || !wStage)
@@ -2451,10 +2597,7 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2451
2597
  const onboardingStatus = (placement === null || placement === void 0 ? void 0 : placement.onboarding) ? ((_a = placement.onboarding.completed) === null || _a === void 0 ? void 0 : _a.submitted) ? placement.onboarding.completed.accepted ? "Onboarding docs approved" : "Onboarding docs completed" : (user.userType === "Staff" ? "Onboarding sent" : "Complete onboarding") : "Add onboarding documents";
2452
2598
  const signOffPlacements = (0, util_1.getAccess)(user, "signOffPlacements");
2453
2599
  let canEdit = false;
2454
- if (((wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Staff" && user.product === "providers") && user.userGroup === "admin") {
2455
- canEdit = true;
2456
- }
2457
- else if (((wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Staff" && user.userType === "Staff" && user.product === "institutes") || (user.product === "providers" && (wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Provider") || user.userType === "Students" && (wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Students") {
2600
+ if (((wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Staff" && user.userType === "Staff" && user.product === "institutes") || (user.product === "providers" && (wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Provider") || user.userType === "Students" && (wStage === null || wStage === void 0 ? void 0 : wStage.userType) === "Students") {
2458
2601
  console.log("ALMOST CAN EDIT");
2459
2602
  if (user.userType === "Staff" && !signOffPlacements) {
2460
2603
  canEdit = false;
@@ -2463,17 +2606,20 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2463
2606
  canEdit = true;
2464
2607
  }
2465
2608
  }
2466
- const onFlagClick = async (e) => {
2609
+ const onFlagClick = async (e, onClose) => {
2467
2610
  if (!placement)
2468
2611
  return;
2469
2612
  if (e === "completeOnboarding" || e === "reviewOnboarding") {
2470
2613
  setOnboardingPopup(true);
2471
2614
  }
2615
+ if (e === "studentNotAccepted") {
2616
+ setShareStudentRequestPopup(true);
2617
+ }
2472
2618
  if (e === "noInsurance") {
2473
- if (!eliURL) {
2619
+ if (!eliData) {
2474
2620
  const storageRef = (0, storage_1.ref)(firebaseConfig_1.storage, `insurance/${placement.providerId}.pdf`);
2475
- const file = await (0, storage_1.getDownloadURL)(storageRef);
2476
- setELIURL(file);
2621
+ const file = await (0, storage_1.getDownloadURL)(storageRef).catch(() => placement.insuranceSkippedReason);
2622
+ setELIData(file);
2477
2623
  }
2478
2624
  setEliPopupOpen(true);
2479
2625
  }
@@ -2494,18 +2640,26 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2494
2640
  }
2495
2641
  setExternalDocPopupOpen("dbsCheck");
2496
2642
  }
2643
+ if (e === "addOnboarding") {
2644
+ if (onClose) {
2645
+ setDismissOnboardingPopup(true);
2646
+ }
2647
+ else {
2648
+ setAddOnboardingDocsPopup(true);
2649
+ }
2650
+ }
2497
2651
  };
2498
2652
  const approveELI = async () => {
2499
2653
  if (!placement)
2500
2654
  return;
2501
- await (0, firebase_1.executeCallable)("insurance-approve", { oId: user.oId, providerId: placement.providerId });
2655
+ await (0, firebase_1.executeCallable)("insurance-approve", { oId: user.oId, providerContactId: placement.providerContactId });
2502
2656
  setEliPopupOpen(false);
2503
2657
  };
2504
2658
  const rejectELI = async ({ reason }) => {
2505
2659
  if (!placement || !institute)
2506
2660
  return;
2507
- console.log("Reject", { reason: reason, placement: placement, instituteName: institute.name, staffEmail: user.email });
2508
- await (0, firebase_1.executeCallable)("insurance-reject", { reason: reason, placementId: placementId, instituteName: institute.name, staffEmail: user.email });
2661
+ console.log("Reject", { reason: reason, placement: placement, instituteName: institute.name });
2662
+ await (0, firebase_1.executeCallable)("insurance-reject", { reason: reason, placementId: placementId, instituteName: institute.name });
2509
2663
  setRejectELIPopup(false);
2510
2664
  setEliPopupOpen(false);
2511
2665
  };
@@ -2524,10 +2678,12 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2524
2678
  if (!placement)
2525
2679
  return;
2526
2680
  if (!placement.providerId) {
2527
- const res = await (0, firebase_1.executeCallable)("placement-uploadProviderDetails", {
2528
- pId: placementId,
2529
- placement: Object.fromEntries(Object.entries(placement).filter(([k]) => k !== "contactId")),
2530
- stage: wStage,
2681
+ const res = await (0, firebase_1.executeCallable)("providerContacts-uploadProviderDetails", {
2682
+ placement: {
2683
+ id: placementId,
2684
+ data: Object.fromEntries(Object.entries(placement).filter(([k]) => k !== "contactId")),
2685
+ stage: wStage,
2686
+ },
2531
2687
  skipSearch: true
2532
2688
  }).catch((e) => {
2533
2689
  throw e;
@@ -2553,15 +2709,17 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2553
2709
  throw new Error("Must be a student to withdraw.");
2554
2710
  await (0, firebase_1.executeCallable)("placement-withdraw", { placementId: placementId });
2555
2711
  };
2556
- return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, setWithdrawFromPlacementPopup, withdrawFromPlacement, onFlagClick, setUploadInsurance, setUploadProviderDocPopup, setUploadRA, setUploadDBS, onboardingStatus, setSkipStagePopup, onboardingPopup, setViewExternalLinkPopup, setOnboardingPopup, setRejectELIPopup, eliURL, riskAssessmentURL, dbsCheckURL, setExternalLinkCopied, skipStagePopup, snackbar, setSnackbar, cohort, disableEmail, rejectELIPopup, eliPopupOpen, rejectExternalDocPopup, externalDocPopupOpen, viewExternalLinkPopup, externalLinkCopied, uploadInsurance, uploadRA, uploadDBS, editStage, sendEmail, canEdit, approveELI, setEliPopupOpen, uploadProviderDocPopup, rejectELI, setRejectExternalDocPopup, setExternalDocPopupOpen, approveProviderDoc, rejectProviderDoc, manuallyConfigureProvider, institute };
2712
+ return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, addOnboardingDocsPopup, setAddOnboardingDocsPopup, dismissOnboardingPopup, setDismissOnboardingPopup, setWithdrawFromPlacementPopup, withdrawFromPlacement, onFlagClick, setUploadInsurance, setUploadProviderDocPopup, setUploadRA, setUploadDBS, onboardingStatus, setSkipStagePopup, onboardingPopup, setViewExternalLinkPopup, setOnboardingPopup, setRejectELIPopup, eliData, riskAssessmentURL, dbsCheckURL, setExternalLinkCopied, skipStagePopup, snackbar, setSnackbar, cohort, disableEmail, rejectELIPopup, eliPopupOpen, rejectExternalDocPopup, externalDocPopupOpen, viewExternalLinkPopup, externalLinkCopied, uploadInsurance, uploadRA, uploadDBS, editStage, sendEmail, canEdit, approveELI, setEliPopupOpen, uploadProviderDocPopup, rejectELI, setRejectExternalDocPopup, setExternalDocPopupOpen, approveProviderDoc, rejectProviderDoc, manuallyConfigureProvider, institute, shareStudentRequestPopup, setShareStudentRequestPopup };
2557
2713
  }
2558
- function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2714
+ function useOnboardingPopup({ onboarding, providerId, placementId, user, onClose }) {
2559
2715
  const [fileUploadPopup, setFileUploadPopup] = (0, react_1.useState)(false);
2560
2716
  const [form, setForm] = (0, react_1.useState)();
2561
2717
  const [rejectPopup, setRejectPopup] = (0, react_1.useState)(false);
2562
2718
  const [mOnboarding, setMOnboarding] = (0, react_1.useState)(onboarding);
2563
2719
  const [completedSections, setCompletedSections] = (0, react_1.useState)();
2564
2720
  const [uploadedFiles, setUploadedFiles] = (0, react_1.useState)({});
2721
+ const [viewableFiles, setViewableFiles] = (0, react_1.useState)();
2722
+ const [formDetails, setFormDetails] = (0, react_1.useState)({});
2565
2723
  const firebaseQuery = new firebaseQuery_1.default();
2566
2724
  const addFile = (files) => {
2567
2725
  if (!files.length || fileUploadPopup === false)
@@ -2571,12 +2729,12 @@ function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2571
2729
  };
2572
2730
  const viewFile = (file, onOpen) => {
2573
2731
  setMOnboarding((a) => {
2574
- var _a, _b, _c;
2732
+ var _a;
2575
2733
  const oldA = { ...a };
2576
2734
  const viewedFiles = a.completed ? ((_a = a.completed) === null || _a === void 0 ? void 0 : _a.filesViewed) || [] : [];
2577
2735
  if (viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.includes(file))
2578
2736
  return a;
2579
- ((_b = a === null || a === void 0 ? void 0 : a.viewableFiles) === null || _b === void 0 ? void 0 : _b[file].url) && onOpen((_c = a === null || a === void 0 ? void 0 : a.viewableFiles) === null || _c === void 0 ? void 0 : _c[file].url);
2737
+ (viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url) && onOpen(viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url);
2580
2738
  viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.push(file);
2581
2739
  const newA = (0, util_1.editNestedObject)(["completed", "filesViewed"], oldA, viewedFiles);
2582
2740
  return newA;
@@ -2597,14 +2755,14 @@ function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2597
2755
  const onboardingNew = { ...onboarding };
2598
2756
  const onboardingFiles = onboarding.files ? Object.fromEntries(await Promise.all((_a = onboarding.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
2599
2757
  const file = await firebaseQuery.getDocData(["files", fileId]);
2600
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${file.fileName}`));
2758
+ file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `providers/${providerId}/${file.fileName}`));
2601
2759
  return [fileId, file];
2602
2760
  }))) : [];
2603
2761
  const onboardingForms = onboarding.forms ? Object.fromEntries(await Promise.all((_b = onboarding.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
2604
2762
  return [formId, await firebaseQuery.getDocData(["forms", formId])];
2605
2763
  }))) : [];
2606
- onboardingNew.viewableFiles = onboardingFiles;
2607
- onboardingNew.formDetails = onboardingForms;
2764
+ setViewableFiles(onboardingFiles);
2765
+ setFormDetails(onboardingForms);
2608
2766
  return onboardingNew;
2609
2767
  };
2610
2768
  getOnboardingData().then(setMOnboarding);
@@ -2659,7 +2817,8 @@ function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2659
2817
  return;
2660
2818
  if (!stagesCompleted())
2661
2819
  throw new Error("Complete all forms before submitting.");
2662
- await firebaseQuery.update(["placements", placementId], { ["onboarding.completed.submitted"]: (0, util_1.convertDate)(new Date(), "dbstring") });
2820
+ await firebaseQuery.update(["placements", placementId], { ["onboarding.completed.accepted"]: false, ["onboarding.completed.submitted"]: true, ["onboarding.completed.submittedDate"]: (0, util_1.convertDate)(new Date(), "dbstring") });
2821
+ //executeCallable("sendOnboardingSubmittedEmail", {});
2663
2822
  };
2664
2823
  (0, react_1.useEffect)(() => {
2665
2824
  const getUploadedFiles = async () => {
@@ -2695,6 +2854,580 @@ function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2695
2854
  getUploadedFiles();
2696
2855
  addCompletedSectionURLs();
2697
2856
  }, [mOnboarding]);
2698
- return { addFile, viewFile, uploadedFiles, setFormComplete, setRejectPopup, stagesCompleted, setForm, mOnboarding, form, submit, acceptOnboarding, rejectOnboarding, rejectPopup, completedSections, fileUploadPopup, setFileUploadPopup };
2857
+ return { addFile, viewFile, uploadedFiles, setFormComplete, setRejectPopup, stagesCompleted, setForm, mOnboarding, form, submit, acceptOnboarding, rejectOnboarding, rejectPopup, completedSections, fileUploadPopup, setFileUploadPopup, viewableFiles, formDetails };
2858
+ }
2859
+ function useLoadAddresses(user, limitItems, queryConstraint, request) {
2860
+ const [addresses, setAddresses] = (0, react_1.useState)({});
2861
+ const [lastDoc, setLastDoc] = (0, react_1.useState)(null);
2862
+ const [loading, setLoading] = (0, react_1.useState)(false);
2863
+ const firebaseQuery = new firebaseQuery_1.default();
2864
+ const [queryConstraints, setQueryConstraints] = (0, react_1.useState)(queryConstraint || []);
2865
+ const changeQueryConstraints = (e) => {
2866
+ setQueryConstraints([...(queryConstraint || []), ...e]);
2867
+ };
2868
+ const loadAddresses = () => {
2869
+ var _a;
2870
+ const constraints = [(0, firestore_1.where)("oId", "==", user.oId), (0, firestore_1.where)("product", "==", user.product), (0, firestore_1.orderBy)((0, firestore_1.documentId)())];
2871
+ if (limitItems) {
2872
+ constraints.push((0, firestore_1.limit)(limitItems));
2873
+ }
2874
+ if (user.viewAddresses === "all" || user.userGroup === "admin" || request) {
2875
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
2876
+ constraints.push((0, firestore_1.startAfter)(lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id));
2877
+ }
2878
+ }
2879
+ else if (user.viewAddresses === "request") {
2880
+ if (!((_a = user.visibleAddresses) === null || _a === void 0 ? void 0 : _a.length))
2881
+ return;
2882
+ constraints.push((0, firestore_1.where)((0, firestore_1.documentId)(), "in", user.visibleAddresses));
2883
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
2884
+ constraints.push((0, firestore_1.startAfter)(lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id));
2885
+ }
2886
+ }
2887
+ else {
2888
+ setLoading(false);
2889
+ return; // viewAddresses === "none", no need to load anything
2890
+ }
2891
+ queryConstraints && constraints.unshift(...queryConstraints);
2892
+ return firebaseQuery.collectionSnapshot((async (snapshot) => {
2893
+ const deletedAddresses = snapshot.docChanges().map((change) => {
2894
+ if (change.type === "removed") {
2895
+ return change.doc.id;
2896
+ }
2897
+ return;
2898
+ });
2899
+ setAddresses((prev) => Object.fromEntries(Object.entries(prev).filter(([k]) => !deletedAddresses.includes(k))));
2900
+ if (!snapshot.empty) {
2901
+ const newAddresses = snapshot.docs.map(doc => ([doc.id, { id: doc.id, ...doc.data() }]));
2902
+ const withListings = Object.fromEntries(await Promise.all(newAddresses.map(async ([k, address]) => {
2903
+ const listings = await firebaseQuery.getCount("placementListings", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("addressId", "==", k)]);
2904
+ return [k, { ...address, listings: listings }];
2905
+ })));
2906
+ setAddresses(prev => ({ ...prev, ...withListings }));
2907
+ setLastDoc(snapshot.docs[snapshot.docs.length - 1]);
2908
+ }
2909
+ setLoading(false);
2910
+ }), "addresses", constraints, undefined, true);
2911
+ };
2912
+ (0, react_1.useEffect)(() => {
2913
+ const unsubscribe = loadAddresses();
2914
+ return () => {
2915
+ if (unsubscribe) {
2916
+ unsubscribe(); // Unsubscribe from the snapshot listener when the component unmounts
2917
+ }
2918
+ };
2919
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2920
+ }, [queryConstraints]);
2921
+ const onScrollBottom = () => {
2922
+ if (!limitItems)
2923
+ return;
2924
+ if (!loading) {
2925
+ setLoading(true);
2926
+ loadAddresses();
2927
+ }
2928
+ };
2929
+ return { addresses, onScrollBottom, loading, changeQueryConstraints };
2930
+ }
2931
+ function useLoadListings(user, queryConstraint, request) {
2932
+ const [listings, setListings] = (0, react_1.useState)([]);
2933
+ const [lastDoc, setLastDoc] = (0, react_1.useState)(null);
2934
+ const [loading, setLoading] = (0, react_1.useState)(false);
2935
+ const firebaseQuery = new firebaseQuery_1.default();
2936
+ const [queryConstraints, setQueryConstraints] = (0, react_1.useState)(queryConstraint || []);
2937
+ const changeQueryConstraints = (e) => {
2938
+ setQueryConstraints([...(queryConstraint || []), ...e]);
2939
+ };
2940
+ const loadListings = () => {
2941
+ var _a, _b;
2942
+ const constraints = [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.limit)(10), (0, firestore_1.orderBy)((0, firestore_1.documentId)())];
2943
+ if (user.viewPlacementListings === "all" || user.userGroup === "admin" || request) {
2944
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
2945
+ constraints.push((0, firestore_1.startAfter)(lastDoc));
2946
+ }
2947
+ }
2948
+ else {
2949
+ if (!((_a = user.visibleListings) === null || _a === void 0 ? void 0 : _a.length))
2950
+ return;
2951
+ constraints.push((0, firestore_1.where)((0, firestore_1.documentId)(), 'in', user.visibleListings));
2952
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
2953
+ constraints.push((0, firestore_1.startAfter)(lastDoc));
2954
+ }
2955
+ }
2956
+ if (user.viewAddresses !== "all" && user.viewPlacementListings === "all" && user.userGroup !== "admin") {
2957
+ if (!((_b = user.visibleAddresses) === null || _b === void 0 ? void 0 : _b.length))
2958
+ return;
2959
+ constraints.push((0, firestore_1.where)('addressId', 'in', user.visibleAddresses));
2960
+ }
2961
+ queryConstraints && constraints.unshift(...queryConstraints);
2962
+ return firebaseQuery.collectionSnapshot((async (snapshot) => {
2963
+ const deletedListings = snapshot.docChanges().map((change) => {
2964
+ if (change.type === "removed") {
2965
+ return change.doc.id;
2966
+ }
2967
+ return;
2968
+ });
2969
+ setListings((prev) => prev.filter(([k]) => !deletedListings.includes(k)));
2970
+ if (!snapshot.empty) {
2971
+ const newListings = snapshot.docs.map(doc => ([doc.id, { ...doc.data(), id: doc.id }]));
2972
+ const listingsWithAdditionalData = await Promise.all(newListings.map(async ([id, listing]) => {
2973
+ const listingWithAdditionalData = { ...listing };
2974
+ if (listingWithAdditionalData.applicants !== undefined)
2975
+ return [id, listingWithAdditionalData];
2976
+ listingWithAdditionalData.applicants = await firebaseQuery.getCount("applications", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("placementId", "==", id), (0, firestore_1.where)("status", "==", "submitted")]);
2977
+ listingWithAdditionalData.scheduled = await firebaseQuery.getCount("placements", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("placementId", "==", id), (0, firestore_1.where)("inProgress", "==", true), (0, firestore_1.where)("startDate", ">", (0, util_1.convertDate)(new Date(), "dbstring"))]);
2978
+ listingWithAdditionalData.active = await firebaseQuery.getCount("placements", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("placementId", "==", id), (0, firestore_1.where)("active", "==", true)]);
2979
+ return [id, listingWithAdditionalData];
2980
+ }));
2981
+ setListings(prev => (Object.entries({ ...Object.fromEntries(prev), ...Object.fromEntries(listingsWithAdditionalData) })));
2982
+ setLastDoc(snapshot.docs[snapshot.docs.length - 1]);
2983
+ }
2984
+ setLoading(false);
2985
+ }), "placementListings", constraints, undefined, true);
2986
+ };
2987
+ (0, react_1.useEffect)(() => {
2988
+ loadListings();
2989
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2990
+ }, [queryConstraints]);
2991
+ const onScrollBottom = () => {
2992
+ if (!loading) {
2993
+ setLoading(true);
2994
+ loadListings();
2995
+ }
2996
+ };
2997
+ return { listings, onScrollBottom, loading, changeQueryConstraints };
2998
+ }
2999
+ function useLoadProviderPlacements(user, queryConstraint, placementId) {
3000
+ const [placements, setPlacements] = (0, react_1.useState)([]);
3001
+ const [lastDoc, setLastDoc] = (0, react_1.useState)(null);
3002
+ const [loading, setLoading] = (0, react_1.useState)(false);
3003
+ const firebaseQuery = new firebaseQuery_1.default();
3004
+ const [queryConstraints, setQueryConstraints] = (0, react_1.useState)(queryConstraint || []);
3005
+ if (user.product !== "providers")
3006
+ throw new Error("Only providers can use this hook.");
3007
+ const changeQueryConstraints = (e) => {
3008
+ setQueryConstraints([...(queryConstraint || []), ...e]);
3009
+ };
3010
+ const loadListings = () => {
3011
+ var _a, _b;
3012
+ const constraints = [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.limit)(10), (0, firestore_1.orderBy)((0, firestore_1.documentId)())];
3013
+ if (placementId) {
3014
+ constraints.push((0, firestore_1.where)("placementId", "==", placementId));
3015
+ }
3016
+ if (user.viewPlacementListings === "all" || user.userGroup === "admin") {
3017
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
3018
+ constraints.push((0, firestore_1.startAfter)(lastDoc));
3019
+ }
3020
+ }
3021
+ else {
3022
+ if (!((_a = user.visibleListings) === null || _a === void 0 ? void 0 : _a.length))
3023
+ return;
3024
+ constraints.push((0, firestore_1.where)("placementId", 'in', user.visibleListings));
3025
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
3026
+ constraints.push((0, firestore_1.startAfter)(lastDoc));
3027
+ }
3028
+ }
3029
+ if (user.viewAddresses !== "all" && user.viewPlacementListings === "all" && user.userGroup !== "admin") {
3030
+ if (!((_b = user.visibleAddresses) === null || _b === void 0 ? void 0 : _b.length))
3031
+ return;
3032
+ constraints.push((0, firestore_1.where)('addressId', 'in', user.visibleAddresses));
3033
+ }
3034
+ queryConstraints && constraints.unshift(...queryConstraints);
3035
+ return firebaseQuery.collectionSnapshot((async (snapshot) => {
3036
+ const deletedListings = snapshot.docChanges().map((change) => {
3037
+ if (change.type === "removed") {
3038
+ return change.doc.id;
3039
+ }
3040
+ return;
3041
+ });
3042
+ setPlacements((prev) => prev.filter(([k]) => !deletedListings.includes(k)));
3043
+ if (!snapshot.empty) {
3044
+ const newPlacements = snapshot.docs.map(doc => ([doc.id, { ...doc.data(), id: doc.id }]));
3045
+ const withAdditionalData = await Promise.all(newPlacements.map(async ([id, placement]) => {
3046
+ const student = placement.uid ? await firebaseQuery.getDocData(["users", placement.uid || ""]).catch(() => false) :
3047
+ {
3048
+ details: {
3049
+ forename: placement.studentForename,
3050
+ surname: placement.studentSurname,
3051
+ },
3052
+ email: placement.studentEmail
3053
+ };
3054
+ const listing = await firebaseQuery.getDocData(["placementListings", placement.placementId || ""]).catch(() => false);
3055
+ return [id, { ...placement, student: student, listing: listing }];
3056
+ }));
3057
+ setPlacements(prev => (Object.entries({ ...Object.fromEntries(prev), ...Object.fromEntries(withAdditionalData) })));
3058
+ setLastDoc(snapshot.docs[snapshot.docs.length - 1]);
3059
+ }
3060
+ setLoading(false);
3061
+ }), "placements", constraints, undefined, true);
3062
+ };
3063
+ (0, react_1.useEffect)(() => {
3064
+ loadListings();
3065
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3066
+ }, [queryConstraints]);
3067
+ const onScrollBottom = () => {
3068
+ if (!loading) {
3069
+ setLoading(true);
3070
+ loadListings();
3071
+ }
3072
+ };
3073
+ return { placements: Object.fromEntries(placements), onScrollBottom, loading, changeQueryConstraints };
3074
+ }
3075
+ function useLoadApplications({ user, applicationType, listingId, queryConstraint }) {
3076
+ const [applications, setApplications] = (0, react_1.useState)([]);
3077
+ const [lastDoc, setLastDoc] = (0, react_1.useState)(null);
3078
+ const [loading, setLoading] = (0, react_1.useState)(false);
3079
+ const [type, setType] = (0, react_1.useState)(applicationType || "all");
3080
+ const firebaseQuery = new firebaseQuery_1.default();
3081
+ const [queryConstraints, setQueryConstraints] = (0, react_1.useState)(queryConstraint || []);
3082
+ const changeQueryConstraints = (e) => {
3083
+ setQueryConstraints([...(queryConstraint || []), ...e]);
3084
+ };
3085
+ const loadApplications = () => {
3086
+ var _a, _b;
3087
+ const constraints = [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.limit)(10), (0, firestore_1.orderBy)((0, firestore_1.documentId)())];
3088
+ if (lastDoc === null || lastDoc === void 0 ? void 0 : lastDoc.id) {
3089
+ constraints.push((0, firestore_1.startAfter)(lastDoc));
3090
+ }
3091
+ switch (type) {
3092
+ case "actionRequired":
3093
+ constraints.push((0, firestore_1.where)("status", "==", "submitted"), (0, firestore_1.where)("reqUserType", "==", "Staff"));
3094
+ break;
3095
+ case "awaitingStudent":
3096
+ constraints.push((0, firestore_1.where)("status", "==", "submitted"), (0, firestore_1.where)("reqUserType", "==", "Students"));
3097
+ break;
3098
+ case "closed":
3099
+ constraints.push((0, firestore_1.where)("status", "in", ["approved", "declined"]));
3100
+ break;
3101
+ default:
3102
+ constraints.push((0, firestore_1.where)("status", "==", "submitted"));
3103
+ }
3104
+ if (listingId) {
3105
+ constraints.push((0, firestore_1.where)("listingId", "==", listingId));
3106
+ }
3107
+ console.log("Constraints before user group check", constraints);
3108
+ if (user.viewAddresses !== "all" && user.userGroup !== "admin") {
3109
+ if (user.viewPlacementListings === "all") {
3110
+ if (!((_a = user.visibleAddresses) === null || _a === void 0 ? void 0 : _a.length))
3111
+ return;
3112
+ constraints.push((0, firestore_1.where)('addressId', 'in', user.visibleAddresses));
3113
+ }
3114
+ else {
3115
+ if (!((_b = user.visibleListings) === null || _b === void 0 ? void 0 : _b.length))
3116
+ return;
3117
+ constraints.push((0, firestore_1.where)('placementId', 'in', user.visibleListings));
3118
+ }
3119
+ }
3120
+ queryConstraints && constraints.unshift(...queryConstraints);
3121
+ console.log("Constraints after user group check", constraints);
3122
+ return firebaseQuery.collectionSnapshot((async (snapshot) => {
3123
+ const deletedApplications = snapshot.docChanges().map((change) => {
3124
+ if (change.type === "removed") {
3125
+ return change.doc.id;
3126
+ }
3127
+ return;
3128
+ });
3129
+ console.log("applicantCount", snapshot.size);
3130
+ setApplications((prev) => prev.filter(([k]) => !deletedApplications.includes(k)));
3131
+ if (!snapshot.empty) {
3132
+ const newApplications = snapshot.docs.map(doc => ([doc.id, { id: doc.id, ...doc.data() }])).filter(([, v]) => v.status !== "draft");
3133
+ setApplications(prev => ([...prev, ...newApplications]));
3134
+ setLastDoc(snapshot.docs[snapshot.docs.length - 1]);
3135
+ }
3136
+ else {
3137
+ setApplications([]);
3138
+ setLastDoc(null);
3139
+ }
3140
+ setLoading(false);
3141
+ }), "applications", constraints, undefined, true);
3142
+ };
3143
+ (0, react_1.useEffect)(() => {
3144
+ loadApplications();
3145
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3146
+ }, [type, queryConstraints]);
3147
+ const onScrollBottom = () => {
3148
+ if (!loading) {
3149
+ setLoading(true);
3150
+ loadApplications();
3151
+ }
3152
+ };
3153
+ return { applications, type, setType, onScrollBottom, loading, changeQueryConstraints };
3154
+ }
3155
+ function useDataViewerPaginator({ view: initialView, sorts, queryLimit = 10, additionalEntryProcessing, formatItems, snapshot, filters: initialFilters, onSearch, data }) {
3156
+ const [tableData, setTableData] = (0, react_1.useState)(Array.isArray(data) ? Object.fromEntries(Object.entries(data).slice(0, queryLimit)) : {});
3157
+ const [page, setPage] = (0, react_1.useState)([1, 0]);
3158
+ const [view, setView] = (0, react_1.useState)(initialView);
3159
+ const [filters, setFilters] = (0, react_1.useState)(initialFilters);
3160
+ const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
3161
+ const [prevEntryIds, setPrevEntryIds] = (0, react_1.useState)({});
3162
+ const [dataListenerUnsubscribe, setDataListenerUnsubscribe] = (0, react_1.useState)();
3163
+ const [loading, setLoading] = (0, react_1.useState)(true);
3164
+ const [searchString, setSearchString] = (0, react_1.useState)();
3165
+ const [sort, setSort] = (0, react_1.useState)();
3166
+ const processedData = async (k, v) => additionalEntryProcessing ? await additionalEntryProcessing(k, v) : v;
3167
+ const setTableDataFromDefinedData = async () => {
3168
+ if (!data || Array.isArray(data))
3169
+ return;
3170
+ const dataWithAdditionalProcessingPossibleNulls = (await Promise.all(Object.entries(data).map(async ([k, v]) => [k, await processedData(k, v)])));
3171
+ const dataWithAdditionalProcessing = dataWithAdditionalProcessingPossibleNulls.filter(([k, v]) => v);
3172
+ const searchedData = searchString ? dataWithAdditionalProcessing.filter(([, v]) => {
3173
+ const values = Object.values(v).join(", ");
3174
+ console.log("VALUESTRING", v);
3175
+ return values.includes(searchString);
3176
+ }) : dataWithAdditionalProcessing;
3177
+ const filteredData = filters && Object.keys(filters).length > 0 ? searchedData.filter(([, dataValue]) => Object.entries(filters).every(([filterKey, filterValue]) => {
3178
+ const value = dataValue[filterKey];
3179
+ if ((typeof value === "number") && value === parseInt(filterValue.value))
3180
+ return true;
3181
+ if ((typeof value === "boolean") && value === (filterValue.value === "true"))
3182
+ return true;
3183
+ if ((typeof value === "boolean") && value === (filterValue.value === "false"))
3184
+ return true;
3185
+ if ((typeof value === "string" || Array.isArray(value)) && value.includes(filterValue.value))
3186
+ return true;
3187
+ return false;
3188
+ })) : searchedData;
3189
+ if (view === "table") {
3190
+ if (!queryLimit)
3191
+ throw new Error("Tables must have a limit defined.");
3192
+ const newData = filteredData.slice((page[0] - 1) * queryLimit, page[0] * queryLimit);
3193
+ setTableData(Object.fromEntries(newData));
3194
+ if (Object.keys(Object.fromEntries(newData)).pop() === Object.keys(data).pop()) {
3195
+ setLoading("loaded");
3196
+ }
3197
+ else {
3198
+ setLoading(false);
3199
+ }
3200
+ return;
3201
+ }
3202
+ if (view === "list") {
3203
+ setTableData(Object.fromEntries(filteredData));
3204
+ setLoading("loaded");
3205
+ }
3206
+ };
3207
+ const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
3208
+ // if (!filters) return;
3209
+ setLoading(true);
3210
+ if (!Array.isArray(data)) {
3211
+ setTableDataFromDefinedData();
3212
+ return;
3213
+ }
3214
+ if (!queryLimit)
3215
+ throw new Error("Firestore queries must have a limit defined.");
3216
+ if (onSearch && (searchString || sort)) {
3217
+ if (typeof onSearch === "boolean")
3218
+ throw new Error("When using Firestore queries, an onSearch function should be passed to retrieve data externally. Additional processing is however completed in this hook.");
3219
+ const data = await onSearch(searchString, sort, page[0], filters, queryLimit);
3220
+ const dataWithAdditionalProcessing = await Promise.all(Object.entries(data).map(async ([k, v]) => [k, await processedData(k, v)]));
3221
+ setTableData((old) => {
3222
+ if (view === "table") {
3223
+ return { ...Object.fromEntries(dataWithAdditionalProcessing) };
3224
+ }
3225
+ return { ...old, ...Object.fromEntries(dataWithAdditionalProcessing) };
3226
+ });
3227
+ if (dataWithAdditionalProcessing.length < queryLimit) {
3228
+ setLoading("loaded");
3229
+ }
3230
+ else {
3231
+ setLoading(false);
3232
+ }
3233
+ return;
3234
+ }
3235
+ let cursorPos;
3236
+ if (page[0] > page[1]) {
3237
+ cursorPos = currentQueryAnchor.endQueryPos;
3238
+ }
3239
+ else {
3240
+ cursorPos = currentQueryAnchor.startQueryPos;
3241
+ }
3242
+ const querySchema = data[cursorPos];
3243
+ const createQuery = (queryData) => {
3244
+ const constraints = [];
3245
+ queryData.where && queryData.where.forEach((w) => {
3246
+ constraints.push((0, firestore_1.where)(...w));
3247
+ });
3248
+ filters && Object.entries(filters).filter(([, value]) => value.value).forEach(([key, value]) => {
3249
+ const filterValue = (value.type === "number" || value.type === "dropdown") ? parseInt(value.value) || value.value : value.value;
3250
+ constraints.push((0, firestore_1.where)(key, "==", filterValue));
3251
+ });
3252
+ constraints.push((0, firestore_1.orderBy)(queryData.orderBy ? queryData.orderBy === "documentId" ? (0, firestore_1.documentId)() : queryData.orderBy : (0, firestore_1.documentId)()));
3253
+ if (page[0] > page[1] && !cursorDirection) { // Going up
3254
+ currentQueryAnchor.endKey && constraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
3255
+ constraints.push((0, firestore_1.limit)(queryLimit));
3256
+ if (!loadMoreFromQuery) {
3257
+ currentQueryAnchor = { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.endQueryPos };
3258
+ }
3259
+ }
3260
+ else if (page[0] < page[1] && !cursorDirection) { // Going down
3261
+ if (!loadMoreFromQuery) {
3262
+ currentQueryAnchor = { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.startQueryPos };
3263
+ }
3264
+ constraints.push((0, firestore_1.limitToLast)(queryLimit));
3265
+ if (currentQueryAnchor.startKey) {
3266
+ currentQueryAnchor.startKey && constraints.push((0, firestore_1.endBefore)(currentQueryAnchor.startKey));
3267
+ }
3268
+ else {
3269
+ currentQueryAnchor.startKey && constraints.push((0, firestore_1.endAt)(currentQueryAnchor.startKey));
3270
+ }
3271
+ }
3272
+ else {
3273
+ if (cursorDirection === "decrease") {
3274
+ constraints.push((0, firestore_1.limitToLast)(queryLimit));
3275
+ }
3276
+ else {
3277
+ constraints.push((0, firestore_1.limit)(queryLimit));
3278
+ }
3279
+ }
3280
+ return constraints;
3281
+ };
3282
+ const constraints = createQuery(querySchema);
3283
+ const q = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, ...querySchema.path), ...(constraints));
3284
+ console.log("Fetching docs", constraints);
3285
+ if (snapshot) {
3286
+ // Use onSnapshot to get real-time updates
3287
+ const unsubscribe = (0, firestore_1.onSnapshot)(q, (querySnapshot) => {
3288
+ handleQuerySnapshot(querySnapshot);
3289
+ });
3290
+ // Save the unsubscribe function to state so we can dispose of it later
3291
+ setDataListenerUnsubscribe((d) => ({ ...(d || {}), [cursorPos]: unsubscribe }));
3292
+ return;
3293
+ }
3294
+ else {
3295
+ // Just get the docs without setting up a listener
3296
+ const queryData = await (0, firestore_1.getDocs)(q);
3297
+ handleQuerySnapshot(queryData);
3298
+ }
3299
+ // Function to handle query snapshot
3300
+ async function handleQuerySnapshot(querySnapshot) {
3301
+ if (!Array.isArray(data))
3302
+ throw new Error("Called querySnapshot but data is defined.");
3303
+ if (!queryLimit)
3304
+ throw new Error("Firestore queries must have a limit defined.");
3305
+ const queryResults = {};
3306
+ let index = 0; // Declare the index variable
3307
+ const reverseIfBack = (docs) => page[0] < page[1] ? docs.reverse() : docs;
3308
+ // Process each document in the querySnapshot
3309
+ for (const doc of reverseIfBack(querySnapshot.docs)) {
3310
+ if ((Object.keys(queryResults).length + Object.keys(itemList).length) === queryLimit) {
3311
+ break;
3312
+ }
3313
+ let position = Object.keys(itemList).length + (page[0] - 1) * queryLimit + index + 1;
3314
+ if (page[0] < page[1]) {
3315
+ position = (page[0]) * queryLimit - index - Object.keys(itemList).length;
3316
+ }
3317
+ if (itemList[doc.id] || (prevEntries[doc.id] && prevEntries[doc.id] !== position)) {
3318
+ console.log("Removing ", doc.id, ": E=", prevEntries[doc.id], ", G=", position);
3319
+ continue;
3320
+ }
3321
+ let item = doc.data();
3322
+ item.id = doc.id;
3323
+ // Apply additionalEntryProcessing if provided
3324
+ if (additionalEntryProcessing) {
3325
+ item = await additionalEntryProcessing(doc.id, item);
3326
+ }
3327
+ if (!item)
3328
+ continue;
3329
+ queryResults[doc.id] = item;
3330
+ index += 1;
3331
+ if (prevEntries[doc.id])
3332
+ continue;
3333
+ prevEntries[doc.id] = position;
3334
+ }
3335
+ if (cursorDirection === "decrease" || page[0] < page[1]) {
3336
+ itemList = { ...Object.fromEntries(Object.entries(queryResults).reverse()), ...itemList };
3337
+ }
3338
+ else {
3339
+ itemList = { ...itemList, ...queryResults };
3340
+ }
3341
+ // Updating state with the new data and query anchors
3342
+ setPrevEntryIds(prevEntries);
3343
+ if (querySnapshot.size < queryLimit && Object.keys(itemList).length < queryLimit) {
3344
+ // If we have ran out of entries, increase or decrease the index.
3345
+ if (page[0] > page[1] && cursorPos + 1 < data.length) {
3346
+ return getDataFromQuery(itemList, { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.endQueryPos + 1 }, "increase", prevEntries);
3347
+ }
3348
+ else if (page[0] < page[1] && cursorPos > 0) {
3349
+ return getDataFromQuery(itemList, { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.startQueryPos - 1 }, "decrease", prevEntries);
3350
+ }
3351
+ }
3352
+ if (Object.keys(itemList).length < queryLimit && querySnapshot.size === queryLimit) {
3353
+ console.log("Shorter than ten");
3354
+ return getDataFromQuery(itemList, { ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] }, undefined, prevEntries, true);
3355
+ }
3356
+ if (querySnapshot.size === 0 &&
3357
+ Object.keys(itemList).length === 0 &&
3358
+ currentQueryAnchor.endQueryPos + 1 === data.length &&
3359
+ page[0] > 1) {
3360
+ if (view === "table") {
3361
+ setTableData({});
3362
+ }
3363
+ setQueryAnchor((a) => ({ ...a, startKey: "" }));
3364
+ setLoading("loaded");
3365
+ return;
3366
+ }
3367
+ if (querySnapshot.size < queryLimit) {
3368
+ setLoading("loaded");
3369
+ }
3370
+ else {
3371
+ setLoading(false);
3372
+ }
3373
+ setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
3374
+ setTableData((old) => {
3375
+ if (view === "table") {
3376
+ return { ...itemList };
3377
+ }
3378
+ return { ...old, ...itemList };
3379
+ });
3380
+ }
3381
+ };
3382
+ const reset = () => {
3383
+ console.log("Resetting after filters?");
3384
+ setPage([1, 0]);
3385
+ setTableData({});
3386
+ setQueryAnchor({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
3387
+ setPrevEntryIds({});
3388
+ dataListenerUnsubscribe && Object.values(dataListenerUnsubscribe).map((u) => u());
3389
+ setDataListenerUnsubscribe(undefined);
3390
+ };
3391
+ (0, react_1.useEffect)(() => {
3392
+ console.log("Filters updates", filters);
3393
+ if (!filters)
3394
+ return;
3395
+ console.log("Resetting cus filters");
3396
+ reset();
3397
+ }, [filters]);
3398
+ (0, react_1.useEffect)(() => {
3399
+ console.log("View reset.");
3400
+ reset();
3401
+ }, [view]);
3402
+ (0, react_1.useEffect)(() => {
3403
+ console.log("search reset.");
3404
+ reset();
3405
+ }, [searchString]);
3406
+ (0, react_1.useEffect)(() => {
3407
+ console.log("data reset.");
3408
+ reset();
3409
+ }, [data]);
3410
+ (0, react_1.useEffect)(() => {
3411
+ console.log("sort reset.");
3412
+ reset();
3413
+ }, [sort]);
3414
+ // Fetch new data when queries or page change
3415
+ (0, react_1.useEffect)(() => {
3416
+ getDataFromQuery();
3417
+ dataListenerUnsubscribe && Object.values(dataListenerUnsubscribe).map((u) => u());
3418
+ }, [page]);
3419
+ const pageUp = () => {
3420
+ setPage((p) => ([p[0] + 1, p[0]]));
3421
+ };
3422
+ const pageDown = () => {
3423
+ setPage((p) => ([p[0] - 1, p[0]]));
3424
+ };
3425
+ const updateSort = (sortLabel) => {
3426
+ if (!sorts || !sorts[sortLabel])
3427
+ return;
3428
+ setSort([sortLabel, sorts[sortLabel]]);
3429
+ };
3430
+ return ({ ...{ tableData, pageUp, pageDown, setFilters, page: page[0], sorts, loading, sort, updateSort: updateSort, setView, updateSearch: setSearchString } });
2699
3431
  }
3432
+ ;
2700
3433
  //# sourceMappingURL=hooks.js.map