musora-content-services 2.32.0 → 2.33.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.
Files changed (58) hide show
  1. package/.coderabbit.yaml +5 -0
  2. package/CHANGELOG.md +15 -0
  3. package/docs/ContentOrganization.html +2 -2
  4. package/docs/Gamification.html +2 -2
  5. package/docs/UserManagementSystem.html +2 -2
  6. package/docs/api_types.js.html +2 -2
  7. package/docs/config.js.html +2 -2
  8. package/docs/content-org_content-org.js.html +2 -2
  9. package/docs/content-org_guided-courses.ts.html +118 -0
  10. package/docs/content-org_playlists-types.js.html +2 -2
  11. package/docs/content-org_playlists.js.html +2 -2
  12. package/docs/content.js.html +2 -2
  13. package/docs/gamification_awards.js.html +1 -1
  14. package/docs/gamification_awards.ts.html +158 -0
  15. package/docs/gamification_gamification.js.html +2 -2
  16. package/docs/gamification_types.js.html +1 -1
  17. package/docs/global.html +2 -222
  18. package/docs/index.html +2 -2
  19. package/docs/module-Accounts.html +1471 -0
  20. package/docs/module-Awards.html +234 -14
  21. package/docs/module-Config.html +2 -2
  22. package/docs/module-Content-Services-V2.html +2 -2
  23. package/docs/module-GuidedCourses.html +108 -0
  24. package/docs/module-Interests.html +2 -2
  25. package/docs/module-Permissions.html +2 -2
  26. package/docs/module-Playlists.html +2 -2
  27. package/docs/module-Railcontent-Services.html +2 -2
  28. package/docs/module-Sanity-Services.html +2 -2
  29. package/docs/module-Sessions.html +2 -2
  30. package/docs/module-UserActivity.html +3 -3
  31. package/docs/module-UserChat.html +2 -2
  32. package/docs/module-UserManagement.html +2 -2
  33. package/docs/module-UserNotifications.html +2 -2
  34. package/docs/module-UserProfile.html +2 -2
  35. package/docs/railcontent.js.html +2 -2
  36. package/docs/sanity.js.html +2 -2
  37. package/docs/userActivity.js.html +7 -7
  38. package/docs/user_account.ts.html +190 -0
  39. package/docs/user_chat.js.html +2 -2
  40. package/docs/user_interests.js.html +2 -2
  41. package/docs/user_management.js.html +3 -2
  42. package/docs/user_notifications.js.html +2 -2
  43. package/docs/user_permissions.js.html +2 -2
  44. package/docs/user_profile.js.html +2 -2
  45. package/docs/user_sessions.js.html +2 -2
  46. package/docs/user_types.js.html +2 -2
  47. package/docs/user_user-management-system.js.html +2 -2
  48. package/jsdoc.json +11 -1
  49. package/package.json +5 -1
  50. package/src/index.d.ts +8 -2
  51. package/src/index.js +8 -2
  52. package/src/services/api/types.ts +22 -0
  53. package/src/services/gamification/awards.ts +86 -0
  54. package/src/services/user/account.ts +52 -0
  55. package/src/services/user/management.js +1 -0
  56. package/src/services/userActivity.js +5 -5
  57. package/src/services/gamification/awards.js +0 -93
  58. package/src/services/gamification/types.js +0 -8
package/jsdoc.json CHANGED
@@ -12,9 +12,19 @@
12
12
  "src/services/railcontent.js",
13
13
  "src/index.js"
14
14
  ],
15
- "includePattern": ".js$",
15
+ "includePattern": "(.js|.ts$)",
16
16
  "excludePattern": "(node_modules/|docs)"
17
17
  },
18
+ "plugins": [
19
+ "node_modules/jsdoc-babel"
20
+ ],
21
+ "babel": {
22
+ "extensions": ["ts"],
23
+ "ignore": ["**/*.(test|spec).ts"],
24
+ "babelrc": false,
25
+ "presets": [["@babel/preset-env", { "targets": { "node": true } }], "@babel/preset-typescript"],
26
+ "plugins": ["@babel/plugin-transform-class-properties", "@babel/plugin-transform-object-rest-spread"]
27
+ },
18
28
  "opts": {
19
29
  "destination": "./docs",
20
30
  "recurse": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.32.0",
3
+ "version": "2.33.1",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -21,9 +21,13 @@
21
21
  },
22
22
  "homepage": "https://github.com/railroadmedia/musora-content-services#readme",
23
23
  "devDependencies": {
24
+ "@babel/plugin-transform-class-properties": "^7.27.1",
25
+ "@babel/plugin-transform-object-rest-spread": "^7.28.0",
26
+ "@babel/preset-typescript": "^7.27.1",
24
27
  "dotenv": "^16.4.5",
25
28
  "jest": "^29.7.0",
26
29
  "jsdoc": "^4.0.3",
30
+ "jsdoc-babel": "^0.5.0",
27
31
  "prettier": "3.4.2",
28
32
  "standard-version": "^9.5.0",
29
33
  "ts-jest": "^29.2.4"
package/src/index.d.ts CHANGED
@@ -95,8 +95,9 @@ import {
95
95
  } from './services/forum.js';
96
96
 
97
97
  import {
98
- fetchAwardsForUser
99
- } from './services/gamification/awards.js';
98
+ fetchAwardsForUser,
99
+ fetchCertificate
100
+ } from './services/gamification/awards.ts';
100
101
 
101
102
  import {
102
103
  applyCloudflareWrapper,
@@ -219,6 +220,8 @@ import {
219
220
  } from './services/sanity.js';
220
221
 
221
222
  import {
223
+ confirmEmailChange,
224
+ requestEmailChange,
222
225
  resetPassword,
223
226
  sendAccountSetupEmail,
224
227
  sendPasswordResetEmail,
@@ -319,6 +322,7 @@ declare module 'musora-content-services' {
319
322
  buildImageSRC,
320
323
  calculateLongestStreaks,
321
324
  closeComment,
325
+ confirmEmailChange,
322
326
  contentStatusCompleted,
323
327
  contentStatusReset,
324
328
  convertToTimeZone,
@@ -348,6 +352,7 @@ declare module 'musora-content-services' {
348
352
  fetchByRailContentIds,
349
353
  fetchByReference,
350
354
  fetchCarouselCardData,
355
+ fetchCertificate,
351
356
  fetchChatAndLiveEnvent,
352
357
  fetchChatSettings,
353
358
  fetchCoachLessons,
@@ -507,6 +512,7 @@ declare module 'musora-content-services' {
507
512
  replyToComment,
508
513
  reportComment,
509
514
  reportPlaylist,
515
+ requestEmailChange,
510
516
  reset,
511
517
  resetPassword,
512
518
  restoreComment,
package/src/index.js CHANGED
@@ -95,8 +95,9 @@ import {
95
95
  } from './services/forum.js';
96
96
 
97
97
  import {
98
- fetchAwardsForUser
99
- } from './services/gamification/awards.js';
98
+ fetchAwardsForUser,
99
+ fetchCertificate
100
+ } from './services/gamification/awards.ts';
100
101
 
101
102
  import {
102
103
  applyCloudflareWrapper,
@@ -219,6 +220,8 @@ import {
219
220
  } from './services/sanity.js';
220
221
 
221
222
  import {
223
+ confirmEmailChange,
224
+ requestEmailChange,
222
225
  resetPassword,
223
226
  sendAccountSetupEmail,
224
227
  sendPasswordResetEmail,
@@ -318,6 +321,7 @@ export {
318
321
  buildImageSRC,
319
322
  calculateLongestStreaks,
320
323
  closeComment,
324
+ confirmEmailChange,
321
325
  contentStatusCompleted,
322
326
  contentStatusReset,
323
327
  convertToTimeZone,
@@ -347,6 +351,7 @@ export {
347
351
  fetchByRailContentIds,
348
352
  fetchByReference,
349
353
  fetchCarouselCardData,
354
+ fetchCertificate,
350
355
  fetchChatAndLiveEnvent,
351
356
  fetchChatSettings,
352
357
  fetchCoachLessons,
@@ -506,6 +511,7 @@ export {
506
511
  replyToComment,
507
512
  reportComment,
508
513
  reportPlaylist,
514
+ requestEmailChange,
509
515
  reset,
510
516
  resetPassword,
511
517
  restoreComment,
@@ -0,0 +1,22 @@
1
+ export interface PaginatedMeta {
2
+ current_page: number
3
+ from: number
4
+ to: number
5
+ per_page: number
6
+ last_page: number
7
+ total: number
8
+ path: string
9
+ }
10
+
11
+ export interface PaginatedLinks {
12
+ first: string
13
+ last: string
14
+ next: string
15
+ prev: string
16
+ }
17
+
18
+ export interface PaginatedResponse<T> {
19
+ meta: PaginatedMeta
20
+ links: PaginatedLinks
21
+ data: Array<T>
22
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @module Awards
3
+ */
4
+
5
+ import { HttpClient } from '../../infrastructure/http/HttpClient'
6
+ import { PaginatedResponse } from '../api/types'
7
+ import { globalConfig } from '../config'
8
+
9
+ const baseUrl = `/api/gamification`
10
+
11
+ export interface Award {
12
+ id: number
13
+ user_id: number
14
+ completed_at: string // ISO-8601 timestamp
15
+ completion_data: Object
16
+ award_id: number
17
+ type: string
18
+ title: string
19
+ badge: string
20
+ }
21
+
22
+ export interface Certificate {
23
+ id: number
24
+ user_name: string
25
+ user_id: number
26
+ completed_at: string // ISO-8601 timestamp
27
+ message: string
28
+ award_id: number
29
+ type: string
30
+ title: string
31
+ musora_logo: string
32
+ musora_logo_64: string
33
+ brand_logo: string
34
+ brand_logo_64: string
35
+ ribbon_image: string
36
+ ribbon_image_64: string
37
+ award_image: string
38
+ award_image_64: string
39
+
40
+ instructor_signature?: string
41
+ instructor_signature_64?: string
42
+ }
43
+ /**
44
+ * Get awards for a specific user.
45
+ *
46
+ * NOTE: needs error handling for the response from http client
47
+ * (Alexandre: I'm doing it in a different branch/PR: https://github.com/railroadmedia/musora-content-services/pull/349)
48
+ * NOTE: This function still expects brand because FE passes the argument. It is ignored for now
49
+ *
50
+ * @param {number} userId - The user ID. If not provided, the authenticated user is used instead.
51
+ * @param {string} _brand - The brand to fetch the awards for.
52
+ * @param {number|null} [page=1] - Page attribute for pagination
53
+ * @param {number|null} [limit=5] - Limit how many items to return
54
+ * @returns {Promise<PaginatedResponse<Award>>} - The awards for the user.
55
+ */
56
+ export async function fetchAwardsForUser(
57
+ userId: number,
58
+ _brand: string,
59
+ page: number = 1,
60
+ limit: number = 5
61
+ ): Promise<PaginatedResponse<Award>> {
62
+ const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
63
+ const response = await httpClient.get<PaginatedResponse<Award>>(
64
+ `${baseUrl}/v1/users/${userId}/awards?limit=${limit}&page=${page}`
65
+ )
66
+
67
+ return response
68
+ }
69
+
70
+ /**
71
+ * Get certificate data for a completed user award
72
+ *
73
+ * NOTE: needs error handling for the response from http client
74
+ * (Alexandre: I'm doing it in a different branch/PR: https://github.com/railroadmedia/musora-content-services/pull/349)
75
+ * NOTE: This function still expects brand because FE passes the argument. It is ignored for now
76
+ *
77
+ * @param {number} userAwardId - The user award progress id
78
+ * @returns {Promise<Certificate>} - The certificate data for the completed user award.
79
+ */
80
+ export async function fetchCertificate(userAwardId: number): Promise<Certificate> {
81
+ const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
82
+ const response = await httpClient.get<Certificate>(
83
+ `${baseUrl}/v1/users/certificate/${userAwardId}`
84
+ )
85
+ return response
86
+ }
@@ -12,6 +12,10 @@ export interface PasswordResetProps {
12
12
  token: string
13
13
  }
14
14
 
15
+ /**
16
+ * @param {string} email - The email address to check the account status for.
17
+ * @returns {Promise<{requires_setup: boolean}|HttpError>} - A promise that resolves to an object indicating whether account setup is required, or an HttpError if the request fails.
18
+ */
15
19
  export async function status(email: string): Promise<{ requires_setup: boolean } | HttpError> {
16
20
  const httpClient = new HttpClient(globalConfig.baseUrl)
17
21
  const response = await httpClient.get<{ requires_setup: boolean }>(
@@ -20,6 +24,10 @@ export async function status(email: string): Promise<{ requires_setup: boolean }
20
24
  return response
21
25
  }
22
26
 
27
+ /**
28
+ * @param {string} email - The email address to send the account setup email to.
29
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the email is sent or an HttpError if the request fails.
30
+ */
23
31
  export async function sendAccountSetupEmail(email: string): Promise<void | HttpError> {
24
32
  const httpClient = new HttpClient(globalConfig.baseUrl)
25
33
  return httpClient.post<void>(
@@ -28,6 +36,14 @@ export async function sendAccountSetupEmail(email: string): Promise<void | HttpE
28
36
  )
29
37
  }
30
38
 
39
+ /**
40
+ * @param {Object} params - The parameters for setting up the account.
41
+ * @property {string} email - The email address for the account.
42
+ * @property {string} password - The new password for the account.
43
+ * @property {string} passwordConfirmation - The confirmation of the new password.
44
+ * @property {string} token - The token sent to the user's email for verification.
45
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the account setup is complete or an HttpError if the request fails.
46
+ */
31
47
  export async function setupAccount({
32
48
  email,
33
49
  password,
@@ -43,6 +59,10 @@ export async function setupAccount({
43
59
  })
44
60
  }
45
61
 
62
+ /**
63
+ * @param {string} email - The email address to send the password reset email to.
64
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the email change request is made.
65
+ */
46
66
  export async function sendPasswordResetEmail(email: string): Promise<void | HttpError> {
47
67
  const httpClient = new HttpClient(globalConfig.baseUrl)
48
68
  return httpClient.post(`/api/user-management-system/v1/accounts/password/reset-email`, {
@@ -50,6 +70,14 @@ export async function sendPasswordResetEmail(email: string): Promise<void | Http
50
70
  })
51
71
  }
52
72
 
73
+ /**
74
+ * @param {Object} params - The parameters for resetting the password.
75
+ * @property {string} email - The email address for the account.
76
+ * @property {string} password - The new password for the account.
77
+ * @property {string} passwordConfirmation - The confirmation of the new password.
78
+ * @property {string} token - The token sent to the user's email for verification.
79
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the password reset is complete or an HttpError if the request fails.
80
+ */
53
81
  export async function resetPassword({
54
82
  email,
55
83
  password,
@@ -64,3 +92,27 @@ export async function resetPassword({
64
92
  token,
65
93
  })
66
94
  }
95
+
96
+ /**
97
+ * @param {string} email - The new email address to set for the user.
98
+ * @param {string} password - The current password of the user for verification.
99
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the email change request is made.
100
+ */
101
+ export async function requestEmailChange(
102
+ email: string,
103
+ password: string
104
+ ): Promise<void | HttpError> {
105
+ const apiUrl = `/api/user-management-system/v1/accounts/${globalConfig.sessionConfig.userId}/email-change`
106
+ const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
107
+ return httpClient.post(apiUrl, { email, password })
108
+ }
109
+
110
+ /**
111
+ * @param {string} token - The token sent to the user's email for verification.
112
+ * @returns {Promise<void|HttpError>} - A promise that resolves when the email change is confirmed.
113
+ */
114
+ export async function confirmEmailChange(token: string): Promise<void | HttpError> {
115
+ const apiUrl = `/api/user-management-system/v1/accounts/email-change/confirm`
116
+ const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
117
+ return httpClient.post(apiUrl, { token })
118
+ }
@@ -146,3 +146,4 @@ export async function updateDisplayName(newDisplayName) {
146
146
  const httpClient = new HttpClient(globalConfig.baseUrl, globalConfig.sessionConfig.token)
147
147
  return httpClient.put(apiUrl, { display_name: newDisplayName })
148
148
  }
149
+
@@ -995,6 +995,7 @@ function generateContentsMap(contents, playlistsContents) {
995
995
  const existingShows = new Set()
996
996
  const contentsMap = new Map()
997
997
  const childToParentMap = {}
998
+ if (!contents) return contentsMap
998
999
  contents.forEach((content) => {
999
1000
  if (Array.isArray(content.parent_content_data) && content.parent_content_data.length > 0) {
1000
1001
  childToParentMap[content.id] =
@@ -1073,20 +1074,20 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
1073
1074
  nonPlaylistContentIds.push(userPinnedItem.id)
1074
1075
  }
1075
1076
  const [playlistsContents, contents] = await Promise.all([
1076
- addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
1077
+ playlistEngagedOnContents ? addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
1077
1078
  addNextLesson: true,
1078
1079
  addNavigateTo: true,
1079
1080
  addProgressStatus: true,
1080
1081
  addProgressPercentage: true,
1081
1082
  addProgressTimestamp: true,
1082
- }),
1083
- addContextToContent(fetchByRailContentIds, nonPlaylistContentIds, 'progress-tracker', brand, {
1083
+ }) : Promise.resolve([]),
1084
+ nonPlaylistContentIds ? addContextToContent(fetchByRailContentIds, nonPlaylistContentIds, 'progress-tracker', brand, {
1084
1085
  addNextLesson: true,
1085
1086
  addNavigateTo: true,
1086
1087
  addProgressStatus: true,
1087
1088
  addProgressPercentage: true,
1088
1089
  addProgressTimestamp: true,
1089
- }),
1090
+ }) : Promise.resolve([]),
1090
1091
  ])
1091
1092
  const contentsMap = generateContentsMap(contents, playlistsContents)
1092
1093
  let combined = await extractPinnedItemsAndSortAllItems(
@@ -1103,7 +1104,6 @@ export async function getProgressRows({ brand = null, limit = 8 } = {}) {
1103
1104
  item.type === 'playlist' ? processPlaylistItem(item) : processContentItem(item)
1104
1105
  )
1105
1106
  )
1106
- console.log('HomePageProgressRows results: remove before merge', results)
1107
1107
  return {
1108
1108
  type: TabResponseType.PROGRESS_ROWS,
1109
1109
  displayBrowseAll: combined.length > limit,
@@ -1,93 +0,0 @@
1
- /**
2
- * @module Awards
3
- */
4
- import '../api/types.js'
5
- import './types.js'
6
-
7
- /**
8
- * Get awards for a specific user.
9
- *
10
- * @param {number|null} userId - The user ID. If not provided, the authenticated user is used instead.
11
- * @param {string|null} brand - Brand
12
- * @param {number|null} page - Page attribute for pagination
13
- * @param {number|null} limit - Limit how many items to return
14
- * @returns {Promise<PaginatedResponse<Award>>} - The awards for the user.
15
- */
16
- export async function fetchAwardsForUser(userId, brand, page = 1, limit = 20) {
17
- // TODO: connect to the API to get the user awards
18
- return {
19
- data: [
20
- {
21
- id: 1,
22
- title: '30-Day Drummer',
23
- challenge_title: '30-Day Drummer',
24
- award:
25
- 'https://cdn.sanity.io/files/4032r8py/staging/5c7449ea40f09a096451a48e4a58e4452244e38d.png',
26
- badge:
27
- 'https://cdn.sanity.io/files/4032r8py/staging/5c7449ea40f09a096451a48e4a58e4452244e38d.png',
28
- date_completed: '2021-01-01',
29
- award_text:
30
- 'You received this award for completing 30-Day Drummer with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
31
- description:
32
- 'You received this award for completing 30-Day Drummer with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
33
- },
34
- {
35
- id: 2,
36
- title: '30-Day Bass',
37
- challenge_title: '30-Day Bass',
38
- award:
39
- 'https://cdn.sanity.io/files/4032r8py/production/b7efb6a2f0bd57e87dd13f85b1c3b0c876272b7c.png',
40
- badge:
41
- 'https://cdn.sanity.io/files/4032r8py/production/b7efb6a2f0bd57e87dd13f85b1c3b0c876272b7c.png',
42
- date_completed: '2022-02-02',
43
- award_text:
44
- 'You received this award for completing 30-Day Bass with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
45
- description:
46
- 'You received this award for completing 30-Day Bass with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
47
- },
48
- {
49
- id: 3,
50
- title: 'New Piano Players Start Here',
51
- challenge_title: 'New Piano Players Start Here',
52
- award:
53
- 'https://cdn.sanity.io/files/4032r8py/staging/94e663015da362b41695f5d6f3a8ca3f8317e1b5.png',
54
- badge:
55
- 'https://cdn.sanity.io/files/4032r8py/staging/94e663015da362b41695f5d6f3a8ca3f8317e1b5.png',
56
- date_completed: '2023-03-03',
57
- award_text:
58
- 'You received this award for completing New Piano Players Start Here with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
59
- description:
60
- 'You received this award for completing New Piano Players Start Here with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
61
- },
62
- {
63
- id: 4,
64
- title: 'Ear Training For Singers',
65
- challenge_title: 'Ear Training For Singers',
66
- award:
67
- 'https://cdn.sanity.io/files/4032r8py/staging/24fc36e8351d7994bfe8fb5c63c577cf5ed16dc9.png',
68
- badge:
69
- 'https://cdn.sanity.io/files/4032r8py/staging/24fc36e8351d7994bfe8fb5c63c577cf5ed16dc9.png',
70
- date_completed: '2024-04-04',
71
- award_text:
72
- 'You received this award for completing Ear Training For Singers with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
73
- description:
74
- 'You received this award for completing Ear Training For Singers with a perfect streak. You practiced a total of 123 minutes over the past 30 days.',
75
- },
76
- ],
77
- meta: {
78
- current_page: 1,
79
- from: 1,
80
- to: 15,
81
- per_page: 15,
82
- last_page: 1,
83
- total: 4,
84
- path: 'to be implemented when BE is ready',
85
- },
86
- links: {
87
- first: 'to be implemented when BE is ready',
88
- last: 'to be implemented when BE is ready',
89
- next: 'to be implemented when BE is ready',
90
- prev: 'to be implemented when BE is ready',
91
- },
92
- }
93
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * @typedef {Object} Award
3
- * @property {number} id - Unique identifier for the award.
4
- * @property {string} title - The title of the award.
5
- * @property {string} badge - URL of the award badge image.
6
- * @property {string} description - Description of the award.
7
- * @property {Date} date_completed - Date when the award was completed.
8
- */