sr-npm 1.0.20 → 1.2.0
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/.github/workflows/update-sites.yml +27 -0
- package/backend/careersMultiBoxesPageIds.js +61 -0
- package/backend/collectionConsts.js +60 -4
- package/backend/consts.js +68 -14
- package/backend/data.js +201 -33
- package/backend/fetchPositionsFromSRAPI.js +2 -24
- package/backend/index.js +2 -0
- package/backend/queries.js +9 -5
- package/backend/secretsData.js +37 -10
- package/backend/utils.js +11 -16
- package/eslint.config.mjs +26 -0
- package/package.json +7 -2
- package/pages/boardPeoplePage.js +28 -0
- package/pages/brandPage.js +12 -0
- package/pages/careersMultiBoxesPage.js +521 -0
- package/pages/careersPage.js +162 -83
- package/pages/homePage.js +79 -8
- package/pages/index.js +6 -0
- package/pages/masterPage.js +35 -0
- package/pages/pagesUtils.js +232 -0
- package/pages/positionPage.js +107 -2
- package/pages/supportTeamsPage.js +92 -0
- package/public/selectors.js +43 -0
- package/public/utils.js +12 -2
- package/tests/brandPageTest.spec.js +45 -0
- package/tests/mockJobBuilder.js +290 -0
- package/tests/multiSearchBoxCareers.spec.js +371 -0
- package/tests/positionPageTest.spec.js +140 -0
package/backend/data.js
CHANGED
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
const { items: wixData } = require('@wix/data');
|
|
2
2
|
const { fetchPositionsFromSRAPI, fetchJobDescription } = require('./fetchPositionsFromSRAPI');
|
|
3
3
|
const { createCollectionIfMissing } = require('@hisense-staging/velo-npm/backend');
|
|
4
|
-
const { COLLECTIONS, COLLECTIONS_FIELDS,JOBS_COLLECTION_FIELDS,TEMPLATE_TYPE,TOKEN_NAME } = require('./collectionConsts');
|
|
5
|
-
const { chunkedBulkOperation, countJobsPerGivenField, fillCityLocationAndLocationAddress ,prepareToSaveArray,
|
|
4
|
+
const { COLLECTIONS, COLLECTIONS_FIELDS,JOBS_COLLECTION_FIELDS,TEMPLATE_TYPE,TOKEN_NAME,CUSTOM_VALUES_COLLECTION_FIELDS } = require('./collectionConsts');
|
|
5
|
+
const { chunkedBulkOperation, countJobsPerGivenField, fillCityLocationAndLocationAddress ,prepareToSaveArray,normalizeString} = require('./utils');
|
|
6
6
|
const { getAllPositions } = require('./queries');
|
|
7
|
-
const {
|
|
7
|
+
const { retrieveSecretVal, getTokenFromCMS ,getApiKeys} = require('./secretsData');
|
|
8
8
|
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
let customValuesToJobs = {}
|
|
12
|
+
let locationToJobs = {}
|
|
13
|
+
let siteconfig;
|
|
14
|
+
const EXCLUDED_CUSTOM_FIELDS = new Set(["Department","Country"]);
|
|
15
|
+
|
|
16
|
+
function getBrand(customField) {
|
|
17
|
+
return customField.find(field => field.fieldLabel === 'Brands')?.valueLabel;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function getSiteConfig() {
|
|
21
|
+
const queryresult = await wixData.query(COLLECTIONS.SITE_CONFIGS).find();
|
|
22
|
+
siteconfig = queryresult.items[0];
|
|
23
|
+
}
|
|
9
24
|
function validatePosition(position) {
|
|
10
25
|
if (!position.id) {
|
|
11
26
|
throw new Error('Position id is required');
|
|
@@ -22,15 +37,91 @@ function validatePosition(position) {
|
|
|
22
37
|
|
|
23
38
|
}
|
|
24
39
|
|
|
40
|
+
async function filterBasedOnBrand(positions) {
|
|
41
|
+
try{
|
|
42
|
+
|
|
43
|
+
const desiredBrand = await getTokenFromCMS(TOKEN_NAME.DESIRED_BRAND);
|
|
44
|
+
validateSingleDesiredBrand(desiredBrand);
|
|
45
|
+
console.log("filtering positions based on brand: ", desiredBrand);
|
|
46
|
+
return positions.content.filter(position => {
|
|
47
|
+
const brand = getBrand(position.customField);
|
|
48
|
+
if (!brand) return false;
|
|
49
|
+
return brand === desiredBrand;
|
|
50
|
+
});
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if(error.message==="[getTokenFromCMS], No desiredBrand found")
|
|
53
|
+
{
|
|
54
|
+
console.log("no desiredBrand found, fetching all positions")
|
|
55
|
+
return positions.content;
|
|
56
|
+
}
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function validateSingleDesiredBrand(desiredBrand) {
|
|
62
|
+
if(typeof desiredBrand !== 'string' || desiredBrand.includes("[") || desiredBrand.includes("]") || desiredBrand.includes(",")){
|
|
63
|
+
throw new Error("Desired brand must be a single brand");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function getLocation(position,basicJob) {
|
|
67
|
+
|
|
68
|
+
locationToJobs[basicJob.cityText] ? locationToJobs[basicJob.cityText].push(position.id) : locationToJobs[basicJob.cityText]=[position.id]
|
|
69
|
+
|
|
70
|
+
}
|
|
71
|
+
function getVisibility(position,customFieldsValues) {
|
|
72
|
+
if (!customFieldsValues["Visibility"]) {
|
|
73
|
+
customFieldsValues["Visibility"] = {};
|
|
74
|
+
}
|
|
75
|
+
let visibility;
|
|
76
|
+
position.visibility.toLowerCase()==="public"? visibility="external" : visibility="internal";
|
|
77
|
+
customFieldsValues["Visibility"][visibility] = visibility;
|
|
78
|
+
customValuesToJobs[visibility] ? customValuesToJobs[visibility].add(position.id) : customValuesToJobs[visibility]=new Set([position.id])
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getEmploymentType(position,customFieldsValues) {
|
|
82
|
+
if (!customFieldsValues["employmentType"]) {
|
|
83
|
+
customFieldsValues["employmentType"] = {};
|
|
84
|
+
}
|
|
85
|
+
customFieldsValues["employmentType"][position.typeOfEmployment.id] = position.typeOfEmployment.label;
|
|
86
|
+
customValuesToJobs[position.typeOfEmployment.id] ? customValuesToJobs[position.typeOfEmployment.id].add(position.id) : customValuesToJobs[position.typeOfEmployment.id]=new Set([position.id])
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function getCustomFieldsAndValuesFromPosition(position,customFieldsLabels,customFieldsValues) {
|
|
90
|
+
const customFieldsArray = Array.isArray(position?.customField) ? position.customField : [];
|
|
91
|
+
for (const field of customFieldsArray) {
|
|
92
|
+
if(EXCLUDED_CUSTOM_FIELDS.has(field.fieldLabel)) continue; //country and department are not custom fields, they are already in the job object
|
|
93
|
+
const fieldId=normalizeString(field.fieldId)
|
|
94
|
+
const fieldLabel = field.fieldLabel;
|
|
95
|
+
const valueId=normalizeString(field.valueId)
|
|
96
|
+
const valueLabel = field.valueLabel
|
|
97
|
+
customFieldsLabels[fieldId] = fieldLabel
|
|
98
|
+
// Build nested dictionary: fieldId -> { valueId: valueLabel }
|
|
99
|
+
if (!customFieldsValues[fieldId]) {
|
|
100
|
+
customFieldsValues[fieldId] = {};
|
|
101
|
+
}
|
|
102
|
+
customFieldsValues[fieldId][valueId] = valueLabel;
|
|
103
|
+
|
|
104
|
+
customValuesToJobs[valueId] ? customValuesToJobs[valueId].add(position.id) : customValuesToJobs[valueId]=new Set([position.id])
|
|
105
|
+
}
|
|
106
|
+
}
|
|
25
107
|
async function saveJobsDataToCMS() {
|
|
26
108
|
const positions = await fetchPositionsFromSRAPI();
|
|
109
|
+
const sourcePositions = await filterBasedOnBrand(positions);
|
|
110
|
+
const customFieldsLabels = {}
|
|
111
|
+
const customFieldsValues = {}
|
|
112
|
+
|
|
113
|
+
const {companyId,templateType} = await getApiKeys();
|
|
114
|
+
if(siteconfig===undefined) {
|
|
115
|
+
await getSiteConfig();
|
|
116
|
+
}
|
|
27
117
|
// bulk insert to jobs collection without descriptions first
|
|
28
|
-
const jobsData =
|
|
118
|
+
const jobsData = sourcePositions.map(position => {
|
|
119
|
+
|
|
29
120
|
const basicJob = {
|
|
30
121
|
_id: position.id,
|
|
31
122
|
title: position.name || '',
|
|
32
123
|
department: position.department?.label || 'Other',
|
|
33
|
-
cityText:
|
|
124
|
+
cityText: normalizeString(position.location?.city),
|
|
34
125
|
location: position.location && Object.keys(position.location).length > 0
|
|
35
126
|
? position.location
|
|
36
127
|
: {
|
|
@@ -46,11 +137,25 @@ async function saveJobsDataToCMS() {
|
|
|
46
137
|
country: position.location?.country || '',
|
|
47
138
|
remote: position.location?.remote || false,
|
|
48
139
|
language: position.language?.label || '',
|
|
140
|
+
brand: siteconfig.disableMultiBrand==="false" ? getBrand(position.customField) : '',
|
|
49
141
|
jobDescription: null, // Will be filled later
|
|
142
|
+
employmentType: position.typeOfEmployment.label,
|
|
143
|
+
releasedDate: position.releasedDate
|
|
50
144
|
};
|
|
145
|
+
|
|
146
|
+
getCustomFieldsAndValuesFromPosition(position,customFieldsLabels,customFieldsValues);
|
|
147
|
+
getEmploymentType(position,customFieldsValues);
|
|
148
|
+
getLocation(position,basicJob);
|
|
149
|
+
if(templateType===TEMPLATE_TYPE.INTERNAL){
|
|
150
|
+
getVisibility(position,customFieldsValues);
|
|
151
|
+
}
|
|
51
152
|
return basicJob;
|
|
52
153
|
});
|
|
53
154
|
|
|
155
|
+
if (siteconfig.customFields==="true") {
|
|
156
|
+
await populateCustomFieldsCollection(customFieldsLabels,templateType);
|
|
157
|
+
await populateCustomValuesCollection(customFieldsValues);
|
|
158
|
+
}
|
|
54
159
|
// Sort jobs by title (ascending, case-insensitive, numeric-aware)
|
|
55
160
|
jobsData.sort((a, b) => {
|
|
56
161
|
const titleA = a.title || '';
|
|
@@ -85,15 +190,60 @@ async function saveJobsDataToCMS() {
|
|
|
85
190
|
}
|
|
86
191
|
},
|
|
87
192
|
});
|
|
88
|
-
|
|
89
193
|
console.log(`✓ All chunks processed. Total jobs saved: ${totalSaved}/${jobsData.length}`);
|
|
90
194
|
}
|
|
91
195
|
|
|
92
|
-
async function
|
|
196
|
+
async function insertJobsReference(valueId) {
|
|
197
|
+
await wixData.insertReference(COLLECTIONS.CUSTOM_VALUES, CUSTOM_VALUES_COLLECTION_FIELDS.MULTI_REF_JOBS_CUSTOM_VALUES,valueId, Array.from(customValuesToJobs[valueId]));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function populateCustomFieldsCollection(customFields,templateType) {
|
|
201
|
+
let fieldstoinsert=[]
|
|
202
|
+
customFields["employmentType"] = "Employment Type";
|
|
203
|
+
if(templateType===TEMPLATE_TYPE.INTERNAL){
|
|
204
|
+
customFields["Visibility"] = "Visibility";
|
|
205
|
+
}
|
|
206
|
+
for(const ID of Object.keys(customFields)){
|
|
207
|
+
fieldstoinsert.push({
|
|
208
|
+
title: customFields[ID],
|
|
209
|
+
_id: ID,
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
await wixData.bulkSave(COLLECTIONS.CUSTOM_FIELDS, fieldstoinsert);
|
|
213
|
+
}
|
|
214
|
+
async function populateCustomValuesCollection(customFieldsValues) {
|
|
215
|
+
let valuesToinsert=[]
|
|
216
|
+
for (const fieldId of Object.keys(customFieldsValues)) {
|
|
217
|
+
const valuesMap = customFieldsValues[fieldId] || {};
|
|
218
|
+
for (const valueId of Object.keys(valuesMap)) {
|
|
219
|
+
valuesToinsert.push({
|
|
220
|
+
_id: valueId,
|
|
221
|
+
title: valuesMap[valueId],
|
|
222
|
+
customField: fieldId,
|
|
223
|
+
count:customValuesToJobs[valueId].size,
|
|
224
|
+
jobIds:Array.from(customValuesToJobs[valueId]),
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
await wixData.bulkSave(COLLECTIONS.CUSTOM_VALUES, valuesToinsert);
|
|
230
|
+
}
|
|
231
|
+
async function saveJobsDescriptionsAndLocationApplyUrlReferencesToCMS() {
|
|
93
232
|
console.log('🚀 Starting job descriptions update process for ALL jobs');
|
|
94
233
|
|
|
95
234
|
try {
|
|
96
235
|
let jobsWithNoDescriptions = await getJobsWithNoDescriptions();
|
|
236
|
+
if (siteconfig.customFields==="true") {
|
|
237
|
+
let customValues=await getAllCustomValues();
|
|
238
|
+
console.log("inserting jobs references to custom values collection");
|
|
239
|
+
console.log("customValues: ",customValues)
|
|
240
|
+
console.log("customValues.items: ",customValues.items)
|
|
241
|
+
for (const value of customValues.items) {
|
|
242
|
+
await insertJobsReference(value._id);
|
|
243
|
+
}
|
|
244
|
+
console.log("inserted jobs references to custom values collection successfully");
|
|
245
|
+
}
|
|
246
|
+
|
|
97
247
|
let totalUpdated = 0;
|
|
98
248
|
let totalFailed = 0;
|
|
99
249
|
let totalProcessed = 0;
|
|
@@ -122,7 +272,7 @@ async function saveJobsDescriptionsAndLocationApplyUrlToCMS() {
|
|
|
122
272
|
const jobLocation = fetchJobLocation(jobDetails);
|
|
123
273
|
const {applyLink , referFriendLink} = fetchApplyAndReferFriendLink(jobDetails);
|
|
124
274
|
|
|
125
|
-
|
|
275
|
+
|
|
126
276
|
const updatedJob = {
|
|
127
277
|
...job,
|
|
128
278
|
locationAddress: jobLocation,
|
|
@@ -186,7 +336,7 @@ async function aggregateJobsByFieldToCMS({ field, collection }) {
|
|
|
186
336
|
console.log(`counting jobs per ${field}.`);
|
|
187
337
|
let results = await getAllPositions();
|
|
188
338
|
const { jobsPerField, cityLocations,citylocationAddress } = iterateOverAllJobs(results, field);
|
|
189
|
-
const toSave = prepareToSaveArray(jobsPerField, cityLocations, field,citylocationAddress);
|
|
339
|
+
const toSave = prepareToSaveArray(jobsPerField, cityLocations, field,citylocationAddress,locationToJobs);
|
|
190
340
|
if (toSave.length === 0) {
|
|
191
341
|
console.log('No jobs found.');
|
|
192
342
|
return { success: true, message: 'No jobs to save.' };
|
|
@@ -202,7 +352,10 @@ async function aggregateJobsByFieldToCMS({ field, collection }) {
|
|
|
202
352
|
return { success: false, error: err.message };
|
|
203
353
|
}
|
|
204
354
|
}
|
|
205
|
-
|
|
355
|
+
async function getAllCustomValues() {
|
|
356
|
+
let customValuesQuery = await wixData.query(COLLECTIONS.CUSTOM_VALUES).limit(1000).find();
|
|
357
|
+
return customValuesQuery;
|
|
358
|
+
}
|
|
206
359
|
async function getJobsWithNoDescriptions() {
|
|
207
360
|
let jobswithoutdescriptionsQuery = await wixData
|
|
208
361
|
.query(COLLECTIONS.JOBS)
|
|
@@ -246,6 +399,7 @@ async function referenceJobsToField({ referenceField, sourceCollection, jobField
|
|
|
246
399
|
return rest;
|
|
247
400
|
});
|
|
248
401
|
|
|
402
|
+
|
|
249
403
|
// Bulk update in chunks of 1000
|
|
250
404
|
const chunkSize = 1000;
|
|
251
405
|
await chunkedBulkOperation({
|
|
@@ -291,10 +445,13 @@ function fetchJobLocation(jobDetails) {
|
|
|
291
445
|
async function createCollections() {
|
|
292
446
|
console.log("Creating collections");
|
|
293
447
|
await Promise.all(
|
|
294
|
-
[createCollectionIfMissing(COLLECTIONS.JOBS,
|
|
448
|
+
[createCollectionIfMissing(COLLECTIONS.JOBS, COLLECTIONS_FIELDS.JOBS,{ insert: 'ADMIN', update: 'ADMIN', remove: 'ADMIN', read: 'ANYONE' }),
|
|
295
449
|
createCollectionIfMissing(COLLECTIONS.CITIES, COLLECTIONS_FIELDS.CITIES),
|
|
296
450
|
createCollectionIfMissing(COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT, COLLECTIONS_FIELDS.AMOUNT_OF_JOBS_PER_DEPARTMENT),
|
|
297
|
-
createCollectionIfMissing(COLLECTIONS.SECRET_MANAGER_MIRROR, COLLECTIONS_FIELDS.SECRET_MANAGER_MIRROR)
|
|
451
|
+
createCollectionIfMissing(COLLECTIONS.SECRET_MANAGER_MIRROR, COLLECTIONS_FIELDS.SECRET_MANAGER_MIRROR),
|
|
452
|
+
createCollectionIfMissing(COLLECTIONS.BRANDS, COLLECTIONS_FIELDS.BRANDS),
|
|
453
|
+
createCollectionIfMissing(COLLECTIONS.CUSTOM_VALUES, COLLECTIONS_FIELDS.CUSTOM_VALUES),
|
|
454
|
+
createCollectionIfMissing(COLLECTIONS.CUSTOM_FIELDS, COLLECTIONS_FIELDS.CUSTOM_FIELDS)
|
|
298
455
|
]);
|
|
299
456
|
console.log("finished creating Collections");
|
|
300
457
|
}
|
|
@@ -303,15 +460,23 @@ async function aggregateJobs() {
|
|
|
303
460
|
console.log("Aggregating jobs");
|
|
304
461
|
await Promise.all([
|
|
305
462
|
aggregateJobsByFieldToCMS({ field: JOBS_COLLECTION_FIELDS.DEPARTMENT, collection: COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT }),
|
|
306
|
-
aggregateJobsByFieldToCMS({ field: JOBS_COLLECTION_FIELDS.CITY_TEXT, collection: COLLECTIONS.CITIES })
|
|
463
|
+
aggregateJobsByFieldToCMS({ field: JOBS_COLLECTION_FIELDS.CITY_TEXT, collection: COLLECTIONS.CITIES }),
|
|
464
|
+
aggregateJobsByFieldToCMS({ field: JOBS_COLLECTION_FIELDS.BRAND, collection: COLLECTIONS.BRANDS })
|
|
307
465
|
]);
|
|
308
466
|
console.log("finished aggregating jobs");
|
|
309
467
|
}
|
|
310
468
|
|
|
311
469
|
async function referenceJobs() {
|
|
312
470
|
console.log("Reference jobs");
|
|
471
|
+
if(siteconfig===undefined) {
|
|
472
|
+
await getSiteConfig();
|
|
473
|
+
}
|
|
313
474
|
await referenceJobsToField({ referenceField: JOBS_COLLECTION_FIELDS.DEPARTMENT_REF, sourceCollection: COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT, jobField: JOBS_COLLECTION_FIELDS.DEPARTMENT });
|
|
314
475
|
await referenceJobsToField({ referenceField: JOBS_COLLECTION_FIELDS.CITY, sourceCollection: COLLECTIONS.CITIES, jobField: JOBS_COLLECTION_FIELDS.CITY_TEXT });
|
|
476
|
+
if(siteconfig.disableMultiBrand==="false"){
|
|
477
|
+
await referenceJobsToField({ referenceField: JOBS_COLLECTION_FIELDS.BRAND_REF, sourceCollection: COLLECTIONS.BRANDS, jobField: JOBS_COLLECTION_FIELDS.BRAND });
|
|
478
|
+
}
|
|
479
|
+
|
|
315
480
|
console.log("finished referencing jobs");
|
|
316
481
|
}
|
|
317
482
|
|
|
@@ -324,20 +489,23 @@ async function syncJobsFast() {
|
|
|
324
489
|
await saveJobsDataToCMS();
|
|
325
490
|
console.log("saved jobs data to CMS successfully");
|
|
326
491
|
console.log("saving jobs descriptions and location apply url to CMS");
|
|
327
|
-
await
|
|
492
|
+
await saveJobsDescriptionsAndLocationApplyUrlReferencesToCMS();
|
|
328
493
|
console.log("saved jobs descriptions and location apply url to CMS successfully");
|
|
329
494
|
await aggregateJobs();
|
|
330
495
|
await referenceJobs();
|
|
331
496
|
console.log("syncing jobs fast finished successfully");
|
|
332
497
|
}
|
|
333
498
|
|
|
499
|
+
|
|
334
500
|
async function clearCollections() {
|
|
335
501
|
console.log("clearing collections");
|
|
336
502
|
await Promise.all([
|
|
337
503
|
wixData.truncate(COLLECTIONS.CITIES),
|
|
338
504
|
wixData.truncate(COLLECTIONS.AMOUNT_OF_JOBS_PER_DEPARTMENT),
|
|
339
505
|
wixData.truncate(COLLECTIONS.JOBS),
|
|
340
|
-
wixData.truncate(COLLECTIONS.
|
|
506
|
+
wixData.truncate(COLLECTIONS.BRANDS),
|
|
507
|
+
wixData.truncate(COLLECTIONS.CUSTOM_VALUES),
|
|
508
|
+
wixData.truncate(COLLECTIONS.CUSTOM_FIELDS),
|
|
341
509
|
]);
|
|
342
510
|
console.log("cleared collections successfully");
|
|
343
511
|
}
|
|
@@ -359,26 +527,26 @@ async function markTemplateAsInternal() {
|
|
|
359
527
|
}
|
|
360
528
|
|
|
361
529
|
async function fillSecretManagerMirror() {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
console.log("companyId inserted into the SecretManagerMirror collection");
|
|
370
|
-
try{
|
|
371
|
-
const token = await getSmartToken();
|
|
372
|
-
await wixData.insert(COLLECTIONS.SECRET_MANAGER_MIRROR, {
|
|
373
|
-
tokenName: TOKEN_NAME.SMART_TOKEN,
|
|
374
|
-
tokenValue: token.value
|
|
375
|
-
});
|
|
376
|
-
console.log("x-smarttoken inserted into the SecretManagerMirror collection");
|
|
377
|
-
} catch (error) {
|
|
378
|
-
console.log("Error creating SecretManagerMirror collection:", error);
|
|
530
|
+
for(const tokenName of Object.values(TOKEN_NAME)){
|
|
531
|
+
try{
|
|
532
|
+
await insertSecretValToCMS(tokenName);
|
|
533
|
+
console.log("inserted ", tokenName, "into the SecretManagerMirror collection successfully");
|
|
534
|
+
} catch (error) {
|
|
535
|
+
console.warn("Error with inserting ", tokenName, "into the SecretManagerMirror collection:", error);
|
|
536
|
+
}
|
|
379
537
|
}
|
|
380
538
|
}
|
|
381
539
|
|
|
540
|
+
async function insertSecretValToCMS(tokenName) {
|
|
541
|
+
const token = await retrieveSecretVal(tokenName);
|
|
542
|
+
console.log("token is: ", token);
|
|
543
|
+
await wixData.save(COLLECTIONS.SECRET_MANAGER_MIRROR, {
|
|
544
|
+
tokenName: tokenName,
|
|
545
|
+
value: token.value,
|
|
546
|
+
_id: normalizeString(tokenName)
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
382
550
|
|
|
383
551
|
module.exports = {
|
|
384
552
|
syncJobsFast,
|
|
@@ -386,7 +554,7 @@ module.exports = {
|
|
|
386
554
|
aggregateJobs,
|
|
387
555
|
createCollections,
|
|
388
556
|
saveJobsDataToCMS,
|
|
389
|
-
|
|
557
|
+
saveJobsDescriptionsAndLocationApplyUrlReferencesToCMS,
|
|
390
558
|
aggregateJobsByFieldToCMS,
|
|
391
559
|
referenceJobsToField,
|
|
392
560
|
fillSecretManagerMirror,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const { fetch } = require('wix-fetch');
|
|
2
|
-
const {
|
|
3
|
-
const {
|
|
2
|
+
const { TEMPLATE_TYPE,TOKEN_NAME } = require('./collectionConsts');
|
|
3
|
+
const { getTokenFromCMS,getApiKeys } = require('./secretsData');
|
|
4
4
|
async function makeSmartRecruitersRequest(path,templateType) {
|
|
5
5
|
const baseUrl = 'https://api.smartrecruiters.com';
|
|
6
6
|
const fullUrl = `${baseUrl}${path}`;
|
|
@@ -114,32 +114,10 @@ async function fetchJobDescription(jobId,testObject=undefined) {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
|
|
117
|
-
async function getTokenFromCMS(tokenName) {
|
|
118
|
-
const result = await wixData.query(COLLECTIONS.SECRET_MANAGER_MIRROR).eq('tokenName',tokenName).find();
|
|
119
|
-
if (result.items.length > 0) {
|
|
120
|
-
return result.items[0].tokenValue;
|
|
121
|
-
} else {
|
|
122
|
-
throw new Error(`[getTokenFromCMS], No ${tokenName} found`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
async function getTemplateTypeFromCMS() {
|
|
126
|
-
const result = await wixData.query(COLLECTIONS.TEMPLATE_TYPE).limit(1).find();
|
|
127
|
-
if (result.items.length > 0) {
|
|
128
|
-
return result.items[0].templateType;
|
|
129
|
-
} else {
|
|
130
|
-
throw new Error('[getTemplateTypeFromCMS], No templateType found');
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
117
|
|
|
134
|
-
async function getApiKeys() {
|
|
135
|
-
const companyId = await getTokenFromCMS(TOKEN_NAME.COMPANY_ID);
|
|
136
|
-
const templateType = await getTemplateTypeFromCMS();
|
|
137
|
-
return {companyId,templateType};
|
|
138
|
-
}
|
|
139
118
|
|
|
140
119
|
module.exports = {
|
|
141
120
|
fetchPositionsFromSRAPI,
|
|
142
121
|
fetchJobDescription,
|
|
143
|
-
getTokenFromCMS,
|
|
144
122
|
makeSmartRecruitersRequest
|
|
145
123
|
};
|
package/backend/index.js
CHANGED
package/backend/queries.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { COLLECTIONS } = require('./collectionConsts');
|
|
1
|
+
const { COLLECTIONS,JOBS_COLLECTION_FIELDS } = require('./collectionConsts');
|
|
2
2
|
const { items: wixData } = require('@wix/data');
|
|
3
3
|
|
|
4
4
|
|
|
@@ -18,7 +18,11 @@ async function getPositionsByField(field, value) {
|
|
|
18
18
|
.then(result => result.items);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
async function getPositionWithMultiRefField(jobId)
|
|
22
|
+
{
|
|
23
|
+
return wixData
|
|
24
|
+
.queryReferenced(COLLECTIONS.JOBS,jobId,JOBS_COLLECTION_FIELDS.MULTI_REF_JOBS_CUSTOM_VALUES)
|
|
25
|
+
.then(result => result.items);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { getAllPositions, getPositionsByField, getPositionWithMultiRefField };
|
package/backend/secretsData.js
CHANGED
|
@@ -1,22 +1,49 @@
|
|
|
1
1
|
const { secrets } = require("@wix/secrets");
|
|
2
2
|
const { auth } = require('@wix/essentials');
|
|
3
|
+
const { items: wixData } = require('@wix/data');
|
|
4
|
+
const { COLLECTIONS,TOKEN_NAME } = require('./collectionConsts');
|
|
3
5
|
|
|
4
6
|
const getSecretValue = auth.elevate(secrets.getSecretValue);
|
|
7
|
+
const elevatedQuery = auth.elevate(wixData.query);
|
|
5
8
|
|
|
6
|
-
function
|
|
7
|
-
|
|
9
|
+
async function retrieveSecretVal(tokenName)
|
|
10
|
+
{
|
|
11
|
+
return getSecretValue(tokenName)
|
|
8
12
|
.then((secret) => {
|
|
9
13
|
return secret;
|
|
14
|
+
}).catch(async (error) => {
|
|
15
|
+
console.warn("Error retrieving secret value: ", error)
|
|
16
|
+
console.warn("Retrying with getTokenFromCMS")
|
|
17
|
+
const secret = await getTokenFromCMS(tokenName);
|
|
18
|
+
return secret
|
|
10
19
|
})
|
|
11
|
-
}
|
|
20
|
+
}
|
|
12
21
|
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
return
|
|
17
|
-
|
|
22
|
+
async function getTokenFromCMS(tokenName) {
|
|
23
|
+
const result = await elevatedQuery(COLLECTIONS.SECRET_MANAGER_MIRROR).eq('tokenName',tokenName).find();
|
|
24
|
+
if (result.items.length > 0) {
|
|
25
|
+
return result.items[0].value;
|
|
26
|
+
} else {
|
|
27
|
+
throw new Error(`[getTokenFromCMS], No ${tokenName} found`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async function getTemplateTypeFromCMS() {
|
|
31
|
+
const result = await elevatedQuery(COLLECTIONS.TEMPLATE_TYPE).limit(1).find();
|
|
32
|
+
if (result.items.length > 0) {
|
|
33
|
+
return result.items[0].templateType;
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error('[getTemplateTypeFromCMS], No templateType found');
|
|
36
|
+
}
|
|
18
37
|
}
|
|
38
|
+
|
|
39
|
+
async function getApiKeys() {
|
|
40
|
+
const companyId = await getTokenFromCMS(TOKEN_NAME.COMPANY_ID);
|
|
41
|
+
const templateType = await getTemplateTypeFromCMS();
|
|
42
|
+
return {companyId,templateType};
|
|
43
|
+
}
|
|
44
|
+
|
|
19
45
|
module.exports = {
|
|
20
|
-
|
|
21
|
-
|
|
46
|
+
getTokenFromCMS,
|
|
47
|
+
getApiKeys,
|
|
48
|
+
retrieveSecretVal
|
|
22
49
|
};
|
package/backend/utils.js
CHANGED
|
@@ -30,12 +30,12 @@ function fillCityLocationAndLocationAddress(jobs, cityLocations,citylocationAddr
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
function prepareToSaveArray(jobsPerField, cityLocations, field,citylocationAddress) {
|
|
33
|
+
function prepareToSaveArray(jobsPerField, cityLocations, field,citylocationAddress,customValuesToJobs=null) {
|
|
34
34
|
if (field === 'cityText') {
|
|
35
35
|
return Object.entries(jobsPerField).map(([value, amount]) => {
|
|
36
36
|
const loc = cityLocations[value] || {};
|
|
37
37
|
const locAddress = citylocationAddress[value] || {};
|
|
38
|
-
value =
|
|
38
|
+
value = normalizeString(value);
|
|
39
39
|
return {
|
|
40
40
|
title: value,
|
|
41
41
|
_id: value.replace(/\s+/g, ''),
|
|
@@ -43,34 +43,29 @@ function prepareToSaveArray(jobsPerField, cityLocations, field,citylocationAddre
|
|
|
43
43
|
locationAddress: locAddress,
|
|
44
44
|
country: loc.country,
|
|
45
45
|
city: loc.city,
|
|
46
|
+
jobIds: customValuesToJobs[value],
|
|
46
47
|
};
|
|
47
48
|
});
|
|
48
49
|
} else {
|
|
49
50
|
return Object.entries(jobsPerField).map(([value, amount]) => ({
|
|
50
51
|
title: value,
|
|
51
|
-
_id:
|
|
52
|
+
_id: normalizeString(value).replace(/&/g, 'and'),
|
|
52
53
|
count: amount,
|
|
53
54
|
}));
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function
|
|
58
|
-
if (!
|
|
58
|
+
function normalizeString(str) {
|
|
59
|
+
if (!str) return str;
|
|
59
60
|
// Remove accents/diacritics, trim whitespace
|
|
60
|
-
return
|
|
61
|
+
return str
|
|
61
62
|
.normalize('NFD')
|
|
62
63
|
.replace(/\p{Diacritic}/gu, '')
|
|
64
|
+
.replace(/[^A-Za-z0-9-]/g, '')
|
|
63
65
|
.trim();
|
|
64
66
|
}
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
if (!input) return '';
|
|
68
|
-
const withoutDiacritics = String(input)
|
|
69
|
-
.normalize('NFD')
|
|
70
|
-
.replace(/\p{Diacritic}/gu, '');
|
|
71
|
-
const withAnd = withoutDiacritics.replace(/&/g, 'and');
|
|
72
|
-
return withAnd.replace(/[^A-Za-z0-9-]/g, '');
|
|
73
|
-
}
|
|
68
|
+
|
|
74
69
|
|
|
75
70
|
module.exports = {
|
|
76
71
|
chunkedBulkOperation,
|
|
@@ -78,6 +73,6 @@ module.exports = {
|
|
|
78
73
|
countJobsPerGivenField,
|
|
79
74
|
fillCityLocationAndLocationAddress,
|
|
80
75
|
prepareToSaveArray,
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
normalizeString,
|
|
77
|
+
|
|
83
78
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import globals from "globals";
|
|
2
|
+
import js from "@eslint/js";
|
|
3
|
+
import { defineConfig } from "eslint/config";
|
|
4
|
+
|
|
5
|
+
export default defineConfig([
|
|
6
|
+
// Core recommended rules (includes no-undef, no-unused-vars, etc.)
|
|
7
|
+
js.configs.recommended,
|
|
8
|
+
|
|
9
|
+
// Node backend files
|
|
10
|
+
{
|
|
11
|
+
files: ["backend/**/*.js"],
|
|
12
|
+
languageOptions: {
|
|
13
|
+
sourceType: "commonjs",
|
|
14
|
+
globals: { ...globals.node },
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
// Browser/frontend files
|
|
19
|
+
{
|
|
20
|
+
files: ["pages/**/*.js", "public/**/*.js"],
|
|
21
|
+
languageOptions: {
|
|
22
|
+
sourceType: "commonjs",
|
|
23
|
+
globals: { ...globals.browser },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sr-npm",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,9 +25,14 @@
|
|
|
25
25
|
"@wix/site-window": "^1.0.0",
|
|
26
26
|
"axios": "^1.11.0",
|
|
27
27
|
"jest": "^30.0.5",
|
|
28
|
+
"psdev-utils": "1.7.3",
|
|
28
29
|
"tests-utils": "^1.0.7"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"
|
|
32
|
+
"@eslint/js": "^9.39.0",
|
|
33
|
+
"eslint": "^9.38.0",
|
|
34
|
+
"globals": "^16.4.0",
|
|
35
|
+
"prettier": "^3.6.2",
|
|
36
|
+
"rewire": "^9.0.1"
|
|
32
37
|
}
|
|
33
38
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
const { location } = require("@wix/site-location");
|
|
3
|
+
async function boardPeoplePageOnReady(_$w,) {
|
|
4
|
+
|
|
5
|
+
await bindBoardPeopleRepeaters(_$w);
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function bindBoardPeopleRepeaters(_$w) {
|
|
11
|
+
|
|
12
|
+
_$w('#directorsRepeaterItem').onClick((event) => {
|
|
13
|
+
const $item = _$w.at(event.context);
|
|
14
|
+
const clickedItemData = $item('#dynamicDataset').getCurrentItem();
|
|
15
|
+
location.to(`/${clickedItemData['link-board-people-title_fld']}`);
|
|
16
|
+
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
_$w('#executivesRepeaterItem').onClick((event) => {
|
|
20
|
+
const $item = _$w.at(event.context);
|
|
21
|
+
const clickedItemData = $item('#dataset1').getCurrentItem();
|
|
22
|
+
location.to(`/${clickedItemData['link-board-people-title_fld']}`);
|
|
23
|
+
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
module.exports = {
|
|
27
|
+
boardPeoplePageOnReady,
|
|
28
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
const { location } = require("@wix/site-location");
|
|
3
|
+
async function brandPageOnReady(_$w,brand) {
|
|
4
|
+
const decodedBrand = decodeURIComponent(brand);
|
|
5
|
+
_$w('#seeJobsButton').onClick(() => {
|
|
6
|
+
location.to(`/search?brand=${decodedBrand}`);
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
brandPageOnReady,
|
|
12
|
+
};
|