placementt-core 11.0.803 → 11.0.914

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/hooks.js CHANGED
@@ -7,20 +7,20 @@ exports.useRefDimensions = void 0;
7
7
  exports.useStudentPlacementList = useStudentPlacementList;
8
8
  exports.useOldInstitutePlacementList = useOldInstitutePlacementList;
9
9
  exports.useNewInstitutePlacementList = useNewInstitutePlacementList;
10
+ exports.useAlumniPaginator = useAlumniPaginator;
10
11
  exports.useVeryOldInstitutePlacementList = useVeryOldInstitutePlacementList;
11
12
  exports.useFilterTablePaginator = useFilterTablePaginator;
12
- exports.usePlacementListingPaginator = usePlacementListingPaginator;
13
+ exports.useProviderContactPaginator = useProviderContactPaginator;
14
+ exports.useNewCohortUserPaginator = useNewCohortUserPaginator;
13
15
  exports.useCohortUserPaginator = useCohortUserPaginator;
14
16
  exports.useAdmissionsPaginator = useAdmissionsPaginator;
15
17
  exports.useLazyLoadQueryList = useLazyLoadQueryList;
16
- exports.usePublicPlacementListingLoader = usePublicPlacementListingLoader;
17
- exports.useCreateApplicationRenderer = useCreateApplicationRenderer;
18
18
  exports.useProposePlacementRenderer = useProposePlacementRenderer;
19
19
  exports.useCreateCohortRenderer = useCreateCohortRenderer;
20
20
  exports.useUserUploadHandler = useUserUploadHandler;
21
21
  exports.useWorkflowEditor = useWorkflowEditor;
22
22
  exports.useApplicantWorkflowEditor = useApplicantWorkflowEditor;
23
- exports.useInstitutePlacementListingHandler = useInstitutePlacementListingHandler;
23
+ exports.useInstituteProviderContactsHandler = useInstituteProviderContactsHandler;
24
24
  exports.useGetIndividualPlacementForPlacementPage = useGetIndividualPlacementForPlacementPage;
25
25
  exports.useOnboardingPopup = useOnboardingPopup;
26
26
  exports.useLoadAddresses = useLoadAddresses;
@@ -266,7 +266,7 @@ function useNewInstitutePlacementList({ id, user, filters, view, cohort, queryCo
266
266
  direction: "asc",
267
267
  },
268
268
  ["Student Email - Desc"]: {
269
- value: "studentEmaile",
269
+ value: "studentEmail",
270
270
  direction: "desc",
271
271
  },
272
272
  ["Provider email - Asc"]: {
@@ -283,15 +283,20 @@ function useNewInstitutePlacementList({ id, user, filters, view, cohort, queryCo
283
283
  if (!(user.studentFilter && user.studentFilterValues))
284
284
  return false;
285
285
  const student = await (0, readDatabase_1.getUserById)(placement.uid).catch(() => false);
286
- if (!student)
286
+ if (!student) {
287
+ console.log("No student");
287
288
  return;
289
+ }
290
+ ;
288
291
  if (!user.studentFilterValues.includes(student.details[user.studentFilter])) {
292
+ console.log("filter not included. Filteres: ", user.studentFilterValues, "value", user.studentFilter, "-", student.details[user.studentFilter]);
289
293
  return false;
290
294
  }
291
295
  }
296
+ console.log("Valid placement", k);
292
297
  return { ...placement, id: k };
293
298
  };
294
- const { tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query, additionalEntryProcessing: additionalProcessing, onSearch: async (s, sort, page, filters, limit) => await algoliaPlacementSearch(user, s, sort, page, filters, limit, cohort, inProgress) });
299
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query, additionalEntryProcessing: additionalProcessing, onSearch: async (s, sort, page, filters, limit) => await algoliaPlacementSearch(query || [], user, s, sort, page, filters, limit, cohort, inProgress) });
295
300
  (0, react_1.useEffect)(() => {
296
301
  var _a;
297
302
  // Sets the query of for the DataViewerPaginator
@@ -299,7 +304,7 @@ function useNewInstitutePlacementList({ id, user, filters, view, cohort, queryCo
299
304
  setQuery(undefined);
300
305
  return;
301
306
  }
302
- if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort))) || (user.viewCohorts === "all" && user.viewStudents === "all")))) {
307
+ if (((user.userGroup === "admin" || (cohort && user.viewCohorts === "some" && ((_a = user === null || user === void 0 ? void 0 : user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort))) || (user.viewCohorts === "all" && user.viewStudents !== "none")))) {
303
308
  const constraints = [["oId", "==", user.oId], ["draft", "==", false]];
304
309
  cohort && constraints.push(["cohort", "==", cohort]);
305
310
  queryConstraints && constraints.unshift(...queryConstraints);
@@ -314,7 +319,76 @@ function useNewInstitutePlacementList({ id, user, filters, view, cohort, queryCo
314
319
  }, [user, queryConstraints, cohort]);
315
320
  return { tableData, page, loading, updateSearch, setFilters, setView, pageUp, pageDown, sorts, updateSort, sort };
316
321
  }
317
- const algoliaPlacementSearch = async (user, query, sort, page, filters, limit, cohort, inProgress) => {
322
+ function useAlumniPaginator({ user, alumniConvoUser, filters, view, school, queryConstraints, ql = DEFAULTQUERYLIMIT }) {
323
+ const [query, setQuery] = (0, react_1.useState)();
324
+ const sorts = {};
325
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query });
326
+ const firebaseQuery = new firebaseQuery_1.default();
327
+ (0, react_1.useEffect)(() => {
328
+ const createQuery = async () => {
329
+ // Sets the query of for the DataViewerPaginator
330
+ const getQueryAccess = async () => {
331
+ var _a;
332
+ const constraints = [];
333
+ if (user) {
334
+ constraints.push(["oId", "==", user.oId]);
335
+ if (user.userGroup === "admin" && user.userType === "Staff")
336
+ return constraints;
337
+ if (user.userType === "Staff") {
338
+ if (user.viewSchools === "all")
339
+ return constraints;
340
+ if (user.viewSchools === "none")
341
+ return false;
342
+ if (user.viewSchools === "some") {
343
+ if (!school)
344
+ return false;
345
+ if ((_a = user === null || user === void 0 ? void 0 : user.visibleSchools) === null || _a === void 0 ? void 0 : _a.includes(school))
346
+ return constraints;
347
+ }
348
+ return false;
349
+ }
350
+ return false;
351
+ }
352
+ if (alumniConvoUser) {
353
+ constraints.push(["oId", "==", alumniConvoUser.oId]);
354
+ console.log("ALUMNI CONVO USER");
355
+ if ((school || alumniConvoUser.schoolId) && school === alumniConvoUser.schoolId)
356
+ return constraints;
357
+ console.log("ALUMNI ACCESS TRUE");
358
+ if (alumniConvoUser.schoolId) {
359
+ const school = await firebaseQuery.getDocData(["schools", alumniConvoUser.schoolId]);
360
+ console.log("SCHOOL ACCESS", school, school.alumniConversations);
361
+ return school.alumniConversations ? constraints : false;
362
+ }
363
+ else {
364
+ const institute = await firebaseQuery.getDocData(["institutes", alumniConvoUser.oId]);
365
+ return institute.alumniConversations ? constraints : false;
366
+ }
367
+ }
368
+ return false;
369
+ };
370
+ const constraints = await getQueryAccess();
371
+ console.log("CONSTRAINTS", constraints);
372
+ if (!constraints)
373
+ return;
374
+ school && constraints.push(["schoolId", "==", school]);
375
+ queryConstraints && constraints.unshift(...queryConstraints);
376
+ return constraints;
377
+ };
378
+ console.log("Creating query");
379
+ createQuery().then((constraints) => {
380
+ setQuery([{
381
+ path: ["alumni"],
382
+ where: constraints
383
+ }]);
384
+ });
385
+ }, [user, queryConstraints, school]);
386
+ (0, react_1.useEffect)(() => {
387
+ console.log("Alumni data", query, tableData);
388
+ }, [tableData]);
389
+ return { tableData, page, loading, setFilters, setView, pageUp, pageDown, sorts };
390
+ }
391
+ const algoliaPlacementSearch = async (data, user, query, sort, page, filters, limit, cohort, inProgress) => {
318
392
  const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
319
393
  const placementsIndex = algoliaClient.initIndex(sort ? Object.values(sort[1]).join("_") : "placements");
320
394
  // const usersIndex = algoliaClient.initIndex("users");
@@ -714,181 +788,184 @@ function useFilterTablePaginator({ data }) {
714
788
  }, [page]);
715
789
  return ({ ...{ tableData, setPage, setFilters, page } });
716
790
  }
717
- function usePlacementListingPaginator({ data, user }) {
718
- const [tableData, setTableData] = (0, react_1.useState)({});
719
- const [page, setPage] = (0, react_1.useState)([1, 0]);
720
- const [filters, setFilters] = (0, react_1.useState)();
721
- const [queryAnchor, setQueryAnchor] = (0, react_1.useState)({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
722
- const [prevEntryIds, setPrevEntryIds] = (0, react_1.useState)({});
791
+ function useProviderContactPaginator({ data, user, view, filters }) {
792
+ const [query, setQuery] = (0, react_1.useState)();
723
793
  const firebaseQuery = new firebaseQuery_1.default();
724
- const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
725
- let cursorPos;
726
- if (page[0] > page[1]) {
727
- cursorPos = currentQueryAnchor.endQueryPos;
794
+ const getAdditionalData = async (k, v) => {
795
+ var _a, _b;
796
+ if ((_b = (_a = v.savedBy) === null || _a === void 0 ? void 0 : _a[user.oId].activities) === null || _b === void 0 ? void 0 : _b.includes("workExperience")) {
797
+ const placementsCount = await firebaseQuery.getCount("placementListings", [(0, firestore_1.where)(`savedBy.${user.oId}.exists`, "==", true), (0, firestore_1.where)("providerContactId", "==", k)]);
798
+ return { ...v, plcaements: placementsCount };
728
799
  }
729
- else {
730
- cursorPos = currentQueryAnchor.startQueryPos;
800
+ return v;
801
+ };
802
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading } = useDataViewerPaginator({ view, filters, queryLimit: 10, data: query, additionalEntryProcessing: getAdditionalData });
803
+ (0, react_1.useEffect)(() => {
804
+ const constraints = [
805
+ [`savedBy.${user.oId}.exists`, "==", true],
806
+ ];
807
+ if (user.userType === "Students") {
808
+ constraints.push([`savedBy.${user.oId}.cohorts.${user.cohort}.listed`, "==", true]);
731
809
  }
732
- const querySchema = data === null || data === void 0 ? void 0 : data[cursorPos];
733
- const createQuery = (queryData) => {
734
- const constraints = [
735
- (0, firestore_1.where)("savedById", "==", user.userType === "Staff" ? user.oId : user.id),
736
- (0, firestore_1.where)("savedByProduct", "==", user.product),
737
- (0, firestore_1.where)("savedByUserType", "==", user.userType === "Staff" ? "Organisation" : "Student"),
738
- ];
739
- if (user.userType === "Students") {
740
- constraints.push((0, firestore_1.where)("listed", "==", true));
741
- }
742
- (queryData === null || queryData === void 0 ? void 0 : queryData.where) && queryData.where.forEach((w) => {
743
- constraints.push((0, firestore_1.where)(...w));
744
- });
745
- filters && Object.entries(filters).forEach(([key, value]) => {
746
- constraints.push((0, firestore_1.where)(key, "==", value));
747
- });
748
- if (queryData === null || queryData === void 0 ? void 0 : queryData.orderBy) {
749
- if (queryData.orderBy === "documentId") {
750
- constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
751
- }
752
- else {
753
- constraints.push((0, firestore_1.orderBy)(queryData.orderBy));
754
- }
755
- }
756
- else {
757
- constraints.push((0, firestore_1.orderBy)((0, firestore_1.documentId)()));
758
- }
759
- if (page[0] > page[1] && !cursorDirection) { // Going up
760
- currentQueryAnchor.endKey && constraints.push((0, firestore_1.startAfter)(currentQueryAnchor.endKey));
761
- constraints.push((0, firestore_1.limit)(10));
762
- if (!loadMoreFromQuery) {
763
- currentQueryAnchor = { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.endQueryPos };
764
- }
810
+ setQuery([{
811
+ path: ["providerContacts"],
812
+ where: constraints
813
+ }]);
814
+ }, [user]);
815
+ return ({ ...{ tableData, pageUp, pageDown, setFilters, page, setView, loading } });
816
+ }
817
+ const algoliaUsersSearch = async (data, user, query, sort, page, filters, limit, cohort) => {
818
+ const algoliaClient = (0, algoliasearch_1.default)(process.env.NODE_ENV === "development" ? "A0ZB50I7VS" : "XMPXCMUUOJ", user.algoliaKey);
819
+ console.log("SORT", sort);
820
+ const userIndex = algoliaClient.initIndex(sort ? `users_${Object.values(sort[1]).join("_")}` : "users");
821
+ const constraints = data[0].where;
822
+ if (!(constraints === null || constraints === void 0 ? void 0 : constraints.length))
823
+ return {};
824
+ let userSearchString = constraints.map(([k, e, v]) => {
825
+ if (e === "==")
826
+ return `${k}:"${v}"`; // Equality check
827
+ if (e === "!=")
828
+ return `${k}:-"${v}"`; // Not equal check
829
+ if (e === "<")
830
+ return `${k}:<${v}`; // Less than check
831
+ if (e === "<=")
832
+ return `${k}:<=${v}`; // Less than or equal check
833
+ if (e === ">")
834
+ return `${k}:>${v}`; // Greater than check
835
+ if (e === ">=")
836
+ return `${k}:>=${v}`; // Greater than or equal check
837
+ if (e === "array-contains")
838
+ return `${k}:"${v}"`; // Array contains check (string format)
839
+ if (e === "array-contains-any")
840
+ return `${k}:"${v.join('","')}"`; // Array contains any of the values (string format)
841
+ if (e === "in")
842
+ return `${k}:(${v.join('","')})`; // In check
843
+ if (e === "not-in")
844
+ return `${k}:(-${v.join('","')})`; // Not in check
845
+ return;
846
+ }).join(" AND ");
847
+ console.log("QUERY", userSearchString);
848
+ filters && Object.entries(filters).filter(([, filter]) => filter.value).map(([id, filter]) => {
849
+ userSearchString = userSearchString + ` AND ${id}:${filter.value}`;
850
+ });
851
+ const options = {
852
+ filters: userSearchString,
853
+ hitsPerPage: limit,
854
+ page: page ? page - 1 : undefined,
855
+ };
856
+ const searchUsersHits = await userIndex.search(query || "", options);
857
+ console.log(searchUsersHits.hits);
858
+ const i = (searchUsersHits ? (await Promise.all(searchUsersHits.hits.map(async (hit) => {
859
+ return [hit.objectID, hit];
860
+ }))) : []).filter((e) => e !== undefined);
861
+ return Object.fromEntries(i);
862
+ };
863
+ function useNewCohortUserPaginator({ user, institute, filters, view, cohort, queryConstraints, ql = DEFAULTQUERYLIMIT, userType }) {
864
+ const [query, setQuery] = (0, react_1.useState)();
865
+ const firebaseQuery = new firebaseQuery_1.default();
866
+ const sorts = {
867
+ ["Student Forename - Asc"]: {
868
+ value: "details.forename",
869
+ direction: "asc",
870
+ },
871
+ ["Student Forename - Desc"]: {
872
+ value: "details.forename",
873
+ direction: "desc",
874
+ },
875
+ ["Student Surname - Asc"]: {
876
+ value: "details.surname",
877
+ direction: "asc",
878
+ },
879
+ ["Student Surname - Desc"]: {
880
+ value: "details.surname",
881
+ direction: "desc",
882
+ },
883
+ ["Student Email - Asc"]: {
884
+ value: "email",
885
+ direction: "asc",
886
+ },
887
+ ["Student Email - Desc"]: {
888
+ value: "email",
889
+ direction: "desc",
890
+ }
891
+ };
892
+ (0, react_1.useEffect)(() => {
893
+ const getAccess = async () => {
894
+ var _a, _b, _c, _d, _e, _f, _g, _h;
895
+ // Sets the query of for the DataViewerPaginator
896
+ if (user.product !== "institutes" || user.userType !== "Staff") {
897
+ setQuery(undefined);
898
+ return;
765
899
  }
766
- else if (page[0] < page[1] && !cursorDirection) { // Going down
767
- if (!loadMoreFromQuery) {
768
- currentQueryAnchor = { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.startQueryPos };
900
+ const constraints = [["oId", "==", user.oId], ["userType", "==", userType]];
901
+ cohort && constraints.push(["cohort", "==", cohort]);
902
+ queryConstraints && constraints.unshift(...queryConstraints);
903
+ const mSetQuery = (clear) => {
904
+ setQuery(clear ? undefined : [{
905
+ path: ["users"],
906
+ where: constraints
907
+ }]);
908
+ return;
909
+ };
910
+ if (institute.package === "institutes-one") {
911
+ if (user.userGroup === "admin" || (user.viewCohorts === "all" && user.viewStudents === "all"))
912
+ return mSetQuery();
913
+ if (!user.viewCohorts || user.viewCohorts === "none" || !user.viewStudents || user.viewStudents === "none" || (user.viewCohorts === "some" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.length)) || (user.viewStudents === "some" && !((_b = user.studentFilterValues) === null || _b === void 0 ? void 0 : _b.length)))
914
+ return mSetQuery(true);
915
+ if (user.viewStudents === "some") {
916
+ constraints.push([`details.${user.studentFilter}`, "in", user.studentFilterValues]);
769
917
  }
770
- constraints.push((0, firestore_1.limitToLast)(10));
771
- if (currentQueryAnchor.startKey) {
772
- currentQueryAnchor.startKey && constraints.push((0, firestore_1.endBefore)(currentQueryAnchor.startKey));
918
+ if (cohort) {
919
+ const canViewCohort = user.viewCohorts === "all" || (user.viewCohorts === "some" && ((_c = user.visibleCohorts) === null || _c === void 0 ? void 0 : _c.includes("cohort")));
920
+ return mSetQuery(canViewCohort);
773
921
  }
774
922
  else {
775
- currentQueryAnchor.startKey && constraints.push((0, firestore_1.endAt)(currentQueryAnchor.startKey));
923
+ // No cohort.
924
+ if (user.viewCohorts === "all")
925
+ return mSetQuery();
926
+ if (user.viewCohorts === "some" && user.visibleCohorts) {
927
+ constraints.push(["cohort", "in", user.visibleCohorts]);
928
+ return mSetQuery();
929
+ }
930
+ return mSetQuery(true);
776
931
  }
777
932
  }
778
- else {
779
- if (cursorDirection === "decrease") {
780
- constraints.push((0, firestore_1.limitToLast)(10));
933
+ if (institute.package === "institutes-two") {
934
+ if (user.userGroup === "admin" || (user.viewSchools === "all" && user.viewCohorts === "all" && user.viewStudents === "all"))
935
+ return mSetQuery();
936
+ if (!user.viewCohorts || !user.viewSchools || user.viewSchools === "none" || user.viewCohorts === "none")
937
+ return mSetQuery(true);
938
+ if (!user.viewStudents || user.viewStudents === "none" || (user.viewStudents === "some" && !((_d = user.studentFilterValues) === null || _d === void 0 ? void 0 : _d.length)) || (user.viewCohorts === "some" && !((_e = user.visibleCohorts) === null || _e === void 0 ? void 0 : _e.length)) || (user.viewSchools === "some" && !((_f = user.visibleSchools) === null || _f === void 0 ? void 0 : _f.length)))
939
+ return mSetQuery(true);
940
+ if (user.viewStudents === "some") {
941
+ constraints.push([`details.${user.studentFilter}`, "in", user.studentFilterValues]);
942
+ }
943
+ if (cohort) {
944
+ if (user.viewCohorts === "some" && !((_g = user.visibleCohorts) === null || _g === void 0 ? void 0 : _g.includes(cohort)))
945
+ return mSetQuery(true);
946
+ if (user.viewSchools === "some") {
947
+ const cohortData = await firebaseQuery.getDocData(["cohorts", cohort]);
948
+ if (!cohortData.schoolId || !((_h = user.visibleSchools) === null || _h === void 0 ? void 0 : _h.includes(cohortData.schoolId)))
949
+ return mSetQuery(true);
950
+ }
781
951
  }
782
952
  else {
783
- constraints.push((0, firestore_1.limit)(10));
953
+ if (user.viewCohorts === "some" && user.visibleCohorts) {
954
+ constraints.push(["cohort", "in", user.visibleCohorts]);
955
+ }
956
+ else if (user.viewSchools === "some" && user.visibleSchools) {
957
+ const cohorts = await firebaseQuery.getDocsWhere("cohorts", [(0, firestore_1.where)("oId", "==", user.oId), (0, firestore_1.where)("schoolId", "in", user.visibleSchools)]);
958
+ constraints.push(["cohort", "in", Object.keys(cohorts || {})]);
959
+ }
784
960
  }
961
+ return mSetQuery();
785
962
  }
786
- return constraints;
787
- };
788
- const constraints = createQuery(querySchema);
789
- // console.log(queryId, "constraints", constraints)
790
- const q = (0, firestore_1.query)((0, firestore_1.collection)(firebaseConfig_1.db, "savedPlacements"), ...(constraints));
791
- const queryResults = {};
792
- const queryData = await (0, firestore_1.getDocs)(q);
793
- // console.log("queryData.size", queryData.size)
794
- const reverseIfBack = (docs) => {
795
- if (page[0] < page[1]) {
796
- return docs.reverse();
797
- }
798
- return docs;
963
+ setQuery(undefined);
799
964
  };
800
- let index = 0;
801
- reverseIfBack(queryData.docs).forEach(async (doc) => {
802
- if (Object.keys(queryResults).length + Object.keys(itemList).length === 10) {
803
- return;
804
- }
805
- let position = Object.keys(itemList).length + (page[0] - 1) * 10 + index + 1;
806
- if (page[0] < page[1]) {
807
- position = (page[0]) * 10 - index - Object.keys(itemList).length;
808
- }
809
- // console.log(index, "doc.id", doc.id, position, "E", prevEntries[doc.id])
810
- if (itemList[doc.id] || (prevEntries[doc.id] && prevEntries[doc.id] !== position)) {
811
- console.log("Removing ", doc.id, ": E=", prevEntries[doc.id], ", G=", position);
812
- return;
813
- }
814
- const item = doc.data();
815
- item.id = doc.id;
816
- queryResults[doc.id] = item;
817
- index = index + 1;
818
- if (prevEntries[doc.id])
819
- return;
820
- prevEntries[doc.id] = position;
821
- });
822
- if (cursorDirection === "decrease" || page[0] < page[1]) {
823
- itemList = { ...Object.fromEntries(Object.entries(queryResults).reverse()), ...itemList };
824
- }
825
- else {
826
- itemList = { ...itemList, ...queryResults };
827
- }
828
- setPrevEntryIds(prevEntries);
829
- if (queryData.size < 10 && Object.keys(itemList).length < 10) {
830
- if (page[0] > page[1] && cursorPos + 1 < data.length) {
831
- console.log("Increase query index");
832
- return getDataFromQuery(itemList, { ...currentQueryAnchor, endQueryPos: currentQueryAnchor.endQueryPos + 1 }, "increase", prevEntries);
833
- }
834
- else if (page[0] < page[1] && cursorPos > 0) {
835
- console.log("Decrease query index");
836
- return getDataFromQuery(itemList, { ...currentQueryAnchor, startQueryPos: currentQueryAnchor.startQueryPos - 1 }, "decrease", prevEntries);
837
- }
838
- }
839
- if (Object.keys(itemList).length < 10 && queryData.size === 10) {
840
- console.log("Shorter than ten");
841
- // if(loadMoreFromQuery){return}
842
- return getDataFromQuery(itemList, { ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] }, undefined, prevEntries, true);
843
- }
844
- if (queryData.size === 0 &&
845
- Object.keys(itemList).length === 0 &&
846
- currentQueryAnchor.endQueryPos + 1 === data.length &&
847
- page[0] > 1) {
848
- setTableData({});
849
- setQueryAnchor((a) => ({ ...a, startKey: "" }));
850
- return;
851
- }
852
- setQueryAnchor({ ...currentQueryAnchor, startKey: Object.keys(itemList)[0], endKey: Object.keys(itemList).slice(-1)[0] });
853
- const finalData = Object.fromEntries(await Promise.all(Object.entries(itemList).map(async ([k, v]) => {
854
- const placement = await firebaseQuery.getDocData(["placementListings", v.placementId || ""]);
855
- const address = await firebaseQuery.getDocData(["addresses", placement.addressId || ""]).catch(() => ({ ["address-line1"]: "Unknown address" }));
856
- const provider = await firebaseQuery.getDocData(["providers", placement.providerId || ""]).catch(() => ({ name: "Unknown" }));
857
- const status = provider.name !== "Unknown" ? getPlacementStatus(provider, placement, v) : "Deleted";
858
- return [k, { ...address, ...provider, ...placement, email: placement.providerEmail, status: status, savedPlacement: v }];
859
- })));
860
- setTableData(finalData);
861
- };
862
- const getPlacementStatus = (provider, placement, savedPlacement) => {
863
- if (savedPlacement.status === "Blocked")
864
- return "blocked";
865
- if (savedPlacement.status === "Accepted")
866
- return "active";
867
- if (user.product === "admin") {
868
- if (placement.mapConsent === undefined)
869
- return "notReviewed";
870
- if (placement.mapConsent === false)
871
- return "notPublic";
872
- return "unknown";
873
- }
874
- ;
875
- if (!provider.insurance)
876
- return "awaitingProviderApproval";
877
- return "requiresApproval";
878
- };
879
- (0, react_1.useEffect)(() => {
880
- if (!filters)
881
- return;
882
- setPage([1, 0]);
883
- setTableData({});
884
- setQueryAnchor({ startKey: "", endKey: "", startQueryPos: 0, endQueryPos: 0 });
885
- setPrevEntryIds({});
886
- }, [filters]);
887
- // Fetch new data when queries or page change
888
- (0, react_1.useEffect)(() => {
889
- getDataFromQuery();
890
- }, [page]);
891
- return ({ ...{ tableData, setPage, setFilters, page } });
965
+ getAccess();
966
+ }, [user, queryConstraints, cohort]);
967
+ const { tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort } = useDataViewerPaginator({ view, filters, sorts, queryLimit: ql, data: query, onSearch: async (s, sort, page, filters, limit) => await algoliaUsersSearch(query || [], user, s, sort, page, filters, limit, cohort) });
968
+ return { tableData, page, loading, updateSearch, setFilters, setView, pageUp, pageDown, sorts, updateSort, sort };
892
969
  }
893
970
  function useCohortUserPaginator({ user, cohort, data, search, userType, sort }) {
894
971
  const [tableData, setTableData] = (0, react_1.useState)({});
@@ -918,8 +995,8 @@ function useCohortUserPaginator({ user, cohort, data, search, userType, sort })
918
995
  return;
919
996
  }
920
997
  if ((!user.viewCohorts && user.userGroup !== "admin") ||
921
- user.viewCohorts === "none" ||
922
- (user.viewCohorts === "some" && cohort !== "all" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort || "")))) {
998
+ (user.viewCohorts === "none" && user.userGroup !== "admin") ||
999
+ (user.viewCohorts === "some" && user.userGroup !== "admin" && cohort !== "all" && !((_a = user.visibleCohorts) === null || _a === void 0 ? void 0 : _a.includes(cohort || "")))) {
923
1000
  setQueries(undefined);
924
1001
  return;
925
1002
  }
@@ -1211,394 +1288,433 @@ function useLazyLoadQueryList({ path, constraints, number, onItemFetched }) {
1211
1288
  }, []);
1212
1289
  return ({ ...{ items, loadMore, loadMoreIcon, reset } });
1213
1290
  }
1214
- function usePublicPlacementListingLoader({ providerId, number = 5 }) {
1215
- const [items, setItems] = (0, react_1.useState)({});
1216
- const [loadMoreIcon, setLoadMoreIcon] = (0, react_1.useState)(false);
1217
- const [lastItem, setLastItem] = (0, react_1.useState)();
1218
- const firebaseQuery = new firebaseQuery_1.default();
1219
- const reset = () => {
1220
- setItems({});
1221
- setLastItem(undefined);
1222
- };
1223
- const loadMore = async () => {
1224
- setLoadMoreIcon(true);
1225
- let formattedConstraints = [
1226
- (0, firestore_1.where)("status", "==", "listed"),
1227
- (0, firestore_1.limit)(number)
1228
- ];
1229
- if (providerId) {
1230
- formattedConstraints.push((0, firestore_1.where)("providerId", "==", providerId));
1231
- }
1232
- if (lastItem) {
1233
- formattedConstraints.push((0, firestore_1.startAfter)(lastItem));
1234
- }
1235
- const documents = await firebaseQuery.getDocsWhere("placementListings", formattedConstraints, true);
1236
- console.log("docs", documents.docs);
1237
- setLastItem(documents.docs[documents.docs.length - 1]);
1238
- const processedItems = Object.fromEntries(await Promise.all(Object.values(documents.docs).map(async (doc) => {
1239
- var _a, _b;
1240
- let itemObj = { ...doc.data(), id: doc.id };
1241
- if (itemObj.addressId) {
1242
- const address = await firebaseQuery.getDocData(["addresses", itemObj.addressId]);
1243
- delete address.id;
1244
- itemObj = { ...address, ...itemObj };
1245
- }
1246
- if (itemObj.applicantWorkflowId) {
1247
- const applicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", itemObj.applicantWorkflowId])).workflow.filter((i) => i.id === 1)[0];
1248
- const applicantFiles = applicantWorkflow.files ? Object.fromEntries(await Promise.all((_a = applicantWorkflow.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
1249
- const file = await firebaseQuery.getDocData(["files", fileId]);
1250
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `providers/${itemObj.providerId}/${file.fileName}`));
1251
- return [fileId, file];
1252
- }))) : [];
1253
- const applicantForms = applicantWorkflow.forms ? Object.fromEntries(await Promise.all((_b = applicantWorkflow.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
1254
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1255
- }))) : [];
1256
- applicantWorkflow.viewableFiles = applicantFiles;
1257
- applicantWorkflow.formDetails = applicantForms;
1258
- itemObj = { ...itemObj, applicantWorkflow: [applicantWorkflow] };
1259
- }
1260
- return [doc.id, itemObj];
1261
- })));
1262
- setItems((i) => ({ ...i, ...processedItems }));
1263
- setLoadMoreIcon(false);
1264
- };
1265
- (0, react_1.useEffect)(() => {
1266
- loadMore();
1267
- }, []);
1268
- return ({ ...{ items, loadMore, loadMoreIcon, reset } });
1269
- }
1270
- function useCreateApplicationRenderer({ user, listingId, listing, provider, application, applicationId, orgContext }) {
1271
- const firebaseQuery = new firebaseQuery_1.default();
1272
- let applicationWithoutAdditionalData = { ...(application || {}) };
1273
- delete applicationWithoutAdditionalData.listing;
1274
- delete applicationWithoutAdditionalData.address;
1275
- delete applicationWithoutAdditionalData.provider;
1276
- const [fApplication, setFApplication] = (0, react_1.useState)(application ? applicationWithoutAdditionalData : {
1277
- uid: user.userType === "Students" ? user.id : undefined,
1278
- listingId: listingId,
1279
- addressId: listing === null || listing === void 0 ? void 0 : listing.addressId,
1280
- stage: 1,
1281
- reqUserType: "Students",
1282
- status: "draft"
1283
- });
1284
- const [fApplicationId, setFApplicationId] = (0, react_1.useState)(applicationId);
1285
- const [draftSaved, setDraftSaved] = (0, react_1.useState)(false);
1286
- const [fProvider, setFProvider] = (0, react_1.useState)(provider);
1287
- const [fListing, setFListing] = (0, react_1.useState)(Object.keys(listing || {}).length > 5 ? listing : undefined);
1288
- const [student, setStudent] = (0, react_1.useState)(user.userType === "Students" ? user : undefined);
1289
- const [profileUrl, setProfileUrl] = (0, react_1.useState)();
1290
- const [successPopup, setSuccessPopup] = (0, react_1.useState)();
1291
- const [uploadedFiles, setUploadedFiles] = (0, react_1.useState)();
1292
- const [currentStageComplete, setCurrentStageComplete] = (0, react_1.useState)();
1293
- (0, react_1.useEffect)(() => {
1294
- const getListing = async () => {
1295
- console.log("Checking ID");
1296
- if (!listingId)
1297
- return;
1298
- console.log("Getting listing");
1299
- console.log("LISTING PARAM", listing, Object.keys(listing || {}).length > 5);
1300
- const listingData = (Object.keys(listing || {}).length > 5) ? listing : await firebaseQuery.getDocData(["placementListings", listingId]).catch(() => false);
1301
- console.log("LISTINGDATA", listingData, Object.keys(listing || {}).length > 5 ? { a: "string" } : "AAA");
1302
- const address = listingData ? (user.product === "providers" && orgContext) ? orgContext.addresses[listingData.addressId || ""] : await firebaseQuery.getDocData(["addresses", listingData.addressId]) : undefined;
1303
- const workflow = listingData ? (user.product === "providers" && orgContext) ? orgContext.applicantWorkflows[listingData.applicantWorkflowId || ""] : await firebaseQuery.getDocData(["applicantWorkflows", listingData.applicantWorkflowId]) : undefined;
1304
- if (workflow && listingData) {
1305
- workflow.workflow = await Promise.all(workflow.workflow.map(async (s) => {
1306
- var _a, _b;
1307
- const applicantFiles = s.files ? Object.fromEntries(await Promise.all((_a = s.files) === null || _a === void 0 ? void 0 : _a.map(async (fileId) => {
1308
- const file = await firebaseQuery.getDocData(["files", fileId]);
1309
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `providers/${listingData === null || listingData === void 0 ? void 0 : listingData.providerId}/${file.fileName}`));
1310
- return [fileId, file];
1311
- }))) : [];
1312
- const applicantForms = s.forms ? Object.fromEntries(await Promise.all((_b = s.forms) === null || _b === void 0 ? void 0 : _b.map(async (formId) => {
1313
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1314
- }))) : [];
1315
- return { ...s, viewableFiles: applicantFiles, formDetails: applicantForms };
1316
- }));
1317
- delete workflow.id;
1318
- }
1319
- if (address) {
1320
- delete address.id;
1321
- }
1322
- console.log("Setting listing");
1323
- setFListing((listingData && workflow) ? { ...listingData, ...address, applicantWorkflow: workflow.workflow } : false);
1324
- if (((fProvider === null || fProvider === void 0 ? void 0 : fProvider.id) === (application === null || application === void 0 ? void 0 : application.providerId)) && user.product === "providers") {
1325
- setFProvider({ details: orgContext === null || orgContext === void 0 ? void 0 : orgContext.details, id: user.oId });
1326
- }
1327
- else if (listingData && (listingData === null || listingData === void 0 ? void 0 : listingData.providerId)) {
1328
- console.log("Getting provider from DB");
1329
- const provider = await firebaseQuery.getDocData(["providers", listingData.providerId]);
1330
- console.log("Provider", provider);
1331
- setFProvider({ details: provider, id: listingData.providerId });
1332
- }
1333
- };
1334
- getListing();
1335
- }, [listingId, listing]);
1336
- (0, react_1.useEffect)(() => {
1337
- if ((student === null || student === void 0 ? void 0 : student.id) !== (application === null || application === void 0 ? void 0 : application.uid)) {
1338
- if (user.userType === "Students") {
1339
- setStudent(user);
1340
- }
1341
- else if (user.product === "providers" && (application === null || application === void 0 ? void 0 : application.uid)) {
1342
- firebaseQuery.getDocData(["users", application.uid]).then((s) => setStudent(s));
1343
- }
1344
- }
1345
- }, []);
1346
- (0, react_1.useEffect)(() => {
1347
- setFListing(Object.keys(listing || {}).length > 5 ? listing : undefined);
1348
- }, [listing]);
1349
- (0, react_1.useEffect)(() => {
1350
- if (provider === null || provider === void 0 ? void 0 : provider.profile)
1351
- return;
1352
- if (!(provider === null || provider === void 0 ? void 0 : provider.id))
1353
- return;
1354
- (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);
1355
- }, [provider]);
1356
- (0, react_1.useEffect)(() => {
1357
- console.log("APPLICATIONID", applicationId);
1358
- if (!applicationId) {
1359
- if (!listingId)
1360
- return;
1361
- 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) => {
1362
- console.log("EXISTING", existingApplication);
1363
- // get uploaded files
1364
- if (existingApplication && Object.keys(existingApplication).length) {
1365
- setFApplication(Object.values(existingApplication)[0]);
1366
- setFApplicationId(Object.keys(existingApplication)[0]);
1367
- }
1368
- });
1369
- return;
1370
- }
1371
- if (applicationId) {
1372
- setFApplicationId(applicationId);
1373
- if (application) {
1374
- let applicationWithoutAdditionalData = { ...(application || {}) };
1375
- delete applicationWithoutAdditionalData.listing;
1376
- delete applicationWithoutAdditionalData.address;
1377
- delete applicationWithoutAdditionalData.provider;
1378
- setFApplication(applicationWithoutAdditionalData);
1379
- }
1380
- else {
1381
- firebaseQuery.getDocData(["applications", applicationId]).then(setFApplication);
1382
- }
1383
- }
1384
- }, [application, applicationId]);
1385
- const getCurrentStage = async (stage) => {
1386
- var _a, _b, _c, _d;
1387
- console.log("fLSITING CURRENT STAGE", fListing);
1388
- if (!fListing)
1389
- throw new Error("Listing deleted");
1390
- if (!(fListing === null || fListing === void 0 ? void 0 : fListing.applicantWorkflowId))
1391
- throw new Error("No workflow stage");
1392
- const mApplicantWorkflow = await firebaseQuery.getDocData(["applicantWorkflows", fListing.applicantWorkflowId]);
1393
- const stageObj = (_a = mApplicantWorkflow === null || mApplicantWorkflow === void 0 ? void 0 : mApplicantWorkflow.workflow) === null || _a === void 0 ? void 0 : _a.find((s) => s.id === stage);
1394
- if (!stageObj)
1395
- throw new Error("Can't find stage.");
1396
- const applicantFiles = stageObj.files ? Object.fromEntries(await Promise.all((_b = stageObj.files) === null || _b === void 0 ? void 0 : _b.map(async (fileId) => {
1397
- const file = await firebaseQuery.getDocData(["files", fileId]);
1398
- file.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `providers/${mApplicantWorkflow === null || mApplicantWorkflow === void 0 ? void 0 : mApplicantWorkflow.oId}/${file.fileName}`));
1399
- return [fileId, file];
1400
- }))) : [];
1401
- const applicantForms = stageObj.forms ? Object.fromEntries(await Promise.all((_c = stageObj.forms) === null || _c === void 0 ? void 0 : _c.map(async (formId) => {
1402
- return [formId, await firebaseQuery.getDocData(["forms", formId])];
1403
- }))) : [];
1404
- stageObj.viewableFiles = applicantFiles;
1405
- stageObj.formDetails = applicantForms;
1406
- return { stage: stageObj, completedSections: ((_d = fApplication === null || fApplication === void 0 ? void 0 : fApplication.completedSections) === null || _d === void 0 ? void 0 : _d[stage]) || {} };
1407
- };
1408
- (0, react_1.useEffect)(() => {
1409
- const getUploadedFiles = async () => {
1410
- if (!fApplication.completedSections) {
1411
- setUploadedFiles({});
1412
- return;
1413
- }
1414
- ;
1415
- const fileIds = Object.values(fApplication.completedSections)
1416
- .flatMap(stageData => Object.values(stageData.filesUploaded || {}).flatMap(fileIds => fileIds));
1417
- const fileDataPromises = fileIds.map(async (fileId) => {
1418
- const fileData = await firebaseQuery.getDocData(["files", fileId]);
1419
- fileData.url = await (0, storage_1.getDownloadURL)((0, storage_1.ref)(firebaseConfig_1.storage, `userFiles/${fileData.fileName}`));
1420
- return [fileId, fileData];
1421
- });
1422
- const fileDataArray = await Promise.all(fileDataPromises);
1423
- const fileDataObj = Object.fromEntries(fileDataArray);
1424
- setUploadedFiles(fileDataObj);
1425
- };
1426
- const addApplication = async () => {
1427
- console.log("ADDING APPLICATION");
1428
- if (!fListing || !(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))
1429
- return;
1430
- const applicationData = {
1431
- uid: student.id,
1432
- listingId: fListing === null || fListing === void 0 ? void 0 : fListing.id,
1433
- addressId: fListing.addressId,
1434
- applicantWorkflowId: fListing === null || fListing === void 0 ? void 0 : fListing.applicantWorkflowId,
1435
- providerId: fProvider.id,
1436
- stage: 1,
1437
- status: "draft",
1438
- created: (new Date()).toISOString(),
1439
- ...fApplication,
1440
- };
1441
- const mApplicationId = (await firebaseQuery.add(["applications"], applicationData)).id;
1442
- console.log("APPLICATION ADDED");
1443
- setFApplicationId(mApplicationId);
1444
- setFApplication(applicationData);
1445
- setDraftSaved(true);
1446
- return;
1447
- };
1448
- getUploadedFiles();
1449
- console.log("Checking IDs");
1450
- if (!fListing || !(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))
1451
- return;
1452
- if (user.product === "providers")
1453
- return;
1454
- console.log("Checking dates and sections");
1455
- if (!fApplication.completedSections && !fApplication.startDate && !fApplication.endDate)
1456
- return;
1457
- console.log("Application updated");
1458
- if (!fApplicationId && !applicationId) {
1459
- // save new
1460
- console.log("Add application");
1461
- addApplication();
1462
- return;
1463
- }
1464
- // update
1465
- firebaseQuery.update(["applications", (fApplicationId || applicationId)], fApplication);
1466
- setDraftSaved(true);
1467
- }, [fApplication]);
1468
- const openSuccessPopup = (type) => {
1469
- setSuccessPopup(type);
1470
- setTimeout(() => {
1471
- // onClose();
1472
- }, 1500);
1473
- };
1474
- (0, react_1.useEffect)(() => {
1475
- const areStagesCompleted = async () => {
1476
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1477
- if (!fListing)
1478
- return;
1479
- if (fApplication.stage === undefined)
1480
- return undefined;
1481
- const currentStage = await getCurrentStage(fApplication.stage);
1482
- console.log("Checking current stage is complete");
1483
- for (const fileViewed of ((_a = currentStage.stage) === null || _a === void 0 ? void 0 : _a.files) || []) {
1484
- 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);
1485
- 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))) {
1486
- console.log("File not viewed");
1487
- return false;
1488
- }
1489
- else {
1490
- console.log("File viewed");
1491
- }
1492
- }
1493
- for (const formCompleted of ((_e = currentStage === null || currentStage === void 0 ? void 0 : currentStage.stage) === null || _e === void 0 ? void 0 : _e.forms) || []) {
1494
- 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);
1495
- if (!Object.keys(((_g = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _g === void 0 ? void 0 : _g.formsCompleted) || {}).includes(formCompleted)) {
1496
- console.log("Form not completed");
1497
- return false;
1498
- }
1499
- else {
1500
- console.log("Form completed");
1501
- }
1502
- }
1503
- 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) {
1504
- if (!Object.keys(((_j = currentStage === null || currentStage === void 0 ? void 0 : currentStage.completedSections) === null || _j === void 0 ? void 0 : _j.filesUploaded) || {}).includes(i.toString())) {
1505
- console.log("Form not uploaded");
1506
- return false;
1507
- }
1508
- else {
1509
- console.log("Form uploaded");
1510
- }
1511
- }
1512
- return true;
1513
- };
1514
- areStagesCompleted().then(setCurrentStageComplete);
1515
- }, [fApplication, fListing]);
1516
- const viewFile = (file, onOpen) => {
1517
- var _a, _b;
1518
- if (fApplication.reqUserType !== user.userType)
1519
- return;
1520
- if (fApplication.stage === undefined)
1521
- throw new Error("Missing applciation stage.");
1522
- if (!fListing)
1523
- throw new Error("No associated listing.");
1524
- 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;
1525
- setFApplication((a) => {
1526
- var _a, _b;
1527
- const oldA = { ...a };
1528
- const viewedFiles = ((_b = (_a = a.completedSections) === null || _a === void 0 ? void 0 : _a[1]) === null || _b === void 0 ? void 0 : _b.filesViewed) || [];
1529
- if (viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.includes(file))
1530
- return a;
1531
- (viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url) && onOpen(viewableFiles === null || viewableFiles === void 0 ? void 0 : viewableFiles[file].url);
1532
- viewedFiles === null || viewedFiles === void 0 ? void 0 : viewedFiles.push(file);
1533
- const newA = (0, util_1.editNestedObject)(["completedSections", 1, "filesViewed"], oldA, viewedFiles);
1534
- return newA;
1535
- });
1536
- };
1537
- const addFile = (files, fileId) => {
1538
- if (fApplication.reqUserType !== user.userType)
1539
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1540
- if (fApplication.stage === undefined)
1541
- throw new Error("Missing applciation stage.");
1542
- if (!files.length)
1543
- throw new Error("No files to upload");
1544
- setFApplication((a) => (0, util_1.editNestedObject)(["completedSections", fApplication.stage, "filesUploaded", fileId], a, files));
1545
- };
1546
- const setFormComplete = (formId, e) => {
1547
- if (fApplication.reqUserType !== user.userType)
1548
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1549
- if (fApplication.stage === undefined)
1550
- throw new Error("Missing applciation stage.");
1551
- setFApplication((a) => (0, util_1.editNestedObject)(["completedSections", fApplication.stage, "formsCompleted", formId], a, e));
1552
- };
1553
- const successText = {
1554
- submitted: {
1555
- title: "Application submitted",
1556
- desc: "We've sent your application to the placement provider. You will hear what the next steps are soon.",
1557
- },
1558
- draftSaved: {
1559
- title: "Draft saved",
1560
- desc: "Your draft has been saved. Go to the 'Applications' section from your home screen to edit.",
1561
- },
1562
- stageComplete: {
1563
- title: "Stage complete",
1564
- desc: "We have sent this to the next stage in the application process. We will update you with any progress.",
1565
- },
1566
- outcome: {
1567
- title: "Outcome submitted",
1568
- desc: "We have sent the student an email with the outcome of their application.",
1569
- },
1570
- };
1571
- const onFApply = async (draft) => {
1572
- if (draft) {
1573
- openSuccessPopup("draftSaved");
1574
- return;
1575
- }
1576
- if (!fApplicationId)
1577
- return;
1578
- // Check all items have been filled in.
1579
- if (!fApplication.startDate || !fApplication.endDate)
1580
- throw new Error("Please select dates for your placement.");
1581
- await (0, firebase_1.executeCallable)("applications-submit", { applicationId: fApplicationId });
1582
- const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]);
1583
- setFApplication(newApplication);
1584
- openSuccessPopup("submitted");
1585
- };
1586
- const progressStage = async (type, e) => {
1587
- // Check all stages completed.
1588
- if (!fApplicationId)
1589
- return;
1590
- if (!currentStageComplete)
1591
- throw new Error("Complete all forms before submitting.");
1592
- if (fApplication.reqUserType !== user.userType)
1593
- throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1594
- if (fApplication.stage === undefined)
1595
- throw new Error("Missing applciation stage.");
1596
- await (0, firebase_1.executeCallable)("applications-changeStage", { applicationId: fApplicationId, type: type, feedback: e === null || e === void 0 ? void 0 : e.feedback });
1597
- const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]);
1598
- setFApplication(newApplication);
1599
- };
1600
- return { successText, onFApply, progressStage, currentStageComplete, uploadedFiles, getCurrentStage, setFormComplete, viewFile, addFile, setSuccessPopup, openSuccessPopup, setFApplication, fApplication, draftSaved, profileUrl, successPopup, fApplicationId, fListing, student, fProvider };
1601
- }
1291
+ // type PublicPlacementListingLoaderParams = {
1292
+ // providerId?: string,
1293
+ // number: number,
1294
+ // }
1295
+ // export function usePublicPlacementListingLoader({providerId, number=5}:PublicPlacementListingLoaderParams) {
1296
+ // const [items, setItems] = useState<{[key:string]:{[key:string]:unknown}}>({});
1297
+ // const [loadMoreIcon, setLoadMoreIcon] = useState<boolean>(false);
1298
+ // const [lastItem, setLastItem] = useState<QueryDocumentSnapshot<DocumentData>>();
1299
+ // const firebaseQuery = new FirebaseQuery();
1300
+ // const reset = () => {
1301
+ // setItems({});
1302
+ // setLastItem(undefined);
1303
+ // };
1304
+ // const loadMore = async () => {
1305
+ // setLoadMoreIcon(true);
1306
+ // let formattedConstraints:QueryConstraint[] = [
1307
+ // where("status", "==", "listed"),
1308
+ // limit(number)
1309
+ // ];
1310
+ // if (providerId) {
1311
+ // formattedConstraints.push(where("providerId", "==", providerId));
1312
+ // }
1313
+ // if (lastItem) {
1314
+ // formattedConstraints.push(startAfter(lastItem));
1315
+ // }
1316
+ // const documents = await firebaseQuery.getDocsWhere("placementListings", formattedConstraints, true) as QuerySnapshot<DocumentData>;
1317
+ // console.log("docs", documents.docs);
1318
+ // setLastItem(documents.docs[documents.docs.length-1]);
1319
+ // const processedItems = Object.fromEntries(await Promise.all(Object.values(documents.docs).map(async (doc) => {
1320
+ // let itemObj = {...doc.data(), id: doc.id} as PlacementListing;
1321
+ // if (itemObj.addressId) {
1322
+ // const address = await firebaseQuery.getDocData(["addresses", itemObj.addressId]) as Address;
1323
+ // delete address.id;
1324
+ // itemObj = {...address, ...itemObj};
1325
+ // }
1326
+ // if (itemObj.applicantWorkflowId) {
1327
+ // const applicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", itemObj.applicantWorkflowId]) as ApplicantWorkflow).workflow.filter((i) => i.id === 1)[0];
1328
+ // const applicantFiles = applicantWorkflow.files ? Object.fromEntries(await Promise.all(applicantWorkflow.files?.map(async (fileId) => {
1329
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1330
+ // file.url = await getDownloadURL(ref(storage, `providers/${itemObj.providerId}/${file.fileName}`));
1331
+ // return [fileId, file];
1332
+ // }))) : [];
1333
+ // const applicantForms = applicantWorkflow.forms ? Object.fromEntries(await Promise.all(applicantWorkflow.forms?.map(async (formId) => {
1334
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1335
+ // }))) : [];
1336
+ // applicantWorkflow.viewableFiles = applicantFiles;
1337
+ // applicantWorkflow.formDetails = applicantForms;
1338
+ // itemObj = {...itemObj, applicantWorkflow: [applicantWorkflow]};
1339
+ // }
1340
+ // return [doc.id, itemObj];
1341
+ // })))
1342
+ // setItems((i) => ({...i, ...processedItems}));
1343
+ // setLoadMoreIcon(false);
1344
+ // };
1345
+ // useEffect(() => {
1346
+ // loadMore();
1347
+ // }, []);
1348
+ // return ({...{items, loadMore, loadMoreIcon, reset}});
1349
+ // }
1350
+ // export type ApplicationHookParams = {
1351
+ // successText: {
1352
+ // submitted: {
1353
+ // title: string;
1354
+ // desc: string;
1355
+ // };
1356
+ // draftSaved: {
1357
+ // title: string;
1358
+ // desc: string;
1359
+ // };
1360
+ // stageComplete: {
1361
+ // title: string;
1362
+ // desc: string;
1363
+ // };
1364
+ // outcome: {
1365
+ // title: string;
1366
+ // desc: string;
1367
+ // };
1368
+ // };
1369
+ // setSuccessPopup: import("react").Dispatch<import("react").SetStateAction<"submitted" | "draftSaved" | "stageComplete" | "outcome" | undefined>>;
1370
+ // openSuccessPopup: (type: "submitted" | "draftSaved" | "stageComplete" | "outcome") => void;
1371
+ // setFApplication: import("react").Dispatch<import("react").SetStateAction<Partial<Application>>>;
1372
+ // fApplication: Partial<Application>;
1373
+ // draftSaved: boolean;
1374
+ // profileUrl: string | undefined;
1375
+ // successPopup: "submitted" | "draftSaved" | "stageComplete" | "outcome" | undefined;
1376
+ // fApplicationId: string | undefined;
1377
+ // fListing: PlacementListing | false | undefined;
1378
+ // student: UserData | undefined;
1379
+ // fProvider: {
1380
+ // details?: ProviderData;
1381
+ // profile?: string;
1382
+ // id?: string;
1383
+ // } | undefined;
1384
+ // onFApply: (draft?: boolean) => Promise<void>;
1385
+ // setFormComplete: (formId: string, e: {
1386
+ // [key: string]: unknown;
1387
+ // }) => void;
1388
+ // viewFile: (file: string, onOpen: (url: string) => void) => void
1389
+ // addFile: (files: string[], fileId: number) => void;
1390
+ // uploadedFiles?: {
1391
+ // [key: string]: FileItem;
1392
+ // };
1393
+ // getCurrentStage: (stage: number) => Promise<{
1394
+ // stage: ApplicantStage;
1395
+ // completedSections: {
1396
+ // submitted?: string | undefined;
1397
+ // filesViewed?: string[] | undefined;
1398
+ // formsCompleted?: {
1399
+ // [key: string]: unknown;
1400
+ // } | undefined;
1401
+ // filesUploaded?: {
1402
+ // [key: number]: string[];
1403
+ // } | undefined;
1404
+ // };
1405
+ // }>;
1406
+ // currentStageComplete?: boolean;
1407
+ // progressStage: (type: number | "accept" | "reject", e?: {
1408
+ // feedback?: string;
1409
+ // }) => Promise<void>;
1410
+ // };
1411
+ // export function useCreateApplicationRenderer({user, listingId, listing, provider, application, applicationId, orgContext}:
1412
+ // {user:UserData, listingId:string, applicationId?: string, listing?: PlacementListing, application?: Partial<Application>,
1413
+ // orgContext?: {details: ProviderData, addresses: {[key: string]: OrganisationAddress}, applicantWorkflows: {[key: string]: ApplicantWorkflow}},
1414
+ // provider?: {details?: ProviderData, profile?: string, id?: string}}) {
1415
+ // const firebaseQuery = new FirebaseQuery();
1416
+ // let applicationWithoutAdditionalData = {...(application || {})} as any;
1417
+ // delete applicationWithoutAdditionalData.listing;
1418
+ // delete applicationWithoutAdditionalData.address;
1419
+ // delete applicationWithoutAdditionalData.provider;
1420
+ // const [fApplication, setFApplication] = useState<Partial<Application>>(application ? applicationWithoutAdditionalData : {
1421
+ // uid: user.userType === "Students" ? user.id : undefined,
1422
+ // listingId: listingId,
1423
+ // addressId: listing?.addressId,
1424
+ // stage: 1,
1425
+ // reqUserType: "Students",
1426
+ // status: "draft"});
1427
+ // const [fApplicationId, setFApplicationId] = useState<string|undefined>(applicationId);
1428
+ // const [draftSaved, setDraftSaved] = useState(false);
1429
+ // const [fProvider, setFProvider] = useState<{details?: ProviderData, profile?: string, id?: string}|undefined>(provider);
1430
+ // const [fListing, setFListing] = useState<PlacementListing|false|undefined>(Object.keys(listing || {}).length > 5 ? listing : undefined);
1431
+ // const [student, setStudent] = useState<UserData|undefined>(user.userType === "Students" ? user : undefined);
1432
+ // const [profileUrl, setProfileUrl] = useState<string>();
1433
+ // const [successPopup, setSuccessPopup] = useState<"submitted"|"draftSaved"|"stageComplete"|"outcome">();
1434
+ // const [uploadedFiles, setUploadedFiles] = useState<{[key: string]: FileItem}>();
1435
+ // const [currentStageComplete, setCurrentStageComplete] = useState<boolean>();
1436
+ // useEffect(() => {
1437
+ // const getListing = async () => {
1438
+ // console.log("Checking ID")
1439
+ // if (!listingId) return;
1440
+ // console.log("Getting listing")
1441
+ // console.log("LISTING PARAM", listing, Object.keys(listing || {}).length > 5);
1442
+ // const listingData = (Object.keys(listing || {}).length > 5) ? listing : (await firebaseQuery.getDocData(["placementListings", listingId]).catch(() => false) as PlacementListing|false);
1443
+ // console.log("LISTINGDATA", listingData, Object.keys(listing || {}).length > 5 ? {a: "string"} : "AAA");
1444
+ // const address = listingData ? (user.product === "providers" && orgContext) ? orgContext.addresses[listingData.addressId || ""] : await firebaseQuery.getDocData(["addresses", listingData.addressId as string]) as Address : undefined;
1445
+ // const workflow = listingData ? (user.product === "providers" && orgContext) ? orgContext.applicantWorkflows[listingData.applicantWorkflowId || ""] as ApplicantWorkflow : await firebaseQuery.getDocData(["applicantWorkflows", listingData.applicantWorkflowId as string]) as ApplicantWorkflow : undefined;
1446
+ // if (workflow && listingData) {
1447
+ // workflow.workflow = await Promise.all(workflow.workflow.map(async (s) => {
1448
+ // const applicantFiles = s.files ? Object.fromEntries(await Promise.all(s.files?.map(async (fileId: string) => {
1449
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1450
+ // file.url = await getDownloadURL(ref(storage, `providers/${listingData?.providerId}/${file.fileName}`));
1451
+ // return [fileId, file];
1452
+ // }))) : [];
1453
+ // const applicantForms = s.forms ? Object.fromEntries(await Promise.all(s.forms?.map(async (formId: string) => {
1454
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1455
+ // }))) : [];
1456
+ // return {...s, viewableFiles: applicantFiles, formDetails: applicantForms};
1457
+ // }));
1458
+ // delete workflow.id;
1459
+ // }
1460
+ // if (address) {
1461
+ // delete address.id;
1462
+ // }
1463
+ // console.log("Setting listing")
1464
+ // setFListing((listingData && workflow) ? {...listingData, ...address, applicantWorkflow: workflow.workflow} as PlacementListing : false);
1465
+ // if ((fProvider?.id === application?.providerId) && user.product === "providers") {
1466
+ // setFProvider({details: orgContext?.details, id: user.oId});
1467
+ // } else if (listingData && listingData?.providerId) {
1468
+ // console.log("Getting provider from DB");
1469
+ // const provider = await firebaseQuery.getDocData(["providers", listingData.providerId]) as ProviderData;
1470
+ // console.log("Provider", provider);
1471
+ // setFProvider({details: provider, id: listingData.providerId});
1472
+ // }
1473
+ // }
1474
+ // getListing();
1475
+ // }, [listingId, listing]);
1476
+ // useEffect(() => {
1477
+ // if (student?.id !== application?.uid) {
1478
+ // if (user.userType === "Students") {
1479
+ // setStudent(user);
1480
+ // } else if (user.product === "providers" && application?.uid) {
1481
+ // firebaseQuery.getDocData(["users", application.uid]).then((s) => setStudent(s as UserData));
1482
+ // }
1483
+ // }
1484
+ // }, []);
1485
+ // useEffect(() => {
1486
+ // setFListing(Object.keys(listing || {}).length > 5 ? listing : undefined);
1487
+ // }, [listing]);
1488
+ // useEffect(() => {
1489
+ // if (provider?.profile) return;
1490
+ // if (!provider?.id) return;
1491
+ // getDownloadURL(ref(storage, `providers/${provider?.id}/profilePic.png`)).then(setProfileUrl).catch(() => null);
1492
+ // }, [provider]);
1493
+ // useEffect(() => {
1494
+ // console.log("APPLICATIONID", applicationId);
1495
+ // if (!applicationId) {
1496
+ // if (!listingId) return;
1497
+ // firebaseQuery.getDocsWhere("applications", [where("status", "not-in", ["approved", "declined"]), where("uid", "==", user.id), where("listingId", "==", listingId)]).then((existingApplication) => {
1498
+ // console.log("EXISTING", existingApplication);
1499
+ // // get uploaded files
1500
+ // if (existingApplication && Object.keys(existingApplication).length) {
1501
+ // setFApplication(Object.values(existingApplication)[0]);
1502
+ // setFApplicationId(Object.keys(existingApplication)[0]);
1503
+ // }
1504
+ // });
1505
+ // return;
1506
+ // }
1507
+ // if (applicationId) {
1508
+ // setFApplicationId(applicationId);
1509
+ // if (application) {
1510
+ // let applicationWithoutAdditionalData = {...(application || {})} as any;
1511
+ // delete applicationWithoutAdditionalData.listing;
1512
+ // delete applicationWithoutAdditionalData.address;
1513
+ // delete applicationWithoutAdditionalData.provider;
1514
+ // setFApplication(applicationWithoutAdditionalData);
1515
+ // } else {
1516
+ // firebaseQuery.getDocData(["applications", applicationId]).then(setFApplication);
1517
+ // }
1518
+ // }
1519
+ // }, [application, applicationId]);
1520
+ // const getCurrentStage = async (stage: number): Promise<{
1521
+ // stage: ApplicantStage; completedSections: {
1522
+ // submitted?: string | undefined;
1523
+ // filesViewed?: string[] | undefined;
1524
+ // formsCompleted?: {
1525
+ // [key: string]: unknown;
1526
+ // } | undefined;
1527
+ // filesUploaded?: {
1528
+ // [key: number]: string[];
1529
+ // } | undefined;
1530
+ // };
1531
+ // }> => {
1532
+ // console.log("fLSITING CURRENT STAGE", fListing);
1533
+ // if (!fListing) throw new Error("Listing deleted");
1534
+ // if (!fListing?.applicantWorkflowId) throw new Error("No workflow stage");
1535
+ // const mApplicantWorkflow = (await firebaseQuery.getDocData(["applicantWorkflows", fListing.applicantWorkflowId]) as ApplicantWorkflow);
1536
+ // const stageObj = mApplicantWorkflow?.workflow?.find((s) => s.id === stage);
1537
+ // if (!stageObj) throw new Error("Can't find stage.");
1538
+ // const applicantFiles = stageObj.files ? Object.fromEntries(await Promise.all(stageObj.files?.map(async (fileId) => {
1539
+ // const file = await firebaseQuery.getDocData(["files", fileId]);
1540
+ // file.url = await getDownloadURL(ref(storage, `providers/${mApplicantWorkflow?.oId}/${file.fileName}`));
1541
+ // return [fileId, file];
1542
+ // }))) : [];
1543
+ // const applicantForms = stageObj.forms ? Object.fromEntries(await Promise.all(stageObj.forms?.map(async (formId) => {
1544
+ // return [formId, await firebaseQuery.getDocData(["forms", formId])];
1545
+ // }))) : [];
1546
+ // stageObj.viewableFiles = applicantFiles;
1547
+ // stageObj.formDetails = applicantForms;
1548
+ // return {stage: stageObj, completedSections: fApplication?.completedSections?.[stage] || {}};
1549
+ // };
1550
+ // useEffect(() => {
1551
+ // const getUploadedFiles = async () => {
1552
+ // if (!fApplication.completedSections) {
1553
+ // setUploadedFiles({});
1554
+ // return
1555
+ // };
1556
+ // const fileIds = Object.values(fApplication.completedSections)
1557
+ // .flatMap(stageData =>
1558
+ // Object.values(stageData.filesUploaded || {}).flatMap(fileIds => fileIds)
1559
+ // );
1560
+ // const fileDataPromises = fileIds.map(async (fileId) => {
1561
+ // const fileData = await firebaseQuery.getDocData(["files", fileId]) as FileItem;
1562
+ // fileData.url = await getDownloadURL(ref(storage, `userFiles/${fileData.fileName}`));
1563
+ // return [fileId, fileData] as [string, FileItem]
1564
+ // });
1565
+ // const fileDataArray = await Promise.all(fileDataPromises);
1566
+ // const fileDataObj = Object.fromEntries(fileDataArray)
1567
+ // setUploadedFiles(fileDataObj)
1568
+ // }
1569
+ // const addApplication = async () => {
1570
+ // console.log("ADDING APPLICATION");
1571
+ // if (!fListing || !fListing?.id || !fProvider?.id || !student?.id) return;
1572
+ // const applicationData = {
1573
+ // uid: student.id,
1574
+ // listingId: fListing?.id,
1575
+ // addressId: fListing.addressId,
1576
+ // applicantWorkflowId: fListing?.applicantWorkflowId,
1577
+ // providerId: fProvider.id,
1578
+ // stage: 1,
1579
+ // status: "draft",
1580
+ // created: (new Date()).toISOString(),
1581
+ // ...fApplication,
1582
+ // } as Partial<Application>;
1583
+ // const mApplicationId = (await firebaseQuery.add(["applications"], applicationData)).id;
1584
+ // console.log("APPLICATION ADDED");
1585
+ // setFApplicationId(mApplicationId);
1586
+ // setFApplication(applicationData);
1587
+ // setDraftSaved(true);
1588
+ // return;
1589
+ // };
1590
+ // getUploadedFiles();
1591
+ // console.log("Checking IDs");
1592
+ // if (!fListing || !fListing?.id || !fProvider?.id || !student?.id) return;
1593
+ // if (user.product === "providers") return;
1594
+ // console.log("Checking dates and sections");
1595
+ // if (!fApplication.completedSections && !fApplication.startDate && !fApplication.endDate) return;
1596
+ // console.log("Application updated");
1597
+ // if (!fApplicationId && !applicationId) {
1598
+ // // save new
1599
+ // console.log("Add application")
1600
+ // addApplication();
1601
+ // return;
1602
+ // }
1603
+ // // update
1604
+ // firebaseQuery.update(["applications", (fApplicationId || applicationId) as string], fApplication);
1605
+ // setDraftSaved(true);
1606
+ // }, [fApplication]);
1607
+ // const openSuccessPopup = (type: "submitted"|"draftSaved"|"stageComplete"|"outcome") => {
1608
+ // setSuccessPopup(type);
1609
+ // setTimeout(() => {
1610
+ // // onClose();
1611
+ // }, 1500);
1612
+ // };
1613
+ // useEffect(() => {
1614
+ // const areStagesCompleted = async ():Promise<boolean|undefined> => {
1615
+ // if (!fListing) return;
1616
+ // if (fApplication.stage === undefined) return undefined;
1617
+ // const currentStage = await getCurrentStage(fApplication.stage);
1618
+ // console.log("Checking current stage is complete");
1619
+ // for (const fileViewed of currentStage.stage?.files || []) {
1620
+ // console.log("Checking file viewed", fileViewed, "in", currentStage?.completedSections?.filesViewed);
1621
+ // if (!currentStage?.completedSections?.filesViewed?.includes(fileViewed)) {
1622
+ // console.log("File not viewed");
1623
+ // return false;
1624
+ // } else {
1625
+ // console.log("File viewed");
1626
+ // }
1627
+ // }
1628
+ // for (const formCompleted of currentStage?.stage?.forms || []) {
1629
+ // console.log("Checking form completed", formCompleted, "in", currentStage?.completedSections?.formsCompleted);
1630
+ // if (!Object.keys(currentStage?.completedSections?.formsCompleted || {}).includes(formCompleted)) {
1631
+ // console.log("Form not completed");
1632
+ // return false;
1633
+ // } else {
1634
+ // console.log("Form completed")
1635
+ // }
1636
+ // }
1637
+ // for (let i = 0; i++; i < (currentStage?.stage?.requiredFiles || []).length) {
1638
+ // if (!Object.keys(currentStage?.completedSections?.filesUploaded || {}).includes(i.toString())) {
1639
+ // console.log("Form not uploaded");
1640
+ // return false;
1641
+ // } else {
1642
+ // console.log("Form uploaded");
1643
+ // }
1644
+ // }
1645
+ // return true;
1646
+ // };
1647
+ // areStagesCompleted().then(setCurrentStageComplete);
1648
+ // }, [fApplication, fListing])
1649
+ // const viewFile = (file: string, onOpen: (url: string) => void) => {
1650
+ // if (fApplication.reqUserType !== user.userType) return;
1651
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1652
+ // if (!fListing) throw new Error("No associated listing.");
1653
+ // const viewableFiles = fListing?.applicantWorkflow?.find((stage) => stage.id === fApplication.stage)?.viewableFiles;
1654
+ // setFApplication((a) => {
1655
+ // const oldA = {...a};
1656
+ // const viewedFiles = a.completedSections?.[1]?.filesViewed || [];
1657
+ // if (viewedFiles?.includes(file)) return a;
1658
+ // viewableFiles?.[file].url && onOpen(viewableFiles?.[file].url);
1659
+ // viewedFiles?.push(file);
1660
+ // const newA = editNestedObject(["completedSections", 1, "filesViewed"], oldA, viewedFiles) as Partial<Application>;
1661
+ // return newA;
1662
+ // });
1663
+ // };
1664
+ // const addFile = (files: string[], fileId: number) => {
1665
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1666
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1667
+ // if (!files.length) throw new Error("No files to upload");
1668
+ // setFApplication((a) => editNestedObject(["completedSections", fApplication.stage as number, "filesUploaded", fileId], a, files) as Application);
1669
+ // };
1670
+ // const setFormComplete = (formId: string, e: {[key: string]: unknown}) => {
1671
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1672
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1673
+ // setFApplication((a) => editNestedObject(["completedSections", fApplication.stage as number, "formsCompleted", formId], a, e) as Application);
1674
+ // };
1675
+ // const successText = {
1676
+ // submitted: {
1677
+ // title: "Application submitted",
1678
+ // desc: "We've sent your application to the placement provider. You will hear what the next steps are soon.",
1679
+ // },
1680
+ // draftSaved: {
1681
+ // title: "Draft saved",
1682
+ // desc: "Your draft has been saved. Go to the 'Applications' section from your home screen to edit.",
1683
+ // },
1684
+ // stageComplete: {
1685
+ // title: "Stage complete",
1686
+ // desc: "We have sent this to the next stage in the application process. We will update you with any progress.",
1687
+ // },
1688
+ // outcome: {
1689
+ // title: "Outcome submitted",
1690
+ // desc: "We have sent the student an email with the outcome of their application.",
1691
+ // },
1692
+ // };
1693
+ // const onFApply = async (draft?: boolean) => {
1694
+ // if (draft) {
1695
+ // openSuccessPopup("draftSaved")
1696
+ // return;
1697
+ // }
1698
+ // if (!fApplicationId) return;
1699
+ // // Check all items have been filled in.
1700
+ // if (!fApplication.startDate || !fApplication.endDate) throw new Error("Please select dates for your placement.");
1701
+ // await executeCallable("applications-submit", {applicationId: fApplicationId});
1702
+ // const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]) as Application;
1703
+ // setFApplication(newApplication);
1704
+ // openSuccessPopup("submitted")
1705
+ // };
1706
+ // const progressStage = async (type: number|"accept"|"reject", e?: {feedback?: string}) => {
1707
+ // // Check all stages completed.
1708
+ // if (!fApplicationId) return;
1709
+ // if (!currentStageComplete) throw new Error("Complete all forms before submitting.");
1710
+ // if (fApplication.reqUserType !== user.userType) throw new Error(`Incorrect user type. Expected ${fApplication.reqUserType}, got: ${user.userType}`);
1711
+ // if (fApplication.stage === undefined) throw new Error("Missing applciation stage.")
1712
+ // await executeCallable("applications-changeStage", {applicationId: fApplicationId, type: type, feedback: e?.feedback});
1713
+ // const newApplication = await firebaseQuery.getDocData(["applications", fApplicationId]) as Application;
1714
+ // setFApplication(newApplication);
1715
+ // };
1716
+ // return {successText, onFApply, progressStage, currentStageComplete, uploadedFiles, getCurrentStage, setFormComplete, viewFile, addFile, setSuccessPopup, openSuccessPopup, setFApplication, fApplication, draftSaved, profileUrl, successPopup, fApplicationId, fListing, student, fProvider}
1717
+ // }
1602
1718
  function useProposePlacementRenderer({ user, orgContext, placement }) {
1603
1719
  const [businessSectionComplete, setBusinessSectionComplete] = (0, react_1.useState)(false);
1604
1720
  const [addressSectionComplete, setAddressSectionComplete] = (0, react_1.useState)(false);
@@ -1649,11 +1765,9 @@ function useProposePlacementRenderer({ user, orgContext, placement }) {
1649
1765
  }, [cohort]);
1650
1766
  const submitSection = (type, data) => {
1651
1767
  setFormData((f) => {
1652
- const { id, ...values } = data;
1653
1768
  return {
1654
1769
  ...f,
1655
- ...values,
1656
- id: ((placement === null || placement === void 0 ? void 0 : placement.placementId) || (formData === null || formData === void 0 ? void 0 : formData.id))
1770
+ ...data
1657
1771
  };
1658
1772
  });
1659
1773
  if ((placement === null || placement === void 0 ? void 0 : placement.placementId) && type === "dates") {
@@ -1728,7 +1842,7 @@ const useRefDimensions = () => {
1728
1842
  return { dimensions, refCallback };
1729
1843
  };
1730
1844
  exports.useRefDimensions = useRefDimensions;
1731
- const cohortStages = ["info", "name", "placementType", "review", "created"];
1845
+ const cohortStages = ["info", "name", "placementType", "database", "review", "created"];
1732
1846
  const defaultCohortData = {
1733
1847
  name: "",
1734
1848
  stage: "info",
@@ -2422,34 +2536,34 @@ function useGenericWorkflowEditor({ user, initialData, defaultData, onSubmit })
2422
2536
  includedForms, fSubmitWorkflow, openPopup, snackbar, setSnackbar, setMousePosFunc, mousePos, tutorialActive,
2423
2537
  onDeleteArrow, fWorkflowNodes, containerRef, setError, setArrows, setFWorkflowNodes } });
2424
2538
  }
2425
- function useInstitutePlacementListingHandler({ user }) {
2539
+ function useInstituteProviderContactsHandler({ user }) {
2426
2540
  const [emptyCellsWarning, setEmptyCellsWarning] = (0, react_1.useState)(false);
2427
2541
  const [alert, setAlert] = (0, react_1.useState)();
2428
- const requiredFields = ["name", "jobTitle", "email", "addressOne", "city", "postCode", "country"];
2542
+ const requiredFields = ["business", "forename", "email"];
2429
2543
  const { execute } = (0, firebase_1.useExecuteCallableJob)({ user: user });
2430
- const checkData = (placements) => {
2544
+ const checkData = (providerContacts) => {
2431
2545
  setAlert(undefined);
2432
- placements = placements.filter((u) => Object.entries(u).some(([, v]) => v));
2433
- if (!Object.entries(placements)) {
2546
+ providerContacts = providerContacts.filter((u) => Object.entries(u).some(([, v]) => v));
2547
+ if (!Object.entries(providerContacts)) {
2434
2548
  return [];
2435
2549
  }
2436
- console.log("P", placements);
2437
- if (placements.filter((u) => u.email).length < placements.length) {
2550
+ console.log("P", providerContacts);
2551
+ if (providerContacts.filter((u) => u.email).length < providerContacts.length) {
2438
2552
  setAlert({ msg: "Your data contains missing email addresses.", severity: "error" });
2439
2553
  return false;
2440
2554
  }
2441
2555
  let emptyOptionalCell = false;
2442
2556
  let emptyRequiredCell = false;
2443
- for (const placement of placements) {
2444
- if (!checkEmailValidity(placement)) {
2557
+ for (const providerContact of providerContacts) {
2558
+ if (!checkEmailValidity(providerContact)) {
2445
2559
  return false;
2446
2560
  }
2447
- if (Object.keys(placement).includes("")) {
2561
+ if (Object.keys(providerContact).includes("")) {
2448
2562
  setAlert({ msg: "Data cannot be uploaded in unnamed columns.", severity: "error" });
2449
2563
  return false;
2450
2564
  }
2451
- for (const field in placement) {
2452
- if (!placement[field]) {
2565
+ for (const field in providerContact) {
2566
+ if (!providerContact[field]) {
2453
2567
  if (requiredFields.includes(field)) {
2454
2568
  setAlert({ msg: "All users must contain " + requiredFields.join(", "), severity: "error" });
2455
2569
  emptyRequiredCell = true;
@@ -2469,39 +2583,26 @@ function useInstitutePlacementListingHandler({ user }) {
2469
2583
  else {
2470
2584
  setEmptyCellsWarning(false);
2471
2585
  }
2472
- return placements;
2586
+ return providerContacts;
2473
2587
  };
2474
- const checkEmailValidity = (placement) => {
2475
- if (!(0, util_1.validateEmail)(placement.email)) {
2476
- setAlert({ msg: `Error in email formatting: ${placement.email}. Amend errors and reupload.`, severity: "error" });
2588
+ const checkEmailValidity = (providerContact) => {
2589
+ if (!(0, util_1.validateEmail)(providerContact.email)) {
2590
+ setAlert({ msg: `Error in email formatting: ${providerContact.email}. Amend errors and reupload.`, severity: "error" });
2477
2591
  return false;
2478
2592
  }
2479
2593
  return true;
2480
2594
  };
2481
- const uploadPlacements = async (placements) => {
2482
- let fPlacements = [];
2483
- console.log("PP", placements);
2484
- const cleanUpload = checkData(placements);
2595
+ const uploadProviderContacts = async (providers, schoolId) => {
2596
+ let fProviders = [];
2597
+ console.log("PP", providers);
2598
+ const cleanUpload = checkData(providers);
2485
2599
  if (!cleanUpload) {
2486
2600
  return false;
2487
2601
  }
2488
- fPlacements = cleanUpload;
2602
+ fProviders = cleanUpload;
2489
2603
  setAlert(undefined);
2490
- if (fPlacements.length) {
2491
- const formattedPlacements = fPlacements.map((v) => ({
2492
- name: v.provider,
2493
- title: v.jobTitle,
2494
- providerEmail: v.email,
2495
- providerPhone: v.phone,
2496
- ["address-line1"]: v.addressOne,
2497
- ["address-line2"]: v.addressTwo,
2498
- locality: v.city,
2499
- postal_code: v.postcode,
2500
- country: v.country,
2501
- }));
2502
- console.log("P", placements);
2503
- console.log("callable params", { placements: formattedPlacements, instituteId: user.product === "admin" ? "admin" : user.oId });
2504
- execute("placementListing-add", { data: formattedPlacements, instituteId: user.product === "admin" ? "admin" : user.oId });
2604
+ if (fProviders.length) {
2605
+ execute("providerContacts-add", { providerContacts: fProviders, instituteId: user.product === "admin" ? "admin" : user.oId, schoolId: schoolId });
2505
2606
  }
2506
2607
  return true;
2507
2608
  };
@@ -2509,7 +2610,7 @@ function useInstitutePlacementListingHandler({ user }) {
2509
2610
  setAlert(undefined);
2510
2611
  setEmptyCellsWarning(false);
2511
2612
  };
2512
- return ({ ...{ uploadPlacements, alert, onChange } });
2613
+ return ({ ...{ uploadProviderContacts, alert, onChange } });
2513
2614
  }
2514
2615
  function useGetIndividualPlacementForPlacementPage({ user, placementId, organisation }) {
2515
2616
  var _a;
@@ -2523,7 +2624,7 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2523
2624
  const [disableEmail, setDisableEmail] = (0, react_1.useState)({ parent: false, provider: false });
2524
2625
  const [rejectELIPopup, setRejectELIPopup] = (0, react_1.useState)(false);
2525
2626
  const [eliPopupOpen, setEliPopupOpen] = (0, react_1.useState)(false);
2526
- const [eliURL, setELIURL] = (0, react_1.useState)("");
2627
+ const [eliData, setELIData] = (0, react_1.useState)();
2527
2628
  const [rejectExternalDocPopup, setRejectExternalDocPopup] = (0, react_1.useState)(false);
2528
2629
  const [externalDocPopupOpen, setExternalDocPopupOpen] = (0, react_1.useState)(false);
2529
2630
  const [riskAssessmentURL, setRiskAssessmentURL] = (0, react_1.useState)("");
@@ -2674,10 +2775,10 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2674
2775
  setShareStudentRequestPopup(true);
2675
2776
  }
2676
2777
  if (e === "noInsurance") {
2677
- if (!eliURL) {
2678
- const storageRef = (0, storage_1.ref)(firebaseConfig_1.storage, `insurance/${placement.providerId}.pdf`);
2679
- const file = await (0, storage_1.getDownloadURL)(storageRef);
2680
- setELIURL(file);
2778
+ if (!eliData) {
2779
+ const storageRef = (0, storage_1.ref)(firebaseConfig_1.storage, `insurance/${placement.providerContactId}.pdf`);
2780
+ const file = await (0, storage_1.getDownloadURL)(storageRef).catch(() => placement.insuranceSkippedReason);
2781
+ setELIData(file);
2681
2782
  }
2682
2783
  setEliPopupOpen(true);
2683
2784
  }
@@ -2710,14 +2811,14 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2710
2811
  const approveELI = async () => {
2711
2812
  if (!placement)
2712
2813
  return;
2713
- await (0, firebase_1.executeCallable)("insurance-approve", { oId: user.oId, providerId: placement.providerId });
2814
+ await (0, firebase_1.executeCallable)("insurance-approve", { oId: user.oId, providerContactId: placement.providerContactId });
2714
2815
  setEliPopupOpen(false);
2715
2816
  };
2716
2817
  const rejectELI = async ({ reason }) => {
2717
2818
  if (!placement || !institute)
2718
2819
  return;
2719
- console.log("Reject", { reason: reason, placement: placement, instituteName: institute.name, staffEmail: user.email });
2720
- await (0, firebase_1.executeCallable)("insurance-reject", { reason: reason, placementId: placementId, instituteName: institute.name, staffEmail: user.email });
2820
+ console.log("Reject", { reason: reason, placement: placement, instituteName: institute.name });
2821
+ await (0, firebase_1.executeCallable)("insurance-reject", { reason: reason, placementId: placementId, instituteName: institute.name });
2721
2822
  setRejectELIPopup(false);
2722
2823
  setEliPopupOpen(false);
2723
2824
  };
@@ -2736,10 +2837,12 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2736
2837
  if (!placement)
2737
2838
  return;
2738
2839
  if (!placement.providerId) {
2739
- const res = await (0, firebase_1.executeCallable)("placement-uploadProviderDetails", {
2740
- pId: placementId,
2741
- placement: Object.fromEntries(Object.entries(placement).filter(([k]) => k !== "contactId")),
2742
- stage: wStage,
2840
+ const res = await (0, firebase_1.executeCallable)("providerContacts-uploadProviderDetails", {
2841
+ placement: {
2842
+ id: placementId,
2843
+ data: Object.fromEntries(Object.entries(placement).filter(([k]) => k !== "contactId")),
2844
+ stage: wStage,
2845
+ },
2743
2846
  skipSearch: true
2744
2847
  }).catch((e) => {
2745
2848
  throw e;
@@ -2765,7 +2868,7 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
2765
2868
  throw new Error("Must be a student to withdraw.");
2766
2869
  await (0, firebase_1.executeCallable)("placement-withdraw", { placementId: placementId });
2767
2870
  };
2768
- return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, addOnboardingDocsPopup, setAddOnboardingDocsPopup, dismissOnboardingPopup, setDismissOnboardingPopup, 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, shareStudentRequestPopup, setShareStudentRequestPopup };
2871
+ return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, addOnboardingDocsPopup, setAddOnboardingDocsPopup, dismissOnboardingPopup, setDismissOnboardingPopup, setWithdrawFromPlacementPopup, withdrawFromPlacement, onFlagClick, setUploadInsurance, setUploadProviderDocPopup, setUploadRA, setUploadDBS, onboardingStatus, setSkipStagePopup, onboardingPopup, setViewExternalLinkPopup, setOnboardingPopup, setRejectELIPopup, eliData, riskAssessmentURL, dbsCheckURL, setExternalLinkCopied, skipStagePopup, snackbar, setSnackbar, cohort, disableEmail, rejectELIPopup, eliPopupOpen, rejectExternalDocPopup, externalDocPopupOpen, viewExternalLinkPopup, externalLinkCopied, uploadInsurance, uploadRA, uploadDBS, editStage, sendEmail, canEdit, approveELI, setEliPopupOpen, uploadProviderDocPopup, rejectELI, setRejectExternalDocPopup, setExternalDocPopupOpen, approveProviderDoc, rejectProviderDoc, manuallyConfigureProvider, institute, shareStudentRequestPopup, setShareStudentRequestPopup };
2769
2872
  }
2770
2873
  function useOnboardingPopup({ onboarding, providerId, placementId, user, onClose }) {
2771
2874
  const [fileUploadPopup, setFileUploadPopup] = (0, react_1.useState)(false);
@@ -3041,13 +3144,7 @@ function useLoadListings(user, queryConstraint, request) {
3041
3144
  }), "placementListings", constraints, undefined, true);
3042
3145
  };
3043
3146
  (0, react_1.useEffect)(() => {
3044
- let unsubscribe;
3045
3147
  loadListings();
3046
- return () => {
3047
- if (unsubscribe) {
3048
- unsubscribe(); // Unsubscribe from the snapshot listener when the component unmounts
3049
- }
3050
- };
3051
3148
  // eslint-disable-next-line react-hooks/exhaustive-deps
3052
3149
  }, [queryConstraints]);
3053
3150
  const onScrollBottom = () => {
@@ -3123,13 +3220,7 @@ function useLoadProviderPlacements(user, queryConstraint, placementId) {
3123
3220
  }), "placements", constraints, undefined, true);
3124
3221
  };
3125
3222
  (0, react_1.useEffect)(() => {
3126
- let unsubscribe;
3127
3223
  loadListings();
3128
- return () => {
3129
- if (unsubscribe) {
3130
- unsubscribe(); // Unsubscribe from the snapshot listener when the component unmounts
3131
- }
3132
- };
3133
3224
  // eslint-disable-next-line react-hooks/exhaustive-deps
3134
3225
  }, [queryConstraints]);
3135
3226
  const onScrollBottom = () => {
@@ -3209,13 +3300,7 @@ function useLoadApplications({ user, applicationType, listingId, queryConstraint
3209
3300
  }), "applications", constraints, undefined, true);
3210
3301
  };
3211
3302
  (0, react_1.useEffect)(() => {
3212
- let unsubscribe;
3213
3303
  loadApplications();
3214
- return () => {
3215
- if (unsubscribe) {
3216
- unsubscribe(); // Unsubscribe from the snapshot listener when the component unmounts
3217
- }
3218
- };
3219
3304
  // eslint-disable-next-line react-hooks/exhaustive-deps
3220
3305
  }, [type, queryConstraints]);
3221
3306
  const onScrollBottom = () => {
@@ -3226,7 +3311,7 @@ function useLoadApplications({ user, applicationType, listingId, queryConstraint
3226
3311
  };
3227
3312
  return { applications, type, setType, onScrollBottom, loading, changeQueryConstraints };
3228
3313
  }
3229
- function useDataViewerPaginator({ view: initialView, sorts, queryLimit, additionalEntryProcessing, formatItems, snapshot, filters: initialFilters, onSearch, data }) {
3314
+ function useDataViewerPaginator({ view: initialView, sorts, queryLimit = 10, additionalEntryProcessing, formatItems, snapshot, filters: initialFilters, onSearch, data }) {
3230
3315
  const [tableData, setTableData] = (0, react_1.useState)(Array.isArray(data) ? Object.fromEntries(Object.entries(data).slice(0, queryLimit)) : {});
3231
3316
  const [page, setPage] = (0, react_1.useState)([1, 0]);
3232
3317
  const [view, setView] = (0, react_1.useState)(initialView);
@@ -3279,8 +3364,7 @@ function useDataViewerPaginator({ view: initialView, sorts, queryLimit, addition
3279
3364
  }
3280
3365
  };
3281
3366
  const getDataFromQuery = async (itemList = {}, currentQueryAnchor = queryAnchor, cursorDirection, prevEntries = prevEntryIds, loadMoreFromQuery = false) => {
3282
- if (!filters)
3283
- return;
3367
+ // if (!filters) return;
3284
3368
  setLoading(true);
3285
3369
  if (!Array.isArray(data)) {
3286
3370
  setTableDataFromDefinedData();
@@ -3291,8 +3375,8 @@ function useDataViewerPaginator({ view: initialView, sorts, queryLimit, addition
3291
3375
  if (onSearch && (searchString || sort)) {
3292
3376
  if (typeof onSearch === "boolean")
3293
3377
  throw new Error("When using Firestore queries, an onSearch function should be passed to retrieve data externally. Additional processing is however completed in this hook.");
3294
- const data = await onSearch(searchString, sort, page[0], filters, queryLimit);
3295
- const dataWithAdditionalProcessing = await Promise.all(Object.entries(data).map(async ([k, v]) => [k, await processedData(k, v)]));
3378
+ const searchData = await onSearch(searchString, sort, page[0], filters, queryLimit);
3379
+ const dataWithAdditionalProcessing = await Promise.all(Object.entries(searchData).map(async ([k, v]) => [k, await processedData(k, v)]));
3296
3380
  setTableData((old) => {
3297
3381
  if (view === "table") {
3298
3382
  return { ...Object.fromEntries(dataWithAdditionalProcessing) };
@@ -3432,8 +3516,11 @@ function useDataViewerPaginator({ view: initialView, sorts, queryLimit, addition
3432
3516
  Object.keys(itemList).length === 0 &&
3433
3517
  currentQueryAnchor.endQueryPos + 1 === data.length &&
3434
3518
  page[0] > 1) {
3435
- setTableData({});
3519
+ if (view === "table") {
3520
+ setTableData({});
3521
+ }
3436
3522
  setQueryAnchor((a) => ({ ...a, startKey: "" }));
3523
+ setLoading("loaded");
3437
3524
  return;
3438
3525
  }
3439
3526
  if (querySnapshot.size < queryLimit) {
@@ -3468,8 +3555,21 @@ function useDataViewerPaginator({ view: initialView, sorts, queryLimit, addition
3468
3555
  reset();
3469
3556
  }, [filters]);
3470
3557
  (0, react_1.useEffect)(() => {
3558
+ console.log("View reset.");
3559
+ reset();
3560
+ }, [view]);
3561
+ (0, react_1.useEffect)(() => {
3562
+ console.log("search reset.");
3563
+ reset();
3564
+ }, [searchString]);
3565
+ (0, react_1.useEffect)(() => {
3566
+ console.log("data reset.");
3567
+ reset();
3568
+ }, [data]);
3569
+ (0, react_1.useEffect)(() => {
3570
+ console.log("sort reset.");
3471
3571
  reset();
3472
- }, [view, searchString, data, sort]);
3572
+ }, [sort]);
3473
3573
  // Fetch new data when queries or page change
3474
3574
  (0, react_1.useEffect)(() => {
3475
3575
  getDataFromQuery();