placementt-core 11.0.914 → 11.0.951

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/src/hooks.tsx CHANGED
@@ -22,6 +22,7 @@ import {
22
22
  import algoliasearch from "algoliasearch";
23
23
  import {getDownloadURL, ref} from "firebase/storage";
24
24
  import {OrganisationAddress} from "placementt-core";
25
+ import {objectsEqualNew} from "./util";
25
26
 
26
27
 
27
28
  type StudentPlacementListParams = {
@@ -161,6 +162,9 @@ type NewInstitutePlacementParams = {
161
162
  inProgress?: boolean,
162
163
  view: "list"|"table",
163
164
  filters?: FilterObject,
165
+ initialSort?: string,
166
+ initialSearch?: string
167
+ uid?: string,
164
168
  }
165
169
 
166
170
 
@@ -323,7 +327,7 @@ export function useOldInstitutePlacementList({id, user, cohort, queryConstraint,
323
327
  }
324
328
 
325
329
 
326
- export function useNewInstitutePlacementList({id, user, filters, view, cohort, queryConstraints, ql=DEFAULTQUERYLIMIT, inProgress}:NewInstitutePlacementParams) {
330
+ export function useNewInstitutePlacementList({id, user, uid, filters, initialSort, initialSearch, view, cohort, queryConstraints, ql=DEFAULTQUERYLIMIT, inProgress}:NewInstitutePlacementParams) {
327
331
  const [query, setQuery] = useState<QueryObject[]>();
328
332
 
329
333
  const sorts:Sorts = {
@@ -377,14 +381,13 @@ export function useNewInstitutePlacementList({id, user, filters, view, cohort, q
377
381
  return false;
378
382
  }
379
383
  }
380
- console.log("Valid placement", k);
381
384
  return {...placement, id: k};
382
385
  }
383
386
 
384
-
385
- 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)})
387
+ const {tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort} = useDataViewerPaginator({view, filters, sorts, queryLimit: ql, initialSort, initialSearch, data: query, additionalEntryProcessing: additionalProcessing, onSearch: async (s, sort, page, filters, limit) => await algoliaPlacementSearch(query || [], user, s, sort, page, filters, limit, cohort, inProgress)})
386
388
 
387
389
  useEffect(() => {
390
+ console.log("SET QUERY", user, queryConstraints, cohort);
388
391
  // Sets the query of for the DataViewerPaginator
389
392
  if(user.product !== "institutes" || user.userType !== "Staff") {
390
393
  setQuery(undefined);
@@ -394,6 +397,8 @@ export function useNewInstitutePlacementList({id, user, filters, view, cohort, q
394
397
  const constraints:QueryObjectConstraint = [["oId", "==", user.oId], ["draft", "==", false]];
395
398
 
396
399
  cohort && constraints.push(["cohort", "==", cohort]);
400
+ uid && constraints.push(["uid", "==", uid]);
401
+
397
402
  queryConstraints && constraints.unshift(...queryConstraints);
398
403
  inProgress !== undefined && constraints.push(["inProgress", "==", inProgress]);
399
404
 
@@ -1044,25 +1049,26 @@ const algoliaUsersSearch = async (data: QueryObject[], user: UserData, query?: s
1044
1049
  if (!constraints?.length) return {};
1045
1050
 
1046
1051
  let userSearchString = constraints.map(([k, e, v]) => {
1047
- if (e === "==") return `${k}:"${v}"`; // Equality check
1048
- if (e === "!=") return `${k}:-"${v}"`; // Not equal check
1052
+ if (e === "==") return `${k}:${v}`; // Equality check
1053
+ if (e === "!=") return `${k}:-${v}`; // Not equal check
1049
1054
  if (e === "<") return `${k}:<${v}`; // Less than check
1050
1055
  if (e === "<=") return `${k}:<=${v}`; // Less than or equal check
1051
1056
  if (e === ">") return `${k}:>${v}`; // Greater than check
1052
1057
  if (e === ">=") return `${k}:>=${v}`; // Greater than or equal check
1053
- if (e === "array-contains") return `${k}:"${v}"`; // Array contains check (string format)
1058
+ if (e === "array-contains") return `${k}:${v}`; // Array contains check (string format)
1054
1059
  if (e === "array-contains-any") return `${k}:"${(v as string[]).join('","')}"`; // Array contains any of the values (string format)
1055
- if (e === "in") return `${k}:(${(v as string[]).join('","')})`; // In check
1056
- if (e === "not-in") return `${k}:(-${(v as string[]).join('","')})`; // Not in check
1060
+ if (e === "in") return `(${(v as string[]).map((value) => `${k}:${value}`).join(' OR ')})`; // In check
1061
+ if (e === "not-in") return (v as string[]).map((value) => `${k}:-${value}`).join(' AND '); // In check
1057
1062
  return;
1058
1063
  }).join(" AND ");
1059
1064
 
1060
- console.log("QUERY", userSearchString);
1061
-
1062
1065
  filters && Object.entries(filters).filter(([, filter]) => filter.value).map(([id, filter]) => {
1063
1066
  userSearchString = userSearchString + ` AND ${id}:${filter.value}`;
1064
1067
  });
1065
1068
 
1069
+ console.log("QUERY", userSearchString);
1070
+
1071
+
1066
1072
  const options = {
1067
1073
  filters: userSearchString,
1068
1074
  hitsPerPage: limit,
@@ -1089,11 +1095,12 @@ type NewUserPaginatorParams = {
1089
1095
  view: "list"|"table",
1090
1096
  filters?: FilterObject,
1091
1097
  institute: InstituteData,
1092
- userType: "Staff"|"Students"
1098
+ userType: "Staff"|"Students",
1099
+ initialSort?: string
1093
1100
  }
1094
1101
 
1095
1102
 
1096
- export function useNewCohortUserPaginator({user, institute, filters, view, cohort, queryConstraints, ql=DEFAULTQUERYLIMIT, userType}:NewUserPaginatorParams) {
1103
+ export function useNewCohortUserPaginator({user, institute, initialSort, filters, view, cohort, queryConstraints, ql=DEFAULTQUERYLIMIT, userType}:NewUserPaginatorParams) {
1097
1104
  const [query, setQuery] = useState<QueryObject[]>();
1098
1105
 
1099
1106
  const firebaseQuery = new FirebaseQuery();
@@ -1202,7 +1209,7 @@ export function useNewCohortUserPaginator({user, institute, filters, view, cohor
1202
1209
  }, [user, queryConstraints, cohort])
1203
1210
 
1204
1211
 
1205
- 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)})
1212
+ const {tableData, pageUp, pageDown, setFilters, page, setView, loading, updateSearch, updateSort, sort} = useDataViewerPaginator({view, filters, initialSort, sorts, queryLimit: ql, data: query, onSearch: async (s, sort, page, filters, limit) => await algoliaUsersSearch(query || [], user, s, sort, page, filters, limit, cohort)})
1206
1213
 
1207
1214
 
1208
1215
  return {tableData, page, loading, updateSearch, setFilters, setView, pageUp, pageDown, sorts, updateSort, sort}
@@ -4129,10 +4136,12 @@ export type DataViewerPaginater = {
4129
4136
  onSearch?: boolean | ((search?: string, sort?: [string, {value: string, direction: "asc"|"desc"}], page?: number, filters?:FilterObject, limit?: number) => Promise<{ [key: string]: any }>);
4130
4137
  data?: {[key:string]:{[key:string]: unknown}}|QueryObject[];
4131
4138
  additionalEntryProcessing?: (k: string, v: any) => Promise<any> | any,
4132
- sorts?: Sorts
4139
+ sorts?: Sorts,
4140
+ initialSort?: string,
4141
+ initialSearch?: string
4133
4142
  }
4134
4143
 
4135
- export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10, additionalEntryProcessing, formatItems, snapshot, filters: initialFilters, onSearch, data}:DataViewerPaginater) {
4144
+ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10, additionalEntryProcessing, formatItems, snapshot, filters: initialFilters, initialSort, initialSearch, onSearch, data}:DataViewerPaginater) {
4136
4145
  const [tableData, setTableData] = useState<{[key:string]:{[key:string]: unknown}}>(Array.isArray(data) ? Object.fromEntries(Object.entries(data).slice(0, queryLimit)) : {});
4137
4146
  const [page, setPage] = useState([1, 0]);
4138
4147
  const [view, setView] = useState(initialView);
@@ -4142,16 +4151,18 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4142
4151
  const [dataListenerUnsubscribe, setDataListenerUnsubscribe] = useState<{[key: string] : Unsubscribe}>();
4143
4152
  const [loading, setLoading] = useState<boolean|"loaded">(true);
4144
4153
  const [searchString, setSearchString] = useState<string>();
4145
- const [sort, setSort] = useState<[string, {value: string, direction: "asc"|"desc"}]>();
4154
+ const [sort, setSort] = useState<[string, {value: string, direction: "asc"|"desc"}]|undefined>((initialSort && sorts && sorts[initialSort]) ? [initialSort, sorts[initialSort]] : undefined);
4155
+ const [fData, setFData] = useState(data);
4146
4156
 
4157
+ console.log("SORT", sort, initialSort, sorts, sorts?.[initialSort || ""]);
4147
4158
 
4148
4159
 
4149
4160
  const processedData = async (k: string, v: any) => additionalEntryProcessing ? await additionalEntryProcessing(k, v) : v;
4150
4161
 
4151
4162
  const setTableDataFromDefinedData = async () => {
4152
- if (!data || Array.isArray(data)) return;
4163
+ if (!fData || Array.isArray(fData)) return;
4153
4164
 
4154
- const dataWithAdditionalProcessingPossibleNulls: [string, any][] = (await Promise.all(Object.entries(data).map(async ([k, v]) => [k, await processedData(k, v)])))
4165
+ const dataWithAdditionalProcessingPossibleNulls: [string, any][] = (await Promise.all(Object.entries(fData).map(async ([k, v]) => [k, await processedData(k, v)])))
4155
4166
  const dataWithAdditionalProcessing = dataWithAdditionalProcessingPossibleNulls.filter(([k, v]) => v);
4156
4167
 
4157
4168
  const searchedData: [string, any][] = searchString ? dataWithAdditionalProcessing.filter(([, v]) => {
@@ -4173,7 +4184,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4173
4184
  if (!queryLimit) throw new Error("Tables must have a limit defined.");
4174
4185
  const newData:[string, any][] = filteredData.slice((page[0] - 1)*queryLimit, page[0]*queryLimit);
4175
4186
  setTableData(Object.fromEntries(newData));
4176
- if (Object.keys(Object.fromEntries(newData)).pop() === Object.keys(data).pop()) {
4187
+ if (Object.keys(Object.fromEntries(newData)).pop() === Object.keys(fData).pop()) {
4177
4188
  setLoading("loaded");
4178
4189
  } else {
4179
4190
  setLoading(false);
@@ -4191,7 +4202,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4191
4202
 
4192
4203
  // if (!filters) return;
4193
4204
  setLoading(true);
4194
- if (!Array.isArray(data)) {
4205
+ if (!Array.isArray(fData)) {
4195
4206
  setTableDataFromDefinedData();
4196
4207
  return;
4197
4208
  }
@@ -4226,7 +4237,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4226
4237
  cursorPos = currentQueryAnchor.startQueryPos;
4227
4238
  }
4228
4239
 
4229
- const querySchema:QueryObject = data[cursorPos];
4240
+ const querySchema:QueryObject = fData[cursorPos];
4230
4241
 
4231
4242
  const createQuery = (queryData:QueryObject) => {
4232
4243
  const constraints:any[] = [];
@@ -4288,7 +4299,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4288
4299
 
4289
4300
  // Function to handle query snapshot
4290
4301
  async function handleQuerySnapshot(querySnapshot: QuerySnapshot) {
4291
- if (!Array.isArray(data)) throw new Error("Called querySnapshot but data is defined.");
4302
+ if (!Array.isArray(fData)) throw new Error("Called querySnapshot but data is defined.");
4292
4303
  if (!queryLimit) throw new Error("Firestore queries must have a limit defined.");
4293
4304
 
4294
4305
  const queryResults: { [key: string]: { [key: string]: unknown } } = {};
@@ -4343,7 +4354,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4343
4354
 
4344
4355
  if (querySnapshot.size < queryLimit && Object.keys(itemList).length < queryLimit) {
4345
4356
  // If we have ran out of entries, increase or decrease the index.
4346
- if (page[0] > page[1] && cursorPos+1 < data.length) {
4357
+ if (page[0] > page[1] && cursorPos+1 < fData.length) {
4347
4358
  return getDataFromQuery(itemList, {...currentQueryAnchor, endQueryPos: currentQueryAnchor.endQueryPos+1}, "increase", prevEntries);
4348
4359
  } else if (page[0] < page[1] && cursorPos > 0) {
4349
4360
  return getDataFromQuery(itemList, {...currentQueryAnchor, startQueryPos: currentQueryAnchor.startQueryPos-1}, "decrease", prevEntries);
@@ -4357,7 +4368,7 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4357
4368
 
4358
4369
  if (querySnapshot.size === 0 &&
4359
4370
  Object.keys(itemList).length === 0 &&
4360
- currentQueryAnchor.endQueryPos+1 === data.length &&
4371
+ currentQueryAnchor.endQueryPos+1 === fData.length &&
4361
4372
  page[0] > 1) {
4362
4373
  if (view === "table") {
4363
4374
  setTableData({});
@@ -4395,31 +4406,34 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4395
4406
  };
4396
4407
 
4397
4408
  useEffect(() => {
4398
- console.log("Filters updates", filters);
4399
- if (!filters) return;
4400
- console.log("Resetting cus filters");
4401
- reset();
4402
- }, [filters]);
4403
-
4404
- useEffect(() => {
4405
- console.log("View reset.")
4409
+ if (objectsEqualNew(fData, data)) return;
4410
+ console.log("data reset.", fData, data);
4411
+ setFData(data);
4406
4412
  reset();
4407
- }, [view]);
4413
+ }, [data]);
4408
4414
 
4409
- useEffect(() => {
4410
- console.log("search reset.")
4411
- reset();
4412
- }, [searchString]);
4415
+ const updateFilters = (fFilters?: FilterObject) => {
4416
+ if (!objectsEqualNew(fFilters, filters)) {
4417
+ console.log("FILTER RESET", fFilters, filters);
4418
+ setFilters(fFilters);
4419
+ reset();
4420
+ }
4421
+ }
4413
4422
 
4414
- useEffect(() => {
4415
- console.log("data reset.")
4423
+
4424
+ const updateSearch = (search: string) => {
4425
+ if (searchString === search) return;
4426
+ console.log("SEARCH RESET", searchString, search);
4427
+ setSearchString(search);
4416
4428
  reset();
4417
- }, [data]);
4429
+ }
4418
4430
 
4419
- useEffect(() => {
4420
- console.log("sort reset.")
4431
+ const updateView = (v: "list" | "table") => {
4432
+ if (v === view) return;
4433
+ console.log("View reset", view, v);
4434
+ setView(v);
4421
4435
  reset();
4422
- }, [sort]);
4436
+ }
4423
4437
 
4424
4438
 
4425
4439
  // Fetch new data when queries or page change
@@ -4436,10 +4450,15 @@ export function useDataViewerPaginator({view: initialView, sorts, queryLimit=10,
4436
4450
  setPage((p) => ([p[0]-1, p[0]]));
4437
4451
  };
4438
4452
 
4439
- const updateSort = (sortLabel: string) => {
4440
- if (!sorts || !sorts[sortLabel]) return;
4441
- setSort([sortLabel, sorts[sortLabel]]);
4453
+ const updateSort = (sortLabel?: string) => {
4454
+ if (!sortLabel) {
4455
+ setSort(undefined);
4456
+ } else {
4457
+ if (!sorts || !sorts[sortLabel]) return;
4458
+ setSort([sortLabel, sorts[sortLabel]]);
4459
+ }
4460
+ reset();
4442
4461
  }
4443
4462
 
4444
- return ({...{tableData, pageUp, pageDown, setFilters, page: page[0], sorts, loading, sort, updateSort: updateSort, setView, updateSearch: setSearchString}});
4463
+ return ({...{tableData, pageUp, pageDown, search: searchString, setFilters: updateFilters, page: page[0], sorts, loading, sort, updateSort: updateSort, setView: updateView, updateSearch}});
4445
4464
  };
@@ -1,6 +1,7 @@
1
1
  import {DocumentData, DocumentReference, Timestamp} from "firebase/firestore";
2
2
  import {Descendant} from "slate";
3
3
  import {emailTemplates} from "./constants";
4
+ import {FilterObject} from "./hooks";
4
5
 
5
6
  export type Products = "institutes"|"providers"|"students"|"admin";
6
7
 
@@ -369,9 +370,24 @@ export type UserData = {
369
370
  provider: number[],
370
371
  student: number[],
371
372
  }},
372
- alumniConversationUid?: string
373
+ alumniConversationUid?: string,
374
+ savedDataViewerFilters?: {
375
+ [cohortId: string]: {
376
+ [key in "cohortPlacements"|"cohortStudents"]: {
377
+ [viewKey: string]: DataViewerFilterView
378
+ }
379
+ }
380
+ }
373
381
  };
374
382
 
383
+ export type DataViewerFilterView = {
384
+ title: string,
385
+ search?: string,
386
+ sort?: string,
387
+ filters?: FilterObject,
388
+ permanent?: boolean,
389
+ }
390
+
375
391
  export type AnalyticsItem = {
376
392
  units?: "placements"|"weeks"|"days"|"hours",
377
393
  name?: string
@@ -798,6 +814,11 @@ export type CohortData = {
798
814
  recurringEmails?: {
799
815
  sendType?: "all"|"lastWeek",
800
816
  frequency?: "oneTime"|"weeklyMonday"|"fortnightlyMonday"
817
+ },
818
+ savedDataViewerFilters?: {
819
+ [key in "cohortPlacements"|"cohortStudents"]: {
820
+ [key: string]: DataViewerFilterView
821
+ }
801
822
  }
802
823
  }
803
824
 
@@ -1028,7 +1049,7 @@ export type EmailTemplateConfig = {
1028
1049
  description: string,
1029
1050
  params: string[],
1030
1051
  button?: {text: string, link: string},
1031
- type: "workflow"|"feedback"
1052
+ type: "workflow"|"feedback"|"event"
1032
1053
  }
1033
1054
 
1034
1055
 
@@ -1071,10 +1092,16 @@ export type SchoolData = OrganisationAddress&{
1071
1092
  export type ExternalEvent = {
1072
1093
  name: string,
1073
1094
  description: string,
1095
+ staffInformation?: string,
1096
+ studentInformation?: string,
1097
+ employerInformation?: string
1098
+ designatedStaff: string,
1074
1099
  created: string,
1075
1100
  activityId?: string,
1101
+ gatsbyBenchmarks?: string, // String array
1076
1102
  oId: string,
1077
1103
  submissionClose: string,
1104
+ acceptingEmployers?: boolean,
1078
1105
  schoolId?: string,
1079
1106
  startDate?: string,
1080
1107
  endDate?: string,
@@ -1094,7 +1121,24 @@ export type ExternalEvent = {
1094
1121
  employers: {
1095
1122
  shareable?: boolean,
1096
1123
  filters: {[key: string]: unknown}
1124
+ },
1125
+ employerFeedback: {
1126
+ files: string[],
1127
+ forms: string[],
1128
+ requiredFiles: {
1129
+ fileName: string,
1130
+ fileType: string
1131
+ }[],
1132
+ },
1133
+ studentFeedback: {
1134
+ files: string[],
1135
+ forms: string[],
1136
+ requiredFiles: {
1137
+ fileName: string,
1138
+ fileType: string
1139
+ }[],
1097
1140
  }
1141
+ completedStages: ("basicDetails"|"employerInvites"|"studentInvites"|"employerSelection"|"feedback")[]
1098
1142
  }&Address
1099
1143
 
1100
1144
 
@@ -1172,4 +1216,24 @@ export type Update = {
1172
1216
  buttonLink?: string,
1173
1217
  date: string,
1174
1218
  imageData?: boolean
1219
+ }
1220
+
1221
+
1222
+
1223
+ export type ExternalEventAttendee = {
1224
+ oId: string,
1225
+ eventId: string,
1226
+ providerContactId: string,
1227
+ status: "invited"|"providerAvailable"|"finalConfirmationSent"|"providerConfirmed"|"feedbackSent"
1228
+ emails: {
1229
+ [k in "invited"|"finalConfirmationSent"|"feedbackSent"]: {
1230
+ sent: number, // If below 3, keep sending reminders.
1231
+ reconciled: string|boolean,
1232
+ log: {
1233
+ [k: string]: string // emailNumber_dateTimeString : eventType
1234
+ }
1235
+ }
1236
+ }
1237
+ attended?: boolean,
1238
+ feedback: {[key: string]: unknown}
1175
1239
  }