placementt-core 1.400.1000 → 1.400.1003
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/features/search/globalSearch.d.ts +116 -0
- package/lib/features/search/globalSearch.js +156 -0
- package/lib/features/search/globalSearch.js.map +1 -0
- package/lib/hooks.d.ts +0 -4
- package/lib/hooks.js +1 -3
- package/lib/hooks.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/typeDefinitions.d.ts +2 -0
- package/package.json +1 -1
- package/src/features/search/globalSearch.ts +243 -0
- package/src/hooks.tsx +1 -3
- package/src/index.ts +1 -0
- package/src/typeDefinitions.ts +2 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* globalSearch — one query box, several Algolia indexes.
|
|
3
|
+
*
|
|
4
|
+
* The navbar search sends whatever the user typed here. Every index that the
|
|
5
|
+
* signed-in institute can see is queried in a single round trip, and the answer
|
|
6
|
+
* comes back keyed by index so the UI can render a section per index.
|
|
7
|
+
*
|
|
8
|
+
* Adding an index is meant to be a single entry in GLOBAL_SEARCH_INDEXES: name
|
|
9
|
+
* the Algolia index, say when an institute can see it, restrict it to that
|
|
10
|
+
* institute's records, and describe how one hit becomes a row.
|
|
11
|
+
*/
|
|
12
|
+
import { InstituteData, UserData } from "../../typeDefinitions";
|
|
13
|
+
/** How many results a section shows before the user drills into one index. */
|
|
14
|
+
export declare const GLOBAL_SEARCH_OVERVIEW_HITS = 5;
|
|
15
|
+
/** How many results one index shows per page once the user has drilled in. */
|
|
16
|
+
export declare const GLOBAL_SEARCH_INDEX_HITS = 10;
|
|
17
|
+
export type GlobalSearchIndexId = "placements" | "employers" | "students";
|
|
18
|
+
/** One row, flattened out of whatever the underlying index stores. */
|
|
19
|
+
export type GlobalSearchHit = {
|
|
20
|
+
/** The Algolia objectID, which is the Firestore document id. */
|
|
21
|
+
id: string;
|
|
22
|
+
/** The bold line. */
|
|
23
|
+
title: string;
|
|
24
|
+
/** The line underneath, if there is anything worth saying. */
|
|
25
|
+
subtitle?: string;
|
|
26
|
+
/** A short right-hand label — a status, a year group, a sector. */
|
|
27
|
+
meta?: string;
|
|
28
|
+
/** Where clicking the row goes. */
|
|
29
|
+
path: string;
|
|
30
|
+
/** The untouched hit, for callers that need more than the fields above. */
|
|
31
|
+
raw: {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** The results for a single index. */
|
|
36
|
+
export type GlobalSearchIndexResults = {
|
|
37
|
+
id: GlobalSearchIndexId;
|
|
38
|
+
/** Plural, for the section heading. */
|
|
39
|
+
label: string;
|
|
40
|
+
/** Singular, for "1 employer". */
|
|
41
|
+
labelSingular: string;
|
|
42
|
+
/** Everything Algolia matched, not the number of hits returned. */
|
|
43
|
+
nbHits: number;
|
|
44
|
+
/** Zero-based, matching Algolia. */
|
|
45
|
+
page: number;
|
|
46
|
+
nbPages: number;
|
|
47
|
+
hits: GlobalSearchHit[];
|
|
48
|
+
};
|
|
49
|
+
/** Keyed by index id, so the UI can render sections in whatever order it likes. */
|
|
50
|
+
export type GlobalSearchResults = {
|
|
51
|
+
[id in GlobalSearchIndexId]?: GlobalSearchIndexResults;
|
|
52
|
+
};
|
|
53
|
+
export type GlobalSearchIndexDefinition = {
|
|
54
|
+
id: GlobalSearchIndexId;
|
|
55
|
+
label: string;
|
|
56
|
+
labelSingular: string;
|
|
57
|
+
/** The Algolia index name — the unsorted, relevance-ordered replica. */
|
|
58
|
+
indexName: string;
|
|
59
|
+
/** Whether this institute has the product that owns these records. */
|
|
60
|
+
available: (institute?: InstituteData, user?: UserData) => boolean;
|
|
61
|
+
/** Restricts the index to records this user is allowed to see. */
|
|
62
|
+
filters: (user: UserData, institute?: InstituteData) => string;
|
|
63
|
+
/** Turns one raw hit into a row. */
|
|
64
|
+
toHit: (hit: any) => GlobalSearchHit;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* The indexes the navbar search covers.
|
|
68
|
+
*
|
|
69
|
+
* Alumni, destinations surveys, leaver surveys and skills surveys belong here
|
|
70
|
+
* too once their Algolia indexes exist — each is one more entry, nothing else.
|
|
71
|
+
*/
|
|
72
|
+
export declare const GLOBAL_SEARCH_INDEXES: GlobalSearchIndexDefinition[];
|
|
73
|
+
/**
|
|
74
|
+
* The indexes this institute can search, in the order they should be shown.
|
|
75
|
+
*
|
|
76
|
+
* @param {InstituteData} [institute] the signed-in institute.
|
|
77
|
+
* @param {UserData} [user] the signed-in user.
|
|
78
|
+
* @return {GlobalSearchIndexDefinition[]} the searchable indexes.
|
|
79
|
+
*/
|
|
80
|
+
export declare const availableGlobalSearchIndexes: (institute?: InstituteData, user?: UserData) => GlobalSearchIndexDefinition[];
|
|
81
|
+
export type GlobalSearchParams = {
|
|
82
|
+
user: UserData;
|
|
83
|
+
institute?: InstituteData;
|
|
84
|
+
/** Whatever the user typed. Blank returns nothing rather than everything. */
|
|
85
|
+
query: string;
|
|
86
|
+
/** Restrict to specific indexes — how the drill-down asks for one index. */
|
|
87
|
+
indexIds?: GlobalSearchIndexId[];
|
|
88
|
+
hitsPerPage?: number;
|
|
89
|
+
/** Zero-based, matching Algolia. */
|
|
90
|
+
page?: number;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Search every index this institute can see, in one round trip.
|
|
94
|
+
*
|
|
95
|
+
* Indexes with no matches are still returned, with `nbHits: 0` and no hits, so
|
|
96
|
+
* a drill-down can say "no results" rather than render nothing. The overview UI
|
|
97
|
+
* is expected to skip empty sections itself.
|
|
98
|
+
*
|
|
99
|
+
* @param {GlobalSearchParams} params the search.
|
|
100
|
+
* @return {Promise<GlobalSearchResults>} results keyed by index id.
|
|
101
|
+
*/
|
|
102
|
+
export declare const globalSearch: ({ user, institute, query, indexIds, hitsPerPage, page, }: GlobalSearchParams) => Promise<GlobalSearchResults>;
|
|
103
|
+
/**
|
|
104
|
+
* The total across every section — what the header count is drawn from.
|
|
105
|
+
*
|
|
106
|
+
* @param {GlobalSearchResults} results a search response.
|
|
107
|
+
* @return {number} the number of matches Algolia reported in total.
|
|
108
|
+
*/
|
|
109
|
+
export declare const totalGlobalSearchHits: (results: GlobalSearchResults) => number;
|
|
110
|
+
/**
|
|
111
|
+
* Only the sections worth rendering, in registry order.
|
|
112
|
+
*
|
|
113
|
+
* @param {GlobalSearchResults} results a search response.
|
|
114
|
+
* @return {GlobalSearchIndexResults[]} sections that matched something.
|
|
115
|
+
*/
|
|
116
|
+
export declare const nonEmptyGlobalSearchSections: (results: GlobalSearchResults) => GlobalSearchIndexResults[];
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* globalSearch — one query box, several Algolia indexes.
|
|
4
|
+
*
|
|
5
|
+
* The navbar search sends whatever the user typed here. Every index that the
|
|
6
|
+
* signed-in institute can see is queried in a single round trip, and the answer
|
|
7
|
+
* comes back keyed by index so the UI can render a section per index.
|
|
8
|
+
*
|
|
9
|
+
* Adding an index is meant to be a single entry in GLOBAL_SEARCH_INDEXES: name
|
|
10
|
+
* the Algolia index, say when an institute can see it, restrict it to that
|
|
11
|
+
* institute's records, and describe how one hit becomes a row.
|
|
12
|
+
*/
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.nonEmptyGlobalSearchSections = exports.totalGlobalSearchHits = exports.globalSearch = exports.availableGlobalSearchIndexes = exports.GLOBAL_SEARCH_INDEXES = exports.GLOBAL_SEARCH_INDEX_HITS = exports.GLOBAL_SEARCH_OVERVIEW_HITS = void 0;
|
|
18
|
+
const algoliasearch_1 = __importDefault(require("algoliasearch"));
|
|
19
|
+
const util_1 = require("../../firebase/util");
|
|
20
|
+
const ALGOLIA_APP_ID = "XMPXCMUUOJ";
|
|
21
|
+
/** How many results a section shows before the user drills into one index. */
|
|
22
|
+
exports.GLOBAL_SEARCH_OVERVIEW_HITS = 5;
|
|
23
|
+
/** How many results one index shows per page once the user has drilled in. */
|
|
24
|
+
exports.GLOBAL_SEARCH_INDEX_HITS = 10;
|
|
25
|
+
const fullName = (forename, surname) => [forename, surname].filter(Boolean).join(" ").trim();
|
|
26
|
+
const joinParts = (...parts) => parts.filter(Boolean).join(" · ") || undefined;
|
|
27
|
+
/** Packages that run their own students, placements and cohorts. */
|
|
28
|
+
const isSchoolPackage = (institute) => institute?.package === "institutes-one" || institute?.package === "institutes-two";
|
|
29
|
+
/**
|
|
30
|
+
* The indexes the navbar search covers.
|
|
31
|
+
*
|
|
32
|
+
* Alumni, destinations surveys, leaver surveys and skills surveys belong here
|
|
33
|
+
* too once their Algolia indexes exist — each is one more entry, nothing else.
|
|
34
|
+
*/
|
|
35
|
+
exports.GLOBAL_SEARCH_INDEXES = [
|
|
36
|
+
{
|
|
37
|
+
id: "placements",
|
|
38
|
+
label: "Placements",
|
|
39
|
+
labelSingular: "placement",
|
|
40
|
+
indexName: "placements",
|
|
41
|
+
available: (institute) => Boolean(isSchoolPackage(institute) && institute?.workExperience),
|
|
42
|
+
filters: (user) => `oId:${(0, util_1.quoteAlgoliaFilterIfNeeded)(user.oId)} AND draft:false`,
|
|
43
|
+
toHit: (hit) => ({
|
|
44
|
+
id: hit.objectID,
|
|
45
|
+
title: hit.title || hit.name || "Untitled placement",
|
|
46
|
+
subtitle: joinParts(hit.title && hit.name, fullName(hit.contactForename, hit.contactSurname) || undefined, hit.locality),
|
|
47
|
+
meta: hit.statusType,
|
|
48
|
+
path: `/institutes/cohorts/placements/${hit.objectID}`,
|
|
49
|
+
raw: hit,
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: "employers",
|
|
54
|
+
label: "Employers",
|
|
55
|
+
labelSingular: "employer",
|
|
56
|
+
indexName: "providerContacts",
|
|
57
|
+
available: (institute) => Boolean(institute && institute.network !== false),
|
|
58
|
+
// The oId is a Firestore id, so it goes into the attribute path unquoted —
|
|
59
|
+
// the same shape algoliaProviderContactSearch already uses.
|
|
60
|
+
filters: (user) => `savedBy.${user.oId}.exists:true`,
|
|
61
|
+
toHit: (hit) => ({
|
|
62
|
+
id: hit.objectID,
|
|
63
|
+
title: hit.name || fullName(hit.contactForename, hit.contactSurname) || "Unnamed employer",
|
|
64
|
+
subtitle: joinParts(fullName(hit.contactForename, hit.contactSurname) || undefined, hit.providerEmail),
|
|
65
|
+
meta: hit.sector,
|
|
66
|
+
path: `/institutes/viewProviderContact/${hit.objectID}`,
|
|
67
|
+
raw: hit,
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "students",
|
|
72
|
+
label: "Students",
|
|
73
|
+
labelSingular: "student",
|
|
74
|
+
indexName: "users",
|
|
75
|
+
available: (institute) => isSchoolPackage(institute),
|
|
76
|
+
filters: (user) => `oId:${(0, util_1.quoteAlgoliaFilterIfNeeded)(user.oId)} AND userType:Students`,
|
|
77
|
+
toHit: (hit) => ({
|
|
78
|
+
id: hit.objectID,
|
|
79
|
+
title: fullName(hit.details?.forename, hit.details?.surname) || hit.email || "Unnamed student",
|
|
80
|
+
subtitle: hit.email,
|
|
81
|
+
meta: hit.details?.year || hit.details?.form,
|
|
82
|
+
path: `/institutes/users/${hit.objectID}`,
|
|
83
|
+
raw: hit,
|
|
84
|
+
}),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
/**
|
|
88
|
+
* The indexes this institute can search, in the order they should be shown.
|
|
89
|
+
*
|
|
90
|
+
* @param {InstituteData} [institute] the signed-in institute.
|
|
91
|
+
* @param {UserData} [user] the signed-in user.
|
|
92
|
+
* @return {GlobalSearchIndexDefinition[]} the searchable indexes.
|
|
93
|
+
*/
|
|
94
|
+
const availableGlobalSearchIndexes = (institute, user) => exports.GLOBAL_SEARCH_INDEXES.filter((index) => index.available(institute, user));
|
|
95
|
+
exports.availableGlobalSearchIndexes = availableGlobalSearchIndexes;
|
|
96
|
+
/**
|
|
97
|
+
* Search every index this institute can see, in one round trip.
|
|
98
|
+
*
|
|
99
|
+
* Indexes with no matches are still returned, with `nbHits: 0` and no hits, so
|
|
100
|
+
* a drill-down can say "no results" rather than render nothing. The overview UI
|
|
101
|
+
* is expected to skip empty sections itself.
|
|
102
|
+
*
|
|
103
|
+
* @param {GlobalSearchParams} params the search.
|
|
104
|
+
* @return {Promise<GlobalSearchResults>} results keyed by index id.
|
|
105
|
+
*/
|
|
106
|
+
const globalSearch = async ({ user, institute, query, indexIds, hitsPerPage = exports.GLOBAL_SEARCH_OVERVIEW_HITS, page = 0, }) => {
|
|
107
|
+
const trimmed = query?.trim();
|
|
108
|
+
if (!trimmed || !user?.algoliaKey)
|
|
109
|
+
return {};
|
|
110
|
+
const indexes = (0, exports.availableGlobalSearchIndexes)(institute, user)
|
|
111
|
+
.filter((index) => !indexIds || indexIds.includes(index.id));
|
|
112
|
+
if (!indexes.length)
|
|
113
|
+
return {};
|
|
114
|
+
const client = (0, algoliasearch_1.default)(ALGOLIA_APP_ID, user.algoliaKey);
|
|
115
|
+
const { results } = await client.multipleQueries(indexes.map((index) => ({
|
|
116
|
+
indexName: index.indexName,
|
|
117
|
+
query: trimmed,
|
|
118
|
+
params: {
|
|
119
|
+
filters: index.filters(user, institute),
|
|
120
|
+
hitsPerPage,
|
|
121
|
+
page,
|
|
122
|
+
},
|
|
123
|
+
})));
|
|
124
|
+
return Object.fromEntries(indexes.map((index, i) => {
|
|
125
|
+
const result = results[i];
|
|
126
|
+
return [index.id, {
|
|
127
|
+
id: index.id,
|
|
128
|
+
label: index.label,
|
|
129
|
+
labelSingular: index.labelSingular,
|
|
130
|
+
nbHits: result?.nbHits ?? 0,
|
|
131
|
+
page: result?.page ?? page,
|
|
132
|
+
nbPages: result?.nbPages ?? 0,
|
|
133
|
+
hits: (result?.hits ?? []).map(index.toHit),
|
|
134
|
+
}];
|
|
135
|
+
}));
|
|
136
|
+
};
|
|
137
|
+
exports.globalSearch = globalSearch;
|
|
138
|
+
/**
|
|
139
|
+
* The total across every section — what the header count is drawn from.
|
|
140
|
+
*
|
|
141
|
+
* @param {GlobalSearchResults} results a search response.
|
|
142
|
+
* @return {number} the number of matches Algolia reported in total.
|
|
143
|
+
*/
|
|
144
|
+
const totalGlobalSearchHits = (results) => Object.values(results).reduce((total, index) => total + (index?.nbHits ?? 0), 0);
|
|
145
|
+
exports.totalGlobalSearchHits = totalGlobalSearchHits;
|
|
146
|
+
/**
|
|
147
|
+
* Only the sections worth rendering, in registry order.
|
|
148
|
+
*
|
|
149
|
+
* @param {GlobalSearchResults} results a search response.
|
|
150
|
+
* @return {GlobalSearchIndexResults[]} sections that matched something.
|
|
151
|
+
*/
|
|
152
|
+
const nonEmptyGlobalSearchSections = (results) => exports.GLOBAL_SEARCH_INDEXES
|
|
153
|
+
.map((index) => results[index.id])
|
|
154
|
+
.filter((section) => Boolean(section && section.nbHits > 0));
|
|
155
|
+
exports.nonEmptyGlobalSearchSections = nonEmptyGlobalSearchSections;
|
|
156
|
+
//# sourceMappingURL=globalSearch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globalSearch.js","sourceRoot":"","sources":["../../../src/features/search/globalSearch.ts"],"names":[],"mappings":"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"}
|
package/lib/hooks.d.ts
CHANGED
|
@@ -602,13 +602,11 @@ export declare function useGetIndividualPlacementForPlacementPage({ user, placem
|
|
|
602
602
|
onboardingStatus: "Add onboarding documents" | "Onboarding sent" | "Onboarding docs completed" | "Onboarding docs approved" | "Complete onboarding";
|
|
603
603
|
setSkipStagePopup: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
604
604
|
onboardingPopup: boolean;
|
|
605
|
-
setViewExternalLinkPopup: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
606
605
|
setOnboardingPopup: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
607
606
|
setRejectELIPopup: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
608
607
|
eliData: string | undefined;
|
|
609
608
|
riskAssessmentURL: string;
|
|
610
609
|
dbsCheckURL: string;
|
|
611
|
-
setExternalLinkCopied: import("react").Dispatch<import("react").SetStateAction<boolean>>;
|
|
612
610
|
skipStagePopup: boolean;
|
|
613
611
|
snackbar: {
|
|
614
612
|
open: boolean;
|
|
@@ -637,8 +635,6 @@ export declare function useGetIndividualPlacementForPlacementPage({ user, placem
|
|
|
637
635
|
eliPopupOpen: boolean;
|
|
638
636
|
rejectExternalDocPopup: false | "riskAssessment" | "dbsCheck";
|
|
639
637
|
externalDocPopupOpen: false | "riskAssessment" | "dbsCheck";
|
|
640
|
-
viewExternalLinkPopup: boolean;
|
|
641
|
-
externalLinkCopied: boolean;
|
|
642
638
|
uploadInsurance: boolean;
|
|
643
639
|
uploadRA: boolean;
|
|
644
640
|
uploadDBS: boolean;
|
package/lib/hooks.js
CHANGED
|
@@ -2982,8 +2982,6 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
|
|
|
2982
2982
|
const [dbsCheckURL, setDbsCheckURL] = (0, react_1.useState)("");
|
|
2983
2983
|
const [uploadProviderDocPopup, setUploadProviderDocPopup] = (0, react_1.useState)();
|
|
2984
2984
|
const [skipStagePopup, setSkipStagePopup] = (0, react_1.useState)(false);
|
|
2985
|
-
const [viewExternalLinkPopup, setViewExternalLinkPopup] = (0, react_1.useState)(false);
|
|
2986
|
-
const [externalLinkCopied, setExternalLinkCopied] = (0, react_1.useState)(false);
|
|
2987
2985
|
const [uploadInsurance, setUploadInsurance] = (0, react_1.useState)(false);
|
|
2988
2986
|
const [uploadRA, setUploadRA] = (0, react_1.useState)(false);
|
|
2989
2987
|
const [uploadDBS, setUploadDBS] = (0, react_1.useState)(false);
|
|
@@ -3246,7 +3244,7 @@ function useGetIndividualPlacementForPlacementPage({ user, placementId, organisa
|
|
|
3246
3244
|
throw new Error("Must be a student to withdraw.");
|
|
3247
3245
|
await (0, firebase_1.executeCallable)("placement-withdraw", { placementId: placementId });
|
|
3248
3246
|
};
|
|
3249
|
-
return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, addOnboardingDocsPopup, setFeedbackComplete, setAddOnboardingDocsPopup, dismissOnboardingPopup, setDismissOnboardingPopup, setWithdrawFromPlacementPopup, withdrawFromPlacement, onFlagClick, setUploadInsurance, setUploadProviderDocPopup, setUploadRA, setUploadDBS, onboardingStatus, setSkipStagePopup, onboardingPopup,
|
|
3247
|
+
return { placement, wStage, student, workflow, editable, withdrawFromPlacementPopup, addOnboardingDocsPopup, setFeedbackComplete, setAddOnboardingDocsPopup, dismissOnboardingPopup, setDismissOnboardingPopup, setWithdrawFromPlacementPopup, withdrawFromPlacement, onFlagClick, setUploadInsurance, setUploadProviderDocPopup, setUploadRA, setUploadDBS, onboardingStatus, setSkipStagePopup, onboardingPopup, setOnboardingPopup, setRejectELIPopup, eliData, riskAssessmentURL, dbsCheckURL, skipStagePopup, snackbar, setSnackbar, cohort, disableEmail, rejectELIPopup, eliPopupOpen, rejectExternalDocPopup, externalDocPopupOpen, uploadInsurance, uploadRA, uploadDBS, editStage, sendEmail, canEdit, approveELI, setEliPopupOpen, uploadProviderDocPopup, rejectELI, setRejectExternalDocPopup, setExternalDocPopupOpen, approveProviderDoc, rejectProviderDoc, manuallyConfigureProvider, institute, shareStudentRequestPopup, setShareStudentRequestPopup };
|
|
3250
3248
|
}
|
|
3251
3249
|
function useOnboardingPopup({ onboarding, providerId, placementId, user, onClose }) {
|
|
3252
3250
|
const [fileUploadPopup, setFileUploadPopup] = (0, react_1.useState)(false);
|