sr-npm 1.0.21 → 1.2.1

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/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,normalizeCityName} = require('./utils');
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 { getCompanyId, getSmartToken } = require('./secretsData');
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 = positions.content.map(position => {
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: normalizeCityName(position.location?.city),
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 saveJobsDescriptionsAndLocationApplyUrlToCMS() {
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, JOBS_COLLECTION_FIELDS.JOBS,{ insert: 'ADMIN', update: 'ADMIN', remove: 'ADMIN', read: 'ANYONE' }),
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,19 +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 saveJobsDescriptionsAndLocationApplyUrlToCMS();
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
- wixData.truncate(COLLECTIONS.JOBS)
505
+ wixData.truncate(COLLECTIONS.JOBS),
506
+ wixData.truncate(COLLECTIONS.BRANDS),
507
+ wixData.truncate(COLLECTIONS.CUSTOM_VALUES),
508
+ wixData.truncate(COLLECTIONS.CUSTOM_FIELDS),
340
509
  ]);
341
510
  console.log("cleared collections successfully");
342
511
  }
@@ -358,27 +527,25 @@ async function markTemplateAsInternal() {
358
527
  }
359
528
 
360
529
  async function fillSecretManagerMirror() {
361
- console.log("Getting the company ID ");
362
- const companyId = await getCompanyId();
363
- console.log("companyId is : ", companyId);
364
- await wixData.insert(COLLECTIONS.SECRET_MANAGER_MIRROR, {
365
- tokenName: TOKEN_NAME.COMPANY_ID,
366
- tokenValue: companyId.value
367
- });
368
- console.log("companyId inserted into the SecretManagerMirror collection");
369
- try{
370
- const token = await getSmartToken();
371
- await wixData.insert(COLLECTIONS.SECRET_MANAGER_MIRROR, {
372
- tokenName: TOKEN_NAME.SMART_TOKEN,
373
- tokenValue: token.value
374
- });
375
- console.log("x-smarttoken inserted into the SecretManagerMirror collection");
376
- } catch (error) {
377
- console.warn("Error with inserting x-smarttoken into the 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
+ }
378
537
  }
379
538
  }
380
539
 
381
-
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
+ }
382
549
 
383
550
 
384
551
  module.exports = {
@@ -387,7 +554,7 @@ module.exports = {
387
554
  aggregateJobs,
388
555
  createCollections,
389
556
  saveJobsDataToCMS,
390
- saveJobsDescriptionsAndLocationApplyUrlToCMS,
557
+ saveJobsDescriptionsAndLocationApplyUrlReferencesToCMS,
391
558
  aggregateJobsByFieldToCMS,
392
559
  referenceJobsToField,
393
560
  fillSecretManagerMirror,
package/backend/index.js CHANGED
@@ -4,4 +4,6 @@ module.exports = {
4
4
  ...require('./fetchPositionsFromSRAPI'),
5
5
  ...require('./consts'),
6
6
  ...require('./data'),
7
+ ...require('./careersMultiBoxesPageIds'),
8
+ ...require ('./secretsData'),
7
9
  };
@@ -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
- module.exports = {
22
- getAllPositions,
23
- getPositionsByField,
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 };
@@ -4,14 +4,7 @@ const { items: wixData } = require('@wix/data');
4
4
  const { COLLECTIONS,TOKEN_NAME } = require('./collectionConsts');
5
5
 
6
6
  const getSecretValue = auth.elevate(secrets.getSecretValue);
7
-
8
- function getSmartToken() {
9
- return retrieveSecretVal(TOKEN_NAME.SMART_TOKEN)
10
- }
11
-
12
- function getCompanyId() {
13
- return retrieveSecretVal(TOKEN_NAME.COMPANY_ID_SM)
14
- }
7
+ const elevatedQuery = auth.elevate(wixData.query);
15
8
 
16
9
  async function retrieveSecretVal(tokenName)
17
10
  {
@@ -27,15 +20,15 @@ function getSmartToken() {
27
20
  }
28
21
 
29
22
  async function getTokenFromCMS(tokenName) {
30
- const result = await wixData.query(COLLECTIONS.SECRET_MANAGER_MIRROR).eq('tokenName',tokenName).find();
23
+ const result = await elevatedQuery(COLLECTIONS.SECRET_MANAGER_MIRROR).eq('tokenName',tokenName).find();
31
24
  if (result.items.length > 0) {
32
- return result.items[0].tokenValue;
25
+ return result.items[0].value;
33
26
  } else {
34
27
  throw new Error(`[getTokenFromCMS], No ${tokenName} found`);
35
28
  }
36
29
  }
37
30
  async function getTemplateTypeFromCMS() {
38
- const result = await wixData.query(COLLECTIONS.TEMPLATE_TYPE).limit(1).find();
31
+ const result = await elevatedQuery(COLLECTIONS.TEMPLATE_TYPE).limit(1).find();
39
32
  if (result.items.length > 0) {
40
33
  return result.items[0].templateType;
41
34
  } else {
@@ -50,8 +43,7 @@ function getSmartToken() {
50
43
  }
51
44
 
52
45
  module.exports = {
53
- getCompanyId,
54
- getSmartToken,
55
46
  getTokenFromCMS,
56
- getApiKeys
47
+ getApiKeys,
48
+ retrieveSecretVal
57
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 = normalizeCityName(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: sanitizeId(value),
52
+ _id: normalizeString(value).replace(/&/g, 'and'),
52
53
  count: amount,
53
54
  }));
54
55
  }
55
56
  }
56
57
 
57
- function normalizeCityName(city) {
58
- if (!city) return city;
58
+ function normalizeString(str) {
59
+ if (!str) return str;
59
60
  // Remove accents/diacritics, trim whitespace
60
- return city
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
- function sanitizeId(input) {
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
- normalizeCityName,
82
- sanitizeId,
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.21",
3
+ "version": "1.2.1",
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
- "prettier": "^3.6.2"
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
+ };