musora-content-services 1.2.5 → 1.3.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.
@@ -2,45 +2,42 @@
2
2
  * @module Sanity-Services
3
3
  */
4
4
  import {
5
- artistOrInstructorName,
6
- artistOrInstructorNameAsArray,
7
- assignmentsField,
8
- descriptionField,
9
- resourcesField,
10
- contentTypeConfig,
11
- DEFAULT_FIELDS,
12
- getFieldsForContentType,
13
- filtersToGroq,
14
- getUpcomingEventsTypes,
15
- showsTypes,
16
- getNewReleasesTypes,
17
- coachLessonsTypes
18
- } from "../contentTypeConfig.js";
5
+ artistOrInstructorName,
6
+ artistOrInstructorNameAsArray,
7
+ assignmentsField,
8
+ descriptionField,
9
+ resourcesField,
10
+ contentTypeConfig,
11
+ DEFAULT_FIELDS,
12
+ getFieldsForContentType,
13
+ filtersToGroq,
14
+ getUpcomingEventsTypes,
15
+ showsTypes,
16
+ getNewReleasesTypes,
17
+ coachLessonsTypes,
18
+ } from '../contentTypeConfig.js'
19
+
20
+ import { processMetadata, typeWithSortOrder } from '../contentMetaData.js'
21
+
22
+ import { globalConfig } from './config.js'
19
23
 
20
24
  import {
21
- processMetadata,
22
- typeWithSortOrder
23
- } from "../contentMetaData.js";
24
-
25
- import {globalConfig} from "./config.js";
26
-
27
- import {
28
- fetchAllCompletedStates,
29
- fetchCompletedChallenges,
30
- fetchOwnedChallenges,
31
- fetchNextContentDataForParent,
32
- fetchHandler,
33
- } from './railcontent.js';
34
- import {arrayToStringRepresentation, FilterBuilder} from "../filterBuilder.js";
35
- import {fetchUserPermissions} from "./userPermissions.js";
36
- import {getAllCompleted, getAllStarted, getAllStartedOrCompleted} from "./contentProgress.js";
25
+ fetchAllCompletedStates,
26
+ fetchCompletedChallenges,
27
+ fetchOwnedChallenges,
28
+ fetchNextContentDataForParent,
29
+ fetchHandler,
30
+ } from './railcontent.js'
31
+ import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
32
+ import { fetchUserPermissions } from './userPermissions.js'
33
+ import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
37
34
 
38
35
  /**
39
36
  * Exported functions that are excluded from index generation.
40
37
  *
41
38
  * @type {string[]}
42
39
  */
43
- const excludeFromGeneratedIndex = ['handleCustomFetchAll'];
40
+ const excludeFromGeneratedIndex = ['handleCustomFetchAll']
44
41
 
45
42
  /**
46
43
  * Fetch a song by its document ID from Sanity.
@@ -54,16 +51,17 @@ const excludeFromGeneratedIndex = ['handleCustomFetchAll'];
54
51
  * .catch(error => console.error(error));
55
52
  */
56
53
  export async function fetchSongById(documentId) {
57
- const fields = getFieldsForContentType('song');
58
- const filterParams = {};
59
- const query = await buildQuery(
60
- `_type == "song" && railcontent_id == ${documentId}`,
61
- filterParams,
62
- fields,
63
- {
64
- isSingle: true,
65
- });
66
- return fetchSanity(query, false);
54
+ const fields = getFieldsForContentType('song')
55
+ const filterParams = {}
56
+ const query = await buildQuery(
57
+ `_type == "song" && railcontent_id == ${documentId}`,
58
+ filterParams,
59
+ fields,
60
+ {
61
+ isSingle: true,
62
+ }
63
+ )
64
+ return fetchSanity(query, false)
67
65
  }
68
66
 
69
67
  /**
@@ -78,13 +76,16 @@ export async function fetchSongById(documentId) {
78
76
  * .catch(error => console.error(error));
79
77
  */
80
78
  export async function fetchArtists(brand) {
81
- const filter = await new FilterBuilder(`_type == "song" && brand == "${brand}" && references(^._id)`, {bypassPermissions: true}).buildFilter();
82
- const query = `
79
+ const filter = await new FilterBuilder(
80
+ `_type == "song" && brand == "${brand}" && references(^._id)`,
81
+ { bypassPermissions: true }
82
+ ).buildFilter()
83
+ const query = `
83
84
  *[_type == "artist"]{
84
85
  name,
85
86
  "lessonsCount": count(*[${filter}])
86
- }[lessonsCount > 0]`;
87
- return fetchSanity(query, true, {processNeedAccess: false});
87
+ }[lessonsCount > 0]`
88
+ return fetchSanity(query, true, { processNeedAccess: false })
88
89
  }
89
90
 
90
91
  /**
@@ -93,13 +94,13 @@ export async function fetchArtists(brand) {
93
94
  * @returns {Promise<int|null>} - The fetched count of artists.
94
95
  */
95
96
  export async function fetchSongArtistCount(brand) {
96
- const query = `count(*[_type == 'artist']{'lessonsCount': count(*[_type == 'song' && brand == '${brand}' && references(^._id)]._id)}[lessonsCount > 0])`;
97
- return fetchSanity(query, true, {processNeedAccess: false});
97
+ const query = `count(*[_type == 'artist']{'lessonsCount': count(*[_type == 'song' && brand == '${brand}' && references(^._id)]._id)}[lessonsCount > 0])`
98
+ return fetchSanity(query, true, { processNeedAccess: false })
98
99
  }
99
100
 
100
101
  export async function fetchPlayAlongsCount(brand) {
101
- const query = `count(*[brand == '${brand}' && _type == "play-along"]) `
102
- return fetchSanity(query, true, {processNeedAccess: false});
102
+ const query = `count(*[brand == '${brand}' && _type == "play-along"]) `
103
+ return fetchSanity(query, true, { processNeedAccess: false })
103
104
  }
104
105
 
105
106
  /**
@@ -115,8 +116,8 @@ export async function fetchPlayAlongsCount(brand) {
115
116
  * .catch(error => console.error(error));
116
117
  */
117
118
  export async function fetchRelatedSongs(brand, songId) {
118
- const now = getSanityDate(new Date());
119
- const query = `
119
+ const now = getSanityDate(new Date())
120
+ const query = `
120
121
  *[_type == "song" && railcontent_id == ${songId}]{
121
122
  "entity": array::unique([
122
123
  ...(*[_type == "song" && brand == "${brand}" && railcontent_id != ${songId} && references(^.artist->_id)
@@ -177,10 +178,10 @@ export async function fetchRelatedSongs(brand, songId) {
177
178
  }]
178
179
  }[0...10])
179
180
  ])[0...10]
180
- }`;
181
+ }`
181
182
 
182
- // Fetch the related songs data
183
- return fetchSanity(query, false);
183
+ // Fetch the related songs data
184
+ return fetchSanity(query, false)
184
185
  }
185
186
 
186
187
  /**
@@ -188,14 +189,17 @@ export async function fetchRelatedSongs(brand, songId) {
188
189
  * @param {string} brand - The brand for which to fetch new releases.
189
190
  * @returns {Promise<Object|null>} - The fetched new releases data or null if not found.
190
191
  */
191
- export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-published_on"} = {}) {
192
- const newTypes = getNewReleasesTypes(brand);
193
- const typesString = arrayToStringRepresentation(newTypes);
194
- const start = (page - 1) * limit;
195
- const end = start + limit;
196
- const sortOrder = getSortOrder(sort, brand);
197
- const filter = `_type in ${typesString} && brand == '${brand}' && show_in_new_feed == true`;
198
- const fields = `
192
+ export async function fetchNewReleases(
193
+ brand,
194
+ { page = 1, limit = 20, sort = '-published_on' } = {}
195
+ ) {
196
+ const newTypes = getNewReleasesTypes(brand)
197
+ const typesString = arrayToStringRepresentation(newTypes)
198
+ const start = (page - 1) * limit
199
+ const end = start + limit
200
+ const sortOrder = getSortOrder(sort, brand)
201
+ const filter = `_type in ${typesString} && brand == '${brand}' && show_in_new_feed == true`
202
+ const fields = `
199
203
  "id": railcontent_id,
200
204
  title,
201
205
  "image": thumbnail.asset->url,
@@ -208,21 +212,16 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
208
212
  "type": _type,
209
213
  web_url_path,
210
214
  "permission_id": permission[]->railcontent_id,
211
- `;
212
- const filterParams = {allowsPullSongsContent: false};
213
- const query = await buildQuery(
214
- filter,
215
- filterParams,
216
- fields,
217
- {
218
- sortOrder: sortOrder,
219
- start,
220
- end: end,
221
- });
222
- return fetchSanity(query, true);
215
+ `
216
+ const filterParams = { allowsPullSongsContent: false }
217
+ const query = await buildQuery(filter, filterParams, fields, {
218
+ sortOrder: sortOrder,
219
+ start,
220
+ end: end,
221
+ })
222
+ return fetchSanity(query, true)
223
223
  }
224
224
 
225
-
226
225
  /**
227
226
  * Fetch upcoming events for a specific brand.
228
227
  *
@@ -237,13 +236,13 @@ export async function fetchNewReleases(brand, {page = 1, limit = 20, sort = "-pu
237
236
  * .then(events => console.log(events))
238
237
  * .catch(error => console.error(error));
239
238
  */
240
- export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
241
- const liveTypes = getUpcomingEventsTypes(brand);
242
- const typesString = arrayToStringRepresentation(liveTypes);
243
- const now = getSanityDate(new Date());
244
- const start = (page - 1) * limit;
245
- const end = start + limit;
246
- const fields = `
239
+ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {}) {
240
+ const liveTypes = getUpcomingEventsTypes(brand)
241
+ const typesString = arrayToStringRepresentation(liveTypes)
242
+ const now = getSanityDate(new Date())
243
+ const start = (page - 1) * limit
244
+ const end = start + limit
245
+ const fields = `
247
246
  "id": railcontent_id,
248
247
  title,
249
248
  "image": thumbnail.asset->url,
@@ -255,17 +254,17 @@ export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
255
254
  published_on,
256
255
  "type": _type,
257
256
  web_url_path,
258
- "permission_id": permission[]->railcontent_id,`;
259
- const query = buildRawQuery(
260
- `_type in ${typesString} && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
261
- fields,
262
- {
263
- sortOrder: 'published_on asc',
264
- start: start,
265
- end: end,
266
- },
267
- );
268
- return fetchSanity(query, true);
257
+ "permission_id": permission[]->railcontent_id,`
258
+ const query = buildRawQuery(
259
+ `_type in ${typesString} && brand == '${brand}' && published_on > '${now}' && status == 'scheduled'`,
260
+ fields,
261
+ {
262
+ sortOrder: 'published_on asc',
263
+ start: start,
264
+ end: end,
265
+ }
266
+ )
267
+ return fetchSanity(query, true)
269
268
  }
270
269
 
271
270
  /**
@@ -282,16 +281,16 @@ export async function fetchUpcomingEvents(brand, {page = 1, limit = 10} = {}) {
282
281
  * .then(content => console.log(content))
283
282
  * .catch(error => console.error(error));
284
283
  */
285
- export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
286
- const upcomingTypes = getUpcomingEventsTypes(brand);
287
- const newTypes = getNewReleasesTypes(brand);
288
-
289
- const scheduledTypes = merge(upcomingTypes, newTypes)
290
- const typesString = arrayJoinWithQuotes(scheduledTypes);
291
- const now = getSanityDate(new Date());
292
- const start = (page - 1) * limit;
293
- const end = start + limit;
294
- const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && published_on > '${now}']{
284
+ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
285
+ const upcomingTypes = getUpcomingEventsTypes(brand)
286
+ const newTypes = getNewReleasesTypes(brand)
287
+
288
+ const scheduledTypes = merge(upcomingTypes, newTypes)
289
+ const typesString = arrayJoinWithQuotes(scheduledTypes)
290
+ const now = getSanityDate(new Date())
291
+ const start = (page - 1) * limit
292
+ const end = start + limit
293
+ const query = `*[_type in [${typesString}] && brand == '${brand}' && status in ['published','scheduled'] && published_on > '${now}']{
295
294
  "id": railcontent_id,
296
295
  title,
297
296
  "image": thumbnail.asset->url,
@@ -304,8 +303,8 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
304
303
  "type": _type,
305
304
  web_url_path,
306
305
  "permission_id": permission[]->railcontent_id,
307
- } | order(published_on asc)[${start}...${end}]`;
308
- return fetchSanity(query, true);
306
+ } | order(published_on asc)[${start}...${end}]`
307
+ return fetchSanity(query, true)
309
308
  }
310
309
 
311
310
  /**
@@ -321,9 +320,9 @@ export async function fetchScheduledReleases(brand, {page = 1, limit = 10}) {
321
320
  * .catch(error => console.error(error));
322
321
  */
323
322
  export async function fetchByRailContentId(id, contentType) {
324
- const fields = getFieldsForContentType(contentType);
325
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
326
- const entityFieldsString = ` ${fields}
323
+ const fields = getFieldsForContentType(contentType)
324
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
325
+ const entityFieldsString = ` ${fields}
327
326
  'child_count': coalesce(count(child[${childrenFilter}]->), 0) ,
328
327
  "lessons": child[${childrenFilter}]->{
329
328
  "id": railcontent_id,
@@ -339,17 +338,17 @@ export async function fetchByRailContentId(id, contentType) {
339
338
  )
340
339
  ),
341
340
  length_in_seconds
342
- ),`;
341
+ ),`
343
342
 
344
- const query = buildRawQuery(
345
- `railcontent_id == ${id} && _type == '${contentType}'`,
346
- entityFieldsString,
347
- {
348
- isSingle: true,
349
- },
350
- );
343
+ const query = buildRawQuery(
344
+ `railcontent_id == ${id} && _type == '${contentType}'`,
345
+ entityFieldsString,
346
+ {
347
+ isSingle: true,
348
+ }
349
+ )
351
350
 
352
- return fetchSanity(query, false);
351
+ return fetchSanity(query, false)
353
352
  }
354
353
 
355
354
  /**
@@ -365,25 +364,25 @@ export async function fetchByRailContentId(id, contentType) {
365
364
  * .catch(error => console.error(error));
366
365
  */
367
366
  export async function fetchByRailContentIds(ids, contentType = undefined) {
368
- const idsString = ids.join(',');
367
+ const idsString = ids.join(',')
369
368
 
370
- const query = `*[railcontent_id in [${idsString}]]{
369
+ const query = `*[railcontent_id in [${idsString}]]{
371
370
  ${getFieldsForContentType(contentType)}
372
371
  }`
373
- const results = await fetchSanity(query, true);
374
-
375
- const sortFuction = function compare(a,b){
376
- const indexA = ids.indexOf(a['id']);
377
- const indexB = ids.indexOf(b['id'])
378
- if(indexA === indexB) return 0;
379
- if(indexA > indexB) return 1;
380
- return -1;
381
- }
372
+ const results = await fetchSanity(query, true)
373
+
374
+ const sortFuction = function compare(a, b) {
375
+ const indexA = ids.indexOf(a['id'])
376
+ const indexB = ids.indexOf(b['id'])
377
+ if (indexA === indexB) return 0
378
+ if (indexA > indexB) return 1
379
+ return -1
380
+ }
382
381
 
383
- // Sort results to match the order of the input IDs
384
- const sortedResults = results.sort(sortFuction);
382
+ // Sort results to match the order of the input IDs
383
+ const sortedResults = results.sort(sortFuction)
385
384
 
386
- return sortedResults;
385
+ return sortedResults
387
386
  }
388
387
 
389
388
  /**
@@ -418,87 +417,96 @@ export async function fetchByRailContentIds(ids, contentType = undefined) {
418
417
  * .then(content => console.log(content))
419
418
  * .catch(error => console.error(error));
420
419
  */
421
- export async function fetchAll(brand, type, {
420
+ export async function fetchAll(
421
+ brand,
422
+ type,
423
+ {
422
424
  page = 1,
423
425
  limit = 10,
424
- searchTerm = "",
425
- sort = "-published_on",
426
+ searchTerm = '',
427
+ sort = '-published_on',
426
428
  includedFields = [],
427
- groupBy = "",
429
+ groupBy = '',
428
430
  progressIds = undefined,
429
431
  useDefaultFields = true,
430
432
  customFields = [],
431
- progress = "all"
432
- } = {}) {
433
- let customResults = await handleCustomFetchAll(brand, type, {
434
- page,
435
- limit,
436
- searchTerm,
437
- sort,
438
- includedFields,
439
- groupBy,
440
- progressIds,
441
- useDefaultFields,
442
- customFields,
443
- progress
444
- });
445
- if (customResults) {
446
- return customResults;
447
- }
448
- let config = contentTypeConfig[type] ?? {};
449
- let additionalFields = config?.fields ?? [];
450
- let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false;
451
- let webUrlPathType = config?.slug ?? type;
452
- const start = (page - 1) * limit;
453
- const end = start + limit;
454
- let bypassStatusAndPublishedValidation = (type == 'instructor' || groupBy == 'artist' || groupBy == 'genre' || groupBy == 'instructor');
455
- let bypassPermissions = bypassStatusAndPublishedValidation;
456
- // Construct the type filter
457
- let typeFilter;
458
-
459
- if (type === 'archives') {
460
- typeFilter = `&& status == "archived"`;
461
- bypassStatusAndPublishedValidation = true;
462
- } else if(type === 'pack'){
463
- typeFilter = `&& (_type == 'pack' || _type == 'semester-pack')`;
464
- } else {
465
- typeFilter = type ? `&& _type == '${type}'` : progress === 'in progress' || progress === 'completed' ? " && (_type != 'challenge-part' && _type != 'challenge')" : "";
466
- }
467
-
468
- // Construct the search filter
469
- const searchFilter = searchTerm
470
- ? groupBy !== "" ?
471
- `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
472
- : `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
473
- : "";
474
-
475
- // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
476
- const includedFieldsFilter = includedFields.length > 0
477
- ? filtersToGroq(includedFields)
478
- : "";
479
-
480
- // limits the results to supplied progressIds for started & completed filters
481
- const progressFilter = await getProgressFilter(progress, progressIds);
482
-
483
- // Determine the sort order
484
- const sortOrder = getSortOrder(sort, brand, groupBy);
485
-
486
- let fields = useDefaultFields ? customFields.concat(DEFAULT_FIELDS, additionalFields) : customFields;
487
- let fieldsString = fields.join(',');
488
-
489
- let customFilter = '';
490
- if (type == 'instructor') {
491
- customFilter = '&& coach_card_image != null'
492
- }
493
- // Determine the group by clause
494
- let query = "";
495
- let entityFieldsString = "";
496
- let filter = "";
497
- if (groupBy !== "" && isGroupByOneToOne) {
498
- const webUrlPath = 'artists';
499
- const lessonsFilter = `_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`;
500
- const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter();
501
- entityFieldsString = `
433
+ progress = 'all',
434
+ } = {}
435
+ ) {
436
+ let customResults = await handleCustomFetchAll(brand, type, {
437
+ page,
438
+ limit,
439
+ searchTerm,
440
+ sort,
441
+ includedFields,
442
+ groupBy,
443
+ progressIds,
444
+ useDefaultFields,
445
+ customFields,
446
+ progress,
447
+ })
448
+ if (customResults) {
449
+ return customResults
450
+ }
451
+ let config = contentTypeConfig[type] ?? {}
452
+ let additionalFields = config?.fields ?? []
453
+ let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false
454
+ let webUrlPathType = config?.slug ?? type
455
+ const start = (page - 1) * limit
456
+ const end = start + limit
457
+ let bypassStatusAndPublishedValidation =
458
+ type == 'instructor' || groupBy == 'artist' || groupBy == 'genre' || groupBy == 'instructor'
459
+ let bypassPermissions = bypassStatusAndPublishedValidation
460
+ // Construct the type filter
461
+ let typeFilter
462
+
463
+ if (type === 'archives') {
464
+ typeFilter = `&& status == "archived"`
465
+ bypassStatusAndPublishedValidation = true
466
+ } else if (type === 'pack') {
467
+ typeFilter = `&& (_type == 'pack' || _type == 'semester-pack')`
468
+ } else {
469
+ typeFilter = type
470
+ ? `&& _type == '${type}'`
471
+ : progress === 'in progress' || progress === 'completed'
472
+ ? " && (_type != 'challenge-part' && _type != 'challenge')"
473
+ : ''
474
+ }
475
+
476
+ // Construct the search filter
477
+ const searchFilter = searchTerm
478
+ ? groupBy !== ''
479
+ ? `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
480
+ : `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
481
+ : ''
482
+
483
+ // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
484
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
485
+
486
+ // limits the results to supplied progressIds for started & completed filters
487
+ const progressFilter = await getProgressFilter(progress, progressIds)
488
+
489
+ // Determine the sort order
490
+ const sortOrder = getSortOrder(sort, brand, groupBy)
491
+
492
+ let fields = useDefaultFields
493
+ ? customFields.concat(DEFAULT_FIELDS, additionalFields)
494
+ : customFields
495
+ let fieldsString = fields.join(',')
496
+
497
+ let customFilter = ''
498
+ if (type == 'instructor') {
499
+ customFilter = '&& coach_card_image != null'
500
+ }
501
+ // Determine the group by clause
502
+ let query = ''
503
+ let entityFieldsString = ''
504
+ let filter = ''
505
+ if (groupBy !== '' && isGroupByOneToOne) {
506
+ const webUrlPath = 'artists'
507
+ const lessonsFilter = `_type == '${type}' && brand == '${brand}' && ^._id == ${groupBy}._ref ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
508
+ const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
509
+ entityFieldsString = `
502
510
  'id': railcontent_id,
503
511
  'type': _type,
504
512
  name,
@@ -509,16 +517,16 @@ export async function fetchAll(brand, type, {
509
517
  ${fieldsString},
510
518
  ${groupBy}
511
519
  }[0...20]
512
- `;
513
- filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
514
- } else if (groupBy !== "") {
515
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
520
+ `
521
+ filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`
522
+ } else if (groupBy !== '') {
523
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
516
524
 
517
- const webUrlPath = (groupBy == 'genre') ? '/genres' : '';
518
- const lessonsFilter = `brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`;
519
- const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter();
525
+ const webUrlPath = groupBy == 'genre' ? '/genres' : ''
526
+ const lessonsFilter = `brand == '${brand}' && ^._id in ${groupBy}[]._ref ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
527
+ const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
520
528
 
521
- entityFieldsString = `
529
+ entityFieldsString = `
522
530
  'id': railcontent_id,
523
531
  'type': _type,
524
532
  name,
@@ -529,12 +537,12 @@ export async function fetchAll(brand, type, {
529
537
  ${fieldsString},
530
538
  'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
531
539
  ${groupBy}
532
- }[0...20]`;
533
- filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`;
534
- } else {
535
- filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
536
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
537
- entityFieldsString = ` ${fieldsString},
540
+ }[0...20]`
541
+ filter = `_type == '${groupBy}' && count(*[${lessonsFilterWithRestrictions}]._id) > 0`
542
+ } else {
543
+ filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
544
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
545
+ entityFieldsString = ` ${fieldsString},
538
546
  'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
539
547
  'length_in_seconds': coalesce(
540
548
  math::sum(
@@ -543,24 +551,21 @@ export async function fetchAll(brand, type, {
543
551
  )
544
552
  ),
545
553
  length_in_seconds
546
- ),`;
547
- }
548
-
549
- const filterWithRestrictions = await new FilterBuilder(filter, {
550
- bypassStatuses: bypassStatusAndPublishedValidation,
551
- bypassPermissions: bypassPermissions,
552
- bypassPublishedDateRestriction: bypassStatusAndPublishedValidation
553
- }).buildFilter();
554
- query = buildEntityAndTotalQuery(
555
- filterWithRestrictions,
556
- entityFieldsString,
557
- {
558
- sortOrder: sortOrder,
559
- start: start,
560
- end: end,
561
- });
562
-
563
- return fetchSanity(query, true);
554
+ ),`
555
+ }
556
+
557
+ const filterWithRestrictions = await new FilterBuilder(filter, {
558
+ bypassStatuses: bypassStatusAndPublishedValidation,
559
+ bypassPermissions: bypassPermissions,
560
+ bypassPublishedDateRestriction: bypassStatusAndPublishedValidation,
561
+ }).buildFilter()
562
+ query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
563
+ sortOrder: sortOrder,
564
+ start: start,
565
+ end: end,
566
+ })
567
+
568
+ return fetchSanity(query, true)
564
569
  }
565
570
 
566
571
  /**
@@ -580,83 +585,110 @@ export async function fetchAll(brand, type, {
580
585
  * @param {string} [params.progress="all"] - An string representing which progress filter to use ("all", "in progress", "complete", "not started").
581
586
  * @returns {Promise<Object|null>} - The fetched content data or null if not found.
582
587
  */
583
- async function handleCustomFetchAll(brand, type, {
588
+ async function handleCustomFetchAll(
589
+ brand,
590
+ type,
591
+ {
584
592
  page = 1,
585
593
  limit = 10,
586
- searchTerm = "",
587
- sort = "-published_on",
594
+ searchTerm = '',
595
+ sort = '-published_on',
588
596
  includedFields = [],
589
- groupBy = "",
597
+ groupBy = '',
590
598
  progressIds = undefined,
591
599
  useDefaultFields = true,
592
600
  customFields = [],
593
- progress = "all"
594
- } = {}) {
595
- if (type === 'challenge') {
596
- if (groupBy === 'completed') {
597
- const completedIds = await fetchCompletedChallenges(brand, page, limit);
598
- return fetchAll(brand, type,
599
- {
600
- page,
601
- limit,
602
- searchTerm,
603
- sort,
604
- includedFields,
605
- groupBy: '',
606
- progressIds: completedIds,
607
- useDefaultFields,
608
- customFields,
609
- progress
610
- });
611
- } else if (groupBy === 'owned') {
612
- const ownedIds = await fetchOwnedChallenges(brand, page, limit);
613
- return fetchAll(brand, type,
614
- {
615
- page,
616
- limit,
617
- searchTerm,
618
- sort,
619
- includedFields,
620
- groupBy: '',
621
- progressIds: ownedIds,
622
- useDefaultFields,
623
- customFields,
624
- progress
625
- });
626
- } else if (groupBy === 'difficulty_string') {
627
- return fetchChallengesByDifficulty(brand, type, page, limit, searchTerm, sort, includedFields, groupBy, progressIds, useDefaultFields, customFields, progress);
628
- }
601
+ progress = 'all',
602
+ } = {}
603
+ ) {
604
+ if (type === 'challenge') {
605
+ if (groupBy === 'completed') {
606
+ const completedIds = await fetchCompletedChallenges(brand, page, limit)
607
+ return fetchAll(brand, type, {
608
+ page,
609
+ limit,
610
+ searchTerm,
611
+ sort,
612
+ includedFields,
613
+ groupBy: '',
614
+ progressIds: completedIds,
615
+ useDefaultFields,
616
+ customFields,
617
+ progress,
618
+ })
619
+ } else if (groupBy === 'owned') {
620
+ const ownedIds = await fetchOwnedChallenges(brand, page, limit)
621
+ return fetchAll(brand, type, {
622
+ page,
623
+ limit,
624
+ searchTerm,
625
+ sort,
626
+ includedFields,
627
+ groupBy: '',
628
+ progressIds: ownedIds,
629
+ useDefaultFields,
630
+ customFields,
631
+ progress,
632
+ })
633
+ } else if (groupBy === 'difficulty_string') {
634
+ return fetchChallengesByDifficulty(
635
+ brand,
636
+ type,
637
+ page,
638
+ limit,
639
+ searchTerm,
640
+ sort,
641
+ includedFields,
642
+ groupBy,
643
+ progressIds,
644
+ useDefaultFields,
645
+ customFields,
646
+ progress
647
+ )
629
648
  }
630
- return null;
649
+ }
650
+ return null
631
651
  }
632
652
 
633
- async function fetchChallengesByDifficulty(brand, type, page, limit, searchTerm, sort, includedFields, groupBy, progressIds, useDefaultFields, customFields, progress) {
634
- let config = contentTypeConfig['challenge'] ?? {};
635
- let additionalFields = config?.fields ?? [];
636
-
637
- // Construct the search filter
638
- const searchFilter = searchTerm
639
- ? groupBy !== "" ?
640
- `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
641
- : `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
642
- : "";
653
+ async function fetchChallengesByDifficulty(
654
+ brand,
655
+ type,
656
+ page,
657
+ limit,
658
+ searchTerm,
659
+ sort,
660
+ includedFields,
661
+ groupBy,
662
+ progressIds,
663
+ useDefaultFields,
664
+ customFields,
665
+ progress
666
+ ) {
667
+ let config = contentTypeConfig['challenge'] ?? {}
668
+ let additionalFields = config?.fields ?? []
643
669
 
644
- // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
645
- const includedFieldsFilter = includedFields.length > 0
646
- ? filtersToGroq(includedFields)
647
- : "";
670
+ // Construct the search filter
671
+ const searchFilter = searchTerm
672
+ ? groupBy !== ''
673
+ ? `&& (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
674
+ : `&& (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
675
+ : ''
648
676
 
649
- // limits the results to supplied progressIds for started & completed filters
650
- const progressFilter = await getProgressFilter(progress, progressIds);
677
+ // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
678
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
651
679
 
652
- let fields = useDefaultFields ? customFields.concat(DEFAULT_FIELDS, additionalFields) : customFields;
653
- let fieldsString = fields.join(',');
680
+ // limits the results to supplied progressIds for started & completed filters
681
+ const progressFilter = await getProgressFilter(progress, progressIds)
654
682
 
683
+ let fields = useDefaultFields
684
+ ? customFields.concat(DEFAULT_FIELDS, additionalFields)
685
+ : customFields
686
+ let fieldsString = fields.join(',')
655
687
 
656
- const lessonsFilter = `_type == 'challenge' && brand == '${brand}' && ^.name == difficulty_string ${searchFilter} ${includedFieldsFilter} ${progressFilter}`;
657
- const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter();
688
+ const lessonsFilter = `_type == 'challenge' && brand == '${brand}' && ^.name == difficulty_string ${searchFilter} ${includedFieldsFilter} ${progressFilter}`
689
+ const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
658
690
 
659
- const query = `{
691
+ const query = `{
660
692
  "entity": [
661
693
  {"name": "All"},
662
694
  {"name": "Novice"},
@@ -674,62 +706,61 @@ async function fetchChallengesByDifficulty(brand, type, page, limit, searchTerm,
674
706
  }[0...20]
675
707
  },
676
708
  "total": 0
677
- }`;
678
- let data = await fetchSanity(query, true);
679
- data.entity = data.entity.filter(function (difficulty) {
680
- return difficulty.lessons.length > 0;
681
- });
682
- return data;
709
+ }`
710
+ let data = await fetchSanity(query, true)
711
+ data.entity = data.entity.filter(function (difficulty) {
712
+ return difficulty.lessons.length > 0
713
+ })
714
+ return data
683
715
  }
684
716
 
685
717
  async function getProgressFilter(progress, progressIds) {
686
- switch (progress) {
687
- case "all":
688
- return progressIds !== undefined ?
689
- `&& railcontent_id in [${progressIds.join(',')}]` : "";
690
- case "in progress": {
691
- const ids = await getAllStarted();
692
- return `&& railcontent_id in [${ids.join(',')}]`;
693
- }
694
- case "completed": {
695
- const ids = await getAllCompleted();
696
- return `&& railcontent_id in [${ids.join(',')}]`;
697
- }
698
- case "not started": {
699
- const ids = await getAllStartedOrCompleted();
700
- return `&& !(railcontent_id in [${ids.join(',')}])`;
701
- }
702
- default:
703
- throw new Error(`'${progress}' progress option not implemented`);
718
+ switch (progress) {
719
+ case 'all':
720
+ return progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
721
+ case 'in progress': {
722
+ const ids = await getAllStarted()
723
+ return `&& railcontent_id in [${ids.join(',')}]`
724
+ }
725
+ case 'completed': {
726
+ const ids = await getAllCompleted()
727
+ return `&& railcontent_id in [${ids.join(',')}]`
728
+ }
729
+ case 'not started': {
730
+ const ids = await getAllStartedOrCompleted()
731
+ return `&& !(railcontent_id in [${ids.join(',')}])`
704
732
  }
733
+ default:
734
+ throw new Error(`'${progress}' progress option not implemented`)
735
+ }
705
736
  }
706
737
 
707
738
  export function getSortOrder(sort = '-published_on', brand, groupBy) {
708
- // Determine the sort order
709
- let sortOrder = '';
710
- const isDesc = sort.startsWith('-');
711
- sort = isDesc ? sort.substring(1) : sort;
712
- switch (sort) {
713
- case "slug":
714
- sortOrder = groupBy ? 'name' : "title";
715
- break;
716
- case "name":
717
- sortOrder = sort;
718
- break;
719
- case "popularity":
720
- if (groupBy == "artist" || groupBy == "genre") {
721
- sortOrder = isDesc ? `coalesce(popularity.${brand}, -1)` : "popularity";
722
- } else {
723
- sortOrder = isDesc ? "coalesce(popularity, -1)" : "popularity";
724
- }
725
- break;
726
- case "published_on":
727
- default:
728
- sortOrder = "published_on";
729
- break;
730
- }
731
- sortOrder += isDesc ? ' desc' : ' asc';
732
- return sortOrder;
739
+ // Determine the sort order
740
+ let sortOrder = ''
741
+ const isDesc = sort.startsWith('-')
742
+ sort = isDesc ? sort.substring(1) : sort
743
+ switch (sort) {
744
+ case 'slug':
745
+ sortOrder = groupBy ? 'name' : 'title'
746
+ break
747
+ case 'name':
748
+ sortOrder = sort
749
+ break
750
+ case 'popularity':
751
+ if (groupBy == 'artist' || groupBy == 'genre') {
752
+ sortOrder = isDesc ? `coalesce(popularity.${brand}, -1)` : 'popularity'
753
+ } else {
754
+ sortOrder = isDesc ? 'coalesce(popularity, -1)' : 'popularity'
755
+ }
756
+ break
757
+ case 'published_on':
758
+ default:
759
+ sortOrder = 'published_on'
760
+ break
761
+ }
762
+ sortOrder += isDesc ? ' desc' : ' asc'
763
+ return sortOrder
733
764
  }
734
765
 
735
766
  /**
@@ -764,42 +795,48 @@ export function getSortOrder(sort = '-published_on', brand, groupBy) {
764
795
  * .catch(error => console.error(error));
765
796
  */
766
797
  export async function fetchAllFilterOptions(
767
- brand,
768
- filters = [],
769
- style,
770
- artist,
771
- contentType,
772
- term,
773
- progressIds,
774
- coachId,
775
- includeTabs = false,
798
+ brand,
799
+ filters = [],
800
+ style,
801
+ artist,
802
+ contentType,
803
+ term,
804
+ progressIds,
805
+ coachId,
806
+ includeTabs = false
776
807
  ) {
777
- if (coachId && contentType !== 'coach-lessons') {
778
- throw new Error(`Invalid contentType: '${contentType}' for coachId. It must be 'coach-lessons'.`);
779
- }
780
-
781
- const includedFieldsFilter = filters?.length ? filtersToGroq(filters) : undefined;
782
- const progressFilter = progressIds ? `&& railcontent_id in [${progressIds.join(',')}]` : "";
783
- const isAdmin = (await fetchUserPermissions()).isAdmin;
784
-
785
- const constructCommonFilter = (excludeFilter) => {
786
- const filterWithoutOption = excludeFilter ? filtersToGroq(filters, excludeFilter) : includedFieldsFilter;
787
- const statusFilter = ' && status == "published"';
788
- const includeStatusFilter = !isAdmin && !['instructor', 'artist', 'genre'].includes(contentType);
789
-
790
- return coachId
791
- ? `brand == '${brand}' && status == "published" && references(*[_type=='instructor' && railcontent_id == ${coachId}]._id) ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
792
- : `_type == '${contentType}' && brand == "${brand}"${includeStatusFilter ? statusFilter : ''}${style && excludeFilter !== "style" ? ` && '${style}' in genre[]->name` : ''}${artist && excludeFilter !== "artist" ? ` && artist->name == '${artist}'` : ''} ${progressFilter} ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`;
793
- };
794
-
795
- const metaData = processMetadata(brand, contentType, true);
796
- const allowableFilters = metaData?.allowableFilters || [];
797
- const tabs = metaData?.tabs || [];
798
- const catalogName = metaData?.shortname || metaData?.name;
799
-
800
- const dynamicFilterOptions = allowableFilters.map(filter => getFilterOptions(filter, constructCommonFilter(filter), contentType, brand)).join(' ');
801
-
802
- const query = `
808
+ if (coachId && contentType !== 'coach-lessons') {
809
+ throw new Error(
810
+ `Invalid contentType: '${contentType}' for coachId. It must be 'coach-lessons'.`
811
+ )
812
+ }
813
+
814
+ const includedFieldsFilter = filters?.length ? filtersToGroq(filters) : undefined
815
+ const progressFilter = progressIds ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
816
+ const isAdmin = (await fetchUserPermissions()).isAdmin
817
+
818
+ const constructCommonFilter = (excludeFilter) => {
819
+ const filterWithoutOption = excludeFilter
820
+ ? filtersToGroq(filters, excludeFilter)
821
+ : includedFieldsFilter
822
+ const statusFilter = ' && status == "published"'
823
+ const includeStatusFilter = !isAdmin && !['instructor', 'artist', 'genre'].includes(contentType)
824
+
825
+ return coachId
826
+ ? `brand == '${brand}' && status == "published" && references(*[_type=='instructor' && railcontent_id == ${coachId}]._id) ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
827
+ : `_type == '${contentType}' && brand == "${brand}"${includeStatusFilter ? statusFilter : ''}${style && excludeFilter !== 'style' ? ` && '${style}' in genre[]->name` : ''}${artist && excludeFilter !== 'artist' ? ` && artist->name == '${artist}'` : ''} ${progressFilter} ${filterWithoutOption || ''} ${term ? ` && (title match "${term}" || album match "${term}" || artist->name match "${term}" || genre[]->name match "${term}")` : ''}`
828
+ }
829
+
830
+ const metaData = processMetadata(brand, contentType, true)
831
+ const allowableFilters = metaData?.allowableFilters || []
832
+ const tabs = metaData?.tabs || []
833
+ const catalogName = metaData?.shortname || metaData?.name
834
+
835
+ const dynamicFilterOptions = allowableFilters
836
+ .map((filter) => getFilterOptions(filter, constructCommonFilter(filter), contentType, brand))
837
+ .join(' ')
838
+
839
+ const query = `
803
840
  {
804
841
  "meta": {
805
842
  "totalResults": count(*[${constructCommonFilter()}
@@ -808,11 +845,11 @@ export async function fetchAllFilterOptions(
808
845
  ${dynamicFilterOptions}
809
846
  }
810
847
  }
811
- }`;
848
+ }`
812
849
 
813
- const results = await fetchSanity(query, true, {processNeedAccess: false});
850
+ const results = await fetchSanity(query, true, { processNeedAccess: false })
814
851
 
815
- return includeTabs ? {...results, tabs, catalogName} : results;
852
+ return includeTabs ? { ...results, tabs, catalogName } : results
816
853
  }
817
854
 
818
855
  /**
@@ -821,17 +858,17 @@ export async function fetchAllFilterOptions(
821
858
  * @returns {Promise<Object|null>} - The fetched foundation data or null if not found.
822
859
  */
823
860
  export async function fetchFoundation(slug) {
824
- const filterParams = {};
825
- const query = await buildQuery(
826
- `_type == 'foundation' && slug.current == "${slug}"`,
827
- filterParams,
828
- getFieldsForContentType('foundation'),
829
- {
830
- sortOrder: 'published_on asc',
831
- isSingle: true,
832
- }
833
- );
834
- return fetchSanity(query, false);
861
+ const filterParams = {}
862
+ const query = await buildQuery(
863
+ `_type == 'foundation' && slug.current == "${slug}"`,
864
+ filterParams,
865
+ getFieldsForContentType('foundation'),
866
+ {
867
+ sortOrder: 'published_on asc',
868
+ isSingle: true,
869
+ }
870
+ )
871
+ return fetchSanity(query, false)
835
872
  }
836
873
 
837
874
  /**
@@ -841,9 +878,9 @@ export async function fetchFoundation(slug) {
841
878
  * @returns {Promise<Object|null>} - The fetched methods data or null if not found.
842
879
  */
843
880
  export async function fetchMethod(brand, slug) {
844
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
881
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
845
882
 
846
- const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
883
+ const query = `*[_type == 'learning-path' && brand == "${brand}" && slug.current == "${slug}"] {
847
884
  "description": ${descriptionField},
848
885
  "instructors":instructor[]->name,
849
886
  published_on,
@@ -880,7 +917,7 @@ export async function fetchMethod(brand, slug) {
880
917
  total_xp
881
918
  }
882
919
  } | order(published_on asc)`
883
- return fetchSanity(query, false);
920
+ return fetchSanity(query, false)
884
921
  }
885
922
 
886
923
  /**
@@ -889,9 +926,9 @@ export async function fetchMethod(brand, slug) {
889
926
  * @returns {Promise<Object|null>} - The fetched next lesson data or null if not found.
890
927
  */
891
928
  export async function fetchMethodChildren(railcontentId) {
892
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
929
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
893
930
 
894
- const query = `*[railcontent_id == ${railcontentId}]{
931
+ const query = `*[railcontent_id == ${railcontentId}]{
895
932
  "child_count":coalesce(count(child[${childrenFilter}]->), 0),
896
933
  "id": railcontent_id,
897
934
  "description": ${descriptionField},
@@ -909,8 +946,8 @@ export async function fetchMethodChildren(railcontentId) {
909
946
  'children': child[(${childrenFilter})]->{
910
947
  ${getFieldsForContentType('method')}
911
948
  },
912
- }[0..1]`;
913
- return fetchSanity(query, true);
949
+ }[0..1]`
950
+ return fetchSanity(query, true)
914
951
  }
915
952
 
916
953
  /**
@@ -924,21 +961,21 @@ export async function fetchMethodChildren(railcontentId) {
924
961
  * .catch(error => console.error(error));
925
962
  */
926
963
  export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
927
- const sortedChildren = await fetchMethodChildrenIds(methodId);
928
- const index = sortedChildren.indexOf(Number(railcontentId));
929
- let nextId = sortedChildren[index + 1];
930
- let previousId = sortedChildren[index - 1];
931
- let ids = [];
932
- if (nextId) ids.push(nextId);
933
- if (previousId) ids.push(previousId);
934
- let nextPrev = await fetchByRailContentIds(ids);
935
- const nextLesson = nextPrev.find((elem) => {
936
- return elem['id'] === nextId
937
- });
938
- const prevLesson = nextPrev.find((elem) => {
939
- return elem['id'] === previousId
940
- });
941
- return {nextLesson, prevLesson};
964
+ const sortedChildren = await fetchMethodChildrenIds(methodId)
965
+ const index = sortedChildren.indexOf(Number(railcontentId))
966
+ let nextId = sortedChildren[index + 1]
967
+ let previousId = sortedChildren[index - 1]
968
+ let ids = []
969
+ if (nextId) ids.push(nextId)
970
+ if (previousId) ids.push(previousId)
971
+ let nextPrev = await fetchByRailContentIds(ids)
972
+ const nextLesson = nextPrev.find((elem) => {
973
+ return elem['id'] === nextId
974
+ })
975
+ const prevLesson = nextPrev.find((elem) => {
976
+ return elem['id'] === previousId
977
+ })
978
+ return { nextLesson, prevLesson }
942
979
  }
943
980
 
944
981
  /**
@@ -947,9 +984,9 @@ export async function fetchMethodPreviousNextLesson(railcontentId, methodId) {
947
984
  * @returns {Promise<Array<Object>|null>} - The fetched children data or null if not found.
948
985
  */
949
986
  export async function fetchMethodChildrenIds(railcontentId) {
950
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
987
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
951
988
 
952
- const query = `*[ railcontent_id == ${railcontentId}]{
989
+ const query = `*[ railcontent_id == ${railcontentId}]{
953
990
  'children': child[${childrenFilter}]-> {
954
991
  'id': railcontent_id,
955
992
  'type' : _type,
@@ -962,22 +999,22 @@ export async function fetchMethodChildrenIds(railcontentId) {
962
999
  }
963
1000
  }
964
1001
  }
965
- }`;
966
- let allChildren = await fetchSanity(query, false);
967
- return getChildrenToDepth(allChildren, 4);
1002
+ }`
1003
+ let allChildren = await fetchSanity(query, false)
1004
+ return getChildrenToDepth(allChildren, 4)
968
1005
  }
969
1006
 
970
1007
  function getChildrenToDepth(parent, depth = 1) {
971
- let allChildrenIds = [];
972
- if (parent && parent['children'] && depth > 0) {
973
- parent['children'].forEach((child) => {
974
- if (!child['children']) {
975
- allChildrenIds.push(child['id']);
976
- }
977
- allChildrenIds = allChildrenIds.concat(getChildrenToDepth(child, depth - 1));
978
- })
979
- }
980
- return allChildrenIds;
1008
+ let allChildrenIds = []
1009
+ if (parent && parent['children'] && depth > 0) {
1010
+ parent['children'].forEach((child) => {
1011
+ if (!child['children']) {
1012
+ allChildrenIds.push(child['id'])
1013
+ }
1014
+ allChildrenIds = allChildrenIds.concat(getChildrenToDepth(child, depth - 1))
1015
+ })
1016
+ }
1017
+ return allChildrenIds
981
1018
  }
982
1019
 
983
1020
  /**
@@ -986,31 +1023,31 @@ function getChildrenToDepth(parent, depth = 1) {
986
1023
  * @returns {Promise<Object|null>} - The fetched next and previous lesson data or null if found.
987
1024
  */
988
1025
  export async function fetchNextPreviousLesson(railcontentId) {
989
- const document = await fetchLessonContent(railcontentId);
990
- if (document.parent_content_data && document.parent_content_data.length > 0) {
991
- const lastElement = document.parent_content_data[document.parent_content_data.length - 1];
992
- const results = await fetchMethodPreviousNextLesson(railcontentId, lastElement.id);
993
- return results;
994
- }
995
- const processedData = processMetadata(document.brand, document.type, true);
996
- let sortBy = processedData?.sortBy ?? 'published_on';
997
- const isDesc = sortBy.startsWith('-');
998
- sortBy = isDesc ? sortBy.substring(1) : sortBy;
999
- let sortValue = document[sortBy];
1000
- if (sortValue == null) {
1001
- sortBy = 'railcontent_id';
1002
- sortValue = document['railcontent_id'];
1003
- }
1004
- const isNumeric = !isNaN(sortValue);
1005
- let prevComparison = isNumeric ? `${sortBy} <= ${sortValue}` : `${sortBy} <= "${sortValue}"`;
1006
- let nextComparison = isNumeric ? `${sortBy} >= ${sortValue}` : `${sortBy} >= "${sortValue}"`;
1007
- const fields = getFieldsForContentType(document.type);
1008
- const query = `{
1026
+ const document = await fetchLessonContent(railcontentId)
1027
+ if (document.parent_content_data && document.parent_content_data.length > 0) {
1028
+ const lastElement = document.parent_content_data[document.parent_content_data.length - 1]
1029
+ const results = await fetchMethodPreviousNextLesson(railcontentId, lastElement.id)
1030
+ return results
1031
+ }
1032
+ const processedData = processMetadata(document.brand, document.type, true)
1033
+ let sortBy = processedData?.sortBy ?? 'published_on'
1034
+ const isDesc = sortBy.startsWith('-')
1035
+ sortBy = isDesc ? sortBy.substring(1) : sortBy
1036
+ let sortValue = document[sortBy]
1037
+ if (sortValue == null) {
1038
+ sortBy = 'railcontent_id'
1039
+ sortValue = document['railcontent_id']
1040
+ }
1041
+ const isNumeric = !isNaN(sortValue)
1042
+ let prevComparison = isNumeric ? `${sortBy} <= ${sortValue}` : `${sortBy} <= "${sortValue}"`
1043
+ let nextComparison = isNumeric ? `${sortBy} >= ${sortValue}` : `${sortBy} >= "${sortValue}"`
1044
+ const fields = getFieldsForContentType(document.type)
1045
+ const query = `{
1009
1046
  "prevLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${prevComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} desc){${fields}}[0...1][0],
1010
1047
  "nextLesson": *[brand == "${document.brand}" && status == "${document.status}" && _type == "${document.type}" && ${nextComparison} && railcontent_id != ${railcontentId}] | order(${sortBy} asc){${fields}}[0...1][0]
1011
- }`;
1048
+ }`
1012
1049
 
1013
- return await fetchSanity(query, true);
1050
+ return await fetchSanity(query, true)
1014
1051
  }
1015
1052
 
1016
1053
  /**
@@ -1023,12 +1060,12 @@ export async function fetchNextPreviousLesson(railcontentId) {
1023
1060
  * .catch(error => console.error(error));
1024
1061
  */
1025
1062
  export async function jumpToContinueContent(railcontentId) {
1026
- const nextContent = await fetchNextContentDataForParent(railcontentId);
1027
- if (!nextContent || !nextContent.id) {
1028
- return null;
1029
- }
1030
- let next = await fetchByRailContentId(nextContent.id, nextContent.type);
1031
- return {next};
1063
+ const nextContent = await fetchNextContentDataForParent(railcontentId)
1064
+ if (!nextContent || !nextContent.id) {
1065
+ return null
1066
+ }
1067
+ let next = await fetchByRailContentId(nextContent.id, nextContent.type)
1068
+ return { next }
1032
1069
  }
1033
1070
 
1034
1071
  /**
@@ -1042,11 +1079,11 @@ export async function jumpToContinueContent(railcontentId) {
1042
1079
  * .catch(error => console.error(error));
1043
1080
  */
1044
1081
  export async function fetchLessonContent(railContentId) {
1045
- const filterParams = {isSingle: true, pullFutureContent: true};
1046
- // Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
1047
- // Currently only for challenges and challenge lessons
1048
- // If you're unsure, message Adrian, or just add them.
1049
- const fields = `title,
1082
+ const filterParams = { isSingle: true, pullFutureContent: true }
1083
+ // Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
1084
+ // Currently only for challenges and challenge lessons
1085
+ // If you're unsure, message Adrian, or just add them.
1086
+ const fields = `title,
1050
1087
  published_on,
1051
1088
  "type":_type,
1052
1089
  "resources": ${resourcesField},
@@ -1094,16 +1131,11 @@ export async function fetchLessonContent(railContentId) {
1094
1131
  "type": *[railcontent_id == ^.id][0]._type,
1095
1132
  },
1096
1133
  sort,
1097
- xp`;
1098
- const query = await buildQuery(
1099
- `railcontent_id == ${railContentId}`,
1100
- filterParams,
1101
- fields,
1102
- {
1103
- isSingle: true,
1104
- }
1105
- );
1106
- return fetchSanity(query, false);
1134
+ xp`
1135
+ const query = await buildQuery(`railcontent_id == ${railContentId}`, filterParams, fields, {
1136
+ isSingle: true,
1137
+ })
1138
+ return fetchSanity(query, false)
1107
1139
  }
1108
1140
 
1109
1141
  /**
@@ -1113,14 +1145,22 @@ export async function fetchLessonContent(railContentId) {
1113
1145
  * @returns {Promise<Array<Object>|null>} - The fetched related lessons data or null if not found.
1114
1146
  */
1115
1147
  export async function fetchRelatedLessons(railContentId, brand) {
1116
- const filterSameTypeAndSortOrder = await new FilterBuilder(`_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`).buildFilter();
1117
- const filterSameType = await new FilterBuilder(`_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`).buildFilter();
1118
- const filterSongSameArtist = await new FilterBuilder(`_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`).buildFilter();
1119
- const filterSongSameGenre = await new FilterBuilder(`_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`).buildFilter();
1120
- const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter();
1121
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1122
-
1123
- const query = `*[railcontent_id == ${railContentId} && brand == "${brand}" && (!defined(permission) || references(*[_type=='permission']._id))]{
1148
+ const filterSameTypeAndSortOrder = await new FilterBuilder(
1149
+ `_type==^._type && _type in ${JSON.stringify(typeWithSortOrder)} && brand == "${brand}" && railcontent_id !=${railContentId}`
1150
+ ).buildFilter()
1151
+ const filterSameType = await new FilterBuilder(
1152
+ `_type==^._type && !(_type in ${JSON.stringify(typeWithSortOrder)}) && !(defined(parent_type)) && brand == "${brand}" && railcontent_id !=${railContentId}`
1153
+ ).buildFilter()
1154
+ const filterSongSameArtist = await new FilterBuilder(
1155
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.artist->_id) && railcontent_id !=${railContentId}`
1156
+ ).buildFilter()
1157
+ const filterSongSameGenre = await new FilterBuilder(
1158
+ `_type=="song" && _type==^._type && brand == "${brand}" && references(^.genre[]->_id) && railcontent_id !=${railContentId}`
1159
+ ).buildFilter()
1160
+ const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter()
1161
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1162
+
1163
+ const query = `*[railcontent_id == ${railContentId} && brand == "${brand}" && (!defined(permission) || references(*[_type=='permission']._id))]{
1124
1164
  _type, parent_type, railcontent_id,
1125
1165
  "related_lessons" : array::unique([
1126
1166
  ...(*[${filterNeighbouringSiblings}][0].child[${childrenFilter}]->{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}),
@@ -1129,8 +1169,8 @@ export async function fetchRelatedLessons(railContentId, brand) {
1129
1169
  ...(*[${filterSameTypeAndSortOrder}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, sort}|order(sort asc, title asc)[0...10]),
1130
1170
  ...(*[${filterSameType}]{_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail_url":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type}|order(published_on desc, title asc)[0...10])
1131
1171
  ,
1132
- ])[0...10]}`;
1133
- return fetchSanity(query, false);
1172
+ ])[0...10]}`
1173
+ return fetchSanity(query, false)
1134
1174
  }
1135
1175
 
1136
1176
  /**
@@ -1142,26 +1182,27 @@ export async function fetchRelatedLessons(railContentId, brand) {
1142
1182
  * @param {number} [params.limit=10] - The number of items per page.
1143
1183
  * @returns {Promise<Array<Object>|null>} - The fetched pack content data or null if not found.
1144
1184
  */
1145
- export async function fetchAllPacks(brand, sort = "-published_on", searchTerm = "", page = 1, limit = 10) {
1146
- const sortOrder = getSortOrder(sort, brand);
1147
- const filter = `(_type == 'pack' || _type == 'semester-pack') && brand == '${brand}' && title match "${searchTerm}*"`
1148
- const filterParams = {};
1149
- const fields = getFieldsForContentType('pack');
1150
- const start = (page - 1) * limit;
1151
- const end = start + limit;
1152
-
1153
- const query = await buildQuery(
1154
- filter,
1155
- filterParams,
1156
- getFieldsForContentType('pack'),
1157
- {
1158
- logo_image_url: 'logo_image_url.asset->url',
1159
- sortOrder: sortOrder,
1160
- start,
1161
- end
1162
- }
1163
- );
1164
- return fetchSanity(query, true);
1185
+ export async function fetchAllPacks(
1186
+ brand,
1187
+ sort = '-published_on',
1188
+ searchTerm = '',
1189
+ page = 1,
1190
+ limit = 10
1191
+ ) {
1192
+ const sortOrder = getSortOrder(sort, brand)
1193
+ const filter = `(_type == 'pack' || _type == 'semester-pack') && brand == '${brand}' && title match "${searchTerm}*"`
1194
+ const filterParams = {}
1195
+ const fields = getFieldsForContentType('pack')
1196
+ const start = (page - 1) * limit
1197
+ const end = start + limit
1198
+
1199
+ const query = await buildQuery(filter, filterParams, getFieldsForContentType('pack'), {
1200
+ logo_image_url: 'logo_image_url.asset->url',
1201
+ sortOrder: sortOrder,
1202
+ start,
1203
+ end,
1204
+ })
1205
+ return fetchSanity(query, true)
1165
1206
  }
1166
1207
 
1167
1208
  /**
@@ -1170,38 +1211,38 @@ export async function fetchAllPacks(brand, sort = "-published_on", searchTerm =
1170
1211
  * @returns {Promise<Array<Object>|null>} - The fetched pack content data or null if not found.
1171
1212
  */
1172
1213
  export async function fetchPackAll(railcontentId, type = 'pack') {
1173
- return fetchByRailContentId(railcontentId, type);
1214
+ return fetchByRailContentId(railcontentId, type)
1174
1215
  }
1175
1216
 
1176
1217
  export async function fetchLiveEvent(brand) {
1177
- //calendarIDs taken from addevent.php
1178
- // TODO import instructor calendars to Sanity
1179
- let defaultCalendarID = '';
1180
- switch (brand) {
1181
- case ('drumeo'):
1182
- defaultCalendarID = 'GP142387';
1183
- break;
1184
- case ('pianote'):
1185
- defaultCalendarID = 'be142408';
1186
- break;
1187
- case ('guitareo'):
1188
- defaultCalendarID = 'IJ142407';
1189
- break;
1190
- case ('singeo'):
1191
- defaultCalendarID = 'bk354284';
1192
- break;
1193
- default:
1194
- break;
1195
- }
1196
- let startDateTemp = new Date();
1197
- let endDateTemp = new Date();
1198
- startDateTemp= new Date (startDateTemp.setMinutes(startDateTemp.getMinutes() + 15));
1199
- endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15));
1200
-
1201
- // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1202
- // this has some +- on times
1203
- // But this query just finds the first scheduled event (sorted by start_time) that ends after now()
1204
- const query = `*[status == 'scheduled' && brand == '${brand}' && defined(live_event_start_time) && live_event_start_time <= '${getSanityDate(startDateTemp, false)}' && live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
1218
+ //calendarIDs taken from addevent.php
1219
+ // TODO import instructor calendars to Sanity
1220
+ let defaultCalendarID = ''
1221
+ switch (brand) {
1222
+ case 'drumeo':
1223
+ defaultCalendarID = 'GP142387'
1224
+ break
1225
+ case 'pianote':
1226
+ defaultCalendarID = 'be142408'
1227
+ break
1228
+ case 'guitareo':
1229
+ defaultCalendarID = 'IJ142407'
1230
+ break
1231
+ case 'singeo':
1232
+ defaultCalendarID = 'bk354284'
1233
+ break
1234
+ default:
1235
+ break
1236
+ }
1237
+ let startDateTemp = new Date()
1238
+ let endDateTemp = new Date()
1239
+ startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + 15))
1240
+ endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15))
1241
+
1242
+ // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1243
+ // this has some +- on times
1244
+ // But this query just finds the first scheduled event (sorted by start_time) that ends after now()
1245
+ const query = `*[status == 'scheduled' && brand == '${brand}' && defined(live_event_start_time) && live_event_start_time <= '${getSanityDate(startDateTemp, false)}' && live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
1205
1246
  'slug': slug.current,
1206
1247
  'id': railcontent_id,
1207
1248
  live_event_start_time,
@@ -1218,8 +1259,8 @@ export async function fetchLiveEvent(brand) {
1218
1259
  web_url_path,
1219
1260
  },
1220
1261
  'videoId': coalesce(live_event_youtube_id, video.external_id),
1221
- } | order(live_event_start_time)[0...1]`;
1222
- return await fetchSanity(query, false, {processNeedAccess: false});
1262
+ } | order(live_event_start_time)[0...1]`
1263
+ return await fetchSanity(query, false, { processNeedAccess: false })
1223
1264
  }
1224
1265
 
1225
1266
  /**
@@ -1233,10 +1274,10 @@ export async function fetchLiveEvent(brand) {
1233
1274
  * .catch(error => console.error(error));
1234
1275
  */
1235
1276
  export async function fetchPackData(id) {
1236
- const query = `*[railcontent_id == ${id}]{
1237
- ${getFieldsForContentType("pack")}
1238
- } [0...1]`;
1239
- return fetchSanity(query, false);
1277
+ const query = `*[railcontent_id == ${id}]{
1278
+ ${getFieldsForContentType('pack')}
1279
+ } [0...1]`
1280
+ return fetchSanity(query, false)
1240
1281
  }
1241
1282
 
1242
1283
  /**
@@ -1256,34 +1297,26 @@ export async function fetchPackData(id) {
1256
1297
  * .then(lessons => console.log(lessons))
1257
1298
  * .catch(error => console.error(error));
1258
1299
  */
1259
- export async function fetchCoachLessons(brand, id, {
1260
- sortOrder = '-published_on',
1261
- searchTerm = '',
1262
- page = 1,
1263
- limit = 20,
1264
- includedFields = [],
1265
- } = {}) {
1266
- const fieldsString = getFieldsForContentType();
1267
- const start = (page - 1) * limit;
1268
- const end = start + limit;
1269
- const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1270
- const includedFieldsFilter = includedFields.length > 0
1271
- ? filtersToGroq(includedFields)
1272
- : "";
1273
- const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && railcontent_id == ${id}]._id)`;
1274
- const filterWithRestrictions = await new FilterBuilder(filter).buildFilter();
1275
-
1276
- sortOrder = getSortOrder(sortOrder, brand);
1277
- const query = buildEntityAndTotalQuery(
1278
- filterWithRestrictions,
1279
- fieldsString,
1280
- {
1281
- sortOrder: sortOrder,
1282
- start: start,
1283
- end: end,
1284
- },
1285
- );
1286
- return fetchSanity(query, true);
1300
+ export async function fetchCoachLessons(
1301
+ brand,
1302
+ id,
1303
+ { sortOrder = '-published_on', searchTerm = '', page = 1, limit = 20, includedFields = [] } = {}
1304
+ ) {
1305
+ const fieldsString = getFieldsForContentType()
1306
+ const start = (page - 1) * limit
1307
+ const end = start + limit
1308
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1309
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
1310
+ const filter = `brand == '${brand}' ${searchFilter} ${includedFieldsFilter} && references(*[_type=='instructor' && railcontent_id == ${id}]._id)`
1311
+ const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
1312
+
1313
+ sortOrder = getSortOrder(sortOrder, brand)
1314
+ const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
1315
+ sortOrder: sortOrder,
1316
+ start: start,
1317
+ end: end,
1318
+ })
1319
+ return fetchSanity(query, true)
1287
1320
  }
1288
1321
 
1289
1322
  /**
@@ -1297,15 +1330,15 @@ export async function fetchCoachLessons(brand, id, {
1297
1330
  * .catch(error => console.error(error));
1298
1331
  */
1299
1332
  export async function fetchParentForDownload(id) {
1300
- const query = buildRawQuery(
1301
- `railcontent_id == ${id}`,
1302
- getFieldsForContentType('parent-download'),
1303
- {
1304
- isSingle: true,
1305
- },
1306
- );
1307
-
1308
- return fetchSanity(query, false);
1333
+ const query = buildRawQuery(
1334
+ `railcontent_id == ${id}`,
1335
+ getFieldsForContentType('parent-download'),
1336
+ {
1337
+ isSingle: true,
1338
+ }
1339
+ )
1340
+
1341
+ return fetchSanity(query, false)
1309
1342
  }
1310
1343
 
1311
1344
  /**
@@ -1319,33 +1352,24 @@ export async function fetchParentForDownload(id) {
1319
1352
  * .then(lessons => console.log(lessons))
1320
1353
  * .catch(error => console.error(error));
1321
1354
  */
1322
- export async function fetchByReference(brand, {
1323
- sortOrder = '-published_on',
1324
- searchTerm = '',
1325
- page = 1,
1326
- limit = 20,
1327
- includedFields = [],
1328
- } = {}) {
1329
- const fieldsString = getFieldsForContentType();
1330
- const start = (page - 1) * limit;
1331
- const end = start + limit;
1332
- const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : '';
1333
- const includedFieldsFilter = includedFields.length > 0
1334
- ? includedFields.join(' && ')
1335
- : "";
1336
-
1337
- const filter = `brand == '${brand}' ${searchFilter} && references(*[${includedFieldsFilter}]._id)`;
1338
- const filterWithRestrictions = await new FilterBuilder(filter).buildFilter();
1339
- const query = buildEntityAndTotalQuery(
1340
- filterWithRestrictions,
1341
- fieldsString,
1342
- {
1343
- sortOrder: getSortOrder(sortOrder, brand),
1344
- start: start,
1345
- end: end,
1346
- },
1347
- );
1348
- return fetchSanity(query, true);
1355
+ export async function fetchByReference(
1356
+ brand,
1357
+ { sortOrder = '-published_on', searchTerm = '', page = 1, limit = 20, includedFields = [] } = {}
1358
+ ) {
1359
+ const fieldsString = getFieldsForContentType()
1360
+ const start = (page - 1) * limit
1361
+ const end = start + limit
1362
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1363
+ const includedFieldsFilter = includedFields.length > 0 ? includedFields.join(' && ') : ''
1364
+
1365
+ const filter = `brand == '${brand}' ${searchFilter} && references(*[${includedFieldsFilter}]._id)`
1366
+ const filterWithRestrictions = await new FilterBuilder(filter).buildFilter()
1367
+ const query = buildEntityAndTotalQuery(filterWithRestrictions, fieldsString, {
1368
+ sortOrder: getSortOrder(sortOrder, brand),
1369
+ start: start,
1370
+ end: end,
1371
+ })
1372
+ return fetchSanity(query, true)
1349
1373
  }
1350
1374
 
1351
1375
  /**
@@ -1367,29 +1391,37 @@ export async function fetchByReference(brand, {
1367
1391
  * .then(lessons => console.log(lessons))
1368
1392
  * .catch(error => console.error(error));
1369
1393
  */
1370
- export async function fetchArtistLessons(brand, name, contentType, {
1394
+ export async function fetchArtistLessons(
1395
+ brand,
1396
+ name,
1397
+ contentType,
1398
+ {
1371
1399
  sort = '-published_on',
1372
1400
  searchTerm = '',
1373
1401
  page = 1,
1374
1402
  limit = 10,
1375
1403
  includedFields = [],
1376
1404
  progressIds = undefined,
1377
- } = {}) {
1378
-
1379
- const fieldsString = DEFAULT_FIELDS.join(',');
1380
- const start = (page - 1) * limit;
1381
- const end = start + limit;
1382
- const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1383
- const sortOrder = getSortOrder(sort, brand);
1384
- const addType = contentType && Array.isArray(contentType) ? `_type in ['${contentType.join("', '")}'] &&` : contentType ? `_type == '${contentType}' && ` : ''
1385
- const includedFieldsFilter = includedFields.length > 0
1386
- ? filtersToGroq(includedFields)
1387
- : "";
1388
-
1389
- // limits the results to supplied progressIds for started & completed filters
1390
- const progressFilter = progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : "";
1391
- const now = getSanityDate(new Date());
1392
- const query = `{
1405
+ } = {}
1406
+ ) {
1407
+ const fieldsString = DEFAULT_FIELDS.join(',')
1408
+ const start = (page - 1) * limit
1409
+ const end = start + limit
1410
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1411
+ const sortOrder = getSortOrder(sort, brand)
1412
+ const addType =
1413
+ contentType && Array.isArray(contentType)
1414
+ ? `_type in ['${contentType.join("', '")}'] &&`
1415
+ : contentType
1416
+ ? `_type == '${contentType}' && `
1417
+ : ''
1418
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
1419
+
1420
+ // limits the results to supplied progressIds for started & completed filters
1421
+ const progressFilter =
1422
+ progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
1423
+ const now = getSanityDate(new Date())
1424
+ const query = `{
1393
1425
  "entity":
1394
1426
  *[_type == 'artist' && name == '${name}']
1395
1427
  {'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
@@ -1397,8 +1429,8 @@ export async function fetchArtistLessons(brand, name, contentType, {
1397
1429
  'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1398
1430
  [${start}...${end}]}
1399
1431
  |order(${sortOrder})
1400
- }`;
1401
- return fetchSanity(query, true);
1432
+ }`
1433
+ return fetchSanity(query, true)
1402
1434
  }
1403
1435
 
1404
1436
  /**
@@ -1419,27 +1451,31 @@ export async function fetchArtistLessons(brand, name, contentType, {
1419
1451
  * .then(lessons => console.log(lessons))
1420
1452
  * .catch(error => console.error(error));
1421
1453
  */
1422
- export async function fetchGenreLessons(brand, name, contentType, {
1454
+ export async function fetchGenreLessons(
1455
+ brand,
1456
+ name,
1457
+ contentType,
1458
+ {
1423
1459
  sort = '-published_on',
1424
1460
  searchTerm = '',
1425
1461
  page = 1,
1426
1462
  limit = 10,
1427
1463
  includedFields = [],
1428
1464
  progressIds = undefined,
1429
- } = {}) {
1430
- const fieldsString = DEFAULT_FIELDS.join(',');
1431
- const start = (page - 1) * limit;
1432
- const end = start + limit;
1433
- const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1434
- const sortOrder = getSortOrder(sort, brand);
1435
- const addType = contentType ? `_type == '${contentType}' && ` : ''
1436
- const includedFieldsFilter = includedFields.length > 0
1437
- ? filtersToGroq(includedFields)
1438
- : "";
1439
- // limits the results to supplied progressIds for started & completed filters
1440
- const progressFilter = progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : "";
1441
- const now = getSanityDate(new Date());
1442
- const query = `{
1465
+ } = {}
1466
+ ) {
1467
+ const fieldsString = DEFAULT_FIELDS.join(',')
1468
+ const start = (page - 1) * limit
1469
+ const end = start + limit
1470
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
1471
+ const sortOrder = getSortOrder(sort, brand)
1472
+ const addType = contentType ? `_type == '${contentType}' && ` : ''
1473
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
1474
+ // limits the results to supplied progressIds for started & completed filters
1475
+ const progressFilter =
1476
+ progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
1477
+ const now = getSanityDate(new Date())
1478
+ const query = `{
1443
1479
  "entity":
1444
1480
  *[_type == 'genre' && name == '${name}']
1445
1481
  {'type': _type, name, 'thumbnail_url':thumbnail_url.asset->url,
@@ -1447,14 +1483,14 @@ export async function fetchGenreLessons(brand, name, contentType, {
1447
1483
  'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
1448
1484
  [${start}...${end}]}
1449
1485
  |order(${sortOrder})
1450
- }`;
1451
- return fetchSanity(query, true);
1486
+ }`
1487
+ return fetchSanity(query, true)
1452
1488
  }
1453
1489
 
1454
1490
  export async function fetchTopLevelParentId(railcontentId) {
1455
- const statusFilter = "&& status in ['scheduled', 'published', 'archived', 'unlisted']";
1491
+ const statusFilter = "&& status in ['scheduled', 'published', 'archived', 'unlisted']"
1456
1492
 
1457
- const query = `*[railcontent_id == ${railcontentId}]{
1493
+ const query = `*[railcontent_id == ${railcontentId}]{
1458
1494
  railcontent_id,
1459
1495
  'parents': *[^._id in child[]._ref ${statusFilter}]{
1460
1496
  railcontent_id,
@@ -1468,24 +1504,24 @@ export async function fetchTopLevelParentId(railcontentId) {
1468
1504
  }
1469
1505
  }
1470
1506
  }
1471
- }`;
1472
- let response = await fetchSanity(query, false, {processNeedAccess: false});
1473
- if (!response) return null;
1474
- let currentLevel = response;
1475
- for (let i = 0; i < 4; i++) {
1476
- if (currentLevel['parents'].length > 0) {
1477
- currentLevel = currentLevel['parents'][0];
1478
- } else {
1479
- return currentLevel['railcontent_id'];
1480
- }
1507
+ }`
1508
+ let response = await fetchSanity(query, false, { processNeedAccess: false })
1509
+ if (!response) return null
1510
+ let currentLevel = response
1511
+ for (let i = 0; i < 4; i++) {
1512
+ if (currentLevel['parents'].length > 0) {
1513
+ currentLevel = currentLevel['parents'][0]
1514
+ } else {
1515
+ return currentLevel['railcontent_id']
1481
1516
  }
1482
- return null;
1517
+ }
1518
+ return null
1483
1519
  }
1484
1520
 
1485
1521
  export async function fetchHierarchy(railcontentId) {
1486
- let topLevelId = await fetchTopLevelParentId(railcontentId);
1487
- const childrenFilter = await new FilterBuilder(``, {isChildrenFilter: true} ).buildFilter();
1488
- const query = `*[railcontent_id == ${topLevelId}]{
1522
+ let topLevelId = await fetchTopLevelParentId(railcontentId)
1523
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1524
+ const query = `*[railcontent_id == ${topLevelId}]{
1489
1525
  railcontent_id,
1490
1526
  'assignments': assignment[]{railcontent_id},
1491
1527
  'children': child[${childrenFilter}]->{
@@ -1503,42 +1539,40 @@ export async function fetchHierarchy(railcontentId) {
1503
1539
  }
1504
1540
  }
1505
1541
  },
1506
- }`;
1507
- let response = await fetchSanity(query, false, {processNeedAccess: false});
1508
- if (!response) return null;
1509
- let data = {
1510
- topLevelId: topLevelId,
1511
- parents: {},
1512
- children: {}
1513
- };
1514
- populateHierarchyLookups(response, data, null);
1515
- return data;
1542
+ }`
1543
+ let response = await fetchSanity(query, false, { processNeedAccess: false })
1544
+ if (!response) return null
1545
+ let data = {
1546
+ topLevelId: topLevelId,
1547
+ parents: {},
1548
+ children: {},
1549
+ }
1550
+ populateHierarchyLookups(response, data, null)
1551
+ return data
1516
1552
  }
1517
1553
 
1518
-
1519
1554
  function populateHierarchyLookups(currentLevel, data, parentId) {
1520
- let contentId = currentLevel['railcontent_id'];
1521
- let children = currentLevel['children'];
1522
-
1523
- data.parents[contentId] = parentId;
1524
- if (children) {
1525
- data.children[contentId] = children.map(child => child['railcontent_id']);
1526
- for (let i = 0; i < children.length; i++) {
1527
- populateHierarchyLookups(children[i], data, contentId);
1528
- }
1529
- } else {
1530
- data.children[contentId] = [];
1555
+ let contentId = currentLevel['railcontent_id']
1556
+ let children = currentLevel['children']
1557
+
1558
+ data.parents[contentId] = parentId
1559
+ if (children) {
1560
+ data.children[contentId] = children.map((child) => child['railcontent_id'])
1561
+ for (let i = 0; i < children.length; i++) {
1562
+ populateHierarchyLookups(children[i], data, contentId)
1531
1563
  }
1532
-
1533
- let assignments = currentLevel['assignments'];
1534
- if (assignments) {
1535
- let assignmentIds = assignments.map(assignment => assignment['railcontent_id']);
1536
- data.children[contentId] = (data.children[contentId] ?? []).concat(assignmentIds);
1537
- assignmentIds.forEach(assignmentId => {
1538
- data.parents[assignmentId] = contentId;
1539
- });
1540
- }
1541
-
1564
+ } else {
1565
+ data.children[contentId] = []
1566
+ }
1567
+
1568
+ let assignments = currentLevel['assignments']
1569
+ if (assignments) {
1570
+ let assignmentIds = assignments.map((assignment) => assignment['railcontent_id'])
1571
+ data.children[contentId] = (data.children[contentId] ?? []).concat(assignmentIds)
1572
+ assignmentIds.forEach((assignmentId) => {
1573
+ data.parents[assignmentId] = contentId
1574
+ })
1575
+ }
1542
1576
  }
1543
1577
 
1544
1578
  /**
@@ -1548,27 +1582,28 @@ function populateHierarchyLookups(currentLevel, data, parentId) {
1548
1582
  * @returns {Promise<Object|null>} - A promise that resolves to an object containing the data
1549
1583
  */
1550
1584
  export async function fetchCommentModContentData(ids) {
1551
- const idsString = ids.join(',');
1552
- const fields = `"id": railcontent_id, "type": _type, title, "url": web_url_path, "parent": *[^._id in child[]._ref]{"id": railcontent_id, title}`;
1553
- const query = await buildQuery(`railcontent_id in [${idsString}]`,
1554
- {bypassPermissions: true},
1555
- fields,
1556
- {end: 50});
1557
- let data = await fetchSanity(query, true);
1558
- let mapped = {};
1559
- data.forEach(function (content) {
1560
- mapped[content.id] = {
1561
- "id": content.id,
1562
- "type": content.type,
1563
- "title": content.title,
1564
- "url": content.url,
1565
- "parentTitle": content.parent[0]?.title ?? null
1566
- };
1567
- });
1568
- return mapped;
1585
+ const idsString = ids.join(',')
1586
+ const fields = `"id": railcontent_id, "type": _type, title, "url": web_url_path, "parent": *[^._id in child[]._ref]{"id": railcontent_id, title}`
1587
+ const query = await buildQuery(
1588
+ `railcontent_id in [${idsString}]`,
1589
+ { bypassPermissions: true },
1590
+ fields,
1591
+ { end: 50 }
1592
+ )
1593
+ let data = await fetchSanity(query, true)
1594
+ let mapped = {}
1595
+ data.forEach(function (content) {
1596
+ mapped[content.id] = {
1597
+ id: content.id,
1598
+ type: content.type,
1599
+ title: content.title,
1600
+ url: content.url,
1601
+ parentTitle: content.parent[0]?.title ?? null,
1602
+ }
1603
+ })
1604
+ return mapped
1569
1605
  }
1570
1606
 
1571
-
1572
1607
  /**
1573
1608
  *
1574
1609
  * @param {string} query - The GROQ query to execute against the Sanity API.
@@ -1584,110 +1619,110 @@ export async function fetchCommentModContentData(ids) {
1584
1619
  * .catch(error => console.error(error));
1585
1620
  */
1586
1621
 
1587
- export async function fetchSanity(query,
1588
- isList,
1589
- {
1590
- customPostProcess = null,
1591
- processNeedAccess = true,
1592
- } = {}
1622
+ export async function fetchSanity(
1623
+ query,
1624
+ isList,
1625
+ { customPostProcess = null, processNeedAccess = true } = {}
1593
1626
  ) {
1594
- // Check the config object before proceeding
1595
- if (!checkSanityConfig(globalConfig)) {
1596
- return null;
1627
+ // Check the config object before proceeding
1628
+ if (!checkSanityConfig(globalConfig)) {
1629
+ return null
1630
+ }
1631
+
1632
+ if (globalConfig.sanityConfig.debug) {
1633
+ console.log('fetchSanity Query:', query)
1634
+ }
1635
+ const perspective = globalConfig.sanityConfig.perspective ?? 'published'
1636
+ const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api'
1637
+ const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`
1638
+ const headers = {
1639
+ Authorization: `Bearer ${globalConfig.sanityConfig.token}`,
1640
+ 'Content-Type': 'application/json',
1641
+ }
1642
+
1643
+ try {
1644
+ const method = 'post'
1645
+ const options = {
1646
+ method,
1647
+ headers,
1648
+ body: JSON.stringify({ query: query }),
1597
1649
  }
1598
1650
 
1599
- if (globalConfig.sanityConfig.debug) {
1600
- console.log("fetchSanity Query:", query);
1651
+ let promisesResult = await Promise.all([
1652
+ fetch(url, options),
1653
+ processNeedAccess ? fetchUserPermissions() : null,
1654
+ ])
1655
+ const response = promisesResult[0]
1656
+ const userPermissions = promisesResult[1]?.permissions
1657
+ const isAdmin = promisesResult[1]?.isAdmin
1658
+
1659
+ if (!response.ok) {
1660
+ throw new Error(`Sanity API error: ${response.status} - ${response.statusText}`)
1601
1661
  }
1602
- const perspective = globalConfig.sanityConfig.perspective ?? 'published';
1603
- const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api';
1604
- const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`;
1605
- const headers = {
1606
- 'Authorization': `Bearer ${globalConfig.sanityConfig.token}`,
1607
- 'Content-Type': 'application/json'
1608
- };
1609
-
1610
- try {
1611
- const method = 'post';
1612
- const options = {
1613
- method,
1614
- headers,
1615
- body: JSON.stringify({'query': query})
1616
- };
1617
-
1618
- let promisesResult = await Promise.all([
1619
- fetch(url, options),
1620
- processNeedAccess ? fetchUserPermissions() : null
1621
- ]);
1622
- const response = promisesResult[0];
1623
- const userPermissions = promisesResult[1]?.permissions;
1624
- const isAdmin = promisesResult[1]?.isAdmin;
1625
-
1626
- if (!response.ok) {
1627
- throw new Error(`Sanity API error: ${response.status} - ${response.statusText}`);
1628
- }
1629
- const result = await response.json();
1630
- if (result.result) {
1631
- if (globalConfig.sanityConfig.debug) {
1632
- console.log("fetchSanity Results:", result);
1633
- }
1634
- let results = isList ? result.result : result.result[0];
1635
- results = processNeedAccess ? await needsAccessDecorator(results, userPermissions, isAdmin) : results;
1636
- return customPostProcess ? customPostProcess(results) : results;
1637
- } else {
1638
- throw new Error('No results found');
1639
- }
1640
- } catch (error) {
1641
- console.error('fetchSanity: Fetch error:', error);
1642
- return null;
1662
+ const result = await response.json()
1663
+ if (result.result) {
1664
+ if (globalConfig.sanityConfig.debug) {
1665
+ console.log('fetchSanity Results:', result)
1666
+ }
1667
+ let results = isList ? result.result : result.result[0]
1668
+ results = processNeedAccess
1669
+ ? await needsAccessDecorator(results, userPermissions, isAdmin)
1670
+ : results
1671
+ return customPostProcess ? customPostProcess(results) : results
1672
+ } else {
1673
+ throw new Error('No results found')
1643
1674
  }
1675
+ } catch (error) {
1676
+ console.error('fetchSanity: Fetch error:', error)
1677
+ return null
1678
+ }
1644
1679
  }
1645
1680
 
1646
1681
  function needsAccessDecorator(results, userPermissions, isAdmin) {
1647
- if (globalConfig.sanityConfig.useDummyRailContentMethods) return results;
1648
-
1649
- userPermissions = new Set(userPermissions);
1650
-
1651
- if (Array.isArray(results)) {
1652
- results.forEach((result) => {
1653
- result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin);
1654
- });
1655
- } else if (results.entity && Array.isArray(results.entity)) {
1656
- // Group By
1657
- results.entity.forEach((result) => {
1658
- if (result.lessons) {
1659
- result.lessons.forEach((lesson) => {
1660
- lesson['need_access'] = doesUserNeedAccessToContent(lesson, userPermissions, isAdmin); // Updated to check lesson access
1661
- });
1662
- } else {
1663
- result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin);
1664
- }
1665
- });
1666
- } else if (results.related_lessons && Array.isArray(results.related_lessons)) {
1667
- results.related_lessons.forEach((result) => {
1668
- result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin);
1682
+ if (globalConfig.sanityConfig.useDummyRailContentMethods) return results
1683
+
1684
+ userPermissions = new Set(userPermissions)
1685
+
1686
+ if (Array.isArray(results)) {
1687
+ results.forEach((result) => {
1688
+ result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
1689
+ })
1690
+ } else if (results.entity && Array.isArray(results.entity)) {
1691
+ // Group By
1692
+ results.entity.forEach((result) => {
1693
+ if (result.lessons) {
1694
+ result.lessons.forEach((lesson) => {
1695
+ lesson['need_access'] = doesUserNeedAccessToContent(lesson, userPermissions, isAdmin) // Updated to check lesson access
1669
1696
  })
1670
- } else {
1671
- results['need_access'] = doesUserNeedAccessToContent(results, userPermissions, isAdmin);
1672
- }
1673
-
1674
- return results;
1697
+ } else {
1698
+ result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
1699
+ }
1700
+ })
1701
+ } else if (results.related_lessons && Array.isArray(results.related_lessons)) {
1702
+ results.related_lessons.forEach((result) => {
1703
+ result['need_access'] = doesUserNeedAccessToContent(result, userPermissions, isAdmin)
1704
+ })
1705
+ } else {
1706
+ results['need_access'] = doesUserNeedAccessToContent(results, userPermissions, isAdmin)
1707
+ }
1708
+
1709
+ return results
1675
1710
  }
1676
1711
 
1677
1712
  function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
1678
- if (isAdmin ?? false) {
1679
- return false;
1680
- }
1681
- const permissions = new Set(result?.permission_id ?? []);
1682
- if (permissions.size === 0) {
1683
- return false;
1713
+ if (isAdmin ?? false) {
1714
+ return false
1715
+ }
1716
+ const permissions = new Set(result?.permission_id ?? [])
1717
+ if (permissions.size === 0) {
1718
+ return false
1719
+ }
1720
+ for (let permission of permissions) {
1721
+ if (userPermissions.has(permission)) {
1722
+ return false
1684
1723
  }
1685
- for (let permission of permissions) {
1686
- if (userPermissions.has(permission)) {
1687
- return false;
1688
- }
1689
- }
1690
- return true;
1724
+ }
1725
+ return true
1691
1726
  }
1692
1727
 
1693
1728
  /**
@@ -1703,15 +1738,15 @@ function doesUserNeedAccessToContent(result, userPermissions, isAdmin) {
1703
1738
  * .catch(error => console.error(error));
1704
1739
  */
1705
1740
  export async function fetchShowsData(brand) {
1706
- let shows = showsTypes[brand] ?? [];
1707
- const showsInfo = [];
1741
+ let shows = showsTypes[brand] ?? []
1742
+ const showsInfo = []
1708
1743
 
1709
- shows.forEach(type => {
1710
- const processedData = processMetadata(brand, type);
1711
- if (processedData) showsInfo.push(processedData)
1712
- });
1744
+ shows.forEach((type) => {
1745
+ const processedData = processMetadata(brand, type)
1746
+ if (processedData) showsInfo.push(processedData)
1747
+ })
1713
1748
 
1714
- return showsInfo;
1749
+ return showsInfo
1715
1750
  }
1716
1751
 
1717
1752
  /**
@@ -1728,135 +1763,122 @@ export async function fetchShowsData(brand) {
1728
1763
  * .catch(error => console.error(error));
1729
1764
  */
1730
1765
  export async function fetchMetadata(brand, type) {
1731
- const processedData = processMetadata(brand, type, true);
1732
- return processedData ? processedData : {};
1766
+ const processedData = processMetadata(brand, type, true)
1767
+ return processedData ? processedData : {}
1733
1768
  }
1734
1769
 
1735
1770
  export async function fetchChatAndLiveEnvent(brand, forcedId = null) {
1736
- const liveEvent = (forcedId !== null) ? await fetchByRailContentIds([forcedId]): [await fetchLiveEvent(brand)];
1737
- if (liveEvent.length === 0 || (liveEvent.length === 1 && liveEvent[0] === undefined)) {
1738
- return null;
1739
- }
1740
- let url = `/content/live-chat?brand=${brand}`;
1741
- const chatData = await fetchHandler(url);
1742
- const mergedData = { ...chatData, ...liveEvent[0] };
1743
- return mergedData;
1771
+ const liveEvent =
1772
+ forcedId !== null ? await fetchByRailContentIds([forcedId]) : [await fetchLiveEvent(brand)]
1773
+ if (liveEvent.length === 0 || (liveEvent.length === 1 && liveEvent[0] === undefined)) {
1774
+ return null
1775
+ }
1776
+ let url = `/content/live-chat?brand=${brand}`
1777
+ const chatData = await fetchHandler(url)
1778
+ const mergedData = { ...chatData, ...liveEvent[0] }
1779
+ return mergedData
1744
1780
  }
1745
1781
 
1746
-
1747
1782
  //Helper Functions
1748
1783
  function arrayJoinWithQuotes(array, delimiter = ',') {
1749
- const wrapped = array.map(value => `'${value}'`);
1750
- return wrapped.join(delimiter)
1784
+ const wrapped = array.map((value) => `'${value}'`)
1785
+ return wrapped.join(delimiter)
1751
1786
  }
1752
1787
 
1753
1788
  function getSanityDate(date, roundToHourForCaching = true) {
1754
- if (roundToHourForCaching) {
1755
- // We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
1756
- // with every request by changing the filter date every second. I've set it to one minute past the current hour
1757
- // because publishing usually publishes content on the hour exactly which means it should still skip the cache
1758
- // when the new content is available.
1759
- // Round to the start of the current hour
1760
- const roundedDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours());
1761
-
1762
- return roundedDate.toISOString();
1763
- }
1764
-
1765
- return date.toISOString();
1789
+ if (roundToHourForCaching) {
1790
+ // We need to set the published on filter date to be a round time so that it doesn't bypass the query cache
1791
+ // with every request by changing the filter date every second. I've set it to one minute past the current hour
1792
+ // because publishing usually publishes content on the hour exactly which means it should still skip the cache
1793
+ // when the new content is available.
1794
+ // Round to the start of the current hour
1795
+ const roundedDate = new Date(
1796
+ date.getFullYear(),
1797
+ date.getMonth(),
1798
+ date.getDate(),
1799
+ date.getHours()
1800
+ )
1801
+
1802
+ return roundedDate.toISOString()
1803
+ }
1804
+
1805
+ return date.toISOString()
1766
1806
  }
1767
1807
 
1768
1808
  const merge = (a, b, predicate = (a, b) => a === b) => {
1769
- const c = [...a]; // copy to avoid side effects
1770
- // add all items from B to copy C if they're not already present
1771
- b.forEach((bItem) => (c.some((cItem) => predicate(bItem, cItem)) ? null : c.push(bItem)))
1772
- return c;
1809
+ const c = [...a] // copy to avoid side effects
1810
+ // add all items from B to copy C if they're not already present
1811
+ b.forEach((bItem) => (c.some((cItem) => predicate(bItem, cItem)) ? null : c.push(bItem)))
1812
+ return c
1773
1813
  }
1774
1814
 
1775
1815
  function checkSanityConfig(config) {
1776
- if (!config.sanityConfig.token) {
1777
- console.warn('fetchSanity: The "token" property is missing in the config object.');
1778
- return false;
1779
- }
1780
- if (!config.sanityConfig.projectId) {
1781
- console.warn('fetchSanity: The "projectId" property is missing in the config object.');
1782
- return false;
1783
- }
1784
- if (!config.sanityConfig.dataset) {
1785
- console.warn('fetchSanity: The "dataset" property is missing in the config object.');
1786
- return false;
1787
- }
1788
- if (!config.sanityConfig.version) {
1789
- console.warn('fetchSanity: The "version" property is missing in the config object.');
1790
- return false;
1791
- }
1792
- return true;
1816
+ if (!config.sanityConfig.token) {
1817
+ console.warn('fetchSanity: The "token" property is missing in the config object.')
1818
+ return false
1819
+ }
1820
+ if (!config.sanityConfig.projectId) {
1821
+ console.warn('fetchSanity: The "projectId" property is missing in the config object.')
1822
+ return false
1823
+ }
1824
+ if (!config.sanityConfig.dataset) {
1825
+ console.warn('fetchSanity: The "dataset" property is missing in the config object.')
1826
+ return false
1827
+ }
1828
+ if (!config.sanityConfig.version) {
1829
+ console.warn('fetchSanity: The "version" property is missing in the config object.')
1830
+ return false
1831
+ }
1832
+ return true
1793
1833
  }
1794
1834
 
1795
-
1796
1835
  function buildRawQuery(
1797
- filter = '',
1798
- fields = '...',
1799
- {
1800
- sortOrder = 'published_on desc',
1801
- start = 0,
1802
- end = 10,
1803
- isSingle = false,
1804
- }
1836
+ filter = '',
1837
+ fields = '...',
1838
+ { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
1805
1839
  ) {
1806
- const sortString = sortOrder ? `order(${sortOrder})` : '';
1807
- const countString = isSingle ? '[0...1]' : `[${start}...${end}]`;
1808
- const query = `*[${filter}]{
1840
+ const sortString = sortOrder ? `order(${sortOrder})` : ''
1841
+ const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
1842
+ const query = `*[${filter}]{
1809
1843
  ${fields}
1810
1844
  } | ${sortString}${countString}`
1811
- return query;
1845
+ return query
1812
1846
  }
1813
1847
 
1814
-
1815
1848
  async function buildQuery(
1816
- baseFilter = '',
1817
- filterParams = {pullFutureContent: false},
1818
- fields = '...',
1819
- {
1820
- sortOrder = 'published_on desc',
1821
- start = 0,
1822
- end = 10,
1823
- isSingle = false,
1824
- },
1849
+ baseFilter = '',
1850
+ filterParams = { pullFutureContent: false },
1851
+ fields = '...',
1852
+ { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
1825
1853
  ) {
1826
- const filter = await new FilterBuilder(baseFilter, filterParams).buildFilter();
1827
- return buildRawQuery(filter, fields, {sortOrder, start, end, isSingle});
1854
+ const filter = await new FilterBuilder(baseFilter, filterParams).buildFilter()
1855
+ return buildRawQuery(filter, fields, { sortOrder, start, end, isSingle })
1828
1856
  }
1829
1857
 
1830
1858
  function buildEntityAndTotalQuery(
1831
- filter = '',
1832
- fields = '...',
1833
- {
1834
- sortOrder = 'published_on desc',
1835
- start = 0,
1836
- end = 10,
1837
- isSingle = false,
1838
- },
1859
+ filter = '',
1860
+ fields = '...',
1861
+ { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
1839
1862
  ) {
1840
- const sortString = sortOrder ? `order(${sortOrder})` : '';
1841
- const countString = isSingle ? '[0...1]' : `[${start}...${end}]`;
1842
- const query = `{
1863
+ const sortString = sortOrder ? `order(${sortOrder})` : ''
1864
+ const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
1865
+ const query = `{
1843
1866
  "entity": *[${filter}] | ${sortString}${countString}
1844
1867
  {
1845
1868
  ${fields}
1846
1869
  },
1847
1870
  "total": 0
1848
- }`;
1849
- return query;
1871
+ }`
1872
+ return query
1850
1873
  }
1851
1874
 
1852
-
1853
1875
  function getFilterOptions(option, commonFilter, contentType, brand) {
1854
- let filterGroq = '';
1855
- const types = Array.from(new Set([...coachLessonsTypes, ...showsTypes[brand]]));
1876
+ let filterGroq = ''
1877
+ const types = Array.from(new Set([...coachLessonsTypes, ...showsTypes[brand]]))
1856
1878
 
1857
- switch (option) {
1858
- case "difficulty":
1859
- filterGroq = `
1879
+ switch (option) {
1880
+ case 'difficulty':
1881
+ filterGroq = `
1860
1882
  "difficulty": [
1861
1883
  {"type": "All", "count": count(*[${commonFilter} && difficulty_string == "All"])},
1862
1884
  {"type": "Introductory", "count": count(*[${commonFilter} && difficulty_string == "Introductory"])},
@@ -1864,84 +1886,82 @@ function getFilterOptions(option, commonFilter, contentType, brand) {
1864
1886
  {"type": "Intermediate", "count": count(*[${commonFilter} && difficulty_string == "Intermediate" ])},
1865
1887
  {"type": "Advanced", "count": count(*[${commonFilter} && difficulty_string == "Advanced" ])},
1866
1888
  {"type": "Expert", "count": count(*[${commonFilter} && difficulty_string == "Expert" ])}
1867
- ][count > 0],`;
1868
- break;
1869
- case "type":
1870
- const typesString = types.map(t => {
1871
- return `{"type": "${t}"}`
1872
- }).join(', ');
1873
- filterGroq = `"type": [${typesString}]{type, 'count': count(*[_type == ^.type && ${commonFilter}])}[count > 0],`;
1874
- break;
1875
- case "genre":
1876
- case "essential":
1877
- case "focus":
1878
- case "theory":
1879
- case "topic":
1880
- case "lifestyle":
1881
- case "creativity":
1882
- filterGroq = `
1889
+ ][count > 0],`
1890
+ break
1891
+ case 'type':
1892
+ const typesString = types
1893
+ .map((t) => {
1894
+ return `{"type": "${t}"}`
1895
+ })
1896
+ .join(', ')
1897
+ filterGroq = `"type": [${typesString}]{type, 'count': count(*[_type == ^.type && ${commonFilter}])}[count > 0],`
1898
+ break
1899
+ case 'genre':
1900
+ case 'essential':
1901
+ case 'focus':
1902
+ case 'theory':
1903
+ case 'topic':
1904
+ case 'lifestyle':
1905
+ case 'creativity':
1906
+ filterGroq = `
1883
1907
  "${option}": *[_type == '${option}' ${contentType ? ` && '${contentType}' in filter_types` : ''} ] {
1884
1908
  "type": name,
1885
1909
  "count": count(*[${commonFilter} && references(^._id)])
1886
- }[count > 0],`;
1887
- break;
1888
- case "instrumentless":
1889
- filterGroq = `
1910
+ }[count > 0],`
1911
+ break
1912
+ case 'instrumentless':
1913
+ filterGroq = `
1890
1914
  "${option}": [
1891
1915
  {"type": "Full Song Only", "count": count(*[${commonFilter} && instrumentless == false ])},
1892
1916
  {"type": "Instrument Removed", "count": count(*[${commonFilter} && instrumentless == true ])}
1893
- ][count > 0],`;
1894
- break;
1895
- case "gear":
1896
- filterGroq = `
1917
+ ][count > 0],`
1918
+ break
1919
+ case 'gear':
1920
+ filterGroq = `
1897
1921
  "${option}": [
1898
1922
  {"type": "Practice Pad", "count": count(*[${commonFilter} && gear match 'Practice Pad' ])},
1899
1923
  {"type": "Drum-Set", "count": count(*[${commonFilter} && gear match 'Drum-Set'])}
1900
- ][count > 0],`;
1901
- break;
1902
- case "bpm":
1903
- filterGroq = `
1924
+ ][count > 0],`
1925
+ break
1926
+ case 'bpm':
1927
+ filterGroq = `
1904
1928
  "${option}": [
1905
1929
  {"type": "50-90", "count": count(*[${commonFilter} && bpm > 50 && bpm < 91])},
1906
1930
  {"type": "91-120", "count": count(*[${commonFilter} && bpm > 90 && bpm < 121])},
1907
1931
  {"type": "121-150", "count": count(*[${commonFilter} && bpm > 120 && bpm < 151])},
1908
1932
  {"type": "151-180", "count": count(*[${commonFilter} && bpm > 150 && bpm < 181])},
1909
1933
  {"type": "180+", "count": count(*[${commonFilter} && bpm > 180])},
1910
- ][count > 0],`;
1911
- break;
1912
- default:
1913
- filterGroq = "";
1914
- break;
1915
- }
1916
-
1917
- return filterGroq;
1934
+ ][count > 0],`
1935
+ break
1936
+ default:
1937
+ filterGroq = ''
1938
+ break
1939
+ }
1940
+
1941
+ return filterGroq
1918
1942
  }
1919
1943
 
1920
1944
  function cleanUpGroq(query) {
1921
- // Split the query into clauses based on the logical operators
1922
- const clauses = query.split(/(\s*&&|\s*\|\|)/).map(clause => clause.trim());
1945
+ // Split the query into clauses based on the logical operators
1946
+ const clauses = query.split(/(\s*&&|\s*\|\|)/).map((clause) => clause.trim())
1923
1947
 
1924
- // Filter out empty clauses
1925
- const filteredClauses = clauses.filter(clause => clause.length > 0);
1948
+ // Filter out empty clauses
1949
+ const filteredClauses = clauses.filter((clause) => clause.length > 0)
1926
1950
 
1927
- // Check if there are valid conditions in the clauses
1928
- const hasConditions = filteredClauses.some(clause => !clause.match(/^\s*&&\s*|\s*\|\|\s*$/));
1951
+ // Check if there are valid conditions in the clauses
1952
+ const hasConditions = filteredClauses.some((clause) => !clause.match(/^\s*&&\s*|\s*\|\|\s*$/))
1929
1953
 
1930
- if (!hasConditions) {
1931
- // If no valid conditions, return an empty string or the original query
1932
- return '';
1933
- }
1954
+ if (!hasConditions) {
1955
+ // If no valid conditions, return an empty string or the original query
1956
+ return ''
1957
+ }
1934
1958
 
1935
- // Remove occurrences of '&& ()'
1936
- const cleanedQuery = filteredClauses.join(' ')
1937
- .replace(/&&\s*\(\)/g, '')
1938
- .replace(/(\s*&&|\s*\|\|)(?=\s*[\s()]*$|(?=\s*&&|\s*\|\|))/g, '')
1939
- .trim();
1959
+ // Remove occurrences of '&& ()'
1960
+ const cleanedQuery = filteredClauses
1961
+ .join(' ')
1962
+ .replace(/&&\s*\(\)/g, '')
1963
+ .replace(/(\s*&&|\s*\|\|)(?=\s*[\s()]*$|(?=\s*&&|\s*\|\|))/g, '')
1964
+ .trim()
1940
1965
 
1941
- return cleanedQuery;
1966
+ return cleanedQuery
1942
1967
  }
1943
-
1944
-
1945
-
1946
-
1947
-