musora-content-services 2.79.0 → 2.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (211) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/conventional-commits.yaml +0 -0
  5. package/.github/workflows/docs.js.yml +0 -0
  6. package/.github/workflows/node.js.yml +0 -0
  7. package/.prettierignore +0 -0
  8. package/.prettierrc +0 -0
  9. package/CHANGELOG.md +14 -0
  10. package/README.md +0 -0
  11. package/babel.config.cjs +0 -0
  12. package/docs/Content.html +269 -0
  13. package/docs/ContentOrganization.html +2 -2
  14. package/docs/Forums.html +2 -2
  15. package/docs/Gamification.html +2 -2
  16. package/docs/TestUser.html +2 -2
  17. package/docs/UserManagementSystem.html +2 -2
  18. package/docs/api_types.js.html +2 -2
  19. package/docs/config.js.html +2 -2
  20. package/docs/content-org_content-org.js.html +2 -2
  21. package/docs/content-org_guided-courses.ts.html +2 -2
  22. package/docs/content-org_learning-paths.ts.html +124 -24
  23. package/docs/content-org_playlists-types.js.html +2 -2
  24. package/docs/content-org_playlists.js.html +2 -2
  25. package/docs/content.js.html +2 -2
  26. package/docs/content_artist.ts.html +212 -0
  27. package/docs/content_content.ts.html +77 -0
  28. package/docs/content_genre.ts.html +211 -0
  29. package/docs/content_instructor.ts.html +203 -0
  30. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  31. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  32. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  33. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  34. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  35. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  36. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  37. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  39. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  40. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  41. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  42. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  43. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  44. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  45. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  46. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  47. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  48. package/docs/forums_categories.ts.html +3 -3
  49. package/docs/forums_discussions.js.html +0 -0
  50. package/docs/forums_forum.js.html +0 -0
  51. package/docs/forums_forums.ts.html +2 -2
  52. package/docs/forums_posts.ts.html +22 -3
  53. package/docs/forums_threads.ts.html +16 -3
  54. package/docs/gamification_awards.js.html +0 -0
  55. package/docs/gamification_awards.ts.html +2 -2
  56. package/docs/gamification_gamification.js.html +2 -2
  57. package/docs/gamification_types.js.html +0 -0
  58. package/docs/global.html +2 -2
  59. package/docs/index.html +2 -2
  60. package/docs/liveTesting.ts.html +2 -2
  61. package/docs/module-Accounts.html +10 -10
  62. package/docs/module-Artist.html +991 -0
  63. package/docs/module-Awards.html +2 -2
  64. package/docs/module-Categories.html +0 -0
  65. package/docs/module-Config.html +2 -2
  66. package/docs/module-Content-Services-V2.html +2 -2
  67. package/docs/module-ForumCategories.html +0 -0
  68. package/docs/module-ForumDiscussions.html +0 -0
  69. package/docs/module-Forums.html +731 -89
  70. package/docs/module-Genre.html +981 -0
  71. package/docs/module-GuidedCourses.html +2 -2
  72. package/docs/module-Instructor.html +929 -0
  73. package/docs/module-Interests.html +2 -2
  74. package/docs/module-LearningPaths.html +640 -12
  75. package/docs/module-Onboarding.html +2 -2
  76. package/docs/module-Payments.html +2 -2
  77. package/docs/module-Permissions.html +2 -2
  78. package/docs/module-Playlists.html +2 -2
  79. package/docs/module-ProgressRow.html +2 -2
  80. package/docs/module-Railcontent-Services.html +2 -2
  81. package/docs/module-Sanity-Services.html +327 -1855
  82. package/docs/module-Sessions.html +2 -2
  83. package/docs/module-Threads.html +0 -0
  84. package/docs/module-UserActivity.html +2 -2
  85. package/docs/module-UserChat.html +2 -2
  86. package/docs/module-UserManagement.html +2 -2
  87. package/docs/module-UserMemberships.html +2 -2
  88. package/docs/module-UserNotifications.html +2 -2
  89. package/docs/module-UserProfile.html +2 -2
  90. package/docs/progress-row_method-card.js.html +8 -7
  91. package/docs/railcontent.js.html +2 -2
  92. package/docs/sanity.js.html +109 -268
  93. package/docs/scripts/collapse.js +0 -0
  94. package/docs/scripts/commonNav.js +0 -0
  95. package/docs/scripts/linenumber.js +0 -0
  96. package/docs/scripts/nav.js +0 -0
  97. package/docs/scripts/polyfill.js +0 -0
  98. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  99. package/docs/scripts/prettify/lang-css.js +0 -0
  100. package/docs/scripts/prettify/prettify.js +0 -0
  101. package/docs/scripts/search.js +0 -0
  102. package/docs/styles/jsdoc.css +0 -0
  103. package/docs/styles/prettify.css +0 -0
  104. package/docs/userActivity.js.html +2 -2
  105. package/docs/user_account.ts.html +5 -11
  106. package/docs/user_chat.js.html +2 -2
  107. package/docs/user_interests.js.html +2 -2
  108. package/docs/user_management.js.html +2 -2
  109. package/docs/user_memberships.js.html +0 -0
  110. package/docs/user_memberships.ts.html +2 -2
  111. package/docs/user_notifications.js.html +2 -2
  112. package/docs/user_onboarding.ts.html +2 -2
  113. package/docs/user_payments.ts.html +2 -2
  114. package/docs/user_permissions.js.html +2 -2
  115. package/docs/user_profile.js.html +2 -2
  116. package/docs/user_sessions.js.html +2 -2
  117. package/docs/user_types.js.html +2 -2
  118. package/docs/user_user-management-system.js.html +2 -2
  119. package/jest.config.js +0 -0
  120. package/jsdoc.json +1 -0
  121. package/package.json +1 -1
  122. package/src/contentMetaData.js +0 -0
  123. package/src/contentTypeConfig.js +2 -2
  124. package/src/filterBuilder.js +0 -0
  125. package/src/index.d.ts +28 -5
  126. package/src/index.js +28 -5
  127. package/src/infrastructure/http/HttpClient.ts +0 -0
  128. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  129. package/src/infrastructure/http/index.ts +0 -0
  130. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  131. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  132. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  133. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  134. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  135. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  136. package/src/lib/httpHelper.js +0 -0
  137. package/src/lib/lastUpdated.js +0 -0
  138. package/src/services/api/types.js +0 -0
  139. package/src/services/api/types.ts +0 -0
  140. package/src/services/config.js +0 -0
  141. package/src/services/content/artist.ts +139 -0
  142. package/src/services/content/content.ts +38 -0
  143. package/src/services/content/genre.ts +139 -0
  144. package/src/services/content/instructor.ts +131 -0
  145. package/src/services/content-org/content-org.js +0 -0
  146. package/src/services/content-org/guided-courses.ts +0 -0
  147. package/src/services/content-org/learning-paths.ts +35 -21
  148. package/src/services/content-org/playlists-types.js +0 -0
  149. package/src/services/content-org/playlists.js +0 -0
  150. package/src/services/content.js +0 -0
  151. package/src/services/contentLikes.js +0 -0
  152. package/src/services/contentProgress.js +0 -0
  153. package/src/services/dataContext.js +0 -0
  154. package/src/services/dateUtils.js +0 -0
  155. package/src/services/eventsAPI.js +0 -0
  156. package/src/services/forums/forums.ts +0 -0
  157. package/src/services/forums/posts.ts +0 -0
  158. package/src/services/forums/threads.ts +0 -0
  159. package/src/services/forums/types.ts +0 -0
  160. package/src/services/gamification/awards.ts +0 -0
  161. package/src/services/gamification/gamification.js +0 -0
  162. package/src/services/imageSRCBuilder.js +0 -0
  163. package/src/services/imageSRCVerify.js +0 -0
  164. package/src/services/liveTesting.ts +0 -0
  165. package/src/services/progress-row/method-card.js +0 -0
  166. package/src/services/railcontent.js +0 -0
  167. package/src/services/recommendations.js +0 -0
  168. package/src/services/sanity.js +105 -266
  169. package/src/services/types.js +0 -0
  170. package/src/services/user/account.ts +3 -9
  171. package/src/services/user/chat.js +0 -0
  172. package/src/services/user/interests.js +0 -0
  173. package/src/services/user/management.js +0 -0
  174. package/src/services/user/memberships.ts +0 -0
  175. package/src/services/user/notifications.js +0 -0
  176. package/src/services/user/onboarding.ts +0 -0
  177. package/src/services/user/payments.ts +0 -0
  178. package/src/services/user/permissions.js +0 -0
  179. package/src/services/user/profile.js +0 -0
  180. package/src/services/user/sessions.js +0 -0
  181. package/src/services/user/types.js +0 -0
  182. package/src/services/user/user-management-system.js +0 -0
  183. package/src/services/userActivity.js +0 -0
  184. package/test/HttpClient.test.js +0 -0
  185. package/test/content.test.js +0 -0
  186. package/test/contentLikes.test.js +0 -0
  187. package/test/contentProgress.test.js +0 -0
  188. package/test/dataContext.test.js +0 -0
  189. package/test/forum.test.js +0 -0
  190. package/test/imageSRCBuilder.test.js +0 -0
  191. package/test/imageSRCVerify.test.js +0 -0
  192. package/test/initializeTests.js +0 -0
  193. package/test/learningPaths.test.js +0 -0
  194. package/test/lib/lastUpdated.test.js +0 -0
  195. package/test/live/contentProgressLive.test.js +0 -0
  196. package/test/live/railcontentLive.test.js +0 -0
  197. package/test/localStorageMock.js +0 -0
  198. package/test/log.js +0 -0
  199. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  200. package/test/mockData/mockData_progress_content.json +0 -0
  201. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  202. package/test/mockData/mockData_user_practices.json +0 -0
  203. package/test/notifications.test.js +0 -0
  204. package/test/progressRows.test.js +0 -0
  205. package/test/sanityQueryService.test.js +0 -0
  206. package/test/streakMessage.test.js +0 -0
  207. package/test/user/permissions.test.js +0 -0
  208. package/test/userActivity.test.js +0 -0
  209. package/tools/generate-index.cjs +0 -0
  210. package/.claude/settings.local.json +0 -14
  211. package/.yarnrc.yml +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.79.0",
3
+ "version": "2.80.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
@@ -81,8 +81,8 @@ export const playAlongMp3sField = `{
81
81
  `
82
82
 
83
83
  export const instructorField = `instructor[]->{
84
- "id": railcontent_id,
85
84
  name,
85
+ slug,
86
86
  short_bio,
87
87
  "biography": short_bio[0].children[0].text,
88
88
  "coach_card_image": coach_card_image.asset->url,
@@ -278,7 +278,7 @@ export const getNextLessonLessonParentTypes = [
278
278
  'pack-bundle',
279
279
  'song-tutorial',
280
280
  'learning-path-v2',
281
- 'skill-pack'
281
+ 'skill-pack',
282
282
  ]
283
283
 
284
284
  export const progressTypesMapping = {
File without changes
package/src/index.d.ts CHANGED
@@ -5,6 +5,24 @@ import {
5
5
  initializeService
6
6
  } from './services/config.js';
7
7
 
8
+ import {
9
+ fetchArtistBySlug,
10
+ fetchArtistLessons,
11
+ fetchArtists
12
+ } from './services/content/artist.ts';
13
+
14
+ import {
15
+ fetchGenreBySlug,
16
+ fetchGenreLessons,
17
+ fetchGenres
18
+ } from './services/content/genre.ts';
19
+
20
+ import {
21
+ fetchInstructorBySlug,
22
+ fetchInstructorLessons,
23
+ fetchInstructors
24
+ } from './services/content/instructor.ts';
25
+
8
26
  import {
9
27
  enrollUserInGuidedCourse,
10
28
  fetchEnrollmentPageMetadata,
@@ -225,21 +243,18 @@ import {
225
243
  } from './services/recommendations.js';
226
244
 
227
245
  import {
246
+ buildEntityAndTotalQuery,
228
247
  fetchAll,
229
248
  fetchAllFilterOptions,
230
249
  fetchAllPacks,
231
- fetchArtistLessons,
232
- fetchArtists,
233
250
  fetchByRailContentId,
234
251
  fetchByRailContentIds,
235
252
  fetchByReference,
236
253
  fetchChatAndLiveEnvent,
237
- fetchCoachLessons,
238
254
  fetchComingSoon,
239
255
  fetchCommentModContentData,
240
256
  fetchContentRows,
241
257
  fetchFoundation,
242
- fetchGenreLessons,
243
258
  fetchHierarchy,
244
259
  fetchLeaving,
245
260
  fetchLessonContent,
@@ -274,6 +289,7 @@ import {
274
289
  fetchTabData,
275
290
  fetchTopLevelParentId,
276
291
  fetchUpcomingEvents,
292
+ getSanityDate,
277
293
  getSortOrder,
278
294
  jumpToContinueContent
279
295
  } from './services/sanity.js';
@@ -406,6 +422,7 @@ declare module 'musora-content-services' {
406
422
  assignModeratorToComment,
407
423
  blockUser,
408
424
  blockedUsers,
425
+ buildEntityAndTotalQuery,
409
426
  buildImageSRC,
410
427
  calculateLongestStreaks,
411
428
  closeComment,
@@ -442,6 +459,7 @@ declare module 'musora-content-services' {
442
459
  fetchAllCompletedStates,
443
460
  fetchAllFilterOptions,
444
461
  fetchAllPacks,
462
+ fetchArtistBySlug,
445
463
  fetchArtistLessons,
446
464
  fetchArtists,
447
465
  fetchAwardsForUser,
@@ -452,7 +470,6 @@ declare module 'musora-content-services' {
452
470
  fetchCertificate,
453
471
  fetchChatAndLiveEnvent,
454
472
  fetchChatSettings,
455
- fetchCoachLessons,
456
473
  fetchComingSoon,
457
474
  fetchComment,
458
475
  fetchCommentModContentData,
@@ -470,9 +487,14 @@ declare module 'musora-content-services' {
470
487
  fetchFollowedThreads,
471
488
  fetchForumCategories,
472
489
  fetchFoundation,
490
+ fetchGenreBySlug,
473
491
  fetchGenreLessons,
492
+ fetchGenres,
474
493
  fetchHandler,
475
494
  fetchHierarchy,
495
+ fetchInstructorBySlug,
496
+ fetchInstructorLessons,
497
+ fetchInstructors,
476
498
  fetchInterests,
477
499
  fetchLastInteractedChild,
478
500
  fetchLatestThreads,
@@ -572,6 +594,7 @@ declare module 'musora-content-services' {
572
594
  getRecommendedForYou,
573
595
  getResumeTimeSeconds,
574
596
  getResumeTimeSecondsByIds,
597
+ getSanityDate,
575
598
  getScheduleContentRows,
576
599
  getSortOrder,
577
600
  getStartedOrCompletedProgressOnly,
package/src/index.js CHANGED
@@ -5,6 +5,24 @@ import {
5
5
  initializeService
6
6
  } from './services/config.js';
7
7
 
8
+ import {
9
+ fetchArtistBySlug,
10
+ fetchArtistLessons,
11
+ fetchArtists
12
+ } from './services/content/artist.ts';
13
+
14
+ import {
15
+ fetchGenreBySlug,
16
+ fetchGenreLessons,
17
+ fetchGenres
18
+ } from './services/content/genre.ts';
19
+
20
+ import {
21
+ fetchInstructorBySlug,
22
+ fetchInstructorLessons,
23
+ fetchInstructors
24
+ } from './services/content/instructor.ts';
25
+
8
26
  import {
9
27
  enrollUserInGuidedCourse,
10
28
  fetchEnrollmentPageMetadata,
@@ -225,21 +243,18 @@ import {
225
243
  } from './services/recommendations.js';
226
244
 
227
245
  import {
246
+ buildEntityAndTotalQuery,
228
247
  fetchAll,
229
248
  fetchAllFilterOptions,
230
249
  fetchAllPacks,
231
- fetchArtistLessons,
232
- fetchArtists,
233
250
  fetchByRailContentId,
234
251
  fetchByRailContentIds,
235
252
  fetchByReference,
236
253
  fetchChatAndLiveEnvent,
237
- fetchCoachLessons,
238
254
  fetchComingSoon,
239
255
  fetchCommentModContentData,
240
256
  fetchContentRows,
241
257
  fetchFoundation,
242
- fetchGenreLessons,
243
258
  fetchHierarchy,
244
259
  fetchLeaving,
245
260
  fetchLessonContent,
@@ -274,6 +289,7 @@ import {
274
289
  fetchTabData,
275
290
  fetchTopLevelParentId,
276
291
  fetchUpcomingEvents,
292
+ getSanityDate,
277
293
  getSortOrder,
278
294
  jumpToContinueContent
279
295
  } from './services/sanity.js';
@@ -405,6 +421,7 @@ export {
405
421
  assignModeratorToComment,
406
422
  blockUser,
407
423
  blockedUsers,
424
+ buildEntityAndTotalQuery,
408
425
  buildImageSRC,
409
426
  calculateLongestStreaks,
410
427
  closeComment,
@@ -441,6 +458,7 @@ export {
441
458
  fetchAllCompletedStates,
442
459
  fetchAllFilterOptions,
443
460
  fetchAllPacks,
461
+ fetchArtistBySlug,
444
462
  fetchArtistLessons,
445
463
  fetchArtists,
446
464
  fetchAwardsForUser,
@@ -451,7 +469,6 @@ export {
451
469
  fetchCertificate,
452
470
  fetchChatAndLiveEnvent,
453
471
  fetchChatSettings,
454
- fetchCoachLessons,
455
472
  fetchComingSoon,
456
473
  fetchComment,
457
474
  fetchCommentModContentData,
@@ -469,9 +486,14 @@ export {
469
486
  fetchFollowedThreads,
470
487
  fetchForumCategories,
471
488
  fetchFoundation,
489
+ fetchGenreBySlug,
472
490
  fetchGenreLessons,
491
+ fetchGenres,
473
492
  fetchHandler,
474
493
  fetchHierarchy,
494
+ fetchInstructorBySlug,
495
+ fetchInstructorLessons,
496
+ fetchInstructors,
475
497
  fetchInterests,
476
498
  fetchLastInteractedChild,
477
499
  fetchLatestThreads,
@@ -571,6 +593,7 @@ export {
571
593
  getRecommendedForYou,
572
594
  getResumeTimeSeconds,
573
595
  getResumeTimeSecondsByIds,
596
+ getSanityDate,
574
597
  getScheduleContentRows,
575
598
  getSortOrder,
576
599
  getStartedOrCompletedProgressOnly,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @module Artist
3
+ */
4
+ import { DEFAULT_FIELDS, filtersToGroq } from '../../contentTypeConfig.js'
5
+ import { FilterBuilder } from '../../filterBuilder.js'
6
+ import { fetchSanity, getSanityDate, getSortOrder } from '../sanity.js'
7
+ import { Lesson } from './content'
8
+
9
+ export interface Artist {
10
+ slug: string
11
+ name: string
12
+ lessons?: Lesson[]
13
+ lessonsCount: number
14
+ }
15
+
16
+ /**
17
+ * Fetch all artists with lessons available for a specific brand.
18
+ *
19
+ * @param {string} brand - The brand for which to fetch artists.
20
+ * @returns {Promise<Artist[]|null>} - A promise that resolves to an array of artist objects or null if not found.
21
+ *
22
+ * @example
23
+ * fetchArtists('drumeo')
24
+ * .then(artists => console.log(artists))
25
+ * .catch(error => console.error(error));
26
+ */
27
+ export async function fetchArtists(brand: string): Promise<Artist[] | null> {
28
+ const filter = await new FilterBuilder(
29
+ `_type == "song" && brand == "${brand}" && references(^._id)`,
30
+ { bypassPermissions: true }
31
+ ).buildFilter()
32
+ const query = `
33
+ *[_type == "artist"]{
34
+ name,
35
+ "slug": slug.current,
36
+ "lessonsCount": count(*[${filter}])
37
+ }[lessonsCount > 0] |order(lower(name)) `
38
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false })
39
+ }
40
+
41
+ /**
42
+ * Fetch a single artist by their Sanity ID.
43
+ *
44
+ * @param {string} slug - The name of the artist to fetch.
45
+ * @param {string} [brand] - The brand for which to fetch the artist.
46
+ * @returns {Promise<Artist|null>} - A promise that resolves to an artist objects or null if not found.
47
+ *
48
+ * @example
49
+ * fetchArtists('drumeo')
50
+ * .then(artists => console.log(artists))
51
+ * .catch(error => console.error(error));
52
+ */
53
+ export async function fetchArtistBySlug(slug: string, brand?: string): Promise<Artist | null> {
54
+ const brandFilter = brand ? `brand == "${brand}" && ` : ''
55
+ const filter = await new FilterBuilder(`${brandFilter} _type == "song" && references(^._id)`, {
56
+ bypassPermissions: true,
57
+ }).buildFilter()
58
+ const query = `
59
+ *[_type == "artist" && slug.current == '${slug}']{
60
+ name,
61
+ "slug": slug.current,
62
+ "lessonsCount": count(*[${filter}])
63
+ }[lessonsCount > 0] |order(lower(name)) `
64
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false })
65
+ }
66
+
67
+ export interface ArtistLessonOptions {
68
+ sort?: string
69
+ searchTerm?: string
70
+ page?: number
71
+ limit?: number
72
+ includedFields?: Array<string>
73
+ progressIds?: Array<number>
74
+ }
75
+
76
+ export interface LessonsByArtistResponse {
77
+ entity: Artist[]
78
+ }
79
+
80
+ /**
81
+ * Fetch the artist's lessons.
82
+ * @param {string} brand - The brand for which to fetch lessons.
83
+ * @param {string} slug - The slug of the artist
84
+ * @param {string} contentType - The type of the lessons we need to get from the artist. If not defined, groq will get lessons from all content types
85
+ * @param {Object} params - Parameters for sorting, searching, pagination and filtering.
86
+ * @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
87
+ * @param {string} [params.searchTerm=""] - The search term to filter the lessons.
88
+ * @param {number} [params.page=1] - The page number for pagination.
89
+ * @param {number} [params.limit=10] - The number of items per page.
90
+ * @param {Array<string>} [params.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
91
+ * @param {Array<number>} [params.progressIds] - The ids of the lessons that are in progress or completed
92
+ * @returns {Promise<LessonsByArtistResponse|null>} - The lessons for the artist and some details about the artist (name and thumbnail).
93
+ *
94
+ * @example
95
+ * fetchArtistLessons('drumeo', '10 Years', 'song', {'-published_on', '', 1, 10, ["difficulty,Intermediate"], [232168, 232824, 303375, 232194, 393125]})
96
+ * .then(lessons => console.log(lessons))
97
+ * .catch(error => console.error(error));
98
+ */
99
+ export async function fetchArtistLessons(
100
+ brand: string,
101
+ slug: string,
102
+ contentType: string,
103
+ {
104
+ sort = '-published_on',
105
+ searchTerm = '',
106
+ page = 1,
107
+ limit = 10,
108
+ includedFields = [],
109
+ progressIds = undefined,
110
+ }: ArtistLessonOptions = {}
111
+ ): Promise<LessonsByArtistResponse | null> {
112
+ const fieldsString = DEFAULT_FIELDS.join(',')
113
+ const start = (page - 1) * limit
114
+ const end = start + limit
115
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
116
+ const sortOrder = getSortOrder(sort, brand)
117
+ const addType =
118
+ contentType && Array.isArray(contentType)
119
+ ? `_type in ['${contentType.join("', '")}'] &&`
120
+ : contentType
121
+ ? `_type == '${contentType}' && `
122
+ : ''
123
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
124
+
125
+ // limits the results to supplied progressIds for started & completed filters
126
+ const progressFilter =
127
+ progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
128
+ const now = getSanityDate(new Date())
129
+ const query = `{
130
+ "entity":
131
+ *[_type == 'artist' && slug.current == '${slug}']
132
+ {'type': _type, name, 'thumbnail':thumbnail_url.asset->url,
133
+ 'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
134
+ 'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
135
+ [${start}...${end}]}
136
+ |order(${sortOrder})
137
+ }`
138
+ return fetchSanity(query, true)
139
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @namespace Content
3
+ * @property {module:Instructor} Instructor
4
+ * @property {module:Genre} Genre
5
+ * @property {module:Artist} Artist
6
+ */
7
+
8
+ export type LessonType = 'workout' | 'challenge-part' | 'song' | string
9
+ export type LessonPageType = 'lesson' | 'song' | string
10
+
11
+ export interface ArtistObject {
12
+ name: string
13
+ thumbnail: string | null
14
+ }
15
+
16
+ export interface Lesson {
17
+ artist: ArtistObject | string | null
18
+ artist_name: string
19
+ brand: string
20
+ child_count: number | null
21
+ difficulty: number | null
22
+ difficulty_string: string | null
23
+ genre: string[] | string | null
24
+ id: number
25
+ image: string
26
+ length_in_seconds: number
27
+ parent_id: number | null
28
+ permission_id: number[]
29
+ published_on: string
30
+ sanity_id: string
31
+ slug: string
32
+ status: string
33
+ thumbnail: string
34
+ title: string
35
+ type: LessonType
36
+ need_access: boolean
37
+ page_type: LessonPageType
38
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @module Genre
3
+ */
4
+ import { DEFAULT_FIELDS, filtersToGroq } from '../../contentTypeConfig.js'
5
+ import { fetchSanity, getSanityDate, getSortOrder } from '../sanity.js'
6
+ import { FilterBuilder } from '../../filterBuilder.js'
7
+ import { Lesson } from './content'
8
+
9
+ export interface Genre {
10
+ lessons?: Lesson[]
11
+ lessons_count: number
12
+ name: string
13
+ slug: string
14
+ thumbnail: string
15
+ type: 'genre'
16
+ }
17
+
18
+ /**
19
+ * Fetch all genres with lessons available for a specific brand.
20
+ *
21
+ * @param {string} [brand] - The brand for which to fetch the genre for. Lesson count will be filtered by this brand if provided.
22
+ * @returns {Promise<Genre[]>} - A promise that resolves to an genre object or null if not found.
23
+ *
24
+ * @example
25
+ * fetchGenres('drumeo')
26
+ * .then(genres => console.log(genres))
27
+ * .catch(error => console.error(error));
28
+ */
29
+ export async function fetchGenres(brand: string): Promise<Genre[]> {
30
+ const filter = await new FilterBuilder(`brand == "${brand}" && references(^._id)`, {
31
+ bypassPermissions: true,
32
+ }).buildFilter()
33
+
34
+ const query = `
35
+ *[_type == 'genre'] {
36
+ 'type': _type,
37
+ name,
38
+ "slug": slug.current,
39
+ 'thumbnail': thumbnail_url.asset->url,
40
+ "lessons_count": count(*[${filter}])
41
+ } |order(lower(name)) `
42
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false})
43
+ }
44
+
45
+ /**
46
+ * Fetch a single genre by their slug and brand
47
+ *
48
+ * @param {string} slug - The slug of the genre to fetch.
49
+ * @param {string} [brand] - The brand for which to fetch the genre. Lesson count will be filtered by this brand if provided.
50
+ * @returns {Promise<Genre[]|null>} - A promise that resolves to an genre object or null if not found.
51
+ *
52
+ * @example
53
+ * fetchGenreBySlug('drumeo')
54
+ * .then(genres => console.log(genres))
55
+ * .catch(error => console.error(error));
56
+ */
57
+ export async function fetchGenreBySlug(slug: string, brand?: string): Promise<Genre | null> {
58
+ const brandFilter = brand ? `brand == "${brand}" && ` : ''
59
+ const filter = await new FilterBuilder(`${brandFilter} references(^._id)`, {
60
+ bypassPermissions: true,
61
+ }).buildFilter()
62
+
63
+ const query = `
64
+ *[_type == 'genre' && slug.current == '${slug}'] {
65
+ 'type': _type, name,
66
+ name,
67
+ "slug": slug.current,
68
+ 'thumbnail':thumbnail_url.asset->url,
69
+ "lessonsCount": count(*[${filter}])
70
+ }`
71
+ return fetchSanity(query, true, { processNeedAccess: false, processPageType: false})
72
+ }
73
+
74
+ export interface FetchGenreLessonsOptions {
75
+ sort?: string
76
+ searchTerm?: string
77
+ page?: number
78
+ limit?: number
79
+ includedFields?: Array<string>
80
+ progressIds?: Array<number>
81
+ }
82
+
83
+ export interface LessonsByGenreResponse {
84
+ entity: Genre[]
85
+ }
86
+
87
+ /**
88
+ * Fetch the genre's lessons.
89
+ * @param {string} brand - The brand for which to fetch lessons.
90
+ * @param {string} slug - The slug of the genre
91
+ * @param {Object} params - Parameters for sorting, searching, pagination and filtering.
92
+ * @param {string} [params.sort="-published_on"] - The field to sort the lessons by.
93
+ * @param {string} [params.searchTerm=""] - The search term to filter the lessons.
94
+ * @param {number} [params.page=1] - The page number for pagination.
95
+ * @param {number} [params.limit=10] - The number of items per page.
96
+ * @param {Array<string>} [params.includedFields=[]] - Additional filters to apply to the query in the format of a key,value array. eg. ['difficulty,Intermediate', 'genre,rock'].
97
+ * @param {Array<number>} [params.progressIds=[]] - The ids of the lessons that are in progress or completed
98
+ * @returns {Promise<LessonsByGenreResponse>} - The lessons for the artist and some details about the artist (name and thumbnail).
99
+ *
100
+ * @example
101
+ * fetchGenreLessons('drumeo', 'Blues', 'song', {'-published_on', '', 1, 10, ["difficulty,Intermediate"], [232168, 232824, 303375, 232194, 393125]})
102
+ * .then(lessons => console.log(lessons))
103
+ * .catch(error => console.error(error));
104
+ */
105
+ export async function fetchGenreLessons(
106
+ brand: string,
107
+ slug: string,
108
+ contentType: string,
109
+ {
110
+ sort = '-published_on',
111
+ searchTerm = '',
112
+ page = 1,
113
+ limit = 10,
114
+ includedFields = [],
115
+ progressIds = [],
116
+ }: FetchGenreLessonsOptions = {}
117
+ ): Promise<LessonsByGenreResponse> {
118
+ const fieldsString = DEFAULT_FIELDS.join(',')
119
+ const start = (page - 1) * limit
120
+ const end = start + limit
121
+ const searchFilter = searchTerm ? `&& title match "${searchTerm}*"` : ''
122
+ const sortOrder = getSortOrder(sort, brand)
123
+ const addType = contentType ? `_type == '${contentType}' && ` : ''
124
+ const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
125
+ // limits the results to supplied progressIds for started & completed filters
126
+ const progressFilter =
127
+ progressIds !== undefined ? `&& railcontent_id in [${progressIds.join(',')}]` : ''
128
+ const now = getSanityDate(new Date())
129
+ const query = `{
130
+ "entity":
131
+ *[_type == 'genre' && slug.current == '${slug}']
132
+ {'type': _type, name, 'thumbnail':thumbnail_url.asset->url,
133
+ 'lessons_count': count(*[${addType} brand == '${brand}' && references(^._id)]),
134
+ 'lessons': *[${addType} brand == '${brand}' && references(^._id) && (status in ['published'] || (status == 'scheduled' && defined(published_on) && published_on >= '${now}')) ${searchFilter} ${includedFieldsFilter} ${progressFilter}]{${fieldsString}}
135
+ [${start}...${end}]}
136
+ |order(${sortOrder})
137
+ }`
138
+ return fetchSanity(query, true)
139
+ }