placementt-core 1.20.197 → 11.0.533

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 (135) hide show
  1. package/.eslintrc.js +40 -40
  2. package/.gitattributes +2 -2
  3. package/lib/config.d.ts +0 -1
  4. package/lib/constants.d.ts +5 -2
  5. package/lib/constants.js +130 -136
  6. package/lib/constants.js.map +1 -1
  7. package/lib/features/analytics/useAnalytics.d.ts +0 -1
  8. package/lib/features/analytics/useAnalytics.js +3 -4
  9. package/lib/features/analytics/useAnalytics.js.map +1 -1
  10. package/lib/features/config.d.ts +133 -133
  11. package/lib/features/config.js +35 -35
  12. package/lib/features/contacts/contacts.d.ts +75 -75
  13. package/lib/features/contacts/contacts.js +105 -105
  14. package/lib/features/contacts/contactsSlice.d.ts +5 -5
  15. package/lib/features/contacts/useContacts.js +1 -2
  16. package/lib/features/contacts/useContacts.js.map +1 -1
  17. package/lib/features/downtime/useDowntime.d.ts +11 -11
  18. package/lib/features/downtime/useDowntime.js +22 -22
  19. package/lib/features/dropdown/useDropdown.d.ts +2 -3
  20. package/lib/features/dropdown/useDropdown.js +1 -2
  21. package/lib/features/dropdown/useDropdown.js.map +1 -1
  22. package/lib/features/global/downtime/useDowntime.d.ts +0 -1
  23. package/lib/features/global/downtime/useDowntime.js +3 -2
  24. package/lib/features/global/downtime/useDowntime.js.map +1 -1
  25. package/lib/features/global/users/useUserFunctions.d.ts +0 -1
  26. package/lib/features/global/users/useUserFunctions.js +6 -7
  27. package/lib/features/global/users/useUserFunctions.js.map +1 -1
  28. package/lib/features/placements/studentPlacements/activePlacement.d.ts +1 -1
  29. package/lib/features/placements/studentPlacements/activePlacement.js +1 -1
  30. package/lib/features/placements/studentPlacements/activePlacement.js.map +1 -1
  31. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.d.ts +4 -5
  32. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js +1 -4
  33. package/lib/features/placements/studentPlacements/completedStudentPlacementsSlice.js.map +1 -1
  34. package/lib/features/placements/studentPlacements/studentPlacementsSlice.d.ts +63 -63
  35. package/lib/features/placements/studentPlacements/studentPlacementsSlice.js +81 -81
  36. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.d.ts +4 -4
  37. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js +1 -1
  38. package/lib/features/placements/studentPlacements/upcomingStudentPlacementsSlice.js.map +1 -1
  39. package/lib/features/placements/studentPlacements/useStudentPlacements.d.ts +0 -1
  40. package/lib/features/placements/studentPlacements/useStudentPlacements.js +1 -2
  41. package/lib/features/placements/studentPlacements/useStudentPlacements.js.map +1 -1
  42. package/lib/features/providerPlacements/providerPlacementsSlice.d.ts +19 -19
  43. package/lib/features/providerPlacements/providerPlacementsSlice.js +24 -24
  44. package/lib/features/referrals/useReferrals.d.ts +0 -1
  45. package/lib/features/referrals/useReferrals.js +1 -2
  46. package/lib/features/referrals/useReferrals.js.map +1 -1
  47. package/lib/features/studentPlacements/studentPlacementsSlice.d.ts +62 -62
  48. package/lib/features/studentPlacements/studentPlacementsSlice.js +87 -87
  49. package/lib/features/studentPlacements/useStudentPlacements.d.ts +6 -6
  50. package/lib/features/studentPlacements/useStudentPlacements.js +18 -18
  51. package/lib/features/updates/useUpdates.js +1 -2
  52. package/lib/features/updates/useUpdates.js.map +1 -1
  53. package/lib/features/userSlice.d.ts +26 -26
  54. package/lib/features/userSlice.js +23 -23
  55. package/lib/features/users/useUserFunctions.d.ts +25 -25
  56. package/lib/features/users/useUserFunctions.js +124 -124
  57. package/lib/features/users/userSlice.d.ts +46 -46
  58. package/lib/features/users/userSlice.js +48 -48
  59. package/lib/firebase/firebase.d.ts +1 -1
  60. package/lib/firebase/firebase.js +9 -4
  61. package/lib/firebase/firebase.js.map +1 -1
  62. package/lib/firebase/firebaseConfig.js +3 -0
  63. package/lib/firebase/firebaseConfig.js.map +1 -1
  64. package/lib/firebase/firebaseQuery.js +3 -0
  65. package/lib/firebase/firebaseQuery.js.map +1 -1
  66. package/lib/firebase/persistence.js +2 -2
  67. package/lib/firebase/persistence.js.map +1 -1
  68. package/lib/firebase/readDatabase.d.ts +9 -6
  69. package/lib/firebase/readDatabase.js +16 -6
  70. package/lib/firebase/readDatabase.js.map +1 -1
  71. package/lib/firebase/util.d.ts +3 -4
  72. package/lib/firebase/util.js +49 -4
  73. package/lib/firebase/util.js.map +1 -1
  74. package/lib/firebase/writeDatabase.d.ts +3 -3
  75. package/lib/firebase/writeDatabase.js +7 -1
  76. package/lib/firebase/writeDatabase.js.map +1 -1
  77. package/lib/hooks.d.ts +384 -23
  78. package/lib/hooks.js +1342 -223
  79. package/lib/hooks.js.map +1 -1
  80. package/lib/images/GatsbyBenchmarks.d.ts +0 -1
  81. package/lib/images/GatsbyBenchmarks.js +1 -1
  82. package/lib/images/GatsbyBenchmarks.js.map +1 -1
  83. package/lib/reduxHooks.d.ts +11 -20
  84. package/lib/reduxHooks.js +28 -18
  85. package/lib/reduxHooks.js.map +1 -1
  86. package/lib/tasksAndTips.d.ts +25 -5
  87. package/lib/tasksAndTips.js +346 -48
  88. package/lib/tasksAndTips.js.map +1 -1
  89. package/lib/typeDefinitions.d.ts +478 -53
  90. package/lib/util.d.ts +1 -0
  91. package/lib/util.js +78 -6
  92. package/lib/util.js.map +1 -1
  93. package/package.json +52 -49
  94. package/src/DatabaseDefinitions.ts +18 -18
  95. package/src/apiCalls.ts +128 -128
  96. package/src/config.ts +50 -50
  97. package/src/constants.ts +708 -707
  98. package/src/databaseTypes.ts +42 -42
  99. package/src/features/analytics/useAnalytics.tsx +55 -55
  100. package/src/features/contacts/contactsSlice.ts +147 -147
  101. package/src/features/contacts/useContacts.tsx +73 -73
  102. package/src/features/dropdown/useDropdown.tsx +52 -52
  103. package/src/features/global/downtime/useDowntime.tsx +19 -18
  104. package/src/features/global/users/useUserFunctions.tsx +132 -132
  105. package/src/features/jobs/jobsSlice.ts +65 -65
  106. package/src/features/placements/studentPlacements/activePlacement.ts +63 -63
  107. package/src/features/placements/studentPlacements/completedStudentPlacementsSlice.ts +94 -97
  108. package/src/features/placements/studentPlacements/upcomingStudentPlacementsSlice.ts +108 -108
  109. package/src/features/placements/studentPlacements/useStudentPlacements.tsx +33 -33
  110. package/src/features/placements/types.ts +10 -10
  111. package/src/features/referrals/useReferrals.tsx +56 -56
  112. package/src/features/updates/useUpdates.tsx +36 -36
  113. package/src/firebase/firebase.tsx +142 -138
  114. package/src/firebase/firebaseConfig.tsx +45 -42
  115. package/src/firebase/firebaseQuery.tsx +143 -140
  116. package/src/firebase/persistence.ts +84 -84
  117. package/src/firebase/readDatabase.tsx +208 -197
  118. package/src/firebase/util.tsx +352 -308
  119. package/src/firebase/writeDatabase.tsx +75 -68
  120. package/src/hooks.tsx +3395 -1943
  121. package/src/images/GatsbyBenchmarks.tsx +711 -711
  122. package/src/images/LogFuturePlacement.jsx +64 -64
  123. package/src/images/LogPreviousPlacement.jsx +228 -228
  124. package/src/images/gatsby_benchmarks.svg +466 -466
  125. package/src/images/log_future_placement.svg +114 -114
  126. package/src/images/log_previous_placement.svg +199 -199
  127. package/src/index.ts +34 -34
  128. package/src/readDatabase.tsx +3 -3
  129. package/src/reduxHooks.ts +183 -170
  130. package/src/tasksAndTips.ts +744 -410
  131. package/src/tutorialTips.ts +58 -58
  132. package/src/typeDefinitions.ts +899 -503
  133. package/src/util.ts +132 -47
  134. package/tsconfig.dev.json +5 -5
  135. package/tsconfig.json +21 -21
package/lib/hooks.js CHANGED
@@ -3,7 +3,25 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.useInstitutePlacementListingHandler = exports.useWorkflowEditor = exports.useUserUploadHandler = exports.useCreateCohortRenderer = exports.useRefDimensions = exports.useProposePlacementRenderer = exports.useLazyLoadQueryList = exports.useAdmissionsPaginator = exports.useCohortUserPaginator = exports.usePlacementListingPaginator = exports.useFilterTablePaginator = exports.useOldInstitutePlacementList = exports.useNewInstitutePlacementList = exports.useStudentPlacementList = void 0;
6
+ exports.useRefDimensions = void 0;
7
+ exports.useStudentPlacementList = useStudentPlacementList;
8
+ exports.useNewInstitutePlacementList = useNewInstitutePlacementList;
9
+ exports.useOldInstitutePlacementList = useOldInstitutePlacementList;
10
+ exports.useFilterTablePaginator = useFilterTablePaginator;
11
+ exports.usePlacementListingPaginator = usePlacementListingPaginator;
12
+ exports.useCohortUserPaginator = useCohortUserPaginator;
13
+ exports.useAdmissionsPaginator = useAdmissionsPaginator;
14
+ exports.useLazyLoadQueryList = useLazyLoadQueryList;
15
+ exports.usePublicPlacementListingLoader = usePublicPlacementListingLoader;
16
+ exports.useCreateApplicationRenderer = useCreateApplicationRenderer;
17
+ exports.useProposePlacementRenderer = useProposePlacementRenderer;
18
+ exports.useCreateCohortRenderer = useCreateCohortRenderer;
19
+ exports.useUserUploadHandler = useUserUploadHandler;
20
+ exports.useWorkflowEditor = useWorkflowEditor;
21
+ exports.useApplicantWorkflowEditor = useApplicantWorkflowEditor;
22
+ exports.useInstitutePlacementListingHandler = useInstitutePlacementListingHandler;
23
+ exports.useGetIndividualPlacementForPlacementPage = useGetIndividualPlacementForPlacementPage;
24
+ exports.useOnboardingPopup = useOnboardingPopup;
7
25
  const firestore_1 = require("firebase/firestore");
8
26
  const react_1 = require("react");
9
27
  const constants_1 = require("./constants");
@@ -13,6 +31,8 @@ const firebaseQuery_1 = __importDefault(require("./firebase/firebaseQuery"));
13
31
  const readDatabase_1 = require("./firebase/readDatabase");
14
32
  const util_1 = require("./firebase/util");
15
33
  const writeDatabase_1 = require("./firebase/writeDatabase");
34
+ const algoliasearch_1 = __importDefault(require("algoliasearch"));
35
+ const storage_1 = require("firebase/storage");
16
36
  const DEFAULTQUERYLIMIT = 5;
17
37
  function useStudentPlacementList({ user, student, queryConstraint, ql = DEFAULTQUERYLIMIT }) {
18
38
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(true);
@@ -101,21 +121,81 @@ function useStudentPlacementList({ user, student, queryConstraint, ql = DEFAULTQ
101
121
  };
102
122
  return ({ ...{ placements, loadMoreIcon, loadMorePlacements, setQuery, setInitialQueryLimit, reset, changeQueryConstraints } });
103
123
  }
104
- exports.useStudentPlacementList = useStudentPlacementList;
105
- function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT }) {
124
+ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT, inProgress }) {
106
125
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(true);
107
- const [query, setQuery] = (0, react_1.useState)("");
126
+ const [query, setQuery] = (0, react_1.useState)();
108
127
  const [initialQueryLimit, setInitialQueryLimit] = (0, react_1.useState)(ql);
109
128
  const [queryConstraints, setQueryConstraints] = (0, react_1.useState)(queryConstraint || []);
110
129
  const [oId, setOId] = (0, react_1.useState)();
111
130
  const [placements, setPlacements] = (0, react_1.useState)();
112
131
  const [startPlacementAfter, setStartPlacementAfter] = (0, react_1.useState)(); // uid, pId
132
+ const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
133
+ const placementsIndex = algoliaClient.initIndex("placements");
134
+ const usersIndex = algoliaClient.initIndex("users");
113
135
  (0, react_1.useEffect)(() => {
114
136
  if (user.product !== "institutes" || user.userType !== "Staff") {
115
137
  setOId(undefined);
116
138
  }
117
139
  setOId(user.oId);
118
140
  }, [user]);
141
+ (0, react_1.useEffect)(() => {
142
+ if (query === undefined)
143
+ return;
144
+ if (!query) {
145
+ reset();
146
+ loadMorePlacements([undefined, 0]);
147
+ }
148
+ const searchPlacements = async () => {
149
+ 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
+ let placementSearchString = `oId:${user.oId} AND ` + (inProgress !== undefined ? (inProgress ? "inProgress:true" : "completed:true") : "");
175
+ if (cohort) {
176
+ placementSearchString = placementSearchString + ` AND cohort:${cohort}`;
177
+ }
178
+ const searchPlacementHits = await placementsIndex.search(query, {
179
+ filters: placementSearchString
180
+ });
181
+ const i = (searchPlacementHits ? (await Promise.all(searchPlacementHits.hits.map(async (hit) => {
182
+ const student = hit.uid === user.id ?
183
+ user : (await (0, readDatabase_1.getUserById)(hit.uid)
184
+ .catch(() => false));
185
+ if (!student)
186
+ return;
187
+ console.log("STUDENNT", hit.objectID, student);
188
+ const finalData = { ...hit, student: student };
189
+ return [hit.objectID, finalData];
190
+ }))) : []).filter((e) => e !== undefined);
191
+ console.log("FOUND", i.length, "placement");
192
+ placementsFound = { ...Object.fromEntries(i), ...placementsFound };
193
+ console.log("found", placementsFound);
194
+ setPlacements(placementsFound);
195
+ setLoadMoreIcon(false);
196
+ };
197
+ searchPlacements();
198
+ }, [query]);
119
199
  const reset = () => {
120
200
  setPlacements(undefined);
121
201
  setStartPlacementAfter([undefined, 0]);
@@ -127,10 +207,10 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
127
207
  setPlacements(undefined);
128
208
  setStartPlacementAfter([undefined, 0]);
129
209
  loadMorePlacements([undefined, 0]);
130
- }, [query, queryConstraints]);
210
+ }, [queryConstraints]);
131
211
  const loadMorePlacements = async (fStartPlacementAfter = startPlacementAfter) => {
132
212
  var _a;
133
- if (user.viewCohorts === "none" || !oId) {
213
+ if (query || user.viewCohorts === "none" || !oId) {
134
214
  setLoadMoreIcon(false);
135
215
  return;
136
216
  }
@@ -144,7 +224,7 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
144
224
  (0, firestore_1.startAfter)(fStartPlacementAfter[0])] :
145
225
  [(0, firestore_1.limit)(placements ? DEFAULTQUERYLIMIT : initialQueryLimit), ...(queryConstraintOrdered ? [] : [(0, firestore_1.orderBy)((0, firestore_1.documentId)())])];
146
226
  queryConstraints && constraints.unshift(...queryConstraints);
147
- query !== "" && constraints.unshift((0, firestore_1.where)("name", "==", query));
227
+ // query && constraints.unshift(where("name", "==", query));
148
228
  cohort && constraints.unshift((0, firestore_1.where)("cohort", "==", cohort));
149
229
  const placementsQuery = await (0, readDatabase_1.getPlacementsWhere)({ w: constraints, oId: oId, raw: true });
150
230
  const placementsWithStudentData = placementsQuery.empty ? [] : await Promise.all(placementsQuery.docs.map(async (placement) => {
@@ -178,7 +258,6 @@ function useNewInstitutePlacementList({ id, user, cohort, queryConstraint, ql =
178
258
  };
179
259
  return ({ ...{ placements, loadMoreIcon, loadMorePlacements, setQuery, setInitialQueryLimit, reset, changeQueryConstraints } });
180
260
  }
181
- exports.useNewInstitutePlacementList = useNewInstitutePlacementList;
182
261
  function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFAULTQUERYLIMIT }) {
183
262
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(true);
184
263
  const [query, setQuery] = (0, react_1.useState)("");
@@ -372,7 +451,6 @@ function useOldInstitutePlacementList({ user, cohort, queryConstraint, ql = DEFA
372
451
  };
373
452
  return ({ ...{ placements, loadMoreIcon, loadMorePlacements, setQuery, setInitialQueryLimit, reset, changeQueryConstraints } });
374
453
  }
375
- exports.useOldInstitutePlacementList = useOldInstitutePlacementList;
376
454
  function useFilterTablePaginator({ data }) {
377
455
  const [tableData, setTableData] = (0, react_1.useState)(Array.isArray(data) ? Object.fromEntries(Object.entries(data).slice(0, 10)) : {});
378
456
  const [page, setPage] = (0, react_1.useState)([1, 0]);
@@ -401,14 +479,7 @@ function useFilterTablePaginator({ data }) {
401
479
  filters && Object.entries(filters).forEach(([key, value]) => {
402
480
  constraints.push((0, firestore_1.where)(key, "==", value));
403
481
  });
404
- if (queryData.orderBy) {
405
- if (queryData.orderBy === "documentId") {
406
- constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
407
- }
408
- else {
409
- constraints.push((0, firestore_1.orderBy)(queryData.orderBy));
410
- }
411
- }
482
+ constraints.push((0, firestore_1.orderBy)(queryData.orderBy ? queryData.orderBy === "documentId" ? (0, firestore_1.documentId)() : queryData.orderBy : (0, firestore_1.documentId)()));
412
483
  if (page[0] > page[1] && !cursorDirection) { // Going up
413
484
  currentQueryAnchor.endKey && constraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
414
485
  constraints.push((0, firestore_1.limit)(10));
@@ -505,6 +576,7 @@ function useFilterTablePaginator({ data }) {
505
576
  const listenForUpdates = () => {
506
577
  if (!Object.keys(itemList).length)
507
578
  return;
579
+ console.log("Fetching filter table updates");
508
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)));
509
581
  const itemUpdateSnapshot = (0, firestore_1.onSnapshot)(itemListUpdateQuery, (querySnapshot) => {
510
582
  querySnapshot.docs.forEach((doc) => {
@@ -535,9 +607,8 @@ function useFilterTablePaginator({ data }) {
535
607
  getDataFromQuery();
536
608
  dataListenerUnsubscribe && dataListenerUnsubscribe();
537
609
  }, [page]);
538
- return ({ ...{ tableData, setPage, setFilters } });
610
+ return ({ ...{ tableData, setPage, setFilters, page } });
539
611
  }
540
- exports.useFilterTablePaginator = useFilterTablePaginator;
541
612
  function usePlacementListingPaginator({ data, user }) {
542
613
  const [tableData, setTableData] = (0, react_1.useState)({});
543
614
  const [page, setPage] = (0, react_1.useState)([1, 0]);
@@ -553,16 +624,23 @@ function usePlacementListingPaginator({ data, user }) {
553
624
  else {
554
625
  cursorPos = currentQueryAnchor.startQueryPos;
555
626
  }
556
- const querySchema = data[cursorPos];
627
+ const querySchema = data === null || data === void 0 ? void 0 : data[cursorPos];
557
628
  const createQuery = (queryData) => {
558
- const constraints = [];
559
- queryData.where && queryData.where.forEach((w) => {
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) => {
560
638
  constraints.push((0, firestore_1.where)(...w));
561
639
  });
562
640
  filters && Object.entries(filters).forEach(([key, value]) => {
563
641
  constraints.push((0, firestore_1.where)(key, "==", value));
564
642
  });
565
- if (queryData.orderBy) {
643
+ if (queryData === null || queryData === void 0 ? void 0 : queryData.orderBy) {
566
644
  if (queryData.orderBy === "documentId") {
567
645
  constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
568
646
  }
@@ -570,6 +648,9 @@ function usePlacementListingPaginator({ data, user }) {
570
648
  constraints.push((0, firestore_1.orderBy)(queryData.orderBy));
571
649
  }
572
650
  }
651
+ else {
652
+ constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
653
+ }
573
654
  if (page[0] > page[1] && !cursorDirection) { // Going up
574
655
  currentQueryAnchor.endKey && constraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
575
656
  constraints.push((0, firestore_1.limit)(10));
@@ -601,7 +682,7 @@ function usePlacementListingPaginator({ data, user }) {
601
682
  };
602
683
  const constraints = createQuery(querySchema);
603
684
  // console.log(queryId, "constraints", constraints)
604
- const q = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, ...querySchema.path), ...(constraints));
685
+ const q = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, "savedPlacements"), ...(constraints));
605
686
  const queryResults = {};
606
687
  const queryData = await (0, firestore_1.getDocs)(q);
607
688
  // console.log("queryData.size", queryData.size)
@@ -665,17 +746,19 @@ function usePlacementListingPaginator({ data, user }) {
665
746
  }
666
747
  setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
667
748
  const finalData = Object.fromEntries(await Promise.all(Object.entries(itemList).map(async ([k, v]) => {
668
- const address = await firebaseQuery.getDocData(["addresses", v.addressId || ""]);
669
- const provider = await firebaseQuery.getDocData(["providers", v.providerId || ""]);
670
- const status = getPlacementStatus(v);
671
- return [k, { ...address, ...provider, ...v, email: v.providerEmail, status: status }];
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 }];
672
754
  })));
673
755
  setTableData(finalData);
674
756
  };
675
- const getPlacementStatus = (placement) => {
676
- var _a;
677
- if (placement.mapConsent === true)
678
- return "public";
757
+ const getPlacementStatus = (provider, placement, savedPlacement) => {
758
+ if (savedPlacement.status === "Blocked")
759
+ return "blocked";
760
+ if (savedPlacement.status === "Accepted")
761
+ return "active";
679
762
  if (user.product === "admin") {
680
763
  if (placement.mapConsent === undefined)
681
764
  return "notReviewed";
@@ -684,13 +767,7 @@ function usePlacementListingPaginator({ data, user }) {
684
767
  return "unknown";
685
768
  }
686
769
  ;
687
- if (!((_a = placement === null || placement === void 0 ? void 0 : placement.savedBy) === null || _a === void 0 ? void 0 : _a[user.oId].providerConsent))
688
- return "awaitingProviderApproval";
689
- if (placement.savedBy[user.oId].status === "approved")
690
- return "active";
691
- if (placement.savedBy[user.oId].status === "blocked")
692
- return "blocked";
693
- if (placement.savedBy[user.oId].status === "rejected")
770
+ if (!provider.insurance)
694
771
  return "awaitingProviderApproval";
695
772
  return "requiresApproval";
696
773
  };
@@ -706,10 +783,9 @@ function usePlacementListingPaginator({ data, user }) {
706
783
  (0, react_1.useEffect)(() => {
707
784
  getDataFromQuery();
708
785
  }, [page]);
709
- return ({ ...{ tableData, setPage, setFilters } });
786
+ return ({ ...{ tableData, setPage, setFilters, page } });
710
787
  }
711
- exports.usePlacementListingPaginator = usePlacementListingPaginator;
712
- function useCohortUserPaginator({ user, cohort, data }) {
788
+ function useCohortUserPaginator({ user, cohort, data, search, userType }) {
713
789
  const [tableData, setTableData] = (0, react_1.useState)({});
714
790
  const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
715
791
  const [filters, setFilters] = (0, react_1.useState)();
@@ -717,13 +793,19 @@ function useCohortUserPaginator({ user, cohort, data }) {
717
793
  const [page, setPage] = (0, react_1.useState)([1, 0]);
718
794
  const [dataListenerUnsubscribe, setDataListenerUnsubscribe] = (0, react_1.useState)();
719
795
  const [queries, setQueries] = (0, react_1.useState)();
796
+ const [prevSearch, setPrevSearch] = (0, react_1.useState)();
797
+ const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
798
+ const usersIndex = algoliaClient.initIndex("users");
720
799
  (0, react_1.useEffect)(() => {
721
800
  var _a;
722
801
  if (user.userType !== "Staff") {
802
+ console.log("Not a staff member", user);
723
803
  setQueries(undefined);
724
804
  return;
725
805
  }
726
- if ((!user.viewCohorts && user.userGroup !== "admin") || user.viewCohorts === "none" || (user.viewCohorts === "some" && cohort !== "all" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.split(",").includes(cohort || "")))) {
806
+ 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 || "")))) {
727
809
  setQueries(undefined);
728
810
  return;
729
811
  }
@@ -754,9 +836,6 @@ function useCohortUserPaginator({ user, cohort, data }) {
754
836
  return finalConstraints;
755
837
  });
756
838
  }, []);
757
- (0, react_1.useEffect)(() => {
758
- console.log("TD", tableData);
759
- }, [tableData]);
760
839
  const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
761
840
  if (!queries) {
762
841
  setTableData({});
@@ -776,7 +855,12 @@ function useCohortUserPaginator({ user, cohort, data }) {
776
855
  console.log("mConstraints", mConstraints);
777
856
  const fConstraints = [...mConstraints];
778
857
  filters && Object.entries(filters).forEach(([key, value]) => {
779
- fConstraints.push((0, firestore_1.where)(key, "==", value));
858
+ if (typeof value === "object") {
859
+ fConstraints.push(value);
860
+ }
861
+ else {
862
+ fConstraints.push((0, firestore_1.where)(key, "==", value));
863
+ }
780
864
  });
781
865
  fConstraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
782
866
  if (page[0] > page[1] && !cursorDirection) { // Going up
@@ -871,6 +955,7 @@ function useCohortUserPaginator({ user, cohort, data }) {
871
955
  const listenForUpdates = () => {
872
956
  if (!Object.keys(itemList).length)
873
957
  return;
958
+ console.log("Fetching cohort user data");
874
959
  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)));
875
960
  const itemUpdateSnapshot = (0, firestore_1.onSnapshot)(itemListUpdateQuery, (querySnapshot) => {
876
961
  querySnapshot.docs.forEach((doc) => {
@@ -886,23 +971,57 @@ function useCohortUserPaginator({ user, cohort, data }) {
886
971
  setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
887
972
  setTableData(itemList);
888
973
  };
974
+ const searchUsers = async () => {
975
+ if (!search)
976
+ return;
977
+ let userSearchString = `userType:${userType} AND oId:${user.oId} AND product:${user.product}`;
978
+ if (cohort && cohort !== "all") {
979
+ userSearchString = userSearchString + ` AND cohort:${cohort}`;
980
+ }
981
+ Object.entries(filters || {}).forEach(([field, value]) => {
982
+ userSearchString = userSearchString + ` AND ${field}:${value}`;
983
+ });
984
+ console.log("Going", page[0] > page[1] ? "up" : "down");
985
+ console.log("Start at", page[0] > page[1] ? page[1] : page[1] - 1);
986
+ const searchStudentHits = await usersIndex.search(search, {
987
+ filters: userSearchString,
988
+ length: 10,
989
+ offset: 10 * (page[0] > page[1] ? page[1] : (page[0] - 1))
990
+ });
991
+ const results = Object.fromEntries(await Promise.all(searchStudentHits.hits.map(async (hit) => {
992
+ return [hit.objectID, hit];
993
+ })));
994
+ setPrevSearch(search);
995
+ setTableData(results);
996
+ };
889
997
  (0, react_1.useEffect)(() => {
890
- if (!filters)
998
+ if (!filters && !prevSearch && !search)
999
+ return;
1000
+ if (search && prevSearch) {
1001
+ console.log("New search");
1002
+ setPrevSearch(search);
891
1003
  return;
1004
+ }
1005
+ console.log("Setting page");
892
1006
  setPage([1, 0]);
1007
+ console.log("Set page");
893
1008
  setTableData({});
894
1009
  setQueryAnchor({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
895
1010
  setPrevEntryIds({});
896
1011
  dataListenerUnsubscribe && dataListenerUnsubscribe();
897
- }, [filters]);
1012
+ }, [filters, search]);
898
1013
  // Fetch new data when queries or page change
899
1014
  (0, react_1.useEffect)(() => {
1015
+ if (search) {
1016
+ console.log("PAGE", page);
1017
+ searchUsers();
1018
+ return;
1019
+ }
900
1020
  getDataFromQuery();
901
1021
  dataListenerUnsubscribe && dataListenerUnsubscribe();
902
- }, [page]);
903
- return ({ ...{ tableData, setPage, setFilters } });
1022
+ }, [page, queries, prevSearch]);
1023
+ return ({ ...{ tableData, setPage, page, setFilters } });
904
1024
  }
905
- exports.useCohortUserPaginator = useCohortUserPaginator;
906
1025
  function useAdmissionsPaginator({ data }) {
907
1026
  const [tableData, setTableData] = (0, react_1.useState)({});
908
1027
  const [page, setPage] = (0, react_1.useState)([1, 0]);
@@ -916,8 +1035,7 @@ function useAdmissionsPaginator({ data }) {
916
1035
  }, [filters]);
917
1036
  return ({ ...{ tableData, setPage, setFilters } });
918
1037
  }
919
- exports.useAdmissionsPaginator = useAdmissionsPaginator;
920
- function useLazyLoadQueryList({ path, constraints, number }) {
1038
+ function useLazyLoadQueryList({ path, constraints, number, onItemFetched }) {
921
1039
  const [items, setItems] = (0, react_1.useState)({});
922
1040
  const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(false);
923
1041
  const [lastItem, setLastItem] = (0, react_1.useState)();
@@ -927,6 +1045,7 @@ function useLazyLoadQueryList({ path, constraints, number }) {
927
1045
  setLastItem(undefined);
928
1046
  };
929
1047
  const loadMore = async () => {
1048
+ var _a;
930
1049
  setLoadMoreIcon(true);
931
1050
  let formattedConstraints = [];
932
1051
  if (constraints) {
@@ -936,8 +1055,73 @@ function useLazyLoadQueryList({ path, constraints, number }) {
936
1055
  formattedConstraints.push((0, firestore_1.startAfter)(lastItem));
937
1056
  }
938
1057
  const documents = await firebaseQuery.getDocsWhere(path, formattedConstraints, true);
1058
+ console.log("docs", documents);
1059
+ if (documents === null || documents === void 0 ? void 0 : documents.empty) {
1060
+ setLoadMoreIcon(false);
1061
+ return;
1062
+ }
1063
+ setLastItem(documents === null || documents === void 0 ? void 0 : documents.docs[((_a = documents === null || documents === void 0 ? void 0 : documents.docs) === null || _a === void 0 ? void 0 : _a.length) - 1]);
1064
+ const processedItems = Object.fromEntries(await Promise.all(Object.values((documents === null || documents === void 0 ? void 0 : documents.docs) || {}).map(async (doc) => {
1065
+ const itemObj = { ...doc.data(), id: doc.id, docPath: doc.ref };
1066
+ return [doc.id, onItemFetched ? await onItemFetched(itemObj) : itemObj];
1067
+ })));
1068
+ 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);
939
1099
  setLastItem(documents.docs[documents.docs.length - 1]);
940
- setItems((i) => ({ ...i, ...(Object.fromEntries(Object.entries(documents.docs).map(([, doc]) => [doc.id, { ...doc.data(), docPath: doc.ref }]))) }));
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 }));
941
1125
  setLoadMoreIcon(false);
942
1126
  };
943
1127
  (0, react_1.useEffect)(() => {
@@ -945,7 +1129,308 @@ function useLazyLoadQueryList({ path, constraints, number }) {
945
1129
  }, []);
946
1130
  return ({ ...{ items, loadMore, loadMoreIcon, reset } });
947
1131
  }
948
- exports.useLazyLoadQueryList = useLazyLoadQueryList;
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 });
1431
+ };
1432
+ return { successText, onFApply, progressStage, currentStageComplete, uploadedFiles, getCurrentStage, setFormComplete, viewFile, addFile, setSuccessPopup, openSuccessPopup, setFApplication, fApplication, draftSaved, profileUrl, successPopup, fApplicationId, fListing, student, fProvider };
1433
+ }
949
1434
  function useProposePlacementRenderer({ user, orgContext, placement }) {
950
1435
  const [businessSectionComplete, setBusinessSectionComplete] = (0, react_1.useState)(false);
951
1436
  const [addressSectionComplete, setAddressSectionComplete] = (0, react_1.useState)(false);
@@ -1013,41 +1498,22 @@ function useProposePlacementRenderer({ user, orgContext, placement }) {
1013
1498
  setFormData(undefined);
1014
1499
  };
1015
1500
  const proposePlacement = async (draft = false) => {
1016
- const getPlacementStatus = (startDate, endDate, today) => {
1017
- if (startDate <= today && endDate >= today)
1018
- return { active: !draft ? true : false, inProgress: true, completed: false };
1019
- else if (endDate <= today)
1020
- return { completed: true, inProgress: false, active: false };
1021
- return { completed: false, inProgress: true, active: false };
1022
- };
1023
1501
  if (!formData || !student) {
1024
1502
  throw new Error("Cannot find placement details.");
1025
1503
  }
1026
- try {
1027
- console.log("formData", formData);
1028
- const today = new Date();
1029
- const status = getPlacementStatus(new Date(formData.startDate), new Date(formData.endDate), today);
1030
- const newFormData = { ...formData, ...status };
1031
- if (newFormData.id && newFormData.uid && draft === newFormData.draft) {
1032
- await firebaseQuery.update(["placements", newFormData.id], newFormData);
1033
- }
1034
- else {
1035
- await (0, writeDatabase_1.addPlacement)(newFormData, student.id, draft);
1036
- }
1037
- if (status.active) {
1038
- return { screen: "Home" };
1039
- }
1040
- else if (status.completed) {
1041
- return { screen: "Completed" };
1042
- }
1043
- else {
1044
- return { screen: "Upcoming" };
1045
- }
1046
- }
1047
- catch (error) {
1048
- console.log("Error:", error);
1049
- throw error;
1050
- }
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) => {
1514
+ setComplete(true);
1515
+ return e;
1516
+ });
1051
1517
  };
1052
1518
  const deletePlacement = async (id) => {
1053
1519
  return await firebaseQuery.delete(["placements", id]);
@@ -1057,7 +1523,6 @@ function useProposePlacementRenderer({ user, orgContext, placement }) {
1057
1523
  setBusinessSectionComplete, setAddressSectionComplete, setPlacementSectionComplete,
1058
1524
  proposePlacement, deletePlacement, setStudent, submitSection, setFormData, resetFormData, setStage, setComplete } });
1059
1525
  }
1060
- exports.useProposePlacementRenderer = useProposePlacementRenderer;
1061
1526
  const useRefDimensions = () => {
1062
1527
  const [dimensions, setDimensions] = (0, react_1.useState)({ width: 1, height: 2 });
1063
1528
  const refCallback = (0, react_1.useCallback)((node) => {
@@ -1073,10 +1538,10 @@ const useRefDimensions = () => {
1073
1538
  return { dimensions, refCallback };
1074
1539
  };
1075
1540
  exports.useRefDimensions = useRefDimensions;
1076
- const cohortStages = ["name", "placementType", "students", "workflow", "review", "created"];
1541
+ const cohortStages = ["info", "name", "placementType", "review", "created"];
1077
1542
  const defaultCohortData = {
1078
1543
  name: "",
1079
- stage: "name",
1544
+ stage: "info",
1080
1545
  startSubmission: "",
1081
1546
  endSubmission: "",
1082
1547
  startPlacements: "",
@@ -1101,7 +1566,7 @@ function useCreateCohortRenderer({ oId, product, initialData = defaultCohortData
1101
1566
  }
1102
1567
  };
1103
1568
  (0, react_1.useEffect)(() => {
1104
- if (!Object.keys(cohortData).length || (0, util_1.objectsEqual)(initialData, cohortData))
1569
+ if (!Object.keys(cohortData).length || cohortData.stage === "name" || (0, util_1.objectsEqual)(initialData, cohortData))
1105
1570
  return;
1106
1571
  if (cohortId) {
1107
1572
  console.log("update", cohortId, cohortData);
@@ -1128,7 +1593,6 @@ function useCreateCohortRenderer({ oId, product, initialData = defaultCohortData
1128
1593
  };
1129
1594
  return ({ ...{ submitCohort, submitSection, back, setCohortData, deleteCohort, cohortData, cohortId } });
1130
1595
  }
1131
- exports.useCreateCohortRenderer = useCreateCohortRenderer;
1132
1596
  function useUserUploadHandler({ product, oId, userType, user, onComplete, userGroupId, cohortId }) {
1133
1597
  const [emptyCellsWarning, setEmptyCellsWarning] = (0, react_1.useState)(false);
1134
1598
  const [alert, setAlert] = (0, react_1.useState)();
@@ -1143,13 +1607,20 @@ function useUserUploadHandler({ product, oId, userType, user, onComplete, userGr
1143
1607
  if (!Object.entries(userData)) {
1144
1608
  return [];
1145
1609
  }
1146
- const uniqueValues = new Set(userData.map((v) => v.email));
1610
+ const emailDuplicateLookup = userData.reduce((acc, e, index) => {
1611
+ if (!e.email)
1612
+ return acc;
1613
+ acc[e.email] = [...(acc[e.email] || []), index];
1614
+ return acc;
1615
+ }, {});
1616
+ const emailDuplicates = Object.entries(emailDuplicateLookup).filter(([_, ids]) => ids.length > 1).map(([email]) => email);
1147
1617
  if (userData.filter((u) => u.email).length < userData.length) {
1148
1618
  setAlert({ msg: "Your data contains missing email addresses.", severity: "error" });
1149
1619
  return false;
1150
1620
  }
1151
- if (uniqueValues.size < userData.length) {
1152
- setAlert({ msg: "Your data contains multiple rows with the same email address. Please remove these and reupload.", severity: "error" });
1621
+ console.log("emailDuplicates", emailDuplicates);
1622
+ if (emailDuplicates.length) {
1623
+ setAlert({ msg: `Your data contains multiple rows with the same email address. Please remove these and reupload.\n\n Duplicate emails: ${emailDuplicates.join(", ")}`, severity: "error" });
1153
1624
  return false;
1154
1625
  }
1155
1626
  let emptyOptionalCell = false;
@@ -1226,11 +1697,300 @@ function useUserUploadHandler({ product, oId, userType, user, onComplete, userGr
1226
1697
  };
1227
1698
  return ({ ...{ uploadUsers, alert, onChange } });
1228
1699
  }
1229
- exports.useUserUploadHandler = useUserUploadHandler;
1230
- function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId }) {
1700
+ function useWorkflowEditor({ user, initialData, onSubmit, cohortId }) {
1701
+ const firebaseQuery = new firebaseQuery_1.default();
1702
+ const { filePopupActive, files, includedFiles, includedForms, uploadFile, fAddNode, fOnDelete, arrows, onChange, error, onMoveEnd, addEdgePoint, fWorkflowNodes, newEdgePoint, setTutorialActive, setFilePopupActive, fSubmitWorkflow, openPopup, setSnackbar, snackbar, setMousePosFunc, mousePos, tutorialActive, onDeleteArrow, containerRef, setError, setArrows, setFWorkflowNodes } = useGenericWorkflowEditor({ ...{ user, initialData }, defaultData: constants_1.defaultInstituteWorkflow, onSubmit: onSubmit });
1703
+ const addNode = ({ userType, nextStage, prevStage } = {}) => {
1704
+ return fAddNode({ userType: userType }, `${userType} review`, nextStage, prevStage);
1705
+ };
1706
+ const onDelete = (0, react_1.useCallback)(async (id) => {
1707
+ if (!id) {
1708
+ return;
1709
+ }
1710
+ return fOnDelete(id, onDeleteValidator);
1711
+ }, []);
1712
+ const onDeleteValidator = async (id) => {
1713
+ const placementsOnStage = await firebaseQuery.getCount("placements", [(0, firestore_1.where)("oId", "==", user.oId), (0, firestore_1.where)("cohort", "==", cohortId), (0, firestore_1.where)("status", "==", id)]);
1714
+ if (placementsOnStage > 0) {
1715
+ return { res: false, reason: "Cannot delete a stage with current active placements." };
1716
+ }
1717
+ if ([1, 6, 8, 11].includes(id)) {
1718
+ return { res: false, reason: "Cannot delete core workflow component." };
1719
+ }
1720
+ return { res: true };
1721
+ };
1722
+ const submitWorkflow = async (workflow, callOnComplete = true) => {
1723
+ const validatorTypeConversion = (newWorkflow, oldWorkflow) => {
1724
+ return validateWorkflow(newWorkflow, oldWorkflow);
1725
+ };
1726
+ return fSubmitWorkflow(validatorTypeConversion, workflow, callOnComplete);
1727
+ };
1728
+ const validateWorkflow = async (newWorkflow, oldWorkflow) => {
1729
+ const getStageById = (id, checkInitialData) => {
1730
+ let node;
1731
+ if (checkInitialData) {
1732
+ if (!(oldWorkflow === null || oldWorkflow === void 0 ? void 0 : oldWorkflow.length)) {
1733
+ throw new Error("Check initial data but no oldWorkflow");
1734
+ }
1735
+ node = (oldWorkflow).find((x) => x.id === id);
1736
+ }
1737
+ else {
1738
+ node = (newWorkflow).find((x) => x.id === id);
1739
+ }
1740
+ if (!node) {
1741
+ throw new Error("Error in configuration. Cannot find stage: " + id);
1742
+ }
1743
+ return node;
1744
+ };
1745
+ console.log("workflows, new, old", newWorkflow, oldWorkflow);
1746
+ // Steps:
1747
+ // Check if any deleted stages that have placements assigned to them
1748
+ const missingNodes = oldWorkflow && oldWorkflow.length ? oldWorkflow.reduce((acc, oldNode) => {
1749
+ const existsInNewWorkflow = Boolean(newWorkflow.find((newNode) => newNode.id === oldNode.id));
1750
+ if (!existsInNewWorkflow) {
1751
+ acc.push(oldNode.id);
1752
+ }
1753
+ return acc;
1754
+ }, []) : [];
1755
+ await Promise.all(missingNodes.map(async (nodeId) => {
1756
+ const placementsOnStage = await firebaseQuery.getCount("placements", [(0, firestore_1.where)("oId", "==", user.oId), (0, firestore_1.where)("cohort", "==", cohortId), (0, firestore_1.where)("status", "==", nodeId)]);
1757
+ if (placementsOnStage > 0) {
1758
+ throw new Error("Cannot delete a stage with current active placements.");
1759
+ }
1760
+ }));
1761
+ // After verifying, we have a list of stages that
1762
+ // Create paths with new workflow
1763
+ // Check cyclic
1764
+ // Check early termination
1765
+ // Check missed items
1766
+ // Create paths from old workflow
1767
+ // Check cyclic
1768
+ // Check early termination
1769
+ // Check missed items
1770
+ const paths = [];
1771
+ const createPaths = (stage, path, checkingInitialData) => {
1772
+ var _a, _b;
1773
+ if (!stage)
1774
+ return;
1775
+ const currentPath = [...(path || [])];
1776
+ // Check if infinite cycles
1777
+ if (currentPath.find((s) => s.id === stage.id)) {
1778
+ // ID must be already in it, so a loop has been formed.
1779
+ // Get just the elements in that cycle, such as [1, 2, 3, 4, 1]
1780
+ const cyclicPath = currentPath.slice(currentPath.findIndex((s) => s.id === stage.id));
1781
+ // If we have a cycle [1, 2, 3, 4, 1], check if any of them have a way out.
1782
+ const escapableCycle = cyclicPath.some((cycleStage, idx) => {
1783
+ let cyclicStageButtons = (cycleStage === null || cycleStage === void 0 ? void 0 : cycleStage.buttons) || [];
1784
+ // Keep only the arrows that don't go to the next one already documented, or the ID being checked.
1785
+ cyclicStageButtons = cyclicStageButtons.filter((button) => ![cyclicPath[idx + 1], stage.id].includes(button.id));
1786
+ return cyclicStageButtons.length > 0;
1787
+ });
1788
+ if (!escapableCycle) {
1789
+ throw new Error(`Inescapable cycle detected at ${stage.name}. Amend and resubmit.`);
1790
+ }
1791
+ return;
1792
+ }
1793
+ currentPath.push(stage);
1794
+ if (!((_a = stage === null || stage === void 0 ? void 0 : stage.buttons) === null || _a === void 0 ? void 0 : _a.length)) {
1795
+ console.log("Final path", currentPath, checkingInitialData);
1796
+ // Check if in order and ends in 11
1797
+ if (stage.id !== 11) {
1798
+ throw new Error("All paths must end with the 'Workflow end' stage. Amend and reupload.");
1799
+ }
1800
+ //Check if a valid path.
1801
+ const validEssentialRoute = [1, "Provider", 6, 8, 11];
1802
+ const currentEssentialRoute = currentPath.reduce((acc, node) => {
1803
+ if ([1, 6, 8, 11].includes(node.id)) {
1804
+ acc.push(node.id);
1805
+ return acc;
1806
+ }
1807
+ if ((node === null || node === void 0 ? void 0 : node.userType) !== "Provider")
1808
+ return acc;
1809
+ if (!acc.includes(6) && !acc.includes("Provider")) {
1810
+ acc.push("Provider");
1811
+ }
1812
+ return acc;
1813
+ }, []);
1814
+ const valid = (0, util_1.arraysEqual)(currentEssentialRoute, validEssentialRoute);
1815
+ if (!valid) {
1816
+ console.log("Error in", currentEssentialRoute);
1817
+ throw new Error(checkingInitialData ?
1818
+ "Cannot submit. Changing workflow will cause current placements to skip core workflow components." :
1819
+ "Missing core workflow components. Ensure all placements have a provider review, start, end and workflow end in that order.");
1820
+ }
1821
+ paths.push(currentPath);
1822
+ return;
1823
+ }
1824
+ if (checkingInitialData) {
1825
+ stage.buttons.forEach((button) => {
1826
+ createPaths(getStageById(button.id, true), currentPath, checkingInitialData);
1827
+ });
1828
+ }
1829
+ const newWorkflowStage = newWorkflow.find((newStage) => newStage.id === stage.id);
1830
+ if (!newWorkflowStage) {
1831
+ if (checkingInitialData) {
1832
+ return;
1833
+ }
1834
+ throw new Error("Cannot find node: " + stage.id);
1835
+ }
1836
+ (_b = newWorkflowStage.buttons) === null || _b === void 0 ? void 0 : _b.forEach((button) => {
1837
+ console.log("stage", stage, checkingInitialData);
1838
+ createPaths(getStageById(button.id), currentPath);
1839
+ });
1840
+ };
1841
+ createPaths(getStageById(1), []);
1842
+ if (oldWorkflow && oldWorkflow.length) {
1843
+ createPaths(getStageById(1, true), [], true);
1844
+ }
1845
+ return paths.every((x) => x);
1846
+ };
1847
+ const workflowNodes = fWorkflowNodes;
1848
+ const setWorkflowNodes = setFWorkflowNodes;
1849
+ return ({ ...{ filePopupActive, files, uploadFile, addNode, onDelete, arrows, onChange, error, includedFiles, onMoveEnd, addEdgePoint, newEdgePoint, setTutorialActive, setFilePopupActive,
1850
+ includedForms, submitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
1851
+ onDeleteArrow, workflowNodes, containerRef, setError, setArrows, setWorkflowNodes } });
1852
+ }
1853
+ function useApplicantWorkflowEditor({ user, initialData, onSubmit, workflowId }) {
1854
+ const firebaseQuery = new firebaseQuery_1.default();
1855
+ const { filePopupActive, includedFiles, includedForms, uploadFile, fAddNode, fOnDelete, arrows, onChange, error, onMoveEnd, addEdgePoint, fWorkflowNodes, newEdgePoint, setTutorialActive, setFilePopupActive, fSubmitWorkflow, openPopup, setSnackbar, snackbar, setMousePosFunc, mousePos, tutorialActive, onDeleteArrow, containerRef, setError, setArrows, setFWorkflowNodes } = useGenericWorkflowEditor({ ...{ user, initialData }, defaultData: workflowId ? [] : constants_1.defaultApplicantWorkflow, onSubmit: onSubmit });
1856
+ (0, react_1.useEffect)(() => {
1857
+ if (!workflowId)
1858
+ return;
1859
+ firebaseQuery.getDocData(["applicantWorkflows", workflowId]).then((e) => setFWorkflowNodes(e.workflow));
1860
+ }, [workflowId]);
1861
+ const addNode = ({ userType, nextStage, prevStage } = {}) => {
1862
+ return fAddNode({ userType: userType, buttons: [{ name: "Reject", id: 12, required: true }] }, "", nextStage, prevStage);
1863
+ };
1864
+ const onDelete = (0, react_1.useCallback)(async (id) => {
1865
+ if (!id) {
1866
+ return;
1867
+ }
1868
+ return fOnDelete(id, onDeleteValidator);
1869
+ }, []);
1870
+ const onDeleteValidator = async (id) => {
1871
+ const placementsOnStage = await firebaseQuery.getCount("placement", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("applicantWorkflowId", "==", workflowId), (0, firestore_1.where)("applicantStage", "==", id)]);
1872
+ if (placementsOnStage > 0) {
1873
+ return { res: false, reason: "Cannot delete a stage with current applicants." };
1874
+ }
1875
+ if ([1, 11, 12].includes(id)) {
1876
+ return { res: false, reason: "Cannot delete core workflow component." };
1877
+ }
1878
+ return { res: true };
1879
+ };
1880
+ const submitWorkflow = async (workflow, callOnComplete = true) => {
1881
+ const validatorTypeConversion = (newWorkflow, oldWorkflow) => {
1882
+ return validateWorkflow(newWorkflow, oldWorkflow);
1883
+ };
1884
+ return fSubmitWorkflow(validatorTypeConversion, workflow, callOnComplete);
1885
+ };
1886
+ // Change validator
1887
+ const validateWorkflow = async (newWorkflow, oldWorkflow) => {
1888
+ const getStageById = (id, checkInitialData) => {
1889
+ let node;
1890
+ if (checkInitialData) {
1891
+ if (!(oldWorkflow === null || oldWorkflow === void 0 ? void 0 : oldWorkflow.length)) {
1892
+ throw new Error("Check initial data but no oldWorkflow");
1893
+ }
1894
+ node = (oldWorkflow).find((x) => x.id === id);
1895
+ }
1896
+ else {
1897
+ node = (newWorkflow).find((x) => x.id === id);
1898
+ }
1899
+ if (!node) {
1900
+ throw new Error("Error in configuration. Cannot find stage: " + id);
1901
+ }
1902
+ return node;
1903
+ };
1904
+ console.log("workflows, new, old", newWorkflow, oldWorkflow);
1905
+ // Steps:
1906
+ // Check if any deleted stages that have placements assigned to them
1907
+ const missingNodes = oldWorkflow && oldWorkflow.length ? oldWorkflow.reduce((acc, oldNode) => {
1908
+ const existsInNewWorkflow = Boolean(newWorkflow.find((newNode) => newNode.id === oldNode.id));
1909
+ if (!existsInNewWorkflow) {
1910
+ acc.push(oldNode.id);
1911
+ }
1912
+ return acc;
1913
+ }, []) : [];
1914
+ await Promise.all(missingNodes.map(async (nodeId) => {
1915
+ const placementsOnStage = await firebaseQuery.getCount("placement", [(0, firestore_1.where)("providerId", "==", user.oId), (0, firestore_1.where)("applicantWorkflow", "==", workflowId), (0, firestore_1.where)("applicantStage", "==", nodeId)]);
1916
+ if (placementsOnStage > 0) {
1917
+ throw new Error("Cannot delete a stage with current active placements.");
1918
+ }
1919
+ }));
1920
+ // After verifying, we have a list of stages that
1921
+ // Create paths with new workflow
1922
+ // Check cyclic
1923
+ // Check early termination
1924
+ // Check missed items
1925
+ // Create paths from old workflow
1926
+ // Check cyclic
1927
+ // Check early termination
1928
+ // Check missed items
1929
+ const paths = [];
1930
+ const createPaths = (stage, path, checkingInitialData) => {
1931
+ var _a, _b;
1932
+ if (!stage)
1933
+ return;
1934
+ const currentPath = [...(path || [])];
1935
+ // Check if infinite cycles
1936
+ if (currentPath.find((s) => s.id === stage.id)) {
1937
+ // ID must be already in it, so a loop has been formed.
1938
+ // Get just the elements in that cycle, such as [1, 2, 3, 4, 1]
1939
+ const cyclicPath = currentPath.slice(currentPath.findIndex((s) => s.id === stage.id));
1940
+ // If we have a cycle [1, 2, 3, 4, 1], check if any of them have a way out.
1941
+ const escapableCycle = cyclicPath.some((cycleStage, idx) => {
1942
+ let cyclicStageButtons = (cycleStage === null || cycleStage === void 0 ? void 0 : cycleStage.buttons) || [];
1943
+ // Keep only the arrows that don't go to the next one already documented, or the ID being checked.
1944
+ cyclicStageButtons = cyclicStageButtons.filter((button) => ![cyclicPath[idx + 1], stage.id].includes(button.id));
1945
+ return cyclicStageButtons.length > 0;
1946
+ });
1947
+ if (!escapableCycle) {
1948
+ throw new Error(`Inescapable cycle detected at ${stage.name}. Amend and resubmit.`);
1949
+ }
1950
+ return;
1951
+ }
1952
+ currentPath.push(stage);
1953
+ if (!((_a = stage === null || stage === void 0 ? void 0 : stage.buttons) === null || _a === void 0 ? void 0 : _a.length)) {
1954
+ console.log("Final path", currentPath, checkingInitialData);
1955
+ // Check if in order and ends in 11
1956
+ if (![11, 12].includes(stage.id)) {
1957
+ throw new Error("All paths must end with the 'Success' or 'Reject' stage. Amend and reupload.");
1958
+ }
1959
+ paths.push(currentPath);
1960
+ return;
1961
+ }
1962
+ if (checkingInitialData) {
1963
+ stage.buttons.forEach((button) => {
1964
+ createPaths(getStageById(button.id, true), currentPath, checkingInitialData);
1965
+ });
1966
+ }
1967
+ const newWorkflowStage = newWorkflow.find((newStage) => newStage.id === stage.id);
1968
+ if (!newWorkflowStage) {
1969
+ if (checkingInitialData) {
1970
+ return;
1971
+ }
1972
+ throw new Error("Cannot find node: " + stage.id);
1973
+ }
1974
+ (_b = newWorkflowStage.buttons) === null || _b === void 0 ? void 0 : _b.forEach((button) => {
1975
+ createPaths(getStageById(button.id), currentPath);
1976
+ });
1977
+ };
1978
+ createPaths(getStageById(1), []);
1979
+ if (oldWorkflow && oldWorkflow.length) {
1980
+ createPaths(getStageById(1, true), [], true);
1981
+ }
1982
+ return paths.every((x) => x);
1983
+ };
1984
+ const workflowNodes = fWorkflowNodes;
1985
+ const setWorkflowNodes = setFWorkflowNodes;
1986
+ return ({ ...{ filePopupActive, uploadFile, addNode, onDelete, arrows, onChange, error, includedFiles, onMoveEnd, addEdgePoint, newEdgePoint, setTutorialActive, setFilePopupActive,
1987
+ includedForms, submitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
1988
+ onDeleteArrow, workflowNodes, containerRef, setError, setArrows, setWorkflowNodes } });
1989
+ }
1990
+ function useGenericWorkflowEditor({ user, initialData, defaultData, onSubmit }) {
1231
1991
  const [filePopupActive, setFilePopupActive] = (0, react_1.useState)(false);
1232
1992
  const [files, setFiles] = (0, react_1.useState)([]);
1233
- const [workflowNodes, setWorkflowNodes] = (0, react_1.useState)([]); // Workflow data
1993
+ const [fWorkflowNodes, setFWorkflowNodes] = (0, react_1.useState)([]); // Workflow data
1234
1994
  const [arrows, setArrows] = (0, react_1.useState)([]);
1235
1995
  const [includedForms, setIncludedForms] = (0, react_1.useState)([]);
1236
1996
  const [includedFiles, setIncludedFiles] = (0, react_1.useState)([]);
@@ -1241,19 +2001,16 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1241
2001
  const [tutorialActive, setTutorialActive] = (0, react_1.useState)(false);
1242
2002
  const containerRef = (0, react_1.useRef)(null);
1243
2003
  let ticking = false;
1244
- const userProduct = user.product === "admin" ? product : user.product;
1245
- const userOId = user.product === "admin" ? oId : user.oId;
1246
2004
  (0, react_1.useEffect)(() => {
1247
2005
  let newData = initialData;
1248
2006
  if (initialData === undefined || initialData.length === 0) {
1249
- newData = constants_1.defaultInstituteWorkflow;
2007
+ newData = defaultData;
1250
2008
  setTimeout(() => {
1251
2009
  setTutorialActive(false);
1252
2010
  }, 1000);
1253
2011
  }
1254
- (0, readDatabase_1.getFiles)(`${userProduct}/${userOId}/files`).then(setFiles);
1255
- setWorkflowNodes(newData);
1256
- console.log("new data", newData);
2012
+ (0, readDatabase_1.getFiles)(`${user.product}/${user.oId}/files`).then(setFiles);
2013
+ setFWorkflowNodes(newData || []);
1257
2014
  }, [initialData]);
1258
2015
  (0, react_1.useEffect)(() => {
1259
2016
  // New edge points
@@ -1283,38 +2040,76 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1283
2040
  ticking = true;
1284
2041
  };
1285
2042
  const uploadFile = async (files) => {
1286
- const res = await (0, writeDatabase_1.uploadFiles)(files, `${userProduct}/${userOId}/files/`);
2043
+ const res = await (0, writeDatabase_1.uploadFiles)(files, `${user.product}/${user.oId}/files/`);
1287
2044
  if (res === 1) {
1288
2045
  // change this
1289
2046
  setFilePopupActive(false);
1290
- (0, readDatabase_1.getFiles)(`${userProduct}/${userOId}/files`).then(setFiles);
2047
+ (0, readDatabase_1.getFiles)(`${user.product}/${user.oId}/files`).then(setFiles);
1291
2048
  }
1292
2049
  };
1293
- const addNode = () => {
1294
- setWorkflowNodes((p) => ([...p, { id: (0, util_1.getUniqueId)(p), name: "", pos: { x: 10, y: 10 }, buttons: [] }]));
2050
+ const fAddNode = (additionalData, name, nextStage, prevStage) => {
2051
+ let nodeId = (0, util_1.getUniqueId)(fWorkflowNodes);
2052
+ // Add user type and name for institute placement workflow!
2053
+ setFWorkflowNodes((p) => {
2054
+ let newWorkflowNodes = ([...p, { id: nodeId, pos: { x: 10, y: 10 }, ...additionalData, name: name || `Stage ${nodeId}`, buttons: nextStage ? [...((additionalData === null || additionalData === void 0 ? void 0 : additionalData.buttons) || []), { required: true, name: "Accept", id: nextStage }] : [] }]);
2055
+ if (prevStage) {
2056
+ const prevStageIndex = p.findIndex((node) => node.id === prevStage);
2057
+ newWorkflowNodes[prevStageIndex] = { ...newWorkflowNodes[prevStageIndex], buttons: [{ required: true, name: "Accept", id: nodeId }] };
2058
+ }
2059
+ return newWorkflowNodes;
2060
+ });
2061
+ return nodeId;
1295
2062
  };
1296
- const onDelete = (0, react_1.useCallback)((id) => {
1297
- if ([1, 6, 8, 11].includes(id)) {
1298
- alert("Cannot delete core workflow component.");
2063
+ const fOnDelete = (0, react_1.useCallback)(async (id, validation) => {
2064
+ if (!id) {
1299
2065
  return;
1300
2066
  }
1301
- setWorkflowNodes((prev) => {
2067
+ if (validation) {
2068
+ // Add placementsOnStage to validation
2069
+ const validationResult = await validation(id);
2070
+ if (!validationResult.res) {
2071
+ setError(validationResult.reason);
2072
+ return;
2073
+ }
2074
+ }
2075
+ setFWorkflowNodes((prev) => {
2076
+ var _a;
1302
2077
  let oldMutable = [...prev];
2078
+ // Buttons going from deleted nodes to new node
2079
+ const deletedNodeButtons = (_a = oldMutable.find((node) => node.id === id)) === null || _a === void 0 ? void 0 : _a.buttons;
1303
2080
  oldMutable = oldMutable.filter((e) => e.id.toString() !== id.toString());
1304
- oldMutable.forEach((e, i) => {
1305
- const node = e;
2081
+ const arrowsToDeletedNode = oldMutable.map((e, i) => {
2082
+ var _a;
2083
+ const node = { ...e };
1306
2084
  // Remove any arrows to this node
1307
- node.buttons = (node.buttons && node.buttons.filter((button) => id.toString() !== button.id.toString())) || [];
2085
+ const nodeFeedsToDeleted = Boolean((_a = node.buttons) === null || _a === void 0 ? void 0 : _a.find((destination) => destination.id === id));
2086
+ if (nodeFeedsToDeleted) {
2087
+ console.log("Delete line from node", node.id);
2088
+ }
2089
+ if (nodeFeedsToDeleted) {
2090
+ const newNodeButtons = node.buttons ? node.buttons.filter((button) => id.toString() !== button.id.toString()) : [];
2091
+ node.buttons = newNodeButtons;
2092
+ console.log("New node buttons", newNodeButtons, node.buttons);
2093
+ }
1308
2094
  oldMutable[i] = node;
1309
- });
1310
- return (oldMutable);
2095
+ return nodeFeedsToDeleted ? node : undefined;
2096
+ }).filter((i) => i);
2097
+ console.log("arrows to and destinations", arrowsToDeletedNode, deletedNodeButtons);
2098
+ if ((deletedNodeButtons === null || deletedNodeButtons === void 0 ? void 0 : deletedNodeButtons.length) && arrowsToDeletedNode.length) {
2099
+ const newNodeDestination = deletedNodeButtons[0].id;
2100
+ const newNodeOrigin = arrowsToDeletedNode[0].id;
2101
+ const indexOfOrigin = oldMutable.findIndex((node) => node.id === newNodeOrigin);
2102
+ oldMutable[indexOfOrigin] = { ...oldMutable[indexOfOrigin], buttons: [...(oldMutable[indexOfOrigin].buttons || []), { id: newNodeDestination, required: true, name: "Submit" }] };
2103
+ }
2104
+ return oldMutable;
1311
2105
  });
1312
2106
  }, []);
1313
2107
  (0, react_1.useEffect)(() => {
1314
- if (!workflowNodes) {
2108
+ error && setError(undefined);
2109
+ if (!fWorkflowNodes) {
1315
2110
  return;
1316
2111
  }
1317
- setArrows(workflowNodes.reduce((acc, node) => {
2112
+ setArrows(fWorkflowNodes.reduce((acc, node) => {
1318
2113
  if (node.buttons) {
1319
2114
  node.buttons.forEach((buttonData) => {
1320
2115
  acc.push({
@@ -1327,112 +2122,69 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1327
2122
  }
1328
2123
  return acc;
1329
2124
  }, []));
1330
- }, [workflowNodes]);
2125
+ const mIncludedFiles = fWorkflowNodes.reduce((acc, node) => {
2126
+ if (node.files) {
2127
+ acc.push(...node.files.map((f) => {
2128
+ try {
2129
+ return JSON.parse(f).name;
2130
+ }
2131
+ catch {
2132
+ return f;
2133
+ }
2134
+ }));
2135
+ }
2136
+ return (0, util_1.arrayUniqueValues)(acc);
2137
+ }, []);
2138
+ const mIncludedForms = fWorkflowNodes.reduce((acc, node) => {
2139
+ if (node.forms) {
2140
+ acc.push(...node.forms);
2141
+ }
2142
+ return (0, util_1.arrayUniqueValues)(acc);
2143
+ }, []);
2144
+ setIncludedFiles(mIncludedFiles);
2145
+ setIncludedForms(mIncludedForms);
2146
+ }, [fWorkflowNodes]);
1331
2147
  const onChange = (0, react_1.useCallback)((path, value) => {
1332
- setWorkflowNodes((prev) => (0, util_1.editNestedObject)(path, prev, value, true));
2148
+ setFWorkflowNodes((prev) => (0, util_1.editNestedObject)(path, prev, value, true));
1333
2149
  }, []);
1334
- const submitWorkflow = async () => {
2150
+ const fSubmitWorkflow = async (validation, workflow, callOnComplete = true) => {
1335
2151
  setError(undefined);
1336
- setIncludedFiles(() => workflowNodes.reduce((acc, node) => {
2152
+ const fWorkflow = workflow || fWorkflowNodes;
2153
+ const mIncludedFiles = fWorkflow.reduce((acc, node) => {
1337
2154
  if (node.files) {
1338
- acc.push(...node.files.map((f) => JSON.parse(f).name));
2155
+ acc.push(...node.files.map((f) => {
2156
+ try {
2157
+ return JSON.parse(f).name;
2158
+ }
2159
+ catch {
2160
+ return f;
2161
+ }
2162
+ }));
1339
2163
  }
1340
2164
  return (0, util_1.arrayUniqueValues)(acc);
1341
- }, []));
1342
- setIncludedForms(workflowNodes.reduce((acc, node) => {
2165
+ }, []);
2166
+ const mIncludedForms = fWorkflow.reduce((acc, node) => {
1343
2167
  if (node.forms) {
1344
2168
  acc.push(...node.forms);
1345
2169
  }
1346
2170
  return (0, util_1.arrayUniqueValues)(acc);
1347
- }, []));
1348
- if (!workflowConseq()) {
1349
- setError("Your workflow is not continuous, check the onAccept and onReject values.");
1350
- return;
1351
- }
1352
- if (initialData === workflowNodes) {
1353
- onSubmit({ workflow: workflowNodes, includedFiles: includedFiles, includedForms: includedForms });
2171
+ }, []);
2172
+ setIncludedFiles(mIncludedFiles);
2173
+ setIncludedForms(mIncludedForms);
2174
+ if (initialData === fWorkflow) {
2175
+ return callOnComplete && onSubmit({ workflow: fWorkflow, includedFiles: mIncludedFiles, includedForms: mIncludedForms });
2176
+ }
2177
+ const validatedWorkflow = await validation(fWorkflow, initialData).catch((e) => {
2178
+ setError(e.message);
2179
+ throw e;
2180
+ });
2181
+ if (!validatedWorkflow) {
1354
2182
  return;
1355
2183
  }
1356
- if (initialData) {
1357
- const restrictedChanges = workflowNodes.map((currentNode) => {
1358
- var _a, _b;
1359
- const editedNode = initialData[currentNode.id];
1360
- const editedButtons = ((_a = editedNode === null || editedNode === void 0 ? void 0 : editedNode.buttons) === null || _a === void 0 ? void 0 : _a.map((x) => x.id)) || [];
1361
- const currentButtons = ((_b = currentNode === null || currentNode === void 0 ? void 0 : currentNode.buttons) === null || _b === void 0 ? void 0 : _b.map((x) => x.id)) || [];
1362
- // Should check IDs rather than the entire thing.
1363
- if (!editedButtons.every((v, i) => v === currentButtons[i])) {
1364
- return true;
1365
- }
1366
- return false;
1367
- });
1368
- if (restrictedChanges.includes(true) && Boolean(cohortId)) {
1369
- const conflicts = await (0, readDatabase_1.checkPlacementConflicts)(userOId, cohortId);
1370
- if (conflicts > 0) {
1371
- setError(`${conflicts} active placements are using this workflow. Complete these placements to edit workflow.`);
1372
- return;
1373
- }
1374
- }
1375
- }
1376
- onSubmit({ workflow: workflowNodes, includedFiles: includedFiles, includedForms: includedForms });
1377
- };
1378
- const workflowConseq = () => {
1379
- const getStageById = (id) => workflowNodes.find((x) => x.id === id);
1380
- const paths = [];
1381
- const createPaths = (stage, path) => {
1382
- var _a;
1383
- if (!stage)
1384
- return;
1385
- const currentPath = [...(path || [])];
1386
- // Check if infinite cycles
1387
- if (currentPath.includes(stage.id)) {
1388
- // ID must be already in it, so a loop has been formed.
1389
- // Get subpath from id onwards, inclusive.
1390
- // Check if there are other ways out. If not, return false!
1391
- const cyclicPath = currentPath.slice(currentPath.indexOf(stage.id));
1392
- const escapableCycle = cyclicPath.some((id, idx) => {
1393
- var _a;
1394
- let cyclicStageButtons = ((_a = getStageById(id)) === null || _a === void 0 ? void 0 : _a.buttons) || [];
1395
- cyclicStageButtons = cyclicStageButtons.filter((button) => ![cyclicPath[idx + 1], stage.id].includes(button.id));
1396
- return cyclicStageButtons.length > 0;
1397
- });
1398
- if (!escapableCycle) {
1399
- paths.push(false);
1400
- }
1401
- return;
1402
- }
1403
- currentPath.push(stage.id);
1404
- if (!((_a = stage === null || stage === void 0 ? void 0 : stage.buttons) === null || _a === void 0 ? void 0 : _a.length)) {
1405
- // Check if in order and ends in 11
1406
- if (stage.id !== 11) {
1407
- paths.push(false);
1408
- return;
1409
- }
1410
- const validEssentialRoute = [1, "Provider", 6, 8, 11];
1411
- const currentEssentialRoute = currentPath.reduce((acc, x) => {
1412
- if ([1, 6, 8, 11].includes(x)) {
1413
- acc.push(x);
1414
- }
1415
- const node = getStageById(x);
1416
- if ((node === null || node === void 0 ? void 0 : node.userType) !== "Provider")
1417
- return acc;
1418
- if (!acc.includes(6) && !acc.includes("Provider")) {
1419
- acc.push("Provider");
1420
- }
1421
- return acc;
1422
- }, []);
1423
- const valid = (0, util_1.arraysEqual)(currentEssentialRoute, validEssentialRoute);
1424
- paths.push(valid ? currentPath : false);
1425
- return;
1426
- }
1427
- stage.buttons.forEach((button) => {
1428
- createPaths(getStageById(button.id), currentPath);
1429
- });
1430
- };
1431
- createPaths(getStageById(1));
1432
- return paths.every((x) => x);
2184
+ return callOnComplete && onSubmit({ workflow: fWorkflow, includedFiles: includedFiles, includedForms: includedForms });
1433
2185
  };
1434
2186
  const deleteEdge = (0, react_1.useCallback)((originId, nodeId) => {
1435
- setWorkflowNodes((prev) => (0, util_1.editNestedObject)([originId, "buttons", nodeId], prev, undefined, true));
2187
+ setFWorkflowNodes((prev) => (0, util_1.editNestedObject)([originId, "buttons", nodeId], prev, undefined, true));
1436
2188
  }, []);
1437
2189
  const onDeleteArrow = (0, react_1.useCallback)((data) => {
1438
2190
  deleteEdge(data.start, data.end);
@@ -1446,7 +2198,7 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1446
2198
  const y = containerRef.current.scrollTop + clientRect.y - top - 26;
1447
2199
  // Make sure x and y are not negative. Contrains canvas by top and left
1448
2200
  const pos = { x: x >= 0 ? x : 0, y: y >= 0 ? y : 0 };
1449
- setWorkflowNodes((p) => (0, util_1.editNestedObject)([id, "pos"], p, pos));
2201
+ setFWorkflowNodes((p) => (0, util_1.editNestedObject)([id, "pos"], p, pos));
1450
2202
  }, []);
1451
2203
  const addEdgePoint = (0, react_1.useCallback)((id, existingEdgePoint) => {
1452
2204
  var _a;
@@ -1455,7 +2207,7 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1455
2207
  setSnackbar("Cannot connect stages after workflow end.");
1456
2208
  return;
1457
2209
  }
1458
- const buttons = (_a = workflowNodes.find((node) => node.id === id)) === null || _a === void 0 ? void 0 : _a.buttons;
2210
+ const buttons = (_a = fWorkflowNodes.find((node) => node.id === id)) === null || _a === void 0 ? void 0 : _a.buttons;
1459
2211
  if ([1, 11].includes(id) && buttons && buttons.length > 0) {
1460
2212
  setSnackbar("Stage can only have one destination.");
1461
2213
  return;
@@ -1468,8 +2220,8 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1468
2220
  setNewEdgePoint(undefined);
1469
2221
  return;
1470
2222
  }
1471
- const originPos = workflowNodes.findIndex((x) => x.id.toString() === existingEdgePoint.toString());
1472
- const buttons = [...(workflowNodes[originPos].buttons || [])];
2223
+ const originPos = fWorkflowNodes.findIndex((x) => x.id.toString() === existingEdgePoint.toString());
2224
+ const buttons = [...(fWorkflowNodes[originPos].buttons || [])];
1473
2225
  if (!buttons.some((x) => x.id.toString() === id.toString())) {
1474
2226
  buttons.push({
1475
2227
  id: id,
@@ -1477,17 +2229,16 @@ function useWorkflowEditor({ user, initialData, onSubmit, cohortId, product, oId
1477
2229
  name: [1, 6, 8].includes(existingEdgePoint) ? false : "",
1478
2230
  });
1479
2231
  }
1480
- setWorkflowNodes((p) => (0, util_1.editNestedObject)([existingEdgePoint, "buttons"], p, buttons, true));
2232
+ setFWorkflowNodes((p) => (0, util_1.editNestedObject)([existingEdgePoint, "buttons"], p, buttons, true));
1481
2233
  setNewEdgePoint(undefined);
1482
- }, [workflowNodes]);
2234
+ }, [fWorkflowNodes]);
1483
2235
  const openPopup = (0, react_1.useCallback)(() => {
1484
2236
  setFilePopupActive(true);
1485
2237
  }, []);
1486
- return ({ ...{ filePopupActive, files, uploadFile, addNode, onDelete, arrows, onChange, error, includedFiles, onMoveEnd, addEdgePoint, newEdgePoint, setTutorialActive, setFilePopupActive,
1487
- includedForms, submitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
1488
- onDeleteArrow, workflowNodes, containerRef, setError, setArrows, setWorkflowNodes } });
2238
+ return ({ ...{ filePopupActive, files, uploadFile, fAddNode, fOnDelete, arrows, onChange, error, includedFiles, onMoveEnd, addEdgePoint, newEdgePoint, setTutorialActive, setFilePopupActive,
2239
+ includedForms, fSubmitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
2240
+ onDeleteArrow, fWorkflowNodes, containerRef, setError, setArrows, setFWorkflowNodes } });
1489
2241
  }
1490
- exports.useWorkflowEditor = useWorkflowEditor;
1491
2242
  function useInstitutePlacementListingHandler({ user }) {
1492
2243
  const [emptyCellsWarning, setEmptyCellsWarning] = (0, react_1.useState)(false);
1493
2244
  const [alert, setAlert] = (0, react_1.useState)();
@@ -1577,5 +2328,373 @@ function useInstitutePlacementListingHandler({ user }) {
1577
2328
  };
1578
2329
  return ({ ...{ uploadPlacements, alert, onChange } });
1579
2330
  }
1580
- exports.useInstitutePlacementListingHandler = useInstitutePlacementListingHandler;
2331
+ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisation }) {
2332
+ var _a;
2333
+ const [placement, setPlacement] = (0, react_1.useState)();
2334
+ const [institute, setInstitute] = (0, react_1.useState)(user.product === "institutes" ? organisation === null || organisation === void 0 ? void 0 : organisation.details : undefined);
2335
+ const [workflow, setWorkflow] = (0, react_1.useState)();
2336
+ const [cohort, setCohort] = (0, react_1.useState)();
2337
+ const [student, setStudent] = (0, react_1.useState)();
2338
+ const [wStage, setWStage] = (0, react_1.useState)();
2339
+ const [snackbar, setSnackbar] = (0, react_1.useState)({ open: false });
2340
+ const [disableEmail, setDisableEmail] = (0, react_1.useState)({ parent: false, provider: false });
2341
+ const [rejectELIPopup, setRejectELIPopup] = (0, react_1.useState)(false);
2342
+ const [eliPopupOpen, setEliPopupOpen] = (0, react_1.useState)(false);
2343
+ const [eliURL, setELIURL] = (0, react_1.useState)("");
2344
+ const [rejectExternalDocPopup, setRejectExternalDocPopup] = (0, react_1.useState)(false);
2345
+ const [externalDocPopupOpen, setExternalDocPopupOpen] = (0, react_1.useState)(false);
2346
+ const [riskAssessmentURL, setRiskAssessmentURL] = (0, react_1.useState)("");
2347
+ const [dbsCheckURL, setDbsCheckURL] = (0, react_1.useState)("");
2348
+ const [uploadProviderDocPopup, setUploadProviderDocPopup] = (0, react_1.useState)();
2349
+ const [skipStagePopup, setSkipStagePopup] = (0, react_1.useState)(false);
2350
+ const [viewExternalLinkPopup, setViewExternalLinkPopup] = (0, react_1.useState)(false);
2351
+ const [externalLinkCopied, setExternalLinkCopied] = (0, react_1.useState)(false);
2352
+ const [uploadInsurance, setUploadInsurance] = (0, react_1.useState)(false);
2353
+ const [uploadRA, setUploadRA] = (0, react_1.useState)(false);
2354
+ const [uploadDBS, setUploadDBS] = (0, react_1.useState)(false);
2355
+ const [onboardingPopup, setOnboardingPopup] = (0, react_1.useState)(false);
2356
+ const [editable, setEditable] = (0, react_1.useState)(false);
2357
+ const [withdrawFromPlacementPopup, setWithdrawFromPlacementPopup] = (0, react_1.useState)(false);
2358
+ const firebaseQuery = new firebaseQuery_1.default();
2359
+ (0, react_1.useEffect)(() => {
2360
+ if (!placementId)
2361
+ return;
2362
+ (0, readDatabase_1.getPlacementbyId)(placementId, setPlacement);
2363
+ }, [placementId]);
2364
+ (0, react_1.useEffect)(() => {
2365
+ var _a;
2366
+ console.log("p", placement);
2367
+ if (!placement) {
2368
+ return;
2369
+ }
2370
+ setEditable(!((placement.providerCompleted && placement.providerCompleted.includes("details")) || placement.completed));
2371
+ if (placement.oId) {
2372
+ if (user.product === "institutes") {
2373
+ setInstitute(organisation === null || organisation === void 0 ? void 0 : organisation.details);
2374
+ }
2375
+ else if (user.product === "providers") {
2376
+ firebaseQuery.getDocData(["institutes", placement.oId]).then((i) => setInstitute(i));
2377
+ }
2378
+ }
2379
+ if (placement.cohort) {
2380
+ if (user.product === "institutes" && user.oId === placement.oId) {
2381
+ setCohort(organisation === null || organisation === void 0 ? void 0 : organisation.cohorts[placement.cohort]);
2382
+ setWorkflow((_a = organisation === null || organisation === void 0 ? void 0 : organisation.cohorts[placement.cohort]) === null || _a === void 0 ? void 0 : _a.workflow);
2383
+ }
2384
+ else {
2385
+ firebaseQuery.getDocData(["cohorts", placement.cohort]).then((w) => {
2386
+ setWorkflow(w.workflow);
2387
+ setCohort(w);
2388
+ });
2389
+ }
2390
+ }
2391
+ else {
2392
+ setWorkflow(constants_1.defaultStudentWorkflow);
2393
+ }
2394
+ if (user.userType === "Students") {
2395
+ setStudent(user);
2396
+ }
2397
+ else {
2398
+ (0, readDatabase_1.getUserById)(placement.uid).then(setStudent);
2399
+ }
2400
+ }, [placement]);
2401
+ (0, react_1.useEffect)(() => {
2402
+ if (!workflow || !placement)
2403
+ 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
+ }
2417
+ }, [placement, workflow]);
2418
+ const editStage = async (nextStageId) => {
2419
+ if (!placementId || !wStage)
2420
+ return;
2421
+ await (0, writeDatabase_1.editPlacementStage)(placementId, wStage.id, nextStageId);
2422
+ setSnackbar({ open: true, message: "Stage updated." });
2423
+ };
2424
+ const sendEmail = (type) => {
2425
+ if (!placement || !student)
2426
+ return undefined;
2427
+ const sendRequest = async () => {
2428
+ await (0, firebase_1.executeCallable)("placement-sendExternalEmail", { pId: placementId, userType: type });
2429
+ setSnackbar({ open: true, message: "Email sent." });
2430
+ return;
2431
+ };
2432
+ if ((type === "provider" && !placement.providerEmail) || (type === "parent" && !student.details.parentEmail)) {
2433
+ setSnackbar({ open: true, message: `Please add a ${type} email.` });
2434
+ return;
2435
+ }
2436
+ if (!placement[`${type}Emailed`]) {
2437
+ setDisableEmail((x) => ({ ...x, [type]: true }));
2438
+ sendRequest().then(() => setDisableEmail((x) => ({ ...x, [type.toLowerCase()]: false })));
2439
+ return;
2440
+ }
2441
+ const previousEmailTime = new Date(placement[`${type}Emailed`].seconds * 1000);
2442
+ const today = new Date();
2443
+ const timeSinceEmail = (0, util_1.getDateDiff)(previousEmailTime, today);
2444
+ if (timeSinceEmail === 0) {
2445
+ setSnackbar({ open: true, message: "Emails can only be sent after 1 day." });
2446
+ return;
2447
+ }
2448
+ setDisableEmail((x) => ({ ...x, [type.toLowerCase()]: true }));
2449
+ sendRequest().then(() => setDisableEmail((x) => ({ ...x, [type.toLowerCase()]: false })));
2450
+ };
2451
+ 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
+ const signOffPlacements = (0, util_1.getAccess)(user, "signOffPlacements");
2453
+ 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") {
2458
+ console.log("ALMOST CAN EDIT");
2459
+ if (user.userType === "Staff" && !signOffPlacements) {
2460
+ canEdit = false;
2461
+ }
2462
+ else {
2463
+ canEdit = true;
2464
+ }
2465
+ }
2466
+ const onFlagClick = async (e) => {
2467
+ if (!placement)
2468
+ return;
2469
+ if (e === "completeOnboarding" || e === "reviewOnboarding") {
2470
+ setOnboardingPopup(true);
2471
+ }
2472
+ if (e === "noInsurance") {
2473
+ if (!eliURL) {
2474
+ 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);
2477
+ }
2478
+ setEliPopupOpen(true);
2479
+ }
2480
+ if (e === "noRiskAssessment") {
2481
+ if (!riskAssessmentURL && placement.riskAssessmentType === "file") {
2482
+ console.log("Risk assessment");
2483
+ const storageRef = (0, storage_1.ref)(firebaseConfig_1.storage, `riskAssessments/${placement.placementId || placement.id}.pdf`);
2484
+ const file = await (0, storage_1.getDownloadURL)(storageRef);
2485
+ setRiskAssessmentURL(file);
2486
+ }
2487
+ setExternalDocPopupOpen("riskAssessment");
2488
+ }
2489
+ if (e === "noDbsCheck") {
2490
+ if (!dbsCheckURL && placement.dbsCheckType === "file") {
2491
+ const storageRef = (0, storage_1.ref)(firebaseConfig_1.storage, `dbsChecks/${placement.placementId || placement.id}.pdf`);
2492
+ const file = await (0, storage_1.getDownloadURL)(storageRef);
2493
+ setDbsCheckURL(file);
2494
+ }
2495
+ setExternalDocPopupOpen("dbsCheck");
2496
+ }
2497
+ };
2498
+ const approveELI = async () => {
2499
+ if (!placement)
2500
+ return;
2501
+ await (0, firebase_1.executeCallable)("insurance-approve", { oId: user.oId, providerId: placement.providerId });
2502
+ setEliPopupOpen(false);
2503
+ };
2504
+ const rejectELI = async ({ reason }) => {
2505
+ if (!placement || !institute)
2506
+ 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 });
2509
+ setRejectELIPopup(false);
2510
+ setEliPopupOpen(false);
2511
+ };
2512
+ const approveProviderDoc = async (type) => {
2513
+ await (0, firebase_1.executeCallable)(`${type}-approve`, { oId: user.oId, placementId: placementId });
2514
+ setExternalDocPopupOpen(false);
2515
+ };
2516
+ const rejectProviderDoc = async ({ reason }, type) => {
2517
+ if (!placement || !institute)
2518
+ return;
2519
+ await (0, firebase_1.executeCallable)(`${type}-reject`, { reason: reason, placementId: placementId, instituteName: institute.name, staffEmail: user.email });
2520
+ setRejectExternalDocPopup(false);
2521
+ setExternalDocPopupOpen(false);
2522
+ };
2523
+ const manuallyConfigureProvider = async () => {
2524
+ if (!placement)
2525
+ return;
2526
+ 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,
2531
+ skipSearch: true
2532
+ }).catch((e) => {
2533
+ throw e;
2534
+ });
2535
+ console.log("RETURN", res.data);
2536
+ setPlacement((p) => ({ ...p, ...res.data }));
2537
+ if (uploadProviderDocPopup === "insurance") {
2538
+ setUploadProviderDocPopup(undefined);
2539
+ setUploadInsurance(true);
2540
+ }
2541
+ else if (uploadProviderDocPopup === "riskAssessment") {
2542
+ setUploadProviderDocPopup(undefined);
2543
+ setUploadRA(true);
2544
+ }
2545
+ else if (uploadProviderDocPopup === "DbsCheck") {
2546
+ setUploadProviderDocPopup(undefined);
2547
+ setUploadDBS(true);
2548
+ }
2549
+ }
2550
+ };
2551
+ const withdrawFromPlacement = async () => {
2552
+ if (user.userType !== "Students")
2553
+ throw new Error("Must be a student to withdraw.");
2554
+ await (0, firebase_1.executeCallable)("placement-withdraw", { placementId: placementId });
2555
+ };
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 };
2557
+ }
2558
+ function useOnboardingPopup({ onboarding, placementId, user, onClose }) {
2559
+ const [fileUploadPopup, setFileUploadPopup] = (0, react_1.useState)(false);
2560
+ const [form, setForm] = (0, react_1.useState)();
2561
+ const [rejectPopup, setRejectPopup] = (0, react_1.useState)(false);
2562
+ const [mOnboarding, setMOnboarding] = (0, react_1.useState)(onboarding);
2563
+ const [completedSections, setCompletedSections] = (0, react_1.useState)();
2564
+ const [uploadedFiles, setUploadedFiles] = (0, react_1.useState)({});
2565
+ const firebaseQuery = new firebaseQuery_1.default();
2566
+ const addFile = (files) => {
2567
+ if (!files.length || fileUploadPopup === false)
2568
+ return;
2569
+ setMOnboarding((a) => (0, util_1.editNestedObject)(["completed", "filesUploaded", fileUploadPopup], a, files));
2570
+ setFileUploadPopup(false);
2571
+ };
2572
+ const viewFile = (file, onOpen) => {
2573
+ setMOnboarding((a) => {
2574
+ var _a, _b, _c;
2575
+ const oldA = { ...a };
2576
+ const viewedFiles = a.completed ? ((_a = a.completed) === null || _a === void 0 ? void 0 : _a.filesViewed) || [] : [];
2577
+ if (viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.includes(file))
2578
+ 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);
2580
+ viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.push(file);
2581
+ const newA = (0, util_1.editNestedObject)(["completed", "filesViewed"], oldA, viewedFiles);
2582
+ return newA;
2583
+ });
2584
+ };
2585
+ const setFormComplete = (e, formId) => {
2586
+ const mFormId = formId || (form === null || form === void 0 ? void 0 : form.id);
2587
+ if (!mFormId)
2588
+ return;
2589
+ setMOnboarding((a) => (0, util_1.editNestedObject)(["completed", "formsCompleted", mFormId], a, e));
2590
+ setFileUploadPopup(false);
2591
+ };
2592
+ (0, react_1.useEffect)(() => {
2593
+ if (!onboarding)
2594
+ return;
2595
+ const getOnboardingData = async () => {
2596
+ var _a, _b;
2597
+ const onboardingNew = { ...onboarding };
2598
+ const onboardingFiles = onboarding.files ? Object.fromEntries(await Promise.all((_a = onboarding.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
2599
+ 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}`));
2601
+ return [fileId, file];
2602
+ }))) : [];
2603
+ const onboardingForms = onboarding.forms ? Object.fromEntries(await Promise.all((_b = onboarding.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
2604
+ return [formId, await firebaseQuery.getDocData(["forms", formId])];
2605
+ }))) : [];
2606
+ onboardingNew.viewableFiles = onboardingFiles;
2607
+ onboardingNew.formDetails = onboardingForms;
2608
+ return onboardingNew;
2609
+ };
2610
+ getOnboardingData().then(setMOnboarding);
2611
+ }, [onboarding]);
2612
+ (0, react_1.useEffect)(() => {
2613
+ console.log("onboarding change", mOnboarding, !(0, util_1.objectsEqual)(mOnboarding, onboarding));
2614
+ if (!(0, util_1.objectsEqual)(mOnboarding, onboarding) && placementId) {
2615
+ firebaseQuery.update(["placements", placementId], { onboarding: mOnboarding });
2616
+ }
2617
+ }, [mOnboarding]);
2618
+ const stagesCompleted = () => {
2619
+ var _a, _b;
2620
+ console.log("CompletedSections", completedSections);
2621
+ for (const fileViewed of (mOnboarding === null || mOnboarding === void 0 ? void 0 : mOnboarding.files) || []) {
2622
+ if (!((_a = completedSections === null || completedSections === void 0 ? void 0 : completedSections.filesViewed) === null || _a === void 0 ? void 0 : _a.includes(fileViewed))) {
2623
+ console.log("Checking file", fileViewed);
2624
+ console.log("File not completed");
2625
+ return false;
2626
+ }
2627
+ }
2628
+ for (const formCompleted of (mOnboarding === null || mOnboarding === void 0 ? void 0 : mOnboarding.forms) || []) {
2629
+ if (!Object.keys((completedSections === null || completedSections === void 0 ? void 0 : completedSections.formsCompleted) || {}).includes(formCompleted)) {
2630
+ console.log("Form not completed");
2631
+ return false;
2632
+ }
2633
+ }
2634
+ for (var i; i++; i < (((_b = mOnboarding === null || mOnboarding === void 0 ? void 0 : mOnboarding.requiredFiles) === null || _b === void 0 ? void 0 : _b.length) || 1)) {
2635
+ if (!Object.keys((completedSections === null || completedSections === void 0 ? void 0 : completedSections.filesUploaded) || {}).includes((i))) {
2636
+ return false;
2637
+ }
2638
+ }
2639
+ return true;
2640
+ };
2641
+ const rejectOnboarding = async ({ reason }) => {
2642
+ if (user.product !== "providers")
2643
+ return;
2644
+ return await (0, firebase_1.executeCallable)("placement-rejectOnboarding", { reason: reason, placementId: placementId }).then(() => {
2645
+ setRejectPopup(false);
2646
+ onClose();
2647
+ }).catch(() => {
2648
+ throw new Error("Error");
2649
+ });
2650
+ };
2651
+ const acceptOnboarding = async () => {
2652
+ if (user.product !== "providers" || !placementId)
2653
+ return;
2654
+ await firebaseQuery.update(["placements", placementId], { "onboarding.completed.accepted": true });
2655
+ };
2656
+ const submit = async () => {
2657
+ // Check all stages completed.
2658
+ if (!placementId)
2659
+ return;
2660
+ if (!stagesCompleted())
2661
+ throw new Error("Complete all forms before submitting.");
2662
+ await firebaseQuery.update(["placements", placementId], { ["onboarding.completed.submitted"]: (0, util_1.convertDate)(new Date(), "dbstring") });
2663
+ };
2664
+ (0, react_1.useEffect)(() => {
2665
+ const getUploadedFiles = async () => {
2666
+ if (!mOnboarding.completed) {
2667
+ setUploadedFiles({});
2668
+ return;
2669
+ }
2670
+ ;
2671
+ const fileIds = Object.values(mOnboarding.completed.filesUploaded || {})
2672
+ .flatMap(fileIds => fileIds);
2673
+ const fileDataPromises = fileIds.map(async (fileId) => {
2674
+ const fileData = await firebaseQuery.getDocData(["files", fileId]);
2675
+ fileData.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${fileData.fileName}`));
2676
+ return [fileId, fileData];
2677
+ });
2678
+ const fileDataArray = await Promise.all(fileDataPromises);
2679
+ const fileDataObj = Object.fromEntries(fileDataArray);
2680
+ setUploadedFiles(fileDataObj);
2681
+ };
2682
+ const addCompletedSectionURLs = async () => {
2683
+ const completedSectionsWithURLs = { ...mOnboarding === null || mOnboarding === void 0 ? void 0 : mOnboarding.completed };
2684
+ completedSectionsWithURLs.filesUploaded = Object.fromEntries(await Promise.all(Object.entries(completedSectionsWithURLs.filesUploaded || {}).map(async ([fileId, items]) => {
2685
+ const filesWithObjects = await Promise.all(items.map(async (itemId) => {
2686
+ const file = await firebaseQuery.getDocData(["files", itemId]);
2687
+ const fileUrl = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${file.fileName}`));
2688
+ console.log("|FILEURL", fileUrl);
2689
+ return fileUrl;
2690
+ }));
2691
+ return [fileId, filesWithObjects];
2692
+ })));
2693
+ setCompletedSections(completedSectionsWithURLs);
2694
+ };
2695
+ getUploadedFiles();
2696
+ addCompletedSectionURLs();
2697
+ }, [mOnboarding]);
2698
+ return { addFile, viewFile, uploadedFiles, setFormComplete, setRejectPopup, stagesCompleted, setForm, mOnboarding, form, submit, acceptOnboarding, rejectOnboarding, rejectPopup, completedSections, fileUploadPopup, setFileUploadPopup };
2699
+ }
1581
2700
  //# sourceMappingURL=hooks.js.map